하리Hari

14. 작은 프로그램 만들기

지금까지 배운 것을 모아 성적표, 숫자 맞히기, 할 일 목록을 처음부터 만들어 봐요.

이 장에서 배울 것

  • 만들고 싶은 것을 작은 조각으로 나누는 법
  • 조각을 하나씩 만들고 실행해 보면서 조금씩 키우는 법
  • 세 가지 작은 프로그램: 성적표, 숫자 맞히기, 할 일 목록

프로그램은 한 번에 만들지 않아요

처음부터 끝까지 한 번에 써 내려가는 사람은 거의 없어요. 대부분 이렇게 만들어요.

  1. 무엇을 만들지 말로 적어 본다.
  2. 그것을 작은 조각으로 나눈다.
  3. 가장 쉬운 조각부터 만들고 바로 실행해 본다.
  4. 잘 되면 다음 조각을 붙인다.

조각마다 실행해 보면 오류가 나도 방금 고친 몇 줄만 보면 되니까 찾기 쉬워요. 이 장의 예제도 단계마다 실행 버튼을 눌러 보면서 따라와 보세요.


1. 성적표 만들기

만들 것: 학생들의 점수를 보고 각자의 등급과 반 평균, 1등을 알려 주는 프로그램이에요.

조각 나누기

  • 점수를 담아 둔다 → 사전 (7장)
  • 점수로 등급을 정한다 → 조건문 (5장) + 함수 (9장)
  • 학생마다 출력한다 → 반복문 (8장)
  • 평균과 1등을 구한다 → 반복하면서 합계와 가장 큰 값 기억하기

1단계: 등급 함수

가장 작은 조각부터 만들어요. 점수를 받아서 등급 글자를 돌려주는 함수예요. 만들고 나면 몇 가지 점수로 시험해 봐요.

<등급>을 만들자 ('점수'):
    만약 ('점수'가 90 이상이다) 라면:
        "A"를 돌려주자
    그렇지 않고 만약 ('점수'가 80 이상이다) 라면:
        "B"를 돌려주자
    그렇지 않고 만약 ('점수'가 70 이상이다) 라면:
        "C"를 돌려주자
    "D"를 돌려주자

(<등급>(95))를 출력하자
(<등급>(80))를 출력하자
(<등급>(42))를 출력하자

A, B, D가 나오면 성공이에요. 경계에 있는 값(80처럼 딱 기준인 점수)을 꼭 넣어 보세요. 실수가 가장 많이 숨어 있는 곳이에요.

마지막 "D"를 돌려주자 앞에 그렇지 않다면:이 없어도 돼요. 앞에서 돌려주자를 만나면 그 자리에서 함수가 끝나니까, 여기까지 왔다는 건 70점 미만이라는 뜻이에요.

2단계: 학생마다 출력하기

이제 점수를 사전에 담고, 학생마다 반복하면서 1단계의 함수를 써요.

<등급>을 만들자 ('점수'):
    만약 ('점수'가 90 이상이다) 라면:
        "A"를 돌려주자
    그렇지 않고 만약 ('점수'가 80 이상이다) 라면:
        "B"를 돌려주자
    그렇지 않고 만약 ('점수'가 70 이상이다) 라면:
        "C"를 돌려주자
    "D"를 돌려주자

'성적'을 {"민지": 92, "서준": 78, "하은": 85, "도윤": 64}로 정하자

'성적'의 '이름'마다 반복하자:
    '점수'를 '성적'의 '이름'으로 정하자
    틀"{'이름'}: {'점수'}점 ({<등급>('점수')})"를 출력하자

3단계: 평균과 1등

반복하면서 두 가지를 기억해 둬요.

  • '합계': 점수를 계속 더해요. 다 돌면 학생 수로 나누면 평균이에요.
  • '최고점'과 '1등': 지금까지 본 점수보다 크면 바꿔 둬요.
<등급>을 만들자 ('점수'):
    만약 ('점수'가 90 이상이다) 라면:
        "A"를 돌려주자
    그렇지 않고 만약 ('점수'가 80 이상이다) 라면:
        "B"를 돌려주자
    그렇지 않고 만약 ('점수'가 70 이상이다) 라면:
        "C"를 돌려주자
    "D"를 돌려주자

