메인 콘텐츠로 바로가기
노트 목록

🌐 globalThis 학습 노트

김성박660
목차7
const store = globalThis as unknown as GlobalStore;

이 코드 한 줄에 들어 있는 세 가지를 정리합니다.

  1. globalThis 가 뭐지?
  2. as unknown as 는 왜 두 번 적나?
  3. globalThis 는 Next.js 기능인가, 그냥 JavaScript 인가?

1. globalThis 가 뭐지?

한 줄 정의

"코드가 어디 있든 누구나 같은 이름으로 접근할 수 있는 단 하나의 큰 객체."

우리가 이미 본 적이 있다

브라우저에서 자주 보던 window 와 같은 것입니다.

window.alert("hi"); window.localStorage.setItem("k", "v");

window 안에 alert, localStorage, document, fetch … 가 다 들어 있어요. 사실 브라우저에서는

window === globalThis // true

같은 객체를 두 이름으로 가리킵니다.

서버(Node.js) 에는 브라우저 창이 없으니 window 가 없어요. 대신 표준 이름인 globalThis 를 씁니다.

환경별 옛 이름

globalThis 가 만들어지기 전에는 환경마다 이름이 달랐습니다.

환경옛 이름
브라우저window
Node.js (서버)global
Web Workerself

"양쪽에서 동작하는 코드" 를 짜려면 매번 환경 체크가 필요해 불편했어요. 그래서 어디서든 동일하게 동작하는 이름 으로 globalThis 가 표준이 됐습니다.


2. 왜 우리 코드에 필요한가? — 방과 로비 비유

lib/books.ts 같은 모듈 파일을 각 방 이라고 생각해요. 그 방 안의 변수 — const books = [...], let nextId = 4 — 는 방에 둔 가구입니다.

Next.js dev 서버는 파일을 저장할 때마다 그 방을 새로 꾸미고 다시 들여보내요(=hot-reload). 가구가 다 새 걸로 바뀝니다. 즉 books 와 nextId 가 초기값으로 리셋돼요. 사용자가 폼에서 등록한 책이 날아갑니다.

globalThis 는 건물의 1층 로비 입니다. 어느 방을 다시 꾸미든 로비는 건드리지 않아요. 우리 책 데이터를 1층 로비에 놓아 두면, 방이 아무리 새로 꾸며져도 데이터는 그대로 남습니다.

// 방 안 가구 — hot-reload 때 사라짐 const books = [...]; // 1층 로비에 둔 짐 — hot-reload 때 살아남음 globalThis.__books = [...];

이게 우리가 globalThis 를 쓰는 핵심 이유입니다.


3. const store = globalThis as unknown as GlobalStore; 한 줄 분해

3-1. 결국 뭘 만드는가?

const store = globalThis as unknown as GlobalStore; console.log(store === globalThis); // true — 똑같은 객체!

store 는 globalThis 와 완전히 같은 객체 입니다. 새 객체를 만들지도, 데이터를 복사하지도 않아요. 그냥 같은 객체에 이름표 하나를 더 붙인 것 뿐.

비유: 똑같은 사람한테 "사장님" 이라는 별칭과 "철수씨" 라는 본명이 같이 있는 거예요.

그럼 왜 굳이 이름 두 개? 답이 as 에 있습니다.

3-2. 부분별로 쪼개기

const store = globalThis as unknown as GlobalStore; └──┬────┘ └─┬───────┘ └────┬───┘ └─────┬─────┘ ① ② ③ ④
부분의미
① const store = ...변수 선언. 오른쪽 값에 store 라는 이름을 붙임
② globalThis이 자리에 들어가는 실제 값 (전역 객체)
③ as unknown"TypeScript 한테는 이걸 unknown 타입처럼 보여줘"
④ as GlobalStore"이번엔 GlobalStore 타입처럼 보여줘"

🔑 중요: as 는 컴파일러한테만 보이는 라벨링 입니다. 실제 값은 1 비트도 안 바뀝니다. 메모리에 있는 globalThis 객체는 그대로예요. 단지 TypeScript 가 이 객체를 어떻게 "이해할지" 만 달라집니다.

3-3. 왜 두 번 as 인가?

이렇게 한 번만 쓰면 좋겠죠?

const store = globalThis as GlobalStore; // ❌ 빨간 줄!

하지만 TypeScript 가 에러를 냅니다:

Conversion of type 'typeof globalThis' to type 'GlobalStore' may be a mistake because neither type sufficiently overlaps with the other.

번역: "이 변환은 실수일 수 있어요. 두 타입이 너무 다릅니다."

왜 너무 다른가?

  • globalThis 의 타입: 수백 개의 필드 (alert, document, setTimeout, fetch, process …)
  • GlobalStore 의 타입: 딱 두 개의 필드 (__books?, __nextId?)

TypeScript 는 "이거 너무 갭이 큰데, 진짜 의도한 게 맞아요?" 라며 거부합니다. 안전 장치예요.

3-4. unknown 을 거치는 게 정답

TypeScript 에는 두 가지 만능 타입 이 있습니다:

타입뜻
any"아무 타입이나 OK, 검사 끄겠음" (위험)
unknown"무엇인지 모름, 그래서 일단 안전"

