LogoSEO Jing
  • All Posts
  • SEO Jing
  • okayJing
  • KD Team
  • CLAB Coreteam
  • Study

Contact Me

© 2026 SEOJing. All rights reserved.

자바스크립트 퀴즈북 리마인드 Day 7: 클로저 버그와 오래된 값

2026년 6월 28일·3분 읽기
오늘의 질문: “클로저는 값을 복사해 둔 스냅샷일까, 변수 바인딩을 계속 따라가는 참조일까?”

먼저 맞혀보기

js
function makeCounters() {
  const result = [];

  for (var i = 0; i < 3; i += 1) {
    result.push(function read() {
      return i;
    });
  }

  return result;
}

const [a, b, c] = makeCounters();
console.log(a(), b(), c());

출력은 3 3 3입니다. 세 함수가 각각 0, 1, 2를 복사해 둔 게 아니라, var i라는 같은 함수 스코프 바인딩을 닫아두었기 때문입니다. 루프가 끝난 뒤 그 바인딩의 값은 3입니다.

let으로 바꾸면 결과가 달라집니다.

js
for (let i = 0; i < 3; i += 1) {
  result.push(() => i);
}
// 0, 1, 2

let은 반복마다 새 블록 바인딩을 만들기 때문에 각 콜백이 닫아두는 대상이 달라집니다.

틀리기 쉬운 직감

“함수가 만들어질 때 값이 복사된다”라고 생각하면 클로저 버그를 잘못 고칩니다. 클로저가 기억하는 것은 보통 값 자체가 아니라 렉시컬 환경에 있는 바인딩입니다. 그래서 같은 바인딩을 여러 함수가 공유하면 나중 값이 같이 보이고, 렌더·이벤트·타이머 사이에 생성 시점이 어긋나면 오래된 경로를 읽습니다.

클로저와 오래된 바인딩 리뷰 지도

stale closure는 “React만의 버그”가 아니다

React에서 자주 보일 뿐, 원인은 자바스크립트 함수 생성 위치입니다.

jsx
function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      console.log(count);
    }, 1000);

    return () => clearInterval(id);
  }, []);
}

의존성 배열이 비어 있으면 interval 콜백은 처음 렌더의 count 바인딩을 닫아둡니다. 화면에서는 값이 바뀌어도 이 콜백은 계속 첫 렌더의 경로를 볼 수 있습니다.

코드 리뷰에서는 “왜 count가 최신이 아니지?”보다 먼저 “이 함수가 어느 렌더에서 만들어졌지?”를 묻는 편이 낫습니다.

고치는 방식도 원인에 맞춰야 한다

상황의심할 부분흔한 해결 방향
루프 안 콜백이 모두 같은 값같은 var 바인딩 공유let, 별도 함수, 값 인자 전달
타이머가 오래된 상태 출력콜백 생성 시점과 상태 변경 시점 분리의존성 배열 보정, ref, updater 함수
이벤트 핸들러가 과거 props 사용핸들러가 오래된 렌더에서 생성최신 값을 인자로 넘기거나 핸들러 갱신
async 후 상태 업데이트 꼬임완료 시점이 생성 시점보다 늦음취소 플래그, AbortController, functional update

핵심은 “클로저를 없앤다”가 아닙니다. 클로저는 유용합니다. 다만 어떤 바인딩을 닫아두는지 코드가 말해주지 않으면 나중 실행에서 버그가 됩니다.

프론트엔드 리뷰 체크리스트

콜백이 만들어지는 위치와 실행되는 위치가 멀리 떨어져 있지 않은가? 반복문 안 콜백이 같은 바인딩을 공유하지 않는가? useEffect, useCallback, 이벤트 리스너의 의존성이 실제로 읽는 값과 맞는가? 최신 값이 필요하다면 state updater, ref, 인자 전달 중 어떤 모델이 더 명확한가? 오래된 async 결과가 최신 화면 상태를 덮어쓸 가능성은 없는가?

클로저를 값 저장소로 외우면 “왜 예전 값이지?”에서 멈춥니다. 바인딩 참조로 보면 생성 위치, 공유 여부, 실행 지연을 순서대로 추적할 수 있습니다.

Day 7 복습 퀴즈