'성적'을 {"민지": 92, "서준": 78, "하은": 85, "도윤": 64}로 정하자

'합계'를 0으로 정하자
'인원'을 0으로 정하자
'최고점'을 0으로 정하자
'1등'을 ""으로 정하자

"===== 성적표 ====="을 출력하자
'성적'의 '이름'마다 반복하자:
    '점수'를 '성적'의 '이름'으로 정하자
    틀"{'이름'}: {'점수'}점 ({<등급>('점수')})"를 출력하자

    '합계'에 '점수'를 더하자
    '인원'에 1을 더하자
    만약 ('점수'가 '최고점'보다 크다) 라면:
        '최고점'을 '점수'로 정하자
        '1등'을 '이름'으로 정하자

"=================="을 출력하자
틀"반 평균: {'합계' / '인원'}점"을 출력하자
틀"1등: {'1등'} ({'최고점'}점)"을 출력하자

'인원'을 따로 세는 이유는 사전에는 '길이'가 없기 때문이에요(7장).

더 해 보기: 학생을 한 명 더 넣어 보세요. 코드는 하나도 안 고쳐도 성적표가 알아서 길어져요. 이게 반복문과 함수로 나눠 만든 덕분이에요.


2. 숫자 맞히기 게임

만들 것: 컴퓨터가 1부터 100 사이의 수를 하나 정하면, 사용자가 맞힐 때까지 추측해요. 틀릴 때마다 “더 커요” / “더 작아요”라고 알려 주고, 맞히면 몇 번 만에 맞혔는지 알려 줘요.

조각 나누기

  • 정답 정하기 → [무작위]의 <정수> (11장)
  • 추측 받기 → 입력받자 (2장)
  • 비교해서 알려 주기 → 조건문
  • 맞힐 때까지 계속 → 동안 반복하자 (8장)
  • 횟수 세기 → 반복할 때마다 1 더하기

1단계: 입력 없이 규칙부터

입력을 받으면 실행할 때마다 직접 쳐야 해서 시험하기 번거로워요. 그래서 정답을 고정하고, 추측도 목록에 미리 적어 두고 규칙부터 확인해요.

'정답'을 42로 정하자
'추측들'을 [50, 25, 37, 43, 40, 42]로 정하자
'횟수'를 0으로 정하자

'추측들'의 '추측'마다 반복하자:
    '횟수'에 1을 더하자
    만약 ('추측'이 '정답'보다 작다) 라면:
        틀"{'추측'}: 더 커요!"를 출력하자
    그렇지 않고 만약 ('추측'이 '정답'보다 크다) 라면:
        틀"{'추측'}: 더 작아요!"를 출력하자
    그렇지 않다면:
        틀"{'추측'}: 정답! {'횟수'}번 만에 맞혔어요."를 출력하자
        반복을 끝내자

'추측들'의 숫자를 바꿔 가며 “더 커요”와 “더 작아요”가 맞게 나오는지 확인해 보세요. 규칙이 맞으면 이제 진짜 입력으로 바꾸기만 하면 돼요.

2단계: 진짜 게임

바뀐 곳은 세 군데예요.

  • 정답을 <정수>(1, 100)으로 뽑아요.
  • 목록 대신 동안 반복하자로 맞힐 때까지 돌아요. '맞혔다'가 거짓인 동안 계속해요.
  • 추측을 [숫자]로 입력받자로 받아요.
[무작위]에서 <정수>를 가져오자

'정답'을 <정수>(1, 100)으로 정하자
'횟수'를 0으로 정하자
'맞혔다'를 거짓으로 정하자

"1부터 100 사이의 수를 하나 정했어요. 맞혀 보세요!"를 출력하자

('맞혔다'가 거짓과 같다)인 동안 반복하자:
    "추측: "을 이어출력하자
    '추측'을 [숫자]로 입력받자
    '횟수'에 1을 더하자

    만약 ('추측'이 '정답'보다 작다) 라면:
        "더 커요!"를 출력하자
    그렇지 않고 만약 ('추측'이 '정답'보다 크다) 라면:
        "더 작아요!"를 출력하자
    그렇지 않다면:
        틀"정답! {'횟수'}번 만에 맞혔어요."를 출력하자
        '맞혔다'를 참으로 정하자

