어긋나는 방식이 세 가지 있다
같은 「오류」라도 드러나는 때가 서로 다릅니다.
간식을 똑같이 나누는 프로그램에 간식 6개, 4명을 넣었더니 「한 사람당 2개, 2개 남음」이 나왔다고 해 봅시다. 맞는 답은 한 사람당 1개, 2개 남음입니다. 이런 것을 찾아 고치는 방법을 봅니다. 먼저 알아 둘 것은 프로그램이 어긋나는 방식이 한 가지가 아니라는 것입니다.
문법 오류 — 규칙에 어긋나게 적었다
실행 전에 검사할 때 드러난다
따옴표를 한쪽만 적었거나, 괄호를 닫지 않았거나, 예약어(프로그래밍 언어가 쓰려고 정해 둔 낱말)의 철자를 틀린 경우입니다. 번역기가 실행하기도 전에 멈추고 어디가 이상한지 알려 줍니다. 이렇게 알려 주니 셋 중 가장 반가운 오류입니다.
실행 오류 — 도중에 멈췄다
그 줄에 이르렀을 때 드러난다
규칙에는 맞게 적혀 있어서 문법 검사는 통과했는데, 실제로 실행해 보니 할 수 없는 일을 만나 멈추는 경우입니다. 0으로 나누려 하거나, 숫자를 받아야 할 자리에 글자가 들어오거나, 없는 파일을 열려고 할 때 이렇게 됩니다. 다만 언어에 따라서는 멈추지 않고 엉뚱한 값을 든 채 계속 돌기도 합니다. 이 경우에는 논리 오류처럼 겉으로 드러나지 않아 더 찾기 어렵습니다.
논리 오류 — 끝까지 도는데 답이 틀렸다
아무도 알려 주지 않는다
번역도 통과하고 멈추지도 않고 결과까지 잘 나옵니다. 그 결과가 틀렸을 뿐입니다. 나누기와 나머지를 바꿔 적었거나, 크다와 크거나 같다를 헷갈렸거나, 순서를 뒤집어 적은 경우입니다. 아무 표시도 나지 않기 때문에 찾아내기가 가장 어렵습니다.
| 종류 | 언제 드러나나 | 간식 나누기에서 예를 들면 |
|---|---|---|
| 문법 오류 | 실행 전에 검사할 때. 실행이 시작되지 않는다 | 안내 문구의 따옴표를 한쪽만 적었다 |
| 실행 오류 | 돌다가 그 줄에서 멈춘다. 멈추지 않고 계속 도는 언어도 있다 | 사람 수 0을 검사하지 않고 그대로 나눴다 |
| 논리 오류 | 드러나지 않는다. 사람이 찾아야 한다 | 몫을 낼 자리에 나머지를 냈다 |
「에러 없이 잘 돌아간다」는 안심할 근거가 아닙니다
빨간 글씨가 안 뜨면 다 된 것 같지만, 그것은 문법 오류와 실행 오류가 없다는 뜻일 뿐입니다. 논리 오류는 원래 아무 표시도 나지 않습니다. 번역을 통과해도 원한 대로 동작한다는 보장이 없는 까닭이 여기 있습니다. 그래서 값을 넣어 보고 결과를 눈으로 대조하는 일이 따로 필요합니다.
「간식 6개, 4명 → 한 사람당 2개, 2개 남음」은 셋 중 어느 오류일까요? 그렇게 판단한 근거를 「언제 드러나나」 열에서 찾아 말해 보세요.
원인을 좁혀 가는 일
어디가 잘못됐는지 모를 때 무엇부터 해야 할까요?
프로그램 속 잘못을 버그(bug)라 하고, 그것을 찾아 고치는 일을 디버깅(debugging)이라고 합니다. 흔히 코드를 노려보며 틀린 곳을 「발견하는」 일로 여기지만, 실제로는 그렇게 되지 않습니다. 범위를 절반씩 줄여 가며 원인을 가두는 일에 가깝습니다.
1. 다시 일어나게 만든다
어떤 값을 넣었을 때 잘못됐는지 정확히 적어 둡니다. 다시 일으킬 수 없는 문제는 고쳤는지도 확인할 수 없습니다.
2. 어디까지 맞는지 확인한다
중간에 값을 찍어 보며 「여기까지는 맞다」는 지점을 찾습니다. 맞는 지점과 틀린 지점 사이가 좁아질수록 원인이 갇힙니다.
3. 왜 그런지 설명해 본다
고치기 전에 「이러이러해서 이런 결과가 나온 것이다」를 말로 해 봅니다. 설명이 안 되면 아직 원인을 못 찾은 것입니다.
4. 고치고 다시 넣어 본다
1번에 적어 둔 값을 다시 넣어 확인합니다. 고치다가 다른 데를 망가뜨렸을 수 있으니 멀쩡하던 값들도 다시 넣어 봅니다.
이것저것 바꿔 보다 고쳐지는 경우
여기저기 손대다 보면 어느 순간 결과가 맞게 나오기도 합니다. 기분은 좋지만 왜 고쳐졌는지 모르는 상태는 고치기 전과 크게 다르지 않습니다. 원인을 모르면 같은 문제가 다른 자리에서 또 나올 때 또 헤매게 되고, 실은 가려 놓았을 뿐 안 고쳐진 경우도 있습니다. 「왜 그런지 설명해 본다」를 건너뛰지 않는 이유입니다.
「고치고 다시 넣어 본다」에서 「멀쩡하던 값들도 다시 넣어 본다」고 했습니다. 왜 그래야 할까요? 고치는 행위가 무엇을 건드렸을지 생각해 보세요.
아무 값이나 넣어 보면 될까
넣어 볼 수 있는 값은 끝이 없는데, 무엇부터 넣어야 할까요?
간식 개수로 넣을 수 있는 숫자는 끝이 없습니다. 전부 넣어 볼 수는 없으니 잘 고르는 수밖에 없습니다. 그리고 잘 고른다는 것은 틀릴 만한 자리를 노린다는 뜻입니다.
경계값을 노린다
조건이 나뉘는 자리에서 실수가 자주 납니다. 「사람 수는 1명 이상」이라는 조건이 있다면 경계인 1과, 그 바로 아래인 0, 바로 위인 2를 함께 넣어 봅니다. 가운데 값 50만 넣어 보면 경계에서 어긋나는 것을 못 잡습니다.
일부러 이상한 값을 넣는다
만든 사람은 바르게 쓸 줄 아니까 바르게만 넣어 봅니다. 쓰는 사람은 그렇지 않습니다. 숫자 자리에 글자를 넣고, 빈칸으로 넘기고, 음수를 넣습니다. 「그렇게 넣을 리가 없는데」라고 생각한 값일수록 넣어 봐야 합니다.
간식 나누기를 실제로 시험한 기록
넣은 값 · 나와야 할 결과 · 실제로 나온 결과를 나란히 적습니다. 「나와야 할 결과」는 프로그램을 실행해 보고 적는 것이 아니라 요구 사항을 보고 미리 적습니다. 실행해 본 뒤에 적으면 틀린 답을 맞다고 적게 됩니다.
이렇게 어긋난 곳을 적어 둔 것을 오류 보고서라고 부릅니다. 고치는 사람은 어떤 값을 넣으면 다시 일어나는지를 알아야 하므로, 「안 돼요」 대신 넣은 값과 나온 결과를 함께 적습니다.
이 시험 기록은 몫 자리에 나머지를 내던 실수를 고친 뒤에 다시 시험한 것입니다.
| 넣은 값 | 나와야 할 결과 | 실제로 나온 결과 | 판정 |
|---|---|---|---|
| 간식 12개, 4명 | 한 사람당 3개, 0개 남음 | 한 사람당 3개, 0개 남음 | 통과 |
| 간식 6개, 4명 | 한 사람당 1개, 2개 남음 | 한 사람당 1개, 2개 남음 | 통과 |
| 간식 3개, 5명 | 한 사람당 0개, 3개 남음 | 한 사람당 0개, 3개 남음 | 통과 |
| 간식 10개, 1명 | 한 사람당 10개, 0개 남음 | 한 사람당 10개, 0개 남음 | 통과 |
| 간식 10개, 0명 | 나눌 수 없다는 안내 | 프로그램이 멈춤 | 실패 |
| 간식 개수에 「다섯」 | ? | 프로그램이 멈춤 | ? |
「간식 10개, 0명」은 고칠 곳이 분명하다
사람 수 0을 넣었더니 멈췄습니다. 요구 사항에 「1보다 작은 수가 들어오면 나눌 수 없다고 알린다」고 적어 두었으니 나와야 할 결과가 이미 정해져 있습니다. 나누기 전에 사람 수가 1보다 작은지 먼저 보는 검사가 코드에 빠져 있는 것입니다. 그 검사를 넣으면 됩니다.
「다섯」을 넣은 줄은 고칠 수가 없다
숫자 자리에 글자를 넣었더니 멈췄습니다. 그런데 나와야 할 결과 칸을 채울 수가 없습니다. 요구 사항 어디에도 글자가 들어왔을 때 어떻게 할지가 적혀 있지 않기 때문입니다. 이건 코드가 틀린 것이 아니라 요구 사항이 빠진 것입니다. 그래서 여기서는 코드를 고치지 않고 요구 분석으로 돌아가 되묻습니다. 다시 입력받을까요, 안내만 하고 끝낼까요? 이렇게 테스트가 요구 분석의 구멍을 찾아 주는 일이 자주 있습니다.
다 통과하면 버그가 없는 건가요
아닙니다. 테스트는 문제가 있다는 것은 보여 주지만, 문제가 없다는 것은 보여 주지 못합니다. 넣어 보지 않은 값이 언제나 남아 있기 때문입니다. 그래서 「버그가 없다」가 아니라 「넣어 본 값에서는 문제가 없었다」가 정확한 말입니다. 값을 잘 고르는 일이 중요한 이유가 여기 있습니다.
시험 기록 표에 한 줄을 더 보태 보세요. 아직 넣어 보지 않은 값 가운데 틀릴 만해 보이는 것을 하나 고르고, 나와야 할 결과를 요구 사항에서 찾아 함께 적으면 됩니다.
작은 것부터 넓혀 간다
전체를 한 번에 시험하면 안 되는 이유가 있을까요?
프로그램이 커지면 한 번에 전체를 실행해 보는 것만으로는 원인을 못 찾습니다. 어딘가 틀렸다는 것만 알 뿐 어디인지 알 수가 없기 때문입니다. 그래서 시험하는 범위를 작은 것에서 큰 것으로 넓혀 갑니다.
단위 테스트
부분 하나만 떼어서
가장 작은 조각 하나가 제 몫을 하는지만 봅니다. 간식 나누기라면 몫과 나머지를 구하는 부분만 떼어 여러 숫자를 넣어 봅니다. 범위가 좁아서 틀리면 어디가 틀렸는지 바로 압니다.
통합 테스트
이어 붙여서
조각들을 이어 붙였을 때 사이에서 값이 제대로 건너가는지를 봅니다. 입력받은 값이 계산하는 부분까지 잘 넘어가는지, 계산 결과가 화면에 내보내는 부분까지 잘 닿는지입니다. 조각 하나하나는 멀쩡한데 이어 놓으면 안 되는 경우가 여기서 잡힙니다.
시스템 테스트
전부 갖춰 놓고
완성된 프로그램을 실제로 쓰는 것처럼 실행해 봅니다. 판정 기준은 요구 분석 단계에서 적어 둔 요구 사항입니다. 「아무 값이나 넣어 보면 될까」 절의 시험 기록이 바로 이 단계에서 만든 것입니다.
세 가지를 다 해야 하나요
수업에서 만드는 짧은 프로그램은 조각이 나뉘어 있지 않아 단위와 시스템의 구별이 흐릿합니다. 셋을 꼭 따로 하라는 뜻이 아니라, 범위를 좁혀 확인한 다음에 넓혀 확인하라는 뜻입니다. 코드를 조금 적고 바로 실행해 확인하는 습관도 단위 테스트와 같은 생각에서 나온 것입니다.
「조각 하나하나는 멀쩡한데 이어 놓으면 안 된다」는 일이 왜 생길까요? 간식 나누기에서 입력받는 부분과 계산하는 부분 사이에 무엇이 어긋날 수 있을지 하나만 떠올려 보세요.
핵심 정리
오류 세 가지
드러나는 때가 다르다
- 문법 오류 실행 전 검사에서 — 번역기가 알려 준다
- 실행 오류 돌다가 그 줄에서 멈춘다. 멈추지 않는 언어에서는 논리 오류처럼 숨는다
- 논리 오류 끝까지 도는데 답이 틀렸다. 아무도 안 알려 준다
디버깅
발견이 아니라 좁히기
- 다시 일어나게 만들고 → 어디까지 맞는지 찾는다
- 고치기 전에 왜 그런지 말로 설명해 본다
- 고친 뒤 멀쩡하던 값들도 다시 넣어 본다
넣어 볼 값 고르기
틀릴 만한 자리를 노린다
- 경계값 — 경계와 그 바로 아래 · 바로 위
- 일부러 이상한 값을 넣는다. 나와야 할 결과는 미리 적는다
- 다 통과해도 「넣어 본 값에서는」일 뿐이다
테스트의 크기
좁게 보고 넓혀 간다
- 단위 부분 하나 · 통합 이어 붙여서 · 시스템 전부 갖춰 놓고
- 시스템 테스트의 판정 기준은 요구 사항이다
- 테스트가 앞 단계의 구멍을 찾아 주기도 한다
확인 퀴즈
배운 내용을 확인해 봅시다.
간식 6개를 4명에게 나눴는데 「한 사람당 2개, 2개 남음」이 나왔습니다. 어떤 오류일까요?
「사람 수는 1명 이상」이라는 조건을 경계값 검사로 확인하려 합니다. 넣어 볼 값으로 가장 알맞은 것은?
오류와 테스트에 대한 설명으로 옳지 않은 것은?