본문으로 건너뛰기

함수 만들고 부르기

자바스크립트 목차함수레슨 26 / 76

목차

함수(function)는 여러 줄의 일을 묶어 이름을 붙여 둔 것입니다. function으로 만들어 두고 이름 뒤에 괄호를 붙여 부르면, 새 칸이 생겨 그 안에서 일을 하고 return으로 값 하나를 돌려줍니다.

같은 계산을 여러 곳에서 하려면 같은 줄을 되풀이해 써야 하고, 계산이 바뀌면 그 줄을 모두 고쳐야 합니다. 함수로 만들어 두면 부를 때마다 같은 일을 하고 고칠 곳도 하나입니다. 지금까지 쓴 console.log·Number도 자바스크립트가 만들어 둔 함수입니다. 이 레슨에서는 함수를 부를 때 일어나는 일을 규칙 5로 세웁니다.

이 단원(함수)을 이미 안다면 단원 연습 문제부터 풀고 틀린 레슨만 보세요. 건너뛸지 볼 때는 힌트를 펴지 않고 풉니다.

파이썬을 배웠다면 — 이 단원에서 다른 곳

파이썬을 배웠다면 이 단원에서 다른 곳: 인자가 모자라도 오류가 아니라 그 매개변수가 undefined에 붙습니다 — 매개변수와 인자. 중괄호 덩어리 안의 이름과 함수 안에서 바깥 이름을 다루는 규칙이 파이썬과 달라, 블록과 함수 안의 이름은 건너뛰지 말고 보세요.

5단계 중 1단계. 함수 선언은 실행 전에 만들어집니다. 그래서 double은 처음부터 함수에 붙어 있고, 실행은 5번 줄부터입니다.

1부르면 새 칸이 생기고, return이 값을 돌려준다

그림의 코드입니다.

function double(n) {
  return n * 2;
}

const x = double(4);

1~3번 줄은 함수를 만들어 이름 double을 붙입니다. function 줄과 그 아래 중괄호 덩어리를 함께 함수 선언(function declaration)이라 하고, 중괄호 안을 함수 몸(body)이라 합니다. 함수 선언은 파일을 실행하기 전에 먼저 만들어지고, 몸은 만들 때 실행하지 않습니다. 그래서 실행은 5번 줄부터입니다.

5번 줄은 오른쪽 double(4)부터 계산합니다(규칙 2). 함수 이름 뒤에 괄호를 붙이면 그 함수를 부릅니다. 부르면 새 칸이 생기고, 괄호 안의 이름 n이 넘긴 값 4에 붙습니다. n처럼 함수를 만들 때 괄호 안에 적는 이름을 매개변수(parameter)라고 합니다. 그림의 칸 머리 'double 호출'이 이 칸이고, 함수를 부르는 일을 자료에 따라 호출(call)이라고도 합니다. 맨 위 '전역' 칸은 파일 맨 바깥 줄의 이름이 있는 칸입니다.

그다음 함수 몸의 2번 줄이 실행됩니다. return n * 2;는 n * 2를 계산한 8을 부른 자리인 double(4) 자리로 돌려줍니다. 돌려주면 칸이 끝나 사라지고, 5번 줄은 돌려받은 8에 이름 x를 붙입니다. n이 붙어 있던 4는 '가리키는 이름 없음'으로 남습니다. 이 코드는 아무것도 찍지 않습니다. x가 붙은 값을 보려면 끝에 console.log(x);를 더합니다.

이번에는 return 없이 console.log만 하는 함수입니다. 무엇이 찍힐까요? 답을 정한 뒤 펼쳐 보세요.

function say() {
  console.log('안녕');
}

const y = say();
console.log(y);
실행 결과 보기
실행 결과
안녕
undefined

예상과 다르면 그곳이 배울 곳입니다. 5번 줄이 say를 부르면 2번 줄이 화면에 안녕을 찍습니다. console.log는 화면에 쓰기만 할 뿐 say가 돌려줄 값을 정하지 않고, say의 몸에는 return이 없습니다. return 없이 몸이 끝나면 함수는 undefined를 돌려줍니다. 그래서 y는 undefined에 붙고, 6번 줄이 undefined를 찍습니다. 안녕이 두 번 찍힌다고 예상했다면, 화면에 찍는 것과 값을 돌려주는 것을 같은 일로 본 것입니다.

