@bstage-sdk/core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,7 +22,7 @@ bstage 커스텀 템플릿의 **색·타이포·그림자**를 bstage 디자인
22
22
  | 유저(유저 플랫폼) | `@bstage-sdk/design/user` | `--user-*` | 라이트/다크 자동 대응 |
23
23
  | 어드민(어드민 플랫폼) | `@bstage-sdk/design/admin` | `--admin-*` | **라이트 전용** |
24
24
 
25
- `bstage init` 스캐폴드에는 `@bstage-sdk/design`가 이미 의존성에 포함된다. 기존 프로젝트라면 `npm i @bstage-sdk/design`로 추가한다. 타깃(user/admin)은 `package.json`의 `bstage.target`으로 정한다(`bstage init --target admin`이 적는다). 선언이 없으면 `user`.
25
+ `bstage init` 스캐폴드에는 `@bstage-sdk/design`이 이미 의존성에 포함된다. 기존 프로젝트라면 `npm i @bstage-sdk/design`로 추가한다. 타깃(user/admin)은 `package.json`의 `bstage.target`으로 정한다(`bstage init --target admin`이 적는다). 선언이 없으면 `user`.
26
26
 
27
27
  ## 사용
28
28
 
@@ -81,7 +81,7 @@ import { color } from '@bstage-sdk/design/user'
81
81
 
82
82
  ## 간격 · 라운드
83
83
 
84
- 간격(spacing)·모서리 반경(radius)은 **전용 토큰이 없다.** 임의 값을 남발하지 말고 **일관된 스케일**을 유지한다 — 간격은 4의 배수(4/8/12/16/24/32…)를 권장하고, radius도 프로젝트 안에서 일관된 값을 재사용한다.
84
+ 간격(spacing)·모서리 반경(radius)은 **전용 토큰이 없다.** 임의 값을 남발하지 말고 **일관된 스케일**을 유지한다 — 간격은 4의 배수(4/8/12/16/24/32…)를 권장하고 radius도 프로젝트 안에서 일관된 값을 재사용한다.
85
85
 
86
86
  ## 테마 (라이트 / 다크)
87
87
 
@@ -277,37 +277,33 @@ VITE_CF_ACCESS_CLIENT_SECRET=
277
277
  - **프로덕션 미포함**: `bstage build`는 이 훅을 거치지 않고, dev 훅도 `ctx.server`(serve)에서만 동작해 빌드 산출물엔 절대 들어가지 않는다.
278
278
  - 유저 대상엔 우하단에 **라이트/다크 토글 버튼**(dev 전용)이 뜬다 — `<html data-bspoke>`를 바꿔 fallback의 다크 모드를 미리본다. 어드민은 light 전용이라 토글이 없다.
279
279
 
280
- ### sandbox phase + Cloudflare WARP (TLS 인증서)
280
+ ### 회사 프록시의 TLS 인스펙션 (`self-signed certificate` 502)
281
281
 
282
- `bstage dev --phase sandbox`에서 게이트웨이 호출이 아래처럼 **502**로 실패한다면:
282
+ `bstage dev`에서 게이트웨이 호출이 아래처럼 **502**로 실패한다면:
283
283
 
284
284
  ```
285
285
  {"message":"Gateway proxy error","error":"Proxy request failed: {space}.sandstage.in/gw/... — self-signed certificate in certificate chain"}
286
286
  ```
287
287
 
288
- **원인은 SDK가 아니라 로컬 Cloudflare WARP(Zero-Trust)입니다.** WARP가 `*.sandstage.in` 트래픽을 TLS 인스펙션(MITM)하면서 인증서를 자체 CA(`Gateway CA - Cloudflare Managed G1`)로 재서명합니다. macOS 키체인은 이 CA를 신뢰하지만(그래서 브라우저·`curl`은 정상), **Node.js는 OS 키체인이 아닌 자체 내장 CA 목록만** 보기 때문에 "모르는 CA = self-signed"로 간주해 거부합니다.
288
+ **원인은 SDK가 아니라 개발 기기의 네트워크 보안 도구일 가능성이 높습니다.** 회사에서 쓰는 Zero Trust 클라이언트나 보안 프록시가 HTTPS 트래픽을 검사하면서 인증서를 자체 CA로 재서명하는 경우가 있습니다. 브라우저와 `curl`은 OS 신뢰 저장소를 보기 때문에 정상이지만, **Node.js는 OS 키체인이 아닌 자체 내장 CA 목록만** 보기 때문에 그 CA를 "모르는 CA = self-signed"로 간주해 거부합니다.
289
289
 
290
- > 사내 전용 phase(`dev`·`qa`)의 게이트웨이는 정식 인증서라 WARP 인스펙션 대상이 아닙니다. 그래서 **이 문제는 `sandbox`(그리고 어드민 `mysandstage.in`)에서만** 나타납니다.
290
+ **해결 — 회사 CA를 Node 신뢰 목록에 추가** (TLS 검증을 끄지 말 것):
291
291
 
292
- **해결 — WARP CA를 Node 신뢰 목록에 추가** (TLS 검증을 끄지 말 것):
292
+ 1. 회사 보안 도구의 루트 CA 인증서를 PEM 파일로 확보합니다. IT·보안 담당에게 문의하거나 OS 신뢰 저장소에서 내보냅니다.
293
+ 2. `NODE_EXTRA_CA_CERTS`로 등록하고 확인합니다.
293
294
 
294
295
  ```bash
295
- # 1) WARP CA를 키체인에서 PEM으로 추출
296
296
  mkdir -p ~/.config/certs
297
- security find-certificate -a -c "Gateway CA" -p /Library/Keychains/System.keychain \
298
- > ~/.config/certs/cloudflare-warp-ca.pem
299
-
300
- # 2) Node가 이 CA를 신뢰하도록 환경변수 등록 (셸 프로파일에 영구 반영)
301
- echo 'export NODE_EXTRA_CA_CERTS="$HOME/.config/certs/cloudflare-warp-ca.pem"' >> ~/.zshrc
297
+ # 확보한 CA를 ~/.config/certs/corporate-ca.pem 으로 저장한 뒤:
298
+ echo 'export NODE_EXTRA_CA_CERTS="$HOME/.config/certs/corporate-ca.pem"' >> ~/.zshrc
302
299
  source ~/.zshrc
303
300
 
304
- # 3) 검증 (cert 에러 없이 상태코드가 찍히면 성공)
301
+ # 검증 (cert 에러 없이 상태코드가 찍히면 성공)
305
302
  node -e "fetch('https://YOUR_SPACE.sandstage.in/gw/').then(r=>console.log('OK',r.status)).catch(e=>console.log('ERR',e.cause?.code||e.message))"
306
303
  ```
307
304
 
308
- - `security ... -c "Gateway CA"`가 매치하지 않으면 `-c "Cloudflare"`로 검색하거나, 키체인 접근에서 인증서 이름을 직접 확인한다.
309
- - **`NODE_TLS_REJECT_UNAUTHORIZED=0`은 쓰지 말 것** — 모든 TLS 검증을 꺼버려 위험하다. 위 방식은 WARP CA만 신뢰 목록에 *추가*하므로 안전하다.
310
- - 이 설정은 **각 개발자 로컬에만** 적용된다(레포·SDK에 반영되지 않음). WARP를 쓰는 팀원은 각자 1회 수행한다.
305
+ - **`NODE_TLS_REJECT_UNAUTHORIZED=0`은 쓰지 말 것** — 모든 TLS 검증을 꺼버려 위험하다. 위 방식은 회사 CA만 신뢰 목록에 *추가*하므로 안전하다.
306
+ - 이 설정은 **각 개발자 로컬에만** 적용된다(레포·SDK에 반영되지 않음).
311
307
 
