무엇을 만드나
달마다 다른 말일을 조건문 열두 개로 적으면 읽기 어렵습니다.
주어진 문제
연도와 월을 입력받아 그 달의 마지막 날이 며칠인지 알려 주는 프로그램을 만듭니다. 예를 들어 2024년 2월이면 29를 내놓습니다.
말일은 달마다 다릅니다. 31일인 달이 일곱, 30일인 달이 넷, 그리고 2월은 해에 따라 28일이 되기도 29일이 되기도 합니다. 이것을 조건문 열두 개로 적으면 만들 수는 있지만 읽기도 고치기도 어려워집니다. 값이 여럿일 때 목록에 담아 두는 방법을 이번에 익힙니다.
| 단계 | 여기서 답할 질문 |
|---|---|
| 1 요구 분석 | 어디까지 받아 주고 어떤 규칙을 따를 것인가 |
| 2 설계 | 열두 개의 값을 어떻게 담고 어떻게 꺼낼 것인가 |
| 3 구현 | 윤년 규칙을 뜻이 흔들리지 않게 어떻게 적을 것인가 |
| 4 테스트 | 윤년과 평년의 경계에서 무엇을 넣어 볼 것인가 |
먼저 개발 문서 양식을 내려받으세요
읽기만 하지 말고 직접 채워 보세요. 단계마다 자기 답을 적어 두면 그 기록이 요구 사항 명세서이자 설계서가 됩니다. 답을 보기 전에 먼저 적어 봅니다.
요구 분석과 설계는 언어와 상관없습니다. 두 양식의 앞부분은 같고, 구현을 적는 자리만 언어에 맞춰 다릅니다. 수업에서 쓰는 언어에 맞는 것을 받으면 됩니다. 이 강의노트의 코드 예시는 파이썬으로 되어 있습니다.
1단계 · 무엇을 만들 것인가
윤년 규칙은 「4의 배수」 하나가 아닙니다.
되물어야 할 것들
- 윤년을 어떻게 정하나요? 4의 배수면 다 윤년인가요
- 월이 13이면? 없는 달을 넣으면 어떻게 하나요
- 연도에 제한이 있나요? 0년이나 음수도 받나요
- 무엇을 내놓나요? 숫자만인가요, 「29일」처럼 글자를 붙이나요
정리한 요구 사항
- 연도와 월을 한 줄에 공백으로 띄어 입력받는다.
- 그 달의 마지막 날을 숫자만 내놓는다.
- 윤년은 400으로 나누어떨어지거나, 4로 나누어떨어지면서 100으로는 나누어떨어지지 않는 해로 한다.
- 윤년이면 2월만 29일로 하고 나머지 달은 그대로 둔다.
- 월은 1부터 12까지만 들어온다고 본다. 벗어난 값은 이번 판에서 다루지 않는다. 연도는 1 이상으로 본다.
「4의 배수면 윤년」은 틀린 규칙입니다
1900년은 4의 배수인데 윤년이 아니고, 2000년은 100의 배수인데 윤년입니다. 100으로 나누어떨어지는 해는 건너뛰되, 그중 400으로도 나누어떨어지는 해는 다시 윤년으로 셉니다. 가까운 해만 넣어 보면 이 규칙이 필요 없어 보입니다 — 2000년과 1900년을 넣어 봐야 드러납니다. 테스트할 값을 고르는 일이 왜 중요한지가 여기서 보입니다.
요구 사항 3번을 「4의 배수면 윤년」으로 줄여 적으면, 몇 년마다 한 번씩 틀린 답이 나올까요? 1900 · 2000 · 2100을 대 보세요.
2단계 · 열두 개를 어떻게 담나
값이 여럿일 때 조건문을 늘리지 않는 방법이 있습니다.
| 토막 | 무엇을 정하는가 |
|---|---|
| 입력 | 연도와 월, 정수 두 개 |
| 처리 | 말일 열두 개를 담아 두고, 윤년이면 2월만 고쳐 쓴다 |
| 출력 | 그 달에 해당하는 값 하나 |
목록에 담고 인덱스로 꺼낸다
1월부터 12월까지의 말일을 순서대로 늘어놓은 목록으로 담아 둡니다. 그러면 조건문 없이 몇 번째인지만 알려 주면 값이 나옵니다.
| 입력한 월 | 목록의 인덱스 | 담긴 값 |
|---|---|---|
| 1월 | 0 | 31 |
| 2월 | 1 | 28 — 윤년이면 여기만 29로 바꾼다 |
| 3월 | 2 | 31 |
| 12월 | 11 | 31 |
인덱스는 0부터 셉니다. 그래서 입력한 월에서 1을 빼야 맞는 자리가 됩니다. 이 한 칸 차이를 빠뜨리면 모든 달이 한 달씩 밀린 답을 내놓습니다. 문법 검사는 통과하고 결과도 나오니 논리 오류(멈추지 않고 틀린 답을 내는 오류)입니다.
푸는 순서
- 연도와 월을 입력받는다.
- 1월부터 12월까지의 말일을 평년 기준으로 목록에 담는다.
- 윤년이면 2월 자리의 값을 29로 바꾼다.
- 목록에서 인덱스 (월 − 1)의 값을 꺼내 내놓는다.
3번과 4번의 순서가 전부입니다
고치는 일이 꺼내는 일보다 앞에 있어야 합니다. 먼저 꺼내 놓고 나중에 목록을 고치면 이미 내놓은 값은 바뀌지 않습니다. 윤년인데도 28이 나오는데, 코드에는 29로 바꾸는 줄이 분명히 있어서 찾기가 아주 어렵습니다.
2월이 아닌 달, 이를테면 2024년 3월을 넣으면 3번 줄은 어떤 일을 할까요? 값을 바꾸는데도 답이 달라지지 않는 이유를 말해 보세요.
3단계 · 파이썬으로 옮기기
조건 세 개가 얽힌 규칙을 어떻게 적을까요?
먼저 스스로 적어 본 뒤에 코드를 보세요.
네 줄짜리 설계라 코드도 짧습니다. 어려운 곳은 윤년을 판별하는 한 줄뿐입니다.
설계에서 말한 목록은 파이썬의 리스트([ ])로 담습니다.
이달말일.기본.py
# 연도와 월을 한 줄에 공백으로 띄어 입력받는다
year, month = map(int, input().split())
# 1월부터 12월까지의 말일. 2월은 일단 평년 값으로 둔다
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
# 윤년이면 2월만 29로 바꾼다. 출력하기 전에 바꿔야 한다
if (year % 400 == 0) or (year % 4 == 0 and year % 100 != 0):
days[1] = 29
# 1월이 인덱스 0이므로 1을 뺀다
print(days[month - 1])괄호를 왜 쳤을까
「그리고」는 「또는」보다 먼저 묶입니다. 곱하기가 더하기보다 먼저 계산되는 것과 같습니다. 그래서 괄호가 없어도 이 식은 우연히 같은 뜻이 됩니다. 그렇다고 빼면 안 됩니다 — 읽는 사람이 그 규칙을 외우고 있어야 뜻을 알 수 있기 때문입니다. 괄호는 컴퓨터가 아니라 사람을 위해 칩니다.
목록을 만드는 줄에서 2월 자리를 처음부터 29로 적어 두면 어떻게 될까요? 그리고 그때 3번 줄은 무엇을 바꾸도록 고쳐야 할까요?
4단계 · 넣어 보고 확인하기
윤년 규칙은 가까운 해만 넣어 보면 통과합니다.
이 프로그램에서 틀려도 티가 안 나는 자리가 둘 있습니다. 하나는 100과 400의 규칙, 다른 하나는 인덱스 한 칸 차이입니다. 둘 다 노리고 값을 골라야 잡힙니다.
| 넣은 값 | 나와야 할 결과 | 무엇을 보려는 값인가 |
|---|---|---|
| 2024 2 | 29 | 4의 배수 — 흔한 윤년 |
| 2023 2 | 28 | 평년의 2월 |
| 1900 2 | 28 | 4의 배수인데 윤년이 아니다 — 100의 규칙 |
| 2000 2 | 29 | 100의 배수인데 윤년이다 — 400의 규칙 |
| 2024 1 | 31 | 첫 달 — 인덱스가 0으로 가는지 |
| 2024 12 | 31 | 끝 달 — 인덱스가 11로 가는지 |
| 2024 4 | 30 | 30일인 달 |
결과가 어긋났다면 여기부터 봅니다
- 모든 달이 한 칸씩 밀린다 — 꺼낼 때 1을 빼지 않았습니다
- 12월을 넣으면 멈춘다 — 같은 이유입니다. 인덱스 12는 목록에 없습니다
- 1900년이 29로 나온다 — 100의 조건이 빠졌습니다
- 2000년이 28로 나온다 — 400의 조건이 빠졌습니다
- 윤년인데 28이 나온다 — 값을 고치는 줄이 꺼내는 줄보다 뒤에 있습니다
표에 한 줄을 더 보태 보세요. 2100년 2월은 무엇이 나와야 할까요? 요구 사항 3번을 그대로 대 보면 됩니다.
더 해 보기
1단계에서 미뤄 둔 것을 데려와 봅시다.
요구 사항 5번에서 월이 1~12를 벗어나는 경우를 다루지 않기로 했습니다. 지금 프로그램에 13을 넣으면 목록에 없는 자리를 찾다가 멈추고, 0을 넣으면 멈추지도 않고 엉뚱한 답을 내놓습니다. 파이썬은 인덱스 −1을 맨 끝 값으로 읽어 12월의 31을 꺼내기 때문입니다. 0을 넣는 쪽이 더 위험합니다. 틀린 줄도 모르고 지나가기 때문입니다.
스스로 해 본 뒤에 열어 보세요
더 알아보기: 월을 검사하는 코드
꺼내기 전에 막습니다. 목록에서 꺼내기 전에 월이 1~12인지 먼저 확인합니다.
# 월이 1~12를 벗어나면 계산하지 않고 알린다
if 1 <= month <= 12:
print(days[month - 1])
else:
print("월은 1부터 12까지만 됩니다")한 걸음 더
- 월 이름을 함께 보여 주기 — 이름도 목록에 담으면 조건문이 필요 없습니다
- 그 해가 모두 며칠인지 세어 보기 (365 또는 366)
- 연도만 받아 열두 달의 말일을 한 번에 보여 주기
「월 이름도 목록에 담으면 조건문이 필요 없다」고 했습니다. 왜 그럴까요? 이번 실습에서 말일에 한 일을 떠올려 보세요.