
이미지 출처: Pexels · Pexels License
카페 주문서를 출력하는 코드를 넘겨받았습니다. 손님 한 명에 6줄, 3명이면 18줄. 그 6줄은 이렇게 생겼습니다.
price = 4500
count = 2
total = price * count
vat = int(total * 0.1)
print("아메리카노", count, "잔")
print("합계", total + vat, "원")
아메리카노 2 잔
합계 9900 원
같은 여섯 줄이 손님 수만큼 아래로 복붙돼 있습니다. 그리고 의뢰가 하나 붙어 왔습니다. 부가세율이 바뀌면 한 곳만 고치게 해 달라. 지금은 0.1이 세 곳에 흩어져 있어서, 하나라도 빠뜨리면 손님마다 다른 세율이 붙습니다.
의뢰서 한 문장에 조건이 몇 개일까요
의뢰서의 문장은 하나. 그런데 안에는 조건이 셋입니다.
- 같은 계산·출력을 한 곳에만 적어 둔다
- 손님마다 다른 값(이름·가격·잔 수)은 밖에서 넣는다
- 세율은 평소 값을 정해 두되 필요하면 바꿀 수 있게 한다
셋을 한꺼번에 만족시키는 도구가 파이썬에 있습니다. 이름은 함수.
함수가 뭔가요 — 이름표를 붙인 코드 묶음
파이썬 공식 자습서(2026년 9월 10일 기준 3.14판) 4.8절은 이렇게 적습니다. 키워드 def는 함수 정의를 시작합니다. 함수 이름과 괄호로 싸인 형식 매개변수 목록이 뒤따르고, 함수의 바디를 이루는 문장들은 다음 줄에서 들여쓰기로 시작합니다.
쓰는 자리는 정해져 있습니다. 같은 일을 두 번 이상 할 때, 그리고 그 일에 이름을 붙이면 읽기 쉬워질 때. 쿠키 커터를 떠올리면 가깝습니다. 틀은 하나인데 반죽을 갈아 넣으면 같은 모양이 몇 개든 나옵니다. 틀을 고치면 그다음부터 찍히는 모양이 전부 바뀝니다.
성질은 넷입니다.
| 성질 | 내용 |
|---|---|
| 정의와 실행이 따로 | def 줄은 만들어 두기만 합니다. 이름 뒤에 괄호를 붙여 불러야 실행됩니다 |
| 값을 받는다 | 괄호 안 이름으로 밖에서 값을 받습니다 |
| 값을 돌려준다 | return으로 결과를 호출한 자리로 보냅니다 |
| 안과 밖이 나뉜다 | 함수 안에서 만든 이름은 밖에서 보이지 않습니다 |
이 편에서 쓸 말도 여기서 정하고 갑니다.
| 용어 | 뜻 |
|---|---|
| 정의 | def로 함수를 만들어 두는 일 |
| 호출 | 이름 뒤에 괄호를 붙여 실제로 실행하는 일 |
| 매개변수 | 정의할 때 괄호 안에 적는 이름. 받을 자리 |
| 인자 | 호출할 때 괄호 안에 넣는 값. 그 자리에 들어가는 것 |
| 반환값 | return이 호출한 자리로 보내는 값 |
def 한 줄에 부품이 넷
가장 짧은 함수부터 만들어 보겠습니다. 이름과 잔 수를 받아 한 줄 찍는 함수입니다.
def order(name, count):
print(name, count, "잔")
order("아메리카노", 2)
order("라떼", 1)📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.
아메리카노 2 잔
라떼 1 잔
첫 줄에 부품이 넷 들어 있습니다. def, 이름, 괄호 안 매개변수, 그리고 콜론. 콜론 다음 줄부터 들여쓴 부분이 함수의 몸입니다. 들여쓰기가 끝나는 곳이 함수가 끝나는 곳입니다.

def 줄에 부품이 넷입니다. 들여쓰기가 함수의 경계라는 점은 6편의 if와 같습니다.
order("아메리카노", 2)에서 괄호 안에 넣은 "아메리카노"와 2가 인자입니다. 이 값들이 정의 쪽 name과 count 자리에 순서대로 들어갑니다. 순서가 곧 짝입니다. 자리를 바꿔 order(2, "아메리카노")라고 쓰면 에러 없이 2 아메리카노 잔이 찍힙니다. 파이썬은 순서만 맞춰 넣을 뿐, 그게 말이 되는지는 보지 않습니다.
열여덟 줄을 아홉 줄로
이제 의뢰를 처리합니다. 복붙된 여섯 줄을 통째로 함수 안으로 옮기고, 손님마다 달랐던 값만 매개변수로 빼냅니다.
def receipt(name, price, count, rate=0.1):
total = price * count
vat = int(total * rate)
print(name, count, "잔")
print("합계", total + vat, "원")
receipt("아메리카노", 4500, 2)
receipt("라떼", 5500, 1)
receipt("녹차", 3800, 3)📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.
아메리카노 2 잔
합계 9900 원
라떼 1 잔
합계 6050 원
녹차 3 잔
합계 12540 원
rate=0.1이 의뢰의 핵심입니다. 매개변수에 등호로 값을 붙여 두면 기본값이 됩니다. 호출할 때 안 넣으면 이 값이 쓰이고, 넣으면 넣은 값이 이깁니다. 세율이 12%로 바뀌면 고칠 곳은 def 줄 하나입니다. 손님이 백 명이어도 마찬가지입니다.
특정 손님만 다른 세율을 적용해야 하면 그 자리만 값을 넘깁니다.
receipt("케이크", 6000, 1, 0.05)
케이크 1 잔
합계 6300 원

