본문으로 건너뛰기

iframe 빠른 시작

이는 백엔드를 먼저 구축하지 않고 개발자 API를 사용해 볼 수 있는 가장 빠른 방법입니다.

iframe 모드에서 앱은 postMessage를 통해 Chastify 부모와 통신하고, Chastify는 앱을 대신하여 서버 API를 호출합니다.

Chastify가 iframe에 전달하는 내용

Chastify는 열릴 때 JSON 페이로드를 location.hash에 저장합니다.
중요 필드:

  • bridge.nonce: 브리지 메시지에 대한 서명 값을 요청합니다.
  • bridge.parentOrigin: postMessage에 필요한 대상 출처입니다.
  • sessionId: 이 잠금에 대한 안정적인 확장 세션 ID입니다.
  • lockId: 활성 잠금 ID.
  • ui: 상위 페이지의 테마 값입니다.
  • 선택 사항: homeActionSlug, homeAction, intent, regularActionsSummary, mainToken.

apps/extension/src/lib/ChastifyBridge.ts의 헬퍼를 사용하세요:

  • parseHashPayload()
  • ChastifyBridgeClient
  • startAutoResizeToParent(...)
  • themeVars(...)

최소한의 React 부트스트랩

import { useEffect, useMemo, useState } from "react";
import {
parseHashPayload,
ChastifyBridgeClient,
startAutoResizeToParent,
themeVars,
} from "../lib/ChastifyBridge";

const payload = parseHashPayload();
if (!payload?.bridge?.nonce || !payload?.bridge?.parentOrigin) {
throw new Error("Missing bridge payload in iframe hash");
}

export function App() {
const [client, setClient] = useState<ChastifyBridgeClient | null>(null);
const [session, setSession] = useState<any>(null);
const [stateDoc, setStateDoc] = useState<any>(null);
const vars = useMemo(() => themeVars(payload.ui ?? null), []);

useEffect(() => {
const c = new ChastifyBridgeClient({
nonce: payload.bridge!.nonce,
targetOrigin: payload.bridge!.parentOrigin,
});
setClient(c);
return () => c.destroy();
}, []);

useEffect(() => {
if (!client) return;
(async () => {
const s = await client.request("session.get", {});
setSession(s);

const st = await client.request("state.get", {});
setStateDoc(st);
})().catch(console.error);
}, [client]);

useEffect(() => {
return startAutoResizeToParent({
nonce: payload.bridge!.nonce,
targetOrigin: payload.bridge!.parentOrigin,
extraPx: 12,
});
}, []);

return (
<div style={{ background: vars.pageBg, color: vars.text, minHeight: "100%" }}>
<h2>Extension Quickstart</h2>
<pre>{JSON.stringify(session, null, 2)}</pre>
<pre>{JSON.stringify(stateDoc, null, 2)}</pre>
</div>
);
}

헬퍼 클래스가 없는 원시 postMessage 예제

ChastifyBridgeClient를 사용하지 않는 경우, 전체 요청 봉투를 수동으로 보내주십시오.

const req = {
type: "chastify:ext:req",
v: 1,
id: crypto.randomUUID(),
nonce,
action: "session.get",
payload: {},
};

window.parent.postMessage(req, parentOrigin);

일치하는 응답을 들어보세요:

window.addEventListener("message", (event) => {
if (event.origin !== parentOrigin) return;
const msg = event.data;
if (!msg || msg.type !== "chastify:ext:resp" || msg.v !== 1) return;
if (msg.id !== req.id) return;

if (msg.ok) console.log("Bridge success:", msg.data);
else console.error("Bridge error:", msg.error);
});

먼저 실행해야 할 테스트 흐름

  1. session.get
  2. state.get
  3. metadata.get
  4. regularActions.get

이는 브리지 인증, 안전한 읽기 및 요청/응답 처리를 확인합니다. 상태 쓰기는 개발자 API 자격 증명을 사용하여 백엔드에서 수행해야 합니다.

iframe을 통한 파일 업로드

확장 프로그램에 이미지나 생성된 미디어가 필요한 경우 브리지 파일 도우미를 사용하세요. Chastify는 관리형 R2 스토리지에 업로드하고 안정적인 파일 ID를 반환하며 렌더링을 위한 단기 서명된 URL을 제공합니다.

const capabilities = await client.filesCapabilities();
if (capabilities.enabled) {
const uploaded = await client.filesUpload({
file,
filename: file.name,
purpose: "puzzle-image",
});

// Send uploaded.file.id to your backend if it should be stored in session state.

const refreshed = await client.filesGet(uploaded.file.id);
image.src = refreshed.file.signedUrl;
}

서명된 URL이 아닌 uploaded.file.id를 저장하세요. 서명된 URL은 만료되므로 iframe이 다시 로드될 때 files.get / client.filesGet(...)로 갱신해야 합니다.

테마 지원 (ui + themeVars)

Chastify는 iframe 해시에 ui 객체를 전달합니다.
themeVars(ui)는 이를 사용 가능한 디자인 토큰으로 변환합니다.

  • pageBg
  • text
  • panel
  • border
  • muted
  • inputBg

확장 프로그램의 기본 스타일로 이러한 값을 사용하여 iframe이 호스트 테마와 일치하도록 하세요.

실시간 테마 업데이트

부모 노드는 iframe이 열려 있는 동안 chastify:ext:ui 이벤트를 보낼 수 있습니다.
실시간 테마 동기화를 원한다면 해당 이벤트를 수신하고 로컬 테마 상태를 업데이트하세요.

자세한 밝은/어두운 테마 처리, 대비 예시 및 Tailwind 패턴은 Iframe 테마를 참조하세요.

자동 크기 조정 지원

부모 요소가 iframe의 크기를 콘텐츠에 맞게 조정할 수 있도록 startAutoResizeToParent(...)를 사용하세요.

이것이 중요한 이유:

  • iframe 내부에서 스크롤 트랩이 발생하는 것을 방지합니다.
  • 설정 모달과 확장 프로그램 페이지의 크기를 올바르게 유지합니다.
  • 확장/축소되는 동적 섹션에 적합합니다.

흔히 저지르는 실수

  • 잘못된 targetOrigin입니다(bridge.parentOrigin와 일치해야 합니다).
  • nonce가 누락되었거나 만료되었습니다.
  • ChastifyBridgeClient에 대해 마운트 해제 시 destroy()를 호출하지 않습니다.
  • session.get의 기능을 먼저 확인하지 않고 지원되지 않는 작업을 전송합니다.

참조 파일

  • apps/extension/src/lib/ChastifyBridge.ts
  • apps/extension/src/pages/MainPage.tsx
  • apps/extension/src/pages/SetupPage.tsx