double과 say를 견주면 함수를 부를 때 일어나는 일이 셋으로 모입니다.

규칙 5. 함수를 부르면 새 칸이 생겨 매개변수와 그 안의 이름이 그 칸에 생깁니다. return은 값 하나를 부른 자리에 돌려주고 칸을 끝냅니다(없으면 undefined). console.log는 찍기만 하고 undefined를 돌려줍니다.

2함수 몸은 부를 때마다 실행된다

함수 몸은 함수를 만들 때가 아니라 부를 때마다 실행됩니다.

function greet() {
  console.log('어서 오세요');
}

console.log('문을 엽니다');
greet();
greet();
console.log('끝');
실행 결과
문을 엽니다
어서 오세요
어서 오세요
끝

1~3번 줄은 함수를 만들기만 해 아무것도 찍지 않습니다. 5번 줄이 먼저 찍고, 6번 줄과 7번 줄이 부를 때마다 새 칸에서 2번 줄이 실행됩니다. 몸이 끝나면 부른 줄로 돌아와 그다음 줄로 갑니다.

3괄호가 함수를 부른다

함수 이름은 함수를 가리킬 뿐이고, 함수를 부르는 것은 이름 뒤의 괄호입니다. 괄호 없이 이름만 찍으면 함수 그 자체가 찍힙니다.

function double(n) {
  return n * 2;
}

console.log(double);
console.log(double(4));
실행 결과
[Function: double]
8

[Function: double]은 'double이라는 함수'를 Node가 찍는 꼴입니다. 6번 줄처럼 괄호를 붙여야 함수를 불러 돌려받은 8이 찍힙니다.

거꾸로 함수가 아닌 값에 괄호를 붙이면 오류가 납니다.

function double(n) {
  return n * 2;
}

const x = double(4);
console.log(x(2));
실행 결과
main.js:6console.log(x(2));            ^ TypeError: x is not a function    at main.js:6:13

TypeError: x is not a function⁠은 'x는 함수가 아니다', 곧 함수가 아닌 값을 부르려 했다는 뜻입니다. x는 함수 double이 아니라 double이 돌려준 8에 붙은 이름입니다. 8을 다시 두 배로 하려면 double(x)처럼 함수에 넘깁니다.

예제

함수로 감싸기 — 잔 수로 값 구하기

잔 수를 받아 커피 값을 돌려주는 함수를 하위 목표 셋으로 나눠 썼습니다.

// 1. 받을 값 정하기
function price(cups) {
  // 2. 계산하기
  const won = cups * 4000;
  // 3. 돌려주기
  return won;
}

console.log(price(2));
console.log(price(3));
실행 결과
8000
12000

받을 값(잔 수)을 매개변수 cups로 정하고, 몸에서 계산하고, return으로 돌려줍니다. won은 함수 칸 안의 이름입니다. 9번 줄은 돌려받은 8000을 console.log에 넘겨 찍고, 10번 줄이 부르면 새 칸이 다시 생겨 cups가 3에 붙고 다시 계산합니다.

자주 틀리는 곳

틀린 생각: 함수 안에서 console.log로 찍은 값이 부른 자리로 돌아온다.

화면에 값이 보이니 함수가 그 값을 내놓은 것처럼 여기게 됩니다. 1절의 say처럼 화면에 쓰는 일과 부른 자리로 값을 돌려주는 일은 다릅니다.

맞는 규칙: console.log는 화면에 쓰기만 하고, return이 없는 함수는 undefined를 돌려줍니다. 값을 돌려받으려면 return을 씁니다(규칙 5).

틀린 생각: 괄호 없이 함수 이름만 써도 함수가 실행된다.

이름이 함수를 나타내니, 이름만 적어도 그 일을 할 것처럼 보입니다. 3절에서 본 것처럼 console.log(double)은 함수를 부르지 않고 함수 그 자체를 찍습니다.

맞는 규칙: 함수 이름은 함수를 가리킬 뿐이라 이름만 쓰면 아무 일도 일어나지 않습니다. 부르려면 double(4)처럼 이름 뒤에 괄호를 붙입니다(규칙 5).

