프로그래밍웹 교과서
이 페이지의 목차
3단원 · 5차시

설계를 코드로 옮기다

구현코딩 규칙설계와 구현의 차이주석

코드 예제: Python · C · 예상 50분

학습 목표

  • 작성한 설계와 알고리즘을 프로그래밍 언어로 구현한다.
  • 구현 중 설계와 달라진 부분을 기록한다.

관련 성취기준

  • 프그03-03

    작성된 설계 프로그램을 프로그래밍 언어를 사용하여 구현할 수 있다.

생각 열기 — 설계는 있는데 어디부터

4차시에서 summarize의 알고리즘 27줄을 다 적었다. 이제 코드로 옮기면 된다. 그런데 편집기를 열고 나면 또 막힌다.

첫 줄부터 그대로 옮겨 쓰면 되나? 다른 부품은 아직 없는데 이것만 실행해 볼 수 있나? 다 쓰고 나서 실행했는데 안 되면 어디를 봐야 하지?

뼈대부터 세우고 하나씩 채운다

설계서의 부품 목록을 그대로 빈 껍데기로 먼저 만든다. 하는 일은 없고 이름과 받는 것·돌려주는 것만 있는 상태다.

이렇게 하면 이른 시점에 프로그램 전체가 한 번 돌아간다. 결과는 엉터리지만 부품들이 서로 잘 이어졌는지는 확인된다. 그다음 껍데기를 하나씩 진짜 코드로 바꾼다.

구현 순서 — 껍데기부터 채워 나간다
차례무엇을 하는가이때 확인되는 것
① 껍데기 만들기부품 이름과 받는 것·돌려주는 것만 만든다. 안은 정해진 값 하나를 돌려준다이름과 주고받는 값의 모양이 설계서와 맞는가
② 이어 보기주 프로그램에서 부품들을 순서대로 부른다부품끼리 이어지는가. 여기서 어긋나면 설계가 틀린 것이다
③ 가장 안쪽부터 채우기남에게 의존하지 않는 부품(is_valid)부터 진짜로 만든다그 부품 하나만 따로 확인할 수 있다
④ 바깥으로 나오기채운 부품을 쓰는 부품(summarize)을 만든다앞에서 확인한 것을 믿고 새 부분만 본다
⑤ 화면 붙이기report와 입력 부분을 만든다설계서의 출력 형식과 글자 단위로 맞는가

③에서 is_valid부터 만드는 것이 요령이다. 이 부품은 남을 부르지 않으므로 혼자서도 완성할 수 있고, 완성해 두면 summarize를 만들 때 그 부분을 의심하지 않아도 된다. 잘못의 위치를 좁히는 일이 여기서도 그대로 적용된다.

코딩 규칙을 먼저 정한다

여럿이 나눠 만들면 코드 모양이 제각각이 된다. 합칠 때 읽기 어려워지므로 몇 가지를 미리 정해 둔다.

  • 규칙 1

    이름 짓는 방식

    뜻이 담긴 이름을 쓴다. 부품 이름은 설계서에 적은 것을 그대로 쓴다 — 문서와 코드가 어긋나지 않게.

  • 규칙 2

    들여쓰기

    한 가지로 통일한다. 공백 4칸을 기본으로 한다. 섞이면 Python 에서는 아예 실행되지 않는다.

  • 규칙 3

    주석에 적을 것

    무엇을 하는지가 아니라 왜 그렇게 했는지를 적는다. 설계에서 판단한 이유가 여기 온다.

  • 규칙 4

    한 부품 한 곳

    한 사람이 한 부품을 맡는다. 같은 부품을 둘이 고치면 6차시에서 충돌이 난다.

알고리즘을 두 언어로 옮겨 보기

4차시의 알고리즘 하나가 언어에 따라 어떻게 갈라지는지 보자. 먼저 평균을 내는 부분만 떼어 낸다. 알고리즘의 23~26줄 — 인원이 0이면 빠져나가고, 아니면 나눈다.

Python평균 부분만 떼어 낸 구현
def average(scores):
    if len(scores) == 0:
        return None
    return sum(scores) / len(scores)

scores = [88, 92, 75, 100, 64]
print("평균:", average(scores))
print("빈 목록:", average([]))
확인된 실행 결과
평균: 83.8
빈 목록: None
C평균 부분만 떼어 낸 구현확인된 출력
#include <stdio.h>

