함정 시리즈

[JavaScript] switch문의 fall-through

노새두마리 2024. 1. 29. 04:42
case마다 반드시 break를 사용할 것

if - else if - else문 대신 switch - case문을 사용해 보려다가 당했습니다. break를 사용하지 않았기 때문입니다.

switch - case

각각의 문자열이 아래와 같이 대응된다고 해봅시다.

  • U: up → y 좌표를 1 증가시킴
  • D: down → y 좌표를 1 감소시킴
  • L: left → x 좌표를 1 감소시킴
  • R: right → x 좌표를 1 증가시킴

간단하게 코드로 나타내 보면 아래와 같습니다.

const string = "UDLR";

let x = 0;
let y = 0;

for (let chr of string){
    switch (chr) {
        case 'U':
            y++;
        case 'D':
            y--;
        case 'L':
            x--;
        case 'R':
            x++;
    }
}

언뜻 보면 굉장히 직관적입니다. string의 각 문자에 대하여 U, D, L, R과 비교하여 매칭시킨 뒤, x 또는 y의 값을 재할당합니다.

하지만 여기에는 중대한 오류가 있으니, break가 사용되지 않았다는 점입니다.

의도한 대로 위, 아래, 왼쪽, 오른쪽(UDLR)으로 움직였다면 x와 y는 각각 아래의 순서로 바뀌었어야 합니다.

시작: (0, 0) → (0, 1) → (0, 0) → (-1, 0) → (0, 0)

하지만 실제로 x, y는 아래의 순서로 바뀝니다.

시작: (0, 0) → (0, 0) → (0, -1) → (0, -1) → (1, -1)

이유

왜 이런 현상이 발생했는가 하면, JavaScript의 switch - case문은 처음 매칭된 case와 그 이후의 모든 코드 블록을 실행해 버리기 때문입니다. 여기에서 말하는 실행은 다음 case로 넘어가 패턴을 비교하는 것을 말하는 것이 아닙니다. 패턴의 일치 여부와 상관없이 작성되어 있는 모든 코드 블록을 실행합니다.

다른 값과 비교가 불가능한 NaN을 넣어 봅시다.

const num = 1;
switch (num) {
	case 1:
    	console.log(num);
    case NaN:
    	console.log('안녕');
}

1
안녕
이 출력됩니다. 

이러한 현상을 "fall-through"라고 합니다. fall through에 "실패하다", "그르치다" 라는 뜻이 있다고 합니다만 어떤 계획의 실패를 나타낸다기 보다는 아래로 쭉 떨어지며 모든 코드를 실행하는 모습으로 이해하는 게 직관적인 것 같습니다.

이러한 특성 탓에 break가 없는 경우 아래의 범위에 해당하는 코드 블록이 모두 실행됩니다. default 블록이 있다면 그 또한 포함됩니다.

  • U는 모든 코드를 실행하여 (0, 0) 만큼 변화시킵니다.
  • D는 y++를 제외한 모든 코드를 실행하여 (0, -1) 만큼 변화시킵니다.
  • L은 y++, y--를 제외한 코드를 실행하여 (0, 0) 만큼 변화시킵니다.
  • R은 x++만을 실행하여 (1, 0) 만큼 변화시킵니다.

해결

case마다 break를 전부 추가하면 원래의 의도대로 동작하게 됩니다.

const string = "UDLR";

let x = 0;
let y = 0;

for (let chr of string){
    switch (chr) {
        case 'U':
            y++;
            break; // 추가
        case 'D':
            y--;
            break; // 추가
        case 'L':
            x--;
            break; // 추가
        case 'R':
            x++;
            break; // 추가
    }
}

알고 있으면 트릭으로 사용할 수는 있겠네요.

 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch