본문 바로가기
JavaScript

[JS] switch 사용법(feat.타입스크립트)

by Vintz 2022. 3. 27.
반응형

switch는 하나의 표현식에 대해 여러 조건들이 존재할 때 유용하게 쓸 수 있다. 즉, 어떤 상황에 대해 여러 경우(분기)로 나누어질 때 사용하면 좋다. (과일 가격 알려주기, 테마 고르기, 방향에 따른 캐릭터의 움직임, 평가된 값에 따라 변환을 해주어야 할 때 등)

 

구문(syntax)과 예시를 통해 살펴보자.

구문(Syntax)

switch (expression) {
  case value1:
    // 표현식(expression)의 결과가 value1과 일치할 때 실행 됨 
    break;
  case value2:
    // 표현식(expression)의 결과가 value2과 일치할 때 실행 됨
    break;
  // ...
  case valueN:
    // 표현식(expression)의 결과가 valueN과 일치할 때 실행 됨
    break;
  default:
    // 표현식(expression)과 value가 일치하지 않을 때 실행 됨
    break;
}

expression

각각의 case 절에 맞추어볼 결과에 대한 표현식(expression).

case valueN(옵션)

표현식에 대해 일치 시키는 데 사용될 case 절이다. 표현식이 지정된 valueN과 일치하는 경우 case 절 내의 명령문은 switch 문의 끝부분이나 break가 올때까지 실행된다.

default(옵션)

default를 넣을 경우, 표현식의 값이 case 절과 일치하지 않을 경우에 실행된다.

예시 - 이건 얼마에요?

다음 예시를 보면 평가값을 '체리'로 주었다. 그럼 switch는 '체리'를 받아 값을 '체리'와 일치시키고, 해당 명령문을 실행한다. 마지막으로 break가 발생하면 switch를 벗어나 다음 명령문인 console.log('더 필요한건 없나요?')를 실행한다. 만약 break가 생략되면 다음 케이스의 명령문도 실행된다.

function getFruitsCost(fruit) {
  switch (fruit) {
    case '오렌지':
      console.log('오렌지는 개당 1,200원입니다.');
      break;
    case '사과':
      console.log('사과는 개당 900원입니다.');
      break;
    case '바나나':
      console.log('바나나는 개당 700원입니다.');
      break;
    case '체리':
      console.log('체리는 개당 3,600원입니다.');
      break;
    case '망고':
    case '파파야':
      console.log('망고와 파파야는 개당 3,000원입니다.');
      break;
    default:
      console.log(`죄송해요, ${fruit}는 다 나갔어요. 😭`);
  }
  console.log('더 필요한건 없나요?');
}

getFruitsCost('체리');
// 체리는 개당 3,600원입니다.
// 더 필요한건 없나요?

break를 생략하면 어떻게 될까?

break를 생략하게 되면 기준이 충족된 경우부터 명령문이 실행되며 그 후는 기준이 충족된 경우와 관계없이 다음 명령문이 실행된다. 다음 예시를 보자.

function getTheme(theme) {
  switch (theme) {
    case 'Light':
      console.log('해당 테마는 라이트 테마입니다.');
      break;
    case 'Dark': // theme은 'Dark'이므로 기준이 충족되어 해당 블록이 실행 됨
      console.log('해당 테마는 다크 테마입니다.');
    // break를 생략(아 맞다!)
    case 'Gen-Z': // case 'Dark'에 break가 없기 때문에 case 'Gen-Z'도 실행 됨
      console.log('해당 테마는 Gen-Z 테마입니다.');
      break; // 여기서 break가 되어 switch를 벗어남
    default:
      console.log(`${theme} 테마는 존재하지 않습니다.`);
  }
}

getTheme('Dark');
// 해당 테마는 다크 테마입니다.
// 해당 테마는 Gen-Z 테마입니다.

case 사이에 default를 넣는 경우

case 사이에 default를 넣으면 다음 예시와 같이 사용할 수 있다. 일치하는 항목을 찾지 못하면 기본테마로 되돌린다.

function getTheme(theme) {
  switch (theme) {
    case 'Dark':
      console.log('다크 테마입니다.');
      break; // break가 있어서 'default:'가 실행되지 않는다.
    default:
      console.log(`${theme} 테마는 존재하지 않습니다. 기본 테마를 적용합니다.`);
    case 'Light':
      console.log('라이트 테마입니다.');
      break;
  }
}

getTheme('Ocean');
// Ocean 테마는 존재하지 않습니다. 기본 테마를 적용합니다.
// 라이트 테마입니다.
물론 다른 모든 case보다 먼저 default를 놓을 때도 작동한다.

타입스크립트와 함께 사용하기

앱의 크기가 커져서 로직을 분리해서 사용한다고 생각해보자. getTheme() 함수를 사용할 때 컴파일 단계(코드 수준)에서 실수를 걸러낼 순 없을까? 타입스크립트를 사용 해보자.

type Theme = 'Light' | 'Dark' | 'Gen-Z';

function exhaustiveCheck(param: never) {}

export default function getTheme(theme: Theme) {
  switch (theme) {
    case 'Light':
      console.log('해당 테마는 라이트 테마입니다.');
      break;
    case 'Dark':
      console.log('해당 테마는 다크 테마입니다.');
      break;
    case 'Gen-Z':
      console.log('해당 테마는 Gen-Z 테마입니다.');
      break;
    default:
      exhaustiveCheck(theme);
      console.log(`${theme} 테마는 존재하지 않습니다.`);
  }
}

// 다른 파일.ts
import getTheme from '...';

getTheme('Gen-Z');

위 예시와 같이 Theme을 미리 정의 해두면 자동완성이 되어 함수를 더 편하게 사용할 수 있으며, 인수로 어떤 값을 주어야 할지 알 수 있다.

인수에 대해 자동완성을 지원한다

위 예시를 보면 전달이 될 수 있는 모든 유니온 타입에 대해 case 처리를 해야한다. 만약 개발자가 실수로 type은 추가하고 case 처리를 하지 않았다면? 타입스크립트는 이런 핸들링 실수를 런타임이 아닌 컴파일 단계에서 발견할 수 있게 해준다. never 타입을 활용해 빠짐없는 검사(exhaustive check)를 해보자.

never 타입은 항상 오류를 출력하거나 리턴 값을 절대로 내보내지 않음을 의미한다.

타입스크립트: "Ocean이라는 값이 전달 될 수도 있는데 왜 핸들링을 안했나요?"

타입스크립트는 "never 타입은 아무 값도 리턴하지 않는데 'Ocean'이 전달 된 것 같아. 문자열은 never 타입의 매개 변수에 할당될 수 없어"라고 말한다. 이렇게 코드 수준에서 개발자의 실수를 줄여줄 수 있다.

참고

Switch - MDN
Exhaustive Type Checking with TypeScript! - Babak
Never 타입 - Typescript Deep Dive
Never 타입 - Typescript 가이드북
반응형