iBetter Books
수정

선택적 매개변수와 기본값

카페에서 커피를 주문할 때를 생각해봅시다. "아메리카노 한 잔 주세요"라고만 해도 됩니다. 직원은 기본 사이즈로 줍니다. 원한다면 "아이스로, 샷 추가해서"라고 덧붙일 수 있습니다. 하지만 이 추가 정보는 필수가 아닙니다.

함수도 마찬가지입니다. 어떤 매개변수는 반드시 필요하고, 어떤 매개변수는 주면 좋지만 없어도 되는 경우가 있습니다. TypeScript는 이런 상황을 두 가지 방법으로 표현합니다. ?로 선택적 매개변수를 만들거나, =로 기본값을 지정하는 것입니다.

선택적 매개변수 (?)

매개변수 이름 뒤에 ?를 붙이면 그 매개변수는 넘겨도 되고 생략해도 됩니다.

// 새 파일: optional-params.tsfunction greet(name: string, greeting?: string): string {  if (greeting) {    return `${greeting}, ${name}님!`;  }  return `안녕하세요, ${name}님!`;}console.log(greet("이민준"));               // 안녕하세요, 이민준님!console.log(greet("박지수", "좋은 아침"));  // 좋은 아침, 박지수님!

greeting이 없으면 undefined가 됩니다. 그래서 바로 사용하지 않고 if (greeting)으로 먼저 확인했습니다.

선택적 매개변수는 반드시 필수 매개변수 뒤에 와야 합니다.

// 오류: 필수 매개변수는 선택적 매개변수 뒤에 올 수 없습니다function wrong(a?: string, b: number): void { }  // 오류!// 올바른 순서function right(a: number, b?: string, c?: boolean): void { }  // 정상

기본값 매개변수 (=)

기본값을 지정하면 인수를 생략했을 때 그 값이 자동으로 들어옵니다. JavaScript에서도 쓰던 문법입니다.

// 새 파일: default-params.tsfunction createEmail(  to: string,  subject: string,  priority: "low" | "normal" | "high" = "normal"): string {  return `[${priority.toUpperCase()}] ${to}에게: ${subject}`;}console.log(createEmail("[email protected]", "안녕하세요"));// [NORMAL] [email protected]에게: 안녕하세요console.log(createEmail("[email protected]", "서버 점검", "high"));// [HIGH] [email protected]에게: 서버 점검

기본값을 지정하면 타입 어노테이션을 생략해도 됩니다. TypeScript가 기본값으로부터 타입을 추론합니다.

// 타입 어노테이션 생략 — TypeScript가 추론합니다function repeat(text: string, times = 3): string {  // times의 타입은 number로 추론됩니다  return text.repeat(times);}console.log(repeat("TypeScript! "));      // TypeScript! TypeScript! TypeScript!console.log(repeat("Go! ", 5));           // Go! Go! Go! Go! Go!

선택적 vs 기본값, 무엇을 써야 할까

두 방식은 비슷해 보이지만 차이가 있습니다.

// 선택적 매개변수: undefined가 될 수 있습니다function withOptional(value?: number): void {  // value의 타입은 number | undefined  console.log(value);   // undefined가 출력될 수 있습니다}// 기본값 매개변수: 항상 지정된 타입입니다function withDefault(value: number = 0): void {  // value의 타입은 number (undefined 없음)  console.log(value);   // 항상 number가 출력됩니다}withOptional();     // undefinedwithDefault();      // 0

대부분의 경우 기본값 방식이 더 안전합니다. undefined를 처리하는 분기문이 필요 없기 때문입니다. "값이 없으면 이 값을 쓰겠다"는 의도가 명확할 때 기본값을 씁니다.

반면 ?는 "이 값이 아예 전달되지 않았다"는 사실 자체가 의미 있을 때 씁니다. 예를 들어 설정 업데이트 함수에서 undefinedfalse를 구별해야 하는 경우가 그렇습니다.

나머지 매개변수 (...args)

개수가 정해지지 않은 인수를 받을 때 나머지 매개변수를 씁니다. ... 뒤에 이름을 붙이면 배열로 모입니다.

// 새 파일: rest-params.tsfunction sum(...numbers: number[]): number {  return numbers.reduce((total, n) => total + n, 0);}console.log(sum(1, 2, 3));           // 6console.log(sum(10, 20, 30, 40));    // 100console.log(sum());                  // 0function logAll(prefix: string, ...messages: string[]): void {  messages.forEach(msg => {    console.log(`[${prefix}] ${msg}`);  });}logAll("INFO", "서버 시작", "포트 3000", "준비 완료");// [INFO] 서버 시작// [INFO] 포트 3000// [INFO] 준비 완료

나머지 매개변수는 타입을 배열 형태로 씁니다. number[]는 "숫자들의 배열"이므로 ...numbers: number[]는 "숫자를 몇 개든 받겠다"는 뜻입니다.

나머지 매개변수는 반드시 마지막 위치에 있어야 합니다.

// 오류: 나머지 매개변수는 마지막이어야 합니다function wrong(...items: string[], separator: string): void { }  // 오류!// 올바른 순서function right(separator: string, ...items: string[]): void { }  // 정상

나머지 매개변수와 유니온 타입 조합

다양한 타입의 인수를 받아야 할 때 유니온 타입을 활용합니다.

// 새 파일: flexible-params.tsfunction printAll(...items: (string | number | boolean)[]): void {  items.forEach(item => {    console.log(String(item));  });}printAll("TypeScript", 42, true, "재미있다", 100);// TypeScript// 42// true// 재미있다// 100

실전 예제 — 공지사항 생성 함수

지금까지 배운 세 가지를 조합한 실전 예제를 살펴봅시다.

// 새 파일: notice.tsinterface Notice {  title: string;  content: string;  author: string;  priority: "low" | "normal" | "high";  tags: string[];}function createNotice(  title: string,  content: string,  author: string,  priority: "low" | "normal" | "high" = "normal",  ...tags: string[]): Notice {  return {    title,    content,    author,    priority,    tags  };}// 필수 매개변수만const basicNotice = createNotice(  "시스템 점검 안내",  "2월 5일 오전 2시부터 4시까지 점검합니다.",  "관리자");// 모든 매개변수 지정const urgentNotice = createNotice(  "긴급 보안 패치",  "즉시 업데이트가 필요합니다.",  "보안팀",  "high",  "보안", "긴급", "필수");console.log(basicNotice.priority);    // normal (기본값)console.log(basicNotice.tags);        // [] (나머지 매개변수 없음)console.log(urgentNotice.priority);   // highconsole.log(urgentNotice.tags);       // ["보안", "긴급", "필수"]

필수 정보는 앞에, 기본값이 있는 건 그 뒤에, 가변 개수의 태그는 맨 마지막에. 각 매개변수의 역할이 명확합니다.


이제 함수를 더 유연하게 설계할 수 있게 되었습니다. 꼭 필요한 것과 선택적인 것을 구분하고, 합리적인 기본값을 설정하면 호출하는 쪽의 코드가 훨씬 간결해집니다.

다음 장에서는 함수를 값처럼 다루는 방법을 배웁니다. 함수를 다른 함수의 인수로 넘기는 콜백 패턴과, 그 콜백에 정확한 타입을 붙이는 방법을 살펴봅니다.