/* 점수가 없으면 평균을 낼 수 없다. 성공 여부를 반환값으로 알린다. */
int average(const int scores[], int n, double *out)
{
    if (n == 0) return 0;
    int sum = 0;
    for (int i = 0; i < n; i++) sum += scores[i];
    *out = (double)sum / n;
    return 1;
}

int main(void)
{
    int scores[] = {88, 92, 75, 100, 64};
    double avg;

    if (average(scores, 5, &avg))
        printf("평균: %.1f\n", avg);
    if (!average(scores, 0, &avg))
        printf("빈 목록: 평균을 낼 수 없다\n");
    return 0;
}
실행 결과
평균: 83.8
빈 목록: 평균을 낼 수 없다

같은 알고리즘, 다른 표기

'값이 없으면 없음을 돌려준다'를 두 언어가 처리하는 방식
정한 것PythonC
목록 넘기기목록 하나만 넘긴다. 길이는 목록이 안다배열과 개수를 따로 넘긴다. 배열은 자기 길이를 모른다
"없음" 돌려주기None 을 돌려준다값과 성공 여부를 함께 돌려줄 수 없어, 성공 여부를 반환값으로 하고 결과는 *out 에 넣는다
정수 나누기/ 가 실수 나눗셈이다정수끼리 나누면 소수가 잘린다. (double) 로 바꿔야 한다
소수 자릿수출력할 때 정한다%.1f 로 정한다

구현 중 설계와 달라진 것을 기록한다

코드를 쓰다 보면 설계가 틀렸거나 부족했다는 것을 알게 된다. 위의 C 반환값 문제가 그렇다. 설계서에는 “쓸 수 있는 값이 없으면 없음을 돌려준다”고만 적었는데, C에는 “없음”이라는 값이 없었다.

이때 코드만 고치고 문서를 그대로 두면 문서를 아무도 믿지 않게 된다. 그래서 짧게라도 남긴다.

날짜 무엇이 달라졌나 왜 문서 반영
구현 1일차 average가 C에서는 값을 두 개 돌려주는 형태가 됨 C에는 “없음”에 해당하는 값이 없어, 성공 여부를 반환값으로 하고 결과는 포인터로 넘김 설계서 「돌려주는 것」 칸에 언어별 표기 추가
구현 1일차 나눗셈에 (double) 추가 정수끼리 나누면 소수가 잘려 F3을 못 지킴 알고리즘 26줄에 주석 추가

직접 옮겨 보기

코드 작성

평균 구하는 부품을 직접 입력해 보기

Python

위 Python 코드를 아래 칸에 직접 입력해 보자. 특히 나누기 전에 길이를 확인하는 줄이 왜 먼저 와야 하는지 생각하면서 친다. 다 적었으면 준비된 개발 환경에서 실행해 결과를 대조한다.

직접 입력해 보자. 이 칸에는 붙여넣기가 되지 않는다. 예제를 옮겨 적는 것이 아니라 손으로 쳐 보아야 문법이 익는다.

0줄 · 0글자
기대하는 실행 결과 보기
평균: 83.8
빈 목록: None

직접 실행해 얻은 결과와 위 내용이 같은지 대조해 보자. 다르면 어느 줄에서 갈렸는지 찾는다.

힌트 보기

먼저 def 로 함수를 만든다. 빈 목록이면 None 을 돌려주고 빠져나가야 하므로, 나누기 전에 if 로 길이를 확인한다. 목록의 합은 sum(), 개수는 len() 으로 구한다.

활동

프로젝트

핵심 기능 구현

약 45분

3차시 설계서와 4차시 알고리즘을 펴 놓고 코드로 옮긴다.

1단계 — 코딩 규칙을 정한다 (5분)

모둠에서 아래를 정해 종이 맨 위에 적는다. 이후 모두 이 규칙을 지킨다.

항목 우리 모둠의 규칙
들여쓰기
부품 이름 표기
주석에 적을 것
누가 어느 부품을 맡는가

2단계 — 껍데기를 만든다 (10분)

설계서의 부품을 전부 빈 껍데기로 만들고, 주 프로그램에서 순서대로 불러 본다. 안은 정해진 값 하나를 돌려주게 둔다.

  • 이 단계에서 한 번 실행해 본다. 결과는 엉터리여도 된다.
  • 실행이 안 되면 부품끼리 이어지는 부분이 틀린 것이다. 여기서 잡고 넘어간다.

3단계 — 안쪽부터 채운다 (25분)

남을 부르지 않는 부품부터 진짜로 만든다. 하나 채울 때마다 실행해 본다.