실행 버튼을 누르면 결과 칸에 입력 칸이 생겨요. 반씩 나눠 가며(50 → 25 또는 75 → …) 추측하면 7번 안에 꼭 맞힐 수 있어요.

3단계: 튼튼하게 만들기

지금은 숫자 대신 글자를 넣으면 프로그램이 오류로 멈춰요. 10장의 안전망으로 감싸면 다시 물어볼 수 있어요. 1부터 100 밖의 수도 걸러 볼게요.

[무작위]에서 <정수>를 가져오자

'정답'을 <정수>(1, 100)으로 정하자
'횟수'를 0으로 정하자
'맞혔다'를 거짓으로 정하자

"1부터 100 사이의 수를 하나 정했어요. 맞혀 보세요!"를 출력하자

('맞혔다'가 거짓과 같다)인 동안 반복하자:
    "추측: "을 이어출력하자
    '추측'을 0으로 정하자
    일단 해보자:
        '추측'을 [숫자]로 입력받자
    오류가 발생했다면:
        "숫자로 입력해 주세요."를 출력하자

    만약 ('추측'이 1보다 작다) 또는 ('추측'이 100보다 크다) 라면:
        "1부터 100 사이의 수만 돼요."를 출력하자
    그렇지 않다면:
        '횟수'에 1을 더하자
        만약 ('추측'이 '정답'보다 작다) 라면:
            "더 커요!"를 출력하자
        그렇지 않고 만약 ('추측'이 '정답'보다 크다) 라면:
            "더 작아요!"를 출력하자
        그렇지 않다면:
            틀"정답! {'횟수'}번 만에 맞혔어요."를 출력하자
            '맞혔다'를 참으로 정하자

'추측'을 안전망 앞에서 먼저 0으로 정해 둔 것을 눈여겨보세요. 글자를 넣어 입력이 실패하면 '추측'은 0인 채로 남아서 “1부터 100 사이의 수만 돼요.“로 넘어가요. 오류가 발생했다면: 묶음 안에서 처음 정한 이름표는 그 묶음이 끝나면 사라지니까, 바깥에서 쓸 이름표는 바깥에서 먼저 정해 두는 게 안전해요. 잘못된 입력은 '횟수'에 세지 않아요.


3. 할 일 목록

만들 것: 할 일을 추가하고, 다 한 일을 표시하고, 목록을 보여 주는 프로그램이에요.

조각 나누기

  • 할 일 하나 = “내용”과 “다 했는지”를 함께 담은 사전
  • 할 일 전체 = 그 사전들을 담은 목록
  • 추가하기 / 완료하기 / 보여 주기 = 각각 함수 하나씩

1단계: 데이터 모양 정하기

할 일 하나는 이렇게 생겼어요.

'할일'을 {"내용": "장보기", "완료": 거짓}으로 정하자
('할일'의 "내용")을 출력하자
('할일'의 "완료")를 출력하자

이런 사전을 목록에 여러 개 담으면 할 일 목록이 돼요. 무엇을 어떤 모양으로 담을지를 먼저 정해 두면 나머지 코드가 쉬워져요.

2단계: 함수 세 개

'목록'을 []로 정하자

<추가하기>를 만들자 ('내용'):
    '목록'에 {"내용": '내용', "완료": 거짓}을 추가하자
    틀"추가했어요: {'내용'}"을 출력하자

<완료하기>를 만들자 ('번호'):
    만약 ('번호'가 1보다 작다) 또는 ('번호'가 '목록'의 '길이'보다 크다) 라면:
        틀"{'번호'}번 할 일은 없어요."를 출력하자
        돌려주자
    '할일'을 '목록'의 '번호'번째로 정하자
    '할일'의 "완료"를 참으로 정하자
    틀"다 했어요: {'할일'의 "내용"}"을 출력하자

<보여주기>를 만들자 ():
    만약 ('목록'의 '길이'가 0과 같다) 라면:
        "할 일이 없어요."를 출력하자
        돌려주자
    '번호'를 1로 정하자
    '목록'의 '할일'마다 반복하자:
        '표시'를 "[ ]"로 정하자
        만약 ('할일'의 "완료") 라면:
            '표시'를 "[v]"로 정하자
        틀"{'번호'}. {'표시'} {'할일'의 "내용"}"을 출력하자
        '번호'에 1을 더하자