같은 결과를 내는 두 코드입니다. 고칠 자리가 셋에서 하나로 줄었다는 점이 줄 수보다 중요합니다.
계산한 값은 어디로 가나요
receipt는 화면에 찍기만 할 뿐 결과를 남기지 않습니다. 세 손님의 합계를 더해 하루 매출을 내려면 값이 손에 남아야 합니다. 그 일을 return이 합니다.
def total_price(price, count, rate=0.1):
total = price * count
return total + int(total * rate)
a = total_price(4500, 2)
b = total_price(5500, 1)
print(a, b, "합계", a + b)📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.
9900 6050 합계 15950
return total + int(total * rate)가 실행되는 순간 함수는 거기서 끝납니다. 아래에 줄이 더 있어도 실행되지 않습니다. 그리고 계산된 값이 total_price(4500, 2)라고 적은 그 자리로 돌아가, 마치 처음부터 9900이라고 쓴 것처럼 대입됩니다.

호출한 자리를 떠났다가 값을 들고 그 자리로 돌아옵니다. return을 만나면 함수는 즉시 끝납니다.
검수 — 아무것도 안 나올 때
의뢰를 넘기기 전에 걸리기 쉬운 자리를 짚어 봅니다. 제가 이 편에서 가장 공들여 말하는 대목입니다.
계산은 맞는데 None이 찍히는 일이 있습니다.
def total_price(price, count):
price * count
print(total_price(4500, 2))📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.
None
공식 자습서 4.8절이 이 상황을 그대로 설명합니다. return 문이 없는 함수도 값을 돌려주는데, 그 값이 None입니다. 계산은 제대로 했지만 결과를 밖으로 보내지 않았으니 호출한 자리에는 아무것도 남지 않은 셈입니다. price * count를 return price * count로 고치면 9000이 나옵니다.
한 가지 더. 값을 안 돌려주는 함수를 그냥 부르기만 하면 화면에 None이 보이지 않습니다. 자습서 표현대로 인터프리터가 None 출력을 억제하기 때문입니다. print()로 감싸는 순간 정체가 드러납니다. 그래서 이 함정은 조용히 지나가다 나중에 계산이 틀어질 때 발견되는 편입니다.
괄호를 빠뜨리는 실수도 흔합니다.
print(total_price)
<function total_price at 0x7f3c...>
에러가 아닙니다. 괄호가 없으면 실행하지 않고 함수 그 자체를 가리킵니다. 값이 필요한 자리에 함수를 넣어 둔 셈이라, 다음 계산에서 엉뚱한 에러가 납니다.
정의보다 먼저 부르는 경우는 에러로 잡힙니다.
greet("손님")
def greet(name):
print(name, "안녕하세요")
NameError: name 'greet' is not defined
파이썬은 위에서 아래로 읽습니다. def 줄을 아직 지나지 않았으면 그 이름은 없는 이름입니다. 함수 정의는 부르는 곳보다 위에 둡니다.
손님이 열 명이면 몇 줄이 될까요
의뢰를 끝내고 두 코드의 길이를 세어 봤습니다.

손님이 늘수록 벌어집니다. 함수 쪽은 호출 한 줄씩만 늘어나고, 고칠 자리는 손님이 몇이든 하나입니다.
줄 수보다 오래 남는 이득은 고칠 자리가 하나라는 점입니다. 복붙 코드에서 세율을 바꾸려면 세 곳을 빠짐없이 찾아야 하고, 손님이 열 명이면 열 곳입니다. 하나를 놓치면 그 손님만 조용히 다른 값이 찍힙니다. 에러가 안 나는 실수라 더 늦게 발견됩니다.
함수 하나로 본 문법 일곱 개
| 문법 | 뜻 | 이 글의 예 |
|---|---|---|
def 이름(매개변수): | 함수를 정의한다 | def receipt(name, price, count): |
| 들여쓰기 | 함수의 몸. 끝나면 함수도 끝 | def 아래 네 줄 |
| 호출 | 이름 뒤 괄호로 실행 | receipt("라떼", 5500, 1) |
| 인자 | 호출할 때 넣는 값. 순서대로 짝 | "라떼", 5500, 1 |
return 값 | 값을 호출한 자리로 보내고 즉시 끝 | return total + vat |
| 기본값 | 안 넣으면 쓰이는 평소 값 | rate=0.1 |
None | return이 없을 때 돌아오는 값 | print(total_price(...)) |
def, 괄호, return. 셋만 자리를 알면 나머지는 따라옵니다. 그리고 오늘 사건의 재발 방지 수칙은 한 줄입니다. 값이 필요하면 return을 쓴다.
한 줄만 바꾸면 출력은 어떻게 될까요
미니 퀴즈
참고 자료
- 파이썬 공식 자습서 4.8 "함수 정의하기" — def 키워드와 매개변수, return 없는 함수가 돌려주는 None https://docs.python.org/ko/3/tutorial/controlflow.html
- 파이썬 공식 자습서 4.9 "함수 정의 더 보기" — 기본값 인자와 키워드 인자 https://docs.python.org/ko/3/tutorial/controlflow.html
- 파이썬 언어 레퍼런스 8.7 "함수 정의" https://docs.python.org/ko/3/reference/compound_stmts.html
포켓코딩(PYTHON1) 10편 · 9편(파이썬 while 반복문)에서 이어집니다. 예제 코드는 강사 개인 GitHub 저장소의 강의 자료를 바탕으로 다시 썼습니다. 다음 편은 딕셔너리입니다. 순서가 아니라 이름표로 값을 꺼내는 {}와, 없는 열쇠를 물었을 때 나는 KeyError를 다룹니다.