부품 채운 뒤 확인한 입력 나온 결과 기대한 결과 (4차시 책상 검사) 같은가

4차시 책상 검사에서 손으로 낸 값을 「기대한 결과」에 적는다. 지금 프로그램이 낸 값을 적지 않는다.

4단계 — 달라진 것을 기록한다 (5분)

설계와 다르게 만든 부분이 있으면 표로 남긴다.

무엇이 달라졌나 왜 문서에 반영했는가

한 부품도 완성하지 못했어도 괜찮다. 껍데기까지는 반드시 만들어 둔다. 6차시에서 그것을 합친다.

개념 확인 문제

보기를 누르면 바로 채점된다. 틀려도 해설을 읽으면 된다.

구현할 때 부품의 빈 껍데기부터 만들고 한 번 실행해 보는 이유로 가장 알맞은 것은?프그03-03

여러 부품 중 is_valid 처럼 남을 부르지 않는 부품부터 만드는 이유는?프그03-03

C에서 정수 419를 정수 5로 나눈 결과를 그대로 쓰면 생기는 문제는?프그03-03

구현 중에 설계와 다르게 만든 부분이 생겼을 때 해야 할 일은?프그03-03

모둠에서 코딩 규칙(들여쓰기·이름 표기)을 미리 정하는 실질적인 이유는?프그03-03

부품을 하나 채운 뒤 확인할 때, '기대한 결과' 칸에 적어야 하는 값은?프그03-03

더 알아보기

심화가짜 부품으로 먼저 돌려 보기

3차시에서 겉모습을 못 박으면 아직 없는 부품도 흉내 낼 수 있다고 했다. 껍데기 단계가 바로 그것이다. summarize가 아직 없어도 “인원 5명, 평균 83.8” 이라고 정해진 값을 돌려주게 만들어 두면, report를 맡은 사람은 그것으로 출력 형식을 완성할 수 있다. 실무에서는 이런 임시 코드를 만들어 두고 여러 사람이 서로를 기다리지 않고 동시에 작업한다. 다만 진짜 부품이 완성되면 임시 코드를 반드시 지워야 한다. 남겨 두면 나중에 왜 이 값이 나오는지 아무도 모르게 된다.

실무'왜'를 적은 주석이 살아남는다

합계 = 합계 + 점수 옆에 “점수를 더한다”고 적은 주석은 코드가 바뀌면 곧 거짓말이 된다. 반대로 “결시생은 0점이 아니라 제외해야 하므로 여기서 걸러 낸다”처럼 판단의 이유를 적은 주석은 코드가 바뀌어도 오래 쓸모가 있다. 위 C 예제의 /* 점수가 없으면 평균을 낼 수 없다 */ 도 같은 종류다 — 왜 반환값을 이렇게 만들었는지를 설명한다. 1단원 5차시와 9차시에서 되풀이해 나온 이야기이며, 실제로 이것을 지키는 사람은 많지 않다.

심화언어가 표현을 바꾸는 지점

같은 알고리즘이 언어에 따라 갈라지는 곳은 대체로 정해져 있다. 여러 값을 한 번에 돌려줄 수 있는가(Python은 가능, C는 포인터가 필요), “값이 없음”을 나타낼 수 있는가(Python은 None, C는 따로 약속해야 한다), 자료의 길이를 자료가 아는가(Python은 알고, C의 배열은 모른다). 새 언어를 배울 때 이 세 가지가 어떻게 되는지 먼저 확인하면 알고리즘을 옮기기가 훨씬 수월하다.

이어지는 학습

이 차시를 이해하려면 다음을 먼저 알아야 한다.

  • 4차시의 알고리즘 — 프로그래밍 프그03-02

교과서에서 찾기

교과서해당 위치쪽수
성림출판(Python)프로그램 설계와 구현 › 01 프로그램 개발 과정 > 3 프로그램 구현216쪽
씨마스커뮤니케이션(Python)프로그램 설계와 구현 › 2 프로그래밍 실제222쪽
삼양미디어(Python)프로그램의 설계와 구현 › 02 프로그램 개발 프로젝트190쪽
삼양미디어(C)프로그램의 설계와 구현 › 02 프로그램 개발 프로젝트197쪽
씨마스(C)프로그래밍 설계와 구현 › 2 프로그래밍의 실제 Ⅰ206쪽
웅보출판사(C)프로그램 설계와 구현 › Chapter 03 프로그램 구현215쪽