@runlot/next
v0.1.0-rc.99
Published
Next.js adapter for Runlot — request-scoped env plus the OpenNext build
Readme
@runlot/next
Next.js 어댑터입니다. 이 문서는 패키지 사용법을 설명합니다.
npm 패키지 이름은
@runlot/next입니다. 예제와 템플릿의 import 지정자도 같습니다. 랜딩의 코드 예시와 템플릿이 쓰는 지정자는runlot/next다 (import { env } from "runlot/next"). 게시할 때 이름을 바꾸는 자리가 여기 하나뿐이도록, 이 패키지 안에서는 자기 이름을 문자열로 쓰지 않는다 — 예외는 빌드가 만드는.open-next/runlot-entry.js의@runlot/next/entry한 줄이다.
표면
import { env, request, waitUntil } from "@runlot/next";
const user = await env.auth.user(); // 요청은 어댑터가 알아서 넘깁니다
const rows = await env.db.exec("select 1");
const url = await env.storage.presign("covers/a.png");
const token = env.MY_SECRET; // 나머지는 문자열이다| 이름 | 무엇 |
|---|---|
| env | 이 요청의 바인딩. 요청 밖에서 읽으면 오류를 냅니다 |
| env.auth.user(request?) | 인자가 없으면 이 요청. signOut 도 같다 |
| request() | 이 요청의 Request |
| waitUntil(p) | 응답 뒤에도 끝까지 도는 일 |
| inRequest() | 지금 요청 안인가. 라이브러리가 갈래를 나눌 때만 |
| wrap(worker) (@runlot/next/entry) | 빌드가 세우는 진입 wrapper |
| buildNext(opts) (@runlot/next/build) | runlot deploy 가 부릅니다 |
env 가 요청 밖에서 오류를 내는 것은 의도된 설계입니다. 모듈 최상단과 빌드 시점 정적 렌더가
그 자리이고, 거기서 조용히 빈 값을 주면 모든 페이지가 로그아웃 상태로
보인다 (docs/auth.md §4 의 같은 문장).
빌드
runlot deploy 가 runlot.json 의 "framework": "next" 를 보고 buildNext 를
부른다. 그 함수가 하는 일:
next build— 사용자의package.jsonscripts 가 아니라 그 프로젝트의 next 바이너리를 직접, standalone 으로.- OpenNext 의 빌드 함수를 직접 부른다 (CLI 도 wrangler 도 거치지 않는다).
설정은
.runlot-next/open-next.config.ts에 우리가 쓴다 — 프로젝트 루트에open-next.config.ts가 있으면 그쪽이 이긴다. .open-next/runlot-entry.js를 세운다.worker.js의 기본 export 를wrap으로 감싸고 나머지 export 는 그대로 통과시킨다.
오버라이드 셋 (docs/nextjs.md §2 의 표):
| 자리 | 우리 값 | 없을 때 |
|---|---|---|
| incrementalCache | env.storage, 키 __next/cache/<buildId>/<key>.<type> | 정적 자산 캐시(읽기 전용) + 한 줄 |
| tagCache | env.db, 표 runlot_next.tags(tag, path, revalidated_at) | no-op + 한 줄 |
| queue | OpenNext 의 memory-queue (WORKER_SELF_REFERENCE 를 쓴다) | — |
| enableCacheInterception | true | — |
wrangler 는 의존이 아닙니다
@opennextjs/cloudflare 의 peerDependencies 에 wrangler 가 있지만, 런타임에
그것을 import 하는 것은 dist/cli/commands/* 뿐이다. 우리가 부르는
dist/cli/build/build.js 는 타입으로만 참조한다. 그래서 이 패키지는 wrangler 를
dependencies 에 두지 않는다 — pnpm 이 peer 를 자동 설치하는 워크스페이스에서는
스토어에 내려와 있을 수 있지만, 우리 코드 경로는 그것을 부르지 않는다.
요청 상태는 globalThis 의 심볼에 둡니다
Symbol.for("runlot.next.store"). 모듈 인스턴스가 하나라는 보장이 없어서다:
OpenNext 의 worker.js 는 이미 묶인 번들이라 그 안에 이 패키지의 사본이 있고,
그 곁에 세우는 runlot-entry.js 를 다시 묶으면 사본이 하나 더 생긴다. 모듈
지역 변수에 두면 wrapper 가 넣은 값을 페이지가 못 본다. OpenNext 자신도 같은
이유로 Symbol.for("__cloudflare-context__") 를 쓴다.
캐시 오버라이드는 그 저장소가 아니라 OpenNext 의 문맥을 읽는다 — 오버라이드는
미들웨어 번들에도 들어가고, 그 번들은 node:crypto 하나만 external 이라
node:async_hooks 를 끌고 오면 빌드가 깨진다.
시험
node build.mjsenv 프록시(저장소 밖 오류·인자 없는 user()·동시 요청 격리)와 캐시 오버라이드
둘을 가짜 바인딩으로 잰다. 실물 next build 는 여기서 돌지 않는다 —
docs/nextjs.md §6 의 TestLiveNext 자리다.