틀린 생각: 몸에서 계산한 값은 return을 쓰지 않아도 돌아온다.

몸의 마지막 줄이 계산이니, 그 값이 함수의 결과가 될 것처럼 보입니다.

function double(n) {
  n * 2;
}

console.log(double(4));
실행 결과
undefined

2번 줄은 8을 계산하고 그대로 버립니다. 몸에 return이 없어 double은 undefined를 돌려줍니다.

맞는 규칙: 함수는 return에 적은 값만 돌려주고, return 없이 몸이 끝나면 undefined를 돌려줍니다(규칙 5).

확인 문제

1. 2절 코드에서 5번 줄과 6번 줄의 차례를 바꿨습니다. 무엇이 찍힐까요? 마지막 줄까지 글자 그대로 쓰세요.

function greet() {
  console.log('어서 오세요');
}

greet();
console.log('문을 엽니다');
greet();
console.log('끝');
힌트

5번 줄부터 차례로 따라가며, greet() 줄에서는 2번 줄로 들어갔다가 돌아와 다음 줄로 갑니다.

정답

모두 4줄입니다.

실행 결과
어서 오세요
문을 엽니다
어서 오세요
끝

1~3번 줄은 함수를 만들기만 합니다. 5번 줄과 7번 줄이 부를 때마다 2번 줄이 '어서 오세요'를 찍고, 그 사이에 6번 줄이 '문을 엽니다'를 찍습니다.

흔한 오답은 맨 위에 '어서 오세요'가 한 줄 더 붙은 다섯 줄입니다. 함수를 만들 때도 몸이 실행된다고 본 것입니다. 몸은 부를 때만 실행됩니다(규칙 5).

2. 분을 받아 초를 돌려주는 함수입니다. 아래 두 줄이 찍히도록 '3. 돌려주기'의 빈칸을 채우세요.

120
300
// 1. 받을 값 정하기
function seconds(min) {
  // 2. 계산하기
  const s = min * 60;
  // 3. 돌려주기
  ____
}

console.log(seconds(2));
console.log(seconds(5));
힌트

9번 줄의 console.log가 찍는 것은 seconds(2)가 부른 자리에 놓는 값입니다. 그 값을 정하는 줄이 빈칸입니다.

정답

return s;입니다.

// 1. 받을 값 정하기
function seconds(min) {
  // 2. 계산하기
  const s = min * 60;
  // 3. 돌려주기
  return s;
}

console.log(seconds(2));
console.log(seconds(5));
실행 결과
120
300

다르게 써도 되는 곳: return min * 60;도 같은 값을 돌려줍니다.

흔한 오답은 console.log(s);입니다. 그러면 120 다음 줄에 undefined, 300 다음 줄에 undefined가 찍혀 네 줄이 됩니다. 찍는 것과 돌려주는 것이 같다고 본 것입니다(console.log와 return). 부른 자리로 값을 보내는 것은 return입니다(규칙 5).

3. 시험 점수에 5점을 더하는 함수입니다. 무엇이 찍힐까요? 두 줄을 글자 그대로 쓰세요.

function bonus(score) {
  return score + 5;
}

console.log(bonus);
console.log(bonus(90));
힌트

5번 줄과 6번 줄에서 이름 bonus 뒤에 괄호가 있는지 봅니다.

정답
실행 결과
[Function: bonus]
95

5번 줄은 괄호가 없어 bonus를 부르지 않고, bonus가 가리키는 함수 그 자체를 찍습니다. 6번 줄은 bonus를 불러 돌려받은 95를 찍습니다.

흔한 오답은 첫 줄에 95나 NaN을 쓰는 것입니다. 괄호 없이 이름만 써도 함수가 불린다고 본 것입니다(괄호 없이도 부른다는 생각). 함수를 부르는 것은 이름 뒤의 괄호입니다(규칙 5).

컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요. 1절 그림 코드 끝에 console.log(x);를 더해 main.js에 저장해 돌리면 8이 찍힙니다. 그다음 2번 줄 return n * 2;를 console.log(n * 2);로 바꾸면 무엇이 찍힐지 먼저 적고, 바꿔 돌려서 맞춰 봅니다. 저장하고 돌리는 법은 내 컴퓨터에서 돌려 보기에 있습니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

자바스크립트 목차로