프로그래밍

비만도 측정

개발 단계를 차례로 밟아
프로그램 하나를 끝까지 만들어 봅니다.

학습 목표

  1. 주어진 문제에서 요구 사항을 추출하고, 애매한 곳을 스스로 되물어 정할 수 있다.
  2. 구간에 따라 계산이 달라지는 문제를 입력 · 처리 · 출력으로 나누어 설계할 수 있다.
  3. 설계한 대로 조건을 여러 갈래로 나누는 코드를 작성할 수 있다.
  4. 경계값을 골라 테스트하고, 결과가 어긋난 까닭을 찾아 고칠 수 있다.

무엇을 만드나

개발 단계를 차례로 직접 밟아 봅니다.

주어진 문제

키와 몸무게를 입력받아, 그 키의 표준 몸무게에서 얼마나 벗어났는지를 계산하고 정상 · 과체중 · 비만 가운데 하나로 판정해 알려 주는 프로그램을 만듭니다.

먼저 밝혀 둘 것 — 이 프로그램은 건강을 판정하지 않습니다

여기서 쓰는 계산법과 구간은 수업에서 조건 나누기를 연습하려고 단순하게 정한 것입니다. 사람의 몸 상태는 키와 몸무게 두 숫자로 정해지지 않고, 실제 건강 판정은 훨씬 많은 것을 함께 봅니다. 이 프로그램이 내놓는 말은 계산 결과일 뿐 어떤 사람에 대한 평가가 아닙니다. 자기 값이나 남의 값을 넣어 보며 비교하는 용도로 쓰지 않도록 합니다.

단계 표의 순서로 갑니다. 요구 분석 · 설계 · 구현 · 테스트 · 유지 보수의 다섯 단계에서 유지 보수만 빠져 있습니다 — 만들자마자 끝나는 수업용 프로그램이기 때문입니다.

단계 여기서 답할 질문
1 요구 분석 정확히 무엇을 만들 것인가 — 애매한 곳을 없앤다
2 설계 무엇을 받아 어떻게 처리해 무엇을 내놓을 것인가
3 구현 정한 순서를 파이썬으로 어떻게 옮기는가
4 테스트 어떤 값을 넣어 보고, 어긋나면 어디를 볼 것인가

먼저 개발 문서 양식을 내려받으세요

읽기만 하지 말고 직접 채워 보세요. 단계마다 자기 답을 적어 두면 그 기록이 요구 사항 명세서이자 설계서가 됩니다. 답을 보기 전에 먼저 적어 봅니다.

요구 분석과 설계는 언어와 상관없습니다. 두 양식의 앞부분은 같고, 구현을 적는 자리만 언어에 맞춰 다릅니다. 수업에서 쓰는 언어에 맞는 것을 받으면 됩니다. 이 강의노트의 코드 예시는 파이썬으로 되어 있습니다.

1단계 · 무엇을 만들 것인가

문제 설명을 그대로 받아 적으면 요구 사항이 될까요?

문제 설명에는 아직 정해지지 않은 것이 남아 있습니다. 되물어 봅시다.

되물어야 할 것들

  • 키를 무엇으로 받나요? 센티미터인가요 미터인가요
  • 표준 몸무게를 어떻게 구하나요? 키에 따라 다른가요
  • 어디까지가 정상인가요? 경계에 딱 걸친 값은 어느 쪽인가요
  • 표준보다 가벼우면? 벗어난 정도가 음수가 되는데 그때는 무엇을 내놓나요
  • 소수점이 있는 키는요? 170.5처럼 들어올 수 있나요

되묻고 나서 이렇게 정했습니다. 다르게 정해도 됩니다. 다만 정했으면 적어 두어야 뒤 단계가 흔들리지 않습니다.

