스코프, 호이스팅, 클로저
렉시컬 스코프, 스코프 체이닝, 실행 타이밍 오해를 정리합니다.
학습 목표
이 문서는 문법 암기보다 다음 판단을 빠르게 만들기 위해 작성되었습니다.
첫째, 어떤 값이 어떤 스코프에서 유효한지
둘째, 언제 접근 가능한 상태인지
셋째, 클로저가 어떤 경로의 상태를 유지하는지 분리해 인식할 수 있어야 합니다.
클로저 자체는 익숙한 개념일 수 있으나, JavaScript는 호이스팅,스코프 체인의 실행 규칙이 달라서
오류가 발생하는 위치와 대응 방식이 다릅니다.
이 문서는 그 규칙을 기준으로 설명합니다.
핵심 용어 정의
스코프는 어떤 식별자(변수, 함수, 클래스명 등)가 어느 구간에서 유효한지(읽고 쓸 수 있는지) 정하는 규칙입니다.
렉시컬 스코프는 코드가 작성된 위치를 기준으로 상위 스코프를 정하는 규칙입니다.
스코프 체이닝은 현재 스코프에서 이름을 찾지 못했을 때 상위 스코프로 올라가며 탐색하는 연결 규칙입니다.
호이스팅은 변수·함수 선언이 실행 전 스코프에 등록되는 준비 단계이고, 값의 접근 가능 여부는 초기화 타이밍에서 달라집니다.
선언 위치가 실행 규칙을 정합니다
JavaScript에서 상태 참조의 기준은 호출 위치보다 선언 위치에 더 가깝습니다.
함수가 어디서 호출되었는가는 실행 타이밍에 영향을 주고,
어느 스코프에 묶였는가는 참조 경로와 접근 가능 범위를 결정합니다.
이는 렉시컬 스코프의 핵심입니다.
함수는 선언될 때 외부 스코프와의 연결이 고정되고, 실행 시에는 그 연결을 따라 값을 찾습니다.
스코프 체이닝은 찾기 규칙입니다.
현재 스코프에서 이름을 찾지 못하면 상위 스코프를 따라가고, 결국 전역까지 탐색합니다.
실행 컨텍스트를 간단히 이해하기
실행 컨텍스트를 깊게 외우지 않아도 됩니다.
작은 관점으로는 현재 실행 스택과 현재 스코프의 저장 구조의 결합으로 이해하면 충분합니다.
중요한 건 두 가지입니다.
첫째, 어떤 바인딩이 지금 단계에서 준비되어 있는지.
둘째, 그 바인딩이 초기화되어 접근 가능한지입니다.
호이스팅은 “끌어올림”이 아니라 타이밍 구분입니다
호이스팅은 선언 자체를 실행보다 먼저 준비하는 관점이므로,
코드의 위계만으로는 판단이 끝나지 않습니다.
let/const는 스코프에 존재를 먼저 알리지만 접근은 초기화 시점 이전에는 불가합니다.
그래서 선언되어 보이더라도 ReferenceError가 나는 구간이 생깁니다.
실무에서는 이 값이 지금 유효한가를 먼저 확인해야 합니다.
의도하지 않은 접근은 조기에 실패해야 하므로, 이 규칙이 코드리뷰에서 버그를 줄이는 핵심 축이 됩니다.
클로저는 값 복사가 아니라 바인딩 캡처입니다
클로저는 “바깥 함수의 변수를 참조하는 함수”입니다.
여기서 중요한 건 클로저가 객체나 함수를 복사해 들고 다니는 구조가 아니라,
선언된 바인딩을 캡처해 유지한다는 점입니다.
이 특성은 전역 상태 의존을 줄이는 장점이 있습니다.
반대로 상태가 기대보다 길게 유지되면, 이벤트 타이밍이 바뀔 때 어떤 경로가 어떤 값을 읽는지 추적이 어려워질 수 있습니다.
클로저 위험을 줄이려면
- 공유 대상의 경계를 먼저 정의합니다.
- 갱신 기준을 문서화해 “최신 반영의 우선순위”를 고정합니다.
- 한 번의 캡처가 여러 경로에서 오래 살아있지 않도록 생존 범위를 제어합니다.
실무 적용 체크
- 동일 이름이 여러 스코프에서 정의될 때, 실제로 읽히는 경로가 무엇인지 추적합니다.
- 함수/변수 접근이 실패하거나 예기치 않게 늦게 반영되는 구간을
선언 위치와초기화 시점으로 분리해 점검합니다. - 클로저 사용 시 “누가, 언제, 어느 값의 변경을 계속 보고 있는가”를 먼저 정의합니다.