서론
클로저를 공부하면서 "렉시컬 스코프"라는 개념이 나왔는데, 사실 스코프 자체를 제대로 정리한 적이 없었음
개발하다 보면 "이 변수 왜 여기서 접근이 안 되지?", "같은 이름의 변수인데 왜 값이 다르지?" 같은 상황을 겪게 되는데, 이게 전부 스코프 때문이었음
면접에서도 "스코프가 뭔가요?", "스코프 체인이 어떻게 동작하나요?" 같은 질문이 자주 나온다고 해서, 이번에 스코프와 스코프 체인을 처음부터 정리해봄
본론
1. 스코프(Scope)란?
스코프는 변수에 접근할 수 있는 범위를 의미함
쉽게 말해서, "이 변수를 어디서 쓸 수 있는가?"를 결정하는 규칙
function greeting() {
const message = '안녕하세요'; // message는 greeting 함수 안에서만 접근 가능
console.log(message); // ✅ '안녕하세요'
}
greeting();
console.log(message); // ❌ ReferenceError: message is not defined
message는 greeting 함수 안에서 선언됐기 때문에, 함수 밖에서는 접근할 수 없음. 이게 스코프의 기본 개념
2. 스코프의 종류
JavaScript에는 크게 3가지 스코프가 있음
2-1. 전역 스코프 (Global Scope)
코드의 가장 바깥에서 선언된 변수. 어디서든 접근 가능
const appName = '가챠 피커'; // 전역 스코프
function printApp() {
console.log(appName); // ✅ 접근 가능
}
function printAgain() {
console.log(appName); // ✅ 여기서도 접근 가능
}
printApp(); // '가챠 피커'
printAgain(); // '가챠 피커'
편하긴 한데, 전역 변수를 남발하면 이름이 충돌하거나 예상치 못한 곳에서 값이 바뀌는 문제가 생김
2-2. 함수 스코프 (Function Scope)
함수 안에서 선언된 변수는 함수 안에서만 접근 가능. var는 함수 스코프를 따름
function calculate() {
var result = 42;
console.log(result); // ✅ 42
}
calculate();
console.log(result); // ❌ ReferenceError
// var의 특이한 점: 블록을 무시함
if (true) {
var leaked = '나는 블록 밖에서도 살아있음';
}
console.log(leaked); // ✅ '나는 블록 밖에서도 살아있음' — 블록을 무시!
var는 if, for 같은 블록({})을 무시하고 함수 단위로만 스코프가 나뉨. 이게 많은 버그의 원인이 됐음
2-3. 블록 스코프 (Block Scope)
let과 const는 블록 스코프를 따름. {}로 감싸진 영역 안에서만 유효
if (true) {
let blockVar = '블록 안에서만';
const blockConst = '나도 블록 안에서만';
console.log(blockVar); // ✅
console.log(blockConst); // ✅
}
console.log(blockVar); // ❌ ReferenceError
console.log(blockConst); // ❌ ReferenceError
for (let i = 0; i < 3; i++) {
// i는 이 블록 안에서만 존재
}
console.log(i); // ❌ ReferenceError
이게 let/const가 var보다 안전한 이유. 블록 밖으로 변수가 새어나가지 않음
비교 정리
| 키워드 | 스코프 | 블록 밖 접근 | 재선언 | 재할당 |
|---|---|---|---|---|
var |
함수 스코프 | ✅ 가능 | ✅ 가능 | ✅ 가능 |
let |
블록 스코프 | ❌ 불가 | ❌ 불가 | ✅ 가능 |
const |
블록 스코프 | ❌ 불가 | ❌ 불가 | ❌ 불가 |
3. 렉시컬 스코프 (Lexical Scope) = 정적 스코프
JavaScript는 렉시컬 스코프(정적 스코프) 를 사용함. 함수가 어디서 호출되었는지가 아니라, 어디서 선언되었는지에 따라 상위 스코프가 결정됨
const x = 10;
function foo() {
console.log(x);
}
function bar() {
const x = 20;
foo(); // foo는 bar 안에서 호출되었지만...
}
bar(); // 10 — foo가 선언된 위치(전역)의 x = 10을 참조
foo가 bar 안에서 호출됐으니 x = 20을 참조할 것 같지만, JavaScript는 선언 위치 기준이라 전역의 x = 10을 참조함
이걸 모르면 "왜 20이 아니라 10이 나오지?" 하고 헷갈릴 수 있음. 면접에서도 자주 나오는 패턴
4. 스코프 체인 (Scope Chain)
스코프 체인은 변수를 찾을 때 안쪽에서 바깥쪽으로 순서대로 탐색하는 메커니즘
const a = '전역';
function outer() {
const b = 'outer';
function inner() {
const c = 'inner';
console.log(c); // ✅ inner (자기 자신에서 찾음)
console.log(b); // ✅ outer (한 단계 위에서 찾음)
console.log(a); // ✅ 전역 (두 단계 위에서 찾음)
}
inner();
}
outer();
inner 함수에서 변수를 찾는 순서:
- inner 스코프 → 있으면 사용
- outer 스코프 → 있으면 사용
- 전역 스코프 → 있으면 사용
- 어디에도 없으면 →
ReferenceError
이 탐색 경로가 마치 체인처럼 연결되어 있어서 스코프 체인이라고 부름
스코프 체인의 핵심: 단방향
function parent() {
const secret = '비밀';
function child() {
const toy = '장난감';
console.log(secret); // ✅ 자식 → 부모 접근 가능
}
child();
console.log(toy); // ❌ 부모 → 자식 접근 불가!
}
parent();
- 안쪽(자식) → 바깥쪽(부모): 접근 가능
- 바깥쪽(부모) → 안쪽(자식): 접근 불가
스코프 체인은 안에서 밖으로만 탐색함. 역방향은 안 됨
5. 실전에서 자주 마주치는 스코프 문제
5-1. 변수 섀도잉 (Variable Shadowing)
같은 이름의 변수가 서로 다른 스코프에 있으면, 안쪽 변수가 바깥 변수를 가림
const name = '전역 그린티';
function greet() {
const name = '로컬 그린티'; // 전역의 name을 가림(섀도잉)
console.log(name); // '로컬 그린티'
}
greet();
console.log(name); // '전역 그린티' — 전역 변수는 영향 없음
의도적으로 쓸 수도 있지만, 실수로 같은 이름을 쓰면 디버깅이 어려워지니 주의
5-2. for 루프와 var의 함정 (클로저 글에서도 다뤘던 것)
// ❌ var — 함수 스코프라서 i가 하나만 존재
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 출력: 3, 3, 3
// ✅ let — 블록 스코프라서 매 반복마다 새로운 i
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 출력: 0, 1, 2
이 문제의 근본 원인이 바로 var의 함수 스코프 vs let의 블록 스코프 차이
5-3. React에서의 스코프
gacha-picker 프로젝트에서 컴포넌트를 만들 때도 스코프를 의식하게 됐음
function GachaMachine() {
const [names, setNames] = useState([]); // 이 컴포넌트 안에서만 유효
const handleDraw = () => {
// handleDraw는 names를 스코프 체인으로 접근
const randomIndex = Math.floor(Math.random() * names.length);
console.log(names[randomIndex]);
};
return <button onClick={handleDraw}>뽑기!</button>;
}
handleDraw 함수가 names에 접근할 수 있는 이유가 바로 스코프 체인(+ 클로저) 덕분
6. 면접 예상 질문 & 답변
Q1. 스코프란 무엇인가요?
스코프는 변수에 접근할 수 있는 유효 범위입니다. JavaScript에는 전역 스코프, 함수 스코프, 블록 스코프가 있으며,
var는 함수 스코프를,let과const는 블록 스코프를 따릅니다.
Q2. 스코프 체인이란?
변수를 참조할 때, 현재 스코프에서 시작해 상위 스코프로 단계적으로 탐색해 나가는 메커니즘입니다. 안쪽에서 바깥쪽 방향으로만 탐색하며, 전역 스코프까지 올라가도 찾지 못하면 ReferenceError가 발생합니다.
Q3. 렉시컬 스코프란?
함수가 호출된 위치가 아니라 선언된 위치에 의해 상위 스코프가 결정되는 방식입니다. JavaScript는 렉시컬 스코프를 사용하며, 이를 정적 스코프라고도 합니다.
Q4. var와 let의 스코프 차이는?
var는 함수 스코프로 블록({})을 무시하고 함수 단위로 스코프가 나뉩니다.let은 블록 스코프로if,for등의 블록 안에서만 유효합니다. 이 차이 때문에 반복문에서var를 쓰면 클로저와 결합해 의도치 않은 동작이 발생할 수 있습니다.
Q5. 변수 섀도잉이란?
내부 스코프에서 외부 스코프와 같은 이름의 변수를 선언하면, 내부 변수가 외부 변수를 가리는 현상입니다. 스코프 체인에서 가장 가까운 스코프의 변수가 우선적으로 참조되기 때문에 발생합니다.
결론
스코프는 "변수가 어디서 유효한가"를 결정하는 JavaScript의 핵심 규칙
- 전역 스코프: 어디서든 접근 가능, 하지만 남용하면 위험
- 함수 스코프:
var가 따르는 스코프, 블록을 무시함 - 블록 스코프:
let/const가 따르는 스코프, 더 안전함 - 렉시컬 스코프: 선언 위치 기준으로 상위 스코프 결정
- 스코프 체인: 안쪽 → 바깥쪽으로 변수를 탐색하는 메커니즘
| 개념 | 설명 |
|---|---|
| 전역 스코프 | 코드 어디서든 접근 가능한 최상위 스코프 |
| 함수 스코프 | 함수 내부에서만 유효 (var) |
| 블록 스코프 | 블록({}) 내부에서만 유효 (let, const) |
| 렉시컬 스코프 | 선언 위치 기준으로 스코프 결정 |
| 스코프 체인 | 안쪽 → 바깥쪽으로 변수 탐색 |
| 변수 섀도잉 | 내부 변수가 외부 동명 변수를 가리는 현상 |
클로저를 공부할 때 렉시컬 스코프가 중요하다는 걸 느꼈는데, 이번에 스코프 전체를 정리하면서 var/let/const 차이나 스코프 체인 동작 원리까지 확실하게 이해하게 됨
앞으로 변수를 선언할 때 스코프를 의식하면서, const 위주로 사용하고 필요할 때만 let을 쓰는 습관을 유지해야겠음!
'프론트엔드 > JavaScript' 카테고리의 다른 글
| (JavaScript) var vs let vs const 차이 (TDZ 포함) (0) | 2026.02.09 |
|---|---|
| (JavaScript) 프로토타입과 프로토타입 체인 (0) | 2026.02.06 |
| (JavaScript) 호이스팅(Hoisting) 완벽 정리 (var, let, const, 함수) (0) | 2026.02.02 |
| (JavaScript) 클로저(Closure)란 무엇인가? (0) | 2026.02.01 |
| (JavaScript) 10. this와 실행 컨텍스트 (0) | 2025.05.02 |