코드 줄의 묶음에 이름을 붙이고 필요할 때 실행하기
- 형식
- 기본 50분 / 확장 60분 · + 10 MIN EXTENSION
- 핵심
def, 함수 이름, 괄호, 콜론, 들여쓰기- 연결
- 여러 줄의 절차 → 이름이 있는 작업 단위
오늘의 학습 목표
- 5주차 생성 코드에서 하나의 작업으로 묶을 수 있는 시작과 끝을 찾을 수 있습니다.
- 이미 사용한
print(),len(),randint()를 함수의 이름·입력·동작 관점에서 읽을 수 있습니다. def, 함수 이름, 괄호, 콜론, 들여쓰기의 역할을 구분할 수 있습니다.- 함수 정의는 실행 준비이고 함수 호출은 본문 실행이라는 차이를 설명할 수 있습니다.
- 프로그램을 위에서 아래로 추적하며 함수 본문이 실행되는 시점을 표시할 수 있습니다.
- 행동을 드러내는 함수 이름을 만들고 정의한 함수를 두 번 이상 호출할 수 있습니다.
- 콜론, 들여쓰기, 호출 괄호, 실행 순서에서 발생하는 대표 오류를 찾아 수정할 수 있습니다.
- 0-6 min5주차 코드 회상
생성 포스터 코드에서 한 번의 작업으로 묶을 범위를 찾습니다.
- 6-15 min이미 사용한 함수
함수 이름, 괄호 안 입력, 실행 결과를 익숙한 코드에서 다시 읽습니다.
- 15-27 min함수 정의 문법
def, 이름, 괄호, 콜론, 들여쓴 본문을 한 줄씩 해석합니다. - 27-37 min정의와 호출
위에서 아래로 실행 순서를 추적하고 본문이 실행되는 시점을 확인합니다.
- 37-45 minPillow 함수
고정된 도형 묶음을 함수로 만들고 여러 번 호출해 한계를 찾습니다.
- 45-50 min오류 진단
대표 오류를 수정하고 2교시의 매개변수가 필요한 이유를 정리합니다.
수업 운영: 기본 50분과 확장 60분
기본 50분에서는 함수가 필요한 이유, 익숙한 함수 읽기, 정의 문법, 호출 시점, 고정 도형 함수, 오류 진단까지 진행합니다. 모든 확인 문제는 먼저 개인 답을 적은 뒤 접이식 기준 답안을 확인합니다. 진도가 빠를 때만 50-60분 확장에서 5주차 코드의 작업 묶음에 적절한 함수 이름을 붙이고 실행 순서를 재구성합니다.
함수를 처음 보는 학생을 위한 읽기 순서
def가 있는 줄을 찾습니다. 이 줄은 새로운 함수를 정의하는 시작입니다.- 함수 이름을 행동으로 읽습니다.
draw_fixed_motif는 “고정 모티프를 그린다”입니다. - 콜론 아래의 들여쓴 범위를 표시합니다. 그 범위가 함수 본문입니다.
- 들여쓰기가 끝나는 줄을 찾습니다. 그 줄부터는 함수 밖의 코드입니다.
- 함수 이름 뒤에 괄호가 붙은 호출을 찾습니다. 호출을 만날 때마다 본문으로 이동해 실행합니다.
0-6분 · 5주차 코드는 한 장을 만들지만 아직 다시 쓰기 어렵다
5주차에는 반복문 안에서 크기, 위치, 색상을 선택하고 조건에 따라 세 가지 도형을 그렸습니다. 시드와 설정값을 바꾸면 새로운 결과를 만들 수 있었지만, 이미지를 만드는 전체 과정에는 아직 하나의 이름이 없었습니다.
from PIL import Image, ImageDraw
from IPython.display import display
import random
canvas_width = 600
canvas_height = 600
seed_number = 73
shape_count = 56
min_size = 20
max_size = 80
margin = 40
medium_threshold = 42
large_threshold = 64
background_color = (235, 241, 250)
primary_color = (48, 112, 166)
secondary_color = (92, 150, 118)
accent_color = (230, 83, 72)
outline_color = (35, 42, 55)
palette = [primary_color, secondary_color, accent_color]
image = Image.new(
"RGB",
(canvas_width, canvas_height),
background_color,
)
draw = ImageDraw.Draw(image)
random.seed(seed_number)
for shape_number in range(shape_count):
size = random.randint(min_size, max_size)
x = random.randint(margin, canvas_width - margin - size)
y = random.randint(margin, canvas_height - margin - size)
base_color = random.choice(palette)
if size >= large_threshold:
draw.rectangle(
(x, y, x + size, y + size),
fill=accent_color,
outline=outline_color,
width=3,
)
elif size >= medium_threshold:
draw.ellipse(
(x, y, x + size, y + size),
fill=base_color,
)
else:
draw.ellipse(
(x, y, x + size, y + size),
fill=background_color,
outline=base_color,
width=4,
)
display(image)
이 코드는 잘못된 코드가 아닙니다. 다만 새로운 포스터를 세 장 만들려면 설정값을 바꾸고 같은 셀을 여러 번 실행해야 하며, 다른 노트북에서 사용하려면 관련 코드를 다시 찾아 옮겨야 합니다. 6주차의 함수는 이미 작동하는 코드를 버리는 문법이 아니라, 작동하는 코드의 시작과 끝을 정하고 그 작업에 이름을 붙이는 구조입니다.
준비, 반복, 난수, 조건, 그리기 코드가 순서대로 이어집니다.
한 장의 이미지를 만드는 코드가 어디서 시작하고 끝나는지 정합니다.
create_poster()라는 짧은 이름으로 전체 작업을 실행합니다.
생성 과정의 시작과 끝 표시하기
5주차 노트북의 작업을 ① 캔버스 만들기, ② 난수 선택, ③ 조건 분기, ④ 도형 그리기, ⑤ 이미지 보여주기, ⑥ 파일 저장으로 나눕니다. “포스터 한 장 만들기”라는 함수에 반드시 포함할 항목과 함수 밖에 둘 수 있는 항목을 각각 적습니다.
작업 범위의 기준 예시
캔버스 만들기부터 도형 그리기까지는 한 장의 이미지를 만드는 핵심 과정이므로 함수 안에 묶을 수 있습니다. 이미지 보여주기와 파일 저장은 생성된 이미지를 어떻게 사용할지에 관한 선택이므로 함수 밖에 둘 수 있습니다. 2교시에는 완성된 이미지를 return으로 함수 밖에 전달합니다.
6-15분 · 우리는 이미 여러 함수를 사용했다
함수라는 이름이 새로울 뿐, 함수 호출은 2주차부터 계속 사용했습니다. print("안녕하세요")에서 print는 함수의 이름이고, 괄호 안의 문자열은 함수가 사용할 입력이며, 화면에 글자를 표시하는 것이 함수의 동작입니다.
| 코드 | 이름 | 괄호 안 입력 | 실행 결과 |
|---|---|---|---|
print("완료") | print | 표시할 문자열 | 출력 영역에 글자를 표시 |
len(palette) | len | 길이를 셀 리스트 | 항목 수를 정수로 돌려줌 |
range(5) | range | 반복에 사용할 끝 기준 | 0부터 4까지의 값 흐름을 만듦 |
random.randint(20, 80) | randint | 최솟값과 최댓값 | 범위 안의 정수 하나를 돌려줌 |
random.choice(palette) | choice | 후보가 담긴 리스트 | 리스트 항목 하나를 돌려줌 |
image.save("poster.png") | save | 저장할 파일명 | 이미지 파일을 생성 |
random.randint()와 image.save()처럼 이름 앞에 점이 붙는 구조도 있습니다. 전자는 random 모듈 안의 함수이고 후자는 이미지 객체가 가진 메서드입니다. 이번 시간에는 이 분류를 외우지 않습니다. 이름 뒤의 괄호를 만나면 정해진 작업을 호출한다는 공통 구조만 정확히 읽습니다.
함수의 이름만 가리킵니다. 아직 괄호 안의 입력을 전달하거나 출력 작업을 실행하지 않습니다.
이름 뒤에 괄호를 붙여 함수를 호출합니다. 문자열을 전달하고 출력 작업을 실행합니다.
이름, 입력, 결과를 세 칸으로 나누기
size = random.randint(20, 80)에서 함수 이름, 함수에 전달한 두 입력, 실행 뒤 변수에 저장되는 결과를 각각 적습니다.
난수 함수 판정 답안
함수 이름은 randint, 두 입력은 20과 80, 실행 결과는 20 이상 80 이하의 정수 하나입니다. 그 정수가 대입 연산자 오른쪽에서 만들어진 뒤 size에 저장됩니다.
15-27분 · def는 새로운 작업의 정의를 시작한다
Python에서 자신만의 함수를 만들 때에는 def로 정의를 시작합니다. 다음 함수는 생성 규칙 한 문장을 출력하는 작은 작업입니다.
def announce_rule():
message = "크기는 무작위로 선택하고 큰 값은 사각형으로 표현한다."
print(message)
announce_rule()
함수의 본문에는 한 줄만 들어갈 수도 있고 여러 줄이 들어갈 수도 있습니다. 중요한 것은 줄 수가 아니라 한 이름으로 부를 만한 하나의 책임을 갖는지입니다. 배경 만들기, 도형 하나 그리기, 포스터 한 장 만들기처럼 결과를 행동으로 설명할 수 있으면 함수 이름도 자연스럽게 정할 수 있습니다.
함수 이름은 수행할 행동을 먼저 말한다
| 함수 이름 | 읽는 문장 | 판정 | 이유 |
|---|---|---|---|
draw_circle | 원을 그린다 | 명확함 | 행동과 대상이 드러남 |
create_poster | 포스터를 만든다 | 명확함 | 작업의 최종 결과가 드러남 |
save_preview | 미리보기를 저장한다 | 명확함 | 행동과 저장 대상이 드러남 |
poster | 포스터 | 모호함 | 값인지 작업인지 구분하기 어려움 |
function1 | 함수 하나 | 모호함 | 코드가 무엇을 하는지 설명하지 못함 |
cp | 뜻을 추측해야 함 | 모호함 | 다시 읽을 때 의미를 복원하기 어려움 |
- 영문 소문자로 시작하고 여러 단어는 밑줄로 연결합니다.
- 함수가 하는 일을 나타내는 동사를 앞에 둡니다:
draw,create,save,show. - Python 키워드인
if,for,def,return은 이름으로 사용하지 않습니다. print,list,range처럼 이미 사용하는 이름을 새 함수 이름으로 덮어쓰지 않습니다.
“팔레트 미리보기를 보여주는 작업”에 알맞은 이름은?
show_palette_preview처럼 행동 show와 대상 palette_preview를 함께 적을 수 있습니다. palette만 쓰면 팔레트 값인지 팔레트를 보여주는 작업인지 구분하기 어렵습니다.
들여쓰기는 함수에 포함되는 코드의 경계다
def show_start_message():
print("이미지 생성을 시작합니다.")
print("설정값을 확인했습니다.")
print("함수 정의가 끝났습니다.")
show_start_message()
앞의 두 print()는 네 칸 들여썼으므로 함수 본문입니다. 세 번째 print()는 왼쪽 끝으로 돌아왔으므로 함수 밖의 코드입니다. 빈 줄은 읽기 쉽게 나눈 것이며 함수의 경계를 결정하지 않습니다. 경계는 들여쓰기 깊이로 결정됩니다.
호출문도 네 칸 들여쓰면 어떻게 되는가?
호출문이 함수 본문 안에 포함됩니다. 함수가 실행되는 동안 자기 자신을 다시 호출하는 재귀 구조가 되어 의도하지 않은 반복과 오류가 생길 수 있습니다. 이번 수업에서는 함수 정의가 끝난 뒤 왼쪽 끝으로 돌아와 호출합니다. 재귀는 이번 학습 범위가 아닙니다.
27-37분 · 정의는 준비이고 호출은 실행이다
def 문장을 실행하면 Python은 함수 이름과 본문을 기억합니다. 그러나 본문 안의 출력이나 도형 그리기를 바로 실행하지는 않습니다. 프로그램이 함수 호출을 만날 때에만 본문으로 이동합니다.
print("1. 프로그램 시작")
def show_generation_step():
print("3. 함수 본문 실행")
print("2. 함수 정의 완료")
show_generation_step()
print("4. 프로그램 종료")
1. 프로그램 시작
2. 함수 정의 완료
3. 함수 본문 실행
4. 프로그램 종료
코드에서 함수 본문이 “2. 함수 정의 완료”보다 위에 적혀 있어도 출력 순서는 3번입니다. Python은 정의 줄에서 본문을 기억한 뒤 건너뛰고, 아래의 호출을 만났을 때 본문으로 돌아갑니다. 본문이 끝나면 호출 다음 줄로 복귀합니다.
def 줄과 들여쓴 본문을 함수 이름에 연결합니다.
show_generation_step()을 만나면 들여쓴 문장을 실행합니다.
본문이 끝나면 호출문 다음의 프로그램 종료 출력을 실행합니다.
함수 본문은 몇 번 실행되는가?
아래 코드의 출력 순서를 실행 전에 적습니다. show_color() 본문이 총 몇 번 실행되는지도 표시합니다.
def show_color():
print("coral")
print("start")
show_color()
show_color()
print("end")
실행 순서 문제 답안
출력은 start, coral, coral, end 순서입니다. 함수 정의만으로는 본문이 실행되지 않으며 호출문이 두 개이므로 본문은 두 번 실행됩니다.
Colab에서는 정의 셀을 먼저 실행해야 한다
Colab은 화면의 셀 위치가 아니라 실제 실행한 순서를 기억합니다. 함수 호출 셀을 먼저 실행하면 Python은 아직 그 함수 이름을 알지 못해 NameError를 표시합니다. 위쪽 정의 셀을 먼저 실행한 뒤 아래쪽 호출 셀을 실행해야 합니다.
def create_poster():create_poster()런타임 → 모두 실행함수를 수정했는데 이전 결과가 계속 나올 때
정의 셀의 글자만 수정하고 실행하지 않으면 런타임에는 이전 함수가 남아 있습니다. 수정한 정의 셀을 다시 실행한 뒤 호출 셀을 실행합니다. 최종 확인에서는 세션을 다시 시작하고 모두 실행하여 화면의 코드와 런타임 상태를 일치시킵니다.
37-45분 · 고정된 도형 묶음을 함수로 만들기
이제 Pillow 도형 세 개를 하나의 고정 모티프로 묶습니다. 다음 코드는 그대로 실행할 수 있는 완전한 예제입니다.
from PIL import Image, ImageDraw
from IPython.display import display
image = Image.new("RGB", (600, 400), (245, 240, 228))
draw = ImageDraw.Draw(image)
def draw_fixed_motif():
draw.rectangle(
(80, 90, 260, 270),
fill=(230, 83, 72),
outline=(35, 42, 55),
width=4,
)
draw.ellipse(
(210, 120, 390, 300),
fill=(48, 112, 166),
)
draw.line(
(70, 330, 530, 330),
fill=(35, 42, 55),
width=8,
)
draw_fixed_motif()
display(image)
함수의 이름만 보아도 “고정된 모티프를 그리는 작업”임을 알 수 있습니다. 호출문에서는 사각형, 타원, 선의 좌표와 색을 다시 적지 않습니다. 함수 내부의 구체적인 절차를 하나의 이름 뒤에 숨겼기 때문입니다.
| 구분 | 함수로 묶기 전 | 함수로 묶은 뒤 |
|---|---|---|
| 실행 의도 | 여러 도형 명령을 끝까지 읽어야 파악 | draw_fixed_motif() 이름으로 먼저 파악 |
| 재사용 | 도형 명령 전체를 다시 작성하거나 복사 | 호출문 한 줄을 추가 |
| 수정 위치 | 복사한 코드마다 값을 찾아 수정 | 함수 본문 한 곳에서 공통 절차 수정 |
| 현재 한계 | 모든 숫자가 고정됨 | 호출해도 같은 위치와 색을 사용함 |
draw_fixed_motif()를 두 번 호출해도 두 번째 모티프는 첫 번째와 정확히 같은 위치에 그려집니다. 두 모티프가 포개지므로 화면에서는 하나처럼 보입니다. 함수는 재사용을 가능하게 했지만, 아직 호출할 때 위치·크기·색을 바꾸는 방법은 없습니다. 이 한계가 2교시에서 매개변수가 필요한 이유입니다.
draw_fixed_motif() 호출 한 줄을 찾아 표시하고, 호출을 두 번 해도 결과가 하나처럼 보이는 이유를 “함수 안의 좌표가 고정되어 있기 때문”이라고 설명할 수 있으면 완료입니다.
함수를 쓰면 항상 코드가 짧아지는가?
항상 그렇지는 않습니다. 짧은 코드를 함수로 묶으면 전체 줄 수가 잠시 늘어날 수도 있습니다. 함수의 핵심 가치는 줄 수를 줄이는 데만 있지 않습니다. 작업에 이름을 붙이고, 수정 위치를 모으고, 같은 절차를 일관되게 다시 실행할 수 있게 하는 데 있습니다.
45-50분 · 대표 오류를 마지막 줄부터 읽기
| 증상 | 확인할 부분 | 수정 기준 |
|---|---|---|
SyntaxError | def draw_motif() 뒤의 콜론 | def draw_motif():로 작성 |
IndentationError | 함수 본문의 앞쪽 공백 | 본문의 모든 줄을 같은 깊이로 네 칸 들여쓰기 |
NameError | 정의 셀보다 호출 셀을 먼저 실행했는지 | 정의 셀을 실행한 뒤 호출 셀 실행 |
| 아무 출력도 없음 | 함수 이름만 적고 괄호를 빠뜨렸는지 | draw_motif()처럼 괄호를 붙여 호출 |
| 수정 전 결과가 나옴 | 수정한 정의 셀을 다시 실행했는지 | 정의 셀 재실행 후 호출 |
RecursionError | 호출문이 함수 본문 안에 들여쓰기 되었는지 | 호출문을 왼쪽 끝으로 이동 |
오류가 보이면 함수 전체를 지우지 않는다
오류 메시지의 마지막 줄에서 오류 종류를 먼저 확인합니다. SyntaxError이면 콜론과 괄호, IndentationError이면 본문의 공백, NameError이면 정의 셀의 실행 여부를 확인합니다. 한 번에 한 원인만 고친 뒤 정의 셀과 호출 셀을 다시 실행합니다.
함수의 정의와 호출을 세 문장으로 설명하기
def가 하는 일을 한 문장으로 적습니다.- 정의만 실행했을 때 함수 본문이 실행되지 않는 이유를 적습니다.
- 같은 함수를 세 번 호출하면 본문이 몇 번 실행되는지 적습니다.
종료 확인 기준 답안
def는 함수 이름과 들여쓴 본문을 하나의 작업으로 정의합니다.- 정의는 실행 준비이며 본문은 호출을 만났을 때 실행되기 때문입니다.
- 호출이 세 번이면 함수 본문도 세 번 실행됩니다.
수업 후 개별 복습
- 당일 3분: 노트를 보지 않고
def, 함수 이름, 괄호, 콜론, 들여쓴 본문, 호출 순서를 적습니다. - 다음 날 5분: 출력 함수 하나를 정의하고 두 번 호출한 뒤 실행 전 예상 출력과 비교합니다.
- 2교시 직전 2분:
draw_fixed_motif()의 위치와 색을 호출할 때 바꾸려면 어떤 입력 자리가 필요한지 세 가지 적습니다.
1교시 핵심 문장
함수 정의는 코드 묶음에 이름을 붙이는 준비이고, 함수 호출은 그 이름으로 묶인 코드를 실제로 실행하는 명령입니다.
50-60분 · 5주차 코드의 작업 이름 설계하기
확장 시간에는 새로운 문법을 추가하지 않습니다. 5주차 포스터 제작 과정을 세 작업으로 나누고, 각 작업에 이름을 붙인 뒤 호출 순서만 작성합니다.
| 시간 | 활동 | 남겨야 할 결과 |
|---|---|---|
| 50-53분 | 캔버스 준비, 포스터 생성, 결과 저장의 시작과 끝 표시 | 세 작업 범위 |
| 53-56분 | 각 작업을 행동과 대상으로 이루어진 이름으로 바꾸기 | prepare_canvas, create_poster, save_poster |
| 56-59분 | 정의가 끝난 뒤 필요한 호출 순서 작성 | 호출문 세 줄 |
| 59-60분 | 함수 안에 둘 코드와 밖에 둘 코드를 한 문장으로 설명 | 작업 경계 설명 |
def prepare_canvas():
print("1. 캔버스를 준비합니다.")
def create_poster():
print("2. 도형을 생성합니다.")
def save_poster():
print("3. 포스터를 저장합니다.")
prepare_canvas()
create_poster()
save_poster()
위 세 줄은 전체 구현이 아니라 작업 순서를 읽기 위한 호출 계획입니다. 다음 시간에는 호출 괄호 안에 시드, 도형 수, 위치, 크기, 색상처럼 바꾸고 싶은 값을 전달해 같은 함수에서 서로 다른 결과를 만듭니다.
참고 자료
- Python 공식 튜토리얼: Defining Functions
def, 함수 이름, 매개변수 괄호, 들여쓴 본문, 호출과return의 공식 설명 - Python 언어 참조: Function definitions함수 정의문의 형식과 함수 본문 실행에 관한 공식 언어 참조
- PEP 8: Function and Variable Names소문자와 밑줄을 사용하는 Python 함수 이름 표기 지침
- Pillow 공식 문서: ImageDraw함수 본문에서 묶어 사용하는 사각형, 타원, 선 그리기 메서드의 공식 설명