게임 전용 임베드 구현과 개발자 전달 가이드
갱신일: 2026-09-18. SDK 런타임 v1.4.0과 호환됩니다. 화면 구성·납품 기준을 보강한 문서이며 새 SDK 프로토콜 버전이 아닙니다.
SDK 연동과 임베드 화면은 별도 작업입니다
SDK는 사용자·점수·저장·플랫폼 기능을 연결합니다. SDK 스크립트를 넣어도 홈페이지 헤더 제거, /embed 생성, 캔버스 크기 조절, 게임 번역, 스크롤 제거가 자동으로 되지는 않습니다. 게임 개발자가 임베드용 화면을 구현하면 tokenApps가 등록된 플레이 URL을 불러옵니다.
홈페이지 주소를 등록하면 히어로·메뉴·랭킹·푸터·FAQ까지 iframe 안으로 들어옵니다. 호스트 폭을 늘려도 이 내용은 없어지지 않습니다. 다른 출처의 페이지이므로 tokenApps가 게임 내부 DOM이나 CSS를 직접 수정할 수도 없습니다.
| 작업 | 게임 프로젝트 | tokenApps |
|---|---|---|
| 게임 전용 주소·반응형 게임 | 구현 후 배포 | 등록된 URL 로드 |
| 보드·HUD·터치 버튼·게임 대화상자 | 프레임 안에 배치 | 사용 가능한 화면 제공 |
| 사용자·점수·저장 | 기존 SDK 호출 | 인증과 요청 처리 |
| 파밍 | 플레이 유지, 자체 TAP 지급 금지 | 유효 플레이 시간 측정·보상 처리 |
| 검수 | 지원 기기별 검증 자료 제공 | 빌드와 리스팅 검토 |
1. 실제로 실행되는 게임 전용 URL 만들기
권장 구조입니다.
https://game.example.com/ 소개·검색 노출·FAQ를 위한 공개 홈페이지
https://game.example.com/embed 게임 보드 + HUD + 필수 조작 버튼
위 주소는 예시입니다. /embed를 직접 구현하고 배포해야 합니다. 기존 주소 뒤에 /embed나 ?embed=1만 붙여서는 작동하지 않습니다. 다른 경로도 괜찮고, /에서 이미 게임만 제공한다면 주소를 바꿀 필요가 없습니다.
기존 게임 컴포넌트·엔진·저장 형식을 재사용하세요. 게임 로직을 따로 복제하지 않습니다. 일시정지·음소거·도움말·재시작·점수·남은 목숨/코인·터치 버튼은 유지합니다. 도움말이나 랭킹은 필요할 때 프레임 안의 대화상자로 열고, 보드 아래에 긴 페이지로 붙이지 않습니다.
게임을 또 다른 iframe으로 감싸지 마세요. SDK는 바로 위 부모와 통신합니다. tokenApps가 직접 임베드하는 문서에 SDK와 게임을 함께 둡니다.
2. 홈페이지 레이아웃 분리하기
React/Vite에서는 /embed를 홈페이지 레이아웃 없이 공통 게임 컴포넌트로 연결합니다. 배포 서버의 SPA fallback도 설정해 /embed 직접 접속과 새로고침이 성공해야 합니다. 일반 HTML은 동일한 게임 모듈을 사용하는 embed.html로도 구현할 수 있습니다.
Next.js App Router에서는 루트 레이아웃을 최소화하고 홈페이지 요소를 route group으로 옮깁니다.
app/layout.tsx html, body, 공통 provider만 유지
app/(website)/layout.tsx 홈페이지 헤더·메뉴·푸터
app/(website)/page.tsx 공개 홈페이지 /
app/embed/page.tsx 공통 게임 클라이언트 컴포넌트
components/Game.tsx 기존 엔진·게임·조작 UI
Route group 이름은 URL에 포함되지 않습니다. /를 중복 담당하는 app/page.tsx를 남기지 마세요. 브라우저 전용 엔진은 서버 렌더링 중이 아니라 클라이언트 effect에서 초기화합니다. 언마운트 시 엔진·observer·입력 리스너·애니메이션 프레임을 정리합니다. React 개발 모드의 Strict Mode가 setup/cleanup을 다시 실행해도 캔버스와 게임 루프가 중복 생성되지 않아야 합니다.
임베드 CSS는 해당 경로나 루트에만 적용합니다. 홈페이지까지 전역으로 스크롤을 막지 마세요. Next.js route group 공식 문서를 참고하세요.
3. HUD와 버튼 공간부터 확보하기
게임이 사용할 수 있는 크기는 iframe 내부의 viewport입니다. tokenApps 헤더·조작 UI 때문에 브라우저 창보다 작고, 회전·전체화면에 따라 달라집니다. 게임 안에서 tokenApps 헤더 높이를 다시 빼거나 screen.width, 고정 900px 높이를 사용하지 마세요.
게임 전용 문서는 다음처럼 세 줄로 나누면 됩니다.
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<main id="embed-root">
<header class="game-hud">점수, 목숨, 일시정지</header>
<div id="board-space"><canvas id="board"></canvas></div>
<nav class="game-controls" aria-label="게임 조작">터치 버튼</nav>
</main>
/* 게임 전용 문서에만 적용. 프레임워크의 중간 wrapper에도 높이가 필요합니다. */
html, body, #root { width: 100%; height: 100%; margin: 0; }
body { overflow: hidden; overscroll-behavior: none; }
#embed-root {
position: fixed; inset: 0; box-sizing: border-box;
display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
gap: 8px; min-width: 0; min-height: 0;
padding: max(8px, env(safe-area-inset-top))
max(8px, env(safe-area-inset-right))
max(8px, env(safe-area-inset-bottom))
max(8px, env(safe-area-inset-left));
}
#board-space { position: relative; min-width: 0; min-height: 0; }
#board { position: absolute; inset: 0; display: block; width: 100%; height: 100%; touch-action: none; }
.game-hud, .game-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.game-controls button { min-width: 44px; min-height: 44px; }
@media (max-height: 360px) {
#embed-root { gap: 4px; }
.optional-description { display: none; }
}
가운데 보드는 HUD와 버튼이 차지한 실제 높이를 제외한 나머지 공간을 사용합니다. min-height: 0은 grid/flex 자식이 문서 높이를 밀어내는 것을 막습니다. overflow: hidden은 최종 경계 처리일 뿐, 화면 밖으로 내려간 버튼을 해결하지 않습니다. 모든 필수 버튼이 보이고 눌리는지 따로 확인해야 합니다.
높이가 짧은 가로 화면에서는 HUD를 간소화하거나 버튼을 보드 옆으로 옮기세요. 장식 문구를 남기려고 터치 버튼을 44 CSS px 미만으로 줄이지 않습니다. 지원할 수 없는 크기는 회전·크기 변경 안내와 나갈 경로를 제공하고 제한사항으로 기록합니다. 이를 검수 통과로 표시하면 안 됩니다.
4. 게임 상태를 유지하며 렌더러 크기 바꾸기
ResizeObserver로 #board-space를 관찰하면 HUD를 제외한 보드 공간 크기를 얻을 수 있습니다. 고정 비율의 논리 보드는 다음처럼 계산합니다.
const scale = Math.min(availableWidth / designWidth, availableHeight / designHeight);
const offsetX = (availableWidth - designWidth * scale) / 2;
const offsetY = (availableHeight - designHeight * scale) / 2;
여백을 허용하는 contain을 사용합니다. cover는 보드를 잘라냅니다. Canvas 2D는 CSS 크기와 drawing buffer 크기를 구분하고, 제한한 device pixel ratio를 buffer에 반영한 뒤 렌더링 transform을 적용합니다. Canvas의 width/height 변경은 context를 초기화하므로 기존 상태를 다시 그리되 새 라운드를 만들지 않습니다. 프레임 크기·픽셀 비율 변경을 반영하고, 해제 시 observer와 리스너도 정리합니다.
터치·마우스 위치는 캔버스의 bounding rectangle, 여백, 배율을 역으로 적용해 논리 좌표로 변환합니다.
const x = (event.clientX - rect.left - offsetX) / scale;
const y = (event.clientY - rect.top - offsetY) / scale;
// [0, designWidth] x [0, designHeight] 밖의 여백을 누르면 무시합니다.
WebGL/Three.js는 drawing buffer와 카메라 projection을, Phaser/Pixi는 실제 설치한 버전의 resize/scale API를 맞춥니다. CSS로 화면만 늘려서는 안 됩니다. 회전 중에도 물리 상태·타이머·점수·랜덤 시드가 유지되어야 합니다.
요소 크기 관찰 방법은 ResizeObserver 문서를 참고하세요. 관찰 대상 자신의 크기를 계속 바꾸는 루프를 만들지 말고, 확보된 공간에 맞춰 렌더러를 갱신합니다.
5. 기존 SDK 연동 유지하기
게임 문서에서 https://tokenapps.io/sdk/v1.js를 한 번 로드합니다. ready()보다 먼저 session 리스너를 등록합니다. SDK 로딩 실패나 호스트 무응답에도 게스트 게임은 한 번 시작되어야 합니다. 로그인·토큰 갱신으로 session이 반복되어도 진행 중인 라운드를 초기화하면 안 됩니다.
SDK v1.4에는 off()가 없습니다. React에서는 지속되는 연동 모듈에 SDK 콜백 하나를 등록하고, 별도의 로컬 subscriber set으로 컴포넌트 구독·해제를 관리하세요. 매 마운트마다 TokenApps.on()을 추가하지 않습니다. 패키지 README에 이 패턴을 제공합니다.
기존 점수·저장·완료 호출은 실제 게임 이벤트에 그대로 연결합니다. 경로 변경만으로 점수·보상을 요청하지 않습니다. 페이지 로드·resize·매 렌더링마다 track("session_complete")를 호출하지 마세요. 자동 파밍은 호스트가 처리하며 기존 완료 이벤트와 일일 보상을 공유합니다.
v1.4에는 SDK resize(), setEmbedMode(), 호스트 자동 높이 메시지가 없습니다. 존재하지 않는 API를 만들거나 홈페이지 높이만큼 tokenApps iframe을 늘려 달라고 요청하지 마세요. 크기 대응은 게임에서 구현합니다. embed 쿼리값은 표시 방식일 뿐 인증 증거가 아니므로 사용자 식별은 SDK session을 사용합니다.
6. 배포한 게임의 iframe 허용 설정
게임 문서의 HTTP 응답에 다음 directive를 기존 CSP와 병합합니다.
Content-Security-Policy: frame-ancestors 'self' https://tokenapps.io
해당 문서의 X-Frame-Options: DENY 또는 SAMEORIGIN 충돌도 제거합니다. frame-ancestors는 HTTP 헤더여야 하며 meta 태그만으로는 적용되지 않습니다. 검수·파트너 출처가 필요하면 승인한 정확한 origin만 추가합니다. 리다이렉트 이후 최종 응답과 CDN 설정까지 확인하세요.
다른 CSP directive는 유지하면서 SDK 스크립트와 실제 사용하는 API 연결을 허용합니다. CORS와 iframe 허용은 별개입니다. 콘솔에서 frame·script·connect 차단 원인을 확인하세요. MDN frame-ancestors 문서를 참고하세요.
7. 실행 예제와 프레임별 검증
실행 가능한 크기 대응 예제를 제공합니다. 캔버스·HUD·터치/키보드 입력·contain 배율·SDK session 구독·게스트 시작을 확인할 수 있습니다. 게임을 대체하는 완제품이 아니라 화면 구현 참고용이며 점수나 보상 이벤트를 전송하지 않습니다. HTML을 저장해 소스를 확인하거나 프로젝트에 맞게 바꿔 사용하세요.
등록 전에 배포 URL을 iframe 안에서 검증합니다. 로컬 wrapper는 화면 검증용이며 tokenApps session을 제공하지 않습니다. 외부 HTTPS 게임의 현재 플레이어 sandbox 권한은 다음과 같습니다.
<iframe
src="https://game.example.com/embed"
title="My game"
style="display:block;width:390px;height:640px;border:0;max-width:100%"
sandbox="allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-pointer-lock allow-orientation-lock allow-presentation allow-same-origin"
allow="fullscreen; accelerometer; gyroscope; autoplay; clipboard-write"
></iframe>
테스트 wrapper의 출처도 게임의 framing policy에서 허용되어야 합니다. 검증 편의를 위해 운영 정책을 무조건 해제하지 마세요. 최종 빌드는 실제 tokenApps 미리보기/플레이어 안에서도 확인합니다.
브라우저 창이 아니라 iframe 내부의 CSS px 기준으로 310x360, 380x640, 558x160, 834x220, 740x780, 1340x560, 1894x875를 테스트합니다. 이는 스트레스 테스트 크기이며 고정된 호스트 계약이 아닙니다. 게스트/로그인 상태에 따라 호스트 UI 높이가 달라질 수도 있습니다. 지원하지 않는 작은 프레임은 정확히 보고하세요.
브라우저 개발자 도구에서 실행 컨텍스트를 게임 iframe으로 바꾸고 다음을 확인합니다.
const root = document.documentElement;
console.table({
frameWidth: innerWidth,
frameHeight: innerHeight,
pageWidth: Math.max(root.scrollWidth, document.body.scrollWidth),
pageHeight: Math.max(root.scrollHeight, document.body.scrollHeight),
});
불필요한 문서 overflow가 없고 모든 조작 버튼이 보이며 눌릴 때만 통과입니다. overflow:hidden으로 잘라버리면 수치상 통과해도 게임은 실패할 수 있습니다. 내부 스크롤 컨테이너도 확인하세요. 도움말·설정 대화상자의 의도적인 스크롤은 허용하지만 플레이를 위해 페이지를 스크롤해야 하면 안 됩니다.
시작 → 플레이 → 일시정지 → 재개 → 게임 오버 → 재시작을 수행하고, 진행 중 회전·크기 변경 후 같은 점수와 상태가 유지되는지 확인합니다. 터치·키보드 focus·오디오 활성화·저장소 차단·SDK/네트워크 실패·게스트에서 로그인·전체화면 거절도 테스트합니다. 실제 지원하는 iOS/Android 기기에서 확인하고, 데스크톱 에뮬레이션만으로 실기기 통과를 주장하지 않습니다.
8. 배포 완료 후 플레이 URL 등록하기
/embed를 배포하고 직접 접속·새로고침·framing 헤더·플레이를 검증합니다.- 그 정확한 주소를 기본 플레이 URL로 제출합니다. 기존 게임은 검수/운영 절차로 등록 URL을 변경해야 합니다. 기기 대응 요청서를 다운로드하거나 저장하는 것만으로 플레이 URL이 바뀌지는 않습니다.
- 요청서의 모바일/PC URL은 검수 참고용입니다. 분리 빌드는 기본 URL에서 적절한 빌드로 연결하고 임베드 모드를 유지해야 합니다.
- 실제 tokenApps 플레이 페이지를 다시 확인합니다. SDK ‘연동 완료’와 헤더 검사 성공은 반응형 화면 검수 통과를 뜻하지 않습니다.
게임 플레이 URL에 https://tokenapps.io/app/your-slug/play를 넣지 마세요. 호스트 플레이어를 다시 자기 안에 넣는 구조가 됩니다.
증상별 해결 방법
| 증상 | 확인·수정할 부분 |
|---|---|
| 게임 위아래에 홈페이지 헤더·FAQ가 나옴 | 잘못된 진입 URL 또는 /embed가 홈페이지 레이아웃을 상속 |
| SDK를 넣었는데 스크롤이 남음 | 게임 문서의 루트와 넘치는 컨테이너 확인; SDK는 레이아웃을 관리하지 않음 |
| 스크롤은 없는데 버튼이 안 보임 | HUD·보드·버튼 배치 전에 overflow를 숨김 |
| 보드는 맞는데 터치 위치가 어긋남 | 입력 좌표에서 contain 배율과 여백을 역변환 |
| 회전·로그인마다 게임이 처음부터 시작됨 | resize 또는 반복 session에서 엔진을 다시 생성 |
| 빈 화면·연결 거부 | 최종 URL·HTTPS·CSP·X-Frame-Options·콘솔·실행 오류 확인 |
| 새 탭에서는 되지만 tokenApps 안에서 멈춤 | 저장소 예외·중첩 iframe·SDK/인증 필수 대기 확인 |
/embed 이동은 되지만 새로고침하면 404 |
배포 경로 또는 SPA fallback 누락 |
바이브 코딩 도구에 그대로 전달할 요청문
기존 게임을 tokenApps 임베드에 맞게 수정해줘. 먼저 현재 라우팅, 레이아웃,
게임 엔진, SDK 초기화, 배포 설정을 확인해줘.
1. 실제로 작동하는 /embed 또는 동등한 게임 전용 URL을 구현해줘.
기존 게임 로직·에셋·저장 형식을 재사용하고 공개 홈페이지는 유지해줘.
2. 임베드에서 홈페이지 헤더·메뉴·히어로·푸터·FAQ·긴 랭킹 영역을 제외해줘.
게임 보드·점수·목숨/코인·일시정지·사운드·재시작·필수 조작은 유지해줘.
3. iframe의 가로와 세로 안에 모두 맞춰줘. HUD와 버튼 공간부터 확보하고
ResizeObserver와 contain 배율로 보드를 맞춰줘. Canvas/WebGL 해상도와
입력 좌표도 갱신해줘. CSS 늘리기나 overflow:hidden으로 버튼을 자르면 안 돼.
4. 지원한다고 표시한 모바일 터치·PC 키보드/마우스를 구현해줘.
터치 버튼은 44 CSS px 이상으로 유지하고 짧은 가로 화면·회전·전체화면
거절에 대응해줘. resize 때 라운드를 유지하고 미지원 환경은 명시해줘.
5. SDK v1.4 연동을 유지해줘. ready 전에 지속되는 리스너를 한 번 등록하고
SDK·네트워크·저장소가 없어도 게스트 시작이 가능하게 해줘. 반복 session으로
재시작하지 말고 중첩 iframe·SDK 중복 로드·존재하지 않는 resize API는 피해야 해.
6. 실제 점수·저장·완료 이벤트를 유지해줘. 자체 TAP 지급이나 로드/resize 시
완료 호출은 추가하지 말고, 플랫폼 사용자 식별과 기존 SDK를 사용해줘.
7. 경로를 배포하고 HTTP frame-ancestors에 https://tokenapps.io를 허용해줘.
해당 문서의 충돌하는 framing 헤더를 제거하되 다른 CSP 규칙은 유지해줘.
8. iframe 310x360, 380x640, 558x160, 834x220, 740x780,
1340x560, 1894x875에서 불필요한 스크롤·잘린 버튼이 없는지 검증해줘.
시작→플레이→일시정지→재개→종료→재시작, 진행 중 resize, 게스트/로그인,
저장소 차단·오디오·전체화면 거절도 확인해줘.
9. 배포한 게임 전용 URL, 변경 파일, 기기/브라우저/빌드 버전, PC·모바일 캡처,
통과·실패·미검증 결과와 남은 문제를 알려줘. 실기기 미검증은 통과라 쓰지 마.
먼저 https://tokenapps.io/ko/dev/sdk 와 https://tokenapps.io/ko/dev/embed 를 읽어줘.
참고 소스: https://tokenapps.io/sdk/examples/embed.html