특징:

  • 모든 타입은 unknown 으로 캐스팅 가능 (받아주는 쪽이 "뭐든 받겠다" 한 거니까)
  • unknown 은 어떤 타입으로든 캐스팅 가능 (반대 방향도 허용됨, 다만 위험은 본인 책임)

그래서 직접 변환이 막혀 있을 때 unknown 을 중간 정거장 처럼 끼워 넣습니다.

globalThis ──❌ 직접 변환 거절 ──> GlobalStore globalThis ──> unknown ──> GlobalStore ✅ 허용 ✅ 허용

비유: 출입국 심사 같아요. A 국가 여권으로 곧장 B 국가에 못 가는 사람이 있다면, 일단 면세 구역(unknown) 으로 들어가서 거기서 다른 신분을 새로 받아 나가는 식.

3-5. 한마디로

const store = globalThis as unknown as GlobalStore;

= "실제 값은 globalThis 그대로 두되, TypeScript 한테는 이걸 GlobalStore 모양인 척 보여줘. 갭이 너무 커서 직접은 안 되니 unknown 한 번 거치겠음."

그 결과:

  • store 라고 적으면 TypeScript 가 __books, __nextId 두 필드만 보이는 객체로 인식 (편하게 자동완성됨)
  • 실행될 때는 그냥 평범한 globalThis 접근이라 빠르고 가벼움

4. globalThis 는 Next.js 가 만든 게 아닙니다

이런 의문이 들 수 있어요: "이거 Next.js 가 제공하는 전역 변수 아닌가?"

아닙니다. JavaScript 표준 에 들어 있는 이름이에요.

어디서 왔나

2020년 발표된 ECMAScript 2020 (ES2020) 라는 JavaScript 표준 명세에 추가됐습니다. Next.js 와 무관하게 모든 JS 환경에 들어 있어요.

어디서 쓸 수 있나

  • ✅ Node.js (Next.js 서버 코드)
  • ✅ 브라우저 (Next.js 클라이언트 코드, 또는 일반 Vite/CRA 프로젝트)
  • ✅ Deno, Bun, Cloudflare Workers
  • ✅ node 명령으로 직접 실행하는 스크립트

즉 Next.js 와 무관하게 그냥 JavaScript 자체에 있는 기능 입니다. 똑같은 줄을 Vite 프로젝트에서도, node script.js 로 돌리는 스크립트에서도 똑같이 쓸 수 있어요.

비슷한 것들과 비교

이런 것들이 "JavaScript 표준" 입니다 (= 어디서든 쓸 수 있음):

  • console, Math, Array, Promise, Date, JSON
  • globalThis ← 같은 카테고리

이런 것들은 "Node.js 만의 것" 입니다 (= 서버에서만):

  • process, Buffer, __dirname, fs (파일 시스템)

이런 것들은 "Next.js 가 추가로 준 것" 입니다 (= Next.js 프로젝트에서만):

  • NextResponse, useRouter, notFound, cookies (from next/headers)

5. 실험으로 직접 확인

scripts/test-books.ts 에 한 줄씩 끼워 보세요.

5-1. store === globalThis 같은 객체인가?

type GlobalStore = { __books?: unknown; __nextId?: number }; const store = globalThis as unknown as GlobalStore; console.log("같은 객체?:", store === globalThis); // true store.__nextId = 999; // @ts-ignore — 일부러 raw 접근 console.log("globalThis 로 읽기:", globalThis.__nextId); // 999

store 에 쓴 값이 globalThis 에서도 보입니다. 같은 객체 라는 증거.

5-2. globalThis 는 표준이라는 증거

console.log("typeof:", typeof globalThis); // "object" console.log("=== global?:", globalThis === global); // true (Node.js)

브라우저 개발자도구 콘솔에서도:

> globalThis === window < true > typeof globalThis < "object"

같은 이름이 양쪽에서 다 동작합니다. 표준의 힘.


6. 정리

의문답
globalThis 가 뭐지?JavaScript 표준 (ES2020) 의 전역 객체. 브라우저의 window, Node 의 global 과 같은 것. 환경 구분 없이 동일한 이름.
왜 우리한테 필요한가?Next.js dev 의 hot-reload 가 모듈 변수를 리셋해도 globalThis 의 속성은 살아남기 때문에, 거기 데이터를 보관하면 등록한 책이 안 날아감.
as unknown as 왜 두 번?TypeScript 가 차이가 큰 두 타입의 직접 캐스팅을 거부함. unknown 을 중간 정거장으로 끼우면 통과.
store === globalThis ?True. 새 객체가 아니라 같은 객체의 별칭. as 는 컴파일러용 라벨일 뿐, 값은 안 바뀜.
Next.js 가 준 거?아님. 그냥 표준 JavaScript. 어디서든 사용 가능.

한 줄 결론

globalThis 는 어디서든 동일한 이름으로 접근할 수 있는 표준 전역 객체. 모듈 변수와 달리 hot-reload 때도 살아남는 성질을 이용해 dev 환경에서 데이터를 보존했고, as unknown as 두 단계 캐스팅은 TypeScript 의 타입 검사를 만족시키기 위한 형식적인 절차일 뿐 — 실제 값은 처음부터 끝까지 같은 객체 입니다.

댓글

불러오는 중...

댓글을 작성하려면 이 필요합니다.