개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

스코프 — 변수가 살아있는 범위

2025년 8월 21일
JavaScript스코프varletconst

분명히 선언한 변수인데 왜 없다고 할까요? 함수 안에서 선언한 변수를 바깥에서 쓰려 할 때 ReferenceError가 납니다. JavaScript가 변수의 유효 범위를 엄격하게 구분하기 때문입니다.

스코프란 무엇인가

스코프는 변수에 접근할 수 있는 유효 범위입니다. 변수가 어디서 선언되었는지에 따라, 코드의 어느 영역에서 그 변수를 읽고 쓸 수 있는지가 결정됩니다. 스코프가 없다면 모든 변수가 프로그램 전체에 노출되고, 이름 충돌과 예기치 않은 값 변경이 끊임없이 발생할 것입니다.

전역과 지역 스코프 — 선언 위치가 기준

// 전역 스코프: 파일 최상단에 선언
const appName = "MyApp";

function greet() {
  // 지역 스코프: 함수 안에서 선언
  const message = "안녕하세요";
  console.log(message); // "안녕하세요" — 접근 가능
  console.log(appName); // "MyApp" — 전역 변수는 안에서 접근 가능
}

greet();
console.log(appName); // "MyApp" — 전역이므로 어디서든 접근 가능
console.log(message); // ReferenceError: message is not defined

전역 스코프에 선언된 변수는 어디서든 읽을 수 있습니다. 반면 함수 안에 선언된 변수는 그 함수 안에서만 살아있고, 함수가 끝나면 사라집니다.

스코프 단위 — var는 함수, let·const는 블록

var는 함수 단위로 스코프가 만들어집니다. if, for 같은 블록 {}은 var에게 새로운 스코프를 만들지 않습니다.

function testVar() {
  if (true) {
    var x = 10; // if 블록 안이지만 함수 스코프
  }
  console.log(x); // 10 — 블록 밖에서도 접근됨
}

function testLet() {
  if (true) {
    let y = 20; // 블록 스코프
    const z = 30; // 블록 스코프
  }
  console.log(y); // ReferenceError
  console.log(z); // ReferenceError
}

let과 const는 {}로 감싸인 블록 단위로 스코프가 생깁니다. for 루프에서 특히 차이가 도드라집니다.

// var: 루프가 끝난 뒤에도 i가 남아있음
for (var i = 0; i < 3; i++) {}
console.log(i); // 3

// let: 루프 블록 안에서만 유효
for (let j = 0; j < 3; j++) {}
console.log(j); // ReferenceError

var / let / const 비교

varletconst
스코프함수 스코프블록 스코프블록 스코프
재선언가능불가능불가능
재할당가능가능불가능
호이스팅선언+undefined 초기화선언만(TDZ 진입)선언만(TDZ 진입)

let과 const는 호이스팅이 되기는 하지만, 선언 줄에 도달하기 전까지 TDZ 구간에 있어 접근하면 ReferenceError가 발생합니다.

호이스팅(Hoisting): 코드 실행 전에 변수·함수 선언을 스코프 맨 위로 끌어올리는 JavaScript 엔진의 동작. var는 선언과 함께 undefined로 초기화되고, let·const는 선언만 등록된 채 TDZ에 진입합니다.

TDZ(Temporal Dead Zone): 변수 선언이 호이스팅된 시점부터 실제 선언 코드가 실행되는 시점까지의 구간. 이 구간에서 변수에 접근하면 에러가 발생합니다.

렉시컬 스코프 — 정의 위치가 기준

JavaScript는 렉시컬 스코프(Lexical Scope) 방식을 따릅니다. 함수가 어디서 호출되었는지가 아니라, 어디서 정의되었는지를 기준으로 스코프가 결정됩니다.

const name = "전역";

function outer() {
  const name = "outer";

  function inner() {
    // inner는 outer 안에서 정의되었으므로
    // outer의 name에 접근할 수 있음
    console.log(name); // "outer"
  }

  return inner;
}

const fn = outer();
fn(); // "outer" — 호출 위치(전역)가 아닌 정의 위치(outer 내부)가 기준

fn을 전역에서 호출했지만, inner가 정의된 곳이 outer 안이므로 outer의 name을 참조합니다.

스코프 체인 — 안에서 바깥으로 탐색

변수를 찾을 때 JavaScript 엔진은 현재 스코프부터 시작해 바깥 스코프로 단계적으로 올라갑니다. 이를 스코프 체인이라고 합니다.

┌─────────────────────────────────┐
│ Global Scope                    │
│  const appName = "MyApp"        │
│                                 │
│  ┌──────────────────────────┐   │
│  │ outer scope              │   │
│  │  const name = "outer"    │   │
│  │                          │   │
│  │  ┌──────────────────┐    │   │
│  │  │ inner scope      │    │   │
│  │  │  search: name    │    │   │
│  │  │  → not in inner  │    │   │
│  │  │  → found: outer  │    │   │
│  │  └──────────────────┘    │   │
│  └──────────────────────────┘   │
└─────────────────────────────────┘

search order: inner → outer → global → ReferenceError

안쪽 스코프에서 변수를 발견하면 탐색을 멈춥니다. 가장 가까운 스코프의 변수가 우선합니다.

전역 오염 문제

var를 사용하면 의도치 않게 전역 변수가 늘어나고, 같은 이름의 변수가 충돌하는 전역 오염 문제가 발생합니다.

// a.js (라이브러리)
var count = 0;
function increment() {
  count++;
}

// b.js (내 코드)
var count = 100; // 같은 이름으로 덮어씀

increment();
console.log(count); // 100? 아니면 1? — 예측 불가

두 파일이 같은 전역 스코프를 공유하기 때문에 count가 충돌합니다. let/const로 바꿔도 같은 전역 스코프라면 동일한 문제가 생깁니다.

요약

개념핵심
전역 스코프파일 최상단 선언, 어디서든 접근 가능
지역 스코프선언된 함수·블록 안에서만 유효
렉시컬 스코프호출 위치가 아닌 정의 위치 기준
스코프 체인안쪽에서 바깥쪽으로 단계적 탐색
키워드스코프재선언재할당호이스팅
var함수가능가능undefined로 초기화
let블록불가가능TDZ
const블록불가불가TDZ

var 대신 let/const를 사용하고 렉시컬 스코프의 동작 방식을 이해하면, 변수 관련 버그의 상당수를 사전에 차단할 수 있습니다.

← 이전 글값과 참조 — 변수의 정체와 복사 전략
다음 글 →실행 컨텍스트 — 코드가 실행되는 환경