junyeokk
Blog
JavaScript·2026. 01. 27

모듈 스코프 vs 함수 스코프 - 실행 시점의 차이

이 문서를 읽기 전에 [[scope|스코프(Scope)]]를 먼저 읽는 것을 권장한다.

개요

JavaScript에서 변수를 어디에 선언하느냐에 따라 실행 시점이 달라진다. 특히 ES 모듈 시스템에서 모듈 스코프에 선언된 코드는 모듈이 처음 import될 때 한 번만 실행된다. SPA(Single Page Application)에서 이 차이를 이해하지 못하면 "간헐적으로 발생하는" 버그를 만들 수 있다.


모듈 스코프란

ES 모듈에서 함수 외부에 선언된 변수는 모듈 스코프에 속한다:

javascript
// myModule.js
const INIT_TIME = Date.now(); // 모듈 스코프

export function getInitTime() {
  return INIT_TIME;
}

INIT_TIME은 이 모듈이 처음 import될 때 한 번 계산되고, 이후로는 그 값이 유지된다.

javascript
// app.js
import { getInitTime } from './myModule.js';

console.log(getInitTime()); // 1706345678901
// ... 10초 후
console.log(getInitTime()); // 1706345678901 (같은 값)

10초가 지나도 같은 값이다. 모듈이 다시 로드되지 않기 때문이다.


함수 스코프와의 차이

함수 내부에 선언하면 함수가 호출될 때마다 실행된다:

javascript
// myModule.js
export function getCurrentTime() {
  const NOW = Date.now(); // 함수 스코프
  return NOW;
}
javascript
// app.js
import { getCurrentTime } from './myModule.js';

console.log(getCurrentTime()); // 1706345678901
// ... 10초 후
console.log(getCurrentTime()); // 1706345688901 (다른 값)

호출할 때마다 새로운 값을 반환한다.


SPA에서 흔히 발생하는 실수

React, Vue 같은 SPA 프레임워크에서 페이지 이동은 실제 새로고침이 아니다. 컴포넌트가 마운트/언마운트될 뿐, 모듈은 다시 로드되지 않는다.

문제가 되는 코드

typescript
// ShotPage.tsx
const SHOT_TIME = getShotTimeFromURL(); // 모듈 스코프 - 한 번만 실행

export function ShotPage() {
  const [time, setTime] = useState(SHOT_TIME);
  // ...
}

getShotTimeFromURL()이 현재 URL을 읽어서 촬영 시간을 반환한다고 가정하자.

  1. /event/a로 접속 → 모듈 로드, SHOT_TIME = 4
  2. /event/b로 이동 → 모듈은 이미 로드됨, SHOT_TIME = 4 (그대로!)
  3. event/b는 10초여야 하는데 4초로 동작

올바른 코드

typescript
// ShotPage.tsx
export function ShotPage() {
  const SHOT_TIME = getShotTimeFromURL(); // 함수 스코프 - 렌더링마다 실행
  const [time, setTime] = useState(SHOT_TIME);
  // ...
}

컴포넌트 내부에 선언하면 컴포넌트가 마운트될 때마다 실행된다.


언제 모듈 스코프를 써야 하나

모듈 스코프는 정말 한 번만 초기화해도 되는 값에 사용한다:

javascript
// 상수
const API_BASE_URL = 'https://api.example.com';

// 싱글톤 인스턴스
const logger = new Logger();

// 무거운 초기화 (한 번만 하고 싶을 때)
const heavyData = loadHeavyData();

반면, 런타임에 바뀔 수 있는 값에 의존하는 경우 모듈 스코프에 두면 안 된다:

javascript
// 나쁜 예 - URL은 런타임에 바뀔 수 있음
const CONFIG = getConfigFromURL();

// 나쁜 예 - 로그인 상태는 런타임에 바뀔 수 있음
const USER = getCurrentUser();

요약

스코프실행 시점적합한 용도
모듈 스코프import 시 한 번상수, 싱글톤, 무거운 초기화
함수 스코프함수 호출마다런타임 의존 값, 동적 계산

변수 선언 위치는 단순한 코드 스타일이 아니라 실행 시점과 생명주기를 결정한다.