🌐 globalThis 학습 노트

const store = globalThis as unknown as GlobalStore;
이 코드 한 줄에 들어 있는 세 가지를 정리합니다.
globalThis가 뭐지?as unknown as는 왜 두 번 적나?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 Worker | self |
"양쪽에서 동작하는 코드" 를 짜려면 매번 환경 체크가 필요해 불편했어요. 그래서 어디서든 동일하게 동작하는 이름 으로 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,JSONglobalThis← 같은 카테고리
이런 것들은 "Node.js 만의 것" 입니다 (= 서버에서만):
process,Buffer,__dirname,fs(파일 시스템)
이런 것들은 "Next.js 가 추가로 준 것" 입니다 (= Next.js 프로젝트에서만):
NextResponse,useRouter,notFound,cookies(fromnext/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 의 타입 검사를 만족시키기 위한 형식적인 절차일 뿐 — 실제 값은 처음부터 끝까지 같은 객체 입니다.

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