312
308
  ---
313
309
 
@@ -12,14 +12,14 @@ b.stage 3rd-party 템플릿을 개발하고 배포하는 전체 흐름을 안내
12
12
 
13
13
  ## 개요
14
14
 
15
- b.stage 3rd-party 템플릿은 **Web Component** 기반으로 동작합니다. SDK가 개발자의 컴포넌트를 Custom Element로 변환하여, b.stage 플랫폼 내에서 독립적으로 실행합니다.
15
+ b.stage 3rd-party 템플릿은 **Web Component** 기반으로 동작합니다. SDK가 개발자의 컴포넌트를 Custom Element로 변환하여 b.stage 플랫폼 내에서 독립적으로 실행합니다.
16
16
 
17
17
  **핵심 특징:**
18
18
 
19
19
  - **Shadow DOM 격리**: 템플릿의 스타일이 플랫폼과 충돌하지 않습니다
20
20
  - **IIFE 번들**: 별도의 모듈 로더 없이 `<script>` 태그로 로드됩니다
21
21
  - **이벤트 기반 통신**: 플랫폼과 CustomEvent를 통해 통신합니다
22
- - **프레임워크 자유**: React, Vanilla JS를 지원하며, Vue/Svelte도 지원 예정입니다
22
+ - **프레임워크 자유**: React, Vanilla JS를 지원하며 Vue/Svelte도 지원 예정입니다
23
23
 
24
24
  설계 원리와 내부 동작은 [SDK_ARCHITECTURE.md](./SDK_ARCHITECTURE.md)를 참고하세요.
25
25
 
@@ -42,7 +42,7 @@ npx @bstage-sdk/cli@latest init
42
42
 
43
43
  API 키는 나중에 설정해도 됩니다. "API 키가 있으신가요?" 질문에 `No`를 선택하면 자리표시자(`YOUR_APP_ID` 등)로 `src/shared/client.ts`가 생성됩니다.
44
44
 
45
- 완료되면 SDK 최신 버전 조회 → 파일 생성 → git 초기화 → 의존성 설치가 자동으로 실행됩니다. `.npmrc`, `vite.config.ts`, `tsconfig.json`, `eslint.config.js`, 에이전트용 `AGENTS.md` / `CLAUDE.md` 등 모든 보일러플레이트가 자동 생성됩니다. CI 워크플로우는 관리도구가 별도 구성합니다 — init은 `.github/workflows/` 파일을 만들지 않습니다. 상세 내용은 [INIT.md](./INIT.md)를 참고하세요.
45
+ 완료되면 SDK 최신 버전 조회 → 파일 생성 → git 초기화 → 의존성 설치가 자동으로 실행됩니다. `.npmrc`, `vite.config.ts`, `tsconfig.json`, `eslint.config.js`, 에이전트용 `AGENTS.md` / `CLAUDE.md` 등 모든 보일러플레이트가 자동 생성됩니다. CI 워크플로우는 관리도구가 별도 구성합니다. init은 `.github/workflows/` 파일을 만들지 않습니다. 상세 내용은 [INIT.md](./INIT.md)를 참고하세요.
46
46
 
47
47
  > 프롬프트 없이 실행하려면 `--yes` 플래그를 사용하세요.
48
48
  >
@@ -108,7 +108,7 @@ npm run dev
108
108
 
109
109
  - 파일명은 반드시 `template.tsx`여야 합니다. 빌드 CLI와 로컬 라우팅이 이 패턴으로 템플릿을 자동 탐색합니다.
110
110
  - **페이지**는 `src/pages/{경로}/template.tsx` — 폴더 구조가 곧 배포 경로가 됩니다.
111
- - **위젯**은 `src/slots/{아무 이름}/template.tsx` — 어느 자리에 붙을지는 `createTemplate`의 `slot` 옵션이 정합니다.
111
+ - **위젯**은 `src/slots/{아무 이름}/template.tsx`입니다. 어느 자리에 붙을지는 `createTemplate`의 `slot` 옵션이 정합니다.
112
112
 
113
113
  ---
114
114
 
@@ -116,7 +116,7 @@ npm run dev
116
116
 
117
117
  ### createTemplate
118
118
 
119
- 각 템플릿은 React 컴포넌트를 작성한 후, `createTemplate()`을 호출하여 Web Component로 등록합니다.
119
+ 각 템플릿은 React 컴포넌트를 작성한 후 `createTemplate()`을 호출하여 Web Component로 등록합니다.
120
120
 
121
121
  ```tsx
122
122
  // src/pages/hello/template.tsx
@@ -146,7 +146,7 @@ createTemplate(BmfHelloTemplate, {
146
146
  | `type` | `string` | | 관리도구에서 사용하는 템플릿 타입 식별자 |
147
147
  | `shadow` | `boolean` | | Shadow DOM 사용 여부 (기본값: `true`) |
148
148
 
149
- > 배치는 소스 위치가 정합니다 — 페이지는 `src/pages/` 폴더 구조, 위젯은 `slot` 옵션. 풀페이지 레이아웃(topBar/bottomBar)은 관리도구가 소유하므로 템플릿 코드에서 선언하지 않습니다. 자세한 내용은 [BUILD_SYSTEM.md](./BUILD_SYSTEM.md)·[SLOT_CATALOG_V2.md](./SLOT_CATALOG_V2.md)를 참고하세요.
149
+ > 배치는 소스 위치가 정합니다(페이지는 `src/pages/` 폴더 구조, 위젯은 `slot` 옵션). 풀페이지 레이아웃(topBar/bottomBar)은 관리도구가 소유하므로 템플릿 코드에서 선언하지 않습니다. 자세한 내용은 [BUILD_SYSTEM.md](./BUILD_SYSTEM.md)·[SLOT_CATALOG_V2.md](./SLOT_CATALOG_V2.md)를 참고하세요.
150
150
 
151
151
  > - 컴포넌트는 반드시 `export default`로 내보내야 합니다
152
152
  > - `createTemplate()`은 반드시 모듈 최상위 레벨에서 호출해야 합니다. 함수 내부에서 호출하면 빌드 시 메타데이터를 추출할 수 없습니다.
@@ -197,7 +197,7 @@ createTemplate(CurationBanner, {
197
197
  })
198
198
  ```
199
199
 
200
- `slot`에 넣을 수 있는 값은 편집기 자동완성으로 볼 수 있고, 전체 목록은 [SLOT_CATALOG_V2.md](./SLOT_CATALOG_V2.md)에 있습니다. 오타가 있으면 `bstage build`가 후보와 함께 막아줍니다.
200
+ `slot`에 넣을 수 있는 값은 편집기 자동완성으로 볼 수 있고 전체 목록은 [SLOT_CATALOG_V2.md](./SLOT_CATALOG_V2.md)에 있습니다. 오타가 있으면 `bstage build`가 후보와 함께 막아줍니다.
201
201
 
