매개변수와 인수의 차이
많은 분들이 헷갈려 하는 두 용어입니다. 매개변수(parameter) 는 함수를 정의할 때 적어두는 이름이고, 인수(argument) 는 함수를 호출할 때 실제로 넘기는 값입니다. 파이썬을 비롯한 대부분의 언어와 개념이 같습니다:
a와 b는 매개변수(parameter)이고, 2와 3은 인수(argument)입니다. 자바스크립트는 이 둘을 위치 기반으로 연결하죠 — 첫 번째 인수는 첫 번째 매개변수에, 그다음은 그다음에 대응되는 식입니다. 사소한 구분처럼 보여도 에러 메시지나 MDN 문서에서 쓰이는 용어이니 익혀 두는 게 좋습니다.
자바스크립트는 인자 개수에 관대하다
다른 많은 언어와 달리, 자바스크립트는 인수를 너무 적게 넘기든 너무 많이 넘기든 개의치 않습니다. 빠진 매개변수는 undefined가 되고, 남는 인수는 조용히 무시됩니다:
첫 번째 호출은 안녕하세요, 에이다 undefined를 출력합니다. last에 값을 넘기지 않았으니 undefined가 되고, 템플릿 리터럴은 그걸 그대로 문자열에 꽂아 넣죠. 에러도, 경고도 없습니다. 이런 관대함이 편할 때도 있지만 버그의 원인이 되기도 하는데, 바로 이 지점 때문에 기본 매개변수가 필요한 겁니다.
자바스크립트 함수 기본값 설정하기
매개변수 이름 뒤에 =와 값을 붙여 주면 됩니다. 호출하는 쪽에서 해당 인수를 넘기지 않거나 undefined를 넘기면, 지정해 둔 기본값이 대신 쓰입니다:
세 번 다 정상적으로 동작합니다. 첫 번째와 세 번째 호출은 기본값이 적용되고, 두 번째는 전달한 값을 그대로 씁니다. 이건 ES6에서 새로 들어온 문법인데요, 2015년 이전에는 함수 본문에 name = name || "friend" 식으로 직접 처리해야 했습니다. 이 방식은 0이나 "" 같은 falsy 값이 들어올 때 의도치 않게 기본값으로 덮어써지는 버그가 생기기 쉬웠죠.
기본값으로는 리터럴뿐 아니라 어떤 표현식이든 쓸 수 있습니다:
표현식은 함수가 정의될 때 한 번만 평가되는 게 아니라 호출할 때마다 매번 평가됩니다. 그래서 Python처럼 가변 기본값(mutable default) 함정 같은 게 없어요 — 호출마다 새로운 new Date()가 생성됩니다.
undefined일 때만 기본값이 적용된다
이게 바로 많은 분들이 헷갈려 하는 규칙입니다. 자바스크립트 기본 매개변수는 인수가 undefined일 때만 발동되고, falsy 값이라고 해서 발동되지 않으며, null일 때도 절대 발동되지 않습니다:
마지막 호출에서만 "친구"가 쓰입니다. null을 명시적으로 넘긴다는 건 "값이 null이다"라고 말하는 셈이고, 자바스크립트는 그 말을 곧이곧대로 받아들입니다. 빈 문자열이나 0도 마찬가지예요. 이들은 엄연한 값이지, 인수를 빠뜨린 게 아닙니다.
null을 값이 없는 것처럼 취급하고 싶다면 직접 처리해 줘야 합니다.
?? 연산자(널 병합 연산자)는 null과 undefined를 모두 "값이 없음"으로 처리합니다. 이에 대한 자세한 내용은 이후 챕터에서 다룹니다.
앞에 있는 매개변수를 참조하는 기본값
매개변수는 왼쪽에서 오른쪽 순서로 평가되기 때문에, 뒤쪽 매개변수의 기본값에서 앞서 선언된 매개변수를 자유롭게 참조할 수 있습니다:
인자를 하나만 넘기면 정육면체가 되고, 두 개를 넘기면 사각기둥이 만들어집니다. 여기서 순서가 중요한데, 아직 선언되지 않은 매개변수는 기본값에서 참조할 수 없습니다:
function bad(a = b, b = 1) {
return a + b;
}
bad(); // ReferenceError: 초기화 전에 'b'에 접근할 수 없습니다
같은 규칙이 적용됩니다. let으로 선언한 변수처럼, 선언하기 전에 사용하면 에러가 납니다.
구조 분해와 기본값 함께 쓰기
매개변수를 구조 분해하면서 동시에 기본값을 지정할 수도 있습니다. "옵션 객체" 형태의 인수를 받을 때 자주 쓰는 패턴이죠:
기본값은 두 단계로 동작합니다. 안쪽의 기본값(role = "member", active = true)은 빠진 속성을 채워 주고, 바깥의 = {}는 호출자가 인수를 아예 넘기지 않은 경우를 처리합니다. 이게 없으면 createUser()처럼 호출했을 때 undefined를 구조 분해하려다 에러가 나죠.
처음에는 문법이 빽빽해 보이지만, 요즘 자바스크립트 코드베이스에서는 정말 흔하게 쓰이는 패턴입니다. { ... } = {} 형태가 "기본값이 있는 옵션 객체"라는 걸 눈에 익혀 두면 훨씬 빠르게 읽힙니다.
중간 인수 건너뛰기
자바스크립트에는 파이썬 같은 키워드 인수가 없습니다. 중간에 있는 매개변수만 건너뛰고 기본값을 쓰고 싶다면, 그 자리에 undefined를 명시적으로 넘겨야 합니다:
prefix 자리에 undefined를 넘기면 기본값이 그대로 유지되긴 합니다. 보기엔 좀 지저분하죠. 호출할 때마다 undefined를 적고 있다면, 옵션 객체 방식으로 바꾸라는 신호입니다:
이제 호출하는 쪽에서 덮어쓸 값만 골라서 이름으로 지정할 수 있고, 순서도 상관없습니다.
기본값이 있는 매개변수는 length에 포함되지 않는다
자주 마주치진 않지만 가끔 헷갈리는 포인트가 있습니다. 함수의 length 프로퍼티는 기본값이 지정된 첫 번째 매개변수 이전 까지의 매개변수 개수만 돌려줍니다:
함수를 들여다보는 라이브러리들(일부 테스트 도구나 DI 도구 같은)은 length를 이용해서 "필수" 매개변수 개수를 세기도 합니다. 이런 규칙이 있다는 정도만 알아두면 충분하고, 본인이 그런 도구를 직접 만드는 게 아니라면 굳이 외울 필요는 없습니다.
다음: Rest와 Spread
기본값은 매개변수를 미리 알고 있을 때 쓰기 좋습니다. 하지만 가끔은 그렇지 않을 때도 있죠. 인자를 몇 개 받을지 정해지지 않았거나, 받은 인자를 그대로 다른 함수에 넘겨주고 싶을 때 말입니다. 바로 이런 상황을 위해 ...rest와 스프레드 연산자가 있습니다. 다음 장에서 자세히 살펴보겠습니다.
자주 묻는 질문
자바스크립트에서 매개변수 기본값은 어떻게 지정하나요?
함수 정의부의 매개변수 이름 뒤에 =와 기본값을 붙여주면 됩니다. 예를 들어 function greet(name = 'friend') { ... } 처럼요. 호출할 때 값을 넘기지 않거나 undefined를 넘기면 이 기본값이 사용됩니다. ES6부터 도입된 문법이라 요즘 쓰는 자바스크립트 환경에서는 전부 지원합니다.
매개변수(parameter)와 인수(argument)는 어떻게 다른가요?
매개변수는 함수 정의에 쓰는 이름입니다. function add(a, b)에서 a, b가 매개변수죠. 반면 인수는 함수를 호출할 때 실제로 넘기는 값입니다. add(2, 3)이라고 쓰면 2와 3이 인수예요. 에러 메시지나 공식 문서를 읽을 때 이 구분을 알아두면 훨씬 이해하기 쉽습니다.
null을 넘기면 기본값이 적용되나요?
아니요, 적용되지 않습니다. 기본값은 인수가 undefined이거나 아예 생략된 경우에만 동작합니다. null을 명시적으로 넘기는 건 "값을 제대로 전달했고, 그 값이 null이다"라는 뜻이라서 기본값이 건너뛰어집니다. null과 undefined를 같은 것으로 취급하는 언어에 익숙한 분들이 자주 헷갈리는 부분이에요.
기본값에 다른 매개변수를 쓸 수 있나요?
네, 가능합니다. 매개변수는 왼쪽에서 오른쪽 순서로 평가되기 때문에 뒤쪽 매개변수의 기본값에 앞쪽 매개변수를 쓸 수 있어요. function box(width, height = width) 같은 형태죠. 또 function log(msg, time = Date.now())처럼 함수 호출을 기본값으로 둘 수도 있는데, 이 표현식은 함수가 호출될 때마다 매번 새로 실행됩니다.