정리한 요구 사항

  1. 키(cm)와 몸무게(kg)를 한 줄에 공백으로 띄어 입력받는다. 소수점이 있어도 받는다.
  2. 표준 몸무게는 키 구간에 따라 다른 식으로 구한다.
  3. 벗어난 정도는 표준 몸무게 대비 백분율로 구한다.
  4. 10% 이하는 정상, 10% 초과 20% 이하는 과체중, 20% 초과는 비만으로 판정한다.
  5. 판정 결과 한 낱말만 화면에 내놓는다.
  6. 표준보다 가벼운 경우는 이번 판에서 따로 다루지 않는다. 정상에 함께 넣는다.

6번을 눈여겨보세요

「하지 않을 것」을 적어 둔 자리입니다. 적어 두지 않으면 나중에 「가벼운 사람한테 정상이라고 나오는데 이거 버그 아니야?」라는 말이 나옵니다. 정해서 뺀 것과 빠뜨린 것은 다릅니다. 적어 두어야 그 둘이 구별됩니다. 이 결정은 「더 해 보기」에서 다시 뒤집어 볼 것입니다.

스스로 확인

요구 사항 4번에서 「이하」와 「초과」를 굳이 나누어 적은 이유는 무엇일까요? 「10%쯤까지 정상」이라고 적었다면 무슨 일이 생길지 생각해 보세요.

2단계 · 어떻게 만들 것인가

계산이 구간마다 달라질 때는 어떻게 적어야 할까요?

토막 무엇을 정하는가
입력 키(cm)와 몸무게(kg) 두 실수를 한 줄에서 받는다
처리 키 구간으로 표준 몸무게를 구하고, 벗어난 정도를 백분율로 구한다
출력 정상 · 과체중 · 비만 가운데 한 낱말

표준 몸무게 — 키 구간에 따라 식이 다르다

키 구간 표준 몸무게 키 165cm이면
150cm 미만 키 − 100 —
150cm 이상 160cm 미만 (키 − 150) ÷ 2 + 50 —
160cm 이상 (키 − 100) × 0.9 (165 − 100) × 0.9 = 58.5kg

벗어난 정도(%) = (실제 몸무게 − 표준 몸무게) × 100 ÷ 표준 몸무게

푸는 순서

  1. 키와 몸무게를 입력받는다.
  2. 키가 150 미만이면 표준 몸무게 = 키 − 100
  3. 아니고 키가 160 미만이면 표준 몸무게 = (키 − 150) ÷ 2 + 50
  4. 둘 다 아니면 표준 몸무게 = (키 − 100) × 0.9
  5. 벗어난 정도 = (몸무게 − 표준 몸무게) × 100 ÷ 표준 몸무게
  6. 벗어난 정도가 10 이하면 「정상」을 내놓는다.
  7. 아니고 20 이하면 「과체중」을 내놓는다.
  8. 둘 다 아니면 「비만」을 내놓는다.

「아니고」가 조건을 줄여 준다

3번을 그냥 「키가 150 이상 160 미만이면」이라고 적을 수도 있습니다. 하지만 2번이 이미 걸러졌으므로 3번에 이르렀다는 것은 벌써 150 이상이라는 뜻입니다. 앞에서 걸러진 것을 다시 적지 않는 것이 조건을 짧고 안전하게 만듭니다.

순서를 바꾸면 답이 바뀐다

6~8번도 같습니다. 「20 이하면 과체중」을 먼저 검사하면 벗어난 정도가 5%인 사람도 20 이하이므로 과체중이 되어 버립니다. 좁은 조건을 먼저, 넓은 조건을 나중에 두어야 합니다.

스스로 확인

키 정확히 160cm인 사람은 「푸는 순서」에서 몇 번 줄로 갈까요? 2번과 3번의 조건을 차례로 대 보며 따라가 보세요.

3단계 · 파이썬으로 옮기기

여덟 줄짜리 순서가 코드에서는 어떻게 될까요?

먼저 스스로 적어 본 뒤에 코드를 보세요. 설계가 끝나 있으므로 새로 생각할 것은 없습니다. 순서를 문법으로 바꾸기만 하면 됩니다.