<보여주기>()를 실행하자
<추가하기>("장보기")를 실행하자
<추가하기>("숙제하기")를 실행하자
<추가하기>("방 청소")를 실행하자
<완료하기>(2)를 실행하자
<완료하기>(9)를 실행하자
<보여주기>()를 실행하자

눈여겨볼 곳:

  • 함수 안에서 바깥의 '목록'을 읽고 바꿀 수 있어요(9장).
  • '할일'을 '목록'의 '번호'번째로 정한 뒤 '할일'의 "완료"를 바꾸면, 목록 안의 그 사전도 바뀌어요. 사전은 복사되지 않고 같은 것을 함께 가리키니까요(7장).
  • 돌려주자만 쓰면 아무것도 돌려주지 않고 그 자리에서 함수를 끝내요. 잘못된 번호일 때 더 진행하지 않으려고 썼어요.

3단계: 명령으로 움직이기

이제 사용자가 추가 우유 사기, 완료 1, 보기, 끝 같은 명령을 입력하게 만들어요. 명령 글자를 <분리하기>(" ")로 쪼개면 첫 단어가 명령이에요(4장). 함수는 2단계와 같아요.

'목록'을 []로 정하자

<추가하기>를 만들자 ('내용'):
    '목록'에 {"내용": '내용', "완료": 거짓}을 추가하자
    틀"추가했어요: {'내용'}"을 출력하자

<완료하기>를 만들자 ('번호'):
    만약 ('번호'가 1보다 작다) 또는 ('번호'가 '목록'의 '길이'보다 크다) 라면:
        틀"{'번호'}번 할 일은 없어요."를 출력하자
        돌려주자
    '할일'을 '목록'의 '번호'번째로 정하자
    '할일'의 "완료"를 참으로 정하자
    틀"다 했어요: {'할일'의 "내용"}"을 출력하자

<보여주기>를 만들자 ():
    만약 ('목록'의 '길이'가 0과 같다) 라면:
        "할 일이 없어요."를 출력하자
        돌려주자
    '번호'를 1로 정하자
    '목록'의 '할일'마다 반복하자:
        '표시'를 "[ ]"로 정하자
        만약 ('할일'의 "완료") 라면:
            '표시'를 "[v]"로 정하자
        틀"{'번호'}. {'표시'} {'할일'의 "내용"}"을 출력하자
        '번호'에 1을 더하자

"명령: 추가 (내용) / 완료 (번호) / 보기 / 끝"을 출력하자
'계속'을 참으로 정하자

('계속'이 참과 같다)인 동안 반복하자:
    "> "를 이어출력하자
    '입력'을 입력받자
    '단어들'을 '입력'의 <분리하기>(" ")로 정하자
    '명령'을 '단어들'의 1번째로 정하자
    '나머지'를 '입력'의 <자르기>('명령'의 '길이' + 2, '입력'의 '길이')로 정하자

    만약 ('명령'이 "추가"와 같다) 라면:
        <추가하기>('나머지')를 실행하자
    그렇지 않고 만약 ('명령'이 "완료"와 같다) 라면:
        일단 해보자:
            <완료하기>(<숫자로>('나머지'))를 실행하자
        오류가 발생했다면:
            "번호를 숫자로 적어 주세요."를 출력하자
    그렇지 않고 만약 ('명령'이 "보기"와 같다) 라면:
        <보여주기>()를 실행하자
    그렇지 않고 만약 ('명령'이 "끝"과 같다) 라면:
        "안녕히 가세요!"를 출력하자
        '계속'을 거짓으로 정하자
    그렇지 않다면:
        "모르는 명령이에요."를 출력하자

'나머지'는 명령 뒤의 글자 전부예요. 추가 우유 사기라면 "추가"(2글자) 다음의 띄어쓰기 한 칸을 건너뛰어 3번째 글자부터 끝까지, 즉 우유 사기가 돼요. <분리하기>로 쪼갠 단어를 다시 붙이지 않아도 돼서 편해요.


