thumbnail

[Ice Break] 자바스크립트 루프 스코핑과 타이밍 이슈 깊이 이해하기

생성일2023. 8. 31.
태그
작성자지한솔

자바스크립트 개발자라면 루프 내에서 비동기 메서드를 사용할 때 발생하는 이상한 행동에 부딪힌 적이 있을 것입니다. 대표적인 예로 setTimeoutfor 루프 안에서 사용하는 경우가 있습니다. 이 시나리오는 수년 동안 자바스크립트 인터뷰에서 꾸준히 나오는 문제였습니다. 이 블로그 포스팅에서는 이 문제가 왜 발생하는지 배경까지 깊이 파해쳐 보고, 여러 가지 해결 방법도 함께 논의해보겠습니다.

목차

  1. 고전적인 문제
  1. 문제 이해하기
  1. 해결책
    1. let 사용하기
    2. setTimeout의 세 번째 인자 사용하기
    3. IIFE 사용하기
  1. 다른 예제와 함정
  1. 결론

고전적인 문제

대부분의 개발자가 처음 부딪히는 고전적인 문제는 다음과 같습니다:
for (var i = 0; i < 5; i++) { setTimeout(() => console.log(i), i * 1000) }
이 코드를 실행하면 콘솔에 0부터 4까지의 숫자가 1초 간격으로 로그될 것으로 예상할 수 있습니다. 그러나 실제로는 숫자 5가 다섯 번 로그됩니다.

문제 이해하기

이 문제가 발생하는 이유는 var 키워드의 스코프 때문입니다. 자바스크립트에서 var는 함수 스코프입니다. 이는 변수 i가 선언된 함수 내에서나 전역적으로 접근 가능하다는 것을 의미합니다. for 루프의 경우, setTimeout 함수가 실행될 때까지 루프가 이미 완료되어 i5의 값을 가지게 됩니다.

해결책

let 사용하기

이 문제를 해결하는 가장 간단한 방법은 var 대신 let을 사용하는 것입니다. let 키워드는 블록 스코프이므로 각 반복마다 자체 스코프를 갖게 됩니다.
for (let i = 0; i < 5; i++) { setTimeout(() => console.log(i), i * 1000) }

setTimeout의 세 번째 인자 사용하기

다른 해결책으로는 setTimeout의 세 번째 인자를 사용하는 것이 있습니다. 이를 통해 함수에 인자를 전달할 수 있습니다.
for (var i = 0; i < 5; i++) { setTimeout((capturedI) => console.log(capturedI), i * 1000, i) }

IIFE 사용하기

IIFE(즉시 실행 함수 표현식)를 사용해 각 반복마다 새로운 스코프를 생성할 수도 있습니다.
for (var i = 0; i < 5; i++) { ((capturedI) => setTimeout(() => console.log(capturedI), i * 1000) )(i); }

다른 예제와 함정

자바스크립트에서 스코프 문제를 이해하는 것은 setTimeout에만 국한되지 않습니다. 이 지식은 프로미스나 async/await과 같은 다른 비동기 작업을 처리할 때도 중요합니다.
프로미스와의 예제:
for (var i = 0; i < 5; i++) { Promise.resolve(i).then((capturedI) => console.log(capturedI)) }
var를 사용하면 여기서도 유사한 문제가 발생합니다. let을 전환하거나 다른 스코프 캡처 기술을 사용하면 문제가 해결됩니다.

결론

자바스크립트의 스코핑과 타이밍의 미묘한 부분을 이해하는 것은 중요합니다, 특히 루프 내에서 비동기 작업을 처리할 때는 더욱 그렇습니다. setTimeout을 루프 안에서 사용하는 고전적인 문제는 모든 자바스크립트 개발자가 알아야 할 언어의 이상한 점을 잘 보여줍니다.
변수를 적절하게 캡처하는 방법을 이해함으로써 더 예측 가능하고 버그가 없는 코드를 작성할 수 있습니다. 이는 당신을 더 나은 개발자로 만들 뿐만 아니라, 다른 사람들도 당신의 코드를 더 쉽게 이해하고 유지보수할 수 있게 해줍니다.
읽어주셔서 감사합니다. 이 포스트가 유용하다고 생각되면 다른 사람들과 공유해 주세요. 행복한 코딩 되세요!