비만도.기본.py

# 키와 몸무게를 한 줄에 공백으로 띄어 입력받는다
height, weight = map(float, input().split())

# 키 구간에 따라 표준 몸무게를 구한다
if height < 150:
    sw = height - 100
elif height < 160:
    sw = (height - 150) / 2 + 50
else:
    sw = (height - 100) * 0.9

# 표준 몸무게에서 얼마나 벗어났는지를 백분율로 구한다
obesity = (weight - sw) * 100 / sw

# 구간을 위에서부터 차례로 검사한다
if obesity <= 10:
    print("정상")
elif obesity <= 20:
    print("과체중")
else:
    print("비만")

첫 줄이 하는 일

입력받은 것은 언제나 글자입니다. 「170 80」이라고 쳤어도 컴퓨터가 받은 것은 숫자 두 개가 아니라 글자가 늘어선 한 덩어리입니다. 그래서 공백에서 잘라 둘로 나누고, 그 각각을 수로 바꿔야 계산할 수 있습니다. 첫 줄이 그 셋을 한꺼번에 합니다. 풀어 쓰면 이렇게 됩니다.

# map을 쓰지 않고 한 줄씩 풀어 쓰면 이렇게 된다
data = input().split()   # "170 80" -> ['170', '80'] 문자열 두 개
height = float(data[0])  # 첫째 값을 실수로 바꾼다
weight = float(data[1])  # 둘째 값을 실수로 바꾼다

정수가 아니라 실수로 바꾸는 것에 유의합니다. 정수로 받으면 요구 사항 1번의 「소수점이 있어도 받는다」를 어기게 됩니다.

설계의 여덟 줄이 어디로 갔나

설계 2~4번이 첫 번째 조건 덩어리가 되고, 5번이 가운데 한 줄, 6~8번이 두 번째 조건 덩어리가 되었습니다. 줄 수도 순서도 설계와 그대로 맞습니다. 설계를 제대로 해 두면 구현이 이렇게 옮겨 적는 일이 됩니다.

스스로 확인

벗어난 정도를 구하는 줄에서 괄호를 빼면 어떻게 될까요? 곱하기와 나누기가 빼기보다 먼저 계산된다는 것을 떠올려 보세요.

4단계 · 넣어 보고 확인하기

이 프로그램에서 틀리기 쉬운 자리는 어디일까요?

조건이 나뉘는 자리가 네 군데입니다. 키에서 150과 160, 판정에서 10과 20입니다. 경계마다 값을 넣어 봐야 합니다. 테스트 표의 「나와야 할 결과」는 실행해 보기 전에 손으로 계산해 적은 것입니다.

넣은 값 표준 벗어난 정도 나와야 할 결과 무엇을 보려는 값인가
170 80 63.0 약 26.98% 비만 160 이상 구간이 도는지
170 69 63.0 약 9.52% 정상 10% 경계 바로 아래
170 70 63.0 약 11.11% 과체중 10% 경계 바로 위
155 53 52.5 약 0.95% 정상 150~160 구간이 도는지
140 40 40.0 0% 정상 150 미만 구간, 딱 0%
160 54 54.0 0% 정상 키 경계 160이 어느 쪽으로 가는지

결과가 어긋났다면 여기부터 봅니다

  • 비만이어야 할 값까지 「정상」으로 나온다 — 벗어난 정도를 구하는 줄에서 괄호가 빠졌는지 봅니다. 몸무게에서 100을 뺀 값이 되어 엉뚱하게 작아집니다
  • 정상이 나와야 할 값이 과체중으로 나온다 — 판정 조건의 순서가 뒤집혔거나 「이하」를 「미만」으로 적었는지 봅니다
  • 키 155에서 엉뚱한 값이 나온다 — 가운데 구간 식에서 나누기와 더하기의 순서를 봅니다
  • 소수점 있는 키를 넣으면 멈춘다 — 실수가 아니라 정수로 바꾸고 있는지 봅니다