막혔을 때 써 볼 방법

  • 중간 값 출력하기: 이상하다 싶은 이름표를 그 자리에서 출력하자로 찍어 보세요. 생각한 값과 다른 곳이 문제가 시작된 곳이에요.
  • 더 작게 줄이기: 코드 일부를 지우거나 (참고)로 막아 두고(줄 앞에 (참고)를 붙이면 그 줄은 실행되지 않아요), 문제가 남는 가장 작은 모양을 찾아요.
  • 입력을 고정하기: 숫자 맞히기 1단계처럼 입력 대신 정해진 값으로 시험하면 같은 상황을 몇 번이고 다시 볼 수 있어요.
  • 오류 메시지 읽기: 몇 번째 줄인지, 어떤 종류인지 알려 줘요. 종류별 뜻은 부록. 자주 만나는 오류에 있어요.

정리

단계 하는 일
말로 적기 무엇을 만들지 한두 문장으로
조각 나누기 데이터는 무엇에 담을지, 어떤 함수가 필요할지
작게 시작 가장 쉬운 조각부터, 만들 때마다 실행
입력은 나중에 정해진 값으로 규칙부터 확인하고 입력으로 바꾸기
튼튼하게 잘못된 입력을 안전망과 만약으로 거르기

연습 문제

1. 성적표에 꼴찌(가장 낮은 점수)도 출력해 보세요. 처음 값을 무엇으로 정해야 할지 생각해 보세요.

정답 예시

최저점의 처음 값은 어떤 점수보다도 커야 해요. 점수는 100점까지니까 101로 시작하면 돼요.

'성적'을 {"민지": 92, "서준": 78, "하은": 85, "도윤": 64}로 정하자

'최저점'을 101로 정하자
'꼴찌'를 ""으로 정하자

'성적'의 '이름'마다 반복하자:
    만약 ('성적'의 '이름'이 '최저점'보다 작다) 라면:
        '최저점'을 '성적'의 '이름'으로 정하자
        '꼴찌'를 '이름'으로 정하자

틀"가장 낮은 점수: {'꼴찌'} ({'최저점'}점)"을 출력하자

2. 숫자 맞히기 1단계 코드에 “10번 안에 못 맞히면 끝”이라는 규칙을 더해 보세요. 추측 목록을 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 42]로 두고, 10번째에서 실패! 정답은 42.가 나오면 돼요.

정답 예시
'정답'을 42로 정하자
'추측들'을 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 42]로 정하자
'횟수'를 0으로 정하자

'추측들'의 '추측'마다 반복하자:
    '횟수'에 1을 더하자
    만약 ('추측'이 '정답'과 같다) 라면:
        틀"정답! {'횟수'}번 만에 맞혔어요."를 출력하자
        반복을 끝내자
    만약 ('횟수'가 10과 같다) 라면:
        틀"실패! 정답은 {'정답'}."를 출력하자
        반복을 끝내자
    만약 ('추측'이 '정답'보다 작다) 라면:
        틀"{'추측'}: 더 커요!"를 출력하자
    그렇지 않다면:
        틀"{'추측'}: 더 작아요!"를 출력하자

정답 검사를 먼저 해야 10번째에 맞힌 경우도 “정답!“이 돼요.

3. 할 일 목록에 남은 할 일 개수를 세는 <남은개수> 함수를 만들어 보세요. 2단계 예제 끝에서 부르면 2가 나와야 해요.

정답 예시
'목록'을 [{"내용": "장보기", "완료": 거짓}, {"내용": "숙제하기", "완료": 참}, {"내용": "방 청소", "완료": 거짓}]으로 정하자

<남은개수>를 만들자 ():
    '개수'를 0으로 정하자
    '목록'의 '할일'마다 반복하자:
        만약 ('할일'의 "완료"가 거짓과 같다) 라면:
            '개수'에 1을 더하자
    '개수'를 돌려주자

(<남은개수>())를 출력하자

여기까지 왔다면 하리로 꽤 많은 것을 만들 수 있어요. 비슷한 것을 여러 개 다루는 프로그램이 커지면 객체지향 문법이 코드를 정리하는 데 도움이 돼요. 파일을 읽고 쓰거나 실행 파일로 묶고 싶다면 Hana 런타임을 보세요.

놀이터
결과
코드를 작성하고 '실행' 버튼을 눌러보세요.