프로그래밍웹 교과서
이 페이지의 목차
2단원 · 13차시

함수는 왜 필요한가

함수라이브러리 함수사용자 정의 함수중복 제거

코드 예제: Python · C · 예상 50분

학습 목표

  • 함수의 개념과 필요성을 설명한다.
  • 라이브러리 함수와 사용자 정의 함수를 구분한다.

관련 성취기준

  • 프그02-05

    함수의 개념과 종류, 필요성을 이해하고, 함수 정의 및 사용자 정의 함수를 호출하고 결괏값을 반환하는 프로그램을 작성할 수 있다.

생각 열기 — 세 반의 평균을 구하려면

반마다 평균을 내야 한다. 4차시에서 배운 것으로 하면 이렇게 된다.

일반 = [88, 92, 75]
합계 = 0
for 점수 in 일반:
    합계 += 점수
print("1반 평균:", 합계 / len(일반))

이반 = [70, 80, 90]
합계 = 0
for 점수 in 이반:
    합계 += 점수
print("2반 평균:", 합계 / len(이반))

삼반 = [100, 60, 80]
...

같은 다섯 줄이 세 번 나온다.

복사해 붙인 코드의 값

같은 코드가 세 곳에 있으면 문제가 셋 생긴다.

복사해 붙였을 때 생기는 문제
문제어떻게 나타나는가
고칠 곳이 늘어난다평균 계산을 바꾸려면 세 곳을 고쳐야 한다
한 곳만 고치게 된다두 곳은 고치고 한 곳은 놓친다. 오류 없이 답만 달라진다
읽기 어려워진다무엇이 같고 무엇이 다른지 눈으로 찾아야 한다

두 번째가 가장 위험하다. 3단원 6차시에서 is_valid 를 세 사람이 각자 만들어 조건이 달라졌던 문제가 바로 이것이다. 프로그램이 멈추지도 않고 답만 조용히 틀린다.

함수 — 하는 일에 이름을 붙인다

함수(function) — 하는 일에 이름을 붙여 묶어 놓은 코드 덩어리. 이름으로 불러 쓴다는 코드 덩어리에 이름을 붙여, 필요할 때마다 이름으로 불러 쓰는 장치다.

3단원 3차시의 「부품」이 코드에서는 함수다. 그때 부품의 겉모습을 다섯 칸으로 못 박았다 — 이름·받는 것·돌려주는 것·하는 일·실패하면. 그 다섯 칸이 함수를 만들 때 그대로 필요하다.

Python평균 구하기를 함수로 묶기
def 평균(점수들):
    return sum(점수들) / len(점수들)

일반 = [88, 92, 75]
이반 = [70, 80, 90]
삼반 = [100, 60, 80]

print(f"1반 평균: {평균(일반):.1f}")
print(f"2반 평균: {평균(이반):.1f}")
print(f"3반 평균: {평균(삼반):.1f}")
확인된 실행 결과
1반 평균: 85.0
2반 평균: 80.0
3반 평균: 80.0
C평균 구하기를 함수로 묶기확인된 출력
#include <stdio.h>

double average(const int a[], int n)
{
    int sum = 0;
    for (int i = 0; i < n; i++) sum += a[i];
    return (double)sum / n;
}

int main(void)
{
    int c1[] = {88, 92, 75};
    int c2[] = {70, 80, 90};
    int c3[] = {100, 60, 80};

    printf("1반 평균: %.1f\n", average(c1, 3));
    printf("2반 평균: %.1f\n", average(c2, 3));
    printf("3반 평균: %.1f\n", average(c3, 3));
    return 0;
}
실행 결과
1반 평균: 85.0
2반 평균: 80.0
3반 평균: 80.0

무엇이 좋아졌나

  • 평균 계산이 한 곳에만 있다. 고칠 때 한 곳만 고친다.
  • 평균(일반) 이라는 말이 무엇을 하는지 그대로 읽힌다. 주석이 필요 없다.
  • 반이 열 개로 늘어도 계산 부분은 그대로다.

두 갈래 — 만들어져 있는 것과 내가 만드는 것

라이브러리 함수와 사용자 정의 함수
라이브러리 함수사용자 정의 함수
누가 만들었나언어나 남이 미리 만들어 둠내가 만든다
보기 (Python)print, len, sum, max, int, input평균, is_valid
보기 (C)printf, scanf, strlen, powaverage, summarize
쓰기 전에어떤 것은 #include 나 import 가 필요먼저 정의해 두어야 한다
안을 보는가안 본다. 무엇을 받고 무엇을 주는지만 알면 된다내가 만들었으니 안다

우리는 이미 라이브러리 함수를 계속 써 왔다. print, len, sum, int, input 이 전부 함수다. sum(점수들) 은 안이 어떻게 되어 있는지 몰라도 쓸 수 있었다.

C는 쓰기 전에 알려야 한다

C 예제에서 average 가 main 위에 있다. 이유가 있다. C는 위에서 아래로 읽으며 컴파일하므로, 부르는 곳보다 함수가 먼저 나와야 한다.

아래에 두려면 위쪽에 어떤 함수가 있을지 미리 알려 주는 한 줄을 적는다.

double average(const int a[], int n);   ← 이런 함수가 있다는 예고

int main(void) { ... average(...) ... }

double average(const int a[], int n) { ... }   ← 실제 내용

Python은 이런 예고가 필요 없다. 다만 부르는 시점에는 정의가 이미 실행되어 있어야 하므로, 관례로 함수를 위쪽에 모아 둔다.

활동

실습

중복된 코드를 함수로 묶어 정리하기