Quiz1 / 4
Q.var를 쓴 첫 예제의 출력은 무엇일까요?

Post Q&A

오케이징에게 물어보기

자바스크립트 퀴즈북 리마인드 Day 7: 클로저 버그와 오래된 값 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!

0/500

포스트 목록

/study/javascript-quizbook
파일 7개, 폴더 0개
자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을 수 없을까자바스크립트 퀴즈북 리마인드 Day 2: 같은 값인지 묻는 네 가지 방법자바스크립트 퀴즈북 리마인드 Day 3: + 연산자와 ToPrimitive 흐름자바스크립트 퀴즈북 리마인드 Day 4: 프로퍼티 descriptor와 freeze의 경계자바스크립트 퀴즈북 리마인드 Day 5: 참조, 복사, Map/Set의 메모리 감각자바스크립트 퀴즈북 리마인드 Day 6: 스코프 체인과 호이스팅자바스크립트 퀴즈북 리마인드 Day 7: 클로저 버그와 오래된 값

같은 섹션의 대표 이미지

7 posts · latest first
클로저가 생성 위치의 바인딩을 기억해 나중 실행에서 오래된 값을 읽을 수 있음을 보여주는 다이어그램
Study26. 06. 29.

자바스크립트 퀴즈북 리마인드 Day 7: 클로저 버그와 오래된.

클로저가 값 복사가 아니라 렉시컬 환경 참조라는 점을 stale closure, 루프 콜백, React 이벤트 리뷰 관점에서 정리합니다.

26. 06. 29.SEOJing
스코프 체인과 호이스팅 관계를 단순화한 다이어그램
Study26. 06. 28.

자바스크립트 퀴즈북 리마인드 Day 6: 스코프 체인과 호이스팅.

스코프 체인, 클로저, var/let/const 호이스팅 차이를 콜백·상태 버그 리뷰 관점에서 짧게 정리합니다.

26. 06. 28.SEOJing
JavaScript 객체 참조와 Map, Set, WeakMap의 참조 구조를 비교한 다이어그램
Study26. 06. 27.

자바스크립트 퀴즈북 리마인드 Day 5: 참조, 복사,.

객체 참조와 얕은 복사, Object와 Map/Set의 차이, WeakMap/WeakSet이 필요한 메모리 상황을 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 06. 27.SEOJing
JavaScript 프로퍼티 descriptor가 data descriptor와 accessor descriptor로 갈라지는 구조 다이어그램
Study26. 06. 26.

자바스크립트 퀴즈북 리마인드 Day 4: 프로퍼티.

객체 프로퍼티를 key/value가 아니라 descriptor로 읽는 법, getter/setter와 defineProperty, preventExtensions/seal/freeze의 경계를 코드 리뷰 관점에서 정리합니다.

26. 06. 26.SEOJing
객체가 ToPrimitive를 거쳐 + 연산자의 문자열 연결 또는 숫자 덧셈으로 갈라지는 흐름 다이어그램
Study26. 06. 25.

자바스크립트 퀴즈북 리마인드 Day 3: + 연산자와.

객체가 원시값으로 바뀌는 ToPrimitive 흐름, + 연산자의 문자열 연결과 숫자 덧셈 분기, Symbol.toPrimitive가 코드 리뷰에서 왜 중요한지 정리합니다.

26. 06. 25.SEOJing
==, ===, Object.is, SameValueZero의 차이를 정리한 JavaScript equality 다이어그램
Study26. 06. 24.

자바스크립트 퀴즈북 리마인드 Day 2: 같은 값인지 묻는 네.

==, ===, Object.is, SameValueZero가 각각 어떤 비교 알고리즘을 쓰는지 정리하고, includes와 indexOf, Map/Set 키 비교에서 생기는 프론트엔드 리뷰 포인트를 잡습니다.

26. 06. 24.SEOJing
JavaScript Number, safe integer, NaN, -0, BigInt를 한 장으로 정리한 다이어그램
Study26. 06. 23.

자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을.

자바스크립트의 Number가 왜 정수처럼 보여도 부동소수점 모델 위에서 움직이는지, NaN과 -0, safe integer, BigInt를 프론트엔드 코드 리뷰 관점에서 다시 정리합니다.

26. 06. 23.SEOJing