조건·반복·함수·스코프
재고가 있으면 주문하고 없으면 알림을 보내며, 여러 상품을 차례로 검사하고, 반복되는 계산을 이름 붙인 처리로 묶는다. 이때 프로그램은 조건에 따라 갈라지고, 종료조건까지 되돌아가며, 함수를 호출했다가 원래 위치로 돌아온다. 어느 이름이 어디에서 보이는지도 정하지 않으면 서로 다른 계산이 값을 덮어쓴다.
코드는 위에서 아래로만 한 번 실행되지 않는다
주문 프로그램은 재고가 있으면 주문하고 없으면 알림을 보낸다. 여러 상품을 차례로 검사하고, 반복되는 계산은 이름 붙인 처리로 묶는다. 그래서 프로그램은 조건에 따라 갈라지고, 종료조건에 이를 때까지 되돌아가며, 함수를 호출했다가 원래 위치로 돌아온다. 이때 어느 이름이 어디에서 보이는지도 정하지 않으면 서로 다른 계산이 같은 값을 덮어쓴다.
이 세 가지를 맡는 개념이 따로 있다. 제어흐름은 실행 경로를 만들고, 함수는 경로의 일부를 재사용 가능한 단위로 묶으며, 스코프는 그 안에서 이름이 보이는 범위를 정한다.
먼저 되짚기: 조건은 값을 읽고 반복은 값을 바꾼다
변수는 값을 가리키거나 보관하도록 이름 붙인 요소였다. 조건은 변수의 현재 값을 비교해 다음 경로를 고르고, 반복은 변수나 입력 상태를 바꾸며 종료조건에 가까워진다. 이름과 값의 구분이 제어흐름을 추적하는 출발점이다.
값의 현재 상태가 다음 실행경로를 고른다
프로그램은 변수의 현재 상태를 조건으로 읽어 분기나 반복의 다음 실행 위치를 고른다. 함수 호출은 다른 처리단위로 이동했다가 반환 지점으로 돌아오며, 이름의 스코프는 호출 안팎에서 어떤 이름을 볼 수 있는지 제한한다. 이 관계는 배송비 예제의 실제 가상 코드와 회차별 풀이로 추적한다.
제어흐름
제어흐름은 조건·반복·호출에 따라 다음 실행 위치가 정해지는 흐름이다. 순차는 다음 문장으로 가고, 분기는 조건 결과에 맞는 경로로 간다. 반복은 종료조건을 만족할 때까지 이전 지점으로 돌아가고, 함수 호출은 다른 처리단위로 이동했다가 반환 지점으로 돌아간다.
함수 전에 제어흐름을 다시 잇는다
제어흐름은 조건·반복·호출에 따라 다음 실행 위치가 정해지는 흐름이다. 함수 호출은 이 흐름을 별도 처리단위로 이동시켰다가 반환 지점으로 되돌린다.
호출도 실행경로의 한 간선이다
함수를 호출하면 인자를 전달하고 함수 본문으로 이동한다. 본문은 결과나 효과를 남기고 호출 위치로 돌아온다. 그래서 호출 전후의 상태와 반환되는 시점을 함께 추적해야 한다.
함수
함수(function)는 입력 매개변수와 상태를 받아 결과나 효과를 만드는 재사용 가능한 처리단위다. 매개변수는 호출자가 전달할 입력의 자리를 정하고, 반환값은 호출자에게 돌려줄 결과를 정한다. 파일 저장처럼 함수 밖의 상태를 바꾸는 효과가 있을 수도 있으므로, 그런 효과는 입력·반환과 함께 함수 설명에 드러나야 한다.
스코프 전에 함수의 이름 경계를 다시 잇는다
함수는 입력과 결과·효과를 가진 호출 단위다. 함수 안에서 쓰는 매개변수와 지역 이름이 바깥 이름과 어떻게 구분되는지 스코프가 정한다.
같은 이름이 같은 저장값을 뜻하지 않을 수 있다
바깥 total과 함수 안의 지역 total은 철자가 같아도 서로 다른 이름 연결일 수 있다. 그래서 이름이 보이는 범위인 스코프와 객체가 존재하는 기간인 수명을 구분해야 한다.
스코프
스코프(scope)는 프로그램의 이름을 참조할 수 있는 코드 영역이다. 함수 안에서 선언한 지역 이름은 보통 그 함수 본문이나 더 작은 블록에서만 참조할 수 있다. 바깥 이름과 같은 이름을 안에서 다시 선언하면 가까운 이름이 바깥 이름을 가릴 수 있다. 값이나 객체가 실제로 존재하는 기간인 수명(lifetime)은 스코프와 별개다. 예를 들어 함수 안의 지역 변수 이름은 반환 뒤 보이지 않지만, 그 변수가 가리키던 객체가 다른 곳에서도 참조되면 객체의 수명은 계속될 수 있다.
여러 주문의 배송비를 계산하는 흐름
함수 배송비(금액):
만약 금액 >= 50000 이면 0을 반환
아니면 3000을 반환
총배송비 = 0
각 주문을 반복:
지역 변수 fee = 배송비(주문.금액)
총배송비 = 총배송비 + fee
반복 종료 후 총배송비 출력각 주문을 처리할 때 조건이 무료배송과 유료배송 경로를 나눈다. 함수 호출은 주문 금액을 매개변수로 전달하고 계산 결과를 반환한다. fee는 한 번의 반복에서 현재 주문의 배송비를 가리키며 해당 회차의 좁은 스코프 안에서만 쓰인다. 반면 총배송비는 반복 전체에 걸쳐 누적된다. 반복이 끝나려면 처리할 주문이 하나씩 줄어드는 진행과 종료조건이 있어야 한다.
이 가상 코드에서 >=는 “크거나 같다”는 뜻이고, 주문.금액의 점은 현재 주문 기록의 금액 항목을 찾으라는 뜻이다. fee = 배송비(주문.금액)는 먼저 현재 금액을 함수에 넘기고, 함수가 반환한 숫자를 fee에 연결한다. 총배송비 = 총배송비 + fee는 이전 총배송비를 읽어 fee를 더한 새 값을 다시 연결한다. 즉 수학적 등식을 선언하는 것이 아니다. 아래 예에서 첫 주문은 30000>=50000이 거짓이어서 함수가 3000을 돌려주고, 두 번째는 참이어서 0을 돌려준다.
CF-1: 세 주문을 실제 실행순서로 추적한다
주문 금액이 [30000, 70000, 120000]이고 배송비(금액)가 50000원 미만 3000원, 그 이상 무료라고 하자.
| 반복회차 | 금액 | 조건 | 배송비 반환 | 누적 배송비 |
|---|---|---|---|---|
| 1 | 30000 | 미만 | 3000 | 3000 |
| 2 | 70000 | 이상 | 0 | 3000 |
| 3 | 120000 | 이상 | 0 | 3000 |
반복 종료조건이 잘못되어 네 번째 인덱스까지 읽으면 실제 주문이 없는 위치에 접근한다. 또 함수 안에서 총배송비라는 지역 이름을 새로 선언했다면, 바깥의 누적 총배송비와 이름이 같더라도 바깥 값이 함께 갱신되지는 않는다.
인덱스는 목록에서 항목을 찾는 위치 번호다. 이 예에서 0부터 번호를 붙이면 세 주문의 번호는 0·1·2이고, 다음 위치 3에는 주문이 없다. 현재 번호 i를 0으로 시작하고 i<3일 때 처리한 뒤 i를 1씩 늘리면 검사값은 0(처리), 1(처리), 2(처리), 3(종료)이 된다. 그런데 i<=3으로 바꾸면 없는 네 번째 항목까지 처리한다. 이처럼 끝 지점에서 한 번 더 또는 덜 처리하는 오류를 off-by-one이라고 부른다. 0개 목록에서는 시작 번호 0이 길이 0보다 작지 않아 본문을 한 번도 실행하지 않고 누적 초깃값 0을 출력한다.
return은 현재 함수의 처리를 마치고 호출한 곳으로 돌아간다는 표시다. 배송비 함수 안의 return은 현재 주문의 배송비만 돌려주므로 다음 주문의 반복은 계속된다. 반대로 전체 주문을 처리하는 함수의 반복 안에서 return을 실행하면 그 함수 자체가 끝나 남은 주문은 처리되지 않는다. 예를 들어 교육용 검증 정책을 “음수 금액은 오류를 기록한 뒤 그 주문만 건너뛴다”로 정하면, [30000,-1,70000]은 첫 주문 3000, 둘째 오류, 셋째 0으로 처리되어 최종 3000이다. 여기서 건너뛰기는 다음 회차로 계속한다는 뜻이며 전체 중단과는 다르다. 함수 안에서 총배송비라는 별도 지역 변수를 0으로 만들더라도 바깥 누적값 3000은 바뀌지 않는다.
구조를 고르는 질문
| 구조 | 쓸 조건 | 확인할 것 |
|---|---|---|
| 조건 분기 | 서로 다른 규칙 중 하나를 골라야 함 | 모든 경우와 경계값을 다뤘는가 |
| 반복 | 같은 규칙을 여러 입력에 적용 | 진행값·종료조건·빈 입력 |
| 함수 | 이름 붙일 만한 하나의 일과 입출력 약속이 있음 | 입력·결과·외부 효과·실패 |
| 좁은 스코프 | 값이 필요한 영역이 제한됨 | 다른 코드가 값을 바꿀 필요가 있는가 |
함수는 짧기만 하면 좋은 것이 아니다. 한마디로 설명할 수 있는 하나의 일을 맡고, 입력과 결과가 분명해야 한다. 스코프를 가능한 한 좁게 두면 이름 충돌과 뜻밖의 변경이 줄어든다. 그런데 여러 곳에서 공유해야 하는 상태까지 매번 복사하면 일관성 문제가 생길 수 있다. 그래서 공유가 필요하면 그 상태를 누가 소유하고 어디에서 바꾸는지 명시한다.
실패조건과 방어
분기를 빠뜨리면 경계값이나 예상하지 못한 상태가 처리되지 않는다. 조건표로 가능한 조합을 모두 적고, 어느 조건에도 맞지 않을 때의 기본 실패 경로를 확인한다.
끝나지 않는 반복은 진행값이 바뀌지 않거나 종료조건에 도달할 수 없을 때 생긴다. 반복마다 무엇이 줄거나 늘어 종료에 가까워지는지 적어 둔다.
숨은 함수 효과는 조회처럼 보이는 함수가 전역 상태나 파일을 바꾸는 경우다. 입력과 반환값뿐 아니라 외부 효과도 함수 설명에 드러낸다.
스코프가 넓으면 먼 코드가 같은 변수를 바꿔 원인을 찾기 어려워진다. 이름은 필요한 최소 범위에 선언하고, 변경하는 곳은 한곳으로 모은다.
이름 가리기는 안팎에서 같은 이름이 서로 다른 값을 가리키는 경우다. 의미가 다른 값에는 다른 이름을 쓰고, 도구가 내는 경고를 활용한다.
자체 10점 문제
조건·반복·함수·스코프의 관계를 설명하시오.
요구어는 설명이다. 실행순서와 호출·반환 지점, 이름이 보이는 범위를 실제 추적과 연결한다.
10점 풀이 방향과 모범답안 보기
10점 답안 목차
- 순차·분기·반복
- 함수 호출·반환
- 매개변수와 효과
- 스코프와 이름 가리기
- 종료조건·경계 오류
10점 답안 도식
반복 회차에서 조건을 판단하고 함수에 인자를 전달한다. 함수가 지역 스코프 안에서 계산한 결과를 반환하면 호출자는 원래 위치에서 다음 처리와 누적을 계속한다. CF-1의 회차별 상태표를 대조하며 함수 반환과 전체 작업 종료를 구별한다.
모범답안 축약본: 실행 경로와 이름 경계를 함께 통제한다
제어흐름은 순차·분기·반복·호출을 통해 다음에 실행할 문장을 정하는 규칙이다. 함수는 입력 매개변수와 상태를 받아 결과 또는 외부 효과를 만드는 이름 붙은 처리단위다. 스코프는 변수·함수 같은 이름을 참조할 수 있는 코드 영역이며, 객체가 실제로 존재하는 수명과는 구분한다. 분기는 가능한 입력 상태를 빠짐없이 처리해야 하고, 반복에는 종료조건과 매회 진전하는 값이 있어야 한다. 함수는 하나의 역할과 입력·반환·외부 효과·실패 규칙을 가져야 하며, 조회처럼 보이는 함수가 전역 상태를 몰래 바꾸지 않게 한다. 이름은 필요한 최소 스코프에 선언하고 같은 이름의 가리기를 피한다. 공유 상태가 필요하면 소유자와 변경경로를 명시해 먼 코드의 뜻밖의 변경을 막는다.
자체 25점 문제
다수 주문을 검증·계산하는 처리흐름을 설계하고 off-by-one·조기 반환·이름 가림 오류를 통제하시오.
요구어는 설계, 통제다. 정상/오류 경로와 함수·스코프를 함께 제시한다.
25점 풀이 방향과 모범답안 보기
25점 답안 목차
- 입력목록·종료조건
- 주문별 검증
- 계산 함수 계약
- 오류 주문 처리
- 누적 상태
- 지역/외부 이름 경계
- 조기 return 위치
- 경계값 추적
25점 답안 도식
오류 주문만 건너뛰고 다음 회차로 진행한다 · 상태 따라가기
원문의 [30000, -1, 70000] 입력에서 합계 0과 첫 위치 i=0으로 시작한다. 모든 단계에서 반복 위치·주문액·배송비·합계의 위치를 유지하며 값과 허용 조건을 함께 확인합니다.
상자 안의 대상과 화살표의 조건을 함께 읽으세요. 좁은 화면에서는 같은 관계를 세로로 재배치한 도식을 보여 줍니다.
총배송비와 위치 번호를 초기화하고 실제 목록 길이보다 작은 동안만 반복한다. 유효 주문은 배송비 함수의 반환값을 해당 회차의 지역 변수 fee에 연결해 바깥 총배송비에 누적한다. 음수 등 오류 주문은 기록하고 그 주문만 건너뛴다. 위치를 증가시킨 후 조건으로 돌아가며 목록 종료 뒤 결과를 낸다. 배송비 함수의 반환과 오류 회차의 건너뛰기는 전체 작업의 return과 다르다.
이 예의 조건
- 총배송비와 위치 번호를 초기화하고 실제 목록 길이보다 작은 동안만 반복한다. 유효 주문은 배송비 함수의 반환값을 해당 회차의 지역 변수 fee에 연결해 바깥 총배송비에 누적한다. 음수 등 오류 주문은 기록하고 그 주문만 건너뛴다. 위치를 증가시킨 후 조건으로 돌아가며 목록 종료 뒤 결과를 낸다. 배송비 함수의 반환과 오류 회차의 건너뛰기는 전체 작업의 return과 다르다.
- 이 원문의 교육용 사례·조건 안에서 상태를 대조합니다. 실제 구축·실험 결과나 원문에 없는 성능 보장을 추가하지 않습니다.
관계도의 보충 설명
| 대상 | 원 설명 |
|---|---|
| 초기 상태 | i=0 · 총배송비=0 |
| i < 목록 길이? | 원그림의 대상 |
| 종료 후 결과 | 바깥 누적값 출력 |
| 유효 주문? | 음수는 오류 |
| 오류 기록 | 그 주문만 건너뜀 |
| 배송비 함수 | 반환값 0 또는 3000 |
| 바깥 총배송비 | 반환값 누적 |
| 다음 위치 | i를 1 증가 |
| 연결 | 원 라벨·방향 |
|---|---|
| 초기 상태 → i < 목록 길이? | 초기화 / 정방향 / 실선 |
| i < 목록 길이? → 종료 후 결과 | 끝 / 정방향 / 실선 |
| i < 목록 길이? → 유효 주문? | 남음 / 정방향 / 실선 |
| 유효 주문? → 오류 기록 | 무효 / 정방향 / 실선 |
| 유효 주문? → 배송비 함수 | 유효 / 정방향 / 실선 |
| 배송비 함수 → 바깥 총배송비 | 반환 / 정방향 / 실선 |
| 바깥 총배송비 → 다음 위치 | 누적 / 정방향 / 실선 |
| 오류 기록 → 다음 위치 | 건너뛰기 / 정방향 / 실선 |
| 다음 위치 → i < 목록 길이? | 다음 회차 / 정방향 / 점선 |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 초기 i=0·합계 0 원문의 [30000, -1, 70000] 입력에서 합계 0과 첫 위치 i=0으로 시작한다. |
|
|
| 첫 주문 3000 누적 30000원은 유효하고 50000원 미만이므로 배송비 3000원을 합산한다. |
|
|
| 오류 주문만 건너뜀 -1원은 오류로 기록하고 합산 없이 다음 반복으로 넘어간다. |
|
|
| 마지막 주문 0 누적 70000원은 유효하고 50000원 이상이어서 배송비 0원이다. |
|
|
| 목록 끝에서 반환 i=3이면 i<3이 거짓이므로 반복을 끝내 합계 3000원을 반환한다. |
|
|
오류 주문만 건너뛰고 다음 회차로 진행한다 · 상태 따라가기
원문의 [30000, -1, 70000] 입력에서 합계 0과 첫 위치 i=0으로 시작한다. 모든 단계에서 반복 위치·주문액·배송비·합계의 위치를 유지하며 값과 허용 조건을 함께 확인합니다.
상자 안의 대상과 화살표의 조건을 함께 읽으세요. 좁은 화면에서는 같은 관계를 세로로 재배치한 도식을 보여 줍니다.
총배송비와 위치 번호를 초기화하고 실제 목록 길이보다 작은 동안만 반복한다. 유효 주문은 배송비 함수의 반환값을 해당 회차의 지역 변수 fee에 연결해 바깥 총배송비에 누적한다. 음수 등 오류 주문은 기록하고 그 주문만 건너뛴다. 위치를 증가시킨 후 조건으로 돌아가며 목록 종료 뒤 결과를 낸다. 배송비 함수의 반환과 오류 회차의 건너뛰기는 전체 작업의 return과 다르다.
이 예의 조건
- 총배송비와 위치 번호를 초기화하고 실제 목록 길이보다 작은 동안만 반복한다. 유효 주문은 배송비 함수의 반환값을 해당 회차의 지역 변수 fee에 연결해 바깥 총배송비에 누적한다. 음수 등 오류 주문은 기록하고 그 주문만 건너뛴다. 위치를 증가시킨 후 조건으로 돌아가며 목록 종료 뒤 결과를 낸다. 배송비 함수의 반환과 오류 회차의 건너뛰기는 전체 작업의 return과 다르다.
- 이 원문의 교육용 사례·조건 안에서 상태를 대조합니다. 실제 구축·실험 결과나 원문에 없는 성능 보장을 추가하지 않습니다.
관계도의 보충 설명
| 대상 | 원 설명 |
|---|---|
| 초기 상태 | i=0 · 총배송비=0 |
| i < 목록 길이? | 원그림의 대상 |
| 종료 후 결과 | 바깥 누적값 출력 |
| 유효 주문? | 음수는 오류 |
| 오류 기록 | 그 주문만 건너뜀 |
| 배송비 함수 | 반환값 0 또는 3000 |
| 바깥 총배송비 | 반환값 누적 |
| 다음 위치 | i를 1 증가 |
| 연결 | 원 라벨·방향 |
|---|---|
| 초기 상태 → i < 목록 길이? | 초기화 / 정방향 / 실선 |
| i < 목록 길이? → 종료 후 결과 | 끝 / 정방향 / 실선 |
| i < 목록 길이? → 유효 주문? | 남음 / 정방향 / 실선 |
| 유효 주문? → 오류 기록 | 무효 / 정방향 / 실선 |
| 유효 주문? → 배송비 함수 | 유효 / 정방향 / 실선 |
| 배송비 함수 → 바깥 총배송비 | 반환 / 정방향 / 실선 |
| 바깥 총배송비 → 다음 위치 | 누적 / 정방향 / 실선 |
| 오류 기록 → 다음 위치 | 건너뛰기 / 정방향 / 실선 |
| 다음 위치 → i < 목록 길이? | 다음 회차 / 정방향 / 점선 |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 초기 i=0·합계 0 원문의 [30000, -1, 70000] 입력에서 합계 0과 첫 위치 i=0으로 시작한다. |
|
|
| 첫 주문 3000 누적 30000원은 유효하고 50000원 미만이므로 배송비 3000원을 합산한다. |
|
|
| 오류 주문만 건너뜀 -1원은 오류로 기록하고 합산 없이 다음 반복으로 넘어간다. |
|
|
| 마지막 주문 0 누적 70000원은 유효하고 50000원 이상이어서 배송비 0원이다. |
|
|
| 목록 끝에서 반환 i=3이면 i<3이 거짓이므로 반복을 끝내 합계 3000원을 반환한다. |
|
|
25점 모범답안
입력 목록의 실제 길이를 기준으로 반복 범위를 정하고, 각 주문을 먼저 검증한다. 유효 주문은 계산 함수에 전달해 결과를 바깥 누적 상태에 더하고, 오류 주문은 중단할지 건너뛸지 정책을 명시한다.
계산 함수는 입력·반환·외부 효과를 명시하고, 지역 이름이 바깥 누적 이름을 가리지 않게 한다. CF-1의 세 회차 표로 3건만 실행되는지, 50000원 기준에서 조건이 기대대로 갈리는지 확인한다.
조기 return은 전체 처리를 끝내는지 현재 주문만 끝내는지 구분한다. 빈 목록, 1건, 마지막 인덱스, 기준 금액 근처를 추적한다. 제어흐름과 스코프 오류를 단순한 문법 문제로 축소하지 않는다.
구체적으로는 위치번호 0부터 목록 길이보다 작은 동안만 처리하고 매회 번호를 하나씩 늘리는 모형을 쓴다. 배송비 함수는 50000원 이상이면 0원, 미만이면 3000원을 반환한다. 음수 금액은 오류로 기록하고 해당 주문만 건너뛴다. 그러면 [30000,-1,70000]의 결과는 3000원·오류·0원으로 누적 3000원이고, 빈 목록은 0원이다. 배송비 함수의 반환은 전체 반복을 끝내지 않으며, 누적 변수는 처리 함수 바깥쪽 범위에 하나만 둔다. 49999원·50000원·50001원이 각각 3000원·0원·0원인지 확인하고, i<=길이나 오류 회차에서 전체를 return하는 방식은 정상 결과와 비교해 기각한다.
출처와 적용 경계
본문은 안정된 제어흐름·함수·스코프 개념과 자체 배송비 예를 사용한다. 조건·반복의 실행과 호출한 함수에서의 반환은 Python Language Reference §8.1–8.3과 §7.6 return에서 구체 언어 사례로 확인할 수 있다. 배송비 가상 코드가 Python의 실제 문법이라는 뜻은 아니다. 세부 스코프·클로저·반복 문법은 언어별 사양을 확인한다.
30초 정리
- 제어흐름은 순차·분기·반복·호출로 다음 실행 위치를 정한다.
- 함수는 입력·결과·효과를 가진 재사용 처리단위이고, 스코프는 이름을 참조할 수 있는 영역을 제한한다. 객체가 존재하는 수명과는 구분한다.
- 분기 완전성, 반복 종료, 함수의 숨은 효과, 과도하게 넓은 스코프를 점검한다.
자기교정과 조건 변경
| 잘못 쓴 답 | 수정 | 복습 |
|---|---|---|
| 반복은 항상 N+1번 돈다 | 종료조건과 인덱스 범위를 실제로 추적한다 | CF-1 |
| 함수 호출은 제어흐름과 무관하다 | 호출·반환도 실행경로다 | 호출 |
| 같은 이름 total은 항상 같은 값이다 | 스코프에 따라 다른 이름 연결일 수 있다 | 스코프 |
| return은 현재 반복만 건너뛴다 | 함수 반환인지 loop 제어인지 구분한다 | CF-1 |
| 객체 수명과 이름 스코프는 같다 | 이름 가시성과 객체 존속을 구분한다 | 정의 |
조건을 바꿔 보자. 주문이 0건이면 반복 본문이 한 번도 실행되지 않고 초기 누적값이 그대로 결과가 되는지 확인한다.
무료 기준이 50000원 초과로 바뀌면 비교연산자를 >=에서 >로 바꾸고, 50000원 테스트의 기대값도 0원에서 3000원으로 바꾼다.
오류 주문 하나에서 전체를 중단하기로 한다면 continue형 정책이 아니라 함수·작업을 끝내는 경로로 답안과 테스트를 바꾼다.