약 25분

1단계 — 고칠 곳을 세어 본다 (5분)

생각 열기의 복사해 붙인 코드와 본문의 함수 코드를 견줘 보자. 아래 요구가 들어왔을 때 고칠 곳이 몇 군데인가.

새 요구 사항 복사판에서 고칠 곳 함수판에서 고칠 곳
평균을 소수 둘째 자리까지
0점을 계산에서 뺀다
반이 다섯 개로 늘어난다

2단계 — 함수를 하나 더 만든다 (10분)

최고점을 구하는 함수를 만들어 붙인다.

  • Python에서는 max 를 쓰면 한 줄이다. 그래도 함수로 감싸는 것이 나은 경우가 있을까? (0점을 뺀 최고점을 구해야 한다면?)
  • C에서는 반복으로 직접 구한다. 3단원 4차시에서 다룬 첫 값 처리를 어떻게 할 것인가?

3단계 — 일부러 한 곳만 고쳐 본다 (5분)

생각 열기의 복사판 코드에서 세 곳 중 한 곳만 계산을 바꿔 본다(예: 하나만 / len(...) 을 / 3 으로).

  • 오류가 나는가?
  • 결과가 틀린 것을 무엇으로 알아챌 수 있는가?
  • 3단원 6차시에서 「가장 찾기 어려운 어긋남」이라고 한 것과 어떻게 이어지는가?

4단계 — 이름을 지어 본다 (5분)

아래 일들에 함수 이름을 붙여 보자. 한 낱말이나 짧은 구로 짓는다.

하는 일 내가 지은 이름
점수 목록에서 90점 이상인 사람 수를 센다
점수 하나가 0~100 범위인지 판정한다
점수를 등급 문자로 바꾼다
점수 목록을 받아 표로 찍는다
  • 이름 짓기가 어려운 것이 있었는가? 있다면 한 가지 일이 아닐 가능성을 살펴본다.

개념 확인 문제

보기를 누르면 바로 채점된다. 틀려도 해설을 읽으면 된다.

같은 코드를 여러 곳에 복사해 붙였을 때 가장 위험한 문제는?프그02-05

3단원 3차시의 「부품」에 해당하는 것은?프그02-05

다음 중 라이브러리 함수가 아닌 것은?프그02-05

sum(점수들) 을 안이 어떻게 되어 있는지 모르고도 쓸 수 있는 까닭은?프그02-05

C에서 함수를 main 아래에 두려면 무엇이 필요한가?프그02-05

함수 이름을 짓기 어려울 때 살펴봐야 하는 것은?프그02-05

더 알아보기

심화같은 코드가 두 번이면 아직 참을 만하다

“같은 코드가 보이면 무조건 함수로 묶어라”가 늘 맞는 것은 아니다. 두 곳에 비슷한 코드가 있는데 앞으로 서로 다르게 바뀔 것 같으면, 억지로 묶은 함수가 오히려 짐이 된다. 두 쪽 요구를 다 만족시키려고 조건과 매개변수가 계속 늘어나기 때문이다.

실무에서 흔히 쓰는 기준은 세 번째로 같은 코드를 적게 될 때 묶는다는 것이다. 세 번 나타났다면 앞으로도 나타날 가능성이 크고, 세 곳의 공통점이 무엇인지도 그때쯤 분명해진다. 다만 이 차시의 평균처럼 하는 일이 명확한 계산은 두 번째부터 묶어도 좋다.

실무함수로 나누면 테스트할 수 있다

3단원 7차시에서 단위 테스트를 다뤘다. 부품 하나를 따로 떼어 정한 입력에 정한 결과가 나오는지 확인하는 것이었다. 함수가 아니면 그것을 할 수 없다. 코드가 한 덩어리로 흘러 있으면 중간의 평균 계산만 떼어 시험할 방법이 없다.

그래서 “테스트하기 쉬운 코드”와 “함수로 잘 나뉜 코드”는 거의 같은 말이다. 나중에 테스트를 붙이려면 지금 나눠 두어야 한다. 16차시에서 프로그램 전체를 함수로 나눌 때 이 이야기를 다시 다룬다.

심화함수를 부르는 값

함수를 부르는 일이 공짜는 아니다. 값을 넘기고 돌아올 자리를 기억해 두는 준비가 필요하다. 그래서 아주 짧은 계산을 수백만 번 부르면 그 준비 비용이 눈에 띌 수 있다.

그렇다고 함수를 아끼는 것은 대체로 잘못된 판단이다. 1단원 6차시에서 다룬 대로 프로그램이 느린 원인은 대개 알고리즘이지 함수 호출이 아니다. 읽기 쉬운 코드로 먼저 만들고, 정말 느리면 어디가 느린지 재어 본 뒤 고치는 것이 순서다. 재어 보지 않고 짐작으로 고치는 것을 실무에서는 가장 흔한 낭비로 본다.

이어지는 학습

이 차시를 이해하려면 다음을 먼저 알아야 한다.

  • 12차시까지 작성한 반복되는 코드 — 프로그래밍 프그02-04

교과서에서 찾기

교과서해당 위치쪽수
성림출판(Python)프로그래밍의 기초 › 05 함수156쪽
씨마스커뮤니케이션(Python)프로그래밍 기초 › 5 함수146쪽
삼양미디어(Python)프로그래밍의 기초 › 04 함수126쪽
삼양미디어(C)프로그래밍의 기초 › 04 함수130쪽
씨마스(C)C 프로그래밍의 기초 › 6 함수126쪽
웅보출판사(C)프로그래밍의 기초 › Chapter 05 함수141쪽