어느 값에서 어긋났는지 적어 두고, 맞는 지점과 틀린 지점 사이를 좁혀 갑니다. 이 프로그램은 짧으니 표준 몸무게가 제대로 나왔는지만 먼저 확인해도 범위가 절반으로 줄어듭니다.

여섯 줄을 다 통과해도

「넣어 본 값에서는 문제가 없었다」까지입니다. 이를테면 몸무게에 0을 넣거나 키에 0이나 100을 넣으면 어떻게 되는지는 아직 넣어 보지 않았습니다. 키가 100이면 표준 몸무게가 0이 되어 0으로 나누다가 실행 오류로 멈추고, 키가 0이면 표준 몸무게가 −100이 되어 엉뚱한 판정이 나옵니다. 요구 사항에 적어 두지 않았으니 고칠 곳이 코드가 아니라 1단계입니다.

경계에 딱 걸친 값도 안심할 수 없습니다. 키 141, 몸무게 45.1은 손으로 계산하면 정확히 10%라 정상이어야 하지만, 컴퓨터의 실수 계산에 아주 작은 오차가 끼어 10.000000000000004가 되므로 과체중이 나옵니다. 경계값에서만 결과가 어긋나면 식보다 먼저 이 오차를 의심해 봅니다.

스스로 확인

표에 한 줄을 더 보태 보세요. 20% 경계를 확인하려면 키 170에 몸무게를 얼마로 넣어야 할까요? 표준 몸무게 63.0에서 거꾸로 계산해 보면 됩니다.

더 해 보기

1단계에서 미뤄 둔 것을 이제 되돌려 봅시다.

요구 사항 6번에서 표준보다 가벼운 경우를 따로 다루지 않기로 정했습니다. 그래서 지금 프로그램은 표준보다 훨씬 가벼운 값을 넣어도 「정상」이라고 답합니다. 벗어난 정도가 음수여도 10 이하라는 조건에 걸리기 때문입니다.

과제 — 요구 사항부터 고친다

「저체중」을 하나 더 알려 주도록 바꿔 봅시다. 코드부터 열지 말고 1단계로 돌아가 요구 사항 6번을 고쳐 적는 것에서 시작합니다. 그다음 설계의 판정 부분을 고치고, 마지막에 코드를 손댑니다. 테스트 표에도 줄을 더해야 합니다 — 저체중이 나와야 할 값을 미리 정해 두세요.

스스로 해 본 뒤에 열어 보세요

더 알아보기: 저체중까지 판정하는 코드

새 조건은 맨 앞에 둡니다. 아래로 내려가면 음수가 「10 이하」에 먼저 걸려 정상으로 나가 버립니다. 설계에서 「좁은 조건을 먼저」라고 한 것이 여기서 그대로 쓰입니다.

비만도.확장.py

height, weight = map(float, input().split())

if height < 150:
    sw = height - 100
elif height < 160:
    sw = (height - 150) / 2 + 50
else:
    sw = (height - 100) * 0.9

obesity = (weight - sw) * 100 / sw

# 음수를 먼저 걸러 낸다. 이 줄이 아래로 내려가면 저체중이 정상에 먹힌다
if obesity < 0:
    print("저체중")
elif obesity <= 10:
    print("정상")
elif obesity <= 20:
    print("과체중")
else:
    print("비만")

한 걸음 더

  • 판정만 내놓지 말고 표준 몸무게와 벗어난 정도까지 함께 보여 주기
  • 키에 0이나 음수가 들어왔을 때 계산하지 않고 안내만 하기 (요구 사항부터 고칠 것)
  • 벗어난 정도를 소수점 첫째 자리까지만 보여 주기
스스로 확인

이번 실습에서 코드를 적은 시간과 무엇을 만들지 정하고 순서를 짠 시간 중 어느 쪽이 길었나요? 코딩은 프로그래밍의 한 단계라는 말과 이어서 생각해 보세요.