202
202
  > 동적 경로(`src/pages/artist/[id]/`)는 아직 지원하지 않습니다.
203
203
 
@@ -236,7 +236,7 @@ function HomeTemplate() {
236
236
 
237
237
  > ⚠️ **`import './home.css'`(문자열이 아닌 부수효과 import)는 배포 산출물에서 사라집니다.**
238
238
  >
239
- > 빌드가 CSS를 별도 `.css` 에셋으로 내는데 산출물에는 `template.js`만 실리기 때문입니다. **로컬 `npm run dev` 화면에서는 적용되므로** 눈치채기 어렵고, 배포한 뒤에야 "스타일이 통째로 빠졌다"로 드러납니다. CSS 파일로 관리하고 싶다면 반드시 `?inline`을 붙여 위 두 방법 중 하나로 넘기세요.
239
+ > 빌드가 CSS를 별도 `.css` 에셋으로 내는데 산출물에는 `template.js`만 실리기 때문입니다. **로컬 `npm run dev` 화면에서는 적용되므로** 눈치채기 어렵고 배포한 뒤에야 "스타일이 통째로 빠졌다"로 드러납니다. CSS 파일로 관리하고 싶다면 반드시 `?inline`을 붙여 위 두 방법 중 하나로 넘기세요.
240
240
  >
241
241
  > 반대로 **`styles` 옵션은 로컬 개발 화면에서 안 보입니다** — 개발 화면은 템플릿을 Shadow DOM 없이 그냥 렌더하기 때문입니다. 최종 모습은 `npx bstage build` 산출물로 확인하세요.
242
242
 
@@ -264,7 +264,7 @@ export const client = new BstageClient({
264
264
  })
265
265
  ```
266
266
 
267
- - 게이트웨이 base URL은 클라이언트가 임베드된 **페이지의 origin**(`location.origin + /gw`)으로 자동 결정됩니다. `{tenant}.sandstage.in`에 배포되면 그 환경의 게이트웨이로 가므로, 환경별로 다시 빌드·설정할 필요가 없습니다.
267
+ - 게이트웨이 base URL은 클라이언트가 임베드된 **페이지의 origin**(`location.origin + /gw`)으로 자동 결정됩니다. `{tenant}.sandstage.in`에 배포되면 그 환경의 게이트웨이로 가므로 환경별로 다시 빌드·설정할 필요가 없습니다.
268
268
  - 로컬 개발에서는 **dev 서버**가 `.env`의 `VITE_BSTAGE_PHASE`(→ `vite.config.ts`의 `bstageDevPlugin`)를 보고 해당 phase 게이트웨이로 프록시합니다. 이 phase는 **dev 서버 설정**이지 `BstageClient`의 옵션이 아닙니다.
269
269
  - 특정 게이트웨이를 직접 지정하려면 `baseUrl` 옵션을 사용합니다(예: 플랫폼 임베드가 아닌 standalone 앱).
270
270
 
@@ -279,7 +279,7 @@ export const client = new BstageClient({
279
279
 
280
280
  ### 6.3 요청 예시
281
281
 
282
- **호출할 경로와 응답 모양은 게이트웨이 API Reference Doc을 보고 정합니다.** SDK는 경로 목록을 들고 있지 않으므로 자동완성이 뜨지 않고, 응답 타입도 제네릭으로 직접 지정합니다. 생략하면 `unknown`이라 필드를 읽을 때 타입 오류가 납니다.
282
+ **호출할 경로와 응답 모양은 게이트웨이 API Reference Doc을 보고 정합니다.** SDK는 경로 목록을 들고 있지 않으므로 자동완성이 뜨지 않고 응답 타입도 제네릭으로 직접 지정합니다. 생략하면 `unknown`이라 필드를 읽을 때 타입 오류가 납니다.
283
283
 
284
284
  ```ts
285
285
  import { client } from '../shared/client'
@@ -364,11 +364,11 @@ try {
364
364
  }
365
365
  ```
366
366
 
367
- `HttpError.message`는 `HTTP 404`처럼 상태 코드만 담습니다. 원인을 분기할 정보는 `e.status`·`e.data`에 있습니다. **`e.data`를 화면에 그대로 띄우지 마세요** — 서버 응답 원문이라 내부 정보가 섞일 수 있습니다. 사용자에게 보여줄 문구는 `e.status`로 갈라 직접 정하세요.
367
+ `HttpError.message`는 `HTTP 404`처럼 상태 코드만 담습니다. 원인을 분기할 정보는 `e.status`·`e.data`에 있습니다. **`e.data`를 화면에 그대로 띄우지 마세요.** 서버 응답 원문이라 내부 정보가 섞일 수 있습니다. 사용자에게 보여줄 문구는 `e.status`로 갈라 직접 정하세요.
368
368
 
369
369
  ### 6.6 파일 업로드 — presigned URL
370
370
 
371
- 이미지·동영상 업로드는 **4단계**입니다. 중간 단계를 건너뛰면 업로드는 성공한 것처럼 보이는데 나중에 404가 나므로, 순서를 지켜야 합니다.
371
+ 이미지·동영상 업로드는 **4단계**입니다. 중간 단계를 건너뛰면 업로드는 성공한 것처럼 보이는데 나중에 404가 나므로 순서를 지켜야 합니다.
372
372
 
373
373
  | 단계 | 호출 | 받는 것 |
374
374
  | ------------ | -------------------------------------------------- | ------------------------------------------------- |
@@ -423,7 +423,7 @@ API 호출은 base URL을 SDK가 알아서 해석하므로 환경을 신경 쓸
423
423
  | real | `{space}.bstage.in` | `{space}.mybstage.in` |
424
424
  | sandbox | `{space}.sandstage.in` | `{space}.mysandstage.in` |
425
425
 
426
- 규칙은 하나입니다 — **어드민은 `my` 접두사**가 붙습니다.
426
+ 규칙은 하나입니다. **어드민은 `my` 접두사**가 붙습니다.
427
427
 
428
428
  > 사내 전용 phase(`dev`·`qa`)는 도메인이 다르고 이 문서에 적지 않습니다. 사내에서 개발한다면 사내 문서를 참고하세요. 아래 코드는 phase와 무관하게 동작합니다.
429
429
 
@@ -441,9 +441,9 @@ function toAdminHost(hostname = location.hostname): string {
441
441
  }
442
442
  ```
443
443
 
444
- phase 자체가 필요하면 호스트 조각으로 판정합니다 — 세 번째 조각이 `dev`·`qa`면 그 phase, `sandstage.in`으로 끝나면 sandbox, 그 밖은 real입니다.
444
+ phase 자체가 필요하면 호스트 조각으로 판정합니다. 세 번째 조각이 `dev`·`qa`면 그 phase, `sandstage.in`으로 끝나면 sandbox, 그 밖은 real입니다.
445
445
 
446
- > `BstageConfig.phase`는 예약 필드이고 **런타임에 쓰이지 않습니다.** 게이트웨이 주소는 이 값이 아니라 현재 origin으로 결정되므로, 링크를 만들 때 이 필드를 읽지 마세요.
446
+ > `BstageConfig.phase`는 예약 필드이고 **런타임에 쓰이지 않습니다.** 게이트웨이 주소는 이 값이 아니라 현재 origin으로 결정되므로 링크를 만들 때 이 필드를 읽지 마세요.
447
447
 
448
448
  ---
449
449
 
@@ -469,7 +469,7 @@ function MySlotWidget() {
469
469
 
470
470
  ### 7.2 useNavigation
471
471
 
472
- `useNavigation` 훅을 사용하여 플랫폼에 네비게이션 이벤트를 전달할 수 있습니다. 플랫폼 임베드 환경에서는 PlatformBridge로 전달되고, 로컬 dev 환경에서는 브라우저 네이티브 API(`location.href`, `history.back`, `window.open`)로 fallback됩니다.
472
+ `useNavigation` 훅을 사용하여 플랫폼에 네비게이션 이벤트를 전달합니다. 플랫폼 임베드 환경에서는 PlatformBridge로 전달되고 로컬 dev 환경에서는 브라우저 네이티브 API(`location.href`, `history.back`, `window.open`)로 fallback됩니다.
473
473
 
474
474
  ```tsx
475
475
  import { useNavigation } from '@bstage-sdk/react'
@@ -519,7 +519,7 @@ function MyComponent() {
519
519
 
520
520
  b.stage 어드민(`{space}.mybstage.in`)에 올라가는 템플릿도 **같은 패키지·같은 명령·같은 스캐폴드**로 만듭니다. 별도 SDK가 없습니다.
521
521
 
522
- 프로젝트를 만들 때 `--target admin`을 주면 `package.json`에 아래가 기록되고, 그 뒤로는 CLI가 이 값을 보고 어드민 디자인 토큰·가이드를 안내합니다.
522
+ 프로젝트를 만들 때 `--target admin`을 주면 `package.json`에 아래가 기록되고 그 뒤로는 CLI가 이 값을 보고 어드민 디자인 토큰·가이드를 안내합니다.
523
523
 
524
524
  ```json
525
525
  {
@@ -533,11 +533,11 @@ npx @bstage-sdk/cli@latest init --target admin
533
533
 
534
534
  ### 한 레포는 유저 아니면 어드민 하나입니다
535
535
 
536
- `target`이 **프로젝트 단위**인 이유는 페이지의 배포 경로가 `src/pages/` 폴더 구조이기 때문입니다. 유저 `/settings`와 어드민 `/settings`를 한 레포에 두려면 둘 다 `src/pages/settings/template.tsx`여야 하는데, 파일이 하나뿐이라 부딪힙니다. 그래서 유저 템플릿과 어드민 템플릿은 **레포를 나눠서** 개발합니다.
536
+ `target`이 **프로젝트 단위**인 이유는 페이지의 배포 경로가 `src/pages/` 폴더 구조이기 때문입니다. 유저 `/settings`와 어드민 `/settings`를 한 레포에 두려면 둘 다 `src/pages/settings/template.tsx`여야 하는데 파일이 하나뿐이라 부딪힙니다. 그래서 유저 템플릿과 어드민 템플릿은 **레포를 나눠서** 개발합니다.
537
537
 
538
538
  ### `target`이 정하는 것
539
539
 
540
- 런타임 동작은 갈리지 않습니다 — 등록 경로도 어댑터도 하나입니다. `target`은 도구가 읽는 값입니다.
540
+ 등록 경로도 어댑터도 하나이므로 런타임 동작은 갈리지 않습니다. `target`은 도구가 읽는 값입니다.
541
541
 
542
542
  - 디자인 토큰: 어드민 프로젝트는 `@bstage-sdk/design/admin`(`--admin-*`), 유저는 `/user`(`--user-*`)를 씁니다. `bstage dev`가 해당 토큰의 fallback을 깝니다.
543
543
  - 에이전트 가이드(AGENTS.md 관리 영역·`bstage-template` 스킬)가 어느 서브패스를 안내할지.
@@ -552,19 +552,67 @@ npx @bstage-sdk/cli@latest init --target admin
552
552
 
553
553
  ### 아직 안 되는 것
554
554
 
555
- **어드민 API 호출 경로는 없습니다.** `BstageClient`는 게이트웨이(유저단 API)만 지원하며, 어드민용 게이트웨이는 아직 열리지 않았습니다. 어드민 데이터가 필요한 템플릿은 그때까지 기다려야 합니다 — 경로를 임의로 만들어 호출하지 마세요.
555
+ **어드민 API 호출 경로는 없습니다.** `BstageClient`는 게이트웨이(유저단 API)만 지원하며 어드민용 게이트웨이는 아직 열리지 않았습니다. 어드민 데이터가 필요한 템플릿은 그때까지 기다려야 합니다 — 경로를 임의로 만들어 호출하지 마세요.
556
556
 
557
557
  ---
558
558
 
559
559
  ## 9. 배포
560
560
 
561
- 배포는 관리도구가 이 레포의 GitHub Actions 워크플로우를 `workflow_dispatch`로 트리거하여 수행합니다. SDK는 워크플로우 파일을 생성·관리하지 않습니다 — 개발자는 코드 작성·push에만 집중하면 됩니다.
561
+ 배포는 포털이 이 레포의 기본 브랜치 HEAD를 clone해 빌드하고, 그 산출물을 배치(페이지·슬롯)에 적용하는 것으로 이뤄집니다. SDK는 워크플로우 파일을 생성·관리하지 않습니다. 포털 화면에서 하거나, 아래 `bstage deploy`로 터미널에서 합니다.
562
+
563
+ ### 9.0 터미널에서 배포하기 — `bstage deploy`
564
+
565
+ 포털 화면(`새 버전 빌드` → `버전 변경` → `게시하기`)과 같은 일을 CLI로 합니다. **포털은 GitHub 기본 브랜치의 HEAD를 clone해 빌드**하므로 로컬 산출물은 올라가지 않습니다 — 커밋·push가 먼저입니다.
566
+
567
+ 1. `npx bstage login` — 터미널에 코드(`XXXX-XXXX`)가 뜨고 브라우저가 열립니다. 포털에 로그인한 뒤 **코드가 같은지 확인**하고 등급(`deploy`)과 스코프(스테이지 선택, 또는 조직 Admin이면 **이 조직의 모든 스테이지**)를 골라 승인하면 터미널이 자동으로 로그인됩니다(토큰은 `~/.config/bstage/credentials.json`에 0600 권한으로 저장되며, 포털이 정한 기간이 지나면 만료됩니다 — 만료일은 로그인 완료 시 터미널에 표시됩니다).
568
+ 2. CI·브라우저 없는 환경은 포털 **설정 › CLI 토큰**에서 발급한 토큰을 `BSTAGE_TOKEN`으로 넘깁니다. 포털 주소는 공개 phase(`real`·`sandbox`)는 CLI에 내장돼 있고, 사내 phase(`dev`·`qa`)는 `.env`의 `VITE_BSTAGE_PORTAL_HOST`로 지정합니다 — 어느 phase를 쓸지는 `.env`의 `VITE_BSTAGE_PHASE`(없으면 `sandbox`)로 정해지며 `bstage dev`와 같은 규칙입니다.
569
+ 3. 프로젝트 디렉터리에서 `npx bstage link` — 조직·스테이지·연결 레포 선택 (`.bstage/project.json`, gitignore 대상)
570
+ 4. `git push` 후 `npx bstage deploy` — 더티 트리·브랜치·push 여부 점검 → 빌드 → 로그 → 배치 적용 확인. **`deploy`는 게시까지 하지 않습니다** — 게시 여부를 바꾸려면 `bstage publish on`/`off`를 따로 실행하세요.
571
+
572
+ **조직마다 한 번 로그인합니다.** CLI 토큰은 조직 하나에 묶여 있어, 여러 조직의 스테이지를 오가려면 조직마다 `bstage login` 을 한 번씩 해야 합니다. 자격증명 파일(`~/.config/bstage/credentials.json`)은 **포털 주소 아래 조직별로** 토큰을 담으므로 두 번째 로그인이 첫 토큰을 덮어쓰지 않습니다. 명령이 쓸 토큰은 `.bstage/project.json` 의 조직으로 골라집니다(CI는 `BSTAGE_ORG`, `link` 는 `--org`). **조직이 정해져 있으면 그 조직 토큰만 씁니다** — 다른 조직 토큰으로 대신하지 않고, 저장된 조직 목록과 "이 조직으로 `bstage login` 하세요" 안내와 함께 종료 코드 3으로 끝납니다. 예외는 하나, 예전 형식(조직을 담지 않던 파일)에서 옮겨 온 토큰이 그 포털의 유일한 토큰일 때입니다 — 이때는 그대로 쓰이고, 다음에 그 포털로 로그인하면 조직이 붙은 토큰으로 대체됩니다. 무엇이 저장돼 있는지는 `bstage whoami` 가 조직·등급·스코프(`이 조직의 모든 스테이지` 또는 스테이지 개수)·만료로 보여 주고, `bstage logout` 은 기본적으로 그 포털의 모든 조직 토큰을 지웁니다(`--org <id>` 로 하나만). 붙여 넣기 경로(`--token`·`BSTAGE_TOKEN`)는 토큰만으로는 어느 조직 것인지 알 수 없어 `--org <id>` 또는 `BSTAGE_ORG` 를 함께 줘야 합니다.
573
+
574
+ **포털 주소는 허용된 곳에서만 옵니다.** `.bstage/project.json`과 `.env`는 저장소 안 파일이라 PR 하나로 바뀝니다 — 토큰을 `Authorization` 헤더로 보내는 주소를 그런 파일이 마음대로 정하면 적대적 PR이 CI 토큰을 다른 서버로 끌어갈 수 있습니다. 그래서 링크 파일의 `portalUrl`은 **허용 목록**(CLI에 내장된 공개 포털 주소 + `--portal`로 준 주소 + `BSTAGE_PORTAL_URL` + `BSTAGE_TOKEN`이 없을 때의 `.env` `VITE_BSTAGE_PORTAL_HOST`) 안의 오리진일 때만 쓰이고, 밖이면 요청을 보내지 않고 종료 코드 2로 끝납니다. **`BSTAGE_TOKEN`이 설정된 환경(CI)에서는 한 단계 더 좁아집니다** — 포털 주소는 `--portal`·`BSTAGE_PORTAL_URL`·공개 phase 매핑으로만 정해지고, 링크 파일과 `.env`의 주소는 아예 쓰이지 않습니다. CI에서는 `BSTAGE_PORTAL_URL`을 명시하세요. `bstage login`도 링크 파일을 보지 않습니다.
575
+
576
+ CI에서는 `BSTAGE_TOKEN`·`BSTAGE_PORTAL_URL`(또는 `VITE_BSTAGE_PHASE`)·`BSTAGE_ORG`·`BSTAGE_SPACE`·`BSTAGE_REPO`·`BSTAGE_SURFACE`를 환경변수로 주고 `bstage deploy --yes --json`을 실행합니다 — `--yes`는 확인 프롬프트를 생략하고, `--json`은 사람이 읽는 안내 없이 구조화된 결과만 stdout에 남겨 스크립트에서 파싱하기 좋습니다.
577
+
578
+ | 종료 코드 | 의미 |
579
+ | --------- | ------------------------------------------ |
580
+ | 0 | 성공 |
581
+ | 1 | 실패 |
582
+ | 2 | 사전조건 미충족 (링크 안 됨, 더티 트리 등) |
583
+ | 3 | 인증 실패 (토큰 없음·만료) |
584
+ | 4 | 충돌 (다른 배포와 경합) |
585
+ | 5 | 요청 제한 (재시도 필요) |
586
+
587
+ | 명령 | 하는 일 |
588
+ | ------------------------------------------ | ------------------------------- |
589
+ | `login` / `logout` / `whoami` | 토큰 등록·삭제·확인 |
590
+ | `link` | 디렉터리 ↔ 조직/스테이지/레포 |
591
+ | `list` | 배치 상태·라이브 커밋·최근 빌드 |
592
+ | `logs [buildId] [-f]` | 빌드 로그 |
593
+ | `deploy [--placement] [--yes] [--no-wait]` | 빌드 → 라이브 적용 |
594
+ | `rollback [buildId]` | 이전 성공 빌드 재적용 |
595
+ | `publish on\|off` | 게시 토글 (라이브 버전은 유지) |
596
+
597
+ 확인 프롬프트가 있는 명령(`deploy`·`rollback`·`publish`·`link`)에서 `--json`은 프롬프트와 섞일 수 없어 **`--yes`와 함께**만 쓸 수 있습니다(`list`·`logs`·`whoami`는 `--json`만으로 됩니다). 출력은 stdout에 객체 **하나**뿐입니다. `deploy`·`rollback`·`publish`는 같은 모양을 씁니다 — 명령마다 다르게 파싱하지 않아도 됩니다.
598
+
599
+ | 키 | 내용 |
600
+ | ------------- | ----------------------------------------------------- |
601
+ | `build` | 이번 빌드 (`deploy`에만 있음) |
602
+ | `changed[]` | 실제로 바뀐 배치 |
603
+ | `unchanged[]` | 이미 그 상태라 건드리지 않은 배치 |
604
+ | `conflicts[]` | 다른 변경이 먼저 적용돼 409로 남은 배치 (종료 코드 4) |
605
+ | `failed[]` | 그 외 실패 (종료 코드 1) |
606
+
607
+ 네 배열은 비어 있어도 항상 나옵니다. 각 항목은 `{ id, label, from?, to?, message? }`이고 `from`·`to`는 라이브 빌드 id입니다(`publish`는 게시 여부만 바꾸므로 없습니다). `bstage logs --json`은 `{ build, log }`이며, `-f`와 함께 써도 진행 중 로그는 stderr로 흐르고 stdout에는 이 객체 하나만 나갑니다.
608
+
609
+ 배치 자체를 만들거나 지우는 일은 포털 화면에서만 합니다.
562
610
 
563
611
  로컬에서 산출물을 확인하고 싶으면 `npx bstage build`를 실행합니다. 페이지는 `dist/{경로}/template.js`, 위젯은 `dist/{슬롯 id}/template.js`로 나옵니다.
564
612
 
565
613
  ### 9.1 인증 값은 빌드 시점에 번들로 들어갑니다
566
614
 
567
- `.env`의 `VITE_BSTAGE_*` 값은 **빌드할 때 번들 안에 문자열로 박힙니다.** 배포한 뒤에 `.env`를 고쳐도 반영되지 않고, 값을 바꿨으면 반드시 다시 빌드해야 합니다.
615
+ `.env`의 `VITE_BSTAGE_*` 값은 **빌드할 때 번들 안에 문자열로 박힙니다.** 배포한 뒤에 `.env`를 고쳐도 반영되지 않습니다. 값을 바꿨으면 반드시 다시 빌드해야 합니다.
568
616
 
569
617
  `.env` 없이 빌드하면 번들에 `undefined`가 박혀 배포 후 모든 API 호출이 401로 실패합니다. `bstage build`는 BstageClient를 쓰면서 인증 값을 `.env`로 주입하는 템플릿에 한해 빌드 끝에 이걸 점검해 알려줍니다.
570
618
 
@@ -577,7 +625,7 @@ npx @bstage-sdk/cli@latest init --target admin
577
625
 
578
626
  ### 9.2 배포 전 점검 — 증상으로 원인 되짚기
579
627
 
580
- 아래 항목은 **SDK가 감지할 수 없습니다.** 키 형식(`bsa_`/`bsp_`)은 ID·시크릿 구분일 뿐 어느 환경에서 발급했는지를 담지 않고, 나머지는 BE·콘솔 설정이라 코드에 흔적이 없습니다. 리얼 배포 후 아래 에러를 만나면 이 표로 되짚으세요.
628
+ 아래 항목은 **SDK가 감지할 수 없습니다.** 키 형식(`bsa_`/`bsp_`)은 ID·시크릿 구분일 뿐 어느 환경에서 발급했는지를 담지 않습니다. 나머지는 BE·콘솔 설정이라 코드에 흔적이 없습니다. 리얼 배포 후 아래 에러를 만나면 이 표로 되짚으세요.
581
629
 
582
630
  | 증상 | 원인 | 조치 | 소관 |
583
631
  | ----------------------- | ---------------------------------------- | ----------------------------------------------- | ------------ |
@@ -588,11 +636,11 @@ npx @bstage-sdk/cli@latest init --target admin
588
636
  | 배포 자체가 안 됨 | 데브포털 접근·리얼 배포 권한 없음 | 권한 요청 | DevOps |
589
637
  | 임베드(iframe)가 차단됨 | CSP `frame-ancestors`에 도메인 없음 | 도메인 추가 요청 | BE·보안 |
590
638
 
591
- **QA에서 됐다고 리얼에서 되지 않습니다.** 위 항목은 전부 환경마다 따로 설정되고, 하나씩 막힐 때마다 요청·대기가 반복되면 며칠이 걸립니다. 처음 리얼로 나갈 때는 위 표의 BE·콘솔 항목을 **한 번에 모아서** 요청하세요.
639
+ **QA에서 됐다고 리얼에서 되지 않습니다.** 위 항목은 전부 환경마다 따로 설정됩니다. 하나씩 막힐 때마다 요청·대기가 반복되면 며칠이 걸립니다. 처음 리얼로 나갈 때는 위 표의 BE·콘솔 항목을 **한 번에 모아서** 요청하세요.
592
640
 
593
641
  ### 9.3 보안성 검토 — 어떤 통제가 어디에 있나
594
642
 
595
- 보안성 검토에서 "API 남용을 막고 있나", "에러에 내부 정보가 노출되지 않나" 같은 질문을 받습니다. 통제 지점이 SDK 안이 아닌 것이 많아, 어디를 가리켜 답해야 하는지 정리했습니다.
643
+ 보안성 검토에서 "API 남용을 막고 있나", "에러에 내부 정보가 노출되지 않나" 같은 질문을 받습니다. 통제 지점이 SDK 안이 아닌 것이 많아 어디를 가리켜 답해야 하는지 정리했습니다.
596
644
 
597
645
  | 요구 | 통제 지점 | 답변 근거 |
598
646
  | --------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
package/docs/INIT.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 1. 개요
4
4
 
5
- `bstage init`은 서드파티 개발자가 b.stage 템플릿 프로젝트를 즉시 시작할 수 있도록 보일러플레이트를 생성하는 CLI 명령어입니다.
5
+ `bstage init`은 보일러플레이트를 생성하는 CLI 명령어입니다. 서드파티 개발자는 이 명령어로 b.stage 템플릿 프로젝트를 즉시 시작할 수 있습니다.
6
6
 
7
7
  ```bash
8
8
  npx @bstage-sdk/cli@latest init
@@ -102,7 +102,7 @@ npx @bstage-sdk/cli@latest init --yes --space bmf --phase dev
102
102
  npx @bstage-sdk/cli@latest init --yes --space my-space --phase dev --target admin
103
103
  ```
104
104
 
105
- 어드민 템플릿 작성법은 [GETTING_STARTED.md](./GETTING_STARTED.md)의 "어드민 템플릿" 절을 참고한다.
105
+ 어드민 템플릿 작성법은 [GETTING_STARTED.md](./GETTING_STARTED.md)의 "어드민 템플릿" 절을 참고하세요.
106
106
 
107
107
  ---
108
108
 
@@ -156,7 +156,7 @@ init 완료 시 다음이 자동으로 실행됩니다:
156
156
 
157
157
  ## 8. 배포
158
158
 
159
- 배포는 관리도구가 이 레포의 GitHub Actions 워크플로우를 `workflow_dispatch`로 트리거하여 수행합니다. SDK는 워크플로우 파일을 생성·관리하지 않으며, 워크플로우의 구성·권한 세팅·CDN 업로드 경로 결정은 모두 관리도구 측에서 이루어집니다.
159
+ 배포는 관리도구가 이 레포의 GitHub Actions 워크플로우를 `workflow_dispatch`로 트리거해서 수행합니다. SDK는 워크플로우 파일을 생성·관리하지 않습니다. 워크플로우의 구성·권한 세팅·CDN 업로드 경로 결정은 모두 관리도구가 맡습니다.
160
160
 
161
161
  ---
162
162
 
@@ -2,14 +2,14 @@
2
2
 
3
3
  ## 1. 개요
4
4
 
5
- bstage Template SDK는 서드파티 개발 파트너가 React 등 모던 프레임워크로 템플릿을 작성하면, 이를 Web Component로 변환하여 bstage 플랫폼에 통합하는 개발 도구입니다.
5
+ bstage Template SDK는 서드파티 개발 파트너가 React 등 모던 프레임워크로 작성한 템플릿을 Web Component로 변환해 bstage 플랫폼에 통합하는 개발 도구입니다.
6
6
 
7
- 서드파티는 Web Component를 직접 다루지 않습니다. 평소 프레임워크 개발과 동일한 방식으로 컴포넌트를 작성하고, SDK가 나머지를 처리합니다.
7
+ 서드파티는 Web Component를 직접 다루지 않습니다. 평소 프레임워크 개발과 동일한 방식으로 컴포넌트를 작성하고 SDK가 나머지를 처리합니다.
8
8
 
9
9
  **설계 목표:**
10
10
 
11
11
  - 서드파티 개발자가 플랫폼 내부 구현을 몰라도 템플릿을 개발할 수 있을 것
12
- - 프레임워크 선택이 SDK에 의해 제한되지 않을 것
12
+ - SDK가 프레임워크 선택을 제한하지 않을 것
13
13
  - 서드파티 코드가 플랫폼의 스타일이나 동작을 오염시키지 않을 것
14
14
  - 빌드 산출물이 단일 JS 파일로 완결되어 배포와 로드가 단순할 것
15
15
 
@@ -19,11 +19,11 @@ bstage Template SDK는 서드파티 개발 파트너가 React 등 모던 프레
19
19
 
20
20
  ### 프레임워크 독립성
21
21
 
22
- Core는 특정 프레임워크에 의존하지 않습니다. `FrameworkAdapter` 인터페이스를 정의하고, 각 프레임워크 바인딩(React, Vue 등)이 이를 구현합니다. 새 프레임워크를 지원할 때 Core를 수정할 필요가 없습니다.
22
+ Core는 특정 프레임워크에 의존하지 않습니다. `FrameworkAdapter` 인터페이스를 정의하고 각 프레임워크 바인딩(React, Vue 등)이 이를 구현합니다. 새 프레임워크를 지원할 때 Core를 수정할 필요가 없습니다.
23
23
 
24
24
  ### 스타일 격리
25
25
 
26
- Shadow DOM을 사용하여 서드파티 CSS가 플랫폼 UI에 영향을 주거나, 플랫폼 CSS가 템플릿을 깨뜨리는 것을 방지합니다. CSS는 빌드 시 JS 번들에 인라인되어 Shadow DOM 내부에서만 적용됩니다.
26
+ Shadow DOM으로 서드파티 CSS가 플랫폼 UI에 영향을 주거나 플랫폼 CSS가 템플릿을 깨뜨리는 것을 방지합니다. CSS는 빌드 시 JS 번들에 인라인되어 Shadow DOM 내부에서만 적용됩니다.
27
27
 
28
28
  ### 단일 번들 출력
29
29
 
@@ -31,11 +31,11 @@ Shadow DOM을 사용하여 서드파티 CSS가 플랫폼 UI에 영향을 주거
31
31
 
32
32
  ### 명시적 통신 경계
33
33
 
34
- 서드파티 코드와 플랫폼 사이의 모든 통신은 PlatformBridge를 통한 CustomEvent로 이루어집니다. 직접적인 DOM 접근이나 전역 상태 공유 없이, 정의된 이벤트 인터페이스를 통해서만 상호작용합니다.
34
+ 서드파티 코드와 플랫폼 사이의 모든 통신은 PlatformBridge의 CustomEvent로 오갑니다. DOM 직접 접근이나 전역 상태 공유 없이 정의된 이벤트 인터페이스로만 상호작용합니다.
35
35
 
36
36
  ### 토큰 비노출
37
37
 
38
- 서드파티 코드에서 플랫폼 사용자의 인증 토큰에 직접 접근하지 않습니다. API 호출은 BstageClient를 통해 `appId`/`appSecret` 기반으로 이루어집니다.
38
+ 서드파티 코드는 플랫폼 사용자의 인증 토큰에 직접 접근하지 않습니다. API 호출은 BstageClient가 `appId`/`appSecret`으로 처리합니다.
39
39
 
40
40
  ---
41
41
 
@@ -50,9 +50,9 @@ Shadow DOM을 사용하여 서드파티 CSS가 플랫폼 UI에 영향을 주거
50
50
  | 양방향 통신 | CustomEvent (동기) | postMessage (비동기) | 직접 호출 |
51
51
  | 제출물 | JS 번들 1개 | HTML + JS (독립 앱) | 빌드 설정 맞춤 필요 |
52
52
 
53
- 1. **기존 Liquid 파이프라인과 가장 유사** — `.liquid` 로드 → 파싱 → HTML 삽입이 `.js` 로드 → 태그 생성 → DOM 추가로 자연스럽게 대체됩니다.
53
+ 1. **기존 Liquid 파이프라인과 가장 유사** — `.liquid` 로드 → 파싱 → HTML 삽입이 `.js` 로드 → 태그 생성 → DOM 추가로 대체됩니다.
54
54
  2. **브라우저 네이티브 표준** — Custom Elements + Shadow DOM은 W3C 표준이며 모든 모던 브라우저에서 별도 라이브러리 없이 동작합니다.
55
- 3. **Shadow DOM 스타일 격리** — 서드파티 CSS가 플랫폼 UI에 영향을 주거나 그 반대 상황을 자연스럽게 방지합니다.
55
+ 3. **Shadow DOM 스타일 격리** — 서드파티 CSS가 플랫폼 UI에 영향을 주는 상황과 그 반대 상황을 방지합니다.
56
56
 
57
57
  ---
58
58
 
@@ -94,9 +94,9 @@ graph LR
94
94
  | **빌드 도구** | cli | 서드파티 | Vite 기반 IIFE 번들 생성 + 산출물 경로 배치, 로컬 개발 서버 |
95
95
  | **디자인 토큰** | design | 서드파티 | 색·타이포·그림자 토큰 (`./user`·`./admin` 서브패스 + css 산출물) |
96
96
 
97
- Core는 프레임워크 바인딩과 플랫폼 통합 양쪽에서 사용되지만, 서드파티에게 직접 노출되지 않습니다. 서드파티는 React 바인딩의 API만 사용하고, 플랫폼은 Host SDK의 API만 사용합니다.
97
+ Core는 프레임워크 바인딩과 플랫폼 통합 양쪽에서 사용되지만 서드파티에게 직접 노출되지 않습니다. 서드파티는 React 바인딩의 API만 쓰고 플랫폼은 Host SDK의 API만 사용합니다.
98
98
 
99
- **어드민 템플릿도 같은 패키지·같은 명령을 씁니다.** 갈리는 것은 템플릿이 선언하는 `target`뿐이며, 런타임 등록 경로와 어댑터는 하나입니다. 다만 **어드민 API 호출 경로는 아직 없습니다** — `BstageClient`는 게이트웨이(유저단)만 지원하고, 어드민용 게이트웨이는 열리지 않았습니다. 자세한 내용은 [GETTING_STARTED.md](./GETTING_STARTED.md)의 "어드민 템플릿" 절을 참조하세요.
99
+ **어드민 템플릿도 같은 패키지·같은 명령을 씁니다.** 갈리는 것은 프로젝트가 `package.json`의 `bstage.target`으로 선언하는 대상뿐이며 런타임 등록 경로와 어댑터는 하나입니다. 다만 **어드민 API 호출 경로는 아직 없습니다** — `BstageClient`는 게이트웨이(유저단)만 지원하고 어드민용 게이트웨이는 열리지 않았습니다. 자세한 내용은 [GETTING_STARTED.md](./GETTING_STARTED.md)의 "어드민 템플릿" 절을 참조하세요.
100
100
 
101
101
  ---
102
102
 
@@ -104,11 +104,11 @@ Core는 프레임워크 바인딩과 플랫폼 통합 양쪽에서 사용되지
104
104
 
105
105
  ### 5.1 FrameworkAdapter
106
106
 
107
- 프레임워크별 렌더링 로직을 캡슐화하는 인터페이스입니다. Core는 이 인터페이스에만 의존하므로, 새 프레임워크 지원 시 Core 수정 없이 어댑터만 추가하면 됩니다.
107
+ 프레임워크별 렌더링 로직을 캡슐화하는 인터페이스입니다. Core는 이 인터페이스에만 의존하므로 새 프레임워크 지원 시 Core 수정 없이 어댑터만 추가하면 됩니다.
108
108
 
109
- **책임:** Shadow DOM 내에 프레임워크 루트를 생성하고, props 갱신과 정리를 처리합니다.
109
+ **책임:** Shadow DOM 내에 프레임워크 루트를 생성하고 props 갱신과 정리를 처리합니다.
110
110
 
111
- **확장 포인트:** 현재 React 어댑터가 구현되어 있으며, 동일 인터페이스로 Vue, Svelte, Vanilla JS 어댑터를 추가할 수 있습니다.
111
+ **확장 포인트:** 현재 React 어댑터가 구현되어 있으며 동일 인터페이스로 Vue, Svelte, Vanilla JS 어댑터를 추가할 수 있습니다.
112
112
 
113
113
  > API 시그니처는 [API_REFERENCE.md](./API_REFERENCE.md#11-frameworkadapter)를 참고하세요.
114
114
 
@@ -122,7 +122,7 @@ FrameworkAdapter를 받아 Web Component(BstageElement)를 생성하는 팩토
122
122
  - `connectedCallback`에서 어댑터와 브릿지 초기화, 렌더링 시작
123
123
  - `disconnectedCallback`에서 어댑터/브릿지 정리 및 참조 해제
124
124
 
125
- Custom Element 등록(`customElements.define`)은 BstageElement 자체가 하지 않고, 프레임워크 바인딩의 `registerTemplate()`이 처리합니다. `createTemplate()`은 메타데이터 첨부와 레지스트리 등록만 수행하며, 빌드 시 CLI의 `registerPlugin`이 `registerTemplate()` 호출 코드를 자동 주입합니다.
125
+ Custom Element 등록(`customElements.define`)은 BstageElement 자체가 하지 않고 프레임워크 바인딩의 `registerTemplate()`이 처리합니다. `createTemplate()`은 메타데이터 첨부와 레지스트리 등록만 수행하며 빌드 시 CLI의 `registerPlugin`이 `registerTemplate()` 호출 코드를 자동 주입합니다.
126
126
 
127
127
  > API 시그니처는 [API_REFERENCE.md](./API_REFERENCE.md#12-createwebcomponent)를 참고하세요.
128
128
 
@@ -143,13 +143,13 @@ Custom Element 등록(`customElements.define`)은 BstageElement 자체가 하지
143
143
 
144
144
  ### 5.4 BstageClient
145
145
 
146
- 파트너에게 공개된 API 접근을 제공하는 HTTP 클라이언트입니다.
146
+ 파트너에게 공개된 API에 접근하는 HTTP 클라이언트입니다.
147
147
 
148
- **설계 의도:** 서드파티가 플랫폼 API를 호출할 때, 플랫폼 사용자의 인증 토큰 대신 `appId`/`appSecret`/`tenantId`를 사용합니다. 이로써 서드파티 코드가 사용자 토큰에 접근할 수 없으며, API 접근 범위를 앱 단위로 제어할 수 있습니다.
148
+ **설계 의도:** 서드파티가 플랫폼 API를 호출할 때, 플랫폼 사용자의 인증 토큰 대신 `appId`/`appSecret`/`tenantId`를 사용합니다. 이로써 서드파티 코드가 사용자 토큰에 접근할 수 없으며 API 접근 범위를 앱 단위로 제어할 수 있습니다.
149
149
 
150
- **base URL 결정:** `resolveBaseUrl()` 함수가 `location.origin/gw`를 반환하여 현재 호스트 기반으로 요청합니다. 로컬 개발 시에는 devVitePlugin이 이 함수를 치환하여 localhost 프록시로 라우팅합니다.
150
+ **base URL 결정:** `resolveBaseUrl()` 함수가 `location.origin/gw`를 반환하여 현재 호스트 기반으로 요청합니다. 로컬 개발 시에는 devVitePlugin이 이 함수를 치환해 localhost 프록시로 라우팅합니다.
151
151
 
152
- **커스텀 fetch 주입:** `globalThis.__bstage_fetch__`를 통해 플랫폼이 인증 헤더(Authorization, CF Access)를 포함하는 fetch를 주입할 수 있습니다. 이를 통해 템플릿이 플랫폼과 동일한 인증 컨텍스트로 API를 호출할 수 있습니다.
152
+ **커스텀 fetch 주입:** 플랫폼은 `globalThis.__bstage_fetch__`를 통해 인증 헤더(Authorization, CF Access)를 포함하는 fetch를 주입할 수 있습니다. 그러면 템플릿이 플랫폼과 동일한 인증 컨텍스트로 API를 호출합니다.
153
153
 
154
154
  > API 시그니처와 사용 예시는 [API_REFERENCE.md](./API_REFERENCE.md#14-bstageclient)를 참고하세요.
155
155
 
@@ -204,7 +204,7 @@ flowchart TD
204
204
  J --> K["6. 버퍼링된 dispatch 리플레이"]
205
205
  ```
206
206
 
207
- **dispatch 버퍼링:** `tpl.dispatch()`를 mount 직후에 호출하면 React 렌더링이 아직 완료되지 않아 이벤트가 유실될 수 있다. TemplateHandle은 템플릿이 `bstage:__ready__` 이벤트를 발행할 때까지 dispatch를 버퍼링하고, ready 후 순서대로 리플레이한다.
207
+ **dispatch 버퍼링:** `tpl.dispatch()`를 mount 직후에 호출하면 React 렌더링이 아직 완료되지 않아 이벤트가 유실될 수 있습니다. TemplateHandle은 템플릿이 `bstage:__ready__` 이벤트를 발행할 때까지 dispatch를 버퍼링하고 ready 후 순서대로 리플레이합니다.
208
208
 
209
209
  ### 런타임 — 언마운트
210
210
 
@@ -242,7 +242,7 @@ Shadow DOM은 **스타일 격리**이지 **보안 격리**가 아닙니다. Java
242
242
  | 토큰/쿠키 탈취 | 민감 정보는 HttpOnly 쿠키로만 관리 |
243
243
  | API 남용 | API Gateway에서 rate limiting |
244
244
 
245
- 서드파티의 API 접근은 BstageClient를 통해 `appId`/`appSecret` 기반으로 이루어지며, 플랫폼 사용자의 인증 토큰에 직접 접근하지 않습니다.
245
+ 서드파티는 BstageClient를 통해 `appId`/`appSecret`으로 API에 접근하고 플랫폼 사용자의 인증 토큰에는 직접 접근하지 않습니다.
246
246
 
247
247
  ---
248
248
 
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@bstage-sdk/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "bstage SDK core — HTTP client for b.stage API",
5
5
  "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
6
9
  "repository": {
7
10
  "type": "git",
8
11
  "url": "https://github.com/bstage-sdk/web-sdk.git",
@@ -32,9 +35,6 @@
32
35
  "typescript": "^5.5.0"
33
36
  },
34
37
  "license": "MIT",
35
- "publishConfig": {
36
- "access": "public"
37
- },
38
38
  "scripts": {
39
39
  "build": "tsup && rm -rf docs && cp -r ../../docs docs",
40
40
  "typecheck": "tsc --noEmit"