# 토스앱에 내 서비스를 오픈해 보세요

명령어 한 줄이면 미니앱 개발이 시작돼요. 만들고, 등록하고, 토스 유저에게 선보이는 것까지 여기서 안내해요.

#### 터미널에 명령어 한 줄로 시작해요

```bash
npx create-ait-app < app-name >
```

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><strong>처음 시작한다면</strong></td><td><ul><li><a href="/spaces/NbJTp2UkOUSb6YpQegOx">바이브 코딩으로 미니앱 만들기</a></li><li><a href="/spaces/8pQgXiR5QAzduV54W8Om/pages/PYXSOGEQWigeFny1Ii8G">AI로 콘솔 사용하기</a></li></ul></td><td><a href="https://developers-apps-in-toss.toss.im/ai-vibe-coding">https://developers-apps-in-toss.toss.im/ai-vibe-coding</a></td><td></td></tr><tr><td><strong>개발 중이라면</strong></td><td><ul><li><a href="/spaces/bbsGTd7OgbyqnSM8Iwcy/pages/SqCHWXFkl9678IgUQIqv">API·SDK 한눈에 보기</a></li><li><a href="/pages/PkKO7UD8Fro5UjAuAeHa">미니앱 테스트하기</a></li></ul></td><td><a href="https://developers-apps-in-toss.toss.im/documentation">https://developers-apps-in-toss.toss.im/documentation</a></td><td></td></tr><tr><td><strong>도움이 필요하다면</strong></td><td><ul><li><a href="/spaces/8pQgXiR5QAzduV54W8Om/pages/RXeA1fUdBlbDa0dH2nKv">자주 묻는 질문</a></li><li><a href="https://techchat-apps-in-toss.toss.im/">개발자 커뮤니티</a></li><li><a href="https://apps-in-toss.channel.io/">문의하기</a></li></ul></td><td></td><td></td></tr></tbody></table>


# 서비스 정책


# 서비스 오픈 정책

{% hint style="info" %}
**확인해 주세요**

앱인토스 미니앱 출시를 위한 필수 정책이에요. 가이드를 지키지 않으면 **앱 정보와 앱 출시 단계에서 반려**될 수 있으며, 기존 **출시된 미니앱의 운영 제한**이 적용될 수 있어요. 서비스 오픈 정책은 안정적인 운영과 사용자의 보호를 위해 수시로 개정될 수 있어요.
{% endhint %}

### 1. 제한되는 서비스/콘텐츠

앱인토스 미니앱 서비스가 토스 플랫폼 정책, 관련 법령, 사용자 보호 기준에 부합하는지를 사전에 점검해 주세요. 아래에 해당하는 서비스/콘텐츠는 출시가 불가해요.

**1) 디지털 자산 및 가상자산 관련 서비스**

디지털 자산의 소유, 이전, 저장, 거래, 중개, 발행(NFT 포함) 등의 기능을 제공하는 서비스는 법적 요건 충족 여부와 관계없이 토스 플랫폼에서는 자산 손실, 소비자 피해, 자금세탁 리스크에 따라 등록이 불가해요.

**2) 자금세탁 가능성이 있는 서비스**

미니앱 내에서 현금 또는 유사 자산의 직접적인 교환, 전환, 환불 기능이 포함된 경우, 거래 구조상 자금세탁 통로로 악용될 수 있기 때문에 등록이 불가해요.

**3) 불법 또는 부정행위를 조장하는 서비스**

법적으로 금지되거나 사회적 물의를 일으킬 수 있는 신분 조작, 해킹, 불법 문서 제공, 정보 수집 우회 등의 기능이 포함된 서비스는 명백히 등록이 불가해요.

**4) 사행성 및 복권/베팅성 콘텐츠 포함 서비스**

사행성 요소가 포함된 콘텐츠는 사용자 재산상 손실, 중독 유발, 연령 제한 문제 등으로 위법 소지가 있으며, 사용자 보호 및 서비스 신뢰도 확보를 위해 등록이 불가해요.

**5) 금융 서비스 및 송금 관련 서비스**

대출, 보험, 카드, 증권 등 금융 상품 관련 서비스는 법적 인허가 여부와 관계없이 소비자 보호, 금융정보의 정확성, 오인 가능성 등으로 인한 운영 리스크를 방지하기 위해 등록이 불가능하며, 향후 내부 정책 및 기준 정비에 따라 오픈 여부가 검토될 수 있어요.

**6) 투자 자문, 리딩방, 유료 정보 제공 서비스**

특정 종목 추천이나 투자 전략 안내 등으로 개인 투자자의 의사결정에 영향을 미치는 서비스는 운영 리스크 및 정책적 수용 미비로 인해 등록이 불가해요.

**7) 의료 관련 서비스**

비대면 진료 제공 또는 연결, 의료 행위로의 직접적인 연결, 병원 예약 기능, 병원으로부터 광고비를 수취하는 구조(유저 유입 기반 수익 모델), 병원 홍보/마케팅으로 해석될 수 있는 수익 구조 등의 경우 서비스 출시가 불가해요.

**8) 이외 내부 정책상 승인 불가 서비스**

법률 위반 여부와 관계없이 토스의 브랜드 신뢰성, UX 정책, 리스크 관리 방침에 따라 등록이 제한될 수 있어요.

* 심사 결과는 내부 정책 및 리스크 검토 절차에 따라 변경될 수 있으며, 사전 등록을 보장하지 않아요.
* 서비스 특수성, 비즈니스 모델에 따라 사전 상담 또는 추가 설명 요청이 있을 수 있어요.
* 기존 회사 및 서비스를 단순 홍보하기 위해서 앱인토스 미니앱을 출시할 수 없어요.
* 위 내용 외에도 사용자 보호 및 신뢰성 확보를 위한 추가 기준이 적용될 수 있어요.

***

### 2. 확인이 필요한 카테고리

아래 카테고리는 **서비스 운영에 필요한 인허가/등록/신고** 등 자격 조건이 충족 되어야 할 수 있어요.

* 의료: 아래 조건을 모두 충족하는 공공데이터 기반 의료 정보 조회 서비스는 출시할 수 있어요.
  * 공공데이터 기반 의료 정보 조회만 제공
  * 순위, 추천, 예약 기능 없음
  * 병원 목록을 동일 기준으로 노출
  * 데이터 출처 명시
  * 병원 광고비 및 유료 프로모션 없음 (병원으로부터 어떠한 형태의 금전 수취도 불가)

아래 내용으로는 서비스 출시가 불가해요.

* 쇼핑몰: 고객센터, 가품 방지 및 환불 정책을 갖추지 않은 서비스는 출시할 수 없어요.
* 교육: 국가 전문 자격증/국가 기술 자격증이 없는 상태에서 수익이 발생하는 서비스는 출시할 수 없어요.

***

### 3. 미니앱 어뷰징 방지 정책

앱인토스는 사용자의 더 높은 수준의 경험과 서비스 품질을 유지하기 위해 동일 워크스페이스 내의 같은 기능의 미니앱을 반복적으로 출시하는 것을 제한하고 있으며, 위반 시 콘텐츠 미노출 및 서비스 운영이 제한될 수 있으니 아래 내용을 꼭 확인해 주세요.

{% hint style="info" %}
**꼭 확인해 주세요**

동일 워크스페이스 내에서 이미 등록된 미니앱과 완전히 다른 목적의 기능만 새로운 미니앱으로 등록할 수 있어요. 검수 단계에서 해당 워크스페이스에 이미 등록된 미니앱과 구조의 유사도가 높다고 판단되면 반려 및 수정을 요청할 수 있어요.
{% endhint %}

**1) 동일 워크스페이스 내에서 유사한 핵심 기능 기반의 앱을 여러 개 출시할 수 없어요.**

핵심 기능이 같다면 테마나 대상, 주제, 스타일 등만 다르게 구성하더라도 별도의 미니앱으로 인정되지 않아요. 새로운 기능이나 콘텐츠를 추가하고 싶을 때는 기존 앱을 업데이트 해주세요.

* **예시 :** 동일 파트너사에서 ‘AI 여자 얼굴 만들기’, ‘AI 남자 얼굴 만들기’ 등과 같이 주제만 다르고 결과물만 달라지는 개별 미니앱으로 출시하는 경우

**2) 동일한 브랜드를 활용하여 여러 개의 미니앱 출시는 불가해요.**

검색 또는 노출을 과도하게 점유하기 위함 등과 같이 동일 워크스페이스 내에서 이름, 로고, 색상 등 브랜드 아이덴티티를 반복하여 여러 개의 미니앱으로 출시하는 것은 허용되지 않아요.

다만, 동일 워크스페이스에서 운영하는 서비스라도 사용자에게 제공하는 서비스 목적과 핵심 기능이 명확히 다르다면 각각 별도의 미니앱으로 출시할 수 있어요.

즉, 하나의 브랜드에서 서로 다른 서비스를 제공하는 경우에는 각각의 미니앱으로 운영할 수 있어요.

* A 브랜드
  * A 렌탈 서비스: 가전·가구 렌탈 신청 및 관리 서비스
  * A 청소 서비스: 방문 청소 예약 및 관리 서비스

위와 같이 서비스 목적과 핵심 기능이 명확히 다르며 이름, 로고, 색상 등을 하나의 브랜드로 표현하지 않는다면 각각의 별도의 미니앱으로 등록할 수 있어요.

**3) 위반 시 조치**

정책 위반 시 다음 조치가 적용될 수 있어요.

* 콘텐츠 수정 요청
* 서비스 미노출 처리
* 반복 위반 시 미니앱 등록 및 운영 제한

***

### 4. 자사 앱 설치/외부 링크

앱인토스는 **자사 앱 설치 유도**와 **외부 링크 이동**을 제한하고 있어요. 위반 시 콘텐츠 비노출 또는 서비스 운영 제한이 적용될 수 있어요.

**1) 자사 앱 설치를 유도할 수 없어요.**

‘자사 앱 설치 유도’란, 미니앱 이용 중 별도 **앱 설치를 권유/강요**하는 모든 행위를 말해요.

> 앱인토스의 핵심 가치: 앱 설치 없이 토스 앱 안에서 간편하게 이용 외부 앱 설치 유도는 사용자 경험을 해치고 플랫폼 일관성을 저해하므로 허용되지 않아요.

**① 자사앱 설치 유도 예시**

* “앱을 설치하시면 더 많은 기능을 이용할 수 있어요.”
* “전용 쿠폰을 받으시려면 자사 앱을 설치하세요.”
* “앱 다운로드 후 첫 구매 시 할인해 드려요.”

**② 제한되는 행위**

* 앱 설치를 직접적으로 유도하는 문구
* 앱 설치 유도 배너/이미지 삽입
* 앱 마켓 링크 삽입
* 앱 설치 시 혜택 제공 안내
* 그 외 설치 유도로 판단될 수 있는 모든 콘텐츠/기능

**2) 외부 링크는 허용된 경우에 한해 제한적으로 사용**

‘외부 링크 이동’은 사용자를 토스 미니앱 환경 밖의 웹/앱으로 보내는 행위예요. 서비스 본질과 직접 관련 없는 결제창/다운로드 페이지/홍보 랜딩 등은 인정되지 않아요.

**① 예시(제한 대상)**

* “자세한 내용은 홈페이지에서 확인하세요.”
* “이동 후 결제를 완료해주세요.”
* “외부 웹사이트에서 가입을 진행해주세요.”

→ 외부 이동은 신뢰·편의성 저하, 개인정보·보안 리스크, 추적 불가 등 문제로 이어질 수 있어요.

**② 제한되는 행위**

* 미니앱 내에서 ‘앱 내 기능’을 완결적으로 제공하지 않는 구조
* 외부 결제창으로 이동
* 앱 다운로드/설치를 위한 외부 페이지 연결
* 공유하기 링크가 자사 웹사이트로 랜딩되는 경우
* 주요 기능/흐름이 외부 링크에 의존하는 구조

**③ 허용될 수 있는 경우**

* 법률상 고지/필수 안내 목적의 외부 링크
* 공공기관·제휴기관 공식 페이지 연결
* 단순 정보 확인을 위한 타사 웹사이트 이동
* 미니앱 기능 내에서 완결되지 않는 일부 특수 상황
  * 각 제품을 소개·추천 후 최저가 구매 플랫폼으로 이동
  * 혜택 제공을 위해 쿠폰 발급 플랫폼으로 이동

**기존 앱의 모든 기능을 옮겨와야 하나요?** 모든 기능을 그대로 이식할 필요는 없어요. 다만, 콘솔에 설정한 **‘앱 내 기능’은 미니앱 내에서 완결적으로 경험**할 수 있어야 해요.

* “\~ 견적내기”라면, 미니앱에서 견적 산출까지 가능해야 해요.
* “\~ 견적내고 결제하기”라면, 견적\~결제까지 미니앱에서 진행되어야 해요.
* 먼저 “\~ 견적내기”로 출시 후 “\~ 결제하기” 기능을 후속 업데이트해도 괜찮아요.

**3) 위반 시 조치**

* 콘텐츠 수정 요청 및 고지
* 미니앱 서비스 준비 중으로 변경 (미노출)
* 반복 위반 시 미니앱 등록·운영 제한

***

### 5. 생성형 AI서비스

생성형 AI를 활용하여 텍스트, 이미지, 음성, 영상 등 결과물을 생성 및 제공하는 등 자동 응답, 요약, 추천, 생성 기능 등 AI가 직접 산출한 결과를 사용자에게 노출하는 미니앱은 꼭 준수해 주세요.

아래 내용은 법률 개정 등에 따라 변동 될 수 있으며 관련하여 파트너사에서는 서비스 출시 전 자체적인 검토(법률 검토 등)등을 권장드려요.

**1) 사전 고지 의무**

* 사용자가 서비스를 처음 이용하거나 생성형 AI 기능을 최초로 사용하는 시점에 해당 서비스 또는 기능이 생성형 AI를 활용한다는 사실을 사용자가 인지할 수 있도록 고지해야 해요.

**2) 표시 의무**

* 사용자에게 노출되는 결과물이 생성형 AI에 의해 생성된 경우 해당 결과물이 AI 결과물임을 명확히 인식할 수 있도록 표시해야 해요. (라벨, 배지, 워터마크 등 사람이 즉시 인식 가능한 방식 등)

**3) 위반 시 조치**

* 관련 법령에 따라 최대 3,000만 원의 과태료가 부과될 수 있어요.
* 관계 기관의 자료 제출 요구가 있을 수 있어요.
* 현장 조사가 진행될 수 있어요.
* 서비스 중지 또는 시정명령 대상이 될 수 있어요.

***

### 6. 로그인 / 결제 / 광고 연동 정책

앱인토스 미니앱에서 사용할 수 있는 로그인, 결제, 광고 방식은 아래로 제한돼요.

**1) 로그인**

* 미니앱 내 로그인은 **토스 로그인만** 사용할 수 있어요.
  * 그 외 로그인 기능 및 소셜, 간편 로그인은 사용할 수 없어요.

**2) 결제**

* 실물 상품 결제는 **토스페이**만 사용할 수 있어요.
  * 토스페이먼츠(PG사)를 포함한 기타 결제 수단은 허용되지 않아요.
* 디지털 상품(비실물) 결제는 **인앱결제**만 사용할 수 있어요.

**3) 광고**

* 미니앱 내 광고는 **앱인토스 전면형, 보상형, 배너 광고**만 사용할 수 있어요.
* 외부 광고 네트워크를 통한 광고 연동은 허용되지 않아요.


# 서비스별 주의사항

앱인토스에서 제공되는 서비스 유형별 주요 정책과 유의사항을 안내드려요.

### 웹보드 게임 <a href="#web-board" id="web-board"></a>

앱인토스는 건전하고 공정한 게임 환경을 위해 웹보드 게임에 대해 법적·운영적·UX·결제 관련 요구사항을 엄격히 관리해요. 아래 요건을 충족하지 않으면 심사 또는 모니터링 과정에서 서비스 오픈이 제한되거나 제재를 받을 수 있어요.

웹보드 게임에 해당하는 경우, **앱인토스 콘솔에서 앱 정보 등록 시 반드시 웹보드 여부를 체크해 주세요.**

{% hint style="info" %}
**웹보드 게임이란**

온라인에서 즐기는 보드·카드·전략형 게임(예: 포커, 고스톱, 맞고, 체스, 장기 등)을 말해요. 게임머니로 베팅하고 상대와 승패를 겨루는 구조를 가지지만, **실제 현금 거래는 엄격히 금지돼요.**
{% endhint %}

***

#### 1. 법적 · 규제 준수

웹보드 게임은 **관련 법령에 따른 규제 준수**가 필수예요. 아래 요건을 충족하지 않으면 출시가 제한되거나 서비스가 중단될 수 있어요.

* **게임물관리위원회 등급분류(GRAC) 필수:** 모든 게임은 「게임산업진흥에 관한 법률」 제21조에 따라 게임물관리위원회(GRAC)의 등급분류를 반드시 받아야 해요.
  * 앱스토어와 구글플레이의 IARC 등급은 국내에서는 효력이 없어요.
* **사행성 방지 및 기준 준수:** 게임 내에서 베팅, 도박, 재산상 이익을 유도하는 구조는 금지돼요.
  * 문화체육관광부 고시 「게임제공업자의 준수사항」과 「게임산업진흥법」 제32조를 준수해 주세요.
* **청소년 보호 의무:** 청소년 이용불가 게임물은 청소년유해매체물 표시를 적용하고, 본인 인증과 이용 제한 기능을 포함해야 해요.
  * 「청소년 보호법」 제26조, 「게임산업진흥법」 제24조를 준수해 주세요.
* **개인정보 보호:** 이용자의 개인정보는 「개인정보보호법」 제28조와 「정보통신망법」 제28조에 따른 안전성 확보 조치를 이행해 주세요.

***

#### 2. 서비스 품질 기준

출시된 미니앱은 안정적으로 운영되어야 하며, 보안, 접근성, 장애 대응 체계가 갖춰져야 해요.

* **서버 안정성 및 장애 대응:** 「정보통신망법」 제45조에 따라 서비스 장애가 발생하면 신속하게 대응하고 장애 이력을 체계적으로 관리해 주세요.
* **보안성 강화 및 부정행위 방지:** 봇, 매크로, 계정 공유, 부정 결제 등 비정상적인 이용 행위를 탐지하고 방지해 주세요.
  * 「게임산업진흥법」 제32조,「정보통신망법」 제28조를 준수해 주세요.
* **접근성 및 최적화:** 모든 이용자가 접근할 수 있는 UI와 UX를 제공해 주세요.
  * 필요한 경우 「장애인차별금지법」 제21조에 따른 접근성 기준을 준용할 수 있어요.

***

#### 3. 운영 및 정책

* **고객센터 운영 및 분쟁 해결:** 이용자 불만이나 분쟁이 발생했을 때 고객센터를 통해 처리 절차를 안내하고 대응해 주세요.
  * 「전자상거래 등에서의 소비자보호법」 제20조를 준수해 주세요.
* **확률형 아이템 정보 공개:** 확률형 아이템은 구성 내용과 확률 정보를 사용자가 쉽게 확인할 수 있도록 공개해 주세요.
  * 「게임산업진흥법」 제32조의 3항을 준수해 주세요.
* **성인 인증 기능 연동:** 웹보드 게임은 만 19세 이상만 이용할 수 있어요. 반드시 성인 인증(본인 확인) 기능을 연동해 주세요.
  * 성인 인증 연동 방법은 별도 가이드를 참고해 주세요.

***

#### 4. 결제·정산 관련

결제 과정은 안전해야 하며, 이용자에게 명확하게 안내되어야 해요.

* **결제 안전성 확보:** 「전자금융거래법」 제6조에 따라 결제 과정에서 보안성과 이용자 보호 조치를 이행해 주세요.
* **환불 및 취소 정책 안내:** 청약 철회, 취소, 환불 절차를 명확히 안내하고 이용자가 쉽게 확인할 수 있도록 해 주세요.
  * 「전자상거래법」 제17조, 제18조를 준수해 주세요.
* **매출 보고 및 정산 투명성:** 매출 내역과 세금계산서 발급 등 거래 내역을 투명하게 관리해 주세요.
  * 「부가가치세법」 제32조를 준수해 주세요.
* **결제 한도 관리 시스템:** 이용자별 일·월 결제 한도를 설정하고 이를 관리할 수 있는 기능을 제공해 주세요.
  * 「게임산업진흥법」 제24조의 2항을 준수해 주세요.

***

#### 5. UX/UI 및 브랜드 적합성

앱인토스 미니앱으로 출시되는 웹보드 게임은 일관된 사용자 경험을 제공해야 하며, 광고와 홍보도 공정하게 운영되어야 해요.

* **건전성 확보:** 게임 내 표현과 구성은 청소년 보호법, 게임산업진흥법에 따라 사회적 통념을 해치지 않도록 설계해 주세요.
* **UI/UX 일관성 유지:** 앱인토스 미니앱 디자인 가이드와 인터랙션 규칙을 반드시 따라 주세요.
  * UI/UX 가이드를 참고해 주세요.
* **광고 및 홍보 제한:** 서비스와 무관한 과도한 광고, 외부 유도형 홍보, 오해를 불러일으킬 수 있는 문구는 사용하지 말아 주세요.
  * 「표시·광고의 공정화에 관한 법률」 제3조를 준수해 주세요.

***

#### 6. 심사·사후 모니터링

앱인토스는 웹보드 게임을 출시 검토 → 운영 → 사후 점검의 단계로 관리해요.

* **출시 검토 항목:** 출시 전 다음 항목을 종합적으로 검토해요.
  * 게임물 등급
  * 서비스 품질
  * 과몰입 방지 기능
  * 보안성
* **사후 모니터링:** 출시 이후에도 다음 항목을 주기적으로 점검해요.
  * 법령 준수 여부
  * 과몰입 방지 기능의 정상 작동 여부
  * 결제 한도와 확률 공개 준수 여부
* 위반 사항이 확인되면 콘텐츠 수정 요청이나 비노출 등의 조치가 이뤄질 수 있어요.
  * 관련 근거「게임산업진흥법」 제24조의 2항, 제32조「전자상거래법」 제20조

***

### 만남, 소개팅 <a href="#social" id="social"></a>

앱인토스 미니앱에서 데이팅, 만남, 소셜 성격의 서비스를 제공하려면 사용자에게 안전하고 건전한 만남 경험을 제공하는 것이 가장 중요해요. 데이팅·소개팅·만남을 목적으로 하는 서비스를 운영하는 파트너사는 아래 기준을 반드시 지켜주세요.

만남, 소개팅 서비스에 해당하는 경우, **앱인토스 콘솔에서 앱 정보 등록 시 반드시 카테고리는 ‘소셜’로 선택하고 만남, 소개팅 여부를 체크해 주세요.**

{% hint style="info" %}
**데이팅, 소개팅, 만남 서비스란**

이용자 간의 교류와 인연 형성을 목적으로 연결해 주는 서비스예요. 프로필 기반 매칭, 채팅, 추천 알고리즘 등을 사용해 온라인에서 사람과 사람을 연결하고 새로운 관계를 만드는 소셜 매칭 플랫폼에 해당해요.
{% endhint %}

***

#### 1. 법적·규제 준수

* **청소년 보호**
  * **만 19세 미만 이용자는 가입과 이용이 불가능**해요.
  * 서비스 진입 전 본인인증(성인 인증) 절차를 반드시 거쳐야 해요.
  * 청소년 접근이 확인되면 서비스는 즉시 퇴출될 수 있어요.
* **개인정보 및 위치정보 보호**
  * 서비스 제공에 직접 필요한 최소한의 정보만 수집해 주세요.
  * 성적 지향, 위치 정보 등 민감정보는 이용자의 명시적 동의가 있을 때만 수집할 수 있어요.
  * 수집한 정보는 암호화해서 안전하게 저장하고, 이용자가 탈퇴하면 즉시 삭제해야 해요.
* **불법 행위 방지**
  * 조건만남, 성매매, 보이스피싱, 사기, 스토킹 등 불법 행위와 연결되지 않도록 적극적으로 관리해 주세요.
  * 신고와 모니터링 체계를 갖추고, 불법 행위가 확인되면 즉시 차단하고 필요한 경우 신고할 수 있어야 해요.
  * 이용약관에는 불법 행위 방조 금지와 사용자 책임에 대한 조항을 반드시 포함해 주세요.
* **결제 및 소비자 보호**
  * 유료 결제 서비스는 환불 정책과 자동 결제 해지 조건을 명확히 안내해 주세요.
  * 전자상거래법 등 관련 법령을 준수하고, 이용자 민원이 발생하면 신속하게 대응해 주세요.

***

#### 2. 서비스 출시 기준

서비스를 출시하려면 최소한 아래 항목을 충족했는지 확인해야 해요. 앱인토스 콘솔에서 앱 정보를 등록할 때 안내되는 체크리스트를 하나씩 확인한 후 등록해 주세요.

<details>

<summary>체크리스트 보기</summary>

□ 법인 등록이 완료됐어요.

□ 허위 프로필·도용 사진을 차단하기 위한 AI·운영·수동 검증 프로세스를 갖추고 있어요.

□ 앱 내 불법 광고(조건 만남, 성매매 등)를 탐지·차단하는 시스템이 있어요.

□ 사용자 신고 기능(원클릭 신고, 차단)을 제공하고 있어요.

□ 신고 접수 후 24시간 이내 대응 프로세스를 운영하고 있어요.

□ 반복 위반자를 영구 차단하는 정책이 있어요.

□ 대화 내용을 저장할 수 있는 시스템이 있어요.

□ 민감정보(성적 지향, 위치 등)는 최소한으로 수집하고 있어요.

□ 청소년 보호법, 개인정보보호법 등 관련 법령을 준수하고 있어요.

□ 불법 행위 발생 시 수사기관에 협조할 수 있는 체계를 갖추고 있어요.

□ 유료 결제 환불 및 분쟁 해결 절차가 있어요.

□ 위반 사항이 발생하면 토스로 신속하게 공유할 수 있어요.

</details>

***

#### 3. 운영 요구사항

* **연령 제한**
  * 서비스는 만 19세 이상만 이용할 수 있어요.
  * 청소년 접근이 확인되면 즉시 차단되며, 상황에 따라 서비스가 퇴출될 수 있어요.
* **신뢰성 확보**
  * 실명 기반 본인인증을 반드시 적용해 주세요.
  * 허위 프로필과 도용을 막기 위한 검증 절차를 운영해 주세요.
  * 연령, 직업, 지역 같은 기본 정보는 입력값을 기준으로 수집하고, 직업이나 소득 등 특정 정보는 증빙 자료를 통한 선택 검증 방식으로 운영할 수 있어요.
* **안전 관리 및 모니터링**
  * 운영 인력 모니터링과 AI 모니터링 시스템을 함께 운영해 주세요.
  * 조건만남 등 불법 키워드는 자동으로 필터링해 주세요.
  * 신고가 접수되면 24시간 이내에 처리해 주세요.
  * 분쟁이나 조사에 대비해 대화 내용을 저장하고 확인할 수 있는 시스템을 권장해요.
* **개인정보 보호**
  * 개인정보는 최소한으로 수집하고 암호화해서 저장해 주세요.
  * 이용자가 탈퇴하면 개인정보를 즉시 삭제해 주세요.
  * 민감정보는 별도 동의를 받은 경우에만 사용해 주세요.
* **법적 대응 체계**
  * 불법 행위가 발생하면 즉시 수사기관에 협조해 주세요.
  * 피해자 보호를 위해 긴급 차단 프로세스를 운영해 주세요.

***

#### 4. 계약 및 제재 정책

* **필수 약관 조항**
  * 불법 행위에 대한 책임은 전적으로 사용자에게 있어요.
  * 토스는 신고 접수와 조치 범위 내에서만 책임을 져요.
* 운영 위반 시 조치: 아래와 같은 경우에는 **서비스 운영이 중단될 수 있어요.**
  * 청소년 접근을 허용한 경우
  * 성매매 광고나 불법 행위를 방치한 경우
  * 대규모 개인정보 유출 등 중대한 위반이 발생한 경우

***

### 민감 콘텐츠 <a href="#sensitive" id="sensitive"></a>

앱인토스는 성인 사용자가 다양한 콘텐츠를 이용할 수 있도록 지원하면서도, 불법·유해 콘텐츠를 예방하고 토스의 신뢰성과 브랜드 가치를 지키기 위해 민감 콘텐츠에 대한 주의사항을 안내해요.

{% hint style="info" %}
**민감 콘텐츠란**

민감 콘텐츠는 사용자에게 **불쾌감, 불안감, 불편함**을 줄 수 있는 표현이나 장면이 포함된 콘텐츠를 말해요. 법적으로 불법은 아니더라도, 사회적 통념상 논란이 될 수 있는 소재나 연출이 포함된 경우도 민감 콘텐츠에 해당해요.

앱인토스에서는 민감 콘텐츠를 크게 다음 세 가지로 나누어 관리해요.

* 선정성
* 폭력성
* 불법·범죄 조장
  {% endhint %}

***

#### 민감 콘텐츠 유형별 기준

**선정성**

* 신체 특정 부위 노출, 성행위 묘사, 자극적인 카메라 연출, 음란 행위 암시는 제한돼요.
* 성행위를 직접적으로 표현하거나, 성적 대상화를 주요 요소로 삼은 콘텐츠는 등록할 수 없어요.
* 음란한 유행어나 불쾌감을 주는 성적 표현도 사용하지 말아 주세요.

**폭력성**

* 단순한 액션이나 코믹한 격투 장면은 사용할 수 있어요.
* 잔혹한 유혈 표현, 학대나 고문 장면은 제한돼요.
* 폭력을 미화하거나 성적·혐오 맥락과 결합된 표현은 즉시 삭제돼요.

**불법·범죄 조장**

* 마약, 불법 도박, 불법 촬영, 성매매, 미성년자 등장 콘텐츠는 즉시 삭제되고 신고돼요.
* 불법 행위를 미화하거나 조장하는 콘텐츠는 사전 경고 없이 제한될 수 있어요.

{% hint style="info" %}
**꼭 확인해 주세요**

민감 콘텐츠 등급에서 경미 또는 주의 단계에 해당하더라도, 내부 심의 결과 수위가 높다고 판단되면 앱 출시가 제한될 수 있어요.

민감 콘텐츠로 분류되어도, 아래와 같은 내용은 포함할 수 없어요.

* 과한 신체 노출, 성행위 묘사, 성적 대상화, 성상품화
* 폭력·학대·혐오·차별·불법 조장 장면 포함
* 허위 정보, 극단적 발언, 사회적 논란을 유발할 수 있는 장면
  {% endhint %}

***

#### 민감 콘텐츠 여부는 어떻게 판단하나요?

앱 출시 검토 요청과 함께 검수가 진행되며, 이 과정에서 민감 콘텐츠 해당 여부를 판단해요. 민감 콘텐츠로 분류되면 워크스페이스에 등록된 멤버에게 메일로 안내해 드려요.

***

#### 민감 콘텐츠로 분류되면 어떻게 되나요?

미니앱이 민감 콘텐츠로 분류되면, 사용자가 미니앱에 진입하기 전에 안내 화면이 노출돼요. 사용자가 한 번 동의하면, 이후에는 민감 콘텐츠로 분류된 미니앱을 별도의 안내 UI 없이 이용할 수 있어요.

<figure><img src="/files/UN50AW7I4h8CBCVADyig" alt=""><figcaption></figcaption></figure>

***

#### 민감 콘텐츠 등급 기준표

| 항목       | 1단계 (경미)                     | 2단계 (주의)                             | 3단계 (출시 불가)                       |
| -------- | ---------------------------- | ------------------------------------ | --------------------------------- |
| 선정성      | 짧은 노출, 수영복·패션 수준 / 대사에 약한 암시 | 신체 특정 부위 강조, 키스·접촉 장면 반복 / 성적 농담 노골적 | 성행위 묘사, 노출 수위 높음, 자극적 편집 / 성적 대상화 |
| 폭력성      | 액션, 코믹 장면 수준 / 부상 없음         | 타격·고통 묘사, 피 보임 / 폭력적 대사 반복           | 유혈·잔혹 장면, 학대·고문 / 폭력 미화           |
| 혐오·차별    | 가벼운 놀림 수준 / 의도가 명확한 유머       | 특정 집단 조롱·비하 / 지속적 반복 (출시 불가)         | 인종·성별·장애 등 명시적 혐오 표현 / 폭력적 혐오 조장  |
| 약물·도박    | 배경에 등장 / 실제 섭취 아님            | 음주·흡연 장면 반복 / 도박 행위 등장               | 마약 사용·판매 묘사 / 도박 미화·권장            |
| 범죄 조장    | 상황적 설정 (예: 드라마 내 설정)         | 절도·폭행 등 범죄를 흥미 요소로 표현                | 불법행위 직접 촬영, 모방 유도 / 범죄 정당화        |
| 공포·자살·자해 | 긴장감 연출 수준 / 유혈 없음            | 공포 장면 반복 / 자해 언급 등장                  | 자살·자해 구체적 묘사 / 모방 위험 있음           |
| 기타 부적절   | 도전 영상·밈 등 경계 사례              | 위험한 행동(불장난, 위험 운전 등) 묘사              | 불법행위 조장 / 허위정보·극단행동 유도            |

***

### AI 채팅·상담

AI 채팅·상담 서비스는 이용자와의 대화를 통해 정보를 제공하거나 도움을 주는 서비스예요. 이런 서비스는 사용자의 상황과 감정에 직접적인 영향을 줄 수 있기 때문에, 이용자 안전과 사회적 책임을 충분히 고려한 운영이 필요해요.

{% hint style="info" %}
**꼭 확인해 주세요**

AI 채팅·상담 서비스는 안전성과 이용자 보호를 최우선으로 고려해 출시하고 운영해 주세요.
{% endhint %}

***

#### 1. 필수 운영 요건

AI 채팅·상담 서비스를 출시하는 파트너사는 아래 사항을 반드시 갖추어야 해요.

* 위험 키워드와 문맥을 함께 판단하는 필터링 시스템을 운영해 주세요.
* 사용자가 금지된 요청이나 부적절한 질문을 했을 때는명확하고 일관된 거절 응답을 제공해 주세요.
* 자살·자해 관련 발화가 감지되면, 이용자 안전을 고려한 안전 응답 프로토콜을 적용해 주세요.
* AI 모델이나 프롬프트를 변경할 경우,앱 배포 검토를 사전에 요청해 주세요.
* 부적절하거나 문제가 될 수 있는 응답이 발생했을 때즉시 수정하거나 차단할 수 있는 운영 체계를 갖춰 주세요.
* 동일한 AI 모델을 사용하는 자체 앱이나 웹 서비스가 있다면,동일한 기준을 일관되게 적용해 주세요.

***

#### 2. 우회 요청에 대한 대응 기준

사용자의 발화가 맥락상 위험하거나 불법 가능성이 있다면 요청 의도와 상관없이 모두 차단해야 해요. 표현 방식이 다르더라도, 결과적으로 위험한 정보가 전달될 가능성이 있다면 응답을 제공해서는 안 돼요. 다음과 같은 형태의 요청은 모두 금지 대상이에요.

* 소설이나 영화 설정임을 전제로 한 요청
* 교육용, 참고용, 이론 설명을 명목으로 한 요청
* 실제로 사용하지 않겠다는 전제를 둔 요청
* 불법 여부 설명을 요구하면서 과정이나 방법 설명으로 이어질 수 있는 요청
* 그 외 결과적으로 위험하거나 제한된 정보 제공으로 이어질 수 있는 요청

***

#### 3. AI 응답 생성 시 제한되는 콘텐츠

AI 채팅·상담 서비스에서는 아래와 같은 콘텐츠를 생성하거나 제공할 수 없어요.

* **불법·범죄 관련**
  * 마약, 총기, 폭발물, 무기 등의 제조·구매·사용 방법
  * 절도, 폭력, 해킹, 불법 침입 등 범죄 수행 방법
  * 범죄를 미화하거나 정당화하는 내용
* **약물·도박**
  * 마약의 복용, 제조, 판매와 관련된 정보
  * 도박 전략, 승률을 높이는 방법, 도박을 조장하는 내용
* **폭력·자해·자살**
  * 자해나 자살 방법에 대한 설명이나 구체적인 묘사
  * 모방 가능성이 있는 표현이나 안내
  * 타인에게 해를 가하는 방법
* **선정성**
  * 성행위에 대한 구체적인 묘사
  * 성적 자극을 목적으로 한 콘텐츠
  * 미성년자와 관련된 성적 내용
* **혐오·차별**
  * 특정 인종, 성별, 장애, 집단에 대한 비하나 혐오 표현
  * 폭력을 조장하거나 편견을 사실처럼 설명하는 내용
* **기타 제한 콘텐츠**
  * 위험한 행동을 조장하는 내용
  * 허위 정보 또는 극단적인 행동을 유도하는 표현
  * 개인정보 수집이나 제공을 유도하는 응답

***

#### 4. 위반 시 조치

가이드라인을 위반한 콘텐츠가 확인되거나, 동일하거나 유사한 위반이 반복되는 경우, 또는 중대한 위반이 발생한 경우에는 다음과 같은 조치가 이뤄질 수 있어요.

* 경고 조치
* 서비스 출시 제한
* 서비스 노출 중단

이용자 안전과 신뢰를 지키기 위해 AI 채팅·상담 서비스 운영 전반에서 위 기준을 반드시 준수해 주세요.

***

### 채팅 서비스

지인 또는 ID 기반으로 특정 사용자와 메시지를 주고받는 채팅 서비스를 앱인토스 미니앱으로 제공할 경우, 이용자 보호와 불법 행위 방지를 위해 아래 기준을 반드시 지켜주세요.

{% hint style="info" %}
**채팅 서비스란?**

* 연락처 또는 ID 기반으로 특정 사용자를 추가한 후 1:1 메시지를 교환하는 서비스예요. (예: 메신저 서비스, 지인 기반 채팅)
  {% endhint %}

{% hint style="info" %}
**⚠️ 꼭 확인해 주세요**

아래 기능이 포함된 경우 채팅 서비스가 아닌 **만남·소개팅 서비스**로 분류되며, 별도의 요건이 적용돼요. 해당하는 경우 만남, 소개팅 가이드를 확인해 주세요.

* 랜덤 매칭 또는 모르는 사람과의 연결 기능 (예: 랜덤 채팅)
* 근처 사용자 탐색 기능
* 공개 프로필 기반 메시지 발송
* 이성 추천 또는 좋아요 구매 등 만남 목적의 수익 모델
  {% endhint %}

***

#### **1. 이용자 보호 기능**

채팅 서비스는 이용자가 안전하게 서비스를 이용할 수 있도록 아래 기능을 반드시 제공해야 해요.

* **신고 기능**: 이용자가 불쾌하거나 문제가 되는 메시지·사용자를 즉시 신고할 수 있어야 해요.
* **차단 기능**: 특정 사용자의 메시지를 받지 않도록 차단할 수 있어야 해요.
* **계정 제재 정책**: 반복적으로 정책을 위반하는 이용자에 대한 계정 제한 또는 정지 정책을 운영해야 해요.
* **신고 대응 프로세스**: 신고 접수 시 운영자가 내용을 확인하고 적절한 조치를 취할 수 있는 프로세스를 갖춰야 해요.

***

#### **2. 불법 행위 방지**

채팅 서비스 내에서 아래 행위가 발생하지 않도록 적극적으로 관리해 주세요.

* 성매매 또는 조건만남을 유도하는 광고·메시지
* 금전 요구 또는 투자 권유를 통한 사기 행위 (로맨스 스캠 포함)
* 불법 광고 및 스팸 메시지
* 불법 정보 유통

위 행위가 방치되거나 확인될 경우 서비스 운영이 제한될 수 있어요.

***

#### **3. 개인정보 및 메시지 데이터 보호**

이용자의 메시지와 개인정보는 관련 법령에 따라 안전하게 보호해야 해요.

* **최소 수집 원칙**: 서비스 운영에 필요한 최소한의 개인정보만 수집해 주세요.
* **데이터 암호화**: 수집한 정보와 메시지 데이터는 암호화하여 안전하게 저장해 주세요.
* **탈퇴 시 삭제**: 이용자가 탈퇴하면 개인정보를 즉시 파기하는 정책을 운영해 주세요.
* **로그 관리**: 이용자 보호 및 법적 대응 목적 범위 내에서 메시지 로그를 관리할 수 있어요.

***

#### **4. 법적 협조 체계**

서비스 내 불법 행위가 발생했을 때 신속하게 대응할 수 있는 체계를 갖춰야 해요.

* 관련 법령에 따라 수사기관의 요청에 협조할 수 있는 내부 프로세스를 운영해 주세요.
* 토스는 이용자 보호 및 서비스 건전성을 위해 추가적인 안전 요건을 요구하거나 입점을 제한할 수 있어요.

***

#### **5. 서비스 운영 약관 제출**

채팅 서비스는 출시 전 아래 내용을 채널톡을 통해 제출해 주세요.

* 이용자 보호 및 불법 행위 방지 조치 내용
* 기본 서비스 운영 약관
* 불법 행위 방지에 대한 운영 정책 및 대응 프로세스

***

#### **6. 위반 시 조치**

가이드라인을 위반하거나 동일한 위반이 반복되는 경우, 다음과 같은 조치가 이뤄질 수 있어요.

* 콘텐츠 수정 요청
* 서비스 노출 중단
* 반복 위반 시 미니앱 등록 및 운영 제한

이용자 안전과 신뢰를 지키기 위해 위 기준을 반드시 준수해 주세요.

***

### 중고거래 서비스

앱인토스 미니앱으로 중고거래 서비스를 운영하려면, 이용자 보호와 안전한 거래 환경을 위해 아래 기준을 반드시 지켜주세요.

{% hint style="info" %}
**중고거래 서비스란?**

* 이용자 간 중고 물품을 직접 거래할 수 있도록 중개하는 서비스예요.
  {% endhint %}

{% hint style="info" %}
**꼭 확인해 주세요**

* 앱 내 결제 기능(PG(Payment Gateway), 간편결제, 토스페이 연동 포함)은 탑재할 수 없어요. **이용자 간 직거래만 허용**돼요.
* 채팅 기능이 포함된 경우 **채팅 서비스** 가이드도 함께 준수해야 해요.
* 재능거래 분야는 현재 입점이 불가해요. 향후 단계적으로 검토할 예정이에요.
  {% endhint %}

***

#### **1. 제출 서류**

중고거래 미니앱 출시를 희망할 경우 아래 서류를 구비하여 채널톡으로 제출해 주세요.

* **운영정책**: 아래 **2\~4번 항목**(거래 금지 항목, 모니터링 정책, 단계별 제재 기준)이 정책에 포함돼야 해요.
* **이용약관**: 아래 **5번 항목**(이용약관 필수 기재 항목)과 개인정보처리방침을 포함하여 반영해야 해요.
* **관련 라이선스**: 서비스 운영에 필요한 인허가 서류(예: 통신판매업 신고, 전자상거래 관련 인허가 등)를 제출해 주세요.
* **유의업종 확약서**: 채널톡으로 문의해 주세요.

***

#### **2. 거래 금지 항목**

운영정책에 거래가 금지되는 물품을 반드시 명시해 주세요. 아래 목록은 최소 기준이며, 법령 개정 및 정책 변경 시 지체 없이 업데이트해야 해요.

**① 법령 위반 및 위험 물품**

* 동물, 무기류 등 생명·안전에 위험이 있는 물품
* 의약품, 의료기기 등 인허가가 필요한 물품
* 인허가 없이 제조·판매된 식품 및 화장품

**② 권리 침해 및 불법 콘텐츠**

* 가품 및 상표권·저작권 침해 물품
* 불법 복제물 및 불법 소프트웨어

**③ 개인정보 및 디지털 자산**

* 타인의 개인정보
* 계정, 아이템 등 디지털 자산 거래

**④ 기타**

* 법정 재판매 금지 상품권 등 유통 제한 물품
* 구인·구직 등 용역 서비스
* 기타 관련 법령 또는 서비스 정책에 위반되는 물품

***

#### **3. 모니터링 정책**

파트너사는 아래 요건을 갖춘 모니터링 체계를 운영정책에 명시하고, 실제 운영에 적용해야 해요.

* 법령 및 정책 위반 의심 거래 탐지 시스템
* 이용자 신고 접수 및 1차 대응 체계 (접수 후 24시간 이내 1차 검토 권장)
* 월 1회 이상 자체 점검 및 결과 기록 보존
* 거래 금지 물품 등록 방지를 위한 키워드·이미지 필터 운영

***

#### **4. 단계별 제재 기준**

위반 이용자에 대한 단계적 제재 기준(예: 경고 → 일시적 이용 정지 → 영구 이용 정지)을 운영정책에 명시해야 해요.

***

#### **5. 이용약관 필수 기재 항목**

서비스 이용약관에 아래 내용을 반드시 포함해 주세요. 아래 항목은 최소 기준이며, 최종 약관은 파트너사에서 자체적으로 법무 검토를 거쳐 확정하는 것을 권장해요.

* 앱인토스는 거래 당사자가 아님을 고지
* 분쟁 발생 시 처리 절차 (신고 방법, 처리 기한 등)
* 이용자 금지행위 명시 (사기·기망행위, 거래 금지 물품 거래 등) 및 신고 방법·절차
* 채팅 기능의 목적 및 이용 범위 (거래 협의 목적 한정)
* 채팅 내 금지행위 명시 (성매매 유도, 스팸, 사기, 개인정보 요구 등)
* 채팅 내용 모니터링 가능성 및 근거 고지
* 이용 제한 사유, 이의신청 절차 및 기한, 이용 제한 조치의 종류
* 이용자 미니앱 탈퇴 절차
* 개인정보처리방침 (수집 항목·목적·보유 기간 등)

***

#### **6. 앱인토스의 모니터링 권한**

앱인토스는 이용자 보호 및 서비스의 안정적 운영을 위해 아래 권한을 보유하며, 파트너사는 이에 적극 협조해야 해요.

* 파트너사 서비스 운영 현황 모니터링 권한
* 법령·약관·운영정책 위반 확인을 위한 자료 요청 권한
* 이용자 피해 확산 방지를 위한 서비스 임시 조치 권한

***

#### **7. 위반 시 조치**

가이드라인을 위반하거나 동일한 위반이 반복되는 경우, 다음과 같은 조치가 이뤄질 수 있어요.

* 콘텐츠 수정 요청
* 서비스 노출 중단
* 반복 위반 시 미니앱 등록 및 운영 제한

이용자 안전과 신뢰를 지키기 위해 위 기준을 반드시 준수해 주세요.


# 체크리스트


# 게임 출시 가이드

미니앱에 공통으로 적용되는 기준과 기능별 출시 가이드예요. 출시하기 전에 반드시 내용을 확인하고 가이드를 지켜 주세요.

{% hint style="info" %}
**참고해 주세요**

아래 출시 가이드의 항목은 지속적으로 최신화되고 있어요.
{% endhint %}

### 접속 및 사운드

* [ ] 미니앱이 10초 이내에 최초 화면까지 정상적으로 열려요.
* [ ] 배경음, 효과음, 햅틱 등 필요한 사운드 기능이 적용돼 있어요. 무음 모드이거나 시스템 진동이 꺼진 경우에도 의도한 동작을 유지해요. (#)
* [ ] 사운드가 있는 경우, 사용자가 직접 On 또는 Off를 설정할 수 있어요.
* [ ] 미니앱이 백그라운드로 전환되면 사운드가 즉시 종료돼요.
* [ ] 백그라운드 상태에서 미니앱으로 다시 돌아오면 사운드가 정상적으로 다시 재생돼요.

### UX 및 내비게이션 바

* [ ] 우측 상단의 닫기 버튼이 정상적으로 노출되고 동작해요. (#)
* [ ] Safe Area 영역을 침범하지 않아요. iOS의 Dynamic Island 영역도 포함해요. (#)
* [ ] 미니앱에 진입하자마자 바텀시트가 자동으로 나타나지 않아요. (#)
* [ ] 특정 화면 전환 시 바텀시트를 사용해서 사용자 행동을 강제로 유도하지 않아요. (#)
* [ ] 모든 화면에서 사용자가 미니앱을 나갈 수 있는 방법이 있어요. (#)
* [ ] CTA 버튼을 보면, 눌렀을 때 어떤 행동이 이어지는지 예측할 수 있어요. (#)
* [ ] 링크나 컴포넌트로 자사 서비스 이동이나 자사 앱 설치를 유도하지 않아요. (#)
* [ ] 인앱 광고는 인트로/로딩/컷신/팝업 모달 등 일시적인 화면에 노출되지 않아요. (#)
* [ ] 앱 정보에 등록한 브랜드 로고와 미니앱 이름(국문)을 내비게이션 바에 반영해요. (#)

### 사용자 식별키 발급

* [ ] 사용자 식별자 값을 확인하여 저장하고, 게임이 정상적으로 시작돼요. (#)
* [ ] 유저의 플레이 기록(예: 랭킹, 레벨, 스테이지 등) 이 유지돼요. (#)
* [ ] 미니앱을 종료했다가 다시 접속해도 필요한 데이터가 유지돼요. (#)

### 보안 및 안정성

* [ ] 외부에서 전달받은 코드를 실행하는 기능은 사용할 수 없어요. (예: `eval` 등)
* [ ] 브라우저 히스토리를 조작해서 자사 사이트로 이동시키는 방식은 사용할 수 없어요. (예: `window.location.replace` 등)
* [ ] 서버 사이드 렌더링(SSR)은 사용할 수 없어요. 클라이언트 사이드 렌더링(CSR) 또는 정적 사이트 생성(SSG) 방식만 사용할 수 있어요.
* [ ] WebSocket을 사용하는 경우, 암호화된 `wss://` 연결만 사용해요. (예: Firebase Firestore, Supabase Realtime 등)
* [ ] API 통신은 암호화된 HTTPS 연결만 사용해요.
* [ ] 사용자 로그인 정보, 결제(토스페이, 인앱 결제) 내역 등 민감 정보는 DB에 암호화해서 저장해야 해요.
* [ ] 개인정보를 클라이언트에서 자체 서버로 전송할 때도 암호화할 것을 권장해요.

### 서비스 이용 동작

* [ ] 인게임 화면은 풀스크린으로 구현돼 있어요. (#)
* [ ] 설정한 가로 모드 또는 세로 모드가 의도한 대로 정상 작동해요. (#)
* [ ] 운영체제의 뒤로가기 제스처는 사용할 수 없어요. (#)
* [ ] 불법성이나 선정성 등 위법의 여지가 있는 콘텐츠가 포함돼 있지 않아요.
* [ ] 모든 UI 컴포넌트가 의도한 대로 정상 작동해요.
* [ ] 스크롤, 터치, 화면 전환 등 인터랙션 반응이 2초 이상 지연되지 않아요. (#)
* [ ] React Native 앱에서 권한을 요청하기 전에 사용자 동의를 먼저 받아요. (#)
* [ ] 사용자가 권한에 동의하지 않아도 나머지 기능은 계속 정상적으로 작동해요.
* [ ] 비정상적으로 네트워크 사용량이 급증하지 않아요.
* [ ] 비정상적으로 메모리 사용량이 급증하지 않아요.
* [ ] 미니앱을 종료할 때 확인 모달이 노출돼요.
* [ ] 게임 내 점수 측정, 스테이지 클리어 등 주요 서비스 흐름에 오류가 없어요.

### 인앱 결제

* [ ] 인앱 결제를 진행할 때, 미니앱에서 재생 중인 음악은 일시 정지돼요.
* [ ] 인앱 결제 주문 금액과 구글 또는 애플 결제창에 표시되는 금액이 일치해요. (#)
* [ ] 인앱 결제가 정상적으로 진행돼요. 구글 결제 테스트 환경에서도 확인해요.
* [ ] 인앱 결제가 완료된 뒤 미니앱으로 돌아오면 결제 결과가 정상적으로 반영돼요.
* [ ] 인앱 결제창에서 취소를 선택하면 주문 화면으로 돌아가요.
* [ ] 잔액 부족 등으로 결제에 실패했을 때, 사용자가 실패 사유를 인지할 수 있어요.
* [ ] 인앱 결제 취소가 정상적으로 처리돼요.
* [ ] 인앱 결제 상품에 대한 결제 내역을 사용자가 확인할 수 있어요.
* [ ] 토스 앱에 로그인된 기기를 변경해도, 기존에 결제한 인앱 결제 데이터(아이템 등)가 유지돼요. (#)

### 인앱 광고

* [ ] 인앱 광고가 재생될 때, 미니앱에서 재생 중인 음악은 일시 정지돼요.
* [ ] 사용자가 예상하기 어려운 순간에 인앱 광고를 노출하지 않아요. (#)
* [ ] 인앱 광고가 정상적으로 노출되고, 중간에 종료되거나 튕기는 현상이 없어요.
* [ ] 인앱 광고는 사전에 로딩돼 있어요. 광고 재생 시점에 실시간으로 로딩하지 않아요. (#)
* [ ] 인앱 광고가 종료되면 미니앱 화면으로 정상적으로 돌아와요. (#)
* [ ] 인앱 광고 종료 후 미니앱 음악이 다시 재생돼요.
* [ ] 리워드 광고를 끝까지 시청하면 보상이 정상적으로 지급돼요. (#)
* [ ] 배너 광고가 상단 또는 하단에만 위치해요. (#)

### 공유 리워드

* [ ] 설정한 공유 리워드 화면이 정상적으로 노출돼요. (#)
* [ ] 공유 리워드 화면을 닫으면 미니앱 화면으로 정상적으로 돌아와요.
* [ ] 친구 초대가 완료되면 보상이 정상적으로 지급돼요. (#)

### 웹보드 게임

* [ ] 성인 인증 기능 연동을 사용해요. (#)
* [ ] 사용자가 문의할 수 있는 고객센터 문의 채널이 제공돼요. (#)
* [ ] 게임 내 확률형 아이템에 대한 확률 정보를 사용자가 확인할 수 있어요. (#)


# 비게임 출시 가이드

미니앱에 공통으로 적용되는 기준과 기능별 출시 가이드예요. 출시하기 전에 반드시 내용을 확인하고 가이드를 지켜 주세요.

{% hint style="info" %}
**참고해 주세요**

아래 출시 가이드의 항목은 지속적으로 최신화되고 있어요.
{% endhint %}

### 접속 및 앱 내 기능

* [ ] 미니앱이 정상적으로 열려요.
* [ ] ‘앱 내 기능’ 으로 안내한 모든 서비스 기능을 미니앱에서도 동일하게 사용할 수 있어요. (#)
* [ ] ‘앱 내 기능’ 에 등록한 스킴이 정상적으로 연결돼요.
* [ ] 앱 스킴으로 진입한 뒤, 뒤로가기 버튼이 정상적으로 작동해요. (#)

### 내비게이션 바

* [ ] 앱인토스 비게임 내비게이션 바를 사용하고 있어요. (#)
* [ ] \[좌측] 뒤로가기 버튼(<)이 모든 화면에서 정상적으로 동작해요. (#)
* [ ] \[중앙] 앱 정보에 등록한 브랜드 로고와 미니앱 이름(국문)이 내비게이션바에 표시돼요. (#)
* [ ] \[중앙] 홈 버튼이 정상적으로 동작해요. (선택 사항) (#)
* [ ] \[우측] 미니앱 기능 버튼은 최대 1개만 노출돼요. (선택 사항) (#)
* [ ] 토스 내비게이션 바의 뒤로가기 버튼과 미니앱에서 자체 구현한 뒤로가기 버튼이 동시에 보이지 않아요. (#)
* [ ] 더보기 버튼(⋯)에서 토스 공통 기능(신고, 공유 등)을 정상적으로 제공해요. (#)
* [ ] 닫기 버튼과 뒤로가기 버튼이 노출되고, 동작이 명확해요.
* [ ] 최초 화면에서 뒤로가기를 누르면 미니앱이 종료돼요.
* [ ] 탭바를 사용하는 경우, 토스 앱과 동일한 플로팅 형태로 구현돼 있어요. (#)

### 사용자 식별키 발급

* [ ] 사용자 식별자 값을 확인하여 저장하고, 미니앱이 정상적으로 시작돼요. (#)
* [ ] 유저의 미니앱 사용 기록이 유지돼요. (#)
* [ ] 미니앱을 종료했다가 다시 접속해도 필요한 데이터가 유지돼요. (#)

### 보안 및 안정성

* [ ] 외부에서 전달받은 코드를 실행하는 기능은 사용할 수 없어요. (예: `eval` 등)
* [ ] 브라우저 히스토리를 조작해서 자사 사이트로 이동시키는 방식은 사용할 수 없어요. (예: `window.location.replace` 등)
* [ ] 서버 사이드 렌더링(SSR)은 사용할 수 없어요. 클라이언트 사이드 렌더링(CSR) 또는 정적 사이트 생성(SSG) 방식만 사용할 수 있어요.
* [ ] WebSocket을 사용하는 경우, 암호화된 `wss://` 연결만 사용해요. (예: Firebase Firestore, Supabase Realtime 등)
* [ ] API 통신은 암호화된 HTTPS 연결만 사용해요.
* [ ] 사용자 로그인 정보, 결제(토스페이, 인앱 결제) 내역 등 민감 정보는 DB에 암호화해서 저장해야 해요.
* [ ] 개인정보를 클라이언트에서 자체 서버로 전송할 때도 암호화할 것을 권장해요.

### 서비스 이용 동작

* [ ] 지도처럼 꼭 필요한 경우를 제외하고, 제스처 기반 확대·축소 기능은 비활성화돼요. (#)
* [ ] 미니앱 테마는 라이트 모드로 구현돼 있어요.
* [ ] 모든 UI 컴포넌트가 의도한 대로 동작해요.
* [ ] 스크롤, 터치, 화면 전환 등 인터랙션 반응이 2초 이상 지연되지 않아요. (#)
* [ ] 미니앱을 종료했다가 다시 들어와도 필요한 데이터가 유지돼요. (#)
* [ ] 서비스 이용에 꼭 필요한 외부 링크가 정상적으로 열려요. (#)
* [ ] 공유 기능에서 `intoss-private://` 링크를 사용하지 않고 `intoss://` 스킴을 사용해요. (#)
* [ ] 미니앱 문구에 비속어, 은어, 과도한 유행어가 포함되지 않아요. (#)
* [ ] 사용자 안내나 확인이 필요한 경우 TDS 모달을 사용해요.
* [ ] 불법성, 선정성 등 위법한 콘텐츠가 포함돼 있지 않아요.
* [ ] React Native 앱에서 권한을 요청하기 전에 사용자 동의를 먼저 받아요. (#)
* [ ] 사용자가 권한을 허용하지 않아도 나머지 기능은 계속 정상적으로 작동해요.
* [ ] 비정상적으로 네트워크 사용량이 급증하지 않아요.
* [ ] 비정상적으로 메모리 사용량이 급증하지 않아요.

### UX

* [ ] 미니앱에 들어오자마자 바텀시트가 자동으로 열리지 않아요. (#)
* [ ] 특정 화면 전환 시 바텀시트로 사용자의 행동을 강제로 유도하지 않아요. (#)
* [ ] 모든 화면에서 사용자가 미니앱을 나갈 수 있는 방법이 명확해요. (#)
* [ ] CTA 버튼을 보면 다음에 어떤 행동이 일어날지 예측할 수 있어요. (#)
* [ ] 링크나 컴포넌트로 자사 서비스 이동이나 자사 앱 설치를 유도하지 않아요. (#)
* [ ] 인앱 광고는 인트로/로딩/컷신/팝업 모달 등 일시적인 화면에 노출되지 않아요. (#)

### 토스 로그인

* [ ] 토스 로그인을 사용하는 경우, 어떤 서비스인지 알 수 있도록 인트로 페이지에서 서비스 설명을 제공해요. (#)
* [ ] 설정한 토스 로그인 약관 화면 URL이 정상적으로 노출돼요. (#)
* [ ] 토스 로그인을 요청하는 화면에서 닫기를 선택하면 미니앱이 종료돼요. (#)
* [ ] 토스 앱에서 로그인 연결을 끊은 뒤 미니앱에 다시 접속하면, 다시 로그인을 요청하는 약관 화면이 노출돼요.
* [ ] 토스 앱에서 로그인 연결을 끊으면 사용자 데이터가 미니앱에 남아 있지 않아요. (#)
* [ ] 토스 로그인이 아닌 자사 로그인이나 기타 로그인 방식은 제공하지 않아요.

### 인앱 결제

* [ ] 인앱 결제를 진행할 때, 미니앱에서 재생 중인 음악은 일시 정지돼요.
* [ ] 인앱 결제 주문 금액과 구글 또는 애플 결제창에 표시되는 금액이 일치해요. (#)
* [ ] 인앱 결제가 정상적으로 진행돼요. 구글 결제 테스트 환경에서도 확인해요.
* [ ] 인앱 결제가 완료된 뒤 미니앱으로 돌아오면 결제 결과가 정상적으로 반영돼요.
* [ ] 인앱 결제창에서 취소를 선택하면 주문 화면으로 돌아가요.
* [ ] 잔액 부족 등으로 결제에 실패했을 때, 사용자가 실패 사유를 인지할 수 있어요.
* [ ] 인앱 결제 취소가 정상적으로 처리돼요.
* [ ] 인앱 결제 상품에 대한 결제 내역을 사용자가 확인할 수 있어요.
* [ ] 토스 앱에 로그인된 기기를 변경해도, 기존에 결제한 인앱 결제 데이터(이용권 등)가 유지돼요. (#)

### 토스페이

* [ ] 토스페이 간편 결제를 진행할 때, 미니앱에서 재생 중인 음악은 일시 정지돼요.
* [ ] 주문 금액과 토스페이 결제창에 표시되는 금액이 일치해요.
* [ ] 토스페이 결제에 실패하면 오류 메시지가 노출되고, 사용자가 실패 사유를 인지할 수 있어요. (#)
* [ ] 토스페이 결제 직전에 취소를 선택하면 이전 화면으로 돌아가요.
* [ ] 토스페이 외의 다른 결제 수단은 제공하지 않아요.
* [ ] 토스페이 결제창에서 취소를 선택하면 주문 화면으로 이동해요.

### 인앱 광고

* [ ] 인앱 광고가 재생될 때, 미니앱에서 재생 중인 음악은 일시 정지돼요.
* [ ] 사용자가 예상하기 어려운 순간에 인앱 광고를 노출하지 않아요. (#)
* [ ] 인앱 광고가 정상적으로 노출되고, 중간에 종료되거나 튕기는 현상이 없어요.
* [ ] 인앱 광고는 사전에 로딩돼 있어요. 광고 재생 시점에 실시간으로 로딩하지 않아요. (#)
* [ ] 인앱 광고가 종료되면 미니앱 화면으로 정상적으로 돌아와요. (#)
* [ ] 인앱 광고 종료 후 미니앱 음악이 다시 재생돼요.
* [ ] 리워드 광고를 끝까지 시청하면 보상이 정상적으로 지급돼요. (#)
* [ ] 배너 광고가 적절한 위치에 노출돼요. (상단/중앙/하단) (#)
* [ ] 배너 광고가 스크롤 가능한 화면에만 노출돼요. (#)

### 공유 리워드

* [ ] 설정한 공유 리워드 화면이 정상적으로 노출돼요. (#)
* [ ] 공유 리워드 화면을 닫으면 미니앱 화면으로 정상적으로 돌아와요.
* [ ] 친구 초대가 완료되면 보상이 정상적으로 지급돼요. (#)


# 보도자료 가이드

앱인토스에 론칭한 미니앱 서비스의 보도자료를 배포할 때 필요한 정보를 확인할 수 있어요. 이를 통해 정확하고 일관된 정보를 언론에 전달할 수 있어요.

### 1. 배포 절차

앱인토스 파트너사는 관련 내용을 보도자료로 배포하는 경우, 토스팀에게 **사전 공유**해 주세요. 사전 리뷰 단계에서 배포 가능 여부, 자료 내용 등을 전반적으로 조율할 수 있어요. 보도자료 리뷰는 [**채널톡**](https://apps-in-toss.channel.io/workflows/787658)을 통해 요청해 주세요.

### 2. 작성 가이드

{% hint style="info" %}
**본 가이드라인에 사용되는 용어의 정의를 알려 드려요.**

* **‘앱인토스(Apps-in-Toss)’** 는 미니앱을 론칭할 수 있는 시스템이자 생태계를 가리키는, 백엔드 및 파트너사 대상의 공식 명칭이에요.
* **‘토스 미니앱(Toss Mini App)’** 은 토스 앱 내에서 B2C 사용자를 대상으로 제공되는 UI 및 마케팅·홍보 용어예요.
* **\[법인명]** 은 앱인토스 파트너사의 회사명이에요.
* **\[서비스명]** 은 토스 미니앱에서 노출되고 있는 명칭 그리고 토스 앱에서 검색 가능한 명칭이에요.
  {% endhint %}

#### **1) 보도자료 제목 템플릿**

아래와 같은 제목을 보도자료에 활용하실 수 있어요. 주의해야 하는 표현들은 [**3) 이런 표현은 지양해 주세요.**](#id-3) 에서 참고해 주세요.

* **\[서비스명], 토스 미니앱 출시**
* **\[법인명] \[서비스명], 토스 미니앱 공식 론칭**
* **\[서비스명], 토스 미니앱 서비스 시작**

#### **2) 보도자료 작성 리소스**

보도자료 본문 작성에 참고할 수 있는 토스 및 미니앱 관련 정보들이에요.

필요한 내용을 발췌하여 사용하실 수 있어요.

* **토스 소개**
  * 토스 누적 가입자: 3000만 명 **(2025년 8월 말 기준)**
  * 토스 월간 활성 이용자 수: 2480만명 **(2024년 말 기준)**
* **미니앱 설명**
  * 토스 미니앱은 토스 앱 내에서 앱인앱 형태로 다양한 서비스들을 이용할 수 있는 서비스다. 이용자들은 별도의 회원가입이나 앱 다운로드 없이 토스 미니앱 또는 게임 메뉴에서 파트너사의 서비스를 이용할 수 있다.

#### **3) 이런 표현은 지양해 주세요.**

‘토스 미니앱 출시’ 외에 **아래 표현들을 보도자료에서 사용하는 것은 어려워요.**

* **토스 미니앱 출시 (O)**
* 토스 공식 파트너사 (X)
* 토스 입점사 (X)
* 토스 제휴사 (X)
* 토스 입점 / 토스 미니앱 입점 (X)

#### **4) 보도자료 작성 예시**

**에어로무브, 토스 미니앱 공식 출시**

* 전동 킥보드·전기 자전거를 토스 앱으로 즉시 대여
* 위치 기반 실시간 배터리 잔량 확인, 원클릭 결제 지원

2025년 9월 15일 – 모빌리티 스타트업 네오트랜스(NeoTrans)가 초단거리 개인 모빌리티 예약 서비스 ‘에어로무브(AeroMove)’를 토스 미니앱으로 공식 출시했다고 15일 밝혔다.

에어로무브는 도심 내 단거리 이동에 최적화된 퍼스널 모빌리티 통합 플랫폼이다. 사용자는 토스 앱을 통해 주변에 배치된 전동 킥보드와 전기 자전거를 실시간으로 확인하고, 원하는 기기를 즉시 대여할 수 있다. 특히 △실시간 위치 추적 △배터리 잔량 표시 △이용 가능 시간 안내 등 다양한 편의 기능을 제공해 이동 전 불확실성을 최소화했다.

또한 토스의 결제 인프라를 기반으로 원클릭 결제·이용 내역 자동 저장을 지원하며, 보험 연동과 안전 가이드를 함께 제공해 이용자 안전성도 강화했다. 이번 토스 미니앱 출시로 이용자들은 별도의 앱 설치나 회원가입 없이 토스 앱 안에서 에어로무브를 간편하게 경험할 수 있다.

한편, 토스 미니앱은 토스 앱 내에서 다양한 서비스를 앱인앱(App in App) 형태로 제공하는 플랫폼이다. 금융뿐만 아니라 생활 밀착형 서비스, 온·오프라인 매장 연계, 게임, 엔터테인먼트까지 폭넓은 영역을 아우르며, 하나의 앱으로 금융과 일상생활을 연결하는 사용자 경험을 제공한다.

네오트랜스 관계자는 “토스 미니앱 출시로 에어로무브가 더 많은 사용자에게 다가갈 수 있게 됐다”며, “앞으로도 누구나 도심 속에서 쉽고 안전하게 이동할 수 있도록 서비스를 발전시켜 나가겠다”고 말했다.

### 3. 앱인토스 로고 및 가이드

앱인토스 로고는 주로 홍보나 보도자료에서 \[회사명(개발사명)]과 함께 활용할 수 있어요. 표기는 아래와 같이 ‘ **| ’(Bar) 표기**를 활용 해야 하며, 로고 파일은 [**채널톡**](https://apps-in-toss.channel.io/workflows/787658) 을 통해 요청해 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fh7ODshPq5IYFJRiwaIAb%2Fimage.png?alt=media&#x26;token=650bbd21-fd17-4846-b82b-0ae330b24718" alt=""><figcaption></figcaption></figure>

### 4. 외부 광고 가이드라인

보도자료 외에 외부 채널에서 마케팅을 진행하는 경우에는 [**외부 광고 가이드**](https://appsintoss.gitbook.io/appsintoss-docs/guide/marketing/guideline)를 참고해 주세요.

### 자주 묻는 질문

<details>

<summary>보도자료를 배포하기 전에 반드시 토스팀과 공유해야 하나요?</summary>

네. **반드시 사전 공유를 부탁드려요.**

토스팀은 보도자료 내용과 배포 가능 여부를 검토하여 언론에 일관된 메시지가 전달될 수 있도록 지원하고자 해요.

보도자료 리뷰는 [**채널톡**](https://apps-in-toss.channel.io/workflows/787658)으로 전달해 주세요.

</details>

<details>

<summary>배포 시점은 어떻게 조율하나요?</summary>

보도자료 배포 일정은 리뷰를 위한 사전 공유 시 함께 알려주시면, 검토 일정 조율 등을 함께 고려할 수 있도록 할게요.

</details>

<details>

<summary>보도자료에서 ‘앱인토스’와 ‘토스 미니앱’은 어떻게 구분해서 써야 하나요?</summary>

**앱인토스(Apps-in-Toss)**: 파트너사 대상의 시스템/플랫폼 명칭 (B2B) **토스 미니앱(Toss Mini App)**: 토스 앱 내 사용자 대상 노출 명칭 (B2C)

→ 보도자료에는 "**토스 미니앱**"이라는 용어를 사용해 주세요.

</details>

<details>

<summary>토스 관련 수치를 인용해도 되나요?</summary>

네. 다만 **가이드에 제공된 최신 수치만 사용**해 주세요.

토스 누적 가입자: 3,000만 명 (2025년 8월 기준) 토스 MAU(월간 활성 이용자): 2,480만 명 (2024년 말 기준)

→ 다른 수치나 추정치는 임의로 작성하지 말아 주세요.

</details>

<details>

<summary>보도자료 외에 SNS나 블로그에도 같은 표현을 써도 되나요?</summary>

SNS나 블로그 등 외부 채널 활용 시에는 **개발자센터의 마케팅 가이드라인**을 반드시 함께 참고해 주세요.

</details>


# 저작권 침해 신고 가이드

본 문서는 앱인토스에서 운영 중인 미니앱에 대해 저작권 침해 신고가 접수되었을 때의 절차와 대응 방법, 그리고 자신의 저작권이 침해되었을 때 신고하는 방법을 안내해요.

***

### 1. 권리 침해 신고

앱인토스에서 출시된 미니앱 또는 미니앱 내 게시물에 대해 제3자(권리주장자)가 자신의 저작권이 침해되었다고 신고하는 것을 말해요. 앱인토스는 저작권법 제103조 등 관련 법령에 따라 신고가 접수되면 임시조치를 취하고, 양측에 공정한 절차를 안내하는 역할을 해요.

> 앱인토스는 권리 침해 분쟁의 당사자가 아니며, 침해 여부에 대한 실체적 판단을 하지 않아요. 임시조치는 중립적이고 예방적인 조치이며, 침해 사실을 인정하는 것이 아니에요.

***

### 2. 저작권 침해 신고 방법

자신의 저작권이 침해되었다고 판단하는 경우, 아래 절차에 따라 신고할 수 있어요.

* **저작권 침해 신고하기 (권리 주장자)**
  * [개인인 경우](https://toss.im/_m/VRWa6Uch)
  * [단체인 경우](https://toss.im/_m/VnbRI1bB)

**2.1 신고 시 필요한 정보**

**신고인 정보**

* 성명 또는 단체명
* 생년월일 또는 사업자등록번호
* 연락처 (전화 / 이메일)
* 주소
* 대리인이 요청하는 경우, 대리인임을 증명하는 서류 (위임장 및 인감 증명서, 대리인 신분증 사본 등)&#x20;

{% file src="/files/3yE5XaTH3ZAJuB1T544k" %}

**신고 내용**

* 침해 서비스명 (침해가 발생한 미니앱 이름)
* 침해 사유 (예: 저작권 침해)
* 구체적 침해 내용 (어떤 부분이 본인의 권리를 어떻게 침해했는지 상세 기재)

**2.2 필수 첨부 서류**

신고 접수 후 임시조치는 형식 요건이 충족된 경우에 한해 실행돼요. 아래 기준을 충족하지 못한 경우 반려돼요.

**① 본인 확인 서류**

* 본인 확인 서류 (신분증 사본 등, 주민등록번호 뒷자리 마스킹 필수)

**② 권리 관계 입증 서류**

아래 유형 중 하나에 해당하는 서류를 제출해야 해요.

**\[유형 A] 저작권 등록증 사본 또는 그에 상당하는 자료**

저작권 등록이 되어 있는 경우에 해당해요.

| 인정 자료              | 기준                                             |
| ------------------ | ---------------------------------------------- |
| 저작권 등록증 사본         | 한국저작권위원회 발행, 신고인 명의로 등록된 것                     |
| 프로그램 등록증 사본        | 한국저작권위원회 발행, 신고인 명의로 등록된 컴퓨터프로그램저작물 (앱 소스코드 등) |
| 저작재산권 양도계약서        | 원권리자→신고인으로의 권리 이전이 명시된 것                       |
| 전속 계약서 또는 이용허락 계약서 | 신고인이 해당 저작물에 대한 권리행사 권한을 가짐이 명시된 것             |

아래는 **단독으로는 인정되지 않는 자료**예요.

* 단순 URL 캡처 또는 스크린샷만 제출한 경우
* 출처·발행 주체가 불명확한 증명서
* 신고인 명의가 표시되지 않은 자료

**\[유형 B] 성명 또는 이명으로서 널리 알려진 것이 표시된 저작물 사본 또는 그에 상응하는 자료**

저작권 등록은 되어 있지 않으나, 신고인이 해당 저작물의 창작자임을 증명할 수 있는 경우에 해당해요. **두 가지 요건을 모두 충족**해야 해요.

**요건 1. 저작물에 신고인의 성명 또는 이명이 표시되어 있을 것**

| 인정 자료             | 기준                                  |
| ----------------- | ----------------------------------- |
| 원본 저작물 파일 또는 게시물  | 신고인의 성명, 서명, 닉네임 등이 저작물 내에 직접 표시된 것 |
| 최초 공표 이력이 확인되는 자료 | 공표 플랫폼의 게시 이력, 타임스탬프 포함             |
| 제작 원본 파일          | 작성자 메타데이터 포함 (문서 속성, 파일 생성 정보 등)    |

아래는 **단독으로는 인정되지 않는 자료**예요.

* 단순 URL 캡처 또는 스크린샷만 제출한 경우
* 저작물에 신고인 표시 없이 본인이 만들었다는 주장만 기재한 경우
* 타인이 작성한 증명서나 확인서만 첨부한 경우

**요건 2. 표시된 성명 또는 이명이 '널리 알려진 것'일 것**

'널리 알려진'의 의미는 단순히 닉네임을 사용한다는 것이 아니라, **해당 이름이 일반 공중에게 신고인과 결부된 것으로 인식될 수 있어야** 해요.

| 인정 기준                   | 예시                        |
| ----------------------- | ------------------------- |
| 해당 분야에서 공개적으로 활동한 이력    | 포트폴리오, 공식 프로필, 언론 보도 등    |
| 동일 이명으로 다수의 저작물을 공표한 이력 | 동일 필명으로 출판된 도서, 공개된 작품집 등 |
| 플랫폼 공식 인증 또는 공인된 등록 이력  | 공신력 있는 플랫폼의 공식 채널 인증 등    |

아래는 **인정되지 않는 경우**예요.

* 해당 신고 건 외에 공개된 활동 이력이 없는 이명
* 불특정 다수에게 알려지지 않은 내부 프로젝트명·팀명

**③ 반려 처리 기준**

아래에 해당하는 경우 **신고 접수 후 임시조치 없이 반려돼요.**

* 권리 관계 입증 서류가 첨부되지 않은 경우
* 위 ② 권리 관계 입증 서류의 요건을 충족하지 않은 경우
* 제출 서류에 신고인 명의가 확인되지 않는 경우
* 침해 주장 저작물과 제출 서류상 저작물의 동일성이 특정되지 않는 경우

**2.3 유의사항**

* 신고 결과에 따라 서비스 운영이 임시 중단된 경우, 해당 미니앱을 운영하는 파트너사에 신고 사실과 함께 신고 요청자 정보(개인: 권리주장자명, 단체: 단체명)가 안내돼요.
* 기재 내용은 사실이어야 하며, **허위 신고로 인해 발생하는 법적 책임은 신고자에게 있어요.**
  * 정당한 권리 없이 복제·전송의 중단을 요구한 신고자는 **손해배상책임**이 있어요(저작권법 제103조 제6항).
  * 자신에게 정당한 권리가 없음을 알면서 고의로 복제·전송의 중단 요구를 하면 1년 이하의 징역 또는 1천만원 이하의 벌금의 **형사처벌**에 처해질 수 있어요(저작권법 제137조 제1항 제6호).
* 동일한 신고자·피신고자·신고내용으로 추가적인 소명자료 없이 반복 신고하는 경우, 기존 신고 건과 동일한 건으로 간주하여 **반려 처리**돼요.

***

### 3. 전체 처리 흐름

저작권 침해 신고가 접수되면 아래 절차로 진행돼요.

* **신고 접수 → 임시조치(미노출) → 파트너사 통지 → 이의제기/서비스 재개 요구 → 신고자의 법적 조치 여부 확인 → 서비스 복원 또는 중단 유지**

<table data-search="false"><thead><tr><th>단계</th><th>내용</th><th>기한</th></tr></thead><tbody><tr><td>신고 접수</td><td>권리주장자가 침해 신고서 제출</td><td>-</td></tr><tr><td>임시조치</td><td>해당 미니앱 임시 미노출 처리</td><td>접수 즉시 ~ 1영업일 이내</td></tr><tr><td>파트너사 통지</td><td>신고 사실 및 이의제기 절차 안내</td><td>임시조치와 함께 통지</td></tr><tr><td>서비스 재개 요구</td><td>파트너사가 소명자료 제출</td><td>임시조치 통보일로부터 30일 이내</td></tr><tr><td>재개예정일 통보</td><td>신고자에게 재개 예정 사실 통보</td><td>재개요구 접수일로부터 3일 이내 결정</td></tr><tr><td>신고자 법적 조치</td><td>소 제기·가처분 등 증빙 제출</td><td>재개예정일 이전 (재개요구 접수일로부터 7일)</td></tr><tr><td>최종 결정</td><td>서비스 복원 또는 중단 유지</td><td>-</td></tr></tbody></table>

***

### 4. 임시조치가 취해졌을 때

**4.1 임시조치란**

관련 법령에 따라 저작권 침해 신고가 접수되고 형식 요건이 충족되면, 해당 미니앱은 **임시적으로 미노출(운영 중단)** 처리돼요. 이 조치는 저작권법 제103조 제2항에 따른 것으로, 침해 여부가 확정된 것이 아니에요.

**4.2 파트너사에 통보되는 내용**

임시조치가 이루어지면 아래 내용이 포함된 통보서가 발송돼요.

* 신고 접수 사실 및 임시 미노출 처리 안내
* 신고 요청자 정보 (개인: 권리주장자명, 단체: 단체명)
* 이의제기(서비스 재개 요구) 절차 안내

***

### 5. 서비스 재개를 요구하는 방법

미니앱이 정당한 권리에 의해 운영되고 있다고 판단하시는 경우, 서비스 재개를 요구할 수 있어요.

**5.1 제출 기한**

**임시조치 통보를 받은 날로부터 30일 이내**에 소명자료를 제출해야 해요. 기한 내에 재개 요구가 없으면 별도의 서비스 재개가 이루어지지 않아요.

**5.2 제출 방법**

저작권법 시행규칙 제15조에 따라, 서비스 재개를 요구하려는 경우 아래 링크를 눌러 접수해 주세요.

* **피신고자 소명 접수하기**
  * [개인인 경우](https://toss.im/_m/fUvscE3l)&#x20;
  * [단체인 경우](https://toss.im/_m/Z3q9wzvt)&#x20;

복제·전송 재개 요청서에는 아래 서류를 첨부해야 해요.

* 저작권법 시행령 제42조 제1항 각 호의 어느 하나에 해당하는 소명자료
* 본인임을 확인할 수 있는 자료
* 대리인이 요청하는 경우, 대리인임을 증명하는 서류 (위임장 및 인감 증명서, 대리인 신분증 사본 등)&#x20;

{% file src="/files/3yE5XaTH3ZAJuB1T544k" %}

**5.3 소명자료 유형**

서비스 재개 요구 시 아래 중 해당하는 소명자료를 첨부해야 해요.

| 소명 유형              | 필요 서류                                      |
| ------------------ | ------------------------------------------ |
| 본인이 권리자인 경우        | 저작권 등록증, 프로그램 등록증 등 권리 등록 사본               |
| 본인 성명이 표시된 저작물인 경우 | 성명/필명이 명시된 원본 콘텐츠, 최초 게시물 캡처, 제작 프로젝트 파일 등 |
| 권리자로부터 허락을 받은 경우   | 라이선스 계약서, 이용 허락 확인서, 구매 영수증 등              |
| 보호기간이 만료된 경우       | 저작자 사후 70년 경과 증빙, 퍼블릭 도메인 확인 자료 등          |

**5.4 유의사항**

* 재개 요구 사실과 제출한 소명 내용은 **신고자(권리주장자)에게 통보**돼요.
* 허위 재개 요구 시 저작권법 제103조 제6항에 따라 **손해배상 책임**이 발생할 수 있어요.
* 서비스 복원 후 신고자와의 분쟁에 대해서는 **파트너사가 책임**을 지게 돼요.

***

### 6. 재개 요구 이후의 절차

**6.1 소명자료 검토**

앱인토스는 재개 요구를 접수한 날로부터 **3일 이내**에 형식적 요건을 확인하고, 서비스 재개예정일을 정해요.

**6.2 재개예정일**

재개예정일은 재개 요구를 접수한 날로부터 **7일 후**로 설정돼요.

**6.3 신고자의 법적 조치**

* 신고자는 **재개예정일 이전까지** 저작권법 제103조 제3항에 따른 소 제기, 가처분 신청 등 법적 조치 증빙을 앱인토스에 제출할 수 있어요.
  * 증빙서류 : 소장 접수증 또는 법원 접수 확인서
  * 확인사항: 피신고자의 침해행위에 대하여 소를 제기한 사실
  * 불인정 케이스: 소 제기는 있으나 해당 복제·전송 행위와 무관한 별개 분쟁인 경우

    > 앱인토스는 소장 표지 및 청구취지 기재 내용을 통해 관련성을 형식적으로 확인하며, 침해행위의 존재 등 소장 내용의 실체적 당부는 판단하지 않아요.
* **법적 조치 증빙이 제출된 경우**: 임시조치(미노출)가 유지돼요.
* **법적 조치 증빙이 없는 경우**: 재개예정일에 서비스가 복원돼요.

> 재개예정일 이전까지 법적 조치 사실이 통보되지 않은 경우, 관련 법령에 따라 서비스는 복원돼요. 재개예정일 이후에 제기되는 법적 분쟁은 사법기관의 판단에 따라요.

***

### 7. 권리침해 신고 수령인 정보

저작권법 제103조 제4항 및 시행령 제44조에 따른 수령인 정보입니다.

* 소속 부서명: 앱인토스 파트너팀 저작권 보호 담당
* 연락처: <jiseop.park@toss.im> / 1599-4905
* 우편물 주소: 서울특별시 강남구 아크플레이스 12층
* 저작권 침해 신고 (권리 주장자)
  * [개인인 경우](https://toss.im/_m/VRWa6Uch)&#x20;
  * [단체인 경우](https://toss.im/_m/VnbRI1bB)&#x20;
* 저작권 권리 침해 소명
  * [개인인 경우](https://toss.im/_m/fUvscE3l)&#x20;
  * [단체인 경우](https://toss.im/_m/Z3q9wzvt)&#x20;

***

### 8. 자주 묻는 질문

<details>

<summary>임시조치가 취해지면 앱이 영구적으로 내려가는 건가요?</summary>

아니요. 임시조치는 일시적이고 예방적인 조치예요. 정당한 권리를 소명하면 서비스가 다시 재개될 수 있어요.

</details>

<details>

<summary>임시조치에 대한 이의제기 기한을 연장할 수 있나요?</summary>

이의제기 기한이 종료되는 시점에 조치가 결정되어야 하므로 기한 연장은 불가해요. 30일 이내에 소명자료를 제출해 주세요.

</details>

<details>

<summary>앱인토스가 침해 여부를 판단하나요?</summary>

아니요. 앱인토스는 침해 여부에 대한 실체적 판단을 하지 않아요. 저작권 침해 여부는 사법부의 판단 영역이에요. 앱인토스는 법령에 따라 임시조치를 실행하고, 양측의 절차를 안내하는 역할만 해요.

</details>

<details>

<summary>신고자에게 제 정보가 공개되나요?</summary>

관련 법령에 따라 재개 요구 사실 및 소명 내용과 함께 성명(상호명), 전화번호(휴대전화번호)가 신고자에게 통보돼요.

</details>

<details>

<summary>사생활 침해나 명예훼손 관련 신고도 이 절차가 적용되나요?</summary>

본 정책은 저작권법에 따른 저작권 침해에 한정돼요.

</details>


# 디자인

## design


# 도구


# 피그마/TDS Mobile UI Kit 라이선스

(English version follows below)

한국어와 영어 버전의 내용이 상충하거나 불일치할 경우, 한국어 버전을 우선합니다.

**1. 라이선스 부여**

본 라이선스는 앱인토스 피그마 UI Kit(이하 “본 UI Kit”)의 사용 조건을 명시합니다. 귀하가 본 UI Kit를 다운로드, 복제, 또는 사용하는 경우, 본 라이선스의 모든 조건에 동의한 것으로 간주됩니다. 본 라이선스는 별도의 서면 합의 없이 회사(㈜비바리퍼블리카)의 재량에 따라 변경될 수 있습니다.

**2. 사용 범위 및 제한**

허용되는 사용:

* 앱인토스 용 애플리케이션의 개발
* 앱인토스 용 애플리케이션 내에서의 디자인 작업
* 앱인토스 용 애플리케이션의 프로토타입 제작

금지되는 사용:

* 본 UI Kit 또는 일부 구성요소를 다른 프로젝트, 제품 또는 서비스에 사용하는 행위
* 본 UI Kit 또는 일부 구성요소를 상업적 용도(판매, 제 3 자 제공 등) 로 활용하는 행위
* 본 UI Kit 또는 일부 구성요소를 복사, 수정, 편집 및 재가공하는 행위
* 본 UI Kit 또는 일부 구성요소를 복사, 수정, 편집 및 재가공하여 다른 프로젝트, 제품 또는 서비스에 사용하거나 상업적 용도(판매, 제 3 자 제공 등)로 활용하는 행위
* 본 UI Kit 또는 일부 구성요소를 재배포하는 행위

**3. 지식재산권**

본 UI Kit에 포함된 모든 브랜드 자산, 로고, 및 시각 요소는 저작권법과 상표법에 의해 보호됩니다. 본 UI Kit 및 그 안에 포함된 모든 디자인 자산, 컴포넌트, 스타일 등에 대한 모든 권리, 소유권 및 이익은 Viva Republica Inc.에 귀속됩니다. 본 라이선스는 사용에 대한 제한적 권한만 부여하며, 어떠한 지식재산권도 양도하지 않습니다.

**4. 보증의 부인**

본 UI Kit는 '있는 그대로' 제공됩니다. 저작권자는 본 UI Kit의 정확성, 완전성, 상품성, 특정 목적에 대한 적합성 또는 제 3 자 권리 비침해에 대해 명시적이거나 묵시적인 어떠한 보증도 하지 않습니다.

**5. 책임의 제한**

법률이 허용하는 최대 범위 내에서, 저작권자는 본 UI Kit의 사용 또는 사용 불능으로 인해 발생하는 직접적, 간접적, 우발적, 특별 또는 결과적 손해에 대해 어떠한 책임도 지지 않습니다.

**6. 라이선스 종료**

귀하가 본 라이선스의 조건을 위반하는 경우, 본 라이선스는 즉시 자동으로 종료됩니다. 종료 시, 귀하는 본 UI Kit의 모든 사용을 즉시 중단하고, 보유 중인 모든 원본 및 복제본을 완전히 삭제해야 합니다.

***

### **TDS Mobile UI Kit for Apps-in-toss License**

In the event of any discrepancy or inconsistency between the Korean and English versions, the Korean version shall prevail.

**1. Grant of License**

This license defines the terms and conditions for using the Apps-in-toss Figma UI Kit (“the UI Kit”). By downloading, copying, or using the UI Kit, you agree to all terms stated herein. This license may be modified at the discretion of Viva Republica Inc. without prior written agreement.

**2. Permitted and Prohibited Uses**

Permitted Uses:

* Development of applications for Apps-in-toss
* Design work within applications developed for Apps-in-toss
* Creation of prototypes for Apps-in-toss applications

Prohibited Uses:

* Using the UI Kit or any part of it in any other project, product, or service
* Using the UI Kit or any part of it for commercial purposes, including sale, distribution, or provision to third parties
* Copying, modifying, editing, or repurposing the UI Kit or any of its components
* Copying, modifying, editing, or repurposing the UI Kit or its components for use in other projects, products, or services, or for any commercial purpose (including sale or provision to third parties)
* Redistributing the UI Kit or any of its components to any third party

**3. Intellectual Property Rights**

All brand assets, logos, and visual elements contained in the UI Kit are protected by copyright and trademark laws. All rights, ownership, and interests in the UI Kit and its assets, components, and styles belong to Viva Republica Inc. This license grants only a limited right to use and does not transfer any intellectual property rights.

**4. Disclaimer of Warranty**

The UI Kit is provided “as is” without any express or implied warranties, including but not limited to warranties of accuracy, completeness, merchantability, fitness for a particular purpose, or non-infringement.

**5. Limitation of Liability**

To the maximum extent permitted by law, the copyright holder shall not be liable for any direct, indirect, incidental, special, or consequential damages arising from the use or inability to use the UI Kit.

**6. Termination**

This license will automatically terminate if you violate any of its terms. Upon termination, you must immediately cease all use of the UI Kit and permanently delete all copies in your possession.


# 디자인 도구

앱인토스 미니앱 UI를 디자인하는 두 가지 방법을 소개해요.

* **피그마**: TDS 컴포넌트 라이브러리를 직접 다운로드해 사용해요.
* **앱빌더**: 별도 설치 없이 콘솔에서 바로 사용할 수 있는 웹 기반 UI 디자인 툴이에요.

***

### 피그마

제공된 라이브러리에 있는 TDS 컴포넌트로 디자인하는 것을 권장해요. UI 스타일 개발에 신경쓰지 않고 개발 문서에 있는 코드를 토대로 빠르게 개발할 수 있어요.

{% hint style="info" %}
**라이선스 안내**

본 UI Kit을 사용함으로써, 본 UI Kit에 포함된 라이선스의 조건에 동의하는 것으로 간주됩니다.
{% endhint %}

***

**파일 직접 다운로드**

UI Kit `.fig` 파일을 다운로드 후 라이브러리에 연결해 사용해요.

**1. 파일 다운로드**

여기서 파일을 다운로드 해주세요.

{% file src="/files/zd1X6DLfKW7TVyS3p5HH" %}

{% hint style="info" %}
**유의사항**

* UI Kit에 적용된 Semantic Color는 현재 버전의 코드와 일치하지 않아요.
* Figma와 Toss 앱에서 서로 다른 Font Family를 사용하고 있어요. Toss Product Sans는 별도 자산으로 배포가 어려워, Figma에서는 SF Pro를 사용해요.\
  Toss 앱에서는 Font Family가 자동으로 Toss Product Sans로 적용되니 참고해 주세요.
  {% endhint %}

**2. 파일 가져오기**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FLUw6DZX9BDDGGoOyHNNz%2Fimage.png?alt=media&#x26;token=da97e40e-94eb-4943-934b-702507808071" alt=""><figcaption></figcaption></figure>

1. 피그마를 실행한 후 상단 메뉴에서 Import 버튼을 클릭해요.
2. 다운로드한 `TDS_Mobile_for_Apps_in_Toss_(2602).fig` 파일을 선택하거나 창으로 드래그&드롭 해주세요.
3. 선택한 프로젝트 또는 Drafts 폴더 안에 파일이 생성돼요.

**3. 라이브러리로 연결 (Publish)**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fwc9llg7td8bjGkPlhaIs%2Fimage.png?alt=media&#x26;token=82f9f750-1f25-411c-aa88-9b59052184f1" alt=""><figcaption></figcaption></figure>

1. 불러온 파일을 열고, 왼쪽 패널에서 Assets 탭을 클릭해요.
2. 오른쪽 상단( 📚 ) 아이콘을 클릭하여 Manage libraries 창을 열어주세요.
3. 파일 옆의 Publish 버튼을 눌러 "이 파일을 라이브러리로 사용"하도록 설정해 주세요.

> 🔸 Publish 전: 다운로드 파일 안에서만 사용 가능\
> 🔸 Publish 후: 다른 피그마 디자인 파일에서도 사용 가능

**4. 컴포넌트 연결 후 사용**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FTRkezrK0UwOgbHIhT4zX%2Fimage.png?alt=media&#x26;token=377f054b-a81f-45b2-9991-d296afa9c9bc" alt=""><figcaption></figcaption></figure>

1. 작업장 왼쪽 패널의 Asset 탭을 눌러주세요.
2. TDS Library 파일이 활성화되어 있지 않다면 Add to file버튼을 클릭해주세요
3. Assets 탭에서 버튼, 아이콘, 색상 등 필요한 컴포넌트를 드래그해서 사용해 주세요.

{% hint style="info" %}
**자동 업데이트는 불가해요**

새 버전이 나오면, 다시 파일을 다운로드 해야해요. 새로운 버전이 나올 경우 공지해 드릴 예정이에요.
{% endhint %}

***

**디자인할 때 꼭 지켜주세요**

아래 항목들은 실제 개발과의 정합성을 맞추기 위해 특히 중요한 기준이에요.

1. 모든 화면의 최상단에는 **❖ Navigation** 컴포넌트를 반드시 사용해 주세요. 필수 컴포넌트가 이미 포함된 🌈 **Screen**을 꺼내 쓰면 더 편리해요.
2. 가능한 한 **오른쪽 패널에서만 속성을 조작**해 주세요. 캔버스에서 직접 수정하면 코드에 없는 속성이 생겨 개발 시 구현이 어렵거나 시간이 오래 걸릴 수 있어요.
3. 화면은 **가로 375px 기준**으로 작업해 주세요. 다른 크기도 가능하지만, **❖ Keypad** 처럼 반응형을 지원하지 않는 컴포넌트가 있어 불편할 수 있어요.
4. 화면 상단에는 **❖ Top,** 그 아래에는 **❖ ListRow**를 사용하면 대부분의 화면을 빠르게 구성할 수 있어요.
5. 대부분의 TDS 컴포넌트에는 기본 패딩이 포함돼 있어서 gap 없이 붙여서 사용해도 자연스럽게 보여요. 예를 들어, **❖ ListRow**에는 S, M, L, XL 네 가지 상하 패딩 옵션이 있어요. 그래도 간격 조절이 필요하다면 오토레이아웃의 gap을 사용해주세요.

***

**자주 묻는 질문**

<details>

<summary>컴포넌트가 원하는 대로 동작하지 않아요.</summary>

[개발자 커뮤니티](https://techchat-apps-in-toss.toss.im/)로 문의해 주세요. 빠르게 도와드릴게요.

</details>

<details>

<summary>원하는 컴포넌트가 없어요.</summary>

대부분의 UI는 TDS 컴포넌트로 대체할 수 있어요. 예를 들어 Card UI 대신 🔵 Mobile\_ListRow를 사용할 수 있어요.

그래도 필요한 컴포넌트가 없다면 채널톡으로 요청해 주세요. 가능한 빠르게 검토할게요.

직접 디자인할 수도 있지만, 다른 TDS 컴포넌트와 어울리게 만들어 주세요.

</details>

<details>

<summary>Toss Product Sans 폰트는 설치해야 하나요?</summary>

각 컴포넌트에 이미 폰트가 적용돼 있어서 별도 설치 없이 사용할 수 있어요.

Missing Fonts로 보여도 실제 사용에는 문제가 없어요.

</details>

<details>

<summary>TDS 컴포넌트를 분리해서 써도 되나요?</summary>

가능하지만 신중하게 판단해 주세요.

컴포넌트를 깨서 일부만 사용하면 개발 시 별도의 코드를 작성해야 해요.

</details>

<details>

<summary>아이콘 색상이 이상해요.</summary>

가끔 발생하는 버그예요. 알맞은 색상으로 수정해서 사용해 주세요.

</details>

<details>

<summary>아이콘이 한글로 검색되지 않아요.</summary>

영어로 검색하거나 유니코드로 검색해 주세요.

피그마의 한계로 현재는 개선이 어려워요.

</details>

<details>

<summary>어떤 디자인이든 토스 앱에 배포할 수 있나요?</summary>

원칙적으로 가능하지만, 심사 과정에서 부적절하거나 사용성을 크게 해치는 경우에는 반려될 수 있어요.

</details>

<details>

<summary>다크 모드는 어떻게 대응하나요?</summary>

다크 모드는 추후 지원할 예정이에요. 지금은 라이트 모드 기준으로만 디자인해 주세요.

</details>

<details>

<summary>더 궁금한 점이 있어요.</summary>

[개발자 커뮤니티](https://techchat-apps-in-toss.toss.im/)로 언제든 문의해 주세요.

</details>

***

### 앱빌더 <a href="#app-builder" id="app-builder"></a>

앱빌더는 앱인토스 파트너사를 위한 **웹 기반 UI 디자인 툴**이에요. 별도 설치 없이 토스의 UI 스타일 가이드를 따르는 화면을 빠르게 만들 수 있어요.

앱빌더는 워크스페이스에서 [앱을 등록한 뒤](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/console-workspace) '디자인' 메뉴에서 시작할 수 있어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FCyR9MnMjnNBDuOUh9hNX%2Fimage.png?alt=media&#x26;token=1569c507-dd78-4a4c-9ad7-98ebe06340e7" alt=""><figcaption></figcaption></figure>

***

**1. 앱빌더 한눈에 보기**

**앱빌더로 할 수 있는 일**

* 토스 UI 스타일에 맞는 화면을 빠르게 만들 수 있어요.
* 디자인 에셋과 컴포넌트를 바로 사용해 화면을 구성할 수 있어요.
* 프로토타입으로 실제 모바일 화면처럼 미리 볼 수 있어요.

**이런 점이 좋아요**

* 디자인 라이브러리를 따로 준비하지 않아도 돼요.
* 오른쪽 속성 패널에서 색상, 텍스트, 간격을 바로 조정할 수 있어요.
* 웹에서 바로 열어서 사용할 수 있어요.
* 콘솔에서 미니앱을 선택하고 디자인 메뉴를 누르면 바로 시작할 수 있어요.

**참고·주의 사항**

* 개발자 모드는 오른쪽 패널 상단 토글을 켜면 사용할 수 있어요.
* 현재는 기본 UI 디자인 기능만 제공돼요.
* 앱빌더는 베타 버전이에요. 일부 기능이 없거나 오류가 있을 수 있어요.
* 공식 출시 전이므로 화면 캡처나 녹화본을 외부에 공유하지 말아 주세요.

***

**2. 시작하기: 프로젝트 생성**

디자인을 시작하려면 먼저 프로젝트를 만들어야 해요. 홈 화면 오른쪽 상단의 '+ 새로 만들기' 버튼을 눌러 프로젝트를 만들 수 있어요.

**프로젝트 정리 방법**

* 프로젝트는 완전히 삭제할 수 없어요.
* 대신 보관함에 넣거나 다시 꺼내는 방식으로 관리해요.

**즐겨찾기와 폴더**

* 프로젝트에 마우스를 올리면 보이는 별 버튼으로 즐겨찾기를 설정할 수 있어요.
* 즐겨찾기한 프로젝트는 화면 상단에 고정돼요.
* 왼쪽 패널의 '+' 버튼으로 프로젝트 폴더를 만들 수 있어요.

***

**3. 디자인 준비: 브랜드 스타일과 페이지**

프로젝트를 열면, 본격적인 디자인 전에 기본 설정을 해요.

**브랜드 스타일 설정**

처음 프로젝트를 열면 '시작하기' 모달이 보여요. 여기서 다음을 설정해 주세요.

* **서비스 이름:** 앱인토스 콘솔에 등록된 이름만 선택할 수 있어요.
* **버튼 색상:** 서비스의 기본 색상(primary color)이에요. 접근성을 위해 일부 색상은 자동으로 보정될 수 있어요.

**페이지 이해하기**

페이지는 하나의 프로젝트 안에서 여러 화면을 나누어 디자인하는 단위예요. 왼쪽 패널 상단에서 페이지를 추가하거나 삭제할 수 있어요.

* **준비하기 페이지:** 앱빌더 기본 사용법을 익히는 페이지예요.
* **템플릿 페이지:** 바로 복제해서 쓸 수 있는 UI 예제가 준비돼 있어요.

***

**4. 화면을 만드는 두 가지 방법**

앱빌더에서는 상황에 따라 두 가지 방식으로 디자인해요.

**1) 퀵스타트**

토스에서 실제 사용하는 UI를 그대로 불러와 텍스트나 일부 옵션만 수정해 사용하는 방식이에요.

* 단일 화면뿐 아니라 여러 화면이 연결된 플로우도 제공돼요.
* 빠르게 화면 구조를 잡고 싶을 때 좋아요.

{% hint style="info" %}
**수정 시 유의사항**

* 오른쪽 패널에서 제공하는 항목만 수정할 수 있어요.
* 내부 요소는 Ctrl(Cmd) + 클릭으로 선택해요.
* 제공된 옵션 외 요소를 변경하거나 삭제하는 건 지원하지 않아요.
  {% endhint %}

**2) 커스텀**

기본 화면을 추가한 뒤, 디자인 에셋을 조합해 UI를 직접 구성하는 방식이에요.

* 필요한 퀵스타트가 없을 때 사용해요.
* 더 자유롭게 레이아웃을 만들 수 있어요.

{% hint style="info" %}
**기본 화면 기준**

* 화면 크기는 아이폰 13 미니 기준(375 × 812)이에요.
* 에셋도 이 너비에 맞춰 제작돼 있어요.
* 화면 너비를 임의로 바꾸는 건 권장하지 않아요.
  {% endhint %}

***

**5. 화면 구성 요소 다루기**

**텍스트 사용하기**

* Text 에셋으로 텍스트에 TDS 스타일을 적용할 수 있어요.
* 오른쪽 패널에서 스타일과 내용을 수정해요.
* 텍스트 종류는 다음과 같아요.
  * **일반형:** 본문용 텍스트
  * **포스트형:** 제목용 볼드 텍스트

{% hint style="info" %}
**입력 팁**

줄바꿈은 Shift + Enter를 사용해 주세요.
{% endhint %}

**아이콘과 그래픽 사용하기**

토스팀이 제공하는 그래픽만 사용할 수 있어요. 그래픽 추가 방법은 두 가지예요.

* 상단 컨트롤바의 리소스에서 선택
* 오른쪽 패널에서 Asset 추가 → 변경하기 → 그래픽 선택

**3D 그래픽**

* 베타 버전에서는 기본 3D 그래픽을 직접 제공하지 않아요.
* 대신 2D 그래픽을 선택해 3D로 변환할 수 있어요.
* 저장하기를 누르면 토스팀 검토 후 사용할 수 있어요.
* 퀵스타트에 포함된 3D 그래픽은 따로 사용할 수 없어요.

***

**6. 레이아웃 정리하기**

여러 요소를 정돈하려면 붙이기 기능을 사용해 주세요.

**붙이기 기본 사용법**

* 여러 에셋을 Shift + 클릭으로 선택해요.
* 가로, 세로, 겹쳐 붙이기로 그룹화할 수 있어요.
* 떼어내기로 그룹을 해제할 수 있어요.

**Stack Layout 옵션**

* Fit: 내용에 맞게
* Fill: 화면을 채우게
* Fixed: 고정 길이
* Gap: 요소 사이 여백
* Padding: 바깥 여백

**Style 옵션**

* Visible: 보이기/숨기기
* Opacity: 투명도
* Fill: 배경색
* Border: 테두리
* Radius: 모서리 둥글기
* Shadow: 그림자

***

**7. 미리보기와 공유**

**프로토타입 확인하기**

오른쪽 상단 플레이 버튼으로 모바일 해상도 기준 미리보기를 확인할 수 있어요.

* 휴대폰 아이콘으로 기기별 프리뷰를 볼 수 있어요.
* 모든 에셋을 Fill로 설정하면 반응형 레이아웃을 만들 수 있어요.

**프로토타입 공유**

프로토타입 공유 버튼으로 링크를 복사하면 실제 모바일 기기에서 바로 UI를 확인할 수 있어요.

***

**8. 튜토리얼로 한 번 더 익히기**

지금까지 소개한 기능을 모두 활용해 검색 UI를 만들어보는 [튜토리얼 영상](https://www.notion.so/237714bbfde780eba495e21c126f1487?pvs=21)을 확인해 보세요.


# UI/UX 가이드

이 문서는 앱인토스 미니앱을 디자인하고 개발할 때 지켜야 할 UI/UX 기준을 한 곳에 모은 가이드예요.

브랜드 로고·이름·컬러 설정 방법부터 다크패턴 방지 정책, UX 라이팅 원칙, 그래픽 리소스 활용법, 해상도 설계 기준까지 순서대로 안내해요. 서비스를 출시하기 전에 각 항목을 꼼꼼히 확인하고 적용해 주세요.

***

### 미니앱 브랜딩 가이드

앱인토스 서비스가 토스와 명확히 구분되는 브랜드 경험을 전달할 수 있도록 준비한 기준들이에요. 사용자가 토스와 앱인토스를 혼동하지 않도록 하는 데 중요한 기준이니, 꼭 가이드를 읽고 지켜주세요.

***

**1. 브랜드 로고 / 이름 / 컬러**

브랜드 로고, 브랜드 이름, 브랜드 컬러를 토스와 앱인토스 화면 곳곳에 노출해 사용자가 브랜드를 분명하게 인지할 수 있게 해요.

**브랜드 로고**

파트너사의 로고는 전체탭, 혜택탭, 푸시, 알림, 내비게이션, 브릿지에 보여져요. 아래에 첨부된 이미지 또는 일러스트 파일을 반드시 사용해 로고를 제작해 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FgoQLBj6ToWx8HhNOxhrR%2Fimage.png?alt=media&#x26;token=f815fc0a-13b8-48ee-baeb-7f3640a5e035" alt=""><figcaption></figcaption></figure>

로고 제작과 적용 시 다음 기준을 지켜주세요.

* 크기는 600×600px의 각진 정사각형이어야 해요. 모서리가 둥근 형태는 사용할 수 없어요.
* 로고 뒤에는 반드시 배경이 있어야 하며, 라이트 모드와 다크 모드 모두에서 잘 보이는 배경 색상을 사용해요.
* 애니팡처럼 로고 자체에 배경이 포함된 경우에는 이미지를 600×600px 영역에 꽉 차게 배치해요.
* 앱인토스 콘솔에 로고 파일을 업로드하고, `granite.config.ts` 파일 상단의 `appsInToss` 함수에서 `brand.icon` 속성에 동일한 로고 링크를 입력해요.

**브랜드 이름**

브랜드 이름은 전체 탭, 혜택 탭, 푸시, 알림, 내비게이션, 브릿지에 노출돼요. 특별한 이유가 없다면 한글로 작성해 주세요. 예를 들면 `토스`는 가능하지만 `Toss`는 권장하지 않아요.

* 앱인토스 콘솔에 브랜드 이름을 입력해요.
* `granite.config.ts` 파일 상단의 `appsInToss` 함수에서 `brand.displayName` 속성에 동일한 브랜드 이름을 입력해요.

**브랜드 컬러**

브랜드 컬러는 토스 내 진입점, 브릿지, 버튼(토스 디자인 시스템 사용 시) 등에 사용돼요. 브랜드 컬러 설정 기준은 다음과 같아요.

* 이미 브랜드 컬러가 있다면 그대로 사용해요.
* 브랜드 컬러가 없다면 로고에서 가장 많이 사용된 색상을 선택해요.
  * 선택이 어렵다면 [컬러 추출 사이트](https://lokeshdhakar.com/projects/color-thief/)를 사용해 로고 이미지에서 대표 색상을 뽑아도 좋아요.
* 브랜드 컬러가 색 대비 기준을 충족하지 못하면, 기존 색상을 최대한 유지하면서 자동으로 보정돼요.
* `granite.config.ts` 파일 상단의 `appsInToss` 함수에서 `brand.primaryColor` 속성에 `#`을 포함한 여섯 자리 헥스 코드 값을 입력해요. 예를 들면 `#3182F6`처럼 입력해요.

***

**2. 내비게이션 바**

내비게이션 바는 화면 상단에 고정되는 영역으로, 앱인토스 전용 컴포넌트를 제공해요. 입점하는 서비스의 유형에 따라 아래 가이드를 참고해 내비게이션 바를 설정해 주세요.

* 게임 가이드
* 비게임 가이드

***

**3. 탭바**

탭바는 필수 컴포넌트는 아니에요. 다만 탭바가 필요하다면, 반드시 토스에서 제공하는 플로팅 형태의 탭바를 사용해서 직접 구현해야 해요.

자체 UI를 사용하더라도 탭바만큼은 제공된 형태에 맞춰 구현해 주세요. 토스 메인 화면의 기본 하단 탭과 형태가 겹치면, 사용자가 현재 위치를 헷갈릴 수 있기 때문이에요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FTRnoQbESHiTi4FyB103B%2Fimage.png?alt=media&#x26;token=b22caa63-02b3-4f61-be10-4407c9305688" alt=""><figcaption></figcaption></figure>

탭바 설정 시 다음 기준을 지켜주세요.

* 탭 개수는 최소 2개, 최대 5개까지 사용할 수 있어요.
* 토스에서 제공하는 플로팅 형태를 유지해야 해요.

***

### 다크패턴 방지 정책

토스 사용자는 어디서든 일관되고 신뢰할 수 있는 사용 경험을 기대해요. 서비스마다 기준이 달라지면 사용자는 혼란을 느끼고, 이는 서비스 전반에 대한 신뢰 저하로 이어질 수 있어요.\
UX 가이드라인은 창의성을 제한하기 위한 규칙이 아니라, 사용자에게 예측 가능하고 편리한 경험을 제공하기 위한 최소한의 기준이에요.

이를 위해 토스는 반드시 지켜야 할 최소한의 사용 경험 기준을 정했고, 아래 사례들은 이 기준을 벗어난 치명적인 사용성 오류로, 앱인토스 서비스로 출시할 수 없는 경우에 해당해요.

***

**1. 서비스에 진입하자마자 바텀시트가 뜨는 경우**

서비스에 들어오자마자 사용자가 기대한 화면 대신 **전면을 가로막는 광고성 바텀시트**가 노출되는 경우예요.\
알림 동의를 요청하는 바텀시트도 여기에 포함돼요.

사용자는 서비스에 진입한 순간, 자신이 의도한 목적을 바로 수행할 수 있기를 기대해요.\
이때 예상하지 못한 인터럽트가 나타나면 몰입이 끊기고, 서비스를 바로 이탈할 가능성이 높아져요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2F4FgE4c6kLAuVONI1p0qh%2Fimage.png?alt=media&#x26;token=6bde490c-91a9-42ce-baca-248baeb80791" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2F8oUcDjrNuggnxQt6HVpK%2Fimage.png?alt=media&#x26;token=d9518749-7c97-4915-9052-ac2f2c4a8104" alt=""><figcaption></figcaption></figure>

***

**2. 뒤로 가기 버튼을 눌렀을 때, 이전 화면을 막는 바텀시트가 뜨는 경우**

사용자가 이전 화면으로 돌아가 다른 서비스를 탐색하려는 순간, 예상과 달리 **알림 동의를 유도하는 바텀시트가 노출되는 경우**를 말해요.

이탈을 막기 위해 의도적으로 설계된 추가 인터럽트는 사용자에게 자율성이 침해된다는 인상을 줄 수 있어요. 이 경험은 서비스에 대한 신뢰를 떨어뜨릴 수 있어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fvin9MSRY1yD0YwkyilSA%2Fimage.png?alt=media&#x26;token=4b42f569-fddf-432a-805b-2ec441257f04" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FKOL16Sm3jWp6RetmgLwy%2Fimage.png?alt=media&#x26;token=e73dfec3-723e-4792-8cda-e8e6ffc5a147" alt=""><figcaption></figcaption></figure>

***

**3. 나갈 수 있는 선택지가 없는 경우**

파트너사가 유도한 CTA를 선택하는 것 외에는 사용자가 다른 선택을 할 수 없는 구조를 의미해요. 이처럼 거절할 수 없는 설계는 사용자에게 강제적으로 느껴질 수 있어요. 결과적으로 서비스에 대한 반감과 불신으로 이어질 가능성이 높아요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2F6N4kMhYWveHL5RezuVZF%2Fimage.png?alt=media&#x26;token=2f03084e-1e40-40c2-93c5-46222b72412c" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2F0kfleL9R4fvBmCwifBg1%2Fimage.png?alt=media&#x26;token=2b977eee-a998-4be7-9990-b3f325181565" alt=""><figcaption></figcaption></figure>

***

**4. 예상하지 못한 순간에 광고가 노출되는 경우**

사용자가 아이템을 받기 위해 메뉴를 선택했는데, 예상과 달리 **전면 광고가 노출되는 경우**예요.

사용 흐름 중 갑작스럽게 등장하는 광고는 몰입을 방해하고, 서비스와 브랜드에 대한 불쾌한 인상을 남길 수 있어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FPkW3YuNsr942y5V4MgH7%2Fimage.png?alt=media&#x26;token=9ca58826-3a6d-44d6-a3b3-9f689cf32232" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FLBjNQuMVWC1eNJx21C5H%2Fimage.png?alt=media&#x26;token=5b7a7d38-55d7-44a3-bc66-d672d15d44b5" alt=""><figcaption></figcaption></figure>

***

**5. CTA 버튼만 보고 다음 행동을 예상할 수 없는 경우**

CTA에 이미 화면에서 설명한 가치를 그대로 반복해서 사용해, 버튼을 눌렀을 때 **어떤 화면이나 행동으로 이어지는지 알 수 없는 경우**예요.

CTA는 사용자가 다음에 무엇을 하게 되는지 명확하게 알려주는 장치예요. 버튼 라벨이 모호하거나 설명만 반복하면 사용자는 클릭 결과를 예측하지 못해 불안함을 느껴요. 이 불안함은 클릭을 망설이게 만들고, 결국 전환율 저하로 이어질 수 있어요.

또한 CTA 위에 과장되거나 중복된 보조 설명을 함께 노출하면 버튼의 역할이 흐려지고, 사용자에게 혼란을 줄 수 있어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FNpastgGNaXESGtvg1WAV%2Fimage.png?alt=media&#x26;token=6ec34f4c-ae7a-4ee9-9ed4-eaa44f1e1ec6" alt=""><figcaption></figcaption></figure>

***

### UX 라이팅

본 가이드라인은 토스 앱의 보이스톤을 적용한 문구를 쓸 수 있도록 제공된 지침이에요.\
아래 가이드라인을 지켜주세요.

***

**1. 해요체**

제품 안의 모든 문구는 '해요체'로 써요.\
일관성 있는 사용자 경험을 만들 수 있도록 **상황, 맥락을 불문하고 모든 문구에 해요체를 적용해주세요.**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fs3xrPf3sMIfvD0GcplCY%2Fimage.png?alt=media&#x26;token=9585da2d-d7de-4b44-b36a-f887513423ed" alt=""><figcaption></figcaption></figure>

***

**2. 능동적 말하기**

제품 안에서 최대한 **능동형 문장**을 써주세요. 수동형 문장은 특정 상황에서만 쓰는 게 좋아요.

**됐어요 → 했어요**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FVJgrA6cCYGQmRwWX0P77%2Fimage.png?alt=media&#x26;token=8f0c9812-f285-42c1-b1f5-88e7e8c8312f" alt=""><figcaption></figcaption></figure>

**'\~었' 빼기**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fsag4ihC5KMbu4vuB7xyy%2Fimage.png?alt=media&#x26;token=2d401000-8a56-4771-a4ba-194eec9f758a" alt=""><figcaption></figcaption></figure>

**동사 바꿔쓰기**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FPAOQrSa96BeC9pk5gPhn%2Fimage.png?alt=media&#x26;token=60226254-77e1-4498-82d8-002f17007cbd" alt=""><figcaption></figcaption></figure>

***

**3. 긍정적 말하기**

제품 안에서 부정적 커뮤니케이션을 최대한 줄이고 긍정형 문장을 써주세요. 예 : 안 돼요, 없어요 (X) → \~하면 할 수 있어요 (O)

**없어요 → 있어요**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FNZ7dqymQvCfeLlEbLUkP%2Fimage.png?alt=media&#x26;token=432a21ab-6e42-41a2-b69d-48b731e6dc08" alt=""><figcaption></figcaption></figure>

**에러 메시지**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FsCRlrlMNHUICEYrSE2sc%2Fimage.png?alt=media&#x26;token=44c64283-11cf-45b2-acd6-c038165ee41f" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**다이얼로그 왼쪽 버튼은 \[닫기]**

다이얼로그 왼쪽 버튼은 **닫기**로 문구를 통일해요. **취소**는 사용자가 하고 있는 작업이 취소된다고 오해할 수 있어 쓰지 않아요.
{% endhint %}

**혜택을 받을 수 없을 때**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FDJwkWhJ89uj6NCj6fBPM%2Fimage.png?alt=media&#x26;token=d2bfcb48-d54d-4b07-a2a4-243f6d1bac84" alt=""><figcaption></figcaption></figure>

**혜택 대상 안내**

**서비스는 쓸 수 있지만, 특정 혜택은 받을 수 없을 때 → 긍정형 문장**\
사용자는 스캔하기 때문에 제품 전체를 쓸 수 없다고 이해하기 쉬워요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fabk1sVHMdATa0BdaUUCN%2Fimage.png?alt=media&#x26;token=c4ffae46-0704-4b4e-9b23-5777760581d7" alt=""><figcaption></figcaption></figure>

***

**4. 캐주얼한 경어**

제품 안에서 '\~시겠어요?', '시나요?', '\~께' 같은 과도한 경어를 쓰지 않아요. 최대한 캐주얼하고 친근한 말투를 쓰는 게 좋아요.

**동사에서 '\~시' 빼기**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FZ5kKRNRJkoXbqJqvF9TW%2Fimage.png?alt=media&#x26;token=5f68f008-bcfb-490e-bdb0-d55248af139b" alt=""><figcaption></figcaption></figure>

**'계시다' → '있다'**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fi1BdEwa8qqlnYezlU0Cc%2Fimage.png?alt=media&#x26;token=0fc0914f-b20f-4107-97a9-dc9f1f1371aa" alt=""><figcaption></figcaption></figure>

**'여쭈다' → '확인하다, 묻다'**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FpbXOokR14nz8oFPa17US%2Fimage.png?alt=media&#x26;token=c1fabe50-b117-4216-9e1f-0ce800afd34e" alt=""><figcaption></figcaption></figure>

**'께' → '에게'**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FVrpOe0dAV50dqCs8jt0f%2Fimage.png?alt=media&#x26;token=694ea0ec-e661-40ee-891b-af07fdd9fc8f" alt=""><figcaption></figcaption></figure>

**경어를 뺐을 때 어색한 경우**

사용자의 정보를 받는 질문에서 기계적으로 '\~시'를 뺐을 때 문장이 어색할 수 있어요.\
**파악하고 싶은 정보를 '주어'로 써서 문장을 새롭게 써보세요.**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2F4xeEnYrBVj65wyAs6Kdf%2Fimage.png?alt=media&#x26;token=4964f484-664d-4cad-b51c-4b359a30667f" alt=""><figcaption></figcaption></figure>

***

**5. '{명사} + {명사}' 쓰지 않기**

**한자어 풀어쓰기**

한자어 명사를 풀어서 동사 형태로 쓸 수 있어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FF1AFeVcSQ97d1e5vOW6r%2Fimage.png?alt=media&#x26;token=2b742870-af38-4d1c-825c-b3a86520450e" alt=""><figcaption></figcaption></figure>

**한자어를 풀어쓰기 어려울 경우**

'{명사}가 {명사}해서' 형태로만 풀어줘도 더 캐주얼하게 쓸 수 있어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FroJcLns5yrT0vi4r9b27%2Fimage.png?alt=media&#x26;token=41fe6ea9-4a19-4f9c-a970-936432277eea" alt=""><figcaption></figcaption></figure>

***

**예외 규칙**

<details>

<summary><strong>수동형 문장을 써도 되는 경우</strong></summary>

수동형 문장이 더 명확 하고 간결한 커뮤니케이션을 만드는 때도 있어요. 수동형으로 더 좋은 문장을 쓸 수 있는 사례를 알려드릴게요

**⚠️ 서비스 종료, 기간 만료**

**수동형 문장**으로 쓰면

```
<ul>
<li>주어(종료 서비스, 기간 등)를 강조할 수 있어요.</li>
<li>'종료'와 '만료'의 뉘앙스를 정확히 전달할 수 있어요.</li>
</ul>
```

**주기적으로 종료가 반복되는 제품에는 '종료돼요'를 쓰지 않아요.**

**⚠️ 사용자에게 미치는 영향을 알려줄 때**

(주요 동사 : 연체, 해지, 적용 등)**수동형 문장**으로 쓰면

```
<ul>
<li><strong>인과 관계</strong>를 명확하게 설명해요. '사용자의 행동에 의해 따라오는 결과'라는 점을 알려줄 수 있어요.</li>
</ul>
```

**⚠️ 사용자 안심**

**수동형 문장**으로 쓰면

```
<ul>
<li>'정보 수집 안내' 등의 민감한 상황에서 사용자를 안심하게 할 수 있어요.</li>
</ul>
```

**⚠️ 되어요 (X) → 돼요 (O)**

모바일 화면의 좁은 공간을 고려, '되어요'는 모두 '돼요'로 통일해서 써주세요.

</details>

<details>

<summary><strong>경어를 써도 되는 경우</strong></summary>

특정 상황에서 제한적으로 '시나요?, 셨나요?' 의문형 어미를 쓸 수 있어요.

**⚠️ 사용자의 맥락을 활용해서 질문할 때**

'시나요?', '셨나요?' 형태의 경어를 활용해서 사용자의 당황스러움을 줄일 수 있어요.

**⚠️ 사용자의 상황을 추정할 때**

토스에 명확한 정보가 없어서 사용자에게 직접 판단하게 해야 할 때 '경어'로 정중하게 질문할 수 있어요.

**⚠️ 사용자의 선의가 필요할 때**

설문조사처럼 사용자의 선의를 기대해야 할 때 경어로 정중하게 질문해요.

</details>

<details>

<summary><strong>부정형 문장을 써도 되는 경우</strong></summary>

사용자에게 명확하게 부정적인 내용을 알려줘야 할 때는 부정형 문장을 써도 좋아요.

**⚠️ 서비스를 정책 상 쓸 수 없을 때**

**부정형 문장**으로 써야

```
<ul>
<li>사용자에게 상황을 명확하게 인지시킬 수 있어요.  쓸 수 없는 이유를 함께 안내해주세요.</li>
</ul>
```

**⚠️ 일부 기능만 쓸 수 없을 때**

**부정형 문장**으로 써야

```
<ul>
<li>사용자가 어떤 기능을 쓸 수 없는지 명확하게 인지 할 수 있어요.</li>
</ul>
```

* 사용자 선택의 결과를 명확하게 안내할 수 있어요.

**⚠️ 사용자 안심**

**부정형 문장**으로 써야

```
<ul>
<li>'정보 수집 안내' 등의 민감한 상황에서 사용자를 안심하게 할 수 있어요.</li>
</ul>
```

</details>

***

### 그래픽

토스에서 제공하는 그래픽 리소스와 올바른 활용 방법을 안내드려요.

{% hint style="info" %}
**그래픽 저작권 안내**

토스의 모든 그래픽 자산 및 토스트를 통해 생성된 그래픽은 「저작권법」 및 관련 법령에 따라 보호받는 ㈜비바리퍼블리카의 지식재산권입니다.

제공된 그래픽은 앱인토스 제휴 환경 내에서의 서비스 운영 및 홍보 목적으로만 사용할 수 있으며, 다른 서비스나 매체에서 복제·수정·배포·전송·공중송신 등으로 활용하는 행위는 금지됩니다.
{% endhint %}

***

**토스에서 제공하는 그래픽 리소스**

**1. 아이콘 & 이모지**

토스에서는 약 7,000개 이상의 아이콘과 이모지 세트를 제공해요. 앱빌더와 피그마에서 디자인을 할 때 아이콘과 이모지 목록을 확인할 수 있어요. 앱빌더는 워크스페이스에서 [앱을 등록한 뒤](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/console-workspace) '디자인' 메뉴에서 시작할 수 있어요. 직접 아이콘을 제작해야 한다면, [앱인토스 아이콘 제작 가이드](https://www.notion.so/21b714bbfde780fb84bac2acfbb4a6b9?pvs=21)를 참고해 기준에 맞게 만들어 주세요.

**\[사용 시 유의사항]**

* 아이콘은 화면에서 24\~40px 크기로 사용해 주세요.
* 아이콘이나 이모지를 두 개 이상 병렬로 조합하는 방식은 지양해요. 한 번에 하나만 사용해 주세요.
* 토스에서 제공하는 그래픽 리소스는 서비스 화면 내 UI 디자인 용도로만 사용할 수 있으며, 앱 로고, 썸네일 등 앱 정보에서 활용할 수 없어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FCjy0zLN2xg0KwX7Zcrko%2Fimage.png?alt=media&#x26;token=c6bc5b9c-fbd0-4ef2-a68f-57e878f32f3f" alt=""><figcaption></figcaption></figure>

**2. 스마트폰 목업 파일**

스마트폰 목업으로 리소스를 제작할 때는 위 제공된 파일을 그대로 사용해 주세요. 아이콘은 크기별로 제공되니, 임의로 크롭하거나 색상을 보정하거나 형태를 왜곡하지 말아 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FnEhRyDRhLuuxKAJQLWdV%2Fimage.png?alt=media&#x26;token=40cb0f07-cd42-4ea6-894e-14b18b6a5dbf" alt=""><figcaption></figcaption></figure>

<details>

<summary>디자인 업데이트 반영을 위해, 개발 시에도 아래 URL로 넣으시는 것을 권장드려요.</summary>

* **Full**
  * <https://static.toss.im/illusts/mockup-template-250508.png>
* **Large**
  * Top: <https://static.toss.im/illusts/mockup-large-top-0513.png>
  * Bottom: <https://static.toss.im/illusts/mockup-large-bottom-0513.png>
* **Medium**
  * Top: <https://static.toss.im/illusts/mockup-medium-top-0513.png>
  * Bottom: <https://static.toss.im/illusts/mockup-medium-bottom-0513.png>
* **Small**
  * Top: <https://static.toss.im/illusts/mockup-small-top-0513.png>
  * Bottom: <https://static.toss.im/illusts/mockup-small-bottom-0513.png>

</details>

**3. 토스트 (AI 이미지 생성 툴)**

1번에서 제공된 아이콘과 이모지를 바탕으로 3D 이미지를 생성할 수 있어요. 생성한 그래픽은 실제 화면에 사용하기 전에 토스 그래픽 디자인 팀의 사용 승인을 받아야 해요. 승인은 보통 1일 이내에 진행돼요.

**4. 그 외**

3D 그래픽이나 애니메이션은 토스에서 제공한 모듈에 포함된 리소스만 사용할 수 있어요.\
제공 범위는 앞으로 점진적으로 확대할 예정이에요.

***

**그래픽의 올바른 사용법**

**1. 문맥에 맞는 그래픽을 사용해 주세요.**

그래픽은 장식이 아니라, 사용자가 화면의 의미를 더 쉽게 이해하도록 돕는 역할이에요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FDBzQJwQn0Ga7KjXRthvL%2Fimage.png?alt=media&#x26;token=d269c07c-60ef-4aab-8e47-ad13708790ac" alt=""><figcaption></figcaption></figure>

**2. 정보 밀도에 맞는 크기로 사용해 주세요.**

단순한 그래픽은 작게, 디테일이 많은 그래픽은 충분히 크게 사용해 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FlxXS0g7kEr42nC25FD0G%2Fimage.png?alt=media&#x26;token=e73a21a8-9b90-4946-a9b1-3f03773a42dd" alt=""><figcaption></figcaption></figure>

**3. 한 화면에 그래픽을 많이 사용하지 마세요.**

비슷한 크기의 그래픽이 많아질수록 시선이 분산돼요.\
가장 핵심적인 그래픽 하나만 사용하고, 나머지는 보조적인 그래픽이나 아이콘으로 대체해 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2F0cNE5OUtThm74NgkpaLZ%2Fimage.png?alt=media&#x26;token=803a52c5-a56e-452d-9229-e21e77ae70a7" alt=""><figcaption></figcaption></figure>

**4. 핵심 정보를 가리지 않게 배치해 주세요.**

중요한 내용이 아래로 밀려 불필요한 스크롤이 생기지 않도록 크기와 위치를 조정해 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Foq2UE9kUI84qMzbCkH0X%2Fimage.png?alt=media&#x26;token=ac8cd647-27b5-4c87-8b45-048b395dca1a" alt=""><figcaption></figcaption></figure>

**5. 부정적이거나 호소하는 감정 표현은 피해주세요.**

사용자에게 불쾌감을 주거나 애원, 호소처럼 느껴지는 표현은 다크 패턴에 해당해요.\
이런 감정 표현은 사용하지 말아 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FBHeAyD3knrAkdH50qBBN%2Fimage.png?alt=media&#x26;token=16761ab6-454f-47b9-9c8e-1b5785eed347" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FqvO4mQlGBhBgqxpPjcE1%2Fimage.png?alt=media&#x26;token=c2d71bb1-ef98-4a1f-a9b0-90ba19745eb6" alt=""><figcaption></figcaption></figure>

**6. 장식적인 효과나 이펙트는 사용하지 마세요.**

의미 없는 묘사, 파티클, 과한 그라데이션 같은 요소는 화면을 복잡하게 만들고 정보 전달을 방해해요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2F31WV951c3K34u0iKEiNE%2Fimage.png?alt=media&#x26;token=f68acb90-ee9c-48fa-83e2-8ae22932116d" alt=""><figcaption></figcaption></figure>

**7. 상황을 정확히 전달하는 그래픽을 사용해 주세요.**

오류가 아닌 상황에서 느낌표 아이콘을 사용하거나,\
기다릴 필요가 없는데 로딩 애니메이션을 사용하는 경우 사용자가 상황을 오해할 수 있어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FfkX0414Yrly8IUoH7lwj%2Fimage.png?alt=media&#x26;token=b16de1cb-69ad-4d75-b228-57f6df3f54e3" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FmDxw8bCjjO0puoJnQume%2Fimage.png?alt=media&#x26;token=bc3ad0de-5549-476d-8716-d7a1dba8a1fd" alt=""><figcaption></figcaption></figure>

***

**파트너사에서 직접 그래픽을 제작할 때 유의사항**

**1. 토스 스타일의 일관성을 지켜주세요.**

토스는 단순하고 명료하며 깨끗한 디지털 그래픽 스타일을 지향해요.\
손그림 느낌, 서정적인 화풍, 만화적인 표현은 화면에서 이질적으로 보일 수 있어요.

**토스의 그래픽 스타일 예시**

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fgc7WGJVu9qUU3HCOzCv7%2Fimage.png?alt=media&#x26;token=158f2477-b066-40a2-bf19-5757e0633ad3" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FKlBQ37Q8hg7pZbdZkAXf%2Fimage.png?alt=media&#x26;token=c4647f81-a045-4bea-aa94-81b0723e5679" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FLYshnC8ID2IBylNzm1SD%2Fimage.png?alt=media&#x26;token=663122cc-d7dd-41ee-a365-c8ff00da660c" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FZE7uNwjLvGav6oiObvEg%2Fimage.png?alt=media&#x26;token=b1ce4209-73ab-482a-82e0-82a9e841c62b" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FHYSIih1iNnsBhCd803ih%2Fimage.png?alt=media&#x26;token=ea269330-b024-4b0e-a9dc-7387db48b2b5" alt=""><figcaption></figcaption></figure>

**2. 고화질의 그래픽을 사용해 주세요.**

그래픽은 선명하고 깨끗한 고화질로 제작해 주세요. 해상도가 낮거나 파티클처럼 자잘한 효과가 많으면 퀄리티가 낮아 보일 수 있어요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FwGTP9NvG1i9SoaQz88EN%2Fimage.png?alt=media&#x26;token=d64917cd-9fb1-43bd-a87c-37d90ea57da1" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FxwGwp6MD3bsW5HIHKaX1%2Fimage.png?alt=media&#x26;token=3411d56d-dc00-40a1-ba84-e89b6f70bf2c" alt=""><figcaption></figcaption></figure>

**3. 다크 모드와 라이트 모드 모두에서 잘 보여야 해요.**

토스 앱에서 사용하는 그래픽은 다크 모드와 라이트 모드 모두를 고려해야 해요.\
너무 밝거나 너무 어두운 색은 특정 모드에서 잘 보이지 않을 수 있으니, 중간 명도의 색감을 사용해 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FMIbxO287cyRNqZnNXocl%2Fimage.png?alt=media&#x26;token=01491a41-9eee-4d01-bc0a-ef8421fb0b87" alt=""><figcaption></figcaption></figure>

**4. 화면 전체와 어울리게 구성해 주세요.**

그래픽이 텍스트나 CTA 버튼 같은 다른 요소보다 튀지 않도록, 화면 전체의 컬러와 레이아웃 균형을 맞춰 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2FxbvapNIefGYnWuXvNfiL%2Fimage.png?alt=media&#x26;token=1f7ecf65-3db6-48a5-9b6f-cec99f6ec6ff" alt=""><figcaption></figcaption></figure>

**5. 긍정적이고 정돈된 인상을 주세요.**

그래픽은 서비스의 안정감과 신뢰감을 만드는 데 중요한 역할을 해요.\
모노톤이 과도하게 많거나, 뿌옇고 칙칙한 인상은 피하고 밝고 정돈된 느낌으로 디자인해 주세요.

<figure><img src="https://3984554959-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGK6CDpIZXnm3aqLRTFjB%2Fuploads%2Fwe2pcDHe4sNaMnwQ3szp%2Fimage.png?alt=media&#x26;token=f649316c-3b02-4416-9931-50169f77905c" alt=""><figcaption></figcaption></figure>

***

### 해상도

이 가이드는 앱인토스 미니앱을 개발할 때 어떤 해상도를 기준으로 화면을 설계하고 최적화하면 좋은지를 안내해요. 권장 기준과 함께, 다양한 디바이스 환경에서도 안정적으로 동작하는 설계 방향을 설명해요.

앱인토스 미니앱은 여러 해상도와 화면 비율의 기기에서 실행돼요. 이 가이드는 이러한 환경 차이로 인한 혼란을 줄이기 위해, 기기별 대응이 아닌 '기준 해상도 중심 설계' 방식을 제안해요.

{% hint style="info" %}
**꼭 확인해 주세요**

하나의 논리 해상도를 기준으로 설계하고 에셋을 1x·2x로 준비하면, 앱인토스 실제 트래픽의 대부분을 안정적으로 커버할 수 있어요.
{% endhint %}

***

**앱인토스 미니앱 풀스크린 설계 기준**

앱인토스의 게임 미니앱은 풀스크린 구현이 필수예요. 풀스크린 환경에서는 다음을 반드시 고려해야 해요.

* 콘텐츠가 화면을 완전히 채워야 해요.
* 웹뷰 여백이나 반투명 영역이 남지 않아야 해요.
* 기기 회전에 따라 비율이 깨지거나 레터박스(검은 여백)가 생기면 안 돼요.
* 노치, 카메라 홀, Dynamic Island는 Safe Area로 처리해야 해요.
* 스케일링으로 인해 에셋 품질이 눈에 띄게 저하되지 않아야 해요.

그래서 해상도는 기기마다 다르게 맞추기보다, 하나의 기준 해상도를 정하고 스케일링으로 대응하는 방식이 중요해요.

***

**해상도를 바라보는 두 가지 기준**

앱인토스 미니앱에서는 해상도를 아래 두 가지로 나누어 생각하는 것을 권장해요.

**① 논리 해상도 (Logical Resolution)**

UI 배치와 좌표 계산, 게임 로직의 기준이 되는 해상도예요.

* 실제 디바이스 픽셀 해상도와는 다른 개념이에요.
* 논리 해상도는 하나만 선택하는 것을 권장해요.
* 모든 디바이스에서 동일한 플레이 감각과 화면 구성을 유지해야 해요.

**② 에셋 해상도 (Asset Resolution)**

이미지, 배경, 캐릭터, UI 리소스의 픽셀 밀도를 의미해요.

* 디바이스별로 에셋을 따로 준비할 필요는 없어요.
* 소수의 해상도 그룹으로 묶어 관리하는 방식이 효율적이에요.

***

**앱인토스 권장 해상도 기준**

**① 논리 해상도 권장 범위**

아래 범위 중 하나를 기준 해상도로 선택해 주세요.

* **세로형 미니앱:** 약 360 × 640 \~ 420 × 740
* **가로형 미니앱:** 약 640 × 360 \~ 740 × 420

이 범위 안에서 하나의 기준 해상도를 정하고, 기기 간 차이는 스케일링으로 대응하는 방식을 권장해요.

**② 에셋 해상도 권장 기준**

에셋은 그룹 단위로 준비하는 것이 좋아요.

* **1x 에셋**: 기본 해상도 대응
* **2x 에셋**: 고해상도 디바이스 대응

그래픽 품질이 특히 중요한 경우에만 3x 에셋을 선택적으로 추가해 주세요.

{% hint style="info" %}
**꼭 확인해 주세요**

대부분의 미니앱은 1x + 2x 구성만으로 충분해요. 에셋 해상도를 과도하게 나누면 메모리 사용량 증가, 로딩 지연, 유지보수 비용 증가로 이어질 수 있어요.
{% endhint %}

***

**데이터 기반 참고 사항**

이 가이드는 **앱인토스 실제 서비스 환경에서 수집된 디바이스 해상도 데이터**를 기반으로 정리했어요.

* 전체 미니앱 트래픽의 약 **70\~80%** 가 가로 약 **800\~900**, 세로 약 **360\~420** 범위의 viewport 해상도에 집중돼 있어요.
* 나머지 트래픽은 다양한 해상도로 분산된 롱테일 형태예요.

그래서 모든 해상도를 개별 대응하기보다, 대표적인 해상도 범위를 기준으로 설계하는 방식이 가장 효율적이에요.

***

**테스트 기기 가이드**

모든 디바이스에서 테스트할 필요는 없어요. 아래 조건을 만족하는 **대표 기기 3\~5종**이면 충분해요.

* 화면 비율이 서로 다른 기기 2\~3종
* Safe Area가 크게 적용되는 기기 1종
* 비교적 작은 화면 크기의 기기 1종

***

**권장하지 않는 방식**

다음과 같은 방식은 피해주세요.

* 디바이스 모델별로 다른 논리 해상도를 사용하는 방식
* 실제 픽셀 해상도를 기준으로 UI나 좌표를 계산하는 방식
* 필요 이상으로 많은 에셋 해상도 그룹을 운영하는 방식
* 해상도마다 다른 UI 레이아웃을 유지하는 방식

이런 접근은 UI 깨짐, 레터박스 발생, 유지보수 비용 증가로 이어질 수 있어요.


# 토스 디자인 시스템 (TDS)

TDS는 Toss Design System의 약자예요. 토스 커뮤니티 전반에서 쓰는 디자인 시스템으로, 토스 제품을 구성하는 공통의 디자인 언어이자 개발자의 도구예요. TDS는 디자이너, 개발자, 기획자 등 직군을 가리지 않고 같은 기준으로 협업할 수 있게 도와줘요.

### TDS 장점

* 사용자에게 일관된 제품 경험을 줄 수 있어요.
* 디자이너는 UI 구현보다 문제 해결에 집중할 수 있어요.
* 개발자는 커스텀 UI를 직접 만들 때보다 3\~5배 빠르게 개발할 수 있어요.

![](https://static.toss.im/3d-common/tds-kv-text-hero.png)

***

### TDS 사용 시 유의사항

TDS를 안전하고 올바르게 쓰려면 아래 내용을 먼저 확인해 주세요.

#### 라이선스 안내

{% hint style="info" %}
이 UI Kit을 사용하면, 여기에 포함된 [라이선스 조건](https://developers-apps-in-toss.toss.im/design/prepare/figma-ui-license)에 동의하는 것으로 봐요.
{% endhint %}

1. **지식재산권:** 토스가 앱인토스(Appintoss) 서비스로 제공하는 모든 자료의 권리는 지식재산권을 포함해 모두 토스에 있어요. 파트너사는 앱인토스 서비스를 이용하는 범위 안에서만 이 자료를 쓸 수 있어요.
2. **사용 권한 범위:** 토스가 TDS 사용을 허가하는 건 앱인토스 서비스를 제공하기 위한 제한적인 권한이에요. 이 범위를 넘는 결과물이나 추가 권리를 얻을 수는 없어요.
3. **준수 의무 및 위반 시 조치:** 파트너사는 이 가이드와 관련 법령을 반드시 지켜 주세요. 이를 어기면 토스는 앱인토스 서비스 제공을 중단하거나 그 밖에 필요한 조치를 할 수 있어요.

***

### 컴포넌트 리스트

앱인토스에서 가장 자주 쓰는 핵심 TDS 컴포넌트 11개를 소개해요. 각 컴포넌트를 눌러 사용 방법과 활용 예시를 자세히 확인해 보세요.

<table data-view="cards"><thead><tr><th align="center"></th><th data-hidden data-card-cover data-type="image">Cover image</th></tr></thead><tbody><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/badge/">Badge</a></td><td><a href="/files/UozoUFxmNjac2Y4B5CLX">/files/UozoUFxmNjac2Y4B5CLX</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/border/">Border</a></td><td><a href="/files/BnThGwJucm7A7PBIDgLI">/files/BnThGwJucm7A7PBIDgLI</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/BottomCTA/check-first/">BottomCTA</a></td><td><a href="/files/i3T42pY1h5FT9vWlZ5QB">/files/i3T42pY1h5FT9vWlZ5QB</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/button/">Button</a></td><td><a href="/files/wC8S4WcRh0YmePsbbsZq">/files/wC8S4WcRh0YmePsbbsZq</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/Asset/check-first/">Asset</a></td><td><a href="/files/sMOvfFBhfq51vAckU59H">/files/sMOvfFBhfq51vAckU59H</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/ListRow/list-row-overview/">ListRow</a></td><td><a href="/files/unQN3YglirFNeZrsIcuf">/files/unQN3YglirFNeZrsIcuf</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/list-header/">ListHeader</a></td><td><a href="/files/zBPYoCXhHhk2ZHUAuL6v">/files/zBPYoCXhHhk2ZHUAuL6v</a></td></tr><tr><td align="center">Navigation</td><td><a href="/files/YQ6vO2sU1cW9IX25pons">/files/YQ6vO2sU1cW9IX25pons</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/paragraph/">Paragraph</a></td><td><a href="/files/4Laz9dX6WwnE61hsd9eE">/files/4Laz9dX6WwnE61hsd9eE</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/tab/">Tab</a></td><td><a href="/files/xquQZHhXyLUoefzsU2w3">/files/xquQZHhXyLUoefzsU2w3</a></td></tr><tr><td align="center"><a href="https://tossmini-docs.toss.im/tds-mobile/components/top/">Top</a></td><td><a href="/files/GN3mKoKVYwZqRtpgBXKB">/files/GN3mKoKVYwZqRtpgBXKB</a></td></tr></tbody></table>


# 개발

## development


# 테스트


# 테스트앱(샌드박스)

## 테스트앱(샌드박스)

앱인토스는 개발용 토스앱을 별도로 제공하지 않아요. 대신 **전용 샌드박스 앱**을 통해 개발·테스트 환경을 구성할 수 있어요.

{% hint style="info" %}
**반드시 확인해주세요**

실서비스 출시 전, 샌드박스 앱에서 기능 검증을 완료해야 해요. 샌드박스 앱에서 테스트를 완료했더라도, 출시 검수에서 가이드에 위반한 사항이 있다면 반려될 수 있어요.

현재 3.x 버전은 샌드박스 앱이 제공되지 않아요. 대신 함께 설치되는 devtools를 통해 브라우저로 개발이 가능해요.
{% endhint %}

***

### 샌드박스 앱이란?

앱인토스는 토스앱 안에서 파트너사의 서비스를 **앱인앱(App-in-App)** 형태로 제공해요. 별도의 개발용 토스앱 대신, **개발·QA 전용 샌드박스 앱**을 통해 연동 테스트를 진행할 수 있어요.

샌드박스 앱을 설치한 뒤 아래 순서로 개발을 시작하세요.

1. 환경 설정
2. 샌드박스 앱 설치
3. 로그인 → 앱 선택 → 스킴(URL) 접속

#### 지원 OS 버전

| 구분      | 최소 버전     |
| ------- | --------- |
| Android | Android 7 |
| iOS     | iOS 16    |

{% hint style="info" %}
**App Transport Security (ATS)**

App Transport Security(ATS) 정책 위반을 방지하기 위해 **샌드박스 앱에서는 http 통신이 허용**돼요. 단, 라이브 환경에서는 **https만 지원**되므로, http 기반 기능은 샌드박스에서만 정상 동작해요.
{% endhint %}

***

### 1. 환경 설정하기

#### iOS 환경 설정

iOS 시뮬레이터에서 테스트하려면 **Xcode**가 필요해요.

{% hint style="info" %}
**iOS의 서드파티 쿠키 차단 정책**

iOS/iPadOS 13.4 이상에서는 **서드파티 쿠키가 완전히 차단**돼요. 앱인토스 도메인이 아닌 파트너사 도메인에서 쿠키 기반 로그인을 구현하면 정상 동작하지 않아요. **토큰 기반 등 대체 인증 방식**을 적용해 주세요.
{% endhint %}

**1-1. Xcode 설치하기**

[Xcode 최신버전 다운로드](https://apps.apple.com/kr/app/xcode/id497799835?mt=12)를 클릭해서 Mac App Store에서 설치해 주세요.

**1-2. iOS 컴포넌트 설치하기**

Xcode를 처음 설치한 경우, iOS 15 이상의 컴포넌트를 추가로 설치해야 해요. 아래와 같은 창이 표시되면 iOS를 선택해 설치해 주세요.

**1-3. Xcode Command Line Tools 설치하기**

Xcode Command Line Tools는 **Xcode 본체와 버전이 동일해야** 해요.

**Xcode 버전 확인하기**

1. Xcode를 열고 상단 메뉴에서 \[Xcode] > \[About Xcode]를 클릭하세요.
2. 화면에 표시된 버전을 확인하세요.

**Xcode Command Line Tools 버전 확인하기**

1. Xcode에서 \[Xcode] > \[Settings]를 클릭하세요.
2. \[Locations] 탭에서 Command Line Tools 항목의 버전을 확인하세요.

**1-4. 시뮬레이터 실행하기**

1. Xcode 상단 메뉴에서 \[Xcode] > \[Open Developer Tool] > \[Simulator]를 선택하세요.
2. iOS 15 이상의 버전을 사용할 수 있는지 확인하세요.

<details>

<summary>시뮬레이터가 보이지 않는다면</summary>

1. Simulator 앱을 열어요.
2. 상단 메뉴에서 \[File] > \[Open Simulator]를 클릭하세요.
3. iOS 15 이상 버전에서 원하는 기기를 선택하세요.

</details>

***

#### Android 환경 설정

React Native를 Android 환경에서 실행하려면 **Android SDK**와 [`adb`(Android Debug Bridge)](https://developer.android.com/tools/adb?hl=ko)가 필요해요.

**1-1. Android Studio 설치하기**

[Android Studio 설치 링크](https://developer.android.com/studio?hl=ko)에서 설치해 주세요.

**1-2. Android SDK Command-line Tools 설치하기**

1. Android Studio에서 상단 메뉴 \[Android Studio] > \[Settings]를 클릭하세요.
2. \[Languages & Frameworks] > \[Android SDK]를 선택하세요.
3. \[SDK Tools] 탭에서 "Android SDK Command-line Tools"를 체크하고 OK를 눌러 설치하세요.

**1-3. 환경 변수 설정하기**

`adb`를 사용하려면 환경 변수를 설정해야 해요.

{% tabs %}
{% tab title="macOS" %}

```bash
# .zshrc 또는 .bashrc에 추가하세요.
export ANDROID_HOME=~/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools
```

{% endtab %}
{% endtabs %}

<details>

<summary>Windows 환경 변수 설정하기</summary>

**1. 실행 프롬프트 열기**

`Windows` + `R` 키를 눌러 실행 창을 열고 `SystemPropertiesAdvanced`를 입력한 뒤 Enter를 눌러요.

**2. 환경 변수 메뉴로 진입하기**

\[시스템 속성] 창에서 \[고급] 탭을 선택하고, 하단의 \[환경 변수] 버튼을 눌러요.

**3. 사용자 변수에서 Path 편집하기**

사용자 변수 섹션에서 `Path` 변수를 선택한 뒤 \[편집] 버튼을 눌러요. `Path` 변수가 없다면 \[새로 만들기] 버튼을 눌러 이름을 `Path`로 설정하세요.

**4. Android SDK 경로 추가하기**

편집 창에서 \[새로 만들기] 버튼을 눌러 다음 경로를 추가하세요. `{사용자명}`은 현재 Windows 사용자 계정 이름으로 바꿔 입력하세요.

`C:\Users\{사용자명}\AppData\Local\Android\sdk\platform-tools`

</details>

환경 변수가 정상적으로 등록되었는지 아래 명령어로 확인하세요.

```sh
adb version
# Android Debug Bridge version 1.0.41
```

**1-4. 기기 연결하기**

**개발자 옵션 활성화**

{% hint style="info" %}
기기 제조사에 따라 개발자 옵션을 활성화하는 방법이 다를 수 있어요. 사용 중인 기기의 제조사별 가이드는 인터넷 검색으로 확인하세요.
{% endhint %}

갤럭시 기기 기준으로 아래와 같이 활성화해요.

1. \[설정] 앱 열기
2. \[휴대전화 정보] > \[소프트웨어 정보] 메뉴로 이동
3. \[빌드 번호] 항목을 빠르게 여러 번 탭하기

**USB 디버깅 활성화**

1. \[설정] > \[개발자 옵션] 메뉴로 이동해요.
2. \[USB 디버깅] 항목을 활성화해요.

**PC와 기기 연결하기**

USB 케이블로 PC와 기기를 연결한 뒤, 아래 명령어로 연결 상태를 확인해요.

```sh
adb devices
# List of devices attached
# R3CTA0BMCPK  device
```

"List of devices attached" 아래에 디바이스 아이디가 표시되면 연결이 성공한 거예요.

<details>

<summary>디바이스 아이디가 표시되지 않는다면</summary>

* **USB 디버깅 활성화 확인**: \[설정] > \[개발자 옵션] > \[USB 디버깅]이 켜져 있는지 확인해요.
* **ADB 서버 재시작**: `adb kill-server` 실행 후 `adb devices`로 다시 확인해요.

</details>

**1-5. 에뮬레이터 설정하기**

> ⚠️ 디버깅과 QA는 가능한 **실제 기기**에서 진행하는 것을 권장해요.

Android Studio를 실행한 후 오른쪽 메뉴에서 \[Virtual Device Manager] > \[+ 버튼]을 눌러 에뮬레이터를 추가해요.

{% hint style="info" %}
**갤럭시 S23 사양 참고**

* 디스플레이: 6.1인치
* 운영체제: API 33부터 지원
  {% endhint %}

\[Pixel 8a] > \[VanillaIceCream (API 35)] > \[AVD Name 설정] 순으로 진행하면 에뮬레이터 설정이 완료돼요.

추가된 에뮬레이터는 \[Virtual Device Manager]에서 재생 버튼을 눌러 실행할 수 있어요.

***

### 2. 샌드박스 앱 설치하기

샌드박스 앱은 수시로 업데이트돼요. 오류가 보이면 **최신 버전으로 업데이트**해 주세요.

| 구분                                                               | 빌드번호       | 다운로드                                                                            |
| ---------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------- |
| Android                                                          | 2026-05-21 | [다운로드](https://static.toss.im/appsintoss/rn-miniapp-real-release-protected.zip) |
| iOS (시뮬레이터)                                                      | 2026-06-02 | [다운로드](https://static.toss.im/appsintoss/apps-in-toss-sandbox-202606022149.zip) |
| iOS (실기기)                                                        | 2026-03-11 |                                                                                 |
| QR 코드 링크: <https://apps.apple.com/kr/app/앱인토스> 샌드박스/id6745618667 |            |                                                                                 |
|                                                                  |            |                                                                                 |

#### iOS에 설치하기

**시뮬레이터**

다운로드한 샌드박스 앱 파일을 **시뮬레이터 화면으로 드래그 앤드 드롭**하세요. 설치가 완료되면 앱이 시뮬레이터 홈 화면에 표시돼요. 설치 완료까지 잠시 기다려 주세요.

**실기기**

위 표의 QR 코드로 앱스토어에서 설치하세요.

#### Android에 설치하기

실제 기기와 에뮬레이터 모두 동일한 APK 파일을 사용해요.

**Android Studio로 설치하기**

1. Android Studio 오른쪽 메뉴 \[Device Manager]에 연결된 기기가 표시되는지 확인해요.
2. \[Start Mirroring] 버튼을 클릭해 기기 화면을 Android Studio에 표시해요.
3. 다운로드한 APK 파일을 기기 화면으로 드래그해서 설치해요.

**adb 명령어로 설치하기**

```sh
# APK 파일이 있는 폴더로 이동 후 실행
adb install -r -t {파일이름}

# 예시
adb install -r -t apssintoss-debug.apk
```

***

### 3. 샌드박스 앱 사용하기

#### 1. 개발자 로그인

콘솔에서 사용하는 토스 비즈니스 계정으로 로그인하세요. 토스 비즈니스 가입이 필요하다면 [콘솔에서 앱 등록하기](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/console-workspace)를 확인해 주세요.

{% hint style="info" %}
**개인 계정을 사용해 주세요**

토스 비즈니스에 가입한 **개인 계정으로 로그인해 주세요.** 공용 계정을 사용할 경우 로그인 실패 또는 세션이 자주 종료될 수 있어요. 개인 계정 사용이 어려운 경우, 채널톡으로 문의해 주세요.
{% endhint %}

#### 2. 앱 선택

소속된 워크스페이스의 앱 목록이 노출돼요. **테스트할 앱**을 선택하세요.

#### 3. 토스 인증

콘솔에 등록한 **토스 계정**으로 본인 인증을 진행해요. 해당 계정의 **토스앱이 설치된 스마트폰**에서 푸시를 열어 인증을 완료해 주세요.

#### 4. 스킴(URL)으로 접속

접속할 스킴을 입력하면 미니앱이 실행돼요.

```
intoss://{appName}
```

***

### 4. 미니앱 실행하기

#### iOS 시뮬레이터에서 실행하기

1. 샌드박스 앱을 실행해요.
2. 스킴을 입력하고 "스키마 열기" 버튼을 눌러요. 예: `intoss://kingtoss`

\[동영상 보기]\(../../resources/development/local-server/local-develop-ios-sim-example.mp4)

#### iOS 실기기에서 실행하기

로컬 서버와 같은 와이파이에 연결되어 있어야 해요.

1. 샌드박스 앱 실행 시 **"로컬 네트워크"** 권한 요청이 표시되면 **"허용"** 버튼을 눌러요.
2. 서버 주소 입력 화면에서 로컬 서버 IP 주소를 입력하고 저장해요.
   * macOS에서는 `ipconfig getifaddr en0` 명령어로 IP 주소를 확인할 수 있어요.
3. "스키마 열기" 버튼을 눌러요.
4. 화면 상단에 `Bundling {n}%...`가 표시되면 연결 성공이에요.

<details>

<summary>"로컬 네트워크" 권한을 수동으로 허용하는 방법</summary>

1. 아이폰 \[설정] 앱에서 **"앱인토스"** 를 검색해 이동해요.
2. **"로컬 네트워크"** 옵션을 켜주세요.

</details>

#### Android 에뮬레이터 또는 실기기에서 실행하기

1. USB 케이블로 PC와 기기를 연결해요.
2. `adb` 명령어로 포트를 연결해요.

   ```sh
   adb reverse tcp:8081 tcp:8081
   adb reverse tcp:5173 tcp:5173
   ```

   특정 기기를 연결하려면 `-s` 옵션을 추가해요.

   ```sh
   adb -s {디바이스아이디} reverse tcp:8081 tcp:8081
   adb -s {디바이스아이디} reverse tcp:5173 tcp:5173
   ```
3. 샌드박스 앱에서 스킴을 입력하고 실행 버튼을 눌러요. 예: `intoss://kingtoss`

\[동영상 보기]\(../../resources/development/local-server/local-develop-android-example.mp4)

<details>

<summary>자주 쓰는 adb 명령어</summary>

```sh
# 연결 끊기
adb kill-server

# 포트 연결하기
adb reverse tcp:8081 tcp:8081
adb reverse tcp:5173 tcp:5173

# 연결 상태 확인하기
adb reverse --list
```

</details>

***

### 테스트 가능한 기능

샌드박스에서 지원하지 않는 기능은 콘솔 '출시하기'의 QR 코드로 [토스앱](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/toss)에서 테스트해 주세요.

| 기능            | 테스트 가능 여부              |
| ------------- | ---------------------- |
| 토스 로그인        | ✅ 가능                   |
| 사용자 식별키 발급    | ✅ 가능 (단, mock 데이터 내려감) |
| 토스페이          | ✅ 가능                   |
| 인앱 결제         | ✅ 가능                   |
| 게임 프로필 & 리더보드 | ✅ 가능                   |
| 분석            | ❌ 불가능                  |
| 공유 리워드        | ❌ 불가능                  |
| 인앱 광고         | ❌ 불가능                  |
| 가로 버전 게임      | ❌ 불가능                  |
| 내비게이션 바 공유하기  | ❌ 불가능                  |

***

### 자주 묻는 질문

<details>

<summary>샌드박스에서 테스트 진행이 잘 안돼요.</summary>

샌드박스 **개발자 로그인**을 진행해 주세요.

로그인이 풀리면 샌드박스 테스트가 원활히 작동하지 않아요.

</details>

<details>

<summary>토스 로그인 테스트를 진행하는데 잘 안돼요.</summary>

샌드박스 **개발자 로그인**을 먼저 진행해 주세요.

로그인이 선행되지 않으면 토스 로그인 테스트가 원활히 작동하지 않을 수 있어요.

</details>

<details>

<summary>토스 로그인 약관 화면이 뜨지 않아요.</summary>

샌드박스 개발자 로그인을 하지 않으면 **토스 로그인 약관 화면이 노출되지 않아요.**

콘솔 내 QR코드를 통해 테스트를 진행해 주세요.

</details>

<details>

<summary>샌드박스앱이 안돼요.</summary>

샌드박스앱은 수시로 업데이트돼요. 오류가 보이면 **최신 버전으로 업데이트해 주세요.**

</details>

***

### 트러블슈팅

<details>

<summary>`서버에 연결할 수 없습니다` 에러가 발생해요 (Android)</summary>

\`granite.config.ts\`의 \`web.commands\`에 \`--host\`를 추가한 뒤 서비스를 실행해 호스트 주소를 확인하세요.

```ts
web: {
  commands: {
    dev: 'vite --host', // --host 추가
    build: 'tsc -b && vite build',
  },
},
```

호스트 주소 확인 후 `web.host`에 입력하세요.

```ts
web: {
  host: 'x.x.x.x', // 서비스가 실행되는 호스트 주소
},
```

</details>

<details>

<summary>Metro 개발 서버가 열려 있는데 `잠시 문제가 생겼어요` 메시지가 표시돼요</summary>

개발 서버에 제대로 연결되지 않은 문제일 수 있어요. \`adb\` 연결을 끊고 8081, 5173 포트를 다시 연결해 보세요.

</details>

<details>

<summary>PC 웹에서 Not Found 오류가 발생해요</summary>

8081 포트는 샌드박스 내에서 인식하기 위한 포트예요. PC 웹에서는 Not Found 오류가 발생해요.

</details>


# 미니앱 만들기

"이런 앱이 있었으면 좋겠다"는 아이디어만 있어도 충분해요. 코드를 몰라도 AI에게 원하는 내용을 말로 설명하면, AI가 대신 코드를 작성해 줘요.

이 문서는 Claude, Codex 같은 AI 도구와 대화하면서 아이디어를 실제로 동작하는 앱인토스 미니앱으로 만드는 전체 과정을, 처음부터 끝까지 순서대로 안내해요.

{% hint style="info" %}
**이런 분께 도움이 돼요**

* 코딩을 전혀 해본 적 없는 분
* 앱 아이디어는 있지만 어떻게 시작해야 할지 모르는 분
* AI의 도움을 받아 빠르게 미니앱을 만들어 보고 싶은 분
  {% endhint %}

이미 웹 프로젝트가 있다면 [기존 Web 프로젝트 개발 가이드](/ai-vibe-coding/tutorials/webview)를, React Native로 개발하고 싶다면 [React Native 개발 가이드](/ai-vibe-coding/tutorials/react-native)를 참고해 주세요.

***

### 1. 준비하기

앱을 대신 만들어 줄 AI 도구를 먼저 설치하고, 개발에 필요한 프로그램까지 준비해요.

#### 1-1. AI 도구 설치하기

Claude나 Codex 중 더 익숙한 도구를 골라 설치해 주세요.

{% tabs %}
{% tab title="Claude" %}
[Claude 다운로드 페이지](https://claude.com/ko/download)에서 사용 중인 운영체제(macOS·Windows)에 맞는 설치 파일을 내려받아 설치해 주세요.

설치가 끝나면 Claude 앱을 실행하고 로그인해 주세요. 왼쪽 위의 'Code'를 눌러 주세요.

<figure><img src="/files/gYL6H6r4cgRafRzTybSW" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="Codex" %}
[Codex 앱 소개 페이지](https://openai.com/ko-KR/index/introducing-the-codex-app/)에서 Codex 앱을 내려받아 설치해 주세요.

설치가 끝나면 Codex 앱을 실행하고 로그인해 주세요.

<figure><img src="/files/jm1xTxvoq0B60YnCOiKS" alt=""><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

#### 1-2. Node.js 설치하기

앱을 만들려면 Node.js라는 프로그램이 필요해요. 이게 있어야 이후에 쓸 개발 도구들이 정상적으로 동작해요.

Claude나 Codex 채팅창에 아래처럼 요청해 보세요. AI가 설치 여부를 확인하고, 필요하면 알아서 설치해 줘요. AI가 응답으로 버전 정보(예: v22.12.0)를 알려주면 정상적으로 설치된 거예요.

```
Node.js가 설치되어 있는지 확인하고, 없으면 설치해줘.
설치가 끝나면 버전 정보도 알려줘.
```

<details>

<summary>Node.js 설치하는 모습을 영상으로 확인해요</summary>

{% embed url="<https://www.loom.com/share/8400b4fa2ef64a95950cea7de83582dc>" %}

</details>

***

### 2. 앱인토스 기능 연결하기

AI가 앱인토스 기능을 정확하게 쓸 수 있도록 MCP를 연결하는 단계예요.

MCP는 AI에게 앱인토스에 대한 정보를 미리 알려주는 연결 고리예요. 연결해 두면 AI가 앱인토스 규칙과 사용법을 정확히 알고 시작해요. 연결하지 않으면 AI가 잘 모른 채로 짐작해서 엉뚱하게 만들 수도 있어요.

#### 2-1. 앱인토스 MCP 연결하기

앱인토스 MCP를 연결하면 AI가 이 문서에서 안내하는 방법대로 앱을 만들어요. 진행하다가 궁금한 점이 생기면 AI에게 그냥 물어봐도, 앱인토스 문서를 찾아서 답해 줘요.

아래처럼 요청해 보세요. 이후 필요한 프로그램 설치와 연결 과정은 AI가 전부 알아서 처리해 줘요. 중간에 설치를 진행해도 될지 물어보면 확인하고 승인해 주세요.

```
ax CLI를 설치하고 앱인토스 개발 MCP를 연결해줘.
문서 검색을 위해 아래 MCP도 추가해줘.
https://developers-apps-in-toss.toss.im/~gitbook/mcp
```

아래처럼 요청해서 연결이 잘 됐는지 확인해 보세요. AI가 `apps-in-toss`, `apps-in-toss-docs`가 목록에 있다고 알려주면 정상적으로 연결된 거예요.

```
연결된 MCP 목록을 보여줘
```

<details>

<summary>앱인토스 MCP 연결하는 모습을 영상으로 확인해요</summary>

{% embed url="<https://www.loom.com/share/3ab73c3b444e45bab63896ffc04c726d>" %}

</details>

#### 2-2. 콘솔 작업용 MCP 연결하기

콘솔은 앱을 등록하고 관리하는 앱인토스 웹사이트예요. 이 MCP를 연결하면 콘솔 사이트에 직접 들어가지 않고도, AI에게 요청하는 것만으로 앱 등록이나 관리 작업을 진행할 수 있어요.

아래처럼 요청해 보세요.

```
콘솔 작업을 위한 MCP를 추가해줘.
URL: https://mcp.toss.im/adapters/apps-in-toss-console/mcp
Client ID: mcp-gateway

연결한 다음에는 인증까지 진행해줘.
```

MCP 연결 후 로그인 창이 뜨면 안내에 따라 인증을 완료해 주세요. 완료되면 아래처럼 확인해 보세요. AI가 정상적으로 연결되고 인증까지 끝났다고 알려주면 완료된 거예요.

```
콘솔 MCP가 잘 연결됐는지 확인해줘
```

<details>

<summary>앱인토스 콘솔 MCP 연결하는 모습을 영상으로 확인해요</summary>

{% embed url="<https://www.loom.com/share/d6e9b00322be4be585e98de3964fb229>" %}

</details>

***

### 3. 개발하기

#### 3-1. 기획하기

만들고 싶은 미니앱 아이디어를 AI에게 설명해 보세요. 아래처럼 요청하면 AI가 앱인토스 정책에 맞는지 확인해 주고, 필요한 화면과 기능을 정리해서 제안해 줘요. 제안이 마음에 들면 그대로 진행하고, 원하는 방향과 다르면 다시 요청해서 다듬어도 돼요.

```
[만들고 싶은 앱 설명]을 만들고 싶어.
1. 앱인토스 오픈 정책에 맞는 아이디어인지 확인해줘
2. 어떤 화면과 기능이 필요할지 정리해서 보여줘
3. 앞으로 개발하면서 앱인토스 가이드에 어긋나는 부분이 있으면 미리 알려줘
```

마음에 드는 디자인을 AI에게 알려주면 비슷하게 만들어 줘요. 아래 사이트에서 앱의 핵심 키워드로 검색한 뒤, 마음에 드는 이미지나 링크를 복사해서 AI에게 알려주세요.

* 참고하면 좋은 사이트: [Dribbble](https://dribbble.com/shots/popular/mobile), [Mobbin](https://mobbin.com/discover/apps/ios/latest)

#### 3-2. 미니앱 만들기

앱인토스에 미니앱을 등록하고, 코드가 담길 공간까지 함께 만들어져요. 아래처럼 요청해 보세요. `{미니앱이름}` 자리에는 원하는 이름을 자유롭게 지어서 넣어 주세요.

```
{미니앱이름} 이름으로 미니앱을 만들어줘.
```

#### 3-3. 원하는 기능 요청하기

이제 AI에게 원하는 기능을 하나씩 요청할 차례예요. 어떤 기능이 필요한지 말만 하면 AI가 알아서 만들어 줘요. 앱인토스에서 제공하는 [기능 문서](/documentation)를 참고해도 좋아요.

```
미니앱 접속하면 유저 식별키를 발급해서 유저별로 데이터를 저장해줘.
유저 식별키 기준으로 닉네임을 임의로 생성해줘.
메인 화면 하단에 배너 광고를 연동해줘.
스테이지가 끝난 후 포인트 지급 프로모션을 추가해줘.
```

{% hint style="info" %}
**AI에게 잘 요청하는 방법**

* 최대한 구체적으로 말해요
  * "버튼 만들어줘" 보다 "화면 중앙에 시작하기 버튼을 만들고, 누르면 보상형 광고가 노출되게 연동해줘"처럼 말하면 더 정확해요.
* 한 번에 완벽하지 않아도 괜찮아요
  * 마음에 안 드는 부분이 있으면 "이 부분은 이렇게 바꿔줘" 처럼 편하게 다시 요청하면 돼요. AI가 바로 수정해 줘요.
    {% endhint %}

<details>

<summary>미니앱을 만드는 모습을 영상으로 확인해요</summary>

{% embed url="<https://www.loom.com/share/ede457a113e44b0cabd2264d35590c78>" %}

</details>

#### 3-4. 외부 저장소 서비스 연동하기

미니앱 자체에는 데이터를 계속 저장하는 기능이 없어요. 예를 들어 사용자가 남긴 게시글, 찜한 상품 목록, 저장한 닉네임처럼 다음에 다시 봐야 하는 데이터가 있다면, 외부 저장소 서비스를 연결하면 돼요. 모든 미니앱에 필요한 건 아니에요. 저장할 데이터가 없다면 이 단계는 건너뛰어도 괜찮아요.

대표적으로 아래 서비스를 많이 써요.

* [Supabase](https://supabase.com/): 오픈소스 기반이고, 데이터베이스와 로그인 기능을 함께 제공해요.
* [Firebase](https://firebase.google.com/?hl=ko): Google이 만든 서비스로, 실시간 업데이트와 푸시 알림 같은 부가 기능이 강점이에요.
* [Cloudflare](https://www.cloudflare.com/ko-kr/): 응답 속도가 빠르고, 가벼운 데이터를 저장할 때 적합해요.

원하는 서비스에 가입하고 프로젝트를 만든 뒤, 발급받은 연결 정보(URL, API 키 등)를 AI에게 알려주면 나머지는 AI가 알아서 연결해 줘요.

```
{서비스명}을 연결해줘.
{복사한 연결 정보}

그리고 [저장하고 싶은 데이터, 예: 사용자 이름]을 저장하고 불러오는 기능을 만들어줘.
```

<details>

<summary>Supabase에서 연결 정보 찾는 방법</summary>

1. Supabase 대시보드에서 프로젝트를 선택해요.
2. 왼쪽 메뉴의 Settings 안에서 API 관련 항목을 선택해요.
3. Project URL과 Publishable key 값을 확인할 수 있어요.
4. 두 값을 복사해서 AI에게 알려주면 돼요.

</details>

{% hint style="warning" %}
**보안 설정을 확인해 주세요**

외부 저장소는 기본적으로 아무나 데이터에 접근할 수 있는 상태인 경우가 많아요.&#x20;

AI에게 "보안 설정도 확인해줘"라고 요청하면 안전하게 설정하는 방법을 안내해 줘요.&#x20;

서비스 대시보드에서 직접 켜야 하는 부분도 있으니, AI가 안내하는 대로 따라가 주세요.
{% endhint %}

***

### 4. 테스트하고 출시하기

미니앱은 다 만든 뒤에만 테스트할 수 있는 게 아니에요. 개발하는 중간중간 화면이 궁금할 때마다 언제든 틈틈이 테스트해 봐도 괜찮아요.

#### 4-1. 토스앱으로 테스트하기

미니앱을 토스 앱에서 테스트할 수 있어요. 아래처럼 요청해 보세요. AI가 요청을 처리하면 토스 앱으로 푸시 알림이 와요. 알림을 누르면 실제 토스 앱에서 미니앱이 바로 실행돼요.

```
미니앱을 테스트해보고 싶어. 테스트할 수 있게 푸시를 보내줘.
```

<details>

<summary>테스트 푸시를 받을 수 있는 조건</summary>

* 토스 앱에 로그인되어 있어야 해요.
* 워크스페이스 멤버여야 해요.
* 만 19세 이상이어야 해요.

</details>

에러나 이상한 화면이 보이면 캡처해서 AI에게 보여주세요. AI는 실제 기기 화면을 볼 수 없어서, 캡처와 함께 설명하면 더 정확하게 고쳐줘요. (예: 캡처 화면과 함께 "결제 버튼을 눌렀는데 아무 반응이 없어")

원하는 대로 안 되면 "이 부분은 이렇게 바꿔줘"처럼 편하게 다시 요청하세요. 수정한 뒤 다시 테스트하면 돼요. (예: "메인 화면 배너 광고를 조금 더 아래로 내려줘")

에러를 미리 예방하고 싶다면 [Sentry 연동하기](/ai-vibe-coding/integration/sentry)를 요청해 보세요. 에러가 났을 때 AI가 로그를 직접 확인할 수 있어서, 원인을 더 빠르고 정확하게 찾아줘요.

<details>

<summary>토스앱에서 테스트하는 모습을 영상으로 확인해요</summary>

{% embed url="<https://www.loom.com/share/c070cf90638248b88d1e94dbae41bae8>" %}

</details>

#### 4-2. 출시하기

미니앱을 출시하려면 먼저 앱인토스팀의 검수를 통과해야 해요. 검수는 미니앱이 정책과 가이드를 잘 지켰는지 확인하는 절차예요. 테스트가 끝나면 검수를 요청해 보세요.

```
미니앱을 출시하고 싶어. 검수를 요청해줘.
```

검수 결과는 콘솔과 이메일로 안내해드려요. 검수가 승인되면 콘솔의 '출시하기' 버튼을 눌러야 미니앱이 사용자에게 공개돼요. 출시하면 1시간 후에 토스 미니앱 리스트에 바로 반영돼요.

앱을 만든 뒤에는 사용자에게 다가가기 위해 [마케팅 가이드](https://appsintoss.gitbook.io/appsintoss-docs/guide/marketing)도 함께 참고해 보세요.


# 튜토리얼


# 기존 웹 프로젝트에 SDK 연동하기

이미 운영 중인 웹 프로젝트에 SDK를 직접 설치해 미니앱으로 전환할 수 있어요.

***

### 1. SDK 설치 및 초기화하기

SDK를 설치한 뒤 환경을 초기화해 주세요.

{% tabs %}
{% tab title="npm" %}

```sh
npm install @apps-in-toss/web-framework
npx ait init
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm add @apps-in-toss/web-framework
pnpm ait init
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn add @apps-in-toss/web-framework
yarn ait init
```

{% endtab %}
{% endtabs %}

***

### 2. 설정 파일 수정하기

프로젝트를 생성하면 `granite.config.ts` 파일이 자동으로 만들어져요. `appName`, `displayName`, `icon`을 앱인토스 콘솔에 등록한 앱 정보와 동일하게 수정해 주세요.

```ts
import { defineConfig } from '@apps-in-toss/web-framework/config';

export default defineConfig({
  appName: 'my-mini-app', // 콘솔에 입력한 appName을 입력하세요.
  brand: {
    displayName: '앱 이름', // 콘솔에 입력한 앱 이름을 입력하세요.
    primaryColor: '#FF91D5', // 화면에 노출될 앱의 기본 색상으로 바꿔주세요.
    icon: '', // 콘솔에서 업로드한 이미지의 URL을 입력하세요.(콘솔의 앱 정보에서 업로드한 이미지를 우클릭해 링크 복사 후 넣어주세요)
  },
  web: {
    host: 'localhost',
    port: 5173,
    commands: {
      dev: 'vite dev',
      build: 'vite build',
    },
  },
  permissions: [],
  outdir: 'dist',
});
```

{% hint style="info" %}
**중요해요**

* `appName`은 각 앱을 식별하는 **고유한 키**로 사용돼요.
* `intoss://{appName}` 형태의 딥링크 경로나 테스트·배포 시에도 사용돼요.
* 샌드박스 앱에서 테스트할 때도 `intoss://{appName}`으로 접근해요. 단, 출시하기 메뉴의 QR 코드로 테스트할 때는 `intoss-private://{appName}`이 사용돼요.
  {% endhint %}

***

### 3. TDS 설치하기

**TDS(Toss Design System) WebView** 패키지를 사용하면 토스 디자인 시스템 기반의 컴포넌트를 쉽게 적용할 수 있어요.

{% tabs %}
{% tab title="npm" %}

```sh
npm install @toss/tds-mobile @toss/tds-mobile-ait @emotion/react@^11 react@^18 react-dom@^18
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm add @toss/tds-mobile @toss/tds-mobile-ait @emotion/react@^11 react@^18 react-dom@^18
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn add @toss/tds-mobile @toss/tds-mobile-ait @emotion/react@^11 react@^18 react-dom@^18
```

{% endtab %}
{% endtabs %}

TDS 컴포넌트 사용법과 가이드는 [TDS WebView 문서](https://tossmini-docs.toss.im/tds-mobile/)를 확인해 주세요.

***

### 4. 프로젝트 실행하기

{% tabs %}
{% tab title="npm" %}

```sh
npm run dev
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm run dev
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn dev
```

{% endtab %}
{% endtabs %}

***

### 5. 미니앱 실행하기

개발 서버가 실행되면 샌드박스 앱에서 미니앱을 확인할 수 있어요. 로컬 샌드박스 앱에서 테스트하는 자세한 방법은 [샌드박스앱](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/development/test/sandbox) 문서를 확인해 주세요.

#### 실기기에서 개발 서버 접근하기

실기기에서 테스트하려면 번들러 실행 시 `--host` 옵션을 활성화하고, `web.host`를 실기기에서 접근할 수 있는 네트워크 주소로 설정해야 해요.

```ts
import { defineConfig } from '@apps-in-toss/web-framework/config';

export default defineConfig({
  appName: 'ping-pong',
  web: {
    host: '192.168.0.100', // 실기기에서 접근할 수 있는 IP 주소로 변경
    port: 5173,
    commands: {
      dev: 'vite --host', // --host 옵션 활성화
      build: 'vite build',
    },
  },
  permissions: [],
});
```

설정이 완료되면 실기기에서 다음 순서로 진행해 주세요.

1. 샌드박스 앱 설치하기를 참고해 기기에 맞는 샌드박스 앱을 설치해요.
2. 샌드박스 앱에서 Metro 서버 주소를 `web.host`에 설정한 IP 주소로 변경해요.
3. `intoss://{appName}` 딥링크로 미니앱에 접근해요.

***

### 6. 디버깅하기

#### Android — Chrome DevTools

{% hint style="info" %}
**준비가 필요해요**

디바이스에서 디버깅하려면 USB 디버깅을 먼저 활성화해야 해요. `설정 → 시스템 → 휴대전화 정보 → 개발자 옵션 → USB 디버깅 활성화`
{% endhint %}

1. Android 에뮬레이터나 실기기에서 미니앱을 실행해요.
2. Chrome 브라우저에서 `chrome://inspect/#devices` 페이지를 열어요.
3. Remote Target에서 디버깅할 WebView 콘텐츠 아래 **inspect** 버튼을 선택해요.
4. 일반 웹 페이지를 디버깅하듯 WebView 콘텐츠를 디버깅할 수 있어요.

#### iOS — Safari 개발자 도구

{% hint style="info" %}
**준비가 필요해요**

* Safari 개발자 메뉴를 활성화해야 해요. `Safari 환경설정 → 고급 탭 → 웹 개발자를 위한 기능 보기 체크박스 활성화`
* 디바이스에서 디버깅하려면 Web Inspector(웹 검사기)를 활성화해야 해요. `설정 → Safari → 고급 → Web Inspector 활성화`
* 개발자용 메뉴에 디바이스가 표시되지 않으면 Safari를 재시작해 보세요.
  {% endhint %}

1. iOS 시뮬레이터 또는 실기기에서 미니앱을 실행해요.
2. Safari 상단 메뉴 `개발자용 → [디바이스 이름] → [앱 이름] → [URL - 제목]`을 선택해요.
3. 웹에서 디버깅하듯 WebView 콘텐츠를 디버깅할 수 있어요.

***

### 7. 빌드하고 토스앱에서 최종 테스트하기

샌드박스 앱에서 개발과 기본 검증이 끝나면 `npm run build`로 앱 번들을 생성한 뒤, 콘솔에 업로드해 최종 테스트를 진행해 주세요. 토스앱 테스트를 완료해야 출시 요청을 보낼 수 있어요. 자세한 방법은 [토스앱 테스트하기](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/toss) 문서를 참고해 주세요.

***

### 8. 출시하기

출시하는 방법은 [미니앱 출시](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/deploy) 문서를 참고해 주세요.


# React Native 시작하기

{% hint style="info" %}
**처음 시작한다면?**

앱인토스 개발이 처음이거나 AI와 함께 빠르게 미니앱을 만들어보고 싶다면, AI로 미니앱 만들기 문서를 먼저 읽어보세요.
{% endhint %}

React Native 기반의 Granite 프레임워크로 개발하는 방식이에요. 네이티브 수준의 UI/UX가 필요하거나, 토스앱과 자연스럽게 어우러지는 경험을 만들고 싶은 팀에 적합해요.

* 토스앱의 네이티브 UI와 일관된 경험을 제공하고 싶어요.
* 복잡한 애니메이션이나 제스처 처리가 필요해요.
* React Native 개발 경험이 있는 팀이에요.

{% hint style="info" %}
**알아두세요**

네이티브 모듈이 필요한 라이브러리는 앱인토스에서 지원하는 범위 내에서만 사용할 수 있어요.
{% endhint %}

WebView로 개발하고 싶다면 → WebView 시작하기

***

### 1. 프로젝트 만들기

앱을 만들 위치에서 다음 명령어를 실행하세요.

{% tabs %}
{% tab title="npm" %}

```sh
npm create granite-app@"^1"
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm create granite-app@"^1"
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn create granite-app@"^1"
```

{% endtab %}
{% endtabs %}

#### 1-1. 앱 이름 지정하기

앱 이름은 [kebab-case](https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case) 형식으로 입력해요.

```sh
my-granite-app
```

#### 1-2. 도구 선택하기

프로젝트 생성 시 코드 품질 도구를 선택할 수 있어요.

* `prettier` + `eslint`: 코드 포맷팅과 린팅을 각각 담당해요. 세밀한 설정과 다양한 플러그인으로 유연한 코드 품질 관리를 지원해요.
* `biome`: Rust 기반의 빠르고 통합적인 포맷팅·린팅 도구예요. 간단한 설정으로 효율적인 작업이 가능해요.

#### 1-3. 의존성 설치하기

프로젝트 폴더로 이동한 뒤 의존성을 설치하세요.

{% tabs %}
{% tab title="npm" %}

```sh
cd my-granite-app
npm install
```

{% endtab %}

{% tab title="pnpm" %}

```sh
cd my-granite-app
pnpm install
```

{% endtab %}

{% tab title="yarn" %}

```sh
cd my-granite-app
yarn install
```

{% endtab %}
{% endtabs %}

\[동영상 보기]\(../resources/tutorials/react-native/react-native-tutorial-scaffold.mp4)

***

### 2. 프레임워크 설치하기

앱인토스 SDK를 사용하려면 `@apps-in-toss/framework` 패키지를 설치해야 해요.

{% tabs %}
{% tab title="npm" %}

```sh
npm install @apps-in-toss/framework
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm add @apps-in-toss/framework
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn add @apps-in-toss/framework
```

{% endtab %}
{% endtabs %}

***

### 3. 설정 파일 수정하기

`ait init` 명령어로 앱 개발에 필요한 기본 환경을 구성할 수 있어요.

{% tabs %}
{% tab title="npm" %}

```sh
npx ait init
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm ait init
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn ait init
```

{% endtab %}
{% endtabs %}

1. 프레임워크를 선택하세요.
2. 앱 이름(`appName`)을 입력하세요. 앱인토스 콘솔에서 등록한 이름과 동일하게 입력해 주세요.

초기화가 완료되면 프로젝트 루트에 `granite.config.ts` 파일이 생성돼요. `appName`, `displayName`, `icon`을 앱인토스 콘솔에 등록한 앱 정보와 동일하게 수정해 주세요.

```ts
import { appsInToss } from '@apps-in-toss/framework/plugins';
import { defineConfig } from '@granite-js/react-native/config';

export default defineConfig({
  appName: '<app-name>', // 앱인토스 콘솔에서 등록한 앱 이름으로 바꿔주세요.
  plugins: [
    appsInToss({
      brand: {
        displayName: '앱 이름', // 화면에 노출될 앱의 한글 이름으로 바꿔주세요.
        primaryColor: '#3182F6', // 화면에 노출될 앱의 기본 색상으로 바꿔주세요.
        icon: null, // 콘솔에서 업로드한 이미지의 URL을 입력하세요.(콘솔의 앱 정보에서 업로드한 이미지를 우클릭해 링크 복사 후 넣어주세요)
      },
      permissions: [],
    }),
  ],
});
```

***

### 4. TDS 설치하기

**TDS(Toss Design System) React Native** 패키지를 사용하면 토스 디자인 시스템 기반의 컴포넌트를 쉽게 적용할 수 있어요.

{% tabs %}
{% tab title="npm" %}

```sh
npm install @toss/tds-react-native
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn add @toss/tds-react-native
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm add @toss/tds-react-native
```

{% endtab %}
{% endtabs %}

TDS 컴포넌트 사용법과 가이드는 [TDS React Native 문서](https://tossmini-docs.toss.im/tds-react-native/)를 확인해 주세요.

{% hint style="info" %}
**로컬에서는 TDS를 테스트할 수 없어요**

로컬 브라우저에서는 TDS가 동작하지 않아요. [샌드박스앱](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/development/test/sandbox)을 통해 테스트해 주세요.
{% endhint %}

***

### 5. 개발 서버 실행하기

{% tabs %}
{% tab title="npm" %}

```sh
npm run dev
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm dev
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn dev
```

{% endtab %}
{% endtabs %}

Metro 개발 서버가 실행되면 샌드박스 앱에서 미니앱을 확인할 수 있어요. 샌드박스 앱에서 테스트하는 자세한 방법은 [샌드박스앱](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/development/test/sandbox) 문서를 확인해 주세요.

{% hint style="info" %}
**too many open files 에러가 발생한다면**

node\_modules 디렉토리를 삭제한 뒤 다시 의존성을 설치해 보세요.

```sh
rm -rf node_modules
npm install  # 또는 yarn, pnpm에 맞게
```

{% endhint %}

***

### 6. 미니앱 실행하기

#### iOS 시뮬레이터에서 실행하기

1. 샌드박스 앱을 실행해요.
2. 스킴을 입력하고 "스키마 열기" 버튼을 눌러요. 예: `intoss://kingtoss`
3. 화면 상단에 `Bundling {n}%...`가 표시되면 연결이 성공한 거예요.

\[동영상 보기]\(../resources/development/local-server/rn-local-develop-ios-sim-example.mp4)

#### iOS 실기기에서 실행하기

아이폰에서 실행하려면 로컬 서버와 같은 와이파이에 연결되어 있어야 해요.

1. 샌드박스 앱을 실행하면 **"로컬 네트워크"** 권한 요청 메시지가 표시돼요. **"허용"** 버튼을 눌러주세요.
2. 서버 주소 입력 화면에서 로컬 서버 IP 주소를 입력하고 저장해요.
   * macOS에서는 `ipconfig getifaddr en0` 명령어로 IP 주소를 확인할 수 있어요.
3. "스키마 열기" 버튼을 눌러요.
4. 화면 상단에 `Bundling {n}%...`가 표시되면 연결이 성공한 거예요.

동영상 보기

<details>

<summary>"로컬 네트워크" 권한을 수동으로 허용하는 방법</summary>

1. 아이폰 \[설정] 앱에서 **"앱인토스"** 를 검색해 이동해요.
2. **"로컬 네트워크"** 옵션을 켜주세요.

</details>

#### Android 실기기 또는 에뮬레이터에서 실행하기

1. Android 실기기를 컴퓨터와 USB로 연결해요.
2. `adb` 명령어로 포트를 연결해요.

   ```sh
   adb reverse tcp:8081 tcp:8081
   adb reverse tcp:5173 tcp:5173
   ```

   특정 기기를 연결하려면 `-s` 옵션을 추가해요.

   ```sh
   adb -s {디바이스아이디} reverse tcp:8081 tcp:8081
   adb -s {디바이스아이디} reverse tcp:5173 tcp:5173
   ```
3. 샌드박스 앱에서 스킴을 입력하고 실행 버튼을 눌러요. 예: `intoss://kingtoss`
4. 화면 상단에 번들링 진행 상태가 표시되면 연결이 완료된 거예요.

\[동영상 보기]\(../resources/development/local-server/rn-local-develop-android-example.mp4)

<details>

<summary>자주 쓰는 adb 명령어</summary>

```sh
# 연결 끊기
adb kill-server

# 포트 연결하기
adb reverse tcp:8081 tcp:8081
adb reverse tcp:5173 tcp:5173

# 연결 상태 확인하기
adb reverse --list
```

</details>

***

### 7. 디버깅하기

#### 준비하기

React Native Debugger는 Chrome 브라우저가 필요해요. 설치되어 있지 않다면 [Chrome 웹브라우저](https://www.google.com/intl/ko_kr/chrome/)를 먼저 다운로드해 주세요.

#### Metro 개발 서버로 디버깅하기

개발 서버가 실행된 상태에서 터미널의 `j` 키를 누르면 React Native Debugger가 열려요. 기기와 Metro 서버가 연결된 상태에서만 열려요.

디버거는 아래 탭을 제공해요.

* **Console**: `console.log` 등으로 기록한 로그를 확인하고, REPL 환경에서 코드를 직접 실행할 수 있어요.
* **Source**: 실행 중인 코드를 보고 중단점을 추가할 수 있어요.
* **Network**: 네트워크 요청과 응답을 확인할 수 있어요.
* **Memory**: Hermes 엔진의 메모리 사용량을 프로파일링할 수 있어요.
* **Profiler**: 코드 실행 성능을 측정할 수 있어요.

**Breakpoints로 디버깅하기**

중단점을 설정하려면 `Cmd` + `P`로 파일 검색 창을 열고 파일을 선택해요. 원하는 줄을 클릭하면 중단점이 추가돼요. 코드가 해당 지점에 도달하면 실행이 멈추고 현재 상태를 확인할 수 있어요.

소스 코드에 `debugger` 키워드를 추가하면 해당 지점에서 자동으로 코드가 중단돼요.

**예외 상황 디버깅하기**

**Source 탭** 우측 상단 Breakpoints 섹션에서 아래 옵션을 활성화할 수 있어요.

* **Pause on uncaught exceptions**: 예기치 못한 예외 발생 시 자동으로 코드를 중단해요.
* **Pause on caught exceptions**: 핸들링 여부와 관계없이 모든 예외에서 중단해요.

{% hint style="info" %}
**유의하세요**

서비스가 완전히 중단된 후에는 예외 Breakpoints가 제대로 동작하지 않는 버그가 있어요. 개발 서버와 React Native Debugger를 재시작하면 해결할 수 있어요.
{% endhint %}

#### React DevTools로 디버깅하기

React DevTools를 사용하면 컴포넌트 구조를 시각적으로 탐색하고 디버깅할 수 있어요.

서비스가 실행 중이라면 개발 모드 RN 뷰를 `R` 키로 새로고침해 주세요. 아래와 같은 화면이 나타나면 연결이 완료된 거예요.

{% hint style="info" %}
**Android 기기를 사용한다면**

`adb reverse tcp:8097 tcp:8097` 명령어로 포트를 열어야 React DevTools가 정상적으로 동작해요.
{% endhint %}

**요소 인스펙팅**

요소 선택 버튼을 누른 뒤 기기에서 확인할 요소를 터치하면 React DevTools에서 해당 요소로 바로 이동해요.

\[동영상 보기]\(../resources/learn-more/debugging/inspecting.mp4)

**Prop 변경하기**

선택한 컴포넌트의 Prop을 확인하고 실시간으로 변경할 수 있어요. 원하는 Prop을 더블 클릭한 뒤 값을 입력하면 바로 반영돼요.

\[동영상 보기]\(../resources/learn-more/debugging/changing-prop.mp4)

#### 트러블슈팅

<details>

<summary>Metro 개발 서버가 열려 있는데 `잠시 문제가 생겼어요` 메시지가 표시돼요</summary>

개발 서버에 제대로 연결되지 않은 문제일 수 있어요. \`adb\` 연결을 끊고 8081, 5173 포트를 다시 연결해 보세요.

</details>

<details>

<summary>연결 가능한 기기가 없다고 떠요</summary>

React Native View가 나타나는 시점에 개발 서버와 기기가 연결돼요. 연결 가능한 기기가 없다면 개발 서버가 제대로 빌드되고 있는지 확인해 보세요.

</details>

<details>

<summary>REPL이 동작하지 않아요</summary>

React Native 버그로 REPL이 멈추는 현상이 발생할 수 있어요. 콘솔 탭 옆 눈 모양 아이콘을 클릭하고 입력 필드에 \`\_\_DEV\_\_\`, \`1\` 등 임의의 코드를 입력하고 평가해 보세요.

</details>

<details>

<summary>네트워크 인스펙터가 동작하지 않아요</summary>

네트워크 인스펙터는 다중 인스턴스를 지원하지 않아요. 소켓 커넥션이 꼬인 경우 아래 순서로 해결해 보세요.

1. 앱을 완전히 종료해요.
2. 개발 서버를 중단하고 네트워크 인스펙터를 닫아요.
3. 앱을 다시 시작하고 `dev` 스크립트를 실행해요.

이 절차로도 해결되지 않으면 담당자에게 제보해 주세요.

</details>

***

### 8. 빌드하기

번들 파일은 `.ait` 확장자를 가진 파일로, 빌드된 프로젝트를 패키징한 결과물이에요.

{% tabs %}
{% tab title="npm" %}

```sh
npm run build
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm build
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn build
```

{% endtab %}
{% endtabs %}

빌드가 완료되면 프로젝트 루트에 `<서비스명>.ait` 파일이 생성돼요. 자세한 테스트 방법은 [토스앱](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/toss) 문서를 참고해 주세요.

***

### 9. 출시하기

출시하는 방법은 [미니앱 출시](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/deploy) 문서를 참고하세요.


# 외부 서비스 연동


# Firebase 연동하기

앱인토스(미니앱) Webview 환경에서 Firebase를 연동하는 방법을 안내해요. 이 문서는 **Vite(React + TypeScript)** 기반 프로젝트를 기준으로 작성되었어요.

***

### 개요

Firebase는 인증, 데이터베이스, 파일 저장 등 다양한 기능을 제공하는 서비스예요. 앱인토스 WebView 환경에서도 동일하게 사용할 수 있지만, **보안 설정과 환경 변수 관리**가 중요해요.

***

### 1. 준비하기

* Firebase 콘솔 계정 ([console.firebase.google.com](https://console.firebase.google.com))
* Vite(React + TypeScript)로 만든 프로젝트
* Node.js, npm (또는 yarn, pnpm)

### 2. Firebase 프로젝트 만들기

1. Firebase 콘솔에서 **프로젝트 생성**을 눌러 새 프로젝트를 만들어요.
2. 프로젝트 설정 → **앱 추가** → **웹(\</>)** 을 선택해요.
3. 앱 닉네임을 입력하고 등록하면, 아래처럼 구성 정보(firebaseConfig)가 표시돼요.

```js
const firebaseConfig = {
  apiKey: '...',
  authDomain: '...',
  databaseURL: '...',
  projectId: '...',
  storageBucket: '...',
  messagingSenderId: '...',
  appId: '...',
  measurementId: '...'
}
```

### 3. 환경 변수 설정하기

Firebase 구성 정보는 보안을 위해 Vite 환경 변수로 관리하는 걸 권장해요.

프로젝트 루트에 `.env` 파일을 만들고 아래처럼 작성하세요.

```bash
VITE_FIREBASE_API_KEY=your_api_key
VITE_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=your-project-id
VITE_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
VITE_FIREBASE_APP_ID=your_app_id
```

코드에서는 `import.meta.env.VITE_FIREBASE_API_KEY`처럼 불러와요.

### 4. Firebase 설치 및 초기화

최신 Firebase 모듈식 SDK(v12+) 기준으로 작성했어요.

```bash
npm install firebase
# 또는
yarn add firebase
```

`src/firebase/init.ts`

```ts
import { initializeApp, getApps } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore'
import { getStorage } from 'firebase/storage'

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
}

const app = getApps().length ? getApps()[0] : initializeApp(firebaseConfig)

export const auth = getAuth(app)
export const db = getFirestore(app)
export const storage = getStorage(app)
```

> **참고:**
>
> * `databaseURL`은 **Realtime Database**를 사용할 때만 필요해요. Firestore를 사용한다면 생략해도 괜찮아요.
> * `measurementId`는 **Firebase Analytics**(Google Analytics)를 쓸 때 필요해요.

### 5. Firestore 사용 예제

Firestore를 초기화했다면, React 컴포넌트 안에서 데이터를 읽거나 쓸 수 있어요. 아래는 `App.tsx`에서 단일 문서를 읽고 저장하는 가장 간단한 예시예요.

```tsx
import { useState, useEffect } from 'react'
import { db } from './firebase/init'
import { doc, getDoc, setDoc } from 'firebase/firestore'

function App() {
  const [name, setName] = useState('')
  const [savedName, setSavedName] = useState('')

  // Firestore에서 데이터 읽기
  useEffect(() => {
    const fetchData = async () => {
      const ref = doc(db, 'users', 'exampleUser')
      const snap = await getDoc(ref)
      if (snap.exists()) {
        setSavedName(snap.data().name)
      }
    }
    fetchData()
  }, [])

  // Firestore에 데이터 쓰기
  const handleSave = async () => {
    const ref = doc(db, 'users', 'exampleUser')
    await setDoc(ref, { name })
    setSavedName(name)
    setName('')
  }

  return (
    <div style={{ padding: 24 }}>
      <h1>Firestore 간단 예제</h1>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="이름 입력"
      />
      <button onClick={handleSave}>저장</button>
      <p>저장된 이름: {savedName || '(없음)'}</p>

  )
}

export default App

```

#### 동작 방식

* 데이터 읽기 (`getDoc`)
  * Firestore의 users/exampleUser 문서를 한 번만 불러와요.
  * 문서가 존재하면 snap.data()의 값을 화면에 표시해요.
* 데이터 쓰기 (`setDoc`)
  * 입력한 이름을 Firestore에 덮어써 저장해요.
  * 문서가 없으면 자동으로 새로 생성돼요.

<figure><img src="/files/le19Xr86a0QBgwQyZviF" alt=""><figcaption></figcaption></figure>

> Firestore는 단일 문서 외에도 여러 기능을 지원해요.
>
> * 실시간 구독 : `onSnapshot(doc(...))`을 사용하면 문서가 변경될 때마다 UI가 자동으로 갱신돼요.
> * 컬렉션 다루기 : `collection()`, `addDoc()`을 사용하면 여러 문서를 추가하고 불러올 수 있어요.
> * 파일 저장 : `getStorage()`로 `Storage`를 연결해 이미지나 파일을 업로드할 수 있어요.
> * 인증 연동 : `getAuth()`와 함께 사용하면 사용자별 데이터 저장이 가능해요.

### 6. 보안 체크리스트

* 민감한 정보 환경 변수로 관리하기
  * Firebase API Key, 서비스 계정 키 등은 코드에 직접 작성하지 않고 `.env`로 관리하세요.
* 환경 파일을 Git 등에 업로드하지 않기
  * `.env` 파일은 `.gitignore`에 반드시 추가하세요.
  * 키가 노출되면 Firebase 콘솔에서 즉시 재발급하고, 관련 프로젝트 권한을 점검하세요.
* Firebase 보안 규칙 설정하기
  * Firestore / Storage는 기본적으로 모든 사용자에게 공개되어 있어요.
  * 배포 전에 반드시 인증된 사용자만 접근하도록 규칙을 수정하세요.
* 출처(Origin) 제한 확인하기
  * Firebase 콘솔의 Authentication / Hosting / API Key 설정에서 허용 도메인을 제한해두세요.
  * 미니앱(WebView) 도메인만 허용하면 무단 접근을 예방할 수 있어요.

{% hint style="info" %}
**허용 대상 도메인**

```
https://<appName>.apps.tossmini.com : 실제 서비스 환경
https://<appName>.private-apps.tossmini.com : 콘솔 QR 테스트 환경
```

{% endhint %}


# Sentry 설정하기

앱에 **Sentry**를 연동하면 JavaScript에서 발생한 오류를 자동으로 감지하고 모니터링할 수 있어요. 이를 통해 앱의 안정성을 높이고, 사용자에게 더 나은 경험을 제공할 수 있어요.

{% hint style="info" %}
**WebView에서 Sentry 연동**

[Sentry 공식 가이드](https://docs.sentry.io/platforms/javascript/)를 참고하여 연동해주세요.
{% endhint %}

### 1. Sentry 초기 설정

[Sentry 공식 가이드](https://docs.sentry.io/platforms/react-native)를 참고하여 앱에서 Sentry를 초기화해주세요.

앱인토스 환경에서는 네이티브 오류 추적 기능을 사용할 수 없으므로 `enableNative` 옵션을 `false`로 설정해야 해요.

{% hint style="info" %}
**네이티브 오류 추적은 지원되지 않아요**

앱인토스 환경에서는 JavaScript 오류만 추적할 수 있어요.
{% endhint %}

```ts
import * as Sentry from '@sentry/react-native';

Sentry.init({
  // ...
  enableNative: false,
});
```

### 2. Sentry 플러그인 설치

프로젝트 루트 디렉터리에서 사용 중인 패키지 관리자에 맞는 명령어를 실행해 Sentry 플러그인을 설치하세요.

{% tabs %}
{% tab title="npm" %}

```sh
npm install @granite-js/plugin-sentry
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm install @granite-js/plugin-sentry
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn add @granite-js/plugin-sentry
```

{% endtab %}
{% endtabs %}

### 3. 플러그인 구성

설치한 `@granite-js/plugin-sentry`를 `granite.config.ts` 파일의 `plugins` 항목에 추가하세요. 앱인토스 환경에서는 **`useClient` 옵션을 반드시 `false`로 설정**해야 해요.

{% hint style="info" %}
**왜 `useClient` 옵션을 꺼야 하나요?**

`useClient`를 `false`로 설정하면 앱 빌드 시 Sentry에 소스맵이 자동으로 업로드되지 않아요. 앱인토스 환경에서는 빌드 후 **수동으로 소스맵을 업로드**해야 하므로, 이 옵션을 꺼야 해요.
{% endhint %}

```ts
import { defineConfig } from '@granite-js/react-native/config';
import { sentry } from '@granite-js/plugin-sentry'; // [!code highlight]
import { appsInToss } from '@apps-in-toss/framework/plugins';

export default defineConfig({
  // ...,
  plugins: [
    sentry({ useClient: false }), // [!code highlight]
    appsInToss({
      // ...
    }),
  ],
});
```

### 4. 앱 출시하기

앱을 출시하는 방법은 [미니앱 출시](https://appsintoss.gitbook.io/appsintoss-docs/guide/operation/deploy) 문서를 참고하세요.

### 5. Sentry에 소스맵 업로드

출시된 미니앱의 오류를 정확히 추적하려면 빌드 후 생성된 **소스맵을 Sentry에 업로드**해야 해요.

아래 명령어를 실행하면 소스맵이 업로드돼요.

{% hint style="info" %}
**입력값 안내**

* `<API_KEY>`: 앱인토스 콘솔에서 발급받은 API 키예요.
* `<APP_NAME>`: Sentry에 등록된 서비스 이름이에요.
* `<DEPLOYMENT_ID>`: 앱을 배포할 때 사용한 배포 ID예요.
  {% endhint %}

{% tabs %}
{% tab title="npm" %}

```sh
npx ait sentry upload-sourcemap \
  --api-key <API_KEY> \
  --app-name <APP_NAME> \
  --deployment-id <DEPLOYMENT_ID>
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm ait sentry upload-sourcemap \
  --api-key <API_KEY> \
  --app-name <APP_NAME> \
  --deployment-id <DEPLOYMENT_ID>
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn ait sentry upload-sourcemap \
  --api-key <API_KEY> \
  --app-name <APP_NAME> \
  --deployment-id <DEPLOYMENT_ID>
```

{% endtab %}
{% endtabs %}

명령어 실행 후 Sentry의 조직(Org), 프로젝트(Project), 인증 토큰 입력이 요청됩니다. 모든 정보를 입력하면 해당 서비스의 소스맵이 Sentry에 업로드돼요.


# Supabase 연동하기

앱인토스(미니앱) WebView 환경에서 Supabase를 연동하는 방법을 안내해요. Supabase JS 클라이언트는 프레임워크에 무관하게 동작해요. 코드 예제는 **Vite(React + TypeScript)** 기준으로 작성되었어요.

***

### 개요

Supabase는 인증, 데이터베이스(PostgreSQL), 파일 저장, 실시간 구독 등을 제공하는 오픈소스 백엔드 서비스예요. 앱인토스 WebView 환경에서도 동일하게 사용할 수 있지만, **보안 설정과 환경 변수 관리**가 중요해요.

***

### 1. 준비하기

* Supabase 계정 ([supabase.com](https://supabase.com))
* Vite(React + TypeScript)로 만든 프로젝트
* Node.js, npm (또는 yarn, pnpm)

### 2. Supabase 프로젝트 만들기

1. Supabase 대시보드에서 **New project**를 눌러 새 프로젝트를 만들어요.
2. 프로젝트 이름, 데이터베이스 비밀번호, 리전을 설정하고 생성을 완료해요.
3. 프로젝트가 준비되면 대시보드에서 아래 정보를 확인할 수 있어요.

```
Project URL     : https://<project-id>.supabase.co
Publishable key : sb_publishable_xxxxxxxxxxxx
```

### 3. 환경 변수 설정하기

Supabase 연결 정보는 보안을 위해 환경 변수로 관리하는 걸 권장해요. 프로젝트 루트에 `.env` 파일을 만들고 아래처럼 작성하세요.

```bash
VITE_SUPABASE_URL=https://<project-id>.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxxxxxxxxxxx
```

### 4. Supabase 설치 및 초기화

`src/supabase/client.ts` 파일을 만들고 아래처럼 Supabase 클라이언트를 초기화해요.

```bash
npm install @supabase/supabase-js
```

```ts
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabasePublishableKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;

export const supabase = createClient(supabaseUrl, supabasePublishableKey);
```

{% hint style="info" %}
**참고**

Publishable key는 Stripe의 `pk_live_...`, Firebase의 `apiKey`처럼 클라이언트에 노출해도 괜찮은 공개 키예요. 단, **RLS(Row Level Security) 정책을 반드시 설정해야** 해요. RLS 없이는 publishable key만 있으면 누구나 테이블 전체를 읽고 쓸 수 있어요.

`secret` 키는 RLS를 전부 우회하는 비밀 키예요. 서버에서만 사용하고 절대 클라이언트에 노출하지 마세요.
{% endhint %}

### 5. 데이터베이스 사용 예제

Supabase 클라이언트를 초기화했다면, React 컴포넌트 안에서 데이터를 읽거나 쓸 수 있어요. 아래는 `App.tsx`에서 단일 행을 읽고 저장하는 가장 간단한 예시예요.

Supabase 대시보드의 **Table Editor**에서 `users` 테이블을 만들고, `id`(int8, primary key)와 `name`(text) 컬럼을 추가해요.

```tsx
import { useState, useEffect } from 'react';
import { supabase } from './supabase/client';

function App() {
  const [name, setName] = useState('');
  const [savedName, setSavedName] = useState('');

  // Supabase에서 데이터 읽기
  useEffect(() => {
    const fetchData = async () => {
      const { data } = await supabase.from('users').select('name').eq('id', 1).single();
      if (data) {
        setSavedName(data.name);
      }
    };
    fetchData();
  }, []);

  // Supabase에 데이터 쓰기
  const handleSave = async () => {
    await supabase.from('users').upsert({ id: 1, name });
    setSavedName(name);
    setName('');
  };

  return (
    <div style={{ padding: 24 }}>
      <h1>Supabase 간단 예제</h1>
      <input value={name} onChange={(e) => setName(e.target.value)} placeholder="이름 입력" />
      <button onClick={handleSave}>저장</button>
      <p>저장된 이름: {savedName || '(없음)'}</p>

  );
}

export default App;
```

#### 동작 방식

* 데이터 읽기 (`.select()`)
  * `users` 테이블에서 `id`가 1인 행을 한 번만 불러와요.
  * 행이 존재하면 `name` 값을 화면에 표시해요.
* 데이터 쓰기 (`.upsert()`)
  * 입력한 이름을 `users` 테이블에 저장해요.
  * 행이 없으면 새로 생성하고, 있으면 덮어써요.

{% hint style="info" %}
**Supabase 추가 기능**

* 실시간 구독: `.channel()`, `.on()`을 사용하면 데이터 변경 시 UI가 자동으로 갱신돼요.
* 파일 저장: `supabase.storage`로 이미지나 파일을 업로드할 수 있어요.
* 인증 연동: `supabase.auth`와 함께 사용하면 사용자별 데이터 저장이 가능해요.
  {% endhint %}

### 6. 보안 체크리스트

* 민감한 정보 환경 변수로 관리하기
  * Supabase URL, publishable key 등은 코드에 직접 작성하지 않고 `.env`로 관리하세요.
* 환경 파일을 Git 등에 업로드하지 않기
  * `.env` 파일은 `.gitignore`에 반드시 추가하세요.
  * 키가 노출되면 Supabase 대시보드에서 즉시 키를 재발급하세요.
* **Row Level Security(RLS) 반드시 설정하기**
  * Supabase의 모든 테이블은 기본적으로 RLS가 비활성화되어 있어요.
  * RLS가 꺼진 상태에서는 publishable key만 있으면 누구나 테이블 전체에 접근할 수 있어요.
  * 배포 전에 반드시 RLS를 활성화하고, 인증된 사용자만 접근하도록 정책(Policy)을 설정하세요.
  * Supabase 대시보드 **Table Editor → 테이블 선택 → RLS** 탭에서 설정할 수 있어요.
* 출처(Origin) 제한 확인하기
  * Supabase 대시보드의 **Authentication → URL Configuration**에서 허용 도메인을 제한해두세요.
  * 미니앱(WebView) 도메인만 허용하면 무단 접근을 예방할 수 있어요.

{% hint style="info" %}
**허용 대상 도메인**

SDK 버전에 따라 달라요.\
\
SDK 3.x\
`https://<appName>.web.tossmini.com` — 실제 서비스 환경 \
`https://<appName>.private-web.tossmini.com` — 콘솔 QR 테스트 환경\
\
SDK 1.x \~ 2.x\
`https://<appName>.apps.tossmini.com` — 실제 서비스 환경 \
`https://<appName>.private-apps.tossmini.com` — 콘솔 QR 테스트 환경
{% endhint %}


# AI로 콘솔 사용하기

앱인토스 콘솔 MCP를 연결하면 콘솔에 들어가지 않아도 AI 도구에서 워크스페이스, 미니앱, 검수, 번들, 대시보드, 인앱 결제, 인앱 광고, 프로모션, 푸시 알림 등 콘솔 작업을 할 수 있어요.

자연어로 요청하면 콘솔 정보를 조회하거나 필요한 작업을 진행해줘요. 예를 들어 "최근 7일 DAU 알려줘"처럼 요청하면 돼요.

***

### 1. 콘솔 MCP 연결하기

콘솔은 앱을 등록하고 관리하는 앱인토스 웹사이트예요. 이 MCP를 연결하면 콘솔 사이트에 직접 들어가지 않고도, AI에게 요청하는 것만으로 워크스페이스, 미니앱, 검수, 번들 같은 콘솔 작업을 진행할 수 있어요.

아래처럼 요청해 보세요.

{% tabs %}
{% tab title="Claude Code" %}

```bash
claude mcp add apps-in-toss-console --transport http https://mcp.toss.im/adapters/apps-in-toss-console/mcp --client-id mcp-gateway
```

{% endtab %}

{% tab title="Codex" %}

```bash
codex mcp add apps-in-toss-console --url https://mcp.toss.im/adapters/apps-in-toss-console/mcp --oauth-client-id mcp-gateway
```

{% endtab %}
{% endtabs %}

MCP 연결 후 로그인 창이 뜨면 안내에 따라 인증을 완료해 주세요. 완료되면 아래처럼 확인해 보세요.\
AI가 정상적으로 연결되고 인증까지 끝났다고 알려주면 완료된 거예요.

```
콘솔 MCP가 잘 연결됐는지 확인해줘
```

***

### 2. 콘솔 작업 요청하기

AI에게 자연어로 요청하면 아래 콘솔 작업을 실행할 수 있어요. 예를 들면 이렇게 요청하면 돼요.

* "최근 7일 DAU 알려줘" → `dashboard_dau`
* "새 푸시 템플릿 만들어줘" → `push_template_create`
* "직전 번들 버전으로 롤백해줘" → `bundle_rollback`

{% hint style="warning" %}
번들 롤백, 프로모션 예산 충전, 푸시 발송처럼 되돌리기 어렵거나 비용이 발생하는 작업은 AI가 제안한 내용을 실행하기 전에 꼭 확인해 주세요.
{% endhint %}

**워크스페이스와 미니앱**

<table data-search="false"><thead><tr><th>도구 이름</th><th>설명</th></tr></thead><tbody><tr><td><code>workspace_list</code></td><td>내가 속한 워크스페이스 목록을 조회해요.</td></tr><tr><td><code>workspace_get</code></td><td>특정 워크스페이스 상세 정보를 조회해요.</td></tr><tr><td><code>workspace_create</code></td><td>새 워크스페이스를 만들어요.</td></tr><tr><td><code>workspace_update</code></td><td>워크스페이스 정보를 수정해요.</td></tr><tr><td><code>workspace_members_list</code></td><td>워크스페이스 멤버 목록을 조회해요.</td></tr><tr><td><code>miniapp_list</code></td><td>워크스페이스 안의 미니앱 목록을 조회해요.</td></tr><tr><td><code>miniapp_get</code></td><td>특정 미니앱 상세 정보를 조회해요.</td></tr><tr><td><code>miniapp_create</code></td><td>새 미니앱을 만들어요.</td></tr><tr><td><code>miniapp_get_status</code></td><td>미니앱 검수와 운영 상태를 확인해요.</td></tr><tr><td><code>miniapp_update_basic_info</code></td><td>미니앱 이름, 설명 같은 기본 정보를 수정해요.</td></tr><tr><td><code>miniapp_update_category</code></td><td>미니앱 카테고리를 변경해요.</td></tr><tr><td><code>miniapp_update_icon</code></td><td>미니앱 아이콘 이미지를 교체해요.</td></tr><tr><td><code>miniapp_update_screenshots</code></td><td>미니앱 스크린샷을 교체해요.</td></tr><tr><td><code>miniapp_update_age_rating</code></td><td>미니앱 연령등급을 변경해요.</td></tr><tr><td><code>miniapp_update_privacy_policy</code></td><td>개인정보처리방침을 업데이트해요.</td></tr></tbody></table>

**토스 로그인 설정**

| 도구 이름                     | 설명                 |
| ------------------------- | ------------------ |
| `toss_login_get_config`   | 토스 로그인 설정을 조회해요.   |
| `toss_login_update_terms` | 토스 로그인 약관을 업데이트해요. |

**미니앱 출시**

<table data-search="false"><thead><tr><th>도구 이름</th><th>설명</th></tr></thead><tbody><tr><td><code>bundle_list</code></td><td>업로드된 번들 목록을 조회해요.</td></tr><tr><td><code>bundle_upload</code></td><td>새 번들을 업로드해요.</td></tr><tr><td><code>bundle_get_live_version</code></td><td>현재 라이브 배포된 버전을 확인해요.</td></tr><tr><td><code>bundle_submit_review</code></td><td>번들 검수를 신청해요.</td></tr><tr><td><code>review_list</code></td><td>검수 요청 목록을 조회해요.</td></tr><tr><td><code>review_get</code></td><td>검수 요청 상세 정보를 조회해요.</td></tr><tr><td><code>review_get_feedback</code></td><td>검수 피드백을 확인해요.</td></tr><tr><td><code>review_submit</code></td><td>검수를 신청해요.</td></tr><tr><td><code>review_cancel</code></td><td>검수를 취소해요.</td></tr><tr><td><code>bundle_set_release_note</code></td><td>릴리즈 노트를 작성해요.</td></tr><tr><td><code>bundle_rollback</code></td><td>이전 버전으로 되돌려요.</td></tr></tbody></table>

**대시보드와 분석**

<table data-search="false"><thead><tr><th>도구 이름</th><th>설명</th></tr></thead><tbody><tr><td><code>dashboard_dau</code></td><td>일간 활성 유저(DAU)를 조회해요.</td></tr><tr><td><code>dashboard_session</code></td><td>세션 수와 세션 길이 통계를 조회해요.</td></tr><tr><td><code>dashboard_retention</code></td><td>리텐션(재방문율)을 확인해요.</td></tr><tr><td><code>dashboard_conversion</code></td><td>전환율 통계를 조회해요.</td></tr><tr><td><code>dashboard_compare_period</code></td><td>기간별 지표를 비교해요.</td></tr><tr><td><code>dashboard_revenue_iap</code></td><td>인앱 결제 매출 현황을 조회해요.</td></tr><tr><td><code>dashboard_revenue_iaa</code></td><td>인앱 광고 매출 현황을 조회해요.</td></tr><tr><td><code>dashboard_export_csv</code></td><td>대시보드 데이터를 CSV로 내보내요.</td></tr></tbody></table>

**이벤트 로그**

| 도구 이름                  | 설명               |
| ---------------------- | ---------------- |
| `event_log_list`       | 이벤트 로그 목록을 조회해요. |
| `event_log_search`     | 이벤트 로그를 검색해요.    |
| `event_pageview_stats` | 페이지뷰 통계를 조회해요.   |
| `event_act_type_get`   | 이벤트 타입 정의를 조회해요. |
| `event_act_type_set`   | 이벤트 타입 정의를 설정해요. |

**인앱 결제**

<table data-search="false"><thead><tr><th>도구 이름</th><th>설명</th></tr></thead><tbody><tr><td><code>iap_product_list</code></td><td>인앱 상품 목록을 조회해요.</td></tr><tr><td><code>iap_product_get</code></td><td>인앱 상품 상세 정보를 조회해요.</td></tr><tr><td><code>iap_product_create_inspection</code></td><td>새 상품 검수를 신청해요.</td></tr><tr><td><code>iap_product_update_inspection</code></td><td>상품 수정 검수를 신청해요.</td></tr><tr><td><code>iap_product_change_status</code></td><td>상품 판매를 시작하거나 중지해요.</td></tr><tr><td><code>iap_order_list</code></td><td>결제 주문 내역을 조회해요.</td></tr><tr><td><code>iap_refund_list</code></td><td>환불 요청 목록을 조회해요.</td></tr><tr><td><code>iap_revenue</code></td><td>인앱 결제 매출 통계를 조회해요.</td></tr></tbody></table>

**인앱 광고**

<table data-search="false"><thead><tr><th>도구 이름</th><th>설명</th></tr></thead><tbody><tr><td><code>iaa_placement_group_list</code></td><td>광고 목록을 조회해요.</td></tr><tr><td><code>iaa_placement_group_get</code></td><td>광고 상세 정보를 조회해요.</td></tr><tr><td><code>iaa_placement_group_create</code></td><td>광고를 생성해요.</td></tr><tr><td><code>iaa_placement_group_update</code></td><td>광고를 수정해요.</td></tr><tr><td><code>iaa_report_performance</code></td><td>성과 리포트를 조회해요.</td></tr><tr><td><code>iaa_report_analytics</code></td><td>분석 리포트를 조회해요.</td></tr><tr><td><code>iaa_dashboard_report_v2</code></td><td>대시보드 리포트를 조회해요.</td></tr><tr><td><code>iaa_workspace_dashboard_report_v2</code></td><td>워크스페이스 전체 미니앱의 대시보드 리포트를 조회해요.</td></tr><tr><td><code>iaa_settlement_summary_v2</code></td><td>광고 정산 요약을 조회해요. <br>(관리자만 조회할 수 있어요)</td></tr></tbody></table>

**프로모션**

<table data-search="false"><thead><tr><th>도구 이름</th><th>설명</th></tr></thead><tbody><tr><td><code>promotion_list</code></td><td>프로모션 목록을 조회해요.</td></tr><tr><td><code>promotion_get</code></td><td>프로모션 상세 정보를 조회해요.</td></tr><tr><td><code>promotion_create</code></td><td>새 프로모션을 만들어요.</td></tr><tr><td><code>promotion_modify</code></td><td>프로모션을 수정해요.</td></tr><tr><td><code>promotion_review_comment</code></td><td>프로모션 검수 코멘트를 확인해요.</td></tr><tr><td><code>promotion_change_status</code></td><td>프로모션 상태를 변경해요.</td></tr><tr><td><code>promotion_money_balance</code></td><td>프로모션 예산 잔액을 확인해요.</td></tr><tr><td><code>promotion_money_charge</code></td><td>프로모션 예산을 충전해요.</td></tr><tr><td><code>promotion_money_history</code></td><td>예산 충전과 사용 내역을 조회해요.</td></tr><tr><td><code>promotion_stats</code></td><td>프로모션 성과 통계를 조회해요.</td></tr></tbody></table>

**푸시 알림**

<table data-search="false"><thead><tr><th>도구 이름</th><th>설명</th></tr></thead><tbody><tr><td><code>push_template_list</code></td><td>푸시 템플릿 목록을 조회해요.</td></tr><tr><td><code>push_template_create</code></td><td>푸시 템플릿을 만들어요.</td></tr><tr><td><code>push_template_update</code></td><td>푸시 템플릿을 수정해요.</td></tr><tr><td><code>push_target_segment_list</code></td><td>타겟 세그먼트 목록을 조회해요.</td></tr><tr><td><code>push_target_segment_create</code></td><td>타겟 세그먼트를 만들어요.</td></tr><tr><td><code>push_send_scheduled</code></td><td>푸시 예약 발송을 등록해요.</td></tr><tr><td><code>push_cancel_scheduled</code></td><td>푸시 예약 발송을 취소해요.</td></tr><tr><td><code>push_history_list</code></td><td>푸시 발송 내역을 조회해요.</td></tr><tr><td><code>push_stats</code></td><td>푸시 통계를 확인해요.</td></tr></tbody></table>

**공지**

| 도구 이름         | 설명              |
| ------------- | --------------- |
| `notice_list` | 공지 목록을 조회해요.    |
| `notice_get`  | 공지 상세 정보를 조회해요. |


# 운영


# 미니앱 등록하기

### 1. 회원가입하기

앱인토스를 이용하려면 먼저 [앱인토스 콘솔](https://apps-in-toss.toss.im/)에 접속해 회원가입을 해주세요. 회원가입은 토스 비즈니스 회원을 기반으로 진행돼요.

가입하려면 두 가지 조건이 필요해요.

* 만 19세 이상이어야 해요.
* 본인 명의로 로그인된 토스 앱이 있어야 해요.

***

### 2. 워크스페이스 만들기

워크스페이스는 팀원들이 함께 프로젝트를 관리하는 공간이에요.

* 워크스페이스는 사업자당 1개만 만들 수 있어요.
* 이름은 다른 워크스페이스와 겹치게 정할 수 없어요.

팀이나 프로젝트 이름처럼 쉽게 알아볼 수 있는 이름을 붙여주세요. 이름은 나중에 바꿀 수 있어요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2Fy3ZFmPosUeo9BZW9zqgQ%2Fimage.png?alt=media&#x26;token=ff3e94b5-ef2d-455e-827c-0170b27554e6" alt=""><figcaption></figcaption></figure>

***

### 3. 멤버 관리하기

워크스페이스를 만든 사람에게 '대표관리자' 권한이 자동으로 부여돼요.

대표관리자는 워크스페이스 운영을 책임지고, 서비스 약관에 동의할 수 있어요. 대표관리자는 관리자 중에서만 신청할 수 있고, 구성원은 신청할 수 없어요. [대표관리자 권한 위임하기](https://developers-apps-in-toss.toss.im/guide/operation/undefined)

#### 멤버 초대하기

'멤버' 메뉴에서 '+초대하기'를 누르고 팀원의 이메일을 입력하면, 권한별로 초대할 수 있어요.

* **관리자**: 워크스페이스 전체를 관리하고, 정산 정보까지 확인할 수 있어요.
* **구성원**: 권한을 받은 앱에서 정보를 조회하고 관리할 수 있어요.

<figure><img src="/files/KnSop1rtzWauhvQEM9xj" alt=""><figcaption></figcaption></figure>

<div><figure><img src="/files/HISkBR0upZhyMMImK0if" alt=""><figcaption></figcaption></figure> <figure><img src="/files/ypd3KL487sTmoww8O8Mw" alt=""><figcaption></figcaption></figure></div>

***

### 4. 앱 등록하기

워크스페이스를 만든 뒤 '앱' 메뉴에서 '+등록하기'를 눌러 앱을 등록해주세요. 아직 개발이 끝나지 않은 앱도 미리 등록할 수 있어요.

> **개발 앱과 라이브 앱을 나눠서 관리하고 싶다면?**
>
> 같은 워크스페이스 안에서 앱을 구분해 등록해주세요. 테스트용 앱과 실제 배포용 앱을 따로 등록할 수 있어요.

<figure><img src="/files/LSHOjkjIy3IxOyiDfP0y" alt=""><figcaption></figcaption></figure>

아래 정보만 입력하면 앱을 만들 수 있어요.&#x20;

나머지 앱 정보는 준비되는 대로 입력하면 되고, 각 항목은 임시 저장할 수 있어요.&#x20;

<figure><img src="/files/iODxlIrzd28tcwMYzkq6" alt=""><figcaption></figcaption></figure>

#### 앱 이름

토스 앱에 노출되는 이름이에요. 어떤 서비스인지 바로 알 수 있게 지어주세요.

* 이미 쓰고 있는 브랜드명이나 서비스명이 있다면, 그대로 쓰는 걸 추천해요.
* 앱 이름은 앱 정보에서 나중에 바꿀 수 있어요.

**영문명은 아래 규칙을 따라주세요.**

* 15자 이내의, 발음하기 쉬운 명사형 이름으로 지어요.
* 명령문이나 동사구는 쓰지 않아요.
  * 'Book a Taxi'가 아니라 'GoRide Taxi'처럼 브랜드명 형태로 지어요.
* 한글 앱 이름과 뜻이 다른 일반 단어를 단독으로 쓰지 않아요.
  * 'Taxi'가 아니라 'GoRide Taxi'로 지어요.
* 단어를 3개 이상 쓰거나, 읽기 어려운 복합어는 피해요.

#### appName

앱 스킴을 호출하면, 이 ID를 기준으로 앱인토스 서비스로 이동해요.

* `intoss://` 형식으로 구성돼요. 규칙을 지키지 않으면 서버 인증서 발급에 실패할 수 있어요.
* appName은 한 번 등록하면 바꿀 수 없으니 신중하게 입력해주세요.

#### 앱 유형

만들려는 앱이 게임인지 게임이 아닌지에 따라 앱 유형을 선택해주세요. 선택한 유형에 따라 이후에 입력할 앱 정보 항목이 달라져요.

***

### 5. 앱 정보 입력하기

앱을 출시하기 전에 앱 정보 메뉴에서 \[수정하기] 버튼을 눌러 나머지 앱 정보를 모두 입력하고 검토를 요청해주세요. 앱 정보 검토가 끝나야 앱을 출시할 수 있어요.

<figure><img src="/files/kJc1TI6BEWJgfSZXpqUO" alt=""><figcaption></figcaption></figure>

#### 5-1. 기본 정보

<figure><img src="/files/Nv0JrHE08rV5b69Dmrh3" alt=""><figcaption></figcaption></figure>

#### 부제

어떤 서비스를 이용할 수 있는지 쉽게 이해할 수 있게, 짧게 적어주세요.

* 전체 메뉴에서 검색했을 때 미니앱 이름 아래에 노출될 수 있어요.
* 비속어와 느낌표 등은 쓸 수 없어요.

#### 상세 설명

상세 설명을 바탕으로 토스 홈 광고 대상으로 선정될 수 있어요. 사용자가 서비스에서 무엇을 경험할 수 있는지 최대한 구체적으로 적어주세요.

사용자가 서비스를 처음 실행한 뒤 핵심 기능을 경험하기까지의 과정을 적어주세요. '서비스 접속 → 행동 → 결과' 흐름에 따라 문장으로 풀어서 설명하면 좋아요. 사용자가 실제로 무엇을 보고, 무엇을 누르고, 어떤 경험을 하는지를 중심으로 자세히 적어주세요.

<details>

<summary>예시: 장학금 정보 서비스</summary>

(X) 필터를 제공해요.

**(O) 해외 장학금, 거주 지역, 가구원 정보를 기반으로 필터를 제공해요.**

(X) 알림을 신청하면 나에게 딱 맞는 장학금 정보를 알려줘요.

**(O) 알림을 신청하면 거주 지역과 가구원 정보를 기반으로, 나에게 딱 맞는 장학금의 금액과 접수 기한을 알림으로 알려줘요.**

</details>

<details>

<summary>예시: 디펜스 게임</summary>

우산을 잡고 내려오는 적들

번개, 화살, 주먹 등의 무기 사용

2번의 턴마다 무기 고르기 가능

몬스터를 10번 무찌르면 골드 20개 받기

오른쪽 출석 버튼을 누르면, 첫 출석 시 다이아 100개 받기

</details>

#### 사용 연령

지금 앱인토스는 만 19세 이상 사용자에게만 제공돼요.

#### 고객문의 이메일

문의에 원활하게 대응할 수 있도록 고객문의 이메일 정보를 입력해주세요.

***

#### 5-2. 카테고리 및 노출

<figure><img src="/files/AWUYuH3tt7wfF92N3tlq" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/Mh5o6WzihwyWKs9UxaS1" alt=""><figcaption></figcaption></figure>

#### 카테고리

출시하는 미니앱에 맞는 카테고리를 선택해주세요. 실제 서비스와 다르면, 검토 과정에서 알맞은 카테고리로 바꾼 뒤 안내해드려요.

<details>

<summary>게임 앱</summary>

출시하는 게임의 장르에 맞는 카테고리를 선택해주세요.

웹보드 게임이라면, '웹보드 여부'를 꼭 체크해야 해요. [웹보드 게임 서비스 주의사항](https://developers-apps-in-toss.toss.im/intro/caution#web-board)

</details>

<details>

<summary>비게임 앱</summary>

대분류, 중분류, 소분류까지 설정할 수 있어요.

이미 선택한 중분류와 같은 카테고리는 또 선택할 수 없어요. 예를 들어 중분류를 '여행'으로 선택했다면, 두 번째 카테고리로 '여행'을 다시 선택할 수 없어요.

</details>

#### 노출 정보

아래는 '카테고리 및 노출' 영역에서 입력할 내용이에요. 모든 미니앱에 공통으로 필요한 내용과, 게임인지 아닌지에 따라 필요한 내용을 각각 확인할 수 있어요.

#### 앱 로고

앱 로고는 토스 앱 전체 메뉴의 미니앱 홈에 노출돼요. 게임이 아닌 앱은 상단 공통 내비게이션 바에도 노출돼요.

* 작은 화면에서도 선명하게 보여야 해요.
* 서비스 특성이 한눈에 드러나야 해요.
* 다른 앱과 헷갈리지 않아야 하고, 저작권 문제가 없어야 해요.
* 다크 모드에서도 자연스럽게 보이는 색과 형태를 써주세요.
* 정사각형 600 × 600px, PNG 파일로 만들어주세요.&#x20;
* 모서리가 둥근 형태는 사용할 수 없어요. (배경색 필수, 투명 배경은 쓸 수 없어요.)
* 토스에서 제공하는 아이콘과 이미지 리소스 등은 앱 로고로 쓸 수 없어요. (2차 가공 포함)

<figure><img src="/files/e5UWB7nv6j0xmzVv9jTk" alt=""><figcaption></figcaption></figure>

#### 썸네일 이미지 (게임 앱)

썸네일 이미지는 토스 앱의 여러 지면에 쓰이고, 사용자가 서비스에 접속하기 전에 가장 먼저 보는 이미지예요. 어떤 서비스인지 한눈에 이해할 수 있고, 좋은 인상을 받을 수 있게 만들어주세요.

* 1932 × 828px, PNG 파일로 업로드해주세요.
* 미니앱의 핵심 플레이 화면이나 주요 기능이 보이는 이미지를 써주세요.
* 선명한 고화질 이미지를 쓰고, 작은 이미지를 늘려서 쓰지 마세요.
* 정해진 규격에 맞춰 만들고, 빈 공간을 색이나 블러로 채우지 마세요.
* 비속어, 정치적 표현 등 편견이 담긴 요소는 넣지 마세요.
* 텍스트는 너무 많이 쓰지 마세요. 필요하다면 로고나 짧은 문구만 적절히 넣어주세요.
* 로고를 쓸 때는 서비스명이 명확히 보이는지 확인해주세요.
* 다른 앱과 헷갈리지 않게 차별화해주세요.
* 저작권 문제가 없는 이미지를 써주세요.
* 토스에서 제공하는 아이콘과 이미지 리소스 등은 썸네일로 쓸 수 없어요. (2차 가공 포함)

<details>

<summary>권장하는 예시</summary>

<figure><img src="/files/MdaJj4I9CRVyG1M0CDRq" alt=""><figcaption></figcaption></figure>

</details>

<details>

<summary>권장하지 않는 예시</summary>

<figure><img src="/files/Ri3JGHj9XsI05llkTcEI" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/Bz7RhgMjHTUg4yOxn08V" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/gacDzrZpnaS0wpr0jzQM" alt=""><figcaption></figcaption></figure>

</details>

#### 스크린샷

스크린샷 이미지는 토스 검색 결과와 앱 미리보기 등에 쓰여요.

* 제출은 선택 사항이에요. 다만 제출한 미니앱부터 순서대로 반영되어, 미니앱 홈에 먼저 노출될 예정이에요.
* 직접 만든 이미지와 스크린샷 형태 모두 쓸 수 있어요.
* 검색 결과와 앱 미리보기에 쓰이니, 미니앱의 특징을 한눈에 알 수 있는 이미지를 써주세요.
* 이미지를 넣는다면 세로형은 최소 3장, 가로형은 최소 1장을 업로드해주세요.
  * 세로형 규격: 636 × 1048px, PNG 파일
  * 가로형 규격: 1504 × 741px, PNG 파일

#### 리더보드 (게임 앱)

사용자 점수를 기준으로 순위를 매기는 기능이에요. 기록할 점수 단위와 정렬 기준 등 정책을 설정해주세요.

***

#### 5-3. 게임 등급분류

게임은 등급 심의 증빙 자료를 꼭 제출해야 해요. 입력한 정보는 토스 앱 5.240.0 버전부터 아래와 같이 노출돼요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FXDgRiVytPp5G0qnAzO20%2Fimage.png?alt=media&#x26;token=81067b30-7996-4abd-939a-2e01440e8353" alt=""><figcaption></figcaption></figure>

#### 1) 스토어에서 출시 심사를 받았다면, 스토어 URL을 입력해주세요

오픈마켓(앱스토어, 구글 플레이 스토어, 원스토어, Microsoft Store)에서 등급 분류를 받았다면, 오픈마켓에 출시된 게임 페이지의 URL과 자체등급분류 게임물 정보를 입력해야 해요.

> **게임 등급 정보와 자체등급분류 게임물 정보를 왜 입력해야 하나요?**&#x20;
>
> 게임물관리위원회 규정에 따라, 자체등급분류사업자에게 등급을 받은 게임과 앱인토스로 출시하려는 게임이 같은 게임인지 확인하기 위해서예요. 이를 지키지 않으면 등급을 받지 않은 게임물로 여겨져, 시정 권고나 수사 의뢰, 행정처분 의뢰 등의 조치로 이어질 수 있어요.

<figure><img src="/files/tWwI6FBnOE0NXP3vDge7" alt=""><figcaption></figcaption></figure>

**게임 등급 정보**

'스토어 링크'로 바꾼 뒤, 게임이 출시된 페이지의 링크를 입력해주세요.

**기본 정보**

* 사업자라면, 사업자등록증에 있는 정보를 입력해주세요.
* 개인 개발자(비사업자)라면, 개인 본인 정보를 입력해주세요.

**자체등급분류 게임물 정보**

아래 정보를 입력하려면, 게임물관리위원회의 '자체등급분류 게임물 조회' 페이지에서 오픈마켓에 출시한 게임을 먼저 조회해주세요.

* **등록자명**: 스토어에 나오는 등록자명을 입력해주세요. 구글 플레이 스토어는 개발자명을, 애플 앱스토어는 제공자명을 입력해주세요.
* **자체등급분류사업자명**: '구글', '애플', '원스토어'처럼 자체등급분류를 진행한 오픈마켓의 사업자명을 입력해주세요.
* **등급분류일자**: 게임물관리위원회에서 조회한 등급 분류 일자와 똑같이 입력해주세요.
* **등급분류번호**: 게임물관리위원회에서 조회한 등급 분류 번호와 똑같이 입력해주세요.
* **이용등급**: 게임물관리위원회에서 조회한 이용등급과 똑같은 등급을 선택해주세요.
* **내용정보**: 게임물관리위원회에서 조회한 내용정보에 표시된 항목을 모두 선택해주세요. 전체 이용가라도 내용정보가 있다면 선택해주세요.
* **대표자 인감 또는 사인 이미지**: 대표자의 인감이나 사인 이미지를 첨부해주세요. 흰 종이에 인감 도장을 찍거나 서명한 뒤, 그 이미지를 첨부해주세요.

**게임의 주요 내용이 담긴 화면 첨부 (플레이 화면)**

* 자체등급분류를 받은(오픈마켓에 출시한) 게임 플레이 화면 2장과, 앱인토스로 출시하는 게임의 플레이 화면 2장을 각각 첨부해주세요.
* 따로 편집하지 말고, 원본 플레이 화면을 그대로 첨부해주세요.
* 내용정보에 선정성이나 폭력성이 있다면, 그에 해당하는 게임 화면 이미지를 각각 첨부해주세요.

<details>

<summary>게임물 등록자명과 사업자명이 다르면 어떻게 하나요?</summary>

자체등급분류 게임물에 적힌 등록자명과 실제 사업자명이 다르면 검수에서 반려돼요. 다른 사유를 소명할 수 있다면, 증빙 자료나 사유를 추가로 제출해주세요.

* **개인(비사업자)**: 등록자명과 사업자명이 다른 사유를 적어주세요.
* **사업자(개인 또는 법인)**: 등기부등본을 제출해, 사업체 이름이 바뀌었다는 등의 사유를 소명해주세요.

</details>

#### 2) 게임물관리위원회에서 심의를 받았다면, 증명서를 PDF 파일로 첨부해주세요

**게임 등급 정보**

'게임물 등급분류증명서'로 바꾼 뒤, 등급 심의 증명서를 PDF로 첨부해주세요. [게임물 등급분류 신청 방법](https://toss.im/apps-in-toss/blog/game_rating_classification)

**자체등급분류 게임물 정보**

게임물 등급분류증명서에 나온 내용과 똑같이 입력해주세요.

***

### 6. 검토 요청하기

앱 등록 가이드라인에 맞게 입력을 마친 뒤 **'검토 요청하기'** 버튼을 눌러주세요. 검토는 영업일 기준 1\~2일 걸리고, 결과는 콘솔과 이메일로 안내해드려요.


# 미니앱 테스트하기

앱 번들(.ait) 파일을 업로드하고 생성된 테스트용 앱스킴으로 토스 앱에서 최종 테스트를 할 수 있어요.

### 1. 앱 번들 파일 생성하기

앱 번들은 `.ait` 확장자를 가진 파일로, 빌드된 프로젝트를 패키징한 결과물이에요. 아래 명령어를 실행해 앱 번들을 생성하세요. 빌드가 끝나면 프로젝트 루트 디렉터리에 `<서비스명>.ait` 파일이 생겨요.

{% tabs %}
{% tab title="npm" %}

```sh
npm run build
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm build
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn build
```

{% endtab %}
{% endtabs %}

***

### 2. 토스 앱 테스트하기

앱 번들을 업로드하고 토스 앱에서 테스트하는 방법은 두 가지예요.

1. 콘솔에서 직접 업로드 후 QR 코드로 테스트
2. CI/CD 명령어로 자동 업로드

앱 번들은 압축 해제 기준 100MB 이하만 업로드할 수 있어요. 이미지·사운드·영상 같은 리소스를 모두 포함하면 용량을 넘길 수 있으니, 리소스 파일은 빌드와 분리해서 관리하세요.

리소스 관리는 이렇게 하는 걸 권장해요.

* 앱 실행에 꼭 필요한 최소 리소스만 번들에 포함하세요.
* 대용량 리소스는 외부 스토리지나 CDN에서 내려받도록 구성해 주세요.
* 추가 리소스는 단계적으로 내려받는 방식(Lazy Loading)을 적용하면 사용자 경험이 좋아져요.

{% hint style="info" %}
**앱 번들 용량 정책**

* 앱 번들은 압축 해제 기준 100MB 이하만 업로드할 수 있어요.
* 리소스를 모두 포함하면 용량을 넘길 수 있으니, 리소스 파일은 빌드와 분리해서 관리하세요.
  {% endhint %}

#### 2-1. 콘솔에서 앱 번들 업로드 후 QR로 테스트하기

먼저 콘솔에 앱 번들(`.ait`) 파일을 업로드하세요. 테스트를 최소 1번 이상 완료해야 검토를 요청할 수 있어요.

> **앱 번들 파일이 업로드되지 않나요?**
>
> 앱이 정상적으로 빌드됐는지 확인해 주세요. `npm run build`로 생성한 번들이 아니거나 프로젝트 구조가 올바르지 않으면, 앱 번들 컴파일이 실패해서 업로드되지 않아요.

앱 번들을 업로드한 뒤 '테스트하기' 버튼을 누르면, 콘솔에서 토스 앱 테스트용 QR 코드를 확인할 수 있어요. QR 코드를 스캔하면 토스 앱에서 미니앱이 실행돼요.

QR 코드 테스트는 아래 조건을 모두 충족해야 실행돼요.

* 토스 앱에 로그인되어 있어야 해요.
* 워크스페이스 멤버여야 해요.
* 만 19세 이상 사용자만 테스트할 수 있어요.

<figure><img src="/files/dAcn1M01nMGsPPpHk5OU" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/ZPht5kMO1oZ6y0sQ9KC7" alt=""><figcaption></figcaption></figure>

#### 2-2. CI/CD 명령어 사용하기

콘솔에 접속하지 않고 CLI로 앱 번들을 업로드할 수 있어요.

CI/CD 명령어로 자동 업로드하려면 SDK v1.4.0 이상이 필요해요. 이전 버전을 쓰고 있다면 먼저 SDK를 업그레이드해 주세요.

먼저 콘솔에서 API 키를 발급해 주세요. 전체 앱 또는 특정 앱 단위로 접근 권한을 설정할 수 있어요.

> 접속 경로: 워크스페이스 선택 → 왼쪽 메뉴에서 '키'

<figure><img src="/files/6t4ZHNioAL62ajKgsRq2" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/dVqkj3iRER6wP0vTj4DG" alt=""><figcaption></figcaption></figure>

아래 명령어를 실행해 앱 번들을 업로드하세요. 정상적으로 업로드되면 테스트용 앱스킴을 확인할 수 있어요.

```
npx ait deploy --api-key {API 키}
```

API 키를 등록해 두면 반복해서 입력하지 않아도 돼요.

```
npx ait token add
npx ait deploy
```

`-m` 옵션으로 번들을 업로드할 때 메모를 함께 남길 수 있어요.

```
npx ait deploy -m "출시메모"
```

필요에 따라 아래 명령어를 사용해 보세요.

| 명령어                                               | 용도                                      |
| ------------------------------------------------- | --------------------------------------- |
| npx ait token --help                              | 도움말 보기                                  |
| npx ait token add \[워크스페이스명] \[API 키]             | 토큰 등록하기                                 |
| npx ait token remove \[워크스페이스명]                   | 등록된 토큰 삭제하기                             |
| npx ait deploy \[워크스페이스명] \[API 키]                | 번들 업로드하기                                |
| npx ait deploy \[워크스페이스명] \[API 키] --timeout \[초] | 배포 상태 확인 최대 대기 시간 설정하기 (10초 이상 300초 이하) |

***

### 3. 기능 테스트하기

`intoss://` 스킴은 앱이 정식 출시된 뒤에만 접근할 수 있어요. 출시 전 기능 테스트는 업로드할 때 생성된 테스트 스킴(QR 코드)으로 해야 해요.

#### QR 코드에서 deploymentId 확인하기

앱 번들을 업로드할 때마다 새로운 `deploymentId`가 발급돼요. 테스트 스킴에서 `_deploymentId`는 필수 파라미터예요.

```
intoss-private://appsintoss?_deploymentId=0198c000-68c3-7d2b-0000-2c00000005ec
```

#### 스킴에 path·query 적용해 테스트하기

하위 path를 적용한 경우:

```
intoss-private://appsintoss/path/pathpath?_deploymentId=0198c000-68c3-7d2b-0000-2c00000005ec
```

쿼리 파라미터를 적용한 경우 (queryParams는 반드시 URL 인코딩해야 해요):

```
intoss-private://appsintoss?_deploymentId=0198c000-68c3-7d2b-0000-2c00000005ec&queryParams=%7B%22categoryKey%22%3A%22
```

***

### 자주 묻는 질문

<details>

<summary>iOS에서 흰 화면이 보여요.</summary>

샌드박스에서는 정상 동작하는데 토스 앱에서 흰 화면이 보인다면, 아래 항목을 순서대로 점검해 보세요.

1. **Sentry로 오류 감지·모니터링하기** — 런타임 에러가 났는데 바로 확인되지 않는 경우가 있어요. Sentry로 에러를 수집해서, 실제 사용자 환경에서 생기는 오류를 추적해 보세요. [Sentry 설정 가이드](https://developers-apps-in-toss.toss.im/ai-vibe-coding/integration/sentry#id-1.-sentry)
2. **메모리·리소스 사용량 점검하기** — 토스 앱에서는 메모리 제약 때문에 앱이 제대로 렌더링되지 못하고 흰 화면이 보일 수 있어요.
   * 이미지·폰트 등 리소스 용량을 줄여 빌드 파일을 최적화하세요.
   * 분할 로딩 구조를 적용해, 처음에는 꼭 필요한 파일만 불러오고 나머지 리소스는 순차적으로 불러오도록 구성해 보세요.
   * 불필요한 객체 생성이나 메모리 누수가 없는지 점검하세요.

</details>

<details>

<summary>토스 앱에서 통신이 되지 않아요.</summary>

* **CORS 설정 확인** — Origin 허용 목록에 아래 도메인을 등록하세요.\
  SDK 버전에 따라 달라요.<br>

  SDK 3.x

  * `https://<appName>.web.tossmini.com` : 실제 서비스 환경
  * `https://<appName>.private-web.tossmini.com` : 콘솔 QR 테스트 환경

  \
  SDK 1.x \~ 2.x

  * `https://<appName>.apps.tossmini.com` : 실제 서비스 환경
  * `https://<appName>.private-apps.tossmini.com` : 콘솔 QR 테스트 환경
* **App Transport Security(ATS) 설정 확인** — 샌드박스에서는 HTTP 요청이 허용되지만, 라이브 환경에서는 HTTPS만 허용돼요. HTTP 기반 API는 토스 앱에서 차단돼요.
* **iOS 서드파티 쿠키 차단 정책 확인** — iOS·iPadOS 13.4 이상에서는 서드파티 쿠키가 완전히 차단돼요. 쿠키 기반 로그인 대신 토큰 기반 인증 방식을 적용하세요.

</details>

<details>

<summary>토스앱에서 미니앱이 열리지 않아요.</summary>

토스앱 하위 버전에서 오류가 발생할 수 있어요 최신 버전의 토스앱에서 테스트를 진행해 주세요.

</details>


# 미니앱 출시하기

토스 앱에서 최종 테스트를 마쳤다면, 앱인토스 콘솔에서 검토를 요청하고 미니앱을 출시할 수 있어요.

### 1. 사전 점검하기

#### 1-1) 게임, 비게임 출시 가이드

출시 전에 검수 가이드와 체크리스트를 꼭 확인하세요. [게임](https://developers-apps-in-toss.toss.im/checklist/app-game)·[비게임](https://developers-apps-in-toss.toss.im/checklist/app-nongame) 출시 가이드를 모두 확인해 개발이 끝났는지 점검해 주세요. 가이드를 지키지 않으면 검토 단계에서 반려될 수 있어요.

#### 1-2) 앱 번들 업로드 시 주의 사항

앱 번들은 압축 해제 기준 100MB 이하만 업로드할 수 있어요. 이미지·사운드·영상 같은 리소스를 모두 포함하면 용량을 넘길 수 있으니, 리소스 파일은 빌드와 분리해서 관리해 주세요.

리소스 관리는 이렇게 하는 걸 권장해요.

* 앱 실행에 꼭 필요한 최소 리소스만 번들에 포함하세요.
* 대용량 리소스는 외부 스토리지나 CDN에서 내려받도록 구성해 주세요.
* 추가 리소스는 단계적으로 내려받는 방식(Lazy Loading)을 적용하면 사용자 경험이 좋아져요.

<figure><img src="/files/ydZiOB3iAcKzbGz4uzRO" alt=""><figcaption></figcaption></figure>

***

### 2. 검토 요청하기

테스트를 마친 뒤 콘솔의 '검토 요청하기' 버튼을 누르면 검토가 시작돼요. 검토는 영업일 기준 최대 3일 걸리고, 앱 카테고리에 따라 7일 이상 걸릴 수 있어요.

* 테스트를 1번 이상 완료해야 검토 요청 버튼이 활성화돼요.
* 검토 요청은 한 번에 한 버전만 제출할 수 있어요.

검토를 요청한 뒤 수정할 버그를 발견했다면, '요청 취소하기' 버튼을 눌러 검토를 취소하세요. 그다음 수정한 새 번들(`.ait`)을 업로드하고 다시 검토를 요청하면 돼요.

<figure><img src="/files/pHKkPFT42jsF4iwgFDVu" alt=""><figcaption></figcaption></figure>

#### 검토가 반려된 경우

'반려 사유 보기' 버튼을 눌러 사유를 확인한 뒤, 문제를 해결한 새 번들을 업로드하고 다시 검토를 요청해 주세요. 반려 사유에 대해 더 궁금한 점이 있다면 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의할 수 있어요.

***

### 3. 출시하기

번들이 승인되면 검수 결과를 이메일로 안내해드려요. 이후 콘솔에서 '출시하기' 버튼을 누르면 미니앱이 사용자에게 공개돼요.

출시하면 전체 사용자에게 즉시 반영돼요. 반드시 충분히 테스트한 뒤 출시해 주세요.

<figure><img src="/files/E4WqUCa6nqOgyvvpVyMb" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**테스트 환경과 실제 환경의 차이**

테스트 환경과 실제 라이브 환경은 CORS 정책과 네트워크 동작이 다를 수 있어요. 특히 아래 기능은 실제 환경에서도 반드시 다시 확인해 주세요.

* 메모리·리소스 사용량
* 네트워크 요청(CORS): Origin 허용 목록에 아래 도메인을 등록해야 해요.\
  SDK 버전에 따라 달라요.<br>

  SDK 3.x

  * 실제 서비스 환경: `https://<appName>.web.tossmini.com`
  * QR 테스트 환경: `https://<appName>.private-web.tossmini.com`

  \
  SDK 1.x \~ 2.x

  * 실제 서비스 환경: `https://<appName>.apps.tossmini.com`
  * QR 테스트 환경: `https://<appName>.private-apps.tossmini.com`
* 권한 처리
* 로그인·세션 유지
* 실제 결제·인증 기능

테스트 환경에서는 정상이어도 실제 환경에서는 오류가 날 수 있어요.
{% endhint %}

***

### 4. 출시 후 관리하기

출시한 뒤에는 앱인토스 콘솔에서 버전 관리와 업데이트를 할 수 있어요. 필요에 따라 새 번들을 올리거나, 기존 버전을 유지·관리할 수 있어요.

#### 4-1. 새 버전(업데이트) 배포하기

* 새 기능을 추가하거나 오류를 수정했다면, 기존과 같은 방식으로 새 앱 번들(`.ait`)을 업로드하세요.
* 업로드한 번들은 다시 검토 요청 → 승인 → 출시 단계를 거쳐요.
* 승인된 뒤 '출시하기' 버튼을 누르면 새 버전이 기존 앱을 대체해요.

출시한 버전은 사용자에게 즉시 반영되니, 미리 충분히 테스트한 뒤 업로드해 주세요.

#### 4-2. 롤백하기

* '앱 출시' 메뉴에서 기존에 출시한 버전 목록을 확인할 수 있어요.
* 문제가 생기면 이전 버전으로 롤백할 수 있어요.
* 롤백할 버전을 선택한 뒤 '출시하기' 버튼을 누르세요.

롤백도 사용자에게 즉시 반영되니, 버전을 신중히 선택해 주세요.

<figure><img src="/files/UogbKB9iLy7evDu11MCa" alt=""><figcaption></figcaption></figure>

#### 4-3. 긴급 수정(핫픽스)

앱 실행에 심각한 오류가 생기면 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 바로 문의해 주세요. 긴급 상황에서는 빠르게 대응해드려요.

#### 4-4. 출시 후 모니터링

출시 직후에는 예상치 못한 오류나 성능 이슈가 생길 수 있어요. 아래 항목을 중심으로 모니터링하는 걸 권장해요.

* 주요 오류 로그와 크래시 로그
* Sentry 설정을 통한 Sentry 모니터링
* API 응답 지연·실패율
* 사용자 피드백과 사용성 문제
  * 내비게이션 바의 '신고하기' 기능으로 사용자 의견을 받을 수 있어요.
  * 접수된 피드백은 콘솔의 '신고 내역' 메뉴에서 확인할 수 있어요.
* 외부 리소스·CDN 로딩 지연이나 실패 이슈

#### 4-5. 사후 검수

앱인토스는 사용자에게 높은 수준의 미니앱 경험을 제공하기 위해, 출시한 뒤에도 미니앱 검수를 진행해요. 사후 검수에서 개선이 필요한 점이 확인되면 개선을 요청드릴 수 있어요. 법·정책 위반 사항이 발견되면 운영 정책에 따라 긴급 운영 중단 조치가 먼저 진행될 수 있어요.


# 사업자 등록하기

이 문서에서는 사업자 등록이 왜 필요한지, 어떻게 등록하는지를 안내해요.

사업자 등록은 필수는 아니에요.&#x20;

다만 수익화 기능인 인앱 광고, 인앱 결제, 토스페이, 프로모션, 비즈 월렛, 토스 로그인을 쓰려면 꼭 필요해요.

사업자는 개인 사업자와 법인 사업자 모두 가능해요. 먼저 [홈택스에서 사업자를 등록](https://toss.im/apps-in-toss/blog/business_registration)해 주세요.

***

### 사업자 등록 전 꼭 알아야 할 점

* 사업자 등록증에 적힌 업종과 미니앱에서 제공하는 서비스 업종이 같아야 해요. 업종이 다르면 미니앱 출시가 제한될 수 있어요.
* 사업자 정보는 앱인토스 콘솔 > 워크스페이스의 '내 정보' 메뉴에서 등록해요.
* 사업자 유형에 맞는 서류를 제출하고 검토를 요청하면, 영업일 기준 1\~2일 정도 걸려요.

<figure><img src="/files/kalCwAb4NlyuDkP9ft8h" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**꼭 확인해 주세요**

* 개인 사정 등으로 세무 업무를 할 수 없거나 세금계산서를 발행할 수 없는 사업자는, 관련 법령에 따른 세금 신고를 할 수 없어 정상적인 서비스 운영이 제한돼요.
* 부가가치세 면세 사업자는 앱인토스에 사업자 등록을 할 수 없어요.
* 위와 같은 사유가 서비스 출시 이후에 확인되면, 토스는 파트너사에 안내한 뒤 수익화 기능이나 서비스 운영을 중단하는 조치를 진행할 수 있어요.
  {% endhint %}

***

### 사업자 등록이 필요한 기능

아래 기능은 계약·결제·정산이 필요하거나 사용자 인증과 직접 연결돼 있어요. 그래서 개인 또는 법인 사업자 등록과 약관 동의가 꼭 필요해요.

* 토스 로그인: 토스 계정으로 사용자를 인증해요.
* 비즈니스 월렛: 마케팅 예산을 관리해요.
* 프로모션: 사용자의 특정 행동을 기준으로 토스 포인트를 지급해요.
* 인앱 광고: 앱 안에 광고를 노출하고 수익을 만들어요.
* 인앱 결제: 앱 안에서 상품이나 서비스를 판매해요.
* 토스페이: 토스 결제 수단으로 결제를 받아요.

***

### 법인 사업자 등록하기

법인 사업자는 기본적으로 아래 다섯 가지 서류를 제출해야 해요.

* 사업자 등록증, 사업자 등기부등본, 법인 인감증명서, 대리인 위임장, 대리인 신분증 사본

대표자 본인이 직접 등록하는 경우에는 **사업자 등록증과 사업자 등기부등본**만 제출하면 돼요.

<figure><img src="/files/xpdqlwvVUsxHCE6jF5Js" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/IVRQEdH067XatXLJfqoq" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
꼭 확인해 주세요

* 사업자 등기부등본은 **발급일 기준 3개월 이내** 서류만 인정돼요. 3개월이 지난 서류로는 사업자 인증이 되지 않아요.
* 공동 대표라면, 공동 대표자 모두의 위임장과 법인 인감증명서가 필요해요.
* 공동 대표 중 한 명만 신청하는 경우에도, 다른 대표자들의 위임장과 법인 인감증명서를 제출해야 해요.
  {% endhint %}

***

### 개인 사업자 등록하기

개인 사업자는 아래 네 가지 서류를 제출해 주세요.

* 사업자 등록증, 개인 인감증명서, 대리인 위임장, 대리인 신분증 사본

대표자 본인이 직접 등록하는 경우에는 **사업자 등록증**만 제출하면 돼요.

<figure><img src="/files/tMpmk3ciCe4ca2bODWRo" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/lbw6jt3ZPJp2x9wgco5x" alt=""><figcaption></figcaption></figure>


# 주요 기능 등록하기

주요 기능은 사용자가 비게임 미니앱 상세에서 특정 기능으로 바로 들어올 수 있게 해주는 기능이에요. 홈 화면을 거치지 않고 원하는 세부 기능으로 바로 이동해요.

<figure><img src="https://4036587353-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fsueslih0uoBKTKHdmDNt%2Fuploads%2FN3pIHX8o5UGrsc93anOL%2Fimage.png?alt=media&#x26;token=6db8f8b2-504e-4329-98fa-65549eba00d2" alt=""><figcaption></figcaption></figure>

***

### 콘솔에서 등록하기

#### 1. 앱 출시 메뉴의 '주요 기능' 탭 들어가기

<figure><img src="/files/EyOi5ohsXWz480vi4oys" alt=""><figcaption></figcaption></figure>

#### 2. 주요 기능 입력하기

주요 기능은 최대 3개까지 등록할 수 있어요.

<figure><img src="/files/IZeeaDbFb6t108auPrgq" alt=""><figcaption></figcaption></figure>

**주요 기능은 '한국어 기능 이름'과 '영어 기능 이름'을 함께 입력해야 해요.**

* **한국어 기능 이름:** 10자 이내, 특수문자는 `:` `.` 만 허용, 이모지 사용 불가
* **영어 기능 이름:** 15자 이내, 첫 글자만 대문자, 특수문자는 `:` `.` 만 허용, 이모지 사용 불가

기능 이름은 토스 UX 라이팅에 따라 '\~하기'나 명사형으로 작성해 주세요.

* 예) '송금 내역 확인하기', '해외 송금하기', '여행 예약 내역 확인하기'
* 미니앱 서비스의 기능이 드러나게 작성해 주세요. '예약 확인하기', '내역 확인', '보러가기', '시청하기'처럼만 쓰면, 무슨 서비스의 기능인지 사용자가 알기 어려워요.

**사용자가 바로 접속할 기능 이름과 이동할 `intoss://pages` 주소를 입력해 주세요.**

* 주요 기능 URL에 정상 접속되지 않으면 반려될 수 있어요.
* 쿼리 파라미터를 설정할 수 있어요.

모두 입력했다면 '검토 요청하기'를 눌러주세요. 검토에는 영업일 기준 1\~2일이 걸릴 수 있어요.

***

### 주요 기능 정보 확인하기

검토 결과는 '주요 기능' 탭에서 확인할 수 있어요.

* '주요 기능' 탭에서 주요 기능만 따로 등록하거나 수정할 수 있어요.
* 주요 기능만 따로 추가·수정하면 주요 기능만 검토하기 때문에, 영업일 기준 1\~2일이 걸릴 수 있어요.
* 이미 운영 중인 미니앱에서 새로 추가하거나 수정할 때도, 주요 기능에 정상 접속되는지 꼭 확인해 주세요.

<figure><img src="https://4036587353-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fsueslih0uoBKTKHdmDNt%2Fuploads%2FniQbAEzUpb1jG5p7V2yl%2Fimage.png?alt=media&#x26;token=699de974-b97c-4fb4-b5fc-27c90b7caa9b" alt=""><figcaption></figcaption></figure>

***

### 개발 환경별 피처 구성

#### Webview

라우터 경로를 피처 주소와 매핑해요.

```
<Route path="/search" element={<SearchPage />} />
```

피처 주소를 `intoss://{appName}/search`로 입력하면 해당 페이지로 이동할 수 있어요.

#### React Native

Next.js와 비슷한 파일 기반 라우팅을 써요.

`/pages/search.tsx` → `/search` 경로 매핑 → `intoss://{appName}/search`로 진입 시 렌더링

자세한 구조는 [파일 기반 라우팅 이해하기](https://appsintoss.gitbook.io/appsintoss-docs/documentation/react-native/screen-navigation/navigation)를 참고하세요.


# 대표관리자 변경하기

### 1. 워크스페이스 '멤버' 탭 누르기

대표관리자를 바꾸려면, 지금 대표관리자가 본인 이름 오른쪽의 '**권한 위임'** 버튼을 눌러주세요.

<figure><img src="/files/cXiTiP5PEe84FxPyIcvU" alt=""><figcaption></figcaption></figure>

### 2. 위임할 멤버 선택하기

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FLb2zmegc3QDSzbgJzNUm%2Fimage.png?alt=media&#x26;token=fc595a03-c508-41a4-b3e2-cd38304ae33b" alt=""><figcaption></figcaption></figure>

<details>

<summary>[권한 위임] 버튼이 비활성화되어 있어요</summary>

아래 경우에는 버튼이 비활성화돼요. 모두 끝난 뒤에 다시 시도해주세요.

* 이미 대표관리자 위임 절차가 진행 중일 때
* 사업자 정보 확인이 아직 끝나지 않았을 때

</details>

***

### 위임 절차

#### 1) 워크스페이스에 사업자 정보가 등록되어 있나요?

사업자 정보를 등록한 적이 없다면, 추가 확인이 필요 없어요. 바로 위임되거나 약관 동의만으로 마무리돼요.

사업자 정보가 이미 등록되어 있다면, 권한을 위임받을 사람이 같은 회사 소속이 맞는지 확인해야 해요. 안내 이메일의 \[사업자 정보 등록하기] 버튼을 눌러 콘솔로 이동한 뒤 정보를 제출해주세요.

* **대표자라면**: 기존 서류로 확인할 수 있어서 추가로 제출하지 않아도 돼요.
* **대리인이라면**: 인감증명서, 위임장, 신분증 사본이 필요해요.

<figure><img src="/files/DMJZxeGhcPTWf5YvfYKc" alt=""><figcaption></figcaption></figure>

> **사업자 인증이 반려됐어요**
>
> 대표관리자 위임 중 사업자 인증이 반려됐다면, 반려 사유를 확인한 뒤 권한을 위임받을 사람이 직접 다시 제출해야 해요. 반려 사유는 이메일과 위 '파트너 정보' 메뉴에서 확인할 수 있어요.

#### 2) 이전 대표관리자가 약관에 동의한 적이 있나요?

* 동의한 적이 없다면, 추가 절차 없이 바로 위임이 끝나요.
* 동의한 적이 있다면, 권한을 위임받을 사람이 이메일로 약관에 동의해야 위임이 끝나요.

<figure><img src="/files/yHV1sdMjgKFQo07TSqII" alt=""><figcaption></figcaption></figure>


# 미성년자 참여하기

### 미성년자의 앱인토스 콘솔 워크스페이스 참여 방법

앱인토스 콘솔 워크스페이스에 참여하려면 먼저 **토스 비즈니스 회원**으로 가입해야 해요. 토스 비즈니스 회원으로 가입하려면 **본인 명의로 개통한 휴대폰으로 토스 앱에 가입**되어 있어야 해요. 아직 토스 비즈니스 회원이 아니라면, 먼저 가입을 완료해 주세요.

### 워크스페이스 생성 가능 연령

앱인토스 콘솔의 워크스페이스는 **만 19세 이상만 직접 만들 수 있어요.**

* 만 19세 이상이라면 직접 워크스페이스를 생성할 수 있어요.
* 만 19세 미만이라면 기존에 만들어진 워크스페이스에 초대를 받아 참여할 수 있어요. 만 19세 미만 사용자가 콘솔에 접속했을 때 워크스페이스를 생성할 수 없다는 안내 화면이 보인다면, 직접 생성이 제한된 상태라는 뜻이에요.

<figure><img src="/files/VtRSRFfA8H6AFp7YSKDz" alt=""><figcaption></figcaption></figure>

### 미성년자 참여 절차

만 19세 미만 사용자가 워크스페이스에 참여하려면 다음 절차를 따라야 해요.

**1. 관리자가 미성년자 회원을 워크스페이스에 초대해요.**

<figure><img src="/files/xeOtjIrgEInrk6EmsFVG" alt=""><figcaption></figcaption></figure>

**2. 대표 관리자와 미성년자 회원 모두 확약서에 동의해요.**

초대가 완료되면 다음 메일이 각각 발송돼요. 메일 본문 아래에 있는 \[확약서 동의하러 가기] 버튼을 눌러 확약서에 동의해 주세요.

* 대표 관리자에게 발송되는 ‘대표 관리자 미성년자 보호·감독 책임 확약서’
* 미성년자 회원에게 발송되는 ‘미성년자 워크스페이스 이용 확약서’

<figure><img src="/files/JeJxY5Yrgb4mP3kfyMcT" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/uoY0C5uIA4lRouOVAgHQ" alt=""><figcaption></figcaption></figure>

**3. 대표 관리자와 미성년자 모두 동의하면 워크스페이스에 접속할 수 있어요.**

두 사람 중 한 명이라도 동의하지 않으면 미성년자는 워크스페이스에 접속할 수 없어요.

메일을 받지 못했거나 동의 과정에서 오류가 발생하면 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의해 주세요. 문의할 때 사용 중인 계정과 현재 보이는 화면 상태를 함께 적어 주면 더 빠르게 도움을 받을 수 있어요.

* 운영 시간: 평일 오전 10시부터 오후 5시까지
* 점심시간: 오후 1시부터 2시까지


# 긴급 점검 설정하기

파트너사는 콘솔에서 미니앱 서비스 점검 일정을 사전에 설정하여, 점검 중인 미니앱에 진입하는 사용자에게 미니앱 서비스 점검 안내를 노출할 수 있어요.

점검 기능을 사용할 경우 유저는 미니앱 접속 시 안내 화면을 통해 점검 상태를 확인할 수 있어요.

{% hint style="info" %}
**꼭 확인해 주세요.**

* 점검 안내는 점검 시작 전에 미리 설정해 주세요.
* 점검 종료 후에는 반드시 상태를 '취소'로 변경해 주세요. 미니앱이 정상화되었음에도 점검 안내가 계속 노출될 수 있어요.
* 점검 중일때는 최신 버전의 출시 검토 요청은 불가해요.
  {% endhint %}

{% hint style="info" %}
**이미 플레이 중인 유저에 대한 처리**

현재 점검 설정은 미니앱 **진입 시점**에만 점검 안내를 노출해요. 이미 미니앱을 이용 중인 유저에 대해서는 앱인토스에서도 상태를 파악할 수 없어요.

따라서 점검이 필요한 경우, **사전 공지 및 별도 조치**가 필요해요.

자체 서버와의 통신이 끊어지는 경우, 클라이언트에서도 알럿을 노출하고 미니앱을 종료하는 로직을 구현하는 것을 권장해요. 미니앱 종료는 closeView를 참고해 주세요.
{% endhint %}

### **긴급 점검 설정 접속**

* **접속 방법:** 앱인토스 콘솔 → 워크스페이스 선택 → 미니앱 선택 → 앱 정보 → 우측 상단  **`긴급 점검 안내하기`**

  <figure><img src="/files/c6sUTivuJPUQCGQwtL8y" alt=""><figcaption></figcaption></figure>

### **긴급 점검 일정 입력하기**

* **점검 시작 일시 / 종료 일시:** 점검이 진행되는 기간을 정확히 설정해 주세요.

<figure><img src="/files/o2awuR9NFF0AKvazZuzp" alt=""><figcaption></figcaption></figure>

### **긴급 점검 일정 종료 및 수정하기**

* 설정된 점검 설정 메뉴를 다시 클릭하면 점검 일정을 취소할 수 있어요.
* 취소하면 사용자에게 노출되던 점검 안내가 즉시 사라져요.
* 일정 연장이 필요할 경우에도 ‘긴급 점검 예정’ 버튼을 눌러 일정을 변경해 주세요.

<figure><img src="/files/sZETjoFdv9fdgFqklPh9" alt=""><figcaption></figcaption></figure>

### **점검 관련 미니앱 노출 화면**

긴급 점검이 시작 되면, 서비스 접속 시 **‘서비스 점검 중이에요’** (좌측 화면)이 노출돼요. \
이 화면에서 점검 종료에 대한 알림 동의 여부를 선택할 수 있고, 알림 받기를 진행한 유저에게는 긴급 점검이 종료 된 후 자동으로 푸시/알림이 발송돼요. (우측 화면) \
토스앱 5.253.0 버전부터 적용돼요.

<figure><img src="/files/qMR5XiBHyKWQgH1Q12zv" alt=""><figcaption></figcaption></figure>


# 서비스 종료하기

앱인토스에서 미니앱 서비스 종료는 파트너사의 자발적 요청 또는 법, 정책 위반 시 진행돼요. 아래의 종료 유형별 프로세스, 고지 의무, 환불 처리 방식, 종료 후 화면 처리를 확인하여 필히 준수해 주세요.

***

### 1. 서비스 종료 유형

서비스 종료는 아래 유형으로 구분해요.

| 유형                 | 설명                                | 사전 고지 의무                    | 환불 및 탈퇴 처리 |
| ------------------ | --------------------------------- | --------------------------- | ---------- |
| **파트너사 요청에 의한 종료** | 파트너사가 직접 서비스 종료를 요청하는 경우          | 30일 전 사전 고지 (파트너사 → 유저)     | 파트너사 처리    |
| **정책/법 위반에 의한 종료** | 정책·법규 위반에 따른 즉시 종료 (개선 미이행 종료 포함) | 선 종료 후 30일 간 고지 (파트너사 → 유저) | 파트너사 처리    |
| **사업자 폐업에 따른 종료**  | 사업자 폐업에 따른 즉시 종료                  | 선 종료 후 30일 간 고지 (파트너사 → 유저) | 파트너사 처리    |

***

### 2. 종료 유형별 프로세스

**2-1. 파트너사 자발적 종료 요청 시 프로세스**

1. 앱인토스 [채널톡](https://apps-in-toss.channel.io/workflows/787658)을 통해 서비스 종료에 대한 의사를 말씀해 주세요.
2. 파트너사에서는 서비스 이용 유저에게 종료 예정일로부터 **30일 전 서비스 종료에 대한 미니앱 내 사전 고지 및 기능성 메시지 발송**이 필요해요.
   * 고지되는 화면 및 기능성 메시지에는 서비스 종료 예정일, 고객센터 문의처를 고지해야 해요.
   * 관련 내용 반영 후 채널톡으로 문의해 주세요.

<figure><img src="/files/TDGFvZsKpsgLSo34QFCk" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**꼭 확인해 주세요**

토스 로그인을 사용하지 않으면 기능성 메시지를 발송할 수 없어요. 유저가 서비스 접속 시 종료 안내를 확인할 수 있도록 미니앱 내에 필수로 고지해 주세요.
{% endhint %}

3. 파트너사에서는 이후 인앱 결제(또는 토스페이) 환불 요청 건을 즉시 이행하고, 유저의 탈퇴 요청 시 즉시 이행해 주세요.
   * 유저가 서비스 탈퇴 및 관련 정보 삭제를 요청하면 지체 없이 파기해야 해요.
   * 인앱결제 및 토스페이 환불 요청 건은 지체 없이 처리해야 해요.
4. 서비스 종료일에 맞춰 채널톡으로 종료 요청을 진행해 주세요.
5. 이후 앱인토스 콘솔 관련 약관 동의 철회가 진행돼요.

**2-2. 정책/법규 위반에 따른 개선 미이행 시 종료**

1. 위반 정책에 따라 개선 기간 및 서비스 종료에 대해 파트너사에 안내해요.
2. 개선 기간 내 미이행 시, 서비스 종료 후 사후 고지 및 기능성 메시지가 발송돼요.
   * 고지되는 화면 및 기능성 메시지에는 **서비스 종료일 및 서비스 종료 사유, 고객센터 문의처**가 고지돼요.
3. 파트너사에서는 이후 인앱 결제(또는 토스페이) 환불 요청 건을 즉시 이행하고, 유저의 탈퇴 요청 시 즉시 이행해 주세요.
   * 유저가 서비스 탈퇴 및 관련 정보 삭제를 요청하면 지체 없이 파기해요.
   * 인앱결제 및 토스페이 환불 요청 건은 지체 없이 처리해요.
4. 이후 앱인토스 콘솔 관련 약관 동의 철회가 진행돼요.

**2-3. 법규 위반 등에 따른 즉시 종료**

{% hint style="info" %}
**참고해 주세요**

법규 위반, 지적재산권 침해 등 중대 위반의 경우 계도 기간 없이 즉시 서비스를 미노출해요.
{% endhint %}

1. 위반 사항 확인 즉시 서비스 종료 후 사후 고지 및 기능성 메시지가 발송돼요. (종료 후 30일 간)
   * 고지되는 화면 및 기능성 메시지에는 **서비스 종료일 및 서비스 종료 사유, 고객센터 문의처**가 고지돼요.
2. 파트너사에 관련 법령 위반으로 인한 서비스 종료 처리됨을 통보하고, 콘솔에서도 확인할 수 있어요.
3. 파트너사에서는 이후 인앱 결제(또는 토스페이) 환불 요청 건을 즉시 이행하고, 유저의 탈퇴 요청 시 즉시 이행해 주세요.
   * 유저가 서비스 탈퇴 및 관련 정보 삭제를 요청하면 지체 없이 파기해요.
4. 이후 앱인토스 콘솔 관련 약관 동의 철회가 진행돼요.

**2-4. 사업자 폐업에 따른 종료**

1. 사업자를 폐업하게 될 경우 비사업자 신분이 되므로 수익화 기능 및 토스로그인 기능 사용이 불가해요.
2. 이에 따라 폐업 시점이 확인 되는 경우 운영 중인 미니앱은 자동 종료 처리 돼요.
3. 서비스 종료 처리 될 경우 파트너사에서는 이후 인앱 결제(또는 토스페이) 환불 요청 건을 즉시 이행하고, 유저의 탈퇴 요청 시 즉시 이행해 주세요.
   * 유저가 서비스 탈퇴 및 관련 정보 삭제를 요청하면 지체 없이 파기해요.
4. 만약, 비사업자로 서비스 출시를 희망할 경우 새로운 워크스페이스를 생성해서 미니앱을 다시 등록하여 출시해 주세요.

***

### 3. 토스페이 및 인앱 결제 환불 처리

**3-1. 환불 책임 원칙**

토스페이 및 인앱 결제의 환불 의무와 책임은 **파트너사**에 있어요. 앱인토스는 원칙적으로 환불 처리에 직접 관여하지 않아요.

* 서비스 종료 고지 시, 파트너사에게 환불 의무를 명확히 안내해요.
* 환불 민원 건이 발생할 경우 앱인토스가 파트너사에 개별 연락하여 환불 처리를 요청할 수 있어요.

**3-2. 구글 플레이스토어 환불 처리**

{% hint style="info" %}
**참고해 주세요**

환불 가능 여부(재화·서비스 사용 완료 여부)는 앱인토스에서 직접 확인이 불가하므로, 파트너사가 직접 처리해 주세요.
{% endhint %}

* 결제 후 **48시간 이내** 건: 구글 플레이스토어에서 판단에 따라 환불 처리될 수 있어요.
* 결제 후 **48시간 이후** 건: 파트너사가 앱인토스 콘솔에서 직접 환불 승인/거절 처리가 필요해요.

**3-3. 애플 앱스토어 환불 처리**

별도 기한 없이 애플 앱스토어에서 판단에 따라 환불 처리될 수 있어요.

***

### 4. 토스 로그인

**4-1. 토스 로그인 처리**

* 서비스 접속 시 화면을 띄워 **회원 탈퇴 창구**를 안내하며, 사용자가 서비스 탈퇴 및 관련 정보 삭제를 요청하면 지체 없이 파기해요.
* 관련 커뮤니케이션 및 사용자와의 이슈 발생 시 책임은 **파트너사**에 있어요.

***

### 5. 앱인토스 관련 약관 동의 철회

**5-1. 약관 동의 철회**

* 서비스 종료 이후, **콘솔 내 기능별 약관 및 서비스 제휴와 관련하여 동의를 철회**하는 방식으로 운영해요.
* 토스 로그인, 인앱 결제 등 기능별로 콘솔에서 약관 동의가 관리되므로, 종료 후 순차적으로 철회돼요.


# 수익화

미니앱에서 결제와 광고를 통해 수익을 만드는 기능을 안내해요. 인앱 결제, 인앱 광고, 토스페이 연동에 필요한 설정과 운영 방법을 확인할 수 있어요.

* [인앱 결제](/guide/monetization/in-app-payment)
* [인앱 광고](/guide/monetization/in-app-ad)
* [토스페이](/guide/monetization/toss-pay)


# 인앱 결제

앱인토스의 인앱 결제를 연동해 디지털 상품과 권한, 콘텐츠를 손쉽게 판매해 보세요. 구매 흐름을 짧게 만들면 사용자는 더 쉽게 결제하고, 매출은 빠르게 늘릴 수 있어요.

### 인앱 결제란

인앱 결제는 앱 안에서 유료 상품을 바로 구매할 수 있는 결제 방식이에요. 사용자는 앱을 떠나지 않고도 필요한 기능이나 아이템, 콘텐츠를 결제할 수 있어요. 인앱 결제 상품은 소모성 아이템과 비소모성 아이템으로 나뉘어요.

* **소모성 아이템:** 사용하면 사라지는 상품이에요. 다시 쓰려면 다시 구매해야 해요. (예: 게임 아이템, 코인, 힌트 이용권)
* **비소모성 아이템:** 한 번 구매하면 계속 쓸 수 있는 상품이에요. (예: 프리미엄 기능 해제, 광고 제거, 특정 콘텐츠 이용 권한)

<figure><img src="/files/0HmDoLASWQhaKMfwcyDv" alt=""><figcaption></figcaption></figure>

***

### 인앱 결제의 좋은 점

* 사용자가 앱을 벗어나지 않고 바로 결제할 수 있어, 결제 중 이탈을 줄일 수 있어요.
* 앱 출시 초기부터 유료 아이템이나 구독 상품을 판매해 바로 수익을 만들 수 있어요.
* 소모성 아이템과 비소모성 아이템을 함께 구성해 다양한 결제 모델을 만들 수 있어요.
* 사용 목적에 맞는 상품을 제공해 매출을 더 크게 키울 수 있어요.

{% hint style="info" %}
**참고해 주세요**

* 인앱 결제 환불은 Apple과 Google의 정책을 따라요.
* 판매가는 공급가에 부가가치세(VAT)가 더해진 금액이에요.
* 결제가 진행되는 동안 앱 안의 기능(음악·영상 재생 등)은 잠시 멈추고, 결제가 끝나면 자동으로 다시 이어지도록 처리해 주세요.
  {% endhint %}

***

### 콘솔에서 설정하기

#### 1) 사업자 정보 등록하기

인앱 결제를 연동하려면 먼저 사업자 정보를 등록해야 해요. 사업자 정보가 등록되어 있어야 다음 단계인 약관 동의와 정산 정보 입력을 진행할 수 있어요. 등록 방법은 다음 [가이드](https://developers-apps-in-toss.toss.im/guide/operation/register-business)를 참고해 주세요.

#### 2) 정산 정보 입력하기

인앱 결제 수익을 정산받으려면 [정산 정보를 등록](https://developers-apps-in-toss.toss.im/guide/settlement)해야 해요. 워크스페이스의 '정보' 탭에서 정산 정보를 입력한 뒤 검토를 요청해 주세요. 검토에는 영업일 기준 평균 2\~3일이 걸려요.

#### 3) 인앱 상품 등록하기

상품 정보를 정확하게 입력하면 사용자가 구매 내용을 명확히 이해할 수 있고, 운영 중 분쟁이나 환불 이슈를 줄일 수 있어요.

등록할 수 있는 상품 수에는 제한이 있어요. 게임 미니앱은 최대 80개, 비게임 미니앱은 최대 30개예요.

<figure><img src="/files/ve6rErcWYrEym316oY43" alt=""><figcaption></figcaption></figure>

**상품 유형**

상품의 사용 방식에 맞게 유형을 선택해 주세요. 현금성·환가성 상품이나 토스 포인트와 결합해 제공하는 상품은 판매할 수 없어요.

* 소모품: 사용하면 소진되는 상품이에요. 다시 쓰려면 재구매해야 해요. (예: 게임 아이템, 내부 재화 충전, 1회 이용권)
* 비소모품: 한 번 구매하면 계속 쓸 수 있는 상품이에요. (예: 광고 제거, 소장형 콘텐츠)
* 자동 갱신 구독: 정해진 주기마다 자동으로 결제되고, 취소 전까지 계속 이용할 수 있어요. (예: 월간 멤버십, 정기 콘텐츠 구독)

**상품명**

* 상품명은 사용자가 받는 기능과 조건을 그대로 드러내야 해요.
* 실제 제공 내용과 일치하게 작성해 주세요.
* 과장하거나 오해를 부르는 표현은 쓸 수 없어요. 예를 들어 이용 기간이 정해져 있는데 "무제한"이라고 쓰면 안 돼요.

**상품 이미지**

* 사용자가 상품을 직관적으로 이해할 수 있게 구성해 주세요.
* "30일 이용권", "100코인"처럼 식별에 필요한 텍스트를 넣을 수 있어요.
* 이벤트성 문구를 넣는다면, 반드시 이벤트 기간을 함께 표시해 주세요.
* 해상도는 1024 × 1024px로 등록해야 해요.
* 저작권 문제가 없는 이미지만 써야 하고, 이미지는 파트너사가 직접 확보해야 해요.
* 선정적이거나 폭력적이거나 불쾌감을 줄 수 있는 이미지는 쓸 수 없어요.

**공급가**

* 공급가는 부가가치세(VAT)가 빠진 금액이에요.
* 최소 400원부터 최대 1,400,000원까지 설정할 수 있어요.
* 10원 단위로만 입력할 수 있어요.
* 공급가를 입력하면 판매가는 자동으로 계산돼요. 판매가를 먼저 입력해 공급가를 자동 계산하는 기능은 지금은 지원하지 않아요.

**판매가**

판매가는 사용자가 실제로 결제하는 최종 금액이에요. 공급가에 부가가치세가 더해진 금액으로 자동 설정돼요.

**소모품 할인**

소모품 유형의 인앱 상품에는 할인 혜택을 설정할 수 있어요. 콘솔의 인앱 상품 목록에서 노출 중인 소모품 상품에 표시되는 '+ 할인' 버튼을 눌러 할인 정보를 등록해 주세요. 할인을 설정할 때는 할인된 공급가, 할인 기간, 할인 대상을 입력해요. 할인 설정을 완료한 뒤에는 수정할 수 없어요.

할인 대상은 세 가지예요.

* 전체 사용자: 결제 이력과 관계없이 할인을 적용해요.
* 결제 이력이 없는 사용자: 한 번도 구매한 적 없는 사용자에게 할인을 적용해요.
* 결제 이력이 있는 사용자: 구매 이력이 있는 사용자에게 할인을 적용해요.

<figure><img src="/files/Mlw5GUFSlfTJ33HXAY33" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/V41gP9YhwHUA5AbVIvHS" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**참고해 주세요**

* 할인된 상품을 이미 구매한 사용자는 할인 대상에 해당해도 추가 할인을 받을 수 없어요.
* 환불된 결제도 구매 이력에 포함돼요.
* 할인 상품도 기존 인앱 결제 SDK로 구매할 수 있어요. 별도 SDK 연동 변경은 필요하지 않아요.
  {% endhint %}

**자동 갱신 구독 전용 설정**

자동 갱신 구독 유형을 선택하면 아래 항목을 추가로 설정할 수 있어요.

* **자동 갱신 주기:** 결제가 반복되는 간격을 선택해요. (매주: 7일마다 / 매월: 30일마다 / 매년: 365일마다 자동 결제)
* **무료 체험:** 일정 기간 결제 없이 정기 결제 혜택을 이용할 수 있어요. 무료 체험이 끝나면 자동으로 유료 결제가 진행돼요. 기간은 3일·1주·2주·1개월 중 선택해요.
* **신규 구독 할인:** 처음 정기 결제를 시작하는 사용자에게 일정 기간 할인된 가격으로 제공해요. 할인 기간과 할인 공급가를 입력하면 할인 판매가가 자동으로 계산돼요.
* **재구독 할인:** 이전에 구독을 해지한 사용자가 재구독할 때 할인된 가격으로 제공해요. 할인 기간과 할인 공급가를 입력하면 할인 판매가가 자동으로 계산돼요.

<figure><img src="/files/kdzy9OeYRjHs4Ux1OOCJ" alt=""><figcaption></figcaption></figure>

#### 4) 결제 알림 URL 등록하기

구독 갱신·해지 등 결제 상태가 바뀔 때 알림을 받을 URL을 등록할 수 있어요. URL을 등록하면 상태가 바뀔 때 그 URL로 HTTP 요청이 전달돼요.

* 결제 알림 URL: 결제 상태 변경 알림을 받을 서버 URL을 입력해 주세요.
* Basic Auth 헤더: 선택적으로 Basic Auth 헤더 값을 입력할 수 있어요. 입력하면 알림 요청의 HTTP 헤더에 아래처럼 포함돼요.

```
Authorization: Basic {Basic Auth 헤더 값}
```

<figure><img src="/files/l40dwvjl9ji4yqdXzL0x" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/mkvpBVWDp2WCSkg1PyhI" alt=""><figcaption></figcaption></figure>

#### 5) 결제·환불 내역 확인하기

<figure><img src="/files/ulHbDGGqXXy6y6K7NHYj" alt=""><figcaption></figcaption></figure>

**결제 내역**

사용자가 미니앱에서 결제한 상품 내역이에요.

* 지급완료: 사용자에게 구매 상품까지 모두 제공됐다는 뜻이에요.
* 결제 완료: 결제는 됐지만 구매 상품이 아직 지급되지 않은 상태예요.

**환불 내역**

사용자가 환불을 요청한 내역이에요. 환불 요청 사유를 확인하고 요청을 반려하거나 승인할 수 있어요. 인앱 결제로 결제한 사용자는 토스 앱에서 '환불받기' 버튼을 눌러 환불을 요청할 수 있어요.

환불 상태는 아래와 같아요.

* 환불 요청 승인: 파트너사가 환불 요청을 승인한 내역이에요. 승인하면 앱마켓에 심사가 요청돼요.
* 환불 요청 반려: 파트너사가 환불 요청을 반려하거나, 앱마켓에서 환불을 반려한 내역이에요.
* 환불 완료: 앱마켓에서 환불이 완료된 내역이에요.

<figure><img src="/files/ZXMFWe317BilQHcAfL2H" alt=""><figcaption></figcaption></figure>

**마켓별 환불 정책**

인앱 결제 환불은 사용자의 운영체제(OS)와 마켓 정책에 따라 처리 방식이 달라요.

<details>

<summary>Android</summary>

* 사용자는 토스 앱에서 직접 환불을 요청해요.
* 파트너사는 앱인토스 콘솔의 '환불 내역' 메뉴에서 요청 건을 확인하고 승인이나 반려를 처리할 수 있어요.
* 단, 최종 승인·거절 여부는 Google Play가 결정해요.
* 환불 처리 결과는 사용자에게 푸시 알림으로 발송되고, 사용자는 주문 상세 화면에서도 확인할 수 있어요. 파트너사나 Google Play가 환불을 거절한 경우에도 똑같이 알림이 발송돼요.
* 파트너사는 내부 정책과 Google Play 정책을 함께 검토한 뒤 환불 요청을 처리해 주세요.

</details>

<details>

<summary>iOS</summary>

* iOS 사용자의 환불은 Apple이 전적으로 관리해요. 모든 환불을 Apple이 직접 판단하고 승인해요.
* 파트너사는 환불을 승인하거나 거절할 수 없어요. Apple이 환불 요청 기능을 외부에 제공하지 않기 때문이에요.
* 파트너사는 결제 상태 조회 API로 상태만 확인할 수 있어요.

</details>

***

### 개발 연동하기

상품 목록 조회, 일회성 결제 요청 등 [개발 연동 방법](https://developers-apps-in-toss.toss.im/documentation/api/iap#undefined)을 확인할 수 있어요.

결제 상태 조회 [개발 연동 방법](https://developers-apps-in-toss.toss.im/documentation/api/iap)을 확인할 수 있어요.

***

### 성과 확인하기

개발과 연동을 마친 인앱 결제의 성과 지표를 한 화면에서 확인할 수 있어요. 매출 흐름과 사용자의 결제 행동을 함께 분석해 상품 구성과 운영 전략을 개선해 보세요. 데이터는 D+1 오전 8시 이후부터 순차적으로 업데이트돼요.

* 매출 지표: 총 결제 금액, 총 매출, 결제자당 평균 매출, 결제 사용자당 매출
* 사용자 지표: 활성 사용자 수, 결제 사용자 수, 신규 결제 사용자 수, 재결제 사용자 수, 결제율, 최종 전환율

<figure><img src="/files/dR5Lq4gcntCV0MmoVtIF" alt=""><figcaption></figcaption></figure>

***

### 자주 묻는 질문

<details>

<summary>인앱 결제 수수료는 어떻게 되나요?</summary>

인앱 결제 수수료는 앱마켓 수수료 15% (향후 매출에 따라 변동 가능) + 토스 수수료 5%가 적용돼요.

자세한 내용은 정산 이해하기 > 인앱 결제 항목을 확인해 주세요.

</details>

<details>

<summary>인앱 결제 테스트를 하고 싶어요.</summary>

인앱 결제는 샌드박스에서 테스트할 수 있어요. 가이드를 확인해 주세요.

</details>

<details>

<summary>사용자가 환불을 희망할 경우 어떻게 해야 하나요?</summary>

iOS 사용자의 경우 애플 고객센터로 안내해 주세요. (애플에 모든 권한이 있어요.)

안드로이드 사용자의 경우 가이드처럼 토스 앱 내에서 환불 신청을 할 수 있게 안내해 주세요.

</details>


# 인앱 광고

인앱 광고는 유료 결제 없이도 앱에서 수익을 만드는 가장 빠른 방법이에요. 앱인토스에 광고를 등록하면 별도의 결제 기능 없이도 앱 출시 첫날부터 수익을 만들 수 있어요. 이 문서에서는 광고 수익이 어떻게 만들어지는지, 어떤 광고를 어디에 배치하면 좋은지를 쉽게 설명해요.

### 인앱 광고란

인앱 광고는 앱 화면 안에서 사용자에게 노출되는 광고예요. 서비스 흐름을 방해하지 않으면서 자연스럽게 보여주고, 그 노출로 수익을 만들어요.

광고 수익은 두 가지로 정해져요.

* 얼마나 많이 보여줬는지 **(노출 수)**
* 한 번 보여줄 때 얼마를 버는지 **(eCPM)**

<details>

<summary>노출 수</summary>

광고가 사용자 화면에 실제로 나타난 횟수예요. 많을수록 수익 기회가 늘지만, 너무 많으면 사용자 경험이 나빠질 수 있어요.

</details>

<details>

<summary>eCPM</summary>

광고 1,000회 노출당 수익이에요. 높을수록 수익이 크고, 광고 유형과 사용자 집중도에 영향을 받아요. 사용자가 집중해서 볼수록 광고 효과가 커져 단가가 올라가고, 집중도가 낮으면 단가가 내려가요.

</details>

이 둘을 곱하면 예상 수익이 나와요.

`예상 수익 = 노출 수 × eCPM ÷ 1,000`

예를 들어 오늘 광고가 100,000번 노출됐고 eCPM이 5,000원이면, 100,000 × 5,000 ÷ 1,000 = 500,000원이에요.

***

### 인앱 광고 유형

아래 세 가지 광고를 함께 쓸 때 가장 효과적이에요. 하나만 쓰는 것보다 조합해서 쓰면 전체 수익이 크게 늘어나요.

#### 전면형 광고

화면 전환 시점에 전체 화면으로 나타나는 광고예요.

* 특징: 강제 노출(사용자가 반드시 보게 돼요)
* 추천 위치: 화면 이동 직전·직후 (예: 결과 화면에서 다음 단계로)
* 노출 수: 중간
* eCPM: 중간
* 적합한 상황: 단계가 끊기는 지점(레벨 완료, 예약 완료 등), 사용자가 자연스럽게 멈추는 순간

<figure><img src="/files/sxYePFuRrLem2cLdZKZJ" alt=""><figcaption></figcaption></figure>

#### 리워드 광고

사용자가 직접 '광고 보기'를 선택하면 재생되는 광고예요.

* 특징: 자발적 시청, 높은 집중도
* 추천 위치: 보상이 필요한 순간 (예: 추가 혜택, 이어하기)
* 노출 수: 낮음
* eCPM: 가장 높음
* 적합한 상황: 포인트 지급, 기능 추가 제공, '광고 보고 혜택 받기' 구조

<figure><img src="/files/yyywITIcAi3b8P2NjDlR" alt=""><figcaption></figcaption></figure>

#### 배너 광고

화면 상단이나 하단에 고정되어 계속 노출되는 광고예요.

* 특징: 항상 노출(자동으로 쌓이는 트래픽)
* 추천 위치: 메인 화면, 리스트 화면
* 노출 수: 가장 많음
* eCPM: 가장 낮음
* 적합한 상황: 사용 시간이 긴 화면, 반복해서 방문하는 화면

<figure><img src="/files/pztmv7mPHmKNgtMgVfrQ" alt=""><figcaption></figcaption></figure>

***

### 인앱 광고의 좋은 점

* 전면형·리워드·배너 광고를 골라, 서비스 흐름에 맞는 위치에 노출할 수 있어요.
* 앱 출시 첫날부터 광고 수익이 생겨서 바로 수익화를 시작할 수 있어요.
* 광고를 계기로 사용자가 서비스를 계속 이용하도록 유도해 리텐션을 높일 수 있어요.
* 게임 서비스에서는 '광고 보고 이어하기'로 도전하던 스테이지를 계속 플레이하게 만들어, 자연스럽게 재이용을 유도할 수 있어요.

***

### 콘솔에서 설정하기

#### 1. 사업자 정보 등록하기

인앱 광고를 연동하려면 먼저 사업자 정보를 등록해야 해요. 사업자 정보가 등록되어 있어야 다음 단계인 약관 동의와 정산 정보 입력을 진행할 수 있어요. 등록 방법은 [가이드](https://developers-apps-in-toss.toss.im/prepare/register-business.html)를 참고해 주세요.

#### 2. 정산 정보 입력하기

인앱 광고를 연동하면 광고 수익이 생겨요. 수익을 정산받으려면 [정산 정보를 등록](https://developers-apps-in-toss.toss.im/guide/settlement)해야 해요. 워크스페이스의 '정보' 탭에서 정산 정보를 입력한 뒤 검토를 요청해 주세요. 검토에는 영업일 기준 평균 2\~3일이 걸려요.

{% hint style="warning" %}
**꼭 확인해 주세요**

예금주명은 통장 사본에 적힌 이름과 한 글자도 다르지 않게 입력해 주세요. 다르게 입력하면 정산이 지연될 수 있어요.
{% endhint %}

#### 3. 광고 그룹 생성하기

앱인토스 인앱 광고는 구글 애드몹 광고 정책을 따라요. 정책을 지키지 않으면 광고가 제한되거나 중단될 수 있어요.

<figure><img src="/files/tRxGWGzsOCkUeSA7ATIs" alt=""><figcaption></figcaption></figure>

**3-1. 광고 그룹 이름**

광고 유형과 노출 위치를 함께 적으면 관리하기 쉬워요. 운영 중에는 여러 광고 그룹을 관리해야 하니, 나중에 봐도 바로 이해할 수 있게 지어 주세요.

* 예) 메인\_전면광고, 게임종료\_리워드광고

**3-2. 광고 유형**

**배너**

* 화면 상단이나 하단에 항상 떠 있는 광고예요.
* 앱을 쓰는 동안 자동으로 노출이 쌓여서 꾸준한 수익을 만들어요.
* 단, 사용자가 익숙해지면 잘 안 보게 되어 단가가 가장 낮아요.

**전면형**

* 화면 전환 시점에 전체 화면으로 등장하는 광고예요.
* 사용자가 무시할 수 없어서 배너보다 단가가 높아요.
* 레벨 클리어, 콘텐츠 전환처럼 흐름이 끊기는 순간에 넣으면 자연스러워요.

**리워드**

* 사용자가 직접 '광고 보기'를 눌러야 시작되는 광고예요.
* 보상을 받으려고 끝까지 집중해서 보기 때문에 단가가 가장 높아요.
* 포인트·아이템 지급처럼 사용자에게 이득이 되는 순간에 연결하면 효과적이에요.

**3-3. 리워드**

리워드 광고를 선택한 경우에만 입력해요. 사용자가 실제로 받는 보상 이름과 수량을 정확히 입력해 주세요. 사용자 화면에 표시되는 내용이라, 서비스에서 쓰는 용어와 같게 적는 게 좋아요.

* 예) 서비스 내 보상 단위: 기회 / 수량 및 금액: 1

**3-4. 미디에이션**

미디에이션은 여러 광고 네트워크를 연동해, 실시간으로 수익이 가장 높은 광고를 자동으로 선택하는 기술이에요. '앱 정보'에 등록한 카테고리에 맞는 광고 네트워크가 자동으로 설정돼요. 다른 카테고리로 바꿀 수 있지만, 바꾼 내용은 앱 정보에 반영되지 않고 광고 그룹을 생성할 때만 적용돼요.

등록한 후에 상세 화면에서 광고 그룹 ID를 확인할 수 있어요.

* 광고 그룹 ID는 구글에 등록되기까지 최대 2시간이 걸릴 수 있어요.
* 광고 그룹을 생성한 뒤 제공되는 광고 그룹 ID로 개발해 주세요.

***

### 광고 운영 정책 <a href="#policy" id="policy"></a>

#### 토스애즈 SSP 정책 <a href="#ssp" id="ssp"></a>

아래 정책을 반드시 지켜 주세요. 위반하면 광고 노출이 제한될 수 있어요.

이 정책에 적혀 있지 않은 경우라도, **광고 노출·클릭·성과를 인위적으로 유도하거나 사용자가 오해하게 만드는 행위는 정책 위반으로 볼 수 있어요.**

정책 위반으로 서비스가 종료되면, 모든 파트너사는 서비스 종료 정책을 지켜야 해요.

| 유형              | 금지 행위                                                                                  | 구체적 예시                                                                                                                                                                                                                                                                                                                                                                                                                   | 정책 기준                                                                                                                                           |
| --------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| UI/UX 품질 저하     | 광고와 콘텐츠의 구분을 불명확하게 하거나, 사용자 의도와 무관한 광고 소비 또는 클릭을 유도하거나, 정상적인 서비스 이용을 방해하도록 UI를 구성하는 행위 | <p></p><ul><li>"추천 서비스", "금융 팁" 등으로 광고를 위장</li><li>Toss Ads 가이드 외 광고 단위의 색상·글꼴 변경</li><li>광고의 타이틀·라벨·CTA 문구 및 디자인 임의 수정</li><li>사용자 상호작용 요소(버튼, 게임 플레이 영역 등)와 인접하게 광고를 배치하여 의도치 않은 클릭이 발생하도록 하는 구조</li><li>동일 화면에 동일 포맷 광고를 2개 이상 배치하는 경우</li><li>사용자가 정상적으로 화면을 종료하거나 이전 화면으로 이동하기 어렵게 하는 막다른(Dead-end) 구조</li><li>광고와 서비스 CTA의 기능을 사용자가 구분하기 어렵게 구성한 구조</li><li>서비스의 정상적인 이용에 필요한 CTA를 인지하거나 접근하기 어렵게 구성한 구조</li></ul> | <p></p><ul><li>광고는 반드시 "Ad" 표기를 유지해야 함</li><li>모든 광고 UI는 web-base 표준 컴포넌트를 사용해야 함</li><li>광고 성과를 인위적으로 유도하거나 사용자 경험을 저해하는 UI/UX 구성 금지</li></ul> |
| 광고 호출 동작 변조     | SDK 기본 이벤트 흐름이나 광고 호출 방식을 변경하거나 우회하는 행위                                                | <ul><li>SDK Click / Impression 이벤트 변조</li><li>광고 SDK를 거치지 않고 자체 로직으로 광고를 호출하거나 SDK 이벤트를 우회하여 구현하는 경우</li><li>Back 버튼을 차단하거나 비정상적으로 제어하여 사용자의 정상적인 화면 종료 또는 이전 화면 이동을 방해하는 경우</li></ul>                                                                                                                                                                                                                                   | <p></p><ul><li>SDK 기본 이벤트(Click / Impression) 구조 변조 금지</li><li>SDK 외부 API 호출 불가</li></ul>                                                       |
| 비정상 트래픽 및 성과 조작 | 자동화 또는 인위적 방식으로 트래픽 및 광고 성과를 왜곡하는 행위                                                   | <ul><li>광고 영역을 주기적으로 Refresh 처리</li><li>인위적으로 성과(클릭·노출 등)를 발생시키는 활동</li></ul>                                                                                                                                                                                                                                                                                                                                            | <ul><li>트래픽 품질 기반의 비정상 패턴이 확인될 경우 광고 제한, 제재, 정산 보류</li></ul>                                                                                    |
| 보상·참여형 클릭 유도    | 광고 클릭과 동시에 보상 또는 혜택을 제공하는 행위                                                           | <ul><li>"광고 클릭 즉시 리워드 제공"</li><li>"광고 클릭하면 포인트 제공"</li></ul>                                                                                                                                                                                                                                                                                                                                                             | <ul><li>광고 소비를 보상과 직접 연결하는 구조 금지</li><li>클릭 보상성 문구·이벤트 연동 금지</li></ul>                                                                          |
| 광고 은닉 또는 겹침     | 광고를 의도적으로 숨기거나 다른 UI 요소에 가려 사용자가 광고의 존재를 명확히 인지하기 어렵게 만드는 행위                           | <p>• 투명 광고 </p><p>• 다른 카드 UI 뒤에 광고 DOM 삽입</p>                                                                                                                                                                                                                                                                                                                                                                            | • 광고는 노출 상태가 명확히 확인 가능해야 함                                                                                                                      |

#### UX / Product Principle 운영 원칙

광고도 토스의 UX 원칙을 따라야 해요.

| **Toss Principle**             | **적용 기준**                                        | **예시**                     |
| ------------------------------ | ------------------------------------------------ | -------------------------- |
| **Simplicity**                 | 광고는 명료해야 하며, 추가 설명 없이 의미를 이해할 수 있어야 함            | "지금 보기", "광고 보기" 등 명확한 CTA |
| **Clear Action**               | 광고 클릭 후 어떤 행동이 발생할지 사용자가 예측 가능해야 함               | 외부 이동 시 고지 문구 제공           |
| **No Deception (UX Red Rule)** | 광고가 예상하지 못한 순간, 형태, 위치에서 등장하거나 사용자를 오인하게 해서는 안 됨 | 광고를 콘텐츠처럼 위장하는 경우          |
| **Value First**                | 광고는 고객의 서비스 목표를 방해하지 않아야 함                       | 결제/계좌 개설 흐름 중 광고 삽입 금지     |

#### 이용 제한 및 제재 조치

앱인토스 광고 지면이나 서비스가 이 정책을 위반하면 제재를 받을 수 있어요.

**제한 절차**

제한 조치는 원칙적으로 위반이 쌓인 정도에 따라 단계적으로 적용돼요. 위반의 유형이나 심각성에 따라서는, 한 번의 위반만으로도 즉시 30일 이용 제한이나 영구 이용 제한이 적용될 수 있어요.

※ 동시에 확인된 위반은 위반 슬롯 개수와 관계없이 1번 위반으로 처리돼요. 이후에 위반이 따로 확인되면 위반 횟수가 쌓여요.

<figure><img src="/files/mbFPCjlE7fei5CJJjDWd" alt=""><figcaption></figcaption></figure>

**부당 수익 처리**

정책 위반, 무효 트래픽, 그 밖의 부정한 방식으로 생긴 수익은 부당 수익으로 볼 수 있어요.

부당 수익이 확인되면 그 금액은 지급이 보류되거나 거절될 수 있어요. 이미 지급된 금액도 환수될 수 있어요.

**이의제기 절차**

* 이용 제한 통지를 받으면 **30일 이내에 이의제기를 신청**할 수 있어요.
  * 이의제기 자료는 채널톡으로 제출할 수 있어요.
* 제출한 자료는 내부 기준에 따라 검토하고, 필요하면 추가 자료를 요청할 수 있어요.
  * 검토에는 영업일 기준 약 1주일이 걸릴 수 있어요.
  * 이의제기는 **제재가 적절했는지를 중심으로 검토**해요. 위반 사항을 수정했거나 재발 방지 계획을 냈다는 사실만으로는 제재가 풀리지 않아요.
  * 제출한 자료로 제재의 근거가 된 위반 사실이 인정되지 않거나, 제재 판단에 명백한 오류가 있다고 확인되면 제재가 풀릴 수 있어요.
* 반복되거나 중대한 위반은 서비스 이용이 영구적으로 제한될 수 있어요.

***

### 개발 연동하기

사용자에게 광고가 너무 자주 노출되지 않도록 주의해 주세요.

인앱 광고 테스트는 반드시 테스트용 ID를 써야 해요. 운영 ID를 쓰면 제재를 받을 수 있어요.

광고가 재생되는 동안 앱 사운드는 잠시 멈추고, 광고가 끝나면 자동으로 다시 재생되도록 처리해 주세요.&#x20;

[전면형/보상형 광고 전면형·보상형 광고 로드 및 표시 API와 SDK 연동 방법](/documentation/common/monetization/iaa/interstitial-rewarded-ad)

[배너 광고(WebView) WebView 배너 광고 SDK 초기화, 부착, 이벤트 콜백 API](/documentation/common/monetization/iaa/web-banner)

[배너 광고(React Native) React Native 배너 광고 InlineAd 컴포넌트 사용법](/documentation/common/monetization/iaa/rn-banner)

***

### 광고 성과 및 정산 내역 확인하기

기간과 운영체제(OS)를 선택해 아래 항목을 확인할 수 있어요.

* 총 광고 노출 수, eCPM, 총 예상 수익
* 성과 데이터는 매일 오전 10시에 업데이트돼요.

<figure><img src="/files/fUC4SL1UNVW58DODMrIJ" alt=""><figcaption></figcaption></figure>

**정산 내역**

* 매월 1일부터 말일까지의 수익은 다음 달 1일에 업데이트돼요.
* 다음 달 1일에 확정된 수익은 그달 말일에 입금돼요.

정산 구조는 [가이드](https://developers-apps-in-toss.toss.im/guide/settlement#id-1)를 참고해 주세요.&#x20;

***

### 광고 분석하기

내 미니앱의 광고가 올바르게 노출되고 있는지, 광고 빈도와 eCPM 추이를 확인할 수 있어요. 광고를 너무 많이 보여주고 있진 않은지, 어떤 광고 유형이 수익에 효과적인지 데이터로 확인해 보세요.

{% hint style="info" %}
**참고해 주세요**

분석 탭은 SDK 2.7.0 이상으로 업데이트해야 실제 데이터를 볼 수 있어요. 데이터는 SDK 업데이트 이후부터 쌓이니, SDK 업데이트를 먼저 진행해 주세요.
{% endhint %}

#### 광고 노출 현황

광고가 요청부터 노출까지 각 단계에 얼마나 도달했는지 확인할 수 있어요.

<figure><img src="/files/0a3wD8iTe1SR8DPBckfx" alt=""><figcaption></figcaption></figure>

| 단계    | 설명                             |
| ----- | ------------------------------ |
| 광고 요청 | 앱에서 광고 서버로 광고를 요청한 횟수예요.       |
| 광고 수신 | 광고 서버에서 광고를 정상적으로 받아온 횟수예요.    |
| 노출 시도 | 받아온 광고를 사용자 화면에 띄우려고 시도한 횟수예요. |
| 노출 성공 | 사용자 화면에 실제로 광고가 노출된 횟수예요.      |

{% hint style="info" %}
**이렇게 활용해 보세요**

* 광고 요청 대비 수신이 낮다면: 광고 네트워크에 채울 광고가 부족한 상태(No Fill)일 수 있어요.
* 수신 대비 노출 시도가 낮다면: 광고를 받아왔지만 실제로 보여주지 않는 경우예요. `load`와 `show` 호출 타이밍을 확인해 주세요.
* 노출 시도 대비 성공이 낮다면: 광고를 그리는 과정에서 실패가 나고 있어요. SDK 버전이나 호출 순서를 점검해 주세요.
  {% endhint %}

#### 광고 노출 빈도 및 eCPM

광고 유형별(배너·전면형·리워드) 노출 빈도와 eCPM 추이를 함께 확인할 수 있어요.

* 막대그래프 (왼쪽 Y축): 사용자당 하루 평균 광고 노출 횟수 (광고 유형별)
* 꺾은선 그래프 (오른쪽 Y축): eCPM (원 단위)

<figure><img src="/files/kzjWQhQyfUljp67Iy63d" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**이렇게 활용해 보세요**

* 노출 빈도가 올라갔는데 eCPM이 떨어졌다면, 광고 피로도가 원인일 수 있어요.
* 리워드 광고의 eCPM이 다른 유형보다 높다면, 리워드 광고 비중을 늘려보는 것도 방법이에요.
* 'OS별로 보기'를 켜면 iOS와 Android의 성과 차이를 확인할 수 있어요.
  {% endhint %}

#### 광고 노출 비중 및 eCPM

앱 사용 시간 대비 광고 노출이 차지하는 비중과 eCPM 추이를 함께 확인할 수 있어요.

* 막대그래프 (왼쪽 Y축): 광고 유형별 노출 비중 (%)
* 꺾은선 그래프 (오른쪽 Y축): eCPM (원 단위)

<figure><img src="/files/bPQYhrfVtGDPsYINdRbC" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**이렇게 활용해 보세요**

* 광고 노출 비중은 전체 체류 시간 대비 광고 노출 시간이 차지하는 비율이에요.
* 광고 비중이 너무 높으면 사용자 경험이 나빠져 이탈이 늘 수 있어요.
* 비중 대비 eCPM이 낮은 광고 유형이 있다면, 그 유형의 배치를 줄이고 eCPM이 높은 유형으로 바꿔 보세요.
* 보통 리워드 광고는 비중이 적어도 eCPM이 높고, 배너 광고는 비중이 높아도 eCPM이 낮아요.
* 배너 광고는 아직 지원되지 않아요.
  {% endhint %}


# 토스페이

결제 과정에서 마찰을 줄이면 전환율은 올라가고, 결제 실패와 이탈은 줄어들어요.\
토스페이를 사용하면 실물 상품과 서비스를 빠르고 매끄럽게 결제할 수 있어요.

***

### 토스페이란 무엇인가요

토스페이는 사용자가 토스 앱에서 **빠르고 안전하게 결제**할 수 있도록 돕는 간편결제 서비스예요.\
사용자가 미리 등록해 둔 결제 정보를 사용해서, 비밀번호 입력만으로 결제를 끝낼 수 있어요.\
공인인증서처럼 복잡한 인증 절차가 없어서 결제 흐름이 끊기지 않고 자연스럽게 이어져요.

<figure><img src="/files/tNwP7YaB7wOEoOhkgOb1" alt=""><figcaption></figcaption></figure>

***

### 토스페이를 사용하면 어떤 점이 좋나요

* 간편결제를 사용하는 고객의 만족도는 95%로, 결제 경험에 대한 만족도를 높일 수 있어요.
* 토스머니와 카드 등 상황에 맞는 결제 수단을 자유롭게 선택할 수 있어요.
* 결제 과정에서 느끼는 부담을 줄여, 결제 전환율을 높이는 데 도움이 돼요.
* 결제 단계에서 사용자가 떠나는 비율을 줄일 수 있어요.

즉, 결제를 망설이던 사용자가 자연스럽게 결제까지 완료하도록 도와줘요.

***

### 연동 프로세스

토스페이 연동은 다음 순서로 진행돼요.

1. 입점 제한 항목 확인
2. 중개 플랫폼에 해당하는 경우 입점 조건 확인
3. 입점 심사
4. 계약 담당자 메일로 계약 서류 안내
5. 서류 제출(등기)
6. 서류 검토 및 심사 → 계약 체결(상점 계정 생성)
7. 개발 연동 → 오픈

키 발급까지는 영업일 기준 7\~14일 정도 걸려요. 서비스 일정에 맞춰 여유 있게 신청해 주세요.

***

### 입점 제한 항목

토스페이 연동을 신청하기 전에, 아래 항목에 해당하지 않는지 먼저 확인해 주세요.

{% hint style="info" %}
**참고하세요**

입점 제한 항목의 적용은 판매 형태(온라인/오프라인)에 따라 차이가 있을 수 있으며, 이외 내부 정책 판단에 의해 입점이 제한되는 항목이 추가·변경될 수 있어요.
{% endhint %}

**불법물**

총포 및 도검류, 전기충격기, 군복, 사행성 업종(경마, 카지노, 도박, 경륜, 대부업, 환전상, 경매, 유흥 구인구직, 도박 기계 판매업, 성인용 게임장 등), 몰래카메라, 마약, 담배, 담배대용품, 복권, 생물(야생/동식물), 도수 있는 안경·콘택트 렌즈, 혈액 및 혈액증서

* 담배, 담배대용품, 복권, 생물, 도수 있는 안경·콘택트 렌즈는 오프라인 판매에 한하여 제한적 허용 가능

**음란물**

성인 전용 사이트, 성인 만화·잡지 등 판매 상품이 미풍양속에 저해되는 경우

**금융 관련 상품 및 정보업**

대출, 예금·적금, 외화환전 서비스, 디지털자산(가상화폐, NFT 등), 가상화폐 거래소(운영/판매/중개 일체), 주식정보 제공 서비스(유사투자자문업)

**권리 침해 상품**

상표권, 지적재산권 침해 물품

**청소년 유해 환경**

청소년유해매체물, 청소년유해약물, 청소년유해물건, 청소년 출입·고용금지업소, 청소년고용금지업소

**기타 취급 제한 유형**

미등록 다단계·방문판매업자, SNS 홍보, 렌탈 업종(휴대폰), 중고자동차, 콘도회원권, 랜덤(럭키)박스 등 민원이 제기될 우려가 있는 상품 및 서비스 일체

***

### 중개 플랫폼인 경우

중개 플랫폼 형태로 서비스를 운영하는 경우, 결제 및 정산 구조에 따라 사전 확인이 필요해요.\
하위 셀러에게 직접 정산하는 구조라면, 전자지급결제대행업(PG업) 등록 여부를 반드시 확인해야 해요.

* **PG 라이센스를 보유한 경우** → 직접 정산 가능
* **PG 라이센스가 없는 경우** → 정산대행 서비스 이용 필수

PG 라이센스가 없다면, 토스페이 청약 신청 전에 정산대행 서비스 계약을 먼저 진행해 주세요. (예: 토스페이먼츠 등)

{% hint style="info" %}
**\[참고] 정산지급대행 서비스 가입 의무 및 법적 근거**

「전자금융거래법」에 따르면 선불업자로 하여금 대표 가맹점을 모집할 때 시행령 제4조의2 각 호의 자가 아닌 자를 모집하는 행위를 금지시키고 있는데, 전자금융거래법 시행령 제4조의2에서는 (i) 전자지급결제대행에 관한 업무를 행하기 위하여 전자금융업 등록을 한 자, (ii) 수취인을 대행하여 지급인이 수취인에게 지급하여야 할 자금의 내역을 전자적인 방법으로 지급인에게 고지하고, 자금을 직접 수수하며 그 정산을 대행하는 업무를 하기 위하여 등록한 자, (iii) 정산을 대행하는 업무를 하고 있기는 하지만 전자금융업 등록이 면제된 금융회사들을 가맹점으로 정의하고 있어요.

만약 중개플랫폼이 하위 셀러들에게 결제대금을 분배(3자 정산)하는 구조라면 PG업 등록 또는 정산지급대행서비스 가입한 경우 입점이 가능하고, 입점 심사 시 관련 서류(정산대행 계약서, 제3자정산 공문 유형C, KYC) 제출이 요구돼요.
{% endhint %}

***

### 자동결제(정기결제) 수수료

자동결제(정기결제) 방식을 사용하는 경우, 일반 결제와 계약서 양식이 다르게 적용돼요.\
해당 방식으로 진행할 예정이라면 미리 알려주세요.

* 기본 수수료: 결제금액의 **3%**
* 최소 수수료: 건당 **100원**

**\[수수료 부과 예시]**

| 결제 금액   | 수수료              |
| ------- | ---------------- |
| 10,000원 | 300원             |
| 5,000원  | 150원             |
| 3,000원  | 100원 (최소 수수료 적용) |

{% hint style="info" %}
**참고해 주세요**

* 이미 토스페이를 사용하고 있더라도, 앱인토스에서는 앱인토스 전용 토스페이 가맹점 키를 발급받아야 해요.
* 주문 번호를 중복해서 사용하면 결제 생성이 거절돼요.
* 결제 전에 아래 정보는 반드시 사용자에게 분명하게 안내해 주세요.
  * 상품 이름, 수량, 총 결제 금액, 할인 내용, 환불 규정
    {% endhint %}

***

### 콘솔에서 설정하기

**1. 계약하기**

토스페이를 사용하려면 먼저 사전 서류를 준비하고 서면 계약을 체결해야 해요.\
계약이 완료되어야 실제 결제를 연동할 수 있어요.

{% hint style="info" %}
**꼭 확인해 주세요**

* 계약을 신청하기 전에 [입점 제한 항목](#입점-제한-항목)과 [중개 플랫폼 조건](#중개-플랫폼인-경우)을 먼저 확인해 주세요.
* `isTestPayment: true`로 설정하면 계약(청약) 전에도 샌드박스 환경에서 결제를 테스트할 수 있어요.\
  단, 결제 생성까지만 가능하며 실제 승인 처리는 지원하지 않아요.
  {% endhint %}

**진행 절차**

1. [채널톡](https://apps-in-toss.channel.io/workflows/767697)으로 정보 입력
2. 1차 검토: 입력한 정보를 바탕으로 토스페이팀이 1차 검토를 진행해요.
3. 추가 안내: 추가 확인이 필요한 경우, 청약 담당자 메일로 별도 안내해 드려요.
4. 청약 서류 제출: 청약 서류를 작성해 제출해 주세요. 내부 검수를 거친 뒤 토스페이 키값을 발급해요.

키 발급까지는 영업일 기준 7\~14일 정도 걸려요.\
서비스 일정에 맞춰 여유 있게 신청해 주세요.

**2. 설정하기**

계약이 완료되면 메일로 토스페이 키값을 받아요.\
발급받은 키값을 앱인토스 콘솔에 등록해야 실제 연동이 가능해요.

**키 등록 경로**

워크스페이스 → 연동 키 → 등록 버튼 선택 → 가맹점 키 입력

<figure><img src="/files/vLx0uwsBxyYa3TF63UJM" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/TT2rKEaAPRaMzSFJian1" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/JaiqDt8bzQ3Q0PbOmVrr" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**꼭 확인해 주세요**

* 기존에 사용하던 일반 토스페이 키값이 아니라, 앱인토스 전용 토스페이 키값을 입력해야 해요.
* 잘못된 키를 입력하면 결제 요청 시 에러가 발생해요.
  {% endhint %}

***

### 개발 연동하기

{% hint style="info" %}
[토스페이 개발 가이드](https://developers-apps-in-toss.toss.im/tosspay/develop.html) 결제 생성, 인증, 실행, 환불 API와 SDK 연동 방법을 확인할 수 있어요.
{% endhint %}

{% hint style="info" %}
[토스페이 정기결제 개발 가이드](https://developers-apps-in-toss.toss.im/tosspay/auto-pay.html) 구독·정기 배송 등 반복 과금 모델 구현에 필요한 API 연동 방법을 확인할 수 있어요.
{% endhint %}


# 마케팅

미니앱의 유입과 재방문을 늘리는 마케팅 기능을 안내해요. 스마트 발송, 프로모션, 공유 리워드처럼 사용자에게 다시 다가가는 기능을 확인할 수 있어요.

<table data-view="cards"><thead><tr><th></th></tr></thead><tbody><tr><td><a href="https://developers-apps-in-toss.toss.im/guide/marketing/smart-message">스마트 발송</a></td></tr><tr><td><a href="https://developers-apps-in-toss.toss.im/guide/marketing/promotion">프로모션</a></td></tr><tr><td><a href="https://developers-apps-in-toss.toss.im/guide/marketing/share-reward">공유 리워드</a></td></tr><tr><td><a href="https://developers-apps-in-toss.toss.im/guide/marketing/open-graph">OG 이미지</a></td></tr><tr><td><a href="https://developers-apps-in-toss.toss.im/guide/marketing/guideline">외부 광고 가이드</a></td></tr><tr><td><a href="https://developers-apps-in-toss.toss.im/guide/marketing/guideline">토스애즈 픽셀 연동</a></td></tr></tbody></table>


# 세그먼트

세그먼트는 앱인토스 콘솔에서 특정 조건에 맞는 유저 그룹을 만들고 관리하는 기능이에요. 마케팅이나 알림 발송처럼 특정 사용자에게만 메시지를 보내고 싶을 때 사용해요.

***

### 세그먼트를 사용하면 어떤 점이 좋나요

* 정확한 타겟팅이 가능해요. 원하는 조건에 맞는 사용자에게만 메시지를 보낼 수 있어요.
* 데이터 기반 마케팅을 할 수 있어요. 사용자의 행동과 특성에 맞춰 캠페인을 설계할 수 있어요.
* 사용자 경험을 높일 수 있어요. 개인화된 메시지를 제공해서 참여율과 만족도를 높일 수 있어요.

**제공하는 조건 카테고리**

거래 정보, 유저 정보, 유저 활동, 유저 프로파일 네 가지 카테고리의 조건을 조합할 수 있어요.

* **거래 정보** — 거래 내역의 브랜드, 광고 카테고리, 토스페이 사용 여부 등 유저의 실제 거래와 관련된 정보예요.
* **유저 정보** — 나이, 성별, 신용 점수, 앱 버전, 통신사 등 기본적인 유저 속성이에요.
* **유저 활동** — 앱 안에서의 행동 이력을 기준으로 해요. 미니앱 로그인이 아닌 **접속 기준**으로만 설정할 수 있어요.
* **유저 프로파일** — 금융 성향, 라이프사이클 단계, 관심사 추정 정보 등 유저를 종합적으로 분석한 정보예요.

<figure><img src="/files/5HpW3n8rezqWxxqRY6PT" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**참고해 주세요**

* 여러 조건을 **AND 조건 또는 OR 조건**으로 조합해서 사용할 수 있어요.
* 타겟 샘플링을 사용하면 세그먼트에 포함되는 유저 수를 조정할 수 있어요.
* 한 번 생성한 세그먼트를 삭제하더라도 같은 이름의 세그먼트는 다시 만들 수 없어요.
  {% endhint %}

***

### 콘솔에서 설정하기

**1. 세그먼트 생성하기**

세그먼트 메뉴로 접속해서 **'+생성하기'** 버튼을 눌러 주세요.

* 접속 방법: 앱인토스 콘솔 → 워크스페이스 선택 → 미니앱 선택 → 좌측 메뉴 '세그먼트' 선택

<figure><img src="/files/RlIh9HdVvXU71spFOr0m" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/8GxL1QjjhZiSdMDRPXCq" alt=""><figcaption></figcaption></figure>

**2. 카테고리 및 조건 설정**

원하는 카테고리와 하위 조건을 선택하고 **'추가하기'** 를 누르면 우측 **'선택한 세그먼트'** 화면에 추가돼요.

* 조건이 1개라면 바로 **'저장하기'** 를 눌러 저장할 수 있어요.
* 여러 조건을 함께 사용하려면 원하는 세그먼트를 모두 추가한 다음 **'AND'** 또는 **'OR'** 조건을 설정해 주세요.
* 일부 예측이 필요한 세그먼트는 정확도 설정이 필요해요.
  * 정확도를 높이면 세그먼트의 크기(모수)가 줄어들 수 있어요.
  * 모수가 중요하다면 정확도를 낮춰서 사용할 수 있어요. 단, 타겟팅 정확도가 떨어질 수 있어요.
  * 일반적인 경우라면 50% 이상으로 설정해 주세요.

{% hint style="info" %}
**예시 — '서울특별시에 거주하면서 자가를 보유한 것 같은 사람'에게 메시지를 보내고 싶다면?**

1. 유저 정보 → 지역 → 서울특별시 선택 후 '추가하기'
2. '홈'을 눌러 처음 카테고리 목록으로 이동
3. 유저 프로파일 → 자가보유\_전체 → 정확도 설정 후 '추가하기'
4. 우측 '선택한 세그먼트'에서 **'AND'** 선택
   * 서울특별시에 거주하거나 자가를 보유한 것 같은 사람에게 보내고 싶다면 **'OR'** 조건 선택
     {% endhint %}

<figure><img src="/files/5vLAeNvr53u2fKOSSNlh" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/fXmbT5w6s9AKJYLWrqXb" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/ccuRpND8rZ4ZrNWE6Pxd" alt=""><figcaption></figcaption></figure>

AND 또는 OR 조건을 선택하면 세그먼트가 1개로 합쳐지고, 해당 조건에 맞는 인원으로 변경돼요.

* **되돌리기** — 다시 각각의 세그먼트로 분리돼요.
* **저장하기** — 조건이 합쳐진 세그먼트로 저장돼요.

<figure><img src="/files/ic3h7YiHc1tq6fY3fHxi" alt=""><figcaption></figcaption></figure>

**3. 저장하기**

저장하기를 누르면 세그먼트명, 설명, 즐겨찾기 및 고정 여부를 설정할 수 있어요.

<figure><img src="/files/ON83yqZPJ0wSmCEfelkd" alt=""><figcaption></figcaption></figure>

* **세그먼트명** — 추가한 세그먼트 이름이 자동으로 입력돼요. 원하는 이름으로 자유롭게 변경할 수 있어요. 단, 삭제 후에는 같은 이름을 다시 사용할 수 없어요.
* **세그먼트 설명** — 해당 세그먼트를 구성한 이유나 관리 메모를 적어 주세요.
* **즐겨찾기** — 설정 시 푸시, 알림, 스마트 발송에서 바로 사용할 수 있어요.
* **고정된 모수로 저장** — 저장 시점의 모수를 고정해서 사용할 수 있어요. 이 경우 모수가 최신화되지 않으니 목적에 맞게 선택해 주세요.

저장 완료 후 세그먼트 메뉴와 푸시, 알림, 스마트 발송에서 바로 확인하고 사용할 수 있어요.


# 프로모션

프로모션으로 가벼운 클릭이 자연스럽게 전환까지 이어지도록 기획해 보세요. 재방문을 유도하고 자연스러운 바이럴까지, 퍼널 곳곳에서 성과를 낼 수 있어요.

### 프로모션이란

앱인토스의 프로모션은 사용자의 특정 행동을 기준으로 토스 포인트를 지급하는 이벤트예요. 예를 들어 가입하기, 첫 이용 완료하기 같은 행동을 기준으로 혜택을 줄 수 있어요.

프로모션은 비즈 월렛에 충전한 예산으로 운영해요. 혜택 탭에 노출할지 여부도 콘솔에서 직접 설정할 수 있어요.

<details>

<summary>프로모션 허용 유형</summary>

* 회원가입·접속 보상 (예: 신규 가입 시 2,000포인트 지급)
* 거래·구매 유도형 (조건과 안내가 명확해야 해요. 예: 5,000원 이상 결제 시 500포인트 지급)
  * 단, 한 번 지급한 토스 포인트는 회수할 수 없어요. 프로모션 조건과 지급 기준을 충분히 검토한 뒤 신중하게 운영해 주세요.
* 이벤트 참여형 (설문·퀴즈·간단한 미션. 단, 시간이나 노동을 많이 요구할 수 없어요.)
* 친구 초대형 (초대한 사람과 초대받은 사람 모두 보상. 어뷰징 방지 로직 필수)
* 게임이 아닌 앱의 확률형·랜덤 보상
  * 프로모션 기간은 1주일 이내로만 운영할 수 있어요.
  * 새로운 사용자를 유입하기 위한 홍보 목적으로만 쓸 수 있어요.
  * 게임이 아닌 앱으로 등록했더라도, 게임물로 분류되면 확률형 프로모션을 진행할 수 없어요.

</details>

<details>

<summary>프로모션 <mark style="color:$danger;">불가</mark> 유형</summary>

* 게임 앱의 확률형·랜덤 보상 (룰렛, 뽑기 등)
* 게임 결과 기반 보상 (점수, 승패, 등수 기반)
* 재화 환전형 리워드 (기프티콘·상품권을 토스 포인트로 전환)
* 현금 보장형·유사 수신 행위 (투자성·사행성 성격)
* 1인당 5,000포인트를 넘는 지급 (추첨형은 별도로 검토)

</details>

<figure><img src="/files/qPcNC2uiiGRsBQTzZDry" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**참고해 주세요**

프로모션 기능은 별도의 서버 없이도 쓸 수 있어요.

게임이 아닌 미니앱은 서버로 프로모션 포인트를 지급하는 방식(Server-to-Server)도 쓸 수 있어요. 요청 위·변조 방지처럼 무결성이 중요하다면, 서버로 지급하는 방식을 이용해 주세요.

프로모션 기능은 별도의 서버 없이도 사용할 수 있어요.

서버로 프로모션을 지급하려면 토스 로그인을 구현해야 해요. [토스 로그인 가이드](/guide/authentication/intro)를 참고해 주세요.
{% endhint %}

프로모션으로 토스 포인트가 지급되면, 사용자 화면 위쪽에 토스트(toast) 알림이 떠요.

* **{{미니앱 이름}}에서 {{금액}}원을 지급했어요** 형식으로 잠깐 떴다가 자동으로 사라져요.
* 토스 앱 알림 메뉴(홈 화면의 종 버튼)에서도 확인할 수 있어요.
  * 단, 사용자의 알림 설정에 따라 알림 메뉴에서 보이지 않을 수 있어요.
* 혜택 탭의 토스 포인트 메뉴에서 적립 내역도 확인할 수 있어요.
  * 토스 앱에서 혜택 → 토스 포인트 → 포인트 금액 → 미니앱 이름 순으로 눌러 적립 내역을 확인할 수 있어요.

<figure><img src="/files/mXyB0PlgzAjk1NVjEVmJ" alt=""><figcaption></figcaption></figure>

***

### 프로모션의 좋은 점

* 비즈니스 월렛을 미리 충전하고 예산과 종료일을 설정해서, 예산을 넘겨 쓸 걱정 없이 운영할 수 있어요.
* 토스 혜택 탭에 노출되면 많은 트래픽과 함께 더 많은 사용자가 들어올 수 있어요.
* 가입하기, 첫 플레이 진행하기처럼 사용자의 행동을 간단하게 유도할 수 있어요.
* 일간·주간 미션으로 사용자가 다시 방문할 이유를 만들어, 리텐션을 높일 수 있어요.
* 포인트 지급과 결과 조회를 API로 처리할 수 있어요.
* 테스트용 코드인 `TEST_{promotionCode}`를 쓰면, 실제 포인트를 차감하지 않고 검증할 수 있어요.

{% hint style="info" %}
**참고해 주세요**

* 앱인토스의 여러 프로모션을 모아 보여주는 '새로운 서비스 써보고' 탭은, 내부 노출 로직에 따라 일부 사용자에게만 보일 수 있어요.
* 사행성 콘텐츠, 자사 앱 설치 유도, 법령 위반 가능성이 있는 콘텐츠로는 프로모션을 진행할 수 없어요.
* 1인 1회 제한, 일일 제한, 쿨다운, 중복 지급 방지 로직은 꼭 적용해 주세요.
* 지급이 지연되면, PENDING 상태 안내와 결과를 확인할 수 있는 경로를 제공해 주세요.
* 프로모션을 시작하려면 사전 검수가 필요해요. 검수에는 영업일 기준 약 2\~3일이 걸려요.
  {% endhint %}

***

### 콘솔에서 프로모션 등록하기

프로모션을 시작하려면 아래 항목을 먼저 준비해 주세요. 이미 준비가 끝났다면 바로 5번(프로모션 등록)으로 넘어가도 돼요.

* 사업자 정보 등록과 정산 정보 검토 (사업자 검토 영업일 기준 1\~2일, 정산 검토 2\~3일 소요)
* 대표관리자의 프로모션 기능 약관 동의와 비즈니스 월렛 약관 동의
* 프로모션 예산을 비즈니스 월렛에 충전 (최소 30만 원, 최대 3,000만 원)

#### 1. 사업자 정보 등록하기

프로모션은 사업자(개인 또는 법인) 계정에서만 쓸 수 있어요. 사업자 등록이 되어 있지 않으면, 콘솔에 '먼저 사업자 정보를 등록해 주세요' 화면이 보여요. '등록하기'를 눌러 사업자 정보를 등록해 주세요.

* 사업자 정보 검토는 **영업일 기준 약 1\~2일** 걸릴 수 있어요.
* 자세한 절차는 [사업자 등록 가이드](https://developers-apps-in-toss.toss.im/guide/operation/register-business)를 확인해 주세요.

<figure><img src="/files/C89LfOZVUO9TS8gDmnUi" alt=""><figcaption></figcaption></figure>

#### 2. 정산 정보 등록하기

사업자 등록이 끝나면 '다음으로 정산 정보를 등록해 주세요' 화면으로 바뀌어요. '등록하기' 버튼을 눌러 파트너 정보 메뉴에서 정산 정보를 등록하고, 검토를 요청해 주세요.

* 정산 정보 검토는 **영업일 기준 평균 2\~3일** 걸려요.
* 정산 정보를 등록하기 전에는 검토를 요청할 수 없어요. 사업자 등록 상태를 먼저 확인해 주세요.

<figure><img src="/files/d5YRWytnGe4fqnciOP3x" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**정산 정보 등록 시 주의사항**

* 예금주명은 통장 사본과 **정확히 똑같이** 입력해 주세요.
* 업태·업종은 숫자 코드가 아니라 **텍스트**로 입력해 검색해 주세요. 예) '소프트웨어 개발업'은 '소프트웨어'로 검색
  {% endhint %}

#### 3. 비즈니스 월렛 충전하기

정산 정보 검토가 끝나면, 프로모션 약관에 동의해 주세요.

그 이후 **프로모션 메뉴 우측 상단 위젯**이나 워크스페이스의 '비즈니스 월렛' 메뉴에서 예산을 충전할 수 있어요.

<figure><img src="/files/fSlpLgg34rgG0SX2lpcR" alt=""><figcaption></figcaption></figure>

* 충전할 수 있는 금액: **최소 50,000원 \~ 최대 30,000,000원**
* 지금 쓸 수 있는 결제 수단: 신용카드(인증 결제, 비인증 결제)
  * 인증 결제: 카드사별 결제 제한 없음
  * 비인증 결제: 하나카드 하루 100만 원 제한, 나머지 카드사 제한 없음 (비인증 결제는 카드 번호를 직접 입력하는 방식이에요.)

충전이 끝나면 상태가 '충전'으로 바뀌고, 충전한 금액이 반영돼요.

자동 충전도 함께 설정할 수 있어요. (예: 10만원 남으면 50만원씩 충전)

**정산 및 운영 원칙**

* 프로모션 예산은 비즈니스 월렛에서 충전한 뒤 설정해요. 예산이 다 소진되면 프로모션이 끝나요.
* 프로모션을 중단하면, 남은 예산은 비즈 월렛으로 반환돼요. (잔액이 있는 경우)
* 중복 참여·어뷰징 방지 로직은 꼭 적용해야 해요.
* 클레임이 생기면, 참여 기록을 확인한 뒤 파트너사에서 처리해야 해요.

#### 4. 프로모션 등록하기

**혜택 탭에 노출하는 경우**

<figure><img src="/files/Vwwd3q2GwRAXBctaafVq" alt=""><figcaption></figcaption></figure>

**혜택 탭에 노출하지 않는 경우**

<figure><img src="/files/0ZAZJVAfnH8TFfdTglct" alt=""><figcaption></figcaption></figure>

* **프로모션 이름:** 사용자가 어떤 조건을 채우면 포인트를 받는지 구체적으로 입력해 주세요. 예) 서비스 로그인 시 10포인트 지급, 튜토리얼 완료 시 20포인트 지급
* **프로모션 종료일:** 원하는 종료일을 설정해 주세요. 예산이 다 소진되면 예정일보다 먼저 끝날 수 있어요.
* **혜택 탭 노출 여부**
  * 노출: 토스 앱에서 혜택 → 새로운 서비스 써보고 메뉴에 함께 노출돼요.
  * 미노출: 미니앱으로 직접 접속하는 사용자에게만 프로모션을 제공해요.
  * 주의: 미노출로 등록한 뒤 나중에 노출하려면 **새 프로모션을 등록**해야 해요. (수정으로는 바꿀 수 없어요.)
* **미션 이름** (혜택 탭 노출 시 필수): 미션 이름은 '\~하기'로 끝나야 해요. 예) 튜토리얼 하기, 로그인 하기, 리뷰 남기기
* **지급 방식** (혜택 탭 노출 시 필수)
  * 고정 금액: 정해진 금액을 지급해요.
  * 최대 금액: 1인당 최대로 지급할 수 있는 금액이에요. 이 범위 안에서 랜덤으로 지급해요. 예) 최대 100원 → 1회 참여 시 0\~100원 중 지급
* **이동 URL** (혜택 탭 노출 시 필수)
  * 형식: `intoss://{{appName}}/ScreenName`
  * 혜택 탭에서 이 URL로 들어와요.
* **예산 정보**
  * 예산은 비즈 월렛 잔액보다 크게 설정할 수 없어요.
  * 진행 중에 예산을 늘리려면, 비즈 월렛을 충전한 뒤 프로모션 수정에서 늘릴 수 있어요.
  * **1인 하루 최대 지급 금액** (필수): 한 명이 하루에 받을 수 있는 최대 금액을 직접 설정해 주세요.
    * 같은 사용자에게 이 금액을 넘겨 지급하지 않아요.
    * 어뷰징을 막고 예산을 안정적으로 관리하는 데 도움이 돼요.
    * 특히 어뷰징이 걱정된다면 꼭 설정해 주세요. 클라이언트에서 포인트 지급을 요청하는 경우에도 적용하시길 권장해요.

<details>

<summary>프로모션 사전 점검 체크리스트</summary>

아래 항목을 모두 충족해야 프로모션을 진행할 수 있어요.

□ 지급하는 포인트가 **1인당 5,000포인트 이하**인가요?\
□ 지급 조건, 지급 시점, 지급 제한을 명확히 안내했나요?\
□ 참여 방식이 단순하고, 시간이나 노동을 많이 요구하지 않나요?\
□ (게임의 경우) 룰렛·뽑기 등 확률형 요소와 결합되지 않았나요?\
□ 지급하는 포인트가 게임 결과나 등수를 기준으로 정해지지 않나요?\
□ 사용자가 가진 재화를 토스 포인트로 교환·전환해 주는 형태는 아닌가요?\
□ 프로모션이 일찍 끝나거나 중단될 수 있다는 점을 미리 안내했나요?\
□ 중복 참여 방지 로직을 적용했나요?\
□ 사행성이 있거나 과장된 프로모션은 아닌가요?

</details>

프로모션 정보를 입력한 뒤 \[검토 요청하기]를 눌러 주세요. 검토는 **영업일 기준 2\~3일** 걸려요.

***

### 개발 연동하기

게임·비게임 미니앱에서 토스 포인트를 지급하는 [SDK 함수와 서버 API 연동 방법](https://developers-apps-in-toss.toss.im/documentation/common/growth/promotion#undefined-5)을 확인할 수 있어요.

프로모션을 안내할 때는 아래 정보를 꼭 알려주세요.

* 지급 시점 (예: 즉시 지급, 다음 날 18시 지급 등)
* 지급 조건 (예: 최초 결제, 5,000원 이상 결제 등)
* 지급 제한 (탈퇴·환불·부정 참여 시 지급 불가)
* "이 프로모션은 사전 고지 없이 중단될 수 있어요" 문구 포함
* 랜덤 지급 불가 (고정 지급만 허용)

***

### 테스트 진행하기

검토가 끝나면 '테스트하기'로 테스트를 진행해 주세요. 테스트할 때는 실제 포인트가 차감되지 않아요.

* 테스트 프로모션 코드는 실제 코드 앞에 `TEST_`가 붙어요.
* 테스트할 때는 포인트가 차감되지 않고, 실제 지급도 되지 않아요.
* 포인트 지급 API를 호출할 때 `resultType`이 `SUCCESS`로 응답되는지 확인해 주세요.
* 검토가 끝난 뒤 포인트 지급 API 테스트를 최소 1번 완료해야, 프로모션을 시작할 수 있어요.

<figure><img src="/files/zyzjlxIqRYd7PXK2Cq8H" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/Bld9NOQBuiyIF3o1DK6c" alt=""><figcaption></figcaption></figure>

***

### 프로모션 시작 및 운영하기

'시작하기' 버튼을 눌러 프로모션을 시작해 보세요.

* 예산 열은 `실제 소진 금액 / 총 예산` 형태로 표시돼, 예산이 얼마나 소진됐는지 확인할 수 있어요.
* 프로모션 수정은 주로 예산을 늘릴 때 써요.
* 프로모션이 끝나면 남은 예산은 비즈 월렛으로 환급돼요.
* 예산이 다 소진되면 혜택 탭에 더 이상 노출되지 않아요. 예산을 주기적으로 관리해 주세요.

<figure><img src="/files/ySc8sVOLkEDh8s3u9atx" alt=""><figcaption></figcaption></figure>

캠페인 이름을 누르면 프로모션 상세 정보를 확인하고, 수정·종료·일시정지를 할 수 있어요.

* **수정하기:** 주로 예산을 늘릴 때 써요. 예산을 늘리려면 캠페인 상세 → 프로모션 수정 버튼 → 늘릴 금액 입력 → 저장 순으로 진행해 주세요. 예산 외 설정을 바꾸려면 내부 정책상 제한이 있을 수 있으니, 검토 가이드라인을 먼저 확인해 주세요.
* **종료하기:** 프로모션이 끝나면 남은 예산은 자동으로 비즈 월렛으로 환급돼요. 환급 금액은 종료 후 바로 반영될 때가 많지만, 시스템 처리 때문에 조금 늦어질 수 있어요. 예산을 자주 확인해서, 예산 소진으로 프로모션이 자동 종료되지 않게 해주세요.

***

### 성과 확인하기

프로모션을 시작하면, 그 프로모션의 성과를 대시보드에서 확인할 수 있어요. 대시보드는 프로모션이 얼마나 효과적이었는지 한눈에 보여주고, 다음 캠페인 전략을 개선하는 데 도움을 줘요.

**혜택 탭에 노출하지 않은 프로모션은 성과 대시보드에 표시되지 않아요.**

#### **기간 설정 및 조회 조건**

성과 데이터는 여러 기준으로 조회할 수 있어요. 분석 목적에 맞게 조건을 바꿔가며 살펴보면 좋아요.

* **일별·주별·월별** 단위로 데이터를 조회할 수 있어요.
* **운영체제(OS)별**로 나눠서 성과를 확인할 수 있어요.
* 지금 미니앱에서 운영 중인 전체 프로모션과 개별 프로모션을 구분해서 조회할 수 있어요.

<figure><img src="/files/tivex2zwKQ9rrQe0SH3h" alt=""><figcaption></figcaption></figure>

프로모션 성과 대시보드에서는 사용자가 프로모션에 참여하는 흐름을 단계별로 확인할 수 있어요. 이 흐름을 보면 **최종 전환율**을 알 수 있고, 어느 단계에서 사용자가 많이 이탈하는지도 파악할 수 있어요.

1. **혜택 탭 진입:** 토스 앱의 혜택 탭에 이 프로모션이 노출되고, 이를 본 사용자 수예요.
2. **프로모션 클릭:** 혜택 탭에서 프로모션을 실제로 눌러 상세 화면으로 들어온 사용자 수예요. 혜택 탭 진입 대비 클릭 수를 보면, 프로모션 제목이나 썸네일이 사용자의 관심을 얼마나 끌었는지 알 수 있어요.
3. **포인트 획득:** 프로모션 조건을 채우고 토스 포인트를 실제로 받은 사용자 수예요. 조건이 어렵거나 과정이 복잡하면, 이 단계에서 많이 이탈할 수 있어요.

<figure><img src="/files/djBjjXIYkIFJoc674nq1" alt=""><figcaption></figcaption></figure>

프로모션으로 유입된 사용자를 성격에 따라 나눠서 성과를 확인할 수 있어요.

* **신규 사용자:** 프로모션을 계기로 미니앱에 처음 들어온 사용자예요.
* **기존 사용자:** 예전에 미니앱을 이용한 적이 있는 사용자예요.

<figure><img src="/files/Eq6OJgLiL4McSslHveDa" alt=""><figcaption></figcaption></figure>

프로모션이 실제 매출에 얼마나 기여했는지도 확인할 수 있어요. 다만 기여 매출은 워크스페이스(사업자 번호) 단위로만 조회할 수 있고, 미니앱별로는 조회할 수 없어요.

* **총 기여 매출:** 프로모션으로 유입된 사용자들이 발생시킨 전체 매출이에요.
* **결제자 기준 1인당 평균 매출:** 프로모션으로 유입된 사용자 중 **실제로 결제한 사용자만**을 기준으로 계산한 평균 매출이에요.
* **전체 사용자 기준 1인당 평균 매출:** 결제 여부와 관계없이 **프로모션으로 유입된 전체 사용자**를 기준으로 계산한 평균 매출이에요.

***

### 자주 묻는 질문

<details>

<summary>모든 프로모션에 1인당 5천 포인트 제한이 있나요?</summary>

네, 맞아요. 가입/거래/이벤트/친구 초대 등 **모든 프로모션 유형은 원칙적으로 1인당 최대 5천 포인트 미만까지만 지급** 가능해요.

</details>

<details>

<summary>추첨이나 랜덤 지급 이벤트는 가능한가요?</summary>

(게임 앱의 경우) 불가해요. 확률형·룰렛·랜덤 뽑기 방식은 **사행행위로 해석될 위험**이 있어 허용되지 않아요.

**고정형 지급**만 허용돼요. (예: "첫 가입 시 500 포인트 지급")

</details>

<details>

<summary>게임 내 점수/승패/등수에 따라 포인트를 지급할 수 있나요?</summary>

불가해요. 관련 법령에 따라 게임 결과와 현금성 보상을 직접 연결하는 것은 **사행성 유도 행위**로 금지되어 있어요.

게임 앱의 경우 **게임 결과와 무관한 조건**(가입, 로그인, 튜토리얼 완료 등)으로만 프로모션 운영이 가능해요.

</details>

<details>

<summary>기프티콘·상품권을 토스 포인트로 교환해줄 수 있나요?</summary>

불가해요. 유저가 보유한 재화를 환전해 주는 리워드는 자금 세탁 등의 우려로 진행이 불가해요.

토스 포인트 지급은 반드시 앱 내 특정 행동(가입, 결제, 이벤트 참여 등)을 기반으로 운영되어야 해요.

</details>

<details>

<summary>포인트 지급 시점은 어떻게 정해야 하나요?</summary>

실제 지급되는 시점은 자체적으로 판단하시되, 반드시 **유저에게 프로모션 참여 전 사전 고지**해야 해요.

예: "이벤트 참여 후 즉시 지급" / "익일 18시까지 지급돼요" 등

</details>

<details>

<summary>프로모션을 도중에 중단할 수 있나요?</summary>

예산 초과 등의 사유 외에는 중단을 권장하지 않아요. 특히, 특정 행동을 달성해야 포인트를 지급받는 경우 유저가 종료 사실을 인지하지 못했을 수 있어요.

프로모션 진행 시 **"본 프로모션은 사전 고지 없이 중단될 수 있습니다"** 문구를 반드시 포함해 주세요.

</details>

<details>

<summary>친구 초대 보상은 어떻게 설계해야 하나요?</summary>

초대한 사람과 초대받은 사람 모두에게 동일한 보상 제공이 가능해야 해요.**중복 참여·어뷰징 방지 로직**이 필수로 적용되어야 해요.보상 한도는 1인당 최대 5천 포인트가 동일하게 적용돼요.

</details>

<details>

<summary>포인트 대신 현금을 지급해도 되나요?</summary>

불가해요. 현금 지급 또는 "입금 시 +N% 보장" 형태는 **유사수신행위·사행성 행위**로 법 위반 소지가 있어요.

반드시 **토스 포인트** 형태로만 지급해야 해요.

</details>


# 스마트 발송

앱인토스의 스마트 발송을 활용하면, 푸시·알림 메시지로 신규 사용자 유입, 리텐션 향상, 서비스 이용 활성화 같은 목적을 효과적으로 이룰 수 있어요.

### 스마트 발송이란

스마트 발송은 발송 대상(세그먼트)과 발송 시점을 함께 고려해 자동으로 최적화하는 푸시·알림 발송 도구예요. 단순한 일회성 발송이 아니라, AI가 아래 두 가지를 예측해 캠페인을 자동으로 운영해요.

* 서비스 고관여자: 설정한 세그먼트의 pCTR(예상 클릭률)을 활용해 서비스 고관여자를 예측해요.
* 클릭 가능성이 높은 발송 시간: 사용자의 패턴을 활용해 클릭이 더 잘 일어나는 시간대를 예측해요.

#### 작동 방식

**1) 테스트 발송**

파트너사의 테스트가 아니라, 내부에서 AI 학습을 위해 진행하는 자동 발송이에요.

* pCTR을 기반으로 서비스 고관여자를 예측해요.
* 예측 결과를 바탕으로 최적의 사용자와 발송 시점을 탐색해요.
* 테스트 발송은 최대 7일간 진행될 수 있어요.
* 클릭 25건 또는 발송 2,500건 중 하나를 충족하면 바로 본 발송이 시작돼요.

**2) 본 발송**

테스트 결과를 반영해 캠페인을 자동으로 최적화해서 발송해요.

#### 푸시와 알림의 개념

* 푸시: 앱을 열지 않은 상태에서도 받는 운영체제(OS) 알림이에요. 앱 이름과 로고가 함께 노출돼요.
* 알림: 토스 앱 오른쪽 위의 종 아이콘을 눌렀을 때 확인할 수 있는 앱 안의 메시지예요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FyL7gYaNc3oBWxYwBlK80%2Fimage.png?alt=media&#x26;token=cdc5c74c-6bf1-43be-af7a-819c360f57d1" alt=""><figcaption></figcaption></figure>

#### 메시지 유형

메시지 내용에 따라 광고성 메시지와 기능성 메시지 중 하나를 골라 보낼 수 있어요.

* **광고성 메시지:** 할인, 이벤트, 신규 상품 안내처럼 마케팅 목적의 메시지예요.
* **기능성 메시지:** 주문, 결제, 배송, 게시글 등록처럼 서비스 이용 과정에서 생기는 필수 정보를 전달하는 메시지예요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FcpHX5nMpjuRogLzdWFVV%2Fimage.png?alt=media&#x26;token=eea3a942-0de4-471f-8d2c-f0f83ee68260" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
구매 유도, 서비스 이용 유도, 혜택 안내, 리텐션 목적의 마케팅 요소가 들어가면 기능성으로 발송할 수 없어요.&#x20;

메시지 문구가 고민된다면 [UX 라이팅 문서](https://developers-apps-in-toss.toss.im/design/consumer-ux-guide#ux)와 아래 메시지 작성 가이드를 참고해 주세요.
{% endhint %}

***

### 스마트 발송의 좋은 점

* 사용자에게 꼭 필요한 메시지를 자동으로 전달할 수 있어요.
* 기능성 안내부터 마케팅 메시지까지 목적에 맞게 활용할 수 있어요.
* 사용자가 미니앱을 열지 않아도 중요한 정보를 놓치지 않게 할 수 있어요.
* 적절한 타이밍의 메시지로 서비스 이용 흐름을 자연스럽게 이어갈 수 있어요.
* 발송 결과를 바탕으로 메시지 내용을 계속 개선할 수 있어요.

#### 꼭 참고해 주세요

* 푸시·알림을 보내려면 메시지 템플릿 문구 검수가 필요해요. (광고성 메시지는 자동으로 검수돼요.)
* 푸시 내용을 작성하면 알림 메시지에 자동으로 반영돼요.
* 워크스페이스(사업자)당 10만 건까지 발송할 수 있어요.
* 기능성 메시지에 서비스 유도, 혜택 안내, 리텐션 등 광고 목적의 내용이 들어가면 광고성 메시지로 발송해야 해요.
* 기능성 메시지를 발송하려면, 사용자에게 미리 그 목적으로 발송하겠다는 알림 동의를 받아야 해요.
* 사용자가 알림 수신을 해제할 수 있는 기능을 제공하고, 해제 방법을 명확하게 안내하는 걸 권장해요.

<details>

<summary>토스 앱 → 전체 탭 → 설정 버튼 → 알림 → 서비스별 알림에서 알림 수신 여부를 직접 제어할 수 있어요.</summary>

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FeR5t52wqNrftZBu4DHj7%2Fimage.png?alt=media&#x26;token=d8668c28-2385-4e27-a0b0-cc9d845c935c" alt=""><figcaption></figcaption></figure>

</details>

***

### 콘솔에서 설정하기

접속 방법: 앱인토스 콘솔 → 워크스페이스 선택 → 미니앱 선택 → 왼쪽 메뉴 '스마트 발송' 선택

#### 1. 광고성 캠페인

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2F0UStKoLELQWf1dLL4AZR%2Fimage.png?alt=media&#x26;token=36115944-da3c-49cd-a8d3-716f7a40c203" alt=""><figcaption></figcaption></figure>

광고성 캠페인은 두 가지 목적의 그룹으로 구성돼요.

* 신규 유입 유도하기: 미니앱을 아직 써보지 않은 사용자에게 첫 진입을 유도해요. 기본 대상은 미니앱을 써보지 않은 사용자 전체예요. 대상을 더 좁히려면 조건을 추가할 수 있어요.
* 재방문 유도하기: 최근 사용이 줄어든 사용자의 재방문을 유도해요. 기본 대상은 최근 사용이 줄어든 사용자 전체예요. 대상을 더 좁히려면 조건을 추가할 수 있어요.

**소재**

* 각 그룹당 최대 2개의 소재(a안, b안)를 등록할 수 있어요.
* 광고성 캠페인은 동적 변수(예: `#{이름}`, `#{금액}`처럼 발송 시점에 치환되는 값)를 쓸 수 없어요.
* 자세한 작성 방법은 메시지 작성 가이드를 참고해 주세요.

**클릭 시 이동할 화면 URL**

* 푸시나 알림을 눌렀을 때 이동할 URL을 입력해 주세요.
* 한 캠페인에서는 소재별로 다른 URL을 설정할 수 없어요. 다른 URL을 쓰려면 캠페인을 새로 만들어야 해요.
* 실제로 정상 접속되는지 꼭 확인해 주세요.

**발송 시점**

발송을 시작할 시점을 선택해요.

* 등록 후 바로 발송하기: 검수가 승인되면 바로 발송을 시작해요.
* 발송 기간 설정하기: 시작일과 종료일을 지정해요. 종료일 없이 계속 발송하려면 '종료일 없음'을 선택할 수 있어요.

**발송 대상**

기본 대상에서 조건을 추가해 발송 범위를 조정할 수 있어요.

* 포함할 사용자: 설정한 모든 조건을 만족하는 사용자에게 발송해요.
* 제외할 사용자: 설정한 모든 조건을 만족하는 사용자를 발송 대상에서 제외해요.
* **단, 재방문 유도하기 발송의 경우 발송 시작일 전 30일 간의 사용자가 100명 이상이어야 발송할 수 있어요.**

#### 2. 기능성 캠페인

기능성 캠페인은 서비스 이용에 직접 필요한 정보를 전달하는 메시지예요. 수신자가 요청한 정보이거나, 서비스 이행을 위한 필수 메시지여야 해요. 서비스 사용 유도, 혜택 안내, 리텐션 등 광고 목적의 내용이 들어가면 광고성 캠페인으로 발송해야 해요.

<details>

<summary>허용되는 예시</summary>

* 결제 완료, 환불, 포인트 적립 등 거래 관련 메시지
* 배송 시작, 배송 완료, 예약 확정 등 서비스 진행 상황 메시지
* 정기 결제 예정, 서비스 만료 예정 등 이용 기간·상태 알림

</details>

<details>

<summary><mark style="color:$danger;">허용되지 않는 예시</mark></summary>

* "이런 상품은 어떠세요?", "오늘만 할인 중이에요." — 판촉·광고 목적
* "이전 구매 고객을 위한 추천 상품" — 부가적 마케팅 정보
* "이벤트 참여하고 혜택 받아가세요." — 이벤트 홍보성 메시지

</details>

**2-1. 직접 API로 발송하기**

파트너사 서버에서 원하는 시점에 발송할 수 있어요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2Fk7om6S42RmDrgXYPQD4M%2Fimage.png?alt=media&#x26;token=0cda12d0-d35e-4ebb-94bb-c2f09af72cd2" alt=""><figcaption></figcaption></figure>

파트너사 서버에서 원하는 시점에 발송할 수 있어요. 발송 API 상세 스펙은 '기능성 메시지 발송하기' 문서에서 확인할 수 있어요.

* 캠페인 제목: 어떤 목적으로 발송하는 캠페인인지 알기 쉽게 지어 주세요.
* 제목: 7자 이내(띄어쓰기 포함), 명사형으로 작성해 주세요. '\~하기' 형태로 쓰면 사용자 행동을 유도하는 목적으로 오해받을 수 있어요.
* 내용: 25자 이내(띄어쓰기 포함), '\~요.' 체로 작성해 주세요. 변수는 2글자로 계산돼요. API로 발송할 때 변수는 직접 개발해야 해요.
* 이동 URL: 푸시나 알림을 눌렀을 때 이동할 페이지를 설정해 주세요. 접속되는지 꼭 확인해 주세요.
* 알림 동의문: 문구 검토에서 알림 동의문이 필요하다고 판단되어 반려되면, 반드시 알림 동의문을 구성한 뒤 재검토를 요청해 주세요.

**2-2. 토스에게 발송 요청하기**

일회성 발송과 정기 발송을 골라서 발송할 수 있어요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FPeKBN4f5ZWaW1WjVxkqG%2Fimage.png?alt=media&#x26;token=6c2fbb20-8879-42ba-8a42-533443d39684" alt=""><figcaption></figcaption></figure>

서버 없이 일회성 발송과 정기 발송을 골라서 발송할 수 있어요.

* 캠페인 제목: 어떤 목적으로 발송하는 캠페인인지 알기 쉽게 지어 주세요.
* 제목: 7자 이내(띄어쓰기 포함), 명사형으로 작성해 주세요.
* 내용: 25자 이내(띄어쓰기 포함), '\~요.' 체로 작성해 주세요. 토스에게 발송을 요청할 때는 이름 변수만 쓸 수 있어요. 변수는 2글자로 계산돼요.
* 이동 URL: 푸시나 알림을 눌렀을 때 이동할 페이지를 설정해 주세요. 접속되는지 꼭 확인해 주세요.
* 알림 동의문: 토스에게 발송을 요청해 발송하는 경우 알림 동의문이 반드시 필요해요.
* 발송 유형: 일회성 발송과 정기 발송 중 선택할 수 있어요.
  * 일회성 발송: 특정 날짜와 시간을 지정해 한 번만 발송해요.
  * 정기 발송: 반복 발송 주기를 설정할 수 있어요. (매일: 매일 지정한 시간에 발송 / 매주: 원하는 요일과 시간을 선택해 발송) 시작일과 종료일을 설정하고, '종료일 없음'을 선택하면 직접 중단할 때까지 계속 발송해요.

**알림 동의문**

기능성 메시지를 보내려면 알림 동의문이 필요할 수 있어요. 알림 동의문은 사용자가 서비스 이용 과정에서 알림 수신에 동의할 수 있도록 제공하는 안내 문구예요. 특정 시점에 알림을 받겠다고 사용자에게 동의를 받는 경우에는 반드시 알림 동의문을 써야 해요.

<details>

<summary>알림동의문이 필요한 경우</summary>

재입고 알림 신청, 이벤트 시작 알림 신청, 가격 변동 알림 신청, 예약 오픈 알림 신청

</details>

<details>

<summary>알림동의문이 필요하지 않은 경우</summary>

결제 완료, 배송 시작, 배송 완료, 환불 완료, 정보 변경 안내, 약관 변경 안내

</details>

알림 동의문이 필요한 메시지인데 동의문을 등록하지 않으면, 기능성 메시지 템플릿을 저장할 수 없어요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FZiL8M4U1TrIslpw4RTU2%2Fimage.png?alt=media&#x26;token=8210c1a0-cccb-4b03-9880-895ab0992ba9" alt=""><figcaption></figcaption></figure>

**알림 동의문 설정하기**

* 알림 동의문 이름: 미니앱 서비스에서 어떤 상황에 이 동의문을 쓰는지 알 수 있게 지어 주세요.
* 알림 발송 시점: 동의하면 언제 발송하는지 작성해 주세요.
  * API로 발송하는 경우: 언제 발송하는지 작성해 주세요. (예: 찜한 상품 가격이 내렸을 때, 매일 날씨 안내를 받기로 했을 때, 서비스 내 정지된 기능이 다시 활성화됐을 때 등)
  * 토스에게 발송을 요청하는 경우: 언제 발송되는지와 함께 발송 시점을 명확하게 작성해 주세요. (예: 매주 화요일 오후 5시에 다음 날 날씨를 알려줄 때 등)

<details>

<summary>알림동의문에 포함되는 내용</summary>

* 서비스명과 서비스 설명 (미니앱 정보에서 자동 반영)
* 알림 발송 시점 (템플릿을 만들 때 입력한 트리거 조건)
* 알림 동의 철회 경로
* 고객센터 연락처 (미니앱 정보에서 자동 반영)

</details>

**알림 발송 방법**

* 특정 조건 충족: 어떤 조건이 충족되면 알림을 발송하는지 입력해 주세요. (예: 찜한 상품 가격 인하, 정지된 기능 재활성화 등)
* 정해진 요일과 시간: 발송 주기와 유형을 입력해 주세요. (예: 매일 날씨 안내, 출석 알림 등)

미니앱에서 사용자에게 알림 수신 동의 UI를 표시하려면 [알림 동의문 요청하기 문서](https://developers-apps-in-toss.toss.im/documentation/common/growth/smart-message#id-4.-requestnotificationagreement)를 참고해 주세요.

{% hint style="info" %}
알림 동의문 호출 시 templateCode

미니앱에서 알림 동의문을 호출할 때 쓰는 `templateCode`는, 그 알림 동의문이 연결된 기능성 메시지의 발송 코드예요. 콘솔 → 스마트 발송 → '기능성' 탭에서 확인할 수 있어요.
{% endhint %}

***

### 메시지 작성 가이드

토스를 쓰는 모든 사용자에게 내 앱을 처음 소개한다는 마음으로 작성해 주세요. 사용자는 이 푸시를 '토스'라는 금융 앱의 맥락 안에서 받아들여요. 메시지가 토스답게 보이고 자연스럽게 이해되도록 써 주세요.

#### 기본 규칙

* 모든 푸시·알림은 해요체로 써요. 본문은 문장형('\~요.')으로 쓰고 문장 끝에 마침표를 찍어요. 일관된 보이스톤을 지키기 위한 규칙이에요.
* 마침표는 본문에만 써요. 제목에는 마침표를 쓰지 않아요.
* 제목은 '\~하기'나 명사형으로 써요. (기능성은 명사형으로 써요.)
* '토스 + 서비스명'으로 작성하는 방식은 쓸 수 없어요.
* 글자 수는 제목 7자, 본문 25자까지예요(공백 포함). 글씨를 크게 키워도 잘리지 않는 기준이에요. (안드로이드에서 큰 글씨 1.3배 이상이 전체의 24%예요.)
* 맞춤법과 띄어쓰기를 꼭 확인해 주세요.

#### 1) 제목과 내용이 자연스럽게 연결돼야 해요.

<details>

<summary><mark style="color:$danger;">안 좋은</mark> 예시</summary>

* \[떡볶이로?] 토스에서 MBTI 심리테스트 진단하세요. → 제목은 음식인데 내용은 MBTI라 연결이 안 돼요
* \[마이너스 통장] 공모주 청약에 쓸 수 있어요. → 통장을 만들라는 건지, 청약 소식을 알리는 건지 모호해요.

</details>

<details>

<summary>좋은 예시</summary>

* \[퍼즐 도전] 토스에서 퍼즐 맞추고 포인트 받아 가세요.
* \[출석 선물] 블록 게임 3일 출석하면 20원을 드려요.
* \[금전운 도착] 토스에서 무료로 운세를 확인해 보세요.

</details>

#### 2) 제목은 그 자체로 완성된 단어나 문장이어야 해요.

<details>

<summary><mark style="color:$danger;">안 좋은</mark> 예시</summary>

* \[문장 카드로] 토스에서 39종 카드 골라보세요.
* \[숏폼보다] 토스에서 39종 카드 골라보세요.

</details>

<details>

<summary>좋은 예시</summary>

* \[문장 카드] 토스에서 명언 고르고 원하는 디자인 선택해요.
* \[블록 게임] 시간 가는 줄 모르는 게임에 도전해 보세요.

</details>

#### 3) 게임 서비스는 '게임'이라고 밝혀요.

사용자가 클릭하기 전에 게임인지 알아야 해요. 서비스명이나 게임 내 용어만 쓰면 모를 수 있어요.

* <mark style="color:$danger;">안 좋은</mark> 예시: \[경쟁하는 퍼즐] 퍼즐 맞추고 내 집도 꾸며요.
* 좋은 예시: \[퍼즐 게임] 퍼즐 맞추고 내 집도 꾸며요.

#### 4) 아래 표현은 쓸 수 없어요.

**일부만 이해할 수 있는 은어·밈·유행어**

<details>

<summary><mark style="color:$danger;">안 좋은</mark> 예시</summary>

* \[엠블렘 등장] 오늘 새로 나온 장비 콘텐츠예요.
* \[애슬레저 맛집] 젝시믹스에서 토스로 결제하면 4천원 할인받아요.

</details>

<details>

<summary>좋은 예시</summary>

* \[새 아이템] 오늘 새로 나온 게임 아이템이 있어요.
* \[젝시믹스 할인] 토스페이로 결제하면 최대 4천원 할인받아요.

</details>

**특정인 이름**

* <mark style="color:$danger;">안 좋은</mark> 예시: \[내 이상형] 장원영인가요, 안유진인가요?
* 좋은 예시: \[이상형 월드컵] 토스에서 이상형에 가까운 아이돌을 골라봐요.

**과장된 광고성 표현**

* 광고성 표현: 지금 바로!, 초특가!, 역대급, 긴급!, 대박
* 불안 조성: 놓치면 후회해요, 지금 안 하면 못 해요
* 과도한 기호: 느낌표, 이모지

**자극적인 소재**

정치, 범죄, 사망 같은 민감한 소재는 피해요.

불안감을 조성하지 않아요. 놓치면 후회한다는 뉘앙스나 사용자를 과도하게 불안하게 하는 단어는 쓰지 않아요. 상황만 차분히 전달해 주세요.

<details>

<summary><mark style="color:$danger;">안 좋은</mark> 예시</summary>

* \[불륜 지수] 토스에서 재미로 테스트해봐요.
* \[오늘의 팁] 임신 중 남편이 사망하면 상속순위는 어떻게 될까요?
* \[아직도 안 써보셨나요?] 예산을 항상 초과해도 괜찮으신가요?
* \[유효기간이 곧 끝나요] 정말 혜택을 놓치시겠어요?

</details>

**띄어쓰기 생략**

글자 수를 맞추려고 띄어쓰기를 생략하지 않아요.

* <mark style="color:$danger;">안 좋은</mark> 예시: \[내타자정확도는] 눈 감고 문장 치기 도전해보세요
* 좋은 예시: \[내 타자 실력] 토스에서 눈 감고 문장 치는 테스트해 보세요.

**어색한 표현**

문법적으로 틀리진 않지만 자연스럽지 않은 표현이에요. 소리 내어 읽어보면 금방 알 수 있어요.

* <mark style="color:$danger;">안 좋은</mark> 예시: \[나만의 소설] 다른 장르도 선택해 보세요. 토스 무료예요. → 조사가 빠져서 어색해요. '토스' 서비스를 이용하는 게 무료라고 읽혀요.
* 좋은 예시: \[나만의 소설] 토스에서 무료로 만들 수 있어요.

#### 5) 게임 장르를 활용할 경우 참고해 주세요.

* 특정 게임 이름에서 파생된 장르는 쓸 수 없어요. (예: 로그라이크, 메트로배니아, 소울라이크, 뱀서라이크 등)
* 사용자가 직관적으로 이해할 수 있는 장르명을 써요.

| 장르             | Do                   |
| -------------- | -------------------- |
| **방치형 RPG**    | **방치형 게임 또는 RPG 게임** |
| **캐주얼 MMORPG** | **캐주얼 게임**           |
| **오픈월드 액션**    | **액션 게임**            |
| **쓰리매치 퍼즐**    | **퍼즐 게임**            |
| **턴제 전략 RPG**  | **전략 게임 또는 RPG 게임**  |
| **2D 횡스크롤 액션** | **액션 게임**            |

<details>

<summary>장르를 조합하거나 세분화하지 않아요.</summary>

<table data-search="false"><thead><tr><th>장르</th><th>뜻</th><th>대체 방향</th></tr></thead><tbody><tr><td><strong>MMORPG</strong></td><td>Massively Multiplayer Online RPG</td><td><strong>RPG 게임</strong></td></tr><tr><td><strong>ARPG</strong></td><td>Action RPG</td><td><strong>RPG 게임 또는 액션 게임</strong></td></tr><tr><td><strong>SRPG</strong></td><td>Simulation/Strategy RPG</td><td><strong>RPG 게임 또는 전략 게임</strong></td></tr><tr><td><strong>JRPG</strong></td><td>Japanese RPG</td><td><strong>일본 RPG 게임</strong></td></tr><tr><td><strong>FPS</strong></td><td>First-Person Shooter</td><td><strong>슈팅 게임</strong></td></tr><tr><td><strong>TPS</strong></td><td>Third-Person Shooter</td><td><strong>슈팅 게임</strong></td></tr><tr><td><strong>MOBA</strong></td><td>Multiplayer Online Battle Arena</td><td><strong>전략 게임</strong></td></tr><tr><td><strong>RTS</strong></td><td>Real-Time Strategy</td><td><strong>전략 게임</strong></td></tr></tbody></table>

</details>

<details>

<summary>게임 관련 전문 용어는 사용할 수 없어요.</summary>

<table data-search="false"><thead><tr><th>장르</th><th>뜻</th><th>대체 방향</th></tr></thead><tbody><tr><td><strong>핵앤슬래시</strong></td><td>Hack and Slash</td><td><strong>액션 게임</strong></td></tr><tr><td><strong>덱빌딩</strong></td><td>Deck Building</td><td><strong>카드 게임</strong></td></tr><tr><td><strong>오토배틀러</strong></td><td>Auto Battler</td><td><strong>전략 게임</strong></td></tr><tr><td><strong>불렛헬</strong></td><td>Bullet Hell</td><td><strong>슈팅 게임</strong></td></tr><tr><td><strong>런앤건</strong></td><td>Run and Gun</td><td><strong>액션 게임 &#x26; 스포츠 게임</strong></td></tr><tr><td><strong>하이퍼캐주얼</strong></td><td>Hyper Casual</td><td><strong>캐주얼 게임</strong></td></tr><tr><td><strong>샌드박스</strong></td><td>Sandbox (게임 맥락)</td><td><strong>시뮬레이션 게임</strong></td></tr></tbody></table>

</details>


# 공유 리워드

앱인토스의 공유 리워드 기능을 도입해, 기존 사용자 초대부터 신규 유입, 재방문과 재공유로 이어지는 자연스러운 바이럴 선순환 구조를 만들어 보세요.

### 공유 리워드란 무엇인가요

공유 리워드는 연락처 모듈을 사용해 토스를 쓰는 사용자의 연락처를 불러오고, 공유를 완료한 사용자에게 리워드를 제공하는 기능이에요. 공유는 토스 알림 푸시로 진행돼요.

공유 리워드는 게임 서비스와 비게임 서비스 모두 쓸 수 있어요. 서비스 유형에 따라 사용자에게 노출되는 푸시 문구는 달라요.

<figure><img src="/files/WuUnYJEiVWChNarrOsRX" alt=""><figcaption></figcaption></figure>

***

### 공유 리워드의 좋은 점

* 친구 초대에 보상을 연결해 자연스럽게 트래픽을 만들 수 있어요.
* 보상을 기준으로 공유를 유도해, 추가 비용 없이 마케팅 효과를 얻을 수 있어요.
* 서비스 안에서만 쓸 수 있는 재화를 제공해 재방문을 유도할 수 있어요.

***

### 발송 제한 및 초기화 기준

공유 리워드는 하루 단위로 발송 제한이 초기화돼요.

* 하나의 리워드 ID를 기준으로, 한 사용자에게 하루에 한 번만 공유 리워드를 보낼 수 있어요.
* 예를 들어 특정 리워드 ID로 오늘 사용자 A에게 공유 리워드를 발송했다면, 같은 리워드 ID로는 내일 다시 발송할 수 있어요.
* 다른 리워드 ID를 쓰면 발송 제한이 각각 독립적으로 적용돼요.

{% hint style="info" %}
**꼭 참고해 주세요**

* 과도한 팝업이나 강제적인 공유 유도는 피해 주세요.
* 보상을 받을 수 있는 조건은 사용자가 쉽게 이해할 수 있도록 정확하게 안내해 주세요.
* 현금성 보상이나 사행성 보상은 등록할 수 없어요.
  {% endhint %}

***

### 콘솔에서 설정하기

앱인토스 콘솔 왼쪽 메뉴에서 '공유 리워드'를 선택해 주세요.

<figure><img src="/files/XmlekKLRT0tuOTajvsET" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/0Fsag4WkNuNY63RSqpsd" alt=""><figcaption></figcaption></figure>

**리워드 이름**

관리자가 내부에서 구분해 쓸 리워드 이름이에요. 운영 화면이나 정산 과정에서 식별하기 쉬운 이름으로 지어 주세요. 사용자에게 노출되는 문구와 다를 수 있으니, 운영 목적에 맞게 구체적으로 적는 게 좋아요.

* 예) 출석 체크 보상 100 하트 / 친구 초대 이벤트 보석 지급 / 신규 가입 웰컴 리워드

**리워드 화면**

사용자에게 실제로 지급할 리워드 정보를 입력해요.

* 리워드: 지급할 리워드의 단위를 입력해요. 하트, 보석, 포인트처럼 사용자가 인지하는 보상 이름을 적어 주세요.
* 수량 및 금액: 사용자에게 지급할 리워드 수량이에요. 이벤트나 정책에 맞는 정확한 수량과 금액을 입력해 주세요. 예를 들어 하트 100개를 지급하려면 수량에 100을 입력해요.

***

### 개발 연동하기

공유 리워드 [SDK 연동 방법과 이벤트 처리 코드 예제](https://developers-apps-in-toss.toss.im/documentation/common/growth/share/reward)를 확인할 수 있어요.


# OG 이미지

### OG 이미지(Open Graph) 란 무엇인가요

OG 이미지는 미니앱의 공유 링크가 카카오톡 같은 소셜 플랫폼에서 공유될 때 함께 보이는 대표 이미지예요.\
사용자가 링크를 받았을 때 가장 먼저 인지하는 시각적 요소라서 첫인상에 큰 영향을 줘요.\
앱인토스에서는 파트너사가 더 효과적으로 마케팅할 수 있도록 OG 이미지 기능을 제공해요.\
미니앱 링크를 공유하면 자동으로 노출되는 썸네일 이미지를 미리 설정할 수 있어요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FvIk2lPJgtafSzoW19s3H%2Fimage.png?alt=media&#x26;token=ae1cae15-c6bf-469d-a452-8910fa36acf0" alt=""><figcaption></figcaption></figure>

***

### OG 이미지를 사용하면 어떤 점이 좋나요

**미니앱 인지도를 높일 수 있어요**

미니앱의 분위기나 핵심 메시지를 담은 이미지를 노출할 수 있어요. 사용자는 링크만 보고도 어떤 서비스인지 쉽게 이해할 수 있어요.

**미니앱 접속으로 이어질 가능성이 높아져요**

시각적으로 눈에 띄는 이미지가 함께 보이면 링크를 눌러볼 확률이 높아져요. 그 결과 미니앱 방문으로 자연스럽게 이어질 수 있어요.

**전달하고 싶은 정보를 효과적으로 보여줄 수 있어요**

이벤트나 캠페인처럼 꼭 알리고 싶은 내용을 이미지 안에 담아 직관적으로 전달할 수 있어요.

***

### OG 이미지 규칙

아래 내용은 OG 이미지를 사용할 때 반드시 지켜야 하는 기준이에요.\
모니터링 과정에서 규칙을 지키지 않은 경우, 수정 요청이나 노출 제한이 있을 수 있어요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FiMkJkyBSSaJFk2GMAypC%2Fimage.png?alt=media&#x26;token=3a102b99-2411-4270-bc10-ce6f0dc59229" alt=""><figcaption></figcaption></figure>

**이미지 사이즈는 1200 × 600으로 만들어 주세요**

OG 이미지는 가로 1200픽셀, 세로 600픽셀 크기로 제작해야 해요.

**고화질 이미지를 사용해 주세요**

선명한 이미지를 등록해 주세요. 저화질 이미지는 완성도를 떨어뜨리고 서비스 인상에도 부정적인 영향을 줄 수 있어요.

**민감하거나 부적절한 용어를 포함하지 마세요**

비속어, 은어, 정치적인 표현처럼 자극적인 용어는 사용하지 말아 주세요. 누구에게나 불편하지 않은 표현을 사용하는 것이 중요해요.

**썸네일에 텍스트를 과도하게 넣지 마세요**

눈길을 과도하게 끄는 크기나 디자인의 텍스트는 사용할 수 없어요. 텍스트가 많으면 이미지가 복잡해 보일 수 있어요. 다만 로고와 함께 사용하는 슬로건이나, 이미지를 이해하는 데 도움이 되는 보조 텍스트는 적절한 크기와 위치라면 사용할 수 있어요.

**라이팅 가이드를 참고해 주세요**

토스의 보이스톤에 맞는 문구를 사용해 주세요. 자세한 기준은 [UX 라이팅 가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/design/consumer-ux-guide)에서 확인할 수 있어요.

***

### OG 이미지 적용하기

OG 이미지의 개념과 규칙을 확인했다면, 이제 미니앱에 실제로 적용해 볼 차례예요.\
토스 앱 공유 링크를 만드는 방법은 개발 문서에서 확인할 수 있어요.


# 외부 광고 가이드

앱인토스에 오픈한 미니앱 서비스를 마케팅할 때 필요한 '마케팅 메시지', '토스 미니앱 배지', '마케팅 디자인 템플릿' 등을 확인할 수 있어요. 이를 활용하면 파트너사가 더 효과적으로 마케팅할 수 있어요.

### 외부 광고 가이드란

앱인토스의 외부 광고 가이드는 앱인토스에 론칭한 미니앱 서비스를 외부 채널에서 홍보할 때 지켜야 할 기준이에요. '토스 미니앱'과 '앱인토스' 브랜드를 정확하게 쓰고, 정책을 지킬 수 있도록 돕는 게 목적이에요.

* **앱인토스(Apps-in-Toss)**: 미니앱을 오픈할 수 있는 시스템이자 생태계를 가리키는, 백엔드·파트너사 대상의 공식 명칭이에요.
* **토스 미니앱(Toss Mini App)**: 토스 앱 안에서 B2C 사용자에게 제공되는 UI 및 마케팅·홍보 용어예요.
* **\[서비스명]**: 토스 미니앱에 노출되는 이름이자, 토스 앱에서 검색할 수 있는 이름이에요.

***

### 마케팅 **메시지**

앱인토스에 오픈한 미니앱을 마케팅할 때, 헤드라인과 카피 문구는 토스 앱의 사용자 경험을 고려해 작성해야 해요. '토스'에서 오픈한 서비스를 찾아보도록 유도하는 콜투액션(CTA)이나, '토스 미니앱'으로 인지할 수 있는 문구를 활용해야 해요.

**대표 문구 템플릿**

아래 메시지와 소셜 해시태그(#)를 활용해서 미니앱 서비스를 알려보세요.

* \[서비스명]을 '토스 미니앱'으로 만나보세요.
* '토스'에서 \[서비스명]을 만나보세요.
* '토스'에서 \[서비스명]을 검색해보세요.
* \[서비스명]을 토스에서 설치 없이 바로 사용해보세요.
* \#서비스명 #토스미니앱 #토스에서만나보세요

**CTA 버튼 예시**

퍼포먼스 마케팅처럼 미니앱 서비스로 전환을 유도할 때, 아래 메시지로 앱 이용을 유도해보세요.

* 토스 미니앱 사용하러 가기
* 토스에서 \[서비스명] 사용하기
* 토스에서 \[서비스명] 써보기

**'토스 미니앱 오픈' 외의 용어는 쓸 수 없어요.**

마케팅 채널에서는 '토스 미니앱 오픈'으로만 표현해야 해요. 아래 표현은 쓰지 말아 주세요.

* 토스 공식 파트너사
* 토스 입점사
* 토스 제휴사

**서비스 제공 주체가 헷갈리지 않게 작성해 주세요.**

파트너사 서비스의 제공 주체가 토스로 오인되거나, 토스와의 제휴·운영 범위에 대해 사용자에게 혼동을 줄 수 있는 이미지·표현은 쓸 수 없어요.

* 이제 토스에서 광고하세요.
* 국민 절반이 쓰는 만큼, 효과는 확실합니다.

***

### 토스 미니앱 배지

모든 디지털·인쇄 마케팅 자료에 아래 파일로 첨부한 '토스 미니앱 배지'를 활용해 앱 이용을 유도해 보세요.

* 블랙 배지를 기본형으로 써요. 화이트 배지는 블랙 배지를 시각적으로 활용하기 어렵다고 판단될 때만 쓸 수 있어요.
* 레이아웃이나 동영상 하나당 '토스 미니앱 배지'를 한 개만 쓸 수 있어요.
* 배지가 중심 아트워크가 되지 않도록, 앱 이미지나 주요 메시지의 하위에 배치해요.
* 배지를 수정하거나, 각도를 바꾸거나, 애니메이션 효과를 넣으면 안 돼요.

{% file src="/files/IhhgY4uFKiEQI24LoiMk" %}

<figure><img src="/files/5rOvJS8x73O9xsCpIye7" alt=""><figcaption></figcaption></figure>

***

### 마케팅 디자인 템플릿

토스 미니앱 출시 소식을, 아래 첨부한 마케팅 디자인 템플릿으로 손쉽게 알려보세요. 별도 디자인 작업 없이 \[서비스명]과 \[서비스 로고]를 넣어 여러 사이즈, 다양한 스타일의 이미지를 만들 수 있어요. 아래 첨부 파일을 확인해 주세요.

{% file src="/files/HHxlDeohCcz8CPvkgWMI" %}

<figure><img src="/files/jYYIcuybEc4pKhhg2yvt" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/skiLuZ6l9mDn2dYO8Iry" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/fPYkAEI7gERaReu4VXVP" alt=""><figcaption></figcaption></figure>

***

### 앱인토스 로고

앱인토스 로고는 주로 홍보나 보도자료에서 \[회사명(개발사명)]과 함께 활용할 수 있어요. 표기할 때는 아래처럼 ' | '(Bar) 표기를 써야 해요. 필요하면 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의해 주세요.

<figure><img src="/files/3vUkTEXH1Wjx8SWXWgDQ" alt=""><figcaption></figcaption></figure>

***

### 권리 고지 및 법적 안내

토스의 상표·로고 중, 이 마케팅 가이드에서 제공하는 토스 미니앱 배지·마케팅 디자인 템플릿·토스 미니앱 UI를 제외한 나머지 로고를 쓰려면 토스의 확인이 필요해요. 아래 기준을 확인해 주세요.

* 사용 가능: 토스 미니앱 배지, 마케팅 디자인 템플릿, 토스 미니앱 UI
* 사전 확인: 앱인토스 로고
* 사용 불가: 토스 로고 단독 사용, 토스 UI·아이콘 등

사전 확인이 필요한 로고(앱인토스 로고)를 쓰려면 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의해 주세요.

앱인토스 서비스를 위해 파트너사에 제공된 프로그램으로 만든 산출물의 소유권과 지식재산권은 토스에 있어요. 이 산출물은 서비스 목적 범위 안에서만 활용할 수 있어요.

***

### 문의 및 검수 요청 채널

마케팅과 관련해 궁금한 점이 있거나 검수 요청이 필요하다면 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의해 주세요.

검수 기준은 아래와 같아요.

* 검수 필수: 신규 마케팅 캠페인 오픈 전, 보도자료 배포 전, 토스 로고·배지를 편집한 경우
* 검수 권장: 제공된 마케팅 디자인 템플릿의 광고 카피 변경, 배지 위치 조정 등
* 검수 제외: 자체 SNS 포스팅 중 일반적인 서비스 소개

***

### 위반 시 조치

이 가이드라인을 위반한 사유가 확인되면, 파트너사에 소명을 요구하거나 파트너사의 앱인토스 서비스 이용을 제한하는 등의 조치를 할 수 있어요.

***

### 자주 묻는 질문

<details>

<summary>SNS (인스타그램, 페이스북 등) 에 서비스 출시를 알리고 싶은데, 검토를 받아야 하나요?</summary>

파트너사의 SNS에 포스팅을 할 때 단순 서비스에 대한 소개인 경우 검토가 필요하지 않아요. 다만 게시 내용 중 "앱인토스 로고" 사용을 희망할 경우 게재가 되는 이미지에 대한 확인이 필요하기 때문에 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의해 주세요.

(위 가이드에 벗어난다면 게시할 수 없어요.)

</details>

<details>

<summary>진행하려는 마케팅이 가이드에 부합하는지 확인이 어려워요.</summary>

관련하여 어려움이 있으신 경우 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의해 주세요. 면밀하게 확인한 다음 안내해 드릴 수 있어요.

</details>

<details>

<summary>가이드에 나온 마케팅 디자인 템플릿이나 배지는 파트너사에서 가공이 불가한가요?</summary>

제공된 마케팅 디자인 템플릿의 광고 카피를 변경하거나 배지 위치를 조정하는 경우 검수를 받아야 해요. 그외 가공은 어려우니 참고해 주세요.

</details>

<details>

<summary>인플루언서 마케팅이나 블로그/커뮤니티 포스팅 시에도 동일한 가이드가 적용되나요?</summary>

인플루언서 마케팅이나 블로그/커뮤니티 포스팅 시에도 해당 가이드를 참고해 주세요. 궁금한 것이 있다면 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의해 주세요. 확인 후 안내해 드릴 수 있어요.

</details>


# 토스애즈 픽셀 연동

토스애즈를 사용하고 있다면 앱인토스 미니앱에 토스 픽셀을 설치해 전환 이벤트를 수집할 수 있어요. 수집한 데이터를 기반으로 광고 성과를 최적화할 수 있어요.

{% hint style="info" %}
**토스 픽셀은 Web 환경에서만 동작해요**

React Native 환경에서는 지원되지 않아요.
{% endhint %}

### 콘솔에서 설정하기

픽셀 연동을 시작하려면 토스 광고 콘솔에서 전환 추적 코드를 먼저 발급해야 해요.

#### 1. 토스 광고 콘솔 로그인

전달받은 계정으로 토스 광고 콘솔에 로그인해 주세요.

#### 2. 전환 추적 코드 생성하기

광고 도구 > 전환 및 추적 연동 메뉴를 선택해주세요.

전환 추적 코드를 생성해 주세요. 전환 추적 코드는 광고 계정 단위로 발급돼요.

생성된 전환 추적 코드 ID를 복사해 주세요.


# 분석

미니앱의 사용자 행동과 핵심 성과를 측정하는 방법을 안내해요. 이벤트 로그를 남기고, 주요 지표를 정의해 운영과 개선에 활용할 수 있어요.

* [로그(이벤트) 가이드](/guide/analytics/logging)
* [핵심 지표](/guide/analytics/conversion-metrics)


# 대시보드

앱인토스 콘솔 대시보드에서는 **DAU, 성별, 연령, 리텐션 등 미니앱의 주요 지표를 한눈에 확인할 수 있어요.** \
대시보드는 파트너사의 미니앱 성장을 가속화하는 전략적 도구로, 미니앱의 성장과 성공을 위해 반드시 활용해야 할 핵심 기능이에요. \
데이터를 꾸준히 확인하고 개선에 활용하면 트래픽 증가와 전환율 향상이라는 실질적인 효과를 경험할 수 있어요.

<figure><img src="/files/W455nTVRUmpenlXdvzrI" alt=""><figcaption></figcaption></figure>

미니앱이 출시되고 로그가 수집되면, 콘솔 홈 메뉴의 **‘분석하기’** 탭에서 대시보드를 확인할 수 있어요. 데이터 확인 전, 아래 내용을 꼭 확인해 주세요.

* **SDK 0.0.26 이상**이 적용된 미니앱만 데이터 확인이 가능해요.
* 샌드박스 또는 출시 준비 단계의 데이터는 제공되지 않으며, **실제 출시 이후 데이터만** 확인할 수 있어요.
* 데이터는 **서비스 출시 다음 날부터** 확인할 수 있어요.

{% hint style="info" %}
**향후 제공 예정 기능**

SDK **0.0.36 이상**을 적용하면 미니앱의 **체류 시간 데이터**를 확인할 수 있어요. 해당 데이터는 추후 대시보드에 제공될 예정이에요.
{% endhint %}

***

### 대시보드에서 확인할 수 있는 데이터

우측 상단의 일별, 주별, 월별 등 기간을 설정해서 확인할 수 있고, 엑셀 파일로 다운 받을 수 있어요.

* 대시보드 데이터는 매일 오전 9시 이후 전일 데이터까지 확인할 수 있어요.
* DAU의 일간데이터는 매 정시 2시간 전까지의 데이터를 확인할 수 있고, 주간 및 월간 데이터도 함께 업데이트 돼요.
  * 예: 17시인 경우 15시까지의 데이터 확인 가능

<figure><img src="/files/Ndq2MKOrgFJvAtODekKO" alt=""><figcaption></figcaption></figure>

**DAU(일간 활성 사용자 수)**

* 서비스 성장세를 가장 직관적으로 보여주는 핵심 지표예요.
* 최근 4주간 하루 단위로 앱을 사용한 사용자 수를 확인할 수 있어요.
* 미니앱 접속 후 ‘서비스를 이용했다’ 라고 판별할 수 있는 로그를 기준으로 측정돼요.

**OS, 토스앱 버전**

* 최근 4주간 안드로이드, iOS 사용자를 구분해서 볼 수 있어요.
* 토스 앱 버전에 따른 사용자도 확인할 수 있어요.
* 운영체제별 최적화 전략 수립에 활용할 수 있어요.

**성별 분포**

* 최근 4주간 남성, 여성 사용자 수를 확인할 수 있어요.
* 타겟 맞춤형(세그먼트 구성 등) 콘텐츠 기획에 꼭 필요한 데이터예요.

**연령 분포**

* 최근 4주간 10대부터 60대 이상까지 연령대별 사용자 수를 확인할 수 있어요.
* 주요 고객층 파악과 마케팅 전략 설계(세그먼트 구성 등)에 활용할 수 있어요.

<figure><img src="/files/218MWKuaXpMyMMIvoORT" alt=""><figcaption></figcaption></figure>

**유입경로**

유입경로는 사용자가 토스 앱 내에서 어떤 경로를 통해 서비스에 진입했는지를 의미해요. \
각각의 유입경로는 사용자 유입 방식과 사용자 기대가 서로 다르다는 것을 알 수 있으며, 이 차이는 이후의 서비스 이용 지속성(리텐션)에 영향을 미칠 수 있어요.

<figure><img src="/files/ahhl8igaSYnYF37Ta5OE" alt=""><figcaption></figcaption></figure>

콘솔 대시보드에서 확인할 수 있는 유입경로 유형은 다음과 같아요.

* 전체탭 (토스 앱 > 오른쪽 아래 전체를 통한 유입)
* 검색 (전체탭 내의 오른쪽 상단 검색 기능을 통한 유입)
* 혜택탭 (토스 앱의 혜택 탭에 프로모션을 노출한 경우 해당 경로를 통한 유입)
* 푸시/알림 (광고성 또는 기능성 푸시/알림을 통한 유입)
* 게임홈 (전체탭 내의 게임 메뉴를 통한 유입)
* 연락처 모듈 (연락처 공유하기 링크를 통한 유입)
* 기타 (그 외 모든 유입)

모든 유입 경로를 확인하려면 유입경로 레퍼러 문서를 참고해 주세요.

**리텐션 및 유입 경로별 리텐션(재방문율)**

사용자가 첫 방문 이후 일정 기간 내에 다시 앱을 이용한 비율을 확인할 수 있어요. \
서비스 충성도와 만족도를 측정하는 핵심 지표예요. \
우측 상단의 ‘유입 경로별 보기’ 를 누르면 유입 경로에 따른 리텐션(재방문율)도 함께 알 수 있어요.

<figure><img src="/files/lbKnoQNkoNs8evQXPSDi" alt=""><figcaption></figcaption></figure>

유입 경로별 리텐션을 확인하게 되면 아래와 같은 인사이트를 얻을 수 있어요.

**① 실제 서비스에 기여하는 유입경로를 파악할 수 있어요.**

* 유입 수는 적지만 리텐션이 높은 경로 → **서비스에 적합한 사용자**
* 유입 수는 많지만 리텐션이 낮은 경로 → **단기 반응형 사용자**

**② 어떤 노출 방식이 장기 이용으로 이어지는지, 단기 활성화에만 기여하는지 파악할 수 있어요.**

* 탐색형 유입: 전체탭, 혜택탭, 게임홈
* 목적형 유입: 검색
* 외부 자극형 유입: 푸시/알림, 프로모션


# 로그(이벤트) 가이드

데이터 로깅은 미니앱 성과 개선에 가장 중요한 도구예요. 사용자 행동과 요소 노출을 기록하면 이탈 지점을 찾고 전환율을 개선하며 마케팅 전략을 고도화할 수 있어요. 단순히 데이터를 쌓는 것이 목적이 아니라, **사용자가 어디에서 멈추는지**와 **무엇에 반응하는지**를 파악하는 것이 핵심이에요.

***

### 요약 빠르게 보기

* 페이지 이동 로그는 자동 기록돼요. 추가 설정 불필요해요.
* 클릭 이벤트와 요소 노출 이벤트는 직접 설정하면 더 정교하게 분석할 수 있어요.
* SDK 버전 `0.0.26` 이상에서 데이터 확인이 가능해요.

***

### 로깅을 잘 활용하는 원칙

* 의미 있는 상호작용만 기록해요 버튼 클릭, 상품 조회, 결제 완료처럼 실제 분석 가치가 있는 이벤트 중심으로 기록하세요.
* 파라미터를 구체적으로 설정해요 예를 들어 `button_name: "subscribe_button"`처럼 구체적으로 지정하면 무엇이 성과를 내는지 명확해져요.
* 전환 퍼널을 중심으로 설계해요 각 단계별 이탈률을 측정해 UI 개선이나 프로모션 타겟팅에 활용하세요.

***

### 사전 요구사항

* SDK 버전 `0.0.26` 이상이어야 해요.
* 샌드박스나 출시 준비 단계 데이터는 제공되지 않으며 실제 런칭 후 데이터부터 집계돼요.
* 서비스 런칭 다음 날부터 분석 화면에서 데이터를 확인할 수 있어요.

SDK에서 `Analytics` 객체를 사용하는 방법은 사용자 행동 기록하기 문서를 참고해 주세요.

***

### 클릭 이벤트 로깅 예시

사용자가 버튼을 클릭했을 때 이벤트를 전송하는 기본 패턴이에요.

```javascript
import { Analytics } from '@apps-in-toss/web-framework';

document.getElementById('myButton').addEventListener('click', function () {
  Analytics.click({ button_name: 'my_button' });
  // 클릭 후 실행할 추가 동작을 여기에 작성하세요.
});
```

* `Analytics.click`는 클릭 이벤트를 로깅해요.
* `button_name`은 버튼을 식별하는 값이에요. 가능한 한 화면과 기능을 쉽게 구분할 수 있는 이름을 사용하세요.

***

### 요소 노출 이벤트 로깅 예시

특정 요소가 화면에 보일 때 노출 이벤트를 보내면 어떤 콘텐츠가 주목받는지 알 수 있어요.

```javascript
import { Analytics } from '@apps-in-toss/web-framework';

const target = document.getElementById('impressionItem');

const observer = new IntersectionObserver(
  ([entry]) => {
    if (entry.isIntersecting) {
      Analytics.impression({ item_id: target.dataset.itemId });
      observer.disconnect();
    }
  },
  { threshold: 0.1 },
);

observer.observe(target);
```

* `IntersectionObserver`는 요소가 화면에 10% 이상 보일 때 콜백을 실행해요.
* `Analytics.impression`은 노출 이벤트를 로깅해요.
* `item_id`는 노출된 아이템을 식별하는 값이에요.

**HTML 예시**

```html
<div id="impressionItem" data-item-id="1234">노출을 감지할 요소
```

***

### 이벤트 파라미터

이벤트 파라미터는 이벤트와 함께 전달하는 추가 정보예요. 같은 이벤트라도 어떤 파라미터를 함께 보내느냐에 따라, 콘솔에서 더 세분화된 분석이 가능해요.

이때 `log_name`은 콘솔에 표시되는 이벤트 이름이에요. 콘솔의 **분석 > 이벤트** 화면에서 이벤트를 구분하는 기준이 되기 때문에, 의미가 명확한 이름을 사용하는 것이 중요해요.

예를 들어 `product_detail_screen`라는 화면 이벤트에 `product_id`, `product_category` 같은 파라미터를 함께 보내면 어떤 상품이나 카테고리가 더 많이 조회되는지 확인할 수 있어요.

**이벤트 파라미터 예시**

상품 상세 화면에 진입했을 때, 화면 이벤트와 함께 현재 보고 있는 상품 정보를 파라미터로 전달할 수 있어요.

```javascript
import { Analytics } from '@apps-in-toss/web-framework';

Analytics.screen({
  log_name: 'product_detail_screen',
  product_id: 'prod_123',
  product_name: '무선 이어폰',
  product_category: 'electronics',
  price: 29900,
});
```

* log\_name은 콘솔에 표시되는 이벤트 명이에요.
* 나머지 값들은 해당 이벤트에 함께 저장되는 커스텀 파라미터예요.

**콘솔에서 어떻게 보이나요**

위와 같이 이벤트를 전송하면, 콘솔 > **분석 > 이벤트** 메뉴에서 이벤트를 확인할 수 있어요. 이벤트 상세 화면에서는 다음 정보를 확인할 수 있어요.

* 이벤트 발생 추이(그래프)
* 최근 발생 수
* 함께 전송된 파라미터 목록
  * `product_id`
  * `product_name`
  * `product_category`
  * `price`

파라미터 목록에서 특정 키를 선택하면, 해당 파라미터의 실제 값과 발생 현황을 확인할 수 있어요.

**Click 이벤트 파라미터 예시**

사용자가 상품 구매 버튼을 클릭했을 때, 클릭 이벤트와 함께 상품 정보를 전달할 수 있어요.

```javascript
import { Analytics } from '@apps-in-toss/web-framework';

Analytics.click({
  log_name: 'purchase_button_click',
  product_id: 'prod_123',
  product_name: '무선 이어폰',
  product_price: 29900,
  product_category: 'electronics',
});
```

이렇게 전송한 이벤트는 콘솔에서 `purchase_button_click` 이벤트로 집계돼요. 이를 통해 다음과 같은 분석이 가능해요.

* 어떤 상품이 가장 많이 클릭됐는지
* 어떤 카테고리의 상품이 전환으로 이어지는지
* 가격대별 클릭 패턴 비교

***

### 콘솔에서 데이터 확인하기

로깅 데이터는 관리 콘솔의 **분석 > 이벤트** 메뉴에서 확인해요. 해당 화면에서 클릭률, 노출 대비 전환율, 주요 이탈 지점을 바로 볼 수 있어요.

***

### 베스트 프랙티스

* 이벤트 이름과 파라미터는 표준화해요 팀 내 이벤트 네이밍 규칙을 만들고 일관되게 사용하세요. 예: `category_action_label` 형태
* 불필요한 이벤트는 기록하지 마세요 너무 많은 이벤트는 노이즈가 돼요. 분석 목적을 기준으로 선별하세요.
* 추가 속성은 구조화해서 전송하세요 예: `item_id`, `item_category`, `price`, `position` 등을 포함하면 세분화된 분석이 가능해요.
* 개인정보나 민감 정보는 로깅하지 마세요 사용자 식별자 사용 시 익명화 또는 해시 처리 정책을 따르세요.
* 에러 핸들링과 재시도 로직을 마련하세요 네트워크 실패로 이벤트 전송이 실패할 때를 대비해 큐잉 또는 재시도 전략을 적용하면 데이터 유실을 줄일 수 있어요.

***

### 트러블슈팅 요약

* 아이템 노출이나 클릭이 기록되지 않을 때
  1. `Analytics` 호출 위치가 DOM 상에 존재하는지 확인하세요.
  2. 콜백 등록 시점이 늦어 이벤트를 놓치지 않았는지 확인하세요. 스크립트는 가급적 상단에 배치하세요.
* 콘솔에 데이터가 보이지 않을 때
  1. SDK 버전이 `0.0.26` 이상인지 확인하세요.
  2. 서비스가 실제로 런칭되어 있는지 확인하세요. 샌드박스 데이터는 제공되지 않아요.
* 이벤트 파라미터가 비어 있을 때
  1. 전송하는 객체의 키 이름과 값이 올바른지 확인하세요.
  2. JSON 직렬화 오류 등 클라이언트 측 에러가 없는지 확인하세요.


# 핵심 지표

핵심 지표는 미니앱의 활성 사용자와 핵심 목표 행동을 정의하는 기준이에요. 이 지표를 바탕으로 우리 앱을 좋아할 만한 사용자를 찾아 노출하고 추천해요. 핵심 지표를 잘 설정한 앱일수록 더 적합한 사용자에게 노출될 기회가 커져요.

### 핵심 지표를 설정하는 이유

* 노출·추천 최적화: 활성 사용자와 비슷한 사용자를 찾아, 우리 앱을 좋아할 만한 사용자에게 노출하고 추천해요.
* 성과 분석: 일회성 방문이 아니라 실제로 재방문이 많은 사용자가 얼마나 늘고 있는지 확인할 수 있어요.
* 운영·개선: 파트너사 내부에서도 "핵심 지표가 무엇인지" 합의하는 기준이 돼요.

***

### 콘솔에서 설정하기

앱인토스 콘솔 → 워크스페이스 선택 → 미니앱 선택 → 왼쪽 메뉴 '핵심 지표'를 선택해 주세요.

<figure><img src="/files/9BwUnDrbdGuSusjeEEoK" alt=""><figcaption></figcaption></figure>

#### 활성 지표

활성 지표는 내 앱을 자주 쓰는 사용자를 정의하는 기준이에요. 앱당 1개만 설정할 수 있어요.

활성 지표는 이렇게 활용돼요.

* 노출·추천: 토스가 활성 사용자와 비슷한 사용자를 찾아, 우리 앱을 좋아할 만한 사용자에게 노출하고 추천해요.
* 성과 분석: 일회성 방문이 아니라 실제로 재방문이 많은 사용자가 얼마나 늘고 있는지 확인할 수 있어요.

**1) 템플릿 선택하기**

3가지 템플릿 중에서 선택할 수 있어요. 선택한 뒤 원하는 값을 입력하면 바로 등록할 수 있어요.

**2) 직접 조합하기**

원하는 템플릿이 없다면, 커스텀 이벤트를 활성 지표로 설정할 수 있어요.

* 지표 이름: 팀에서 구분할 수 있도록, 어떤 지표인지 드러나게 적어 주세요.
* 설명: 지표를 한눈에 이해할 수 있도록 짧게 적어 주세요.
* 이벤트를 OR 조건으로 조합하고, 파라미터도 함께 정의할 수 있어요.

#### 전환 지표

전환 지표는 미니앱에서 가장 중요하게 보고 싶은 사용자 행동(핵심 목표 행동)을 정의하는 기준이에요. 단순히 앱을 실행하거나 화면을 조회하는 것만이 아니라, 서비스 목적에 가까운 순간(예: 회원가입 완료, 첫 결제 완료, 핵심 콘텐츠 소비 등)을 전환으로 두는 게 좋아요. 전환 지표는 최대 3개까지 등록할 수 있어요.

**1) 템플릿 선택하기**

자주 쓰는 지표를 토스팀이 템플릿으로 만들어 뒀어요. 선택만 하면 바로 등록할 수 있어요.

**2) 직접 조합하기**

* 지표 이름: 팀에서 구분할 수 있도록, 어떤 전환인지 드러나게 적어 주세요.
* 설명: 지표를 한눈에 이해할 수 있도록 짧게 적어 주세요.
* 이벤트를 OR 조건으로 조합하고, 파라미터도 함께 정의할 수 있어요.

원하는 템플릿이 없다면, 목록 아래의 '원하는 지표가 없어요'를 선택해 필요한 지표를 토스팀에 요청할 수 있어요.

<table data-search="false"><thead><tr><th>템플릿</th><th>설명</th></tr></thead><tbody><tr><td>수익 발생한 유저</td><td>인앱 광고, 인앱 결제, 토스페이 중 하나 이상으로 매출이 발생한 사용자예요</td></tr><tr><td>토스페이 결제한 유저</td><td>토스페이로 결제를 완료한 사용자예요</td></tr><tr><td>인앱 상품 구매한 유저</td><td>인앱 상품을 결제한 사용자예요</td></tr><tr><td>인앱 광고 시청한 유저</td><td>인앱 광고를 1회 이상 시청한 사용자예요</td></tr><tr><td>한 명당 평균 매출 (ARPU)</td><td>매출을 사용자 수로 나눈 평균 금액이에요</td></tr><tr><td>한 건당 평균 결제 금액 (AOV)</td><td>결제 한 건당 평균 금액이에요</td></tr><tr><td>토스로그인 완료한 유저</td><td>토스 계정으로 로그인을 완료한 사용자예요</td></tr><tr><td>유저 식별키 발급받은 유저</td><td>별도 서버 없이 유저 식별키를 발급받은 사용자예요</td></tr><tr><td>게임 플레이 완료한 유저</td><td>게임을 1회 이상 완료한 사용자예요</td></tr><tr><td>알림 받기 동의한 유저</td><td>미니앱에서 보내는 알림을 받기로 동의한 사용자예요</td></tr><tr><td>특정 경로로 들어온 유저</td><td>특정 경로로 미니앱에 들어온 사용자예요</td></tr></tbody></table>

**대표 전환과 보조 전환**

전환 지표 중 대표 전환은 1개만 둘 수 있어요.

* 대표 전환: 이 미니앱에서 가장 우선순위로 두는 전환이에요. 노출·추천 등 최적화와 분석에서 기본으로 보는 지표가 돼요.
* 그 외 전환: 대표 전환까지 이어지는 과정을 함께 보기 위한 지표예요.

예를 들어 대표 전환이 결제 완료라면, 장바구니 담기·결제 시도를 보조로 두어 어느 단계에서 이탈하는지를 함께 볼 수 있어요.

처음 만든 전환 지표는 자동으로 대표 전환으로 지정돼요. 바꾸고 싶다면 다른 전환 지표를 선택해 대표 전환으로 체크할 수 있어요.

{% hint style="warning" %}
**꼭 확인해 주세요**

* 변경한 지표는 다음 날부터 적용돼요.
* 핵심 지표는 노출·추천과 성과 분석의 기준으로 쓰이니 신중하게 설정해 주세요.
  {% endhint %}

***

### 지표 확인하기

등록한 핵심 지표는 목록 화면에서 차트로 확인할 수 있어요.

* 상단의 날짜 범위와 시간 단위(일별·주별·월별)를 선택해 데이터를 조회할 수 있어요.
* 활성 지표는 활성 사용자 수를, 전환 지표는 전환 사용자 수를 보여줘요.

<figure><img src="/files/yqI5gars5zFhIsF2415O" alt=""><figcaption></figcaption></figure>


# 사용자 인증

앱인토스에서 사용자를 식별하고 인증하는 방법을 안내해요. 토스 인증과 토스 로그인을 연동할 때 필요한 흐름과 마이그레이션 가이드를 확인할 수 있어요.

* [토스 인증](/guide/authentication/contract)
* [토스 로그인](/guide/authentication/intro)
* [토스 로그인 마이그레이션](/guide/authentication/migration)


# 토스 인증

토스 인증은 사용자가 입력한 정보(또는 토스 앱에 저장된 정보)를 기반으로 실명·생년월일·휴대전화번호 등을 안전하게 확인하고, 토스 앱 인증으로 신원을 검증하는 서비스예요.  로그인, 가입, 조회처럼 사용자 식별이 필요한 서비스에서, CI(연계정보)를 포함한 식별자를 안정적으로 확보할 수 있어요.

<details>

<summary>'토스 인증'과 '토스 로그인'이 헷갈려요.</summary>

앱인토스는 파트너사에 토스 인증의 '본인 확인' 기능을 제공해요. '본인 확인'은 사용자의 이름·생년월일·휴대전화번호를 검증해 신원을 확인하는 서비스예요. 연령 확인, 실명 인증, 웹보드 게임처럼 법적 신원 확인이 필요할 때 써요.

'토스 로그인'은 간편 인증 방식이에요. 사용자가 별도의 정보 입력 없이 토스 앱으로 간편하게 로그인하는 기능이고, '토스 인증'과는 목적과 계약 구조가 달라요. 토스 로그인을 연동하려면 토스 로그인 가이드 문서를 참고해 주세요.

일부 파트너사에서 두 서비스를 헷갈려 잘못 계약한 사례가 있었어요. 계약 전에 요청하는 서비스가 '토스 인증'인지 '토스 로그인'인지 반드시 확인해 주세요.

</details>

{% hint style="warning" %}
**웹보드 게임은 본인 확인이 필수예요**

관련 법령에 따라 웹보드 게임은 본인 확인 절차가 반드시 필요해요. 토스 인증을 연동하면 본인 확인(필요하면 성인 인증까지)을 간편하게 진행할 수 있어요.
{% endhint %}

### 토스 인증 유형

토스 인증은 두 가지 방식을 제공해요. 두 방식 모두 최종적으로 토스 앱 인증으로 사용자를 확인한다는 점은 같고, 클라이언트에서 개인정보를 입력받는지 여부가 가장 큰 차이예요.

#### 1) 개인정보 기반 인증

클라이언트에서 이름·생년월일·휴대전화번호를 입력받아 암호화한 뒤 전송하는 방식이에요.

**권장 상황**

* 가입이나 전환 화면에서 이미 개인정보를 수집하고 있는 경우
* 입력값과 실제 가입 정보의 일치 여부를 즉시 검증해야 하는 경우

**흐름**

1. 사용자가 화면에서 개인정보 입력
2. 입력값을 암호화해 토스 인증으로 전송
3. 토스 앱 인증(푸시 또는 생체 인증 등)
4. 결과 수신(CI, 이름, 휴대전화번호, 인증 시각 등)

**특징**

* 입력값 검증(형식, 오타 등)에 유리해요.
* 입력 과정이 있어 사용자 이탈률이 다소 높을 수 있어요.

#### 2) 원터치 인증

클라이언트에서 개인정보를 입력받지 않고, 토스 앱을 바로 호출해 한 번의 인증으로 절차를 끝내는 간소화된 방식이에요.

원터치 인증은 토스 인증 서비스를 이용하는 것과 같고, 아래처럼 동작해요.

* 기기에 토스 인증서가 있다면: PIN 인증 또는 단말 생체 인증(Face ID, 지문 인증 등)
* 기기에 토스 인증서가 없다면: 토스 인증서를 발급한 뒤 PIN 인증 또는 단말 생체 인증

**권장 상황**

* 이탈 최소화나 전환율 최적화가 중요한 경우
* 앱 안에서 간결한 로그인·재인증 UX가 필요한 경우

**흐름**

1. '본인 인증' 버튼 클릭
2. 토스 앱 호출 후 사용자 인증
3. 결과 수신(CI, 인증 시각 등)

**특징**

* 입력 단계가 없어 UX가 매우 간결해요.
* 기존 계정과의 매칭 로직(CI 등) 설계가 중요해요.

***

### 운영 팁

* 웹보드·성인물 서비스: 본인 확인 후 서비스 정책에 따라 성인 여부(`ageGroup` 기반 정책)를 적용하세요.
* 재인증 정책: 장기간 미사용이나 주요 정보 변경(이름·번호 변경 등) 시 재인증 주기를 정의하면 안전해요.
* 개인정보 최소화: 원터치 인증을 기본으로 검토하고, 필요할 때만 입력 기반 인증을 조합해 개인정보 수집을 최소화하세요.

***

### 계약하기

토스 인증을 사용하려면 사전 계약이 필요해요. 계약에는 영업일 기준 7\~14일이 걸릴 수 있어요.

토스 인증 계약은 콘솔이 아니라 파트너 인증 사이트에서 인증팀이 진행해요. 아래 절차를 참고해 주세요.

#### 1. 회원가입

[파트너 인증 사이트](https://partner-auth.toss.im/login)에 접속해 회원가입을 해 주세요.

#### 2. 사업자 등록

'사업자 등록' 메뉴에서 필수 서류를 제출한 뒤 '등록하기' 버튼을 눌러 주세요. 사업자 유형에 맞는 서류를 제출하고 검토를 요청하면, 영업일 기준 1\~2일 정도 걸려요.

* 일반: 사업자 등록증, 사업자 등기부등본, 법인 인감증명서, 대리인 위임장, 대리인 신분증 사본
* 대표자 본인이 직접 등록하는 경우: 사업자 등록증, 사업자 등기부등본

#### 3. 계약 생성

'계약 목록'이나 '사업자 등록' 메뉴에서 '계약 생성하기' 버튼을 눌러 주세요. 인증 서비스 필수 약관에 동의한 뒤 계약 정보를 등록해 주세요. 검토를 요청하면 영업일 기준 1\~2일 정도 걸려요.

#### 4. 이용기관 생성 요청

'이용기관 목록' 메뉴에서 필요한 서비스를 등록해 주세요. 검토를 요청하면 영업일 기준 1\~2일 정도 걸려요.

#### 5. 계약 완료 및 키 발급

계약이 끝나면 파트너 인증 사이트에서 `client_id`와 `client_secret` 키를 확인할 수 있어요. 발급받은 키를 개발 환경에 적용해 주세요.

***

### 개발 연동하기

AccessToken 발급, 사용자 정보 조회 등 [연동 방법](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-auth#id-2)을 확인할 수 있어요.


# 토스 로그인

앱인토스에서 토스 회원을 한 번에 연동해 보세요. 한 번의 동의로 가입부터 로그인, 정보 제공까지 이어져서 토스 회원 연동을 간단하게 구현할 수 있어요.

### 토스 로그인의 좋은 점

* 별도의 가입 폼 없이 바로 가입과 로그인이 이뤄져서, 매끄러운 회원가입 경험을 만들 수 있어요.
* 토스가 직접 제공하는 신뢰도 높은 사용자 정보를 활용할 수 있어요.
* 재방문할 때 자동 로그인이나 원클릭 로그인을 쓸 수 있어요.
* 앱을 다시 설치하거나 기기를 바꿔도 같은 사용자로 매칭돼서, 고객 문의 대응 부담이 줄어요.

{% hint style="info" %}
**꼭 확인해 주세요**

* 미니앱에서는 로그인 기능으로 토스 로그인만 쓸 수 있어요. 자사 로그인이나 다른 간편 로그인 방식은 쓸 수 없어요.
* 기능성 푸시와 알림, 프로모션, 토스페이를 쓰려면 토스 로그인을 반드시 연동해야 해요.
  {% endhint %}

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FLvK8oLVnZMo83WkfM4X2%2Fimage.png?alt=media&#x26;token=d25c4cad-d817-4992-8eaa-95b26aa11059" alt=""><figcaption></figcaption></figure>

***

### 콘솔에서 설정하기

#### 1. 약관 동의하기

토스 로그인을 쓰려면 먼저 약관에 동의해야 해요. 약관 동의는 앱인토스 콘솔에서 할 수 있고, 대표관리자로 지정된 분의 계정에서만 할 수 있어요.

#### 2. 설정하기

로그인을 연동하려면 콘솔에서 사전 설정을 끝내야 해요. 입력한 정보를 기반으로 사용자 약관 동의 화면이 자동으로 구성돼요.

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2F7wKRUxTjtvQLTMMaZrKl%2F%E1%84%8B%E1%85%AF%E1%86%AB%E1%84%87%E1%85%A9%E1%86%AB%2084%20(1).png?alt=media&#x26;token=e6b95b66-7b03-4f99-9c40-60b6a94ae6bc" alt=""><figcaption></figcaption></figure>

**연동할 서비스**

이미 토스 로그인을 쓰고 있는 서비스가 있으면 노출되는 영역이에요. 기존 서비스의 회원 식별자(`userKey`)를 앱인토스 토스 로그인과 똑같이 설정할 수 있어요. 목록에서 서비스 이름을 선택하면, 선택한 서비스의 `userKey` 값이 똑같이 매핑돼요.

단, `userKey`는 해당 앱 안에서만 고유한 값이에요. 같은 사용자라도 앱이 다르면 `userKey`가 달라질 수 있어요.

**동의 항목**

토스 로그인으로 수집할 **사용자 권한(스코프)**&#xC744; 선택해 주세요. 이름, 이메일, 성별 외의 항목을 선택하면 **연결 끊기 콜백 정보**를 반드시 입력해야 해요.

<table data-search="false"><thead><tr><th>항목</th><th>설명</th></tr></thead><tbody><tr><td>이름 (USER_NAME)</td><td>사용자의 이름이에요.</td></tr><tr><td>이메일 (USER_EMAIL)</td><td>사용자의 이메일이에요. (토스 가입 시 필수가 아니라 값이 없을 수 있고, 이 경우 <code>null</code>로 전달돼요.)</td></tr><tr><td>성별 (USER_GENDER)</td><td>사용자의 성별이에요.</td></tr><tr><td>생일 (USER_BIRTHDAY)</td><td>사용자의 생년월일이에요.</td></tr><tr><td>국적 (USER_NATIONALITY)</td><td>사용자의 국적이에요.</td></tr><tr><td>전화번호 (USER_PHONE)</td><td>사용자의 전화번호예요.</td></tr><tr><td>CI (USER_CI)</td><td>사용자를 식별하는 고유한 KEY 값이에요. (Connection Information)</td></tr></tbody></table>

{% hint style="warning" %}
토스는 이메일 주소를 꼭 받지는 않아서, 이메일 값이 없는(null) 유저가 있을 수 있어요.

값이 없어도 앱이 정상 작동하도록 처리해주세요.
{% endhint %}

<details>

<summary>CI란?</summary>

CI(Connection Information)는 본인인증 기관에서 발급하는 **고유 식별값**이에요. 같은 사용자가 여러 서비스에 가입해도 **같은 본인으로 식별할 수 있도록 생성되는 불변값**이에요. CI는 실명 인증이 필요한 서비스에서 **중복 가입 방지나 본인 식별** 목적으로 자주 써요.

CI는 개인정보보호법상 개인식별정보(PII)에 해당해요. 저장하거나 쓸 때는 반드시 **암호화**하고, **최소 수집 원칙**을 지켜 주세요.

</details>

**약관/동의문**

앱인토스에서 서비스를 운영하려면 약관을 등록해야 해요. **토스 로그인 필수 약관**(서비스 약관, 개인정보 제3자 제공 동의)은 자동으로 포함돼요. **파트너사 서비스 약관, 개인정보 수집·이용 동의, 마케팅 정보 수신 동의(선택)** 등은 직접 등록해야 해요. 서비스 목적에 맞는 **정확한 약관 링크**를 첨부해 주세요.

약관 유형은 기본 제공 예시 중에서 선택하거나 직접 입력할 수 있어요. 약관을 구분해서 관리하고 싶다면 직접 입력하는 걸 추천해요.

해외 클라우드 리전이나 해외 사업자의 서버에 토스 로그인으로 받은 개인정보를 저장하거나 이전한다면, **개인정보 국외 이전 동의문**을 필수로 등록해 주세요. 동의문에는 이전받는 자, 이전되는 국가, 이전받는 자의 연락처, 이전 항목, 이전 시점과 방법, 이용 목적, 보유·이용 기간을 포함해야 해요.

<figure><img src="/files/eLR5eyaFkyTX5x57evlg" alt=""><figcaption></figcaption></figure>

모든 약관 링크가 정확히 연결되고, 화면에 명확하게 노출되는지 확인해 주세요.

{% hint style="warning" %}
**주의해 주세요**

이 영역은 **법적 요건을 충족해야 하는 부분**이에요. 서비스 성격에 따라 내용이 달라질 수 있으니, **최신 법령과 가이드라인을 확인하고 법률 자문을 받는 것**을 권장해요.
{% endhint %}

<details>

<summary>등록할 수 있는 약관 항목</summary>

* **서비스 이용약관** — 권리·의무, 책임 범위, 중단·종료, 분쟁 해결, 약관 변경 고지, (유료라면) 결제·환불 규정
* **개인정보 수집·이용 동의** — 수집 항목, 이용 목적, 보유·이용 기간, 동의 거부 시 불이익
* **마케팅 정보 수신 동의(선택)** — 수집 항목, 이용 목적, 보유 기간, 거부 시 불이익, 전자적 전송매체 광고 수신 동의
* **야간 혜택 수신 동의(선택)** — 야간(21:00\~08:00) 발송 여부 명시
* **개인정보 국외 이전 동의(해당 시)** — 개인정보를 국외로 이전한다면, 이전받는 자, 이전되는 국가, 이전받는 자의 연락처, 이전 시점과 방법, 이전 항목, 이용 목적, 보유·이용 기간을 명시

</details>

**연결 끊기 콜백 정보**

사용자가 토스 앱에서 로그인 연결을 해제하면, 등록한 콜백 URL로 이벤트를 받을 수 있어요.

사용자가 연결을 해제하면 토스는 **동의 약관과 로그인 정보를 모두 삭제**해요. 서비스에서도 세션이나 토큰 정리 같은 후처리를 꼭 해 주세요.

또한 사용자가 토스 앱에서 로그인 연결을 해제하면, 서비스에서도 **자동 로그아웃 처리**나 **재로그인 요청 안내**를 제공하는 걸 권장해요. 예를 들어 "토스 연결이 해제되어 다시 로그인해야 해요" 같은 문구를 보여주면 좋아요.

| 항목            | 설명                                                    |
| ------------- | ----------------------------------------------------- |
| 콜백 URL        | 사용자가 로그인 연결을 해제했을 때 호출할 URL이에요.                       |
| HTTP 메서드      | `GET` 또는 `POST` 중 하나를 선택해 주세요.                        |
| Basic Auth 헤더 | 호출할 때 base64로 인코딩돼요. 디코딩한 뒤 콘솔에 입력한 값과 일치하는지 검증해 주세요. |

**연결 끊기 이벤트 경로**

사용자가 토스 앱에서 로그인 연결을 해제하는 경로는 총 **3가지**예요. 콜백 요청 시 `referrer` 값으로 구분할 수 있어요.

| referrer           | 설명                                                                                                                       |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| `UNLINK`           | 사용자가 **앱에서 직접 연결을 끊었을 때** 호출돼요. 미니앱에서는 이 이벤트를 받으면 **로그아웃 처리**를 해 주세요. (경로: 토스 앱 > 설정 > 인증 및 보안 > 토스로 로그인한 서비스 > '연결 끊기') |
| `WITHDRAWAL_TERMS` | 사용자가 **로그인 서비스 약관을 철회할 때** 호출돼요. (경로: 토스 앱 > 설정 > 법적 정보 및 기타 > 약관 및 개인정보 처리 동의 > 서비스별 동의 내용: "토스 로그인" > '동의 철회하기')       |
| `WITHDRAWAL_TOSS`  | 사용자가 **토스 회원을 탈퇴할 때** 호출돼요.                                                                                              |

***

### 이메일로 복호화 키 받기

토스 로그인 정보 등록이 끝나면 복호화 키를 확인할 수 있어요. 이 키는 토스 로그인 응답 데이터를 복호화할 때 써요. '이메일로 복호화 키 받기' 버튼을 눌러 안전하게 받아보세요.

{% hint style="warning" %}
**복호화 키는 민감한 보안 정보예요.**

* 절대 외부에 노출하지 마세요.
* 안전한 내부 비밀 저장소(Secret Manager 등)에 보관해 주세요.
* 다시 발급받아야 한다면 채널톡으로 문의해 주세요.
  {% endhint %}

<figure><img src="https://3177177630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8pQgXiR5QAzduV54W8Om%2Fuploads%2FRLbRu8k6r3e7sKyJyRpv%2F%E1%84%80%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%B5%E1%84%89%E1%85%A1%E1%84%92%E1%85%A1%E1%86%BC%2010.png?alt=media&#x26;token=de4bead1-da2b-4f05-a594-bcf3c901e24b" alt=""><figcaption></figcaption></figure>

***

### 개발 연동하기

인가 코드 발급(SDK), AccessToken 발급, 사용자 정보 조회 등 [연동 방법](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login#undefined)을 확인할 수 있어요.

***

### 자체 웹·앱에 토스 로그인 연동하기

자체 웹·앱에 토스 로그인을 적용하려면 원래 토스 인증 부서와 별도 계약이 필요해요. 다만 앱인토스에서 토스 로그인을 쓰는 파트너사라면, 별도 계약 없이도 쓸 수 있어요.

#### 1. 앱인토스 콘솔에서 토스 로그인 신청하기

위 가이드를 확인한 뒤, 앱인토스 콘솔에서 토스 로그인을 먼저 신청해 주세요.

#### 2. 필요한 정보를 작성해 Client ID 발급 요청하기

아래 항목을 모두 작성해 토스 인증 부서(<cert.support@toss.im>)로 이메일을 보내 주세요.

<table data-search="false"><thead><tr><th>항목</th><th>설명</th><th>예시</th></tr></thead><tbody><tr><td>웹/앱 여부</td><td>적용하려는 서비스 형태를 작성해 주세요 (웹, 앱, 또는 둘 다)</td><td>웹, 앱</td></tr><tr><td>회원 식별 키</td><td>사용자 식별에 사용할 키를 작성해 주세요</td><td>CI, 이메일</td></tr><tr><td>필요한 개인정보 항목</td><td>제공받고자 하는 개인정보 항목을 작성해 주세요(앱인토스 콘솔 > 토스 로그인 > 동의 항목을 참고해 주세요)</td><td>이름, 이메일 주소</td></tr><tr><td>약관 목록</td><td>사용할 약관의 제목, URL, 필수 여부를 작성해 주세요</td><td>이용약관 (필수) - https://example.com/terms</td></tr><tr><td>redirect_uri</td><td>로그인 완료 후 이동할 URL을 작성해 주세요</td><td>https://example.com/callback</td></tr><tr><td>연동 예정 앱 버전</td><td>제휴사 앱에도 도입하는 경우 앱 버전을 작성해 주세요</td><td>iOS 3.2.0, Android 2.8.1</td></tr><tr><td>로그인 연결 끊기 API 사용 여부</td><td>해당 API 사용 여부를 작성해 주세요 (미작성 시 '사용 안 함'으로 설정돼요)</td><td>사용 안 함</td></tr><tr><td>네트워크 정보</td><td>VPN을 사용하거나 개발계 연동이 필요한 경우 별도 등록이 필요해요서버의 IP 또는 IP 대역을 작성해 주세요</td><td>123.45.67.89</td></tr></tbody></table>


# 토스 로그인 마이그레이션

**토스 로그인(`userKey`)** 을 사용하는 미니앱을 **사용자 식별키(`hash`)** 로 전환하는 방법을 안내해요.

이 문서를 따라 하면, 현재 토스 로그인을 쓰는 유저를 점진적으로 사용자 식별키로 매핑하고, 모든 유저가 이전되면 토스 로그인 의존성을 완전히 제거할 수 있어요.

### 언제 이 가이드를 사용하나요?

* 기존에 토스 로그인 `userKey` 로 사용자 식별을 하고 있어요.
* 앞으로는 사용자 식별키 `hash`값을 표준 식별자로 쓰고 싶어요.

### 핵심 개념

* **사용자 식별키 hash**: `getUserKeyForGame()` 호출로 발급되는 게임용 고유 식별자
* **토스 로그인 userKey**: 기존 토스 로그인 기반 사용자 식별자
* **매핑**: 동일 사용자의 `userKey` 와 `hash` 값을 1:1로 연결한 상태

{% hint style="info" %}
**참고해 주세요**

각 게임별로 `hash` 값은 달라요.
{% endhint %}

### 전체 전환 흐름

1. 클라이언트에서 `getUserKeyForGame()` 으로 사용자 식별키 `hash` 값을 발급받아요.
2. `getIsTossLoginIntegratedService()` 으로 토스 로그인 연동 여부를 확인해요.
3. 파트너사 서버에 매핑 여부를 조회해요.
4. 매핑되지 않았다면 `appLogin()` 을 통해 토스 로그인을 진행하고, `hash` 값을 서버로 전송해요.
5. 서버에서 토스 로그인 `userKey` 와 사용자 식별키 `hash` 값을 매핑 테이블에 저장해요.
6. 이후에는 `hash` 값만으로 사용자를 식별할 수 있어요. 모든 유저가 매핑되면 토스 로그인 의존성을 제거하세요.

### 사전 구현이 필요한 API

파트너사는 아래 두 가지 API를 **직접 구현해야 해요.** 이 API들은 앱인토스에서 제공하지 않으며, 아래 예시를 참고해 파트너사 서버에서 자체적으로 개발해 주세요.

* **매핑 여부 조회**
  * `POST /api/auth/migration/status`
  * **Req**: `{ hash: string }`
  * **Res**: `{ isMapped: boolean }`
* **매핑 생성**
  * `POST /api/auth/migration/link`
  * **Req**: `{ hash: string; authorizationCode: string; referrer?: string }`
  * **Res**: `{ success: true }`

***

### 클라이언트 구현 단계

#### 1. SDK 가져오기

```tsx
import { getUserKeyForGame, getIsTossLoginIntegratedService, appLogin } from '@apps-in-toss/web-framework';
```

#### 2. 게임 hash 값 발급

```tsx
const result = await getUserKeyForGame();
if (!result) return console.warn('지원하지 않는 앱 버전이에요.');
if (result === 'INVALID_CATEGORY') return console.error('게임 카테고리가 아닌 미니앱이에요.');
if (result === 'ERROR') return console.error('사용자 키 조회 중 오류가 발생했어요.');
if (result.type !== 'HASH') return console.error('알 수 없는 반환값이에요.');
const { hash } = result;
```

#### 3. 토스 로그인 연동 여부 확인

```tsx
const status = await getIsTossLoginIntegratedService();
if (status === 'INVALID_CLIENT') {
  console.log('토스 로그인이 연동되어 있지 않은 미니앱이에요.');
  return;
}
```

자세한 API 명세는 아래 [`getIsTossLoginIntegratedService`](#%ED%86%A0%EC%8A%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%97%B0%EB%8F%99-%EC%97%AC%EB%B6%80-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0) 섹션을 참고해 주세요.

#### 4. 파트너사 서버에 매핑 여부 조회 및 매핑

```tsx
if (status === true) {
  // 토스 로그인 연동된 유저
  const { isMapped } = await fetch('/api/auth/migration/status', {
    // 매핑 여부 확인
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ hash }),
  }).then((r) => r.json());

  if (!isMapped) {
    const { authorizationCode, referrer } = await appLogin(); // 미매핑이면 토스 로그인 후 매핑 생성
    await fetch('/api/auth/migration/link', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ authorizationCode, referrer, hash }),
    });
  }

  console.log('매핑 완료 또는 이미 매핑된 사용자예요.');
  return;
}

console.log('토스 로그인 미연동 사용자예요.'); // status === false
```

#### 5. 사용자 식별키 hash 사용

이제 사용자 식별은 사용자 식별키 `hash`값을 기준으로 하면 돼요. 토스 로그인 `userKey` 대신, `getUserKeyForGame()` 으로 발급받은 사용자 식별키 `hash`값을 서버와 클라이언트 모두에서 사용자 식별자로 사용해 주세요.

***

### 전체 예시 코드

```tsx
import { getUserKeyForGame, getIsTossLoginIntegratedService, appLogin } from '@apps-in-toss/web-framework';

async function migrateIfNeeded() {
  const res = await getUserKeyForGame();
  if (!res) return console.warn('지원하지 않는 앱 버전이에요.');
  if (res === 'INVALID_CATEGORY') return console.error('게임 카테고리가 아닌 미니앱이에요.');
  if (res === 'ERROR') return console.error('사용자 키 조회 중 오류가 발생했어요.');
  if (res.type !== 'HASH') return console.error('알 수 없는 반환값이에요.');
  const { hash } = res;

  let status: boolean;
  try {
    status = await getIsTossLoginIntegratedService();
  } catch (error: any) {
    console.error('토스 로그인 연동 여부 확인 중 오류 발생:', error);
    return;
  }

  if (status === true) {
    // 매핑 여부 조회
    const { isMapped } = await fetch('/api/auth/migration/status', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ hash }),
    }).then((r) => r.json());

    if (!isMapped) {
      // 미매핑이면 토스 로그인 후 매핑 생성
      const { authorizationCode, referrer } = await appLogin();

      await fetch('/api/auth/migration/link', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ authorizationCode, referrer, hash }),
      });
    }

    console.log('매핑 완료 또는 이미 매핑된 사용자예요.');
    return;
  }

  // status === false : 토스 로그인 기능은 있으나 현재 유저는 미연동
  console.log('토스 로그인 미연동 사용자예요.');
}
```

{% hint style="info" %}
**예외 처리**

토스 로그인을 사용하지 않는 미니앱에서 `getIsTossLoginIntegratedService()`를 호출하면 아래 예외가 발생할 수 있어요.

```tsx
@throw {message: "oauth2ClientId 설정이 필요합니다."}
```

이 경우 토스 로그인 기능이 없는 환경이므로 별도 처리가 필요하지 않아요.
{% endhint %}

***

### 토스 로그인 연동 여부 확인하기

**SDK 함수:** `getIsTossLoginIntegratedService`

`getIsTossLoginIntegratedService`는 **현재 유저가 토스 로그인과 연동된 유저인지 여부를 확인하는 API**예요.

이 함수는 주로 **토스 로그인 → 사용자 식별키 발급으로 마이그레이션하는 과정**에서 사용돼요. 기존 토스 로그인 유저인지 여부에 따라, 로그인 플로우나 데이터 이전 처리를 분기할 때 활용할 수 있어요.

**시그니처**

```tsx
function getIsTossLoginIntegratedService(): Promise<boolean>;
```

| 반환 타입              | 설명                                                  |
| ------------------ | --------------------------------------------------- |
| `Promise<boolean>` | 현재 서비스가 토스 로그인과 연동되어 있다면 `true`, 아니면 `false`를 반환해요. |

#### 주의사항

* 이 API는 토스 로그인 기능을 사용하는(또는 사용했던) 미니앱에서만 의미가 있어요.
* 토스 로그인을 전혀 사용하지 않는 미니앱에서 호출하면 아래와 같은 예외가 발생할 수 있어요.

```tsx
@throw { message: 'oauth2ClientId 설정이 필요합니다.' }
```

**예제 : 토스 로그인 연동 여부 확인하기**

아래 예제는 유저가 토스 로그인 연동 유저인지 확인한 뒤, 상태에 따라 서로 다른 처리를 하는 기본적인 흐름을 보여줘요.

{% tabs %}
{% tab title="js" %}

```js
import { getIsTossLoginIntegratedService } from '@apps-in-toss/web-framework';

async function handleGetIsTossLoginIntegratedService() {
  try {
    const result = await getIsTossLoginIntegratedService();

    if (result === undefined) {
      console.warn('지원하지 않는 앱 버전이에요.');
      return;
    }
    if (result === true) {
      console.log('토스 로그인이 연동된 유저에요.');
      // 여기에서 토스 로그인 연동 유저에 대한 처리를 할 수 있어요.
    }
    if (result === false) {
      console.log('토스 로그인이 연동되지 않은 유저에요.');
      // 여기에서 토스 로그인 연동 유저가 아닌 경우에 대한 처리를 할 수 있어요.
    }
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}

{% tab title="React" %}

```tsx
import { getIsTossLoginIntegratedService } from '@apps-in-toss/web-framework';

function GetIsTossLoginIntegratedServiceButton() {
  async function handleClick() {
    try {
      const result = await getIsTossLoginIntegratedService();

      if (result === undefined) {
        console.warn('지원하지 않는 앱 버전이에요.');
        return;
      }
      if (result === true) {
        console.log('토스 로그인이 연동된 유저에요.');
        // 여기에서 토스 로그인 연동 유저에 대한 처리를 할 수 있어요.
      }
      if (result === false) {
        console.log('토스 로그인이 연동되지 않은 유저에요.');
        // 여기에서 토스 로그인 연동 유저가 아닌 경우에 대한 처리를 할 수 있어요.
      }
    } catch (error) {
      console.error(error);
    }
  }

  return <button onClick={handleClick}>토스 로그인 통합 서비스 여부 확인</button>;
}
```

{% endtab %}

{% tab title="React Native" %}

```tsx
import { Button } from 'react-native';
import { getIsTossLoginIntegratedService } from '@apps-in-toss/framework';

function GetIsTossLoginIntegratedServiceButton() {
  async function handlePress() {
    try {
      const result = await getIsTossLoginIntegratedService();

      if (result === undefined) {
        console.warn('지원하지 않는 앱 버전이에요.');
        return;
      }
      if (result === true) {
        console.log('토스 로그인이 연동된 유저에요.');
        // 여기에서 토스 로그인 연동 유저에 대한 처리를 할 수 있어요.
      }
      if (result === false) {
        console.log('토스 로그인이 연동되지 않은 유저에요.');
        // 여기에서 토스 로그인 연동 유저가 아닌 경우에 대한 처리를 할 수 있어요.
      }
    } catch (error) {
      console.error(error);
    }
  }

  return <Button onPress={handlePress} title="토스 로그인 통합 서비스 여부 확인" />;
}
```

{% endtab %}
{% endtabs %}

#### 언제 사용하면 좋을까요?

* 토스 로그인 기반 서비스에서 **사용자 식별키로 전환(마이그레이션)** 할 때
* 기존 유저와 신규 유저를 구분해 **데이터 이전/보상 처리**를 해야 할 때
* 토스 로그인 연동 여부에 따라 **서로 다른 UX를 제공**해야 할 때

#### 참고사항

* `getIsTossLoginIntegratedService`는 마이그레이션 보조 API예요.
* 인증/로그인 기능은 아래를 참고해 주세요.
  * 토스 로그인
  * [사용자 식별키 발급](https://appsintoss.gitbook.io/appsintoss-docs/documentation/common/authentication/hash-key)


# 유저 정보 연동하기

사용자가 직접 입력하지 않아도, 필요한 정보를 토스에서 안전하게 불러올 수 있어요. 예를 들어 택배를 보낼 때 이름, 휴대전화번호, 주소 등을 사용자가 일일이 입력하지 않아도 토스에 저장된 정보를 동의 후 바로 불러올 수 있어요.

{% hint style="info" %}
**꼭 확인해 주세요**

* 사용자 정보 불러오기는 **토스 로그인과 별개의 기능**이에요. 토스 로그인 없이도 사용할 수 있어요.
* 사용자에게 **개인정보 제3자 제공 동의**를 받은 후에만 정보가 전달돼요.
* 노출 시점은 최대 **5개**까지 등록할 수 있어요.
* 별도 서버 없이 사용할 수 있으며, SDK **v2.7.0** 이상에서 사용할 수 있어요.
  {% endhint %}

***

### 1. 사용자 정보 불러오기 접속하기

콘솔에서 사용자 정보 불러오기 메뉴로 접속해 주세요.

* **접속 방법:** 앱인토스 콘솔 → 워크스페이스 선택 → 미니앱 선택 → 좌측 메뉴 **'유저정보 불러오기'**

처음 접속하면 아직 등록된 정보가 없는 빈 화면이 노출돼요. `등록하기` 버튼을 눌러 설정을 시작해 주세요.

<figure><img src="/files/EpreoOGHv1PWrUIayQlt" alt=""><figcaption></figcaption></figure>

***

### 2. 동의문 노출 시점 설정하기

사용자에게 정보 제공 동의를 요청할 시점을 설정해요. 서비스 내에서 사용자 정보가 필요한 상황에 맞춰 제목을 작성해 주세요.

<figure><img src="/files/HXhwckCeLBXnEWeMLQQt" alt=""><figcaption></figcaption></figure>

**동의 화면 제목**

동의 화면에 노출되는 제목이에요. 입력한 텍스트 뒤에 **"때 필요한 정보를 불러올까요?"** 가 자동으로 붙어요. 예를 들어 `택배 보낼`을 입력하면, 사용자에게는 **"택배 보낼 때 필요한 정보를 불러올까요?"** 로 노출돼요.

**불러올 사용자 정보**

해당 시점에 불러올 사용자 정보를 선택해 주세요. 선택할 수 있는 항목은 다음과 같아요. 사용자 정보는 토스앱 → 설정 → 내 정보 · 주소 관리 에 있는 정보를 불러와요.

| 항목      | 설명         | 비고                                            |
| ------- | ---------- | --------------------------------------------- |
| 이름      | 사용자 이름     |                                               |
| 성별      | 사용자 성별     |                                               |
| 내국인/외국인 | 내국인/외국인 여부 |                                               |
| 생년월일    | 사용자 생년월일   |                                               |
| 휴대전화번호  | 사용자 휴대전화번호 |                                               |
| 주소      | 사용자 집 주소   | 토스 가입 시 필수가 아니어서 값이 없을 수 있고, 이 경우 null로 전달돼요. |
| 이메일 주소  | 사용자 이메일 주소 | 토스 가입 시 필수가 아니어서 값이 없을 수 있고, 이 경우 null로 전달돼요. |

**노출 시점 추가하기**

서비스 내에서 사용자 정보가 필요한 시점이 여러 개라면, `노출 시점 추가하기` 버튼으로 시점을 추가할 수 있어요. 최대 **5개**까지 등록할 수 있어요. 시점마다 불러올 사용자 정보를 다르게 설정할 수 있어요. 예를 들어 택배 발송 시에는 이름·휴대전화번호·주소를, 본인 인증 시에는 이름·생년월일만 요청할 수 있어요.

***

### 3. 동의문 내용 확인하기

사용자에게 노출되는 동의문은 **개인정보 제3자 제공 동의** 형식으로 자동 생성돼요. 선택한 사용자 정보 항목에 맞춰 동의문 내용이 자동으로 구성돼요. `보기` 버튼을 눌러 동의문 전체 내용을 미리 확인할 수 있어요.

***

### 4. 콜백 설정하기 (선택)

사용자가 미니앱에서 회원 탈퇴하면, 등록된 콜백 URL로 탈퇴 이벤트가 전달돼요. 이벤트를 받은 뒤에는 해당 사용자의 정보를 파기해 주세요.

{% hint style="info" %}
콜백은 선택 사항이에요. 콜백을 사용하려면 토글을 **ON**으로 설정해 주세요.
{% endhint %}

**콜백 URL**

탈퇴 이벤트를 수신할 URL을 입력해 주세요.

**HTTP 메서드**

콜백 호출 시 사용할 HTTP 메서드를 **GET** 또는 **POST** 중 선택해 주세요.

**Basic Auth 헤더**

콜백 요청 시 HTTP 헤더에 포함되는 인증 정보예요. `Authorization: Basic {입력한 값}` 형태로 전달돼요.

**콜백 테스트**

설정을 저장하기 전에 `테스트하기` 버튼으로 콜백이 정상적으로 동작하는지 확인해 주세요. 테스트에 성공해야 저장할 수 있어요.

***

### 5. 등록 완료 및 코드 복사하기

등록이 완료되면 각 노출 시점마다 `cud_`로 시작하는 고유 코드(`consentedUserDataKey`)가 생성돼요. 이 코드는 SDK에서 사용자 정보를 불러올 때 사용해요. 복사 아이콘을 눌러 코드를 복사해 주세요.

등록 후에는 동의문 정보 화면에서 노출 시점, 불러올 사용자 정보, 코드를 한눈에 확인할 수 있어요. `수정하기` 버튼으로 설정을 변경할 수 있어요.

<figure><img src="/files/Rwf5t5x3aTsCALBga9kF" alt=""><figcaption></figcaption></figure>

***

### 6. 철회하기

동의한 동의문은 아래 경로에서 철회할 수 있어요. 개별 철회가 가능해요.

* **철회 방법:** 토스 앱 → 설정 → 약관 및 개인정보 처리 동의 → 미니앱 이름

***

### 개발 연동하기

> 사용자 정보 >
>
> SDK를 통해 사용자 정보를 불러올 수 있어요.


# 정산

정산은 서비스 운영과 수익 관리를 위해 반드시 이해해야 하는 과정이에요. 이 문서에서는 정산 정보 등록부터 수익금을 실제로 받기까지의 흐름을 정리했어요. 정산 구조를 미리 이해하면, 매출 확인부터 세금계산서 발행과 입금 일정까지 한 번에 관리할 수 있어요.

### 정산 정보 등록하기

정산 정보는 아래 경로에서 등록해요.

* 나의 워크스페이스 → 파트너 정보 → 정산 정보

모든 정산은 앱 단위가 아니라 사업자 단위로 진행해요. 하나의 사업자로 여러 앱을 운영하면, 각 앱의 수익과 비용을 합산해 정산해요. 그래서 특정 앱의 매출만 따로 입금되지 않고, 사업자 전체를 기준으로 최종 정산 금액을 계산해요.<br>

<figure><img src="/files/4XAVgrsz6cWrYAmfpICC" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/GsvRSlRhUE6ziSrbc5FR" alt=""><figcaption></figcaption></figure>

***

### 인앱 광고 정산 구조

인앱 광고 수익은 광고 송출 비용과 앱인토스 수수료를 차감한 뒤 정산돼요.

**광고 송출 비용 등 운영 수수료**

앱인토스는 광고 송출을 위해 외부 미디에이션 플랫폼과 Toss Ads를 사용해요. 이 과정에서 광고 매출의 약 30% 수준을 운영비로 공제해요.

**외부 미디에이션 플랫폼**

외부 미디에이션 플랫폼을 통해 광고가 송출되면, 플랫폼 수수료를 제외한 금액이 광고 수익이 돼요.

| 광고 매출   | 광고 수익  | 광고 수익 부가세 |
| ------- | ------ | --------- |
| ₩10,000 | ₩7,000 | ₩700      |

외부 플랫폼 사정으로 일부 광고 수익이 매체별로 정확히 집계되지 않는 경우에는, 해당 일자의 수익 비중에 따라 파트너사별로 나눠서 정산해요.

* (일 별 파트너사 당 광고 매체 수익 합계 / 일 별 유실 수익 총합) \* 일별 유실 수익 총합 = 일 별 파트너사 당 유실된 광고 매체 수익

**토스 애즈**

토스 애즈로 광고가 송출된 경우, 광고 매출(부가세 포함)에서 운영비 30%를 공제한 금액이 광고 수익이 돼요.

| 광고 매출   | 광고 수익  | 광고 수익 부가세 |
| ------- | ------ | --------- |
| ₩11,000 | ₩7,700 | ₩770      |

**앱인토스 수수료 (15%)**

광고 수익에서 앱인토스 수수료 15%를 추가로 공제해요. 이 수수료는 광고 대행에 대한 비용이에요. 현재 수수료 면제 프로모션이 진행 중이에요. 추후 별도 고지 후 변경될 수 있어요.

**최종 정산금**

최종 정산금은 아래 기준으로 계산돼요.

* 광고 수익(구글 애드몹, 토스 애즈)과 그에 대한 부가세를 더한 금액에서
* 앱인토스 수수료와 수수료 부가세를 뺀 금액

| 광고 수익   | 광고 수익 부가세 | 앱인토스 수수료 | 수수료 부가세 | 파트너사 정산금 |
| ------- | --------- | -------- | ------- | -------- |
| ₩14,700 | ₩1,470    | ₩2,205   | ₩221    | ₩13,774  |

광고 수익은 파트너사의 매출로 신고해야 해요. 광고 수익에 대한 부가세는 정산금과 함께 지급돼요.

#### **정산 절차**

사업자 회원의 경우, 최종 수입 금액에 대한 세금계산서 승인 절차를 통해 익월 말일(영업일 기준)에 지급해요.

**수입 지급 절차**

1. 당월 매출 발생
2. 익월 2영업일에 정산 내역 확정 및 토스에서 세금계산서 역발행
3. 익월 4영업일까지 역발행 세금계산서 승인
4. 익월 말일(영업일 기준)에 승인된 금액 지급

아래 사유에 해당될 경우에는 수입 지급이 이월될 수 있어요.

* 세금계산서 승인 기한까지 승인을 하지 않았거나, 세금계산서 반려 후 기한 내 재승인이 이루어지지 않았을 경우
* 실제 지급 과정에서 증빙 서류 불충분, 계좌 불능 등으로 지급이 실패한 경우

정산 받을 수입이 **5,000원 이하**인 경우 다음 달로 이월하여 누적해요. 일반과세자/간이과세자 구분은 콘솔에서 제휴사가 직접 선택해 주세요. 설정한 정보에 따라 정산이 진행되며, 정산 확정 이후에는 사업자 유형을 변경할 수 없어요.

#### **사업자 유형별 정산 안내**

정산 방식은 콘솔에 설정된 사업자 유형에 따라 자동으로 결정됩니다. 정산 전, 콘솔에서 본인의 사업자 유형이 올바르게 설정되어 있는지 꼭 확인해 주세요.

**일반과세자**

* 세금계산서 발행 기준으로 정산이 진행됩니다.
* 세금계산서는 역발행 승인 방식으로만 처리되며, 원활한 처리를 위해 팝빌 가입 및 공인인증서 등록이 필요합니다.
* 정발행 방식은 더 이상 지원되지 않으며, 정발행으로 처리된 건은 광고수익 지급 대상에서 제외됩니다.
  * 정발행으로 발행된 경우, 정발행 취소 후 역발행 승인으로 재처리해 주세요.

**간이과세자**

* 세금계산서 발행 없이 광고수익에 대한 정산금이 지급됩니다.
* 정산금 수령 후, 홈택스에서 지출증빙용 현금영수증을 직접 발행해 주세요.
* 현금영수증 미발급·지연 발급·사실과 다른 발급으로 인한 책임은 제휴사에 있습니다.
* 정산 완료된 광고수익에 대해 2개월 이상 현금영수증이 발행되지 않을 경우, 광고 송출이 일시 보류될 수 있습니다.

<figure><img src="/files/NJKAn9GFT9bN3BtfLeWf" alt=""><figcaption></figcaption></figure>

**사업자 유형 변경이 필요한 경우**

* 채널톡으로 문의해 주세요.
* 정산은 당월 콘솔 설정값을 기준으로 처리되므로, 정산 당월에 변경하지 않으면 해당 월에는 반영되지 않습니다.

#### **세금계산서 발행**

인앱광고와 관련된 부가세는 **두 가지 유형**이 있어요. 각 유형별로 **세금계산서 발행 방식이 다르니**, 아래 내용을 꼭 확인해 주세요.

**1) 광고 수익 부가세**

광고 수익에 대한 부가세는 **토스가 파트너사로 세금계산서를 역발행**해요. 파트너사에서 **팝빌을 통해 승인해야** 세금계산서가 최종 발행돼요.

{% hint style="info" %}
**세금계산서 역발행이란?**

토스가 세금계산서를 먼저 작성하고, 파트너사가 이를 확인한 뒤 승인하는 방식이에요.

* **토스(공급받는 자)** 가 세금계산서를 작성해요.

* **파트너사(공급자)** 가 승인해야 발행이 완료돼요.

* 승인하지 않으면 세금계산서는 발행되지 않아요.
  {% endhint %}

* **매월 2영업일 오후 3시 이내**: 역발행 세금계산서에 대한 **승인 요청 메일**이 발송

* **승인 마감일은 매월 4영업일까지예요.**
  * 기한 내 승인하지 않으면 **정산금 지급이 이월돼요**.
  * 기한 내 역발행 미승인이 **2회 이상** 발생할 경우, 미니앱 운영이 중단될 수 있어요.
  * 승인 방법은 아래 **(별첨) 역발행 세금계산서 승인 방법**을 참고해 주세요.

세금계산서 역발행 승인 외 세무 업무는 지원 및 운영이 불가해요. 아래 첨부된 **\[토스] 팝빌 역발행 세금계산서 안내 매뉴얼**을 확인한 뒤 정산을 진행해 주세요.

{% file src="/files/Z8eUlcxJnMVlWjGQxjXV" %}

※ 광고 수익 부가세는 별도로 지급하지 않고, 정산금 지급 시 정산금과 함께 포함해서 지급돼요.

**2) 앱인토스 수수료 부가세**

* 앱인토스 수수료에 대한 부가세는 **토스가 선공제해요**.
* 세금계산서는 **토스가 직접 발행해요**.
* 세금계산서는 **매월 3영업일 이내에 발행돼요**.

***

### 인앱 결제 정산 구조

인앱 결제는 앱마켓 수수료와 토스 수수료가 함께 발생해요. CBT 기간에는 한시적으로 토스 수수료 0%가 적용돼요.

**수수료 구성**

* **앱마켓 수수료:** 공급가 기준 15% 또는 30%
  * 현재는 15%가 적용돼요.
  * 총 수익이 늘어나면 30%로 변경될 수 있어요.
* **토스 수수료:** 결제 금액 기준 5%

앱마켓에서 지급한 정산금에서 토스 수수료와 부가세를 제외한 금액이 파트너사에 지급돼요.

* 앱마켓 정산금은 서울 외국환중개소 매매기준율 최초고시환율을 기준으로 당월 말일자 환율을 적용하여 원화로 변환해요.

**애플**

| **결제 금액 (부가세포함)** | **(-)앱마켓 수수료(VAT 포함)** | **(-)토스 수수료** | **(-)토스 수수료 부가세** | **(=)파트너사 정산금** |
| ----------------- | ---------------------- | ------------- | ----------------- | --------------- |
| ₩11,000           | ₩1,650                 | ₩550          | ₩55               | ₩8,745          |

**구글**

| **결제 금액 (부가세포함)** | **(-)앱마켓 수수료** | **(-)토스 수수료** | **(-)토스 수수료 부가세** | **(=)파트너사 정산금** |
| ----------------- | -------------- | ------------- | ----------------- | --------------- |
| ₩11,000           | ₩1,500         | ₩550          | ₩55               | ₩8,895          |

**정산 일정**

* 정산금 지급: 앱마켓(`App Store`, `Google Play`) 이 토스에 정산한 대금이 입금된 날로부터 3영업일 내
  * 예시 :
    * 앱마켓이 5월 정산대금을 토스에 지급 (6/5)
    * 토스는 3영업일 내 5월 정산대금을 파트너사에 지급 (\~6/8)

앱인토스 콘솔 > 미니앱 > 좌측 수익화 메뉴 > 인앱 결제 > 정산 내역에서 확인할 수 있어요.

**세금계산서**

* 애플 수수료에는 부가세가 발생해요. 애플 대신 토스가 세금계산서를 발행해요.
* 토스 수수료에 대한 부가세는 토스가 선공제 후 발행해요.

#### **세금계산서 발행**

인앱 결제 정산과 관련해 세금계산서는 애플 수수료와 토스 수수료 기준으로 나뉘어요.

**애플 수수료에 대한 부가세**

* 구글 인앱 결제에는 별도의 부가세가 없어요.
* 애플은 마켓 수수료(15% 또는 30%)에 부가세가 발생해요.
* 애플은 파트너사에 세금계산서를 직접 발행하지 않아요. 그래서 토스가 대신 세금계산서를 발행해요.
* 애플 수수료에 대한 부가세는 애플 정산금에서 먼저 공제돼요.
* 공제된 부가세는 추후 환급받을 수 있어요.

**토스 수수료에 대한 부가세**

* 토스 수수료에 대한 부가세는 정산금 지급 시 토스가 선공제해요.
* 토스가 세금계산서를 직접 발행해요.

{% hint style="info" %}
**세금계산서 금액이 건별 정산 내역 합계와 다른 경우**

* 세금계산서는 국세청 권고 기준에 맞춰 발행해요.
* 2019년 5월 개정된 전자 세금계산서 시스템 지침에 따라, 부가세는 공급가액의 10%가 되도록 다시 계산해요.
* 건별 정산 내역을 단순 합산해 세금계산서를 발행하면 국세청 권고 기준에 맞지 않을 수 있어요.
* 그래서 월 단위 정산 내역을 기준으로 금액을 보정한 뒤, 해당 금액으로 세금계산서를 발행해요.
  {% endhint %}

#### **현금 영수증**

구글 기프트카드로 결제된 건은 파트너사가 결제일 기준 5일 이내에 현금 영수증을 발행해야 할 의무가 있어요.

* 2025년 12월 1일 이후에는 토스가 현금 영수증을 대행 발행해요. 별도로 발행하지 않아도 돼요.
  * 토스가 대행 발행한 금액은 앱인토스 콘솔 > 인앱 결제 > 정산 내역에서 확인할 수 있어요.
* 주문별로 기프트카드 결제 여부를 확인한 뒤, 결제 상태에 따라 현금 영수증을 발급하거나 취소해 주세요.
  * 결제 수단 정보는 결제 후 1\~2일 뒤에 업데이트돼요.
  * 앱마켓 직권 취소 등으로 정산금에 포함되지 않는 환불 건은 결제 완료 건으로 처리해야 해요. 이 경우 현금 영수증을 취소하면 안 돼요. 익월 초에 제공되는 정산 리포트를 함께 참고해 주세요.
* 결제가 환불되면 해당 결제 건의 현금 영수증도 함께 취소돼요. 이 금액은 월별 집계 금액에서 차감돼요.
* 현금 영수증 발행과 취소 내역은 국세청 처리 일정에 따라 결제일 또는 환불일로부터 약 3영업일 후 집계에 반영될 수 있어요.

***

### 해외 파트너사 정산 구조

해외 파트너사란, 국외에서 사업자를 등록한 파트너사를 의미해요. 해외 사업자는 세금계산서 대신 인보이스를 사용해요.

* 매출은 원화(KRW) 기준으로 집계해요.
* 정산금은 기본적으로 송금 당일 하나은행 최초 고시환율을 적용해 미국 달러(USD)로 지급해요.
  * 원화(KRW)로 정산금을 받고 싶다면, 거래처 등록 시 자유원 계좌를 등록해야 해요.
* 정산금을 USD로 지급할 때는 송금 수수료가 발생해요.

  * 한국에서 발생하는 송금 수수료는 토스가 부담해요.
  * 그 외 해외 구간에서 발생하는 수수료는 파트너사가 부담해요.
  * 송금 수수료 부담 방식을 변경해야 한다면,앱인토스와 별도로 협의할 수 있어요.

***

### 비즈 월렛

비즈 월렛은 토스 안에서 프로모션을 운영하기 위해 미리 충전해 두는 금액이에요.

* 프로모션에서 토스 포인트를 지급하거나 광고 푸시를 발송할 때 사용해요.
* 현재는 신용카드(법인카드 포함)로만 충전할 수 있어요.

**사용 내역**

사용 내역은 나의 워크스페이스 > 좌측 비즈 월렛 메뉴에서 아래 정보를 실시간으로 확인할 수 있어요.

* 당월 충전, 환불, 사용, 환급 내역
* 현재 사용할 수 있는 금액

**사용 내역과 관련해 아래 내용을 꼭 알아두세요.**

* 사용 항목은 마케팅이 등록되는 시점에 업데이트돼요. 실제로 사용자가 포인트를 받았는지 여부는 인보이스에서 확인해요.
* 환급 항목은 마케팅 등록 시 예상한 예산과 실제 사용한 예산의 차액이에요. 마케팅이 끝난 뒤 최대 1\~2일 이내에 환급돼요.
* 현재 사용 가능한 금액은 `충전 금액 - (환불 금액 + 사용 금액) + 환급 금액`으로 계산해요. 이 금액은 실사용 금액이 아니라 사용 금액 기준이라, 인보이스 금액과 차이가 날 수 있어요.

**인보이스**

인보이스는 나의 워크스페이스 > 좌측 비즈 월렛 메뉴 > 인보이스 보기에서 확인할 수 있어요. 인보이스에서는 월 단위 정산 기준 정보를 확인할 수 있고, 익월 1일에서 2일 사이에 업데이트돼요.

* 당월 충전, 환불, 실사용 내역
* 기초 잔액과 기말 잔액

***

### 프로모션 (토스 포인트)

**과금 구조**

* 프로모션을 등록하면, 설정한 예산만큼 비즈 월렛에서 먼저 차감돼요.
* 프로모션이 끝나면 실제로 사용한 예산을 기준으로 다시 계산하고, 남은 금액은 자동으로 환급돼요.
* 토스 포인트는 무상으로 지급하는 포인트예요. 그래서 부가세와 세금계산서는 발생하지 않아요.

***

### 광고 푸시 요금

**과금 구조**

광고 푸시 비용은 두 단계로 계산해요.

* 푸시를 등록할 때 `예상 발송 건수 × 건당 수수료` 기준으로 비즈 월렛에서 먼저 차감해요.
* 실제 발송이 끝나면 `발송에 성공한 건수 × 건당 수수료` 기준으로 다시 계산해요. 이때 차액은 자동으로 환급돼요.
* 현재 건당 수수료는 0원이에요. 추후 수수료가 부과될 때는 사전에 공지할 예정이에요.

**예시**

| 발송 건수 | 건당 수수료 | 건당 부가세 | 건당 수수료(부가세 포함) | 발송 비용 |
| ----- | ------ | ------ | -------------- | ----- |
| 100   | ₩9     | ₩0.9   | ₩9.9           | ₩990  |

**세금계산서 발행**

광고 푸시 비용에 대한 세금계산서는 실제 발송에 성공한 건수를 기준으로 계산해요. 세금계산서는 매월 3영업일까지 발행해요.


# 자주 묻는 질문

{% hint style="info" %}
**확인해 주세요**

자주 묻는 질문의 내용은 운영 정책 개편과 콘솔 기능 업데이트 등의 사유로 변경될 수 있어요. 자주 묻는 질문에 없는 개발 문의는 [개발자 커뮤니티](https://techchat-apps-in-toss.toss.im/), 운영 문의는 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 문의해 주세요.
{% endhint %}

### 앱인토스 서비스

<details>

<summary>앱인토스는 어떤 서비스인가요?</summary>

앱인토스는 파트너사가 개발한 미니앱 서비스를 토스 앱 안에서 '앱인앱(App-in-App)' 형태로 출시할 수 있는 플랫폼이에요.

WebView, Unity, React Native 기반 SDK를 통해 손쉽게 개발할 수 있고, 토스 디자인 시스템(TDS)도 함께 활용할 수 있어요. 푸시·알림과 프로모션 등을 활용해 효과적인 마케팅도 진행할 수 있어요.

</details>

<details>

<summary>앱인토스에서 어떤 기능을 제공하나요?</summary>

토스의 SDK와 API, UI 컴포넌트 등을 통해 로그인, 결제, 인증 등 핵심 기능을 빠르게 적용할 수 있어요.

개발 환경, 디자인, 마케팅, 수익화, 정산까지 통합 솔루션을 제공해요.

</details>

<details>

<summary>앱인토스 미니앱으로 오픈할 수 없는 서비스가 있나요?</summary>

앱인토스는 토스 플랫폼 정책, 관련 법령, 사용자 보호 기준에 따라 오픈이 제한되는 서비스가 있어요.

관련 내용은 [서비스 오픈 정책 ](/intro/guide)에서 확인할 수 있어요.

</details>

<details>

<summary>앱인토스 미니앱 개발부터 출시까지 얼마나 걸리나요?</summary>

게임 서비스는 약 2\~4주, 비게임 서비스는 약 1\~3개월 정도 소요돼요.

서비스 유형과 기능 범위, 검수 과정에 따라 일정은 달라질 수 있어요.

</details>

<details>

<summary>앱인토스 미니앱 서비스를 이용할 수 있는 사용자는 누구인가요?</summary>

앱인토스 미니앱 서비스는 만 19세 이상의 사용자를 대상으로 제공돼요.

Android 7 이상 또는 iOS 16 이상 환경에서 이용할 수 있어요.

</details>

<details>

<summary>앱인토스에 서비스를 등록하고 싶은데 절차가 어떻게 되나요?</summary>

콘솔 가입 → 워크스페이스 생성 → 사업자 등록 → 앱 등록 → 검수 요청 순서로 진행돼요.

개발자센터의 시작하기 문서에서 단계별 가이드를 확인할 수 있어요.

</details>

<details>

<summary>앱인토스 웨비나 자료는 어디서 확인할 수 있나요?</summary>

앱인토스 웨비나 자료는 앱인토스 콘솔과 [비즈니스 어드민 아카데미](https://business.toss.im/dashboard/business-academy)에서 확인이 가능해요.

</details>

### 앱 등록

<details>

<summary>앱 등록을 위해서는 어떤 정보가 필요한가요?</summary>

게임 서비스인지, 비게임 서비스인지에 따라 작성하는 정보가 달라요.

주로 서비스 기획의 내용을 작성해요.

* 게임 : 앱 로고, 앱 이름, appName, 고객문의 이메일, 카테고리, 등급 정보, 앱 검색 키워드, 게임 앱 설명, 썸네일, 리더보드 등
* 비게임: 앱 로고, 앱 이름, appName, 고객문의 이메일, 카테고리, 앱 설명, 앱 검색 키워드 등

자세한 내용은 콘솔에서 앱 등록하기 가이드를 참고해 주세요.

</details>

<details>

<summary>앱 등록 검수는 얼마나 걸리나요?</summary>

검토 완료까지 영업일 기준 1\~2일 소요돼요.

자세한 내용은 콘솔에서 앱 등록하기 가이드를 참고해 주세요.

</details>

<details>

<summary>appName을 수정하거나 삭제할 수 없나요?</summary>

appName은 앱 ID와 같은 개념이에요.

앱 ID는 앱 정보에서 등록하고 수정 및 삭제가 불가능해요.

자세한 내용은 콘솔에서 앱 등록하기 가이드를 참고해 주세요.

</details>

<details>

<summary>게임 서비스의 등급 심사를 어떻게 받을 수 있나요?</summary>

게임 미니앱을 출시하기 위해서는 '게임 등급분류'가 필요해요.

게임산업진흥에 관한 법률에 따르면, 게임을 유통하거나 서비스할 목적으로 제작∙배급하려면 반드시 등급분류를 받아야 해요.법적으로 정식 출시 전에 등급을 받지 않은 게임은 서비스를 출시할 수 없어요.

등급 정보를 등록하기 위해서는 스토어 링크 혹은 게임물관리위원회의 서류가 필요해요.

스토어와 게임물관리위원회에서 등급 분류를 받는 방법은 [앱인토스 공식 홈페이지의 블로그](https://toss.im/apps-in-toss/blog/game_rating_classification)에서 확인할 수 있어요.

게임산업진흥에 관한 법률에 따르면, 게임을 유통하거나 서비스할 목적으로 제작∙배급하려면 반드시 등급분류를 받아야 해요.

법적으로 정식 출시 전에 등급을 받지 않은 게임은 서비스를 출시할 수 없어요.

등급 정보를 등록하기 위해서는 스토어 링크 혹은 게임물관리위원회의 서류가 필요해요. 스토어와 게임물관리위원회에서 등급 분류를 받는 방법은 [앱인토스 공식 홈페이지의 블로그](https://toss.im/apps-in-toss/blog/game_rating_classification) 에서 확인할 수 있어요.

</details>

<details>

<summary>사업자 등록 검수는 얼마나 걸리나요?</summary>

검토 완료까지 영업일 기준 1일 소요돼요.

검수 결과는 콘솔에서 확인할 수 있어요.

자세한 내용은 사업자 등록하기 가이드를 참고해 주세요.

</details>

<details>

<summary>사업자등록증을 변경/추가 제출해야 하는데 어떻게 하나요?</summary>

콘솔 > 설정 > 사업자 정보에서 사업자등록증을 업로드할 수 있어요.

변경 시 재검토가 필요하며, 영업일 1\~2일 소요돼요.

개인→법인 변경 시 사업자번호가 달라지면 워크스페이스를 새로 만들어야 해요.

자세한 내용은 사업자 등록하기 가이드를 참고해 주세요.

</details>

<details>

<summary>사업자가 없으면 앱을 출시할 수 없나요?</summary>

사업자가 없어도 앱을 출시할 수 있어요.

다만, 사업자가 없으면 계약 및 정산 등을 위해 일부 기능을 사용하지 못해요.

사업자가 필요한 기능: 토스 로그인, 비즈월렛, 프로모션, 인앱 광고, 토스페이, 인앱 결제

자세한 내용은 사업자 등록하기 가이드를 참고해 주세요.

</details>

### 디자인

<details>

<summary>앱인토스 미니앱 디자인의 다크패턴 관련 정책이 있나요?</summary>

고객의 예상을 벗어난 설계를 한 다크패턴 디자인의 미니앱은 출시할 수 없어요.

대표적인 다크패턴의 예시는 아래와 같아요.

* 서비스에 진입하자마자 바텀싯이 뜨는 경우
* Back 버튼을 눌렀을 때, 이전 화면을 막는 바텀시트가 뜨는 경우
* 나갈 수 있는 기능이 없는 경우
* 예상치 못한 순간에 광고가 뜨는 경우
* CTA 버튼을 보고 자신이 해야할 행동을 예상할 수 없는 경우

자세한 내용은 개발자센터 디자인 파트 내 [다크패턴 방지 정책 가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/design/consumer-ux-guide)를 참고해 주세요.

</details>

<details>

<summary>TDS가 무엇인가요?</summary>

TDS는 Toss Design System의 약자이며, 토스 커뮤니티에서 사용하는 디자인 시스템이에요.

TDS는 토스 제품을 구성하며 직군을 초월한 협업을 가능케하는 디자인 언어이면서 개발자의 도구이기도 해요.

TDS를 사용하면 사용자에게 일관된 제품 경험을 제공하면서 디자이너가 문제 해결에만 집중할 수 있어요.

개발도 커스텀 UI 보다 3\~5배 빠르게 완료할 수 있어요.

관련 내용은 [개발자센터의 디자인 파트 내 토스 디자인 시스템 가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/design/components) 를 참고해 주세요.

</details>

<details>

<summary>TDS를 사용하기 전에 주의할 점이 있나요?</summary>

TDS를 안전하고 올바르게 사용하기 위해 아래와 같은 내용을 주의해 주세요.

지식재산권: 토스가 앱인토스 서비스를 통하여 제공하는 모든 자료의 권리는 토스에 귀속돼요. 파트너사는 해당 자료를 오직 앱인토스 서비스 이용을 위한 범위 내에서만 사용할 수 있어요.

사용 권한: 토스의 TDS 사용 허가는 앱인토스 서비스 제공을 위한 제한적 사용권을 부여하는 것이에요. 따라서 파트너사는 이 범위를 넘어서는 결과물이나 권리를 취득할 수 없어요.

준수 의무 및 위반 시 조치: 파트너사는 본 조항 및 관련 법령을 준수해야 해요. 파트너사가 이를 위반할 경우, 토스는 앱인토스 서비스 제공을 중단하거나 기타 필요한 조치를 취할 수 있어요.

자세한 내용은 피그마/TDS UI 라이선스 가이드를 참고해 주세요.

</details>

<details>

<summary>앱빌더가 무엇인가요?</summary>

앱빌더는 앱인토스 파트너사를 위한 UI 디자인 툴이에요.

별다른 설치 없이 토스의 UI 스타일 가이드를 따른 화면을 빠르게 완성할 수 있어요.

관련 내용은 [개발자센터의 디자인 파트 내 앱빌더 가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/design/prepare/design) 를 참고해 주세요.

</details>

<details>

<summary>앱인토스 미니앱은 시스템 모드의 다크 모드를 지원하나요?</summary>

현재 앱인토스 미니앱에서는 다크 모드를 지원하지 않아요.

라이트 모드 기준으로 개발/디자인 및 출시해야 해요.

</details>

<details>

<summary>피그마 리소스는 어디서 받을 수 있나요?</summary>

개발자센터의 디자인 파트 내 피그마 가이드 에서 확인할 수 있어요.

</details>

<details>

<summary>반드시 TDS를 사용해야 하나요?</summary>

모든 컴포넌트를 반드시 TDS로 디자인해야 하는 것은 아니에요.

다만, 일관된 사용자 경험을 위해 가능한 한 TDS 사용을 권장하며, TDS로 해결이 어려운 영역은 자체 개발해 주세요.

자세한 내용은 토스 디자인 시스템 가이드를 참고해 주세요.

</details>

<details>

<summary>Toss Products Sans 폰트를 제공해줄 수 있나요?</summary>

Toss Products Sans 폰트는 별도로 제공하지 않아요.

토스 프로덕트 산스체를 적용하려면 TDS 컴포넌트를 이용해주세요.

자세한 내용은 토스 디자인 시스템 가이드를 참고해 주세요.

</details>

### 개발

<details>

<summary>현재 운영 중인 서비스가 있어요. 앱인토스 미니앱으로 어떻게 배포하나요?</summary>

기존 서비스를 앱인토스 환경에 맞게 클라이언트 측을 개발한 뒤, 필요에 따라 자체 백엔드 서버와 연동할 수 있어요.

자세한 내용은 [가이드](/ai-vibe-coding/tutorials/webview)를 참고해주세요.

</details>

<details>

<summary>SDK 3.x로 마이그레이션해야 하나요? 언제까지 해야 하나요?</summary>

네, WebView 및 Unity 프로젝트를 운영 중인 전체 파트너사는 2026년 9월 14일까지 SDK 3.x로 업데이트해야 해요.

SDK 내부 처리 로직이 서버 기반으로 바뀌면서, SDK 이슈가 발생해도 파트너사의 재배포 없이 앱인토스 서버에서 바로 수정하고 반영할 수 있게 돼요.

Unity SDK는 별도 설정 변경 없이 git 패키지를 3.x 버전으로 업데이트하면 전환이 완료돼요.

자세한 내용은 [SDK 3.x 마이그레이션 가이드](/documentation/integration/sdk-3.x)를 참고해 주세요.

</details>

<details>

<summary>내비게이션 바는 직접 구현해야 하나요?</summary>

아니요. 앱인토스 SDK를 설치하면 기본 내비게이션 바가 자동으로 적용돼요.

비게임 미니앱은 `navigationBar` 옵션으로 뒤로가기·홈 버튼 노출 여부, 배경 투명화, 테마(라이트/다크), 액세서리 아이콘 등을 커스터마이징할 수 있어요.

자세한 내용은 [내비게이션 바 설정 가이드를](/documentation/common/navigationbar) 참고해 주세요.

</details>

<details>

<summary>앱인토스 개발을 위해 서버 사용이 꼭 필요한가요?</summary>

아니요. 별도 서버 없이 SDK만으로도 미니앱을 개발할 수 있어요.

토스 로그인, 토스페이, 스마트 발송처럼 서버 간 통신이 필요한 기능을 사용하는 경우에만 자체 서버가 필요하고, 이때 mTLS·CORS·방화벽 같은 서버 API 연동 설정을 추가로 진행하면 돼요.

자세한 내용은[ API 사용하기 가이드](/documentation/integration/server-api)를 참고해 주세요.

</details>

<details>

<summary>외부 API 호출 시 CORS 에러가 발생해요.</summary>

API 서버의 CORS 허용 목록에 앱인토스 도메인을 추가해야 해요.

등록할 도메인은 SDK 버전에 따라 달라요.

| SDK 버전   | 실제 서비스 환경                             | QR 테스트 환경                                     |
| -------- | ------------------------------------- | --------------------------------------------- |
| 3.x      | `https://<appName>.web.tossmini.com`  | `https://<appName>.private-web.tossmini.com`  |
| 1.x\~2.x | `https://<appName>.apps.tossmini.com` | `https://<appName>.private-apps.tossmini.com` |

자세한 내용은 [미니앱 출시하기 가이드](/guide/operation/deploy)를 참고해주세요.

</details>

<details>

<summary>mTLS 인증이 꼭 필요한가요?</summary>

네. 앱인토스 API의 서버간 통신에는 mTLS 인증을 필수로 요구해요.

적용하지 않으면 `ERR_NETWORK` 오류가 발생할 수 있어요.

자세한 내용은 [API 사용하기 가이드](/documentation/integration/server-api)를 참고해 주세요.

</details>

<details>

<summary>방화벽에서 허용해야 하는 IP가 있나요?</summary>

네. 서버에서 Inbound·Outbound 방화벽을 관리하고 있다면 앱인토스 IP를 반드시 허용해야 해요.

허용하지 않으면 API 호출이 실패하거나, 콘솔에 등록한 콜백 URL로 구독 상태 변경 콜백 등을 받지 못해요.

자세한 IP·포트 목록은 [API 사용하기 가이드](/documentation/integration/server-api)를 참고해 주세요.

</details>

<details>

<summary>API 요청 횟수에 제한이 있나요?</summary>

네. 앱인토스 API는 미니앱 기준으로 분당 3,000건(QPM, Queries Per Minute)까지 요청할 수 있어요.

한도를 초과하면 일정 시간 동안 추가 요청이 차단될 수 있어요.

더 많은 요청이 필요하면 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 QPM 상향을 요청할 수 있어요. 이때 사용 목적, 예상 트래픽 규모, 피크 시간대 요청량을 함께 전달해 주세요.

자세한 내용은 [API 사용하기 가이드](/documentation/integration/server-api)를 참고해 주세요.

</details>

<details>

<summary>미니앱 개발 시 보안적으로 체크해야 하는 내용이 있나요?</summary>

네, 아래 내용을 확인해 주세요.

* 외부에서 전달받은 코드를 실행하는 기능은 사용할 수 없어요. (예: `eval` 등)
* 브라우저 히스토리를 조작해서 자사 사이트로 이동시키는 방식은 사용할 수 없어요. (예: `window.location.replace` 등)
* 서버 사이드 렌더링(SSR)은 사용할 수 없어요. 클라이언트 사이드 렌더링(CSR) 또는 정적 사이트 생성(SSG) 방식만 사용할 수 있어요.
* WebSocket을 사용하는 경우, 암호화된 `wss://` 연결만 사용해요. (예: Firebase Firestore, Supabase Realtime 등)
* API 통신은 암호화된 HTTPS 연결만 사용해요.
* 사용자 로그인 정보, 결제(토스페이, 인앱 결제) 내역 등 민감 정보는 DB에 암호화해서 저장해야 해요.
* 토스 로그인, 약관 동의 등 사용자의 동의 이력은 반드시 관리해야 해요.
* 개인정보를 클라이언트에서 자체 서버로 전송할 때도 암호화할 것을 권장해요.

</details>

<details>

<summary>테스트 환경에서는 정상 동작했는데, 실제 환경에서 API 호출이 실패해요.</summary>

테스트 환경(샌드박스, AIT Devtools 등)에서는 HTTP 요청이 허용되지만, 라이브 환경에서는 HTTPS만 허용돼요. HTTP 기반 API는 토스 앱에서 차단돼요.

API 서버가 HTTPS를 지원하는지 확인해 주세요.

자세한 내용은 [미니앱 테스트하기 가이드](/guide/operation/toss)를 참고해 주세요.

</details>

<details>

<summary>iOS에서 로그인 세션이 유지되지 않아요.</summary>

iOS·iPadOS 13.4 이상에서는 서드파티 쿠키가 완전히 차단돼요.

쿠키 기반 로그인 대신 토큰 기반 인증 방식을 적용해 주세요.

자세한 내용은 [미니앱 테스트하기 가이드](/guide/operation/toss)를 참고해 주세요.

</details>

<details>

<summary>QR 테스트 중 에러가 발생했어요.</summary>

`Sentry`를 연동해 클라이언트 로그를 확인하면 디버깅할 수 있어요.

자세한 내용은 Sentry 설정하기 가이드를 참고해 주세요.

</details>

<details>

<summary>안드로이드 샌드박스 앱에서 `스키마를 여는데 실패했어요` 에러가 발생해요.</summary>

안드로이드 샌드박스 앱 연결을 위해 adb 설정이 필요해요.

자세한 내용은 [가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/development/test/sandbox#_8-%EB%AF%B8%EB%8B%88%EC%95%B1-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0) 를 참고해주세요.

</details>

<details>

<summary>유니티 게임을 앱인토스 미니앱으로 런칭할 수 있나요?</summary>

가능해요. Unity 게임을 WebGL로 빌드하면 앱인토스 미니앱으로 만들 수 있어요.

앱인토스 Unity SDK(유니티 패키지)를 설치하면 별도의 Vite 프로젝트 구성이나 JS Bridge 구현 없이, WebGL 빌드부터 `.ait` 패키징까지 한 번에 처리할 수 있어요. Unity 2021.3 이상을 지원해요.

자세한 내용은 [Unity SDK 연동하기 가이드](/documentation/unity/integration)를 참고해 주세요.

</details>

<details>

<summary>앱인토스 Unity SDK를 사용하면 어떤 점이 좋나요?</summary>

앱인토스 Unity SDK(유니티 패키지)를 사용하면 아래와 같은 이점이 있어요.

* **짧은 포팅 기간**: 별도의 Vite 프로젝트 구성이나 JS Bridge 구현 없이 쉽고 빠르게 미니앱으로 포팅할 수 있어요.
* **향상된 안정성과 사용자 경험**: 로딩 시간을 1/5 수준으로 단축해요.
* **더 많은 성능 지표 확인**: 크래시 발생률, 로딩 시간, FPS 등 다양한 게임 성능 지표를 확인할 수 있어요.
* **기본 제공되는 로딩 화면**: 별도 구성 없이 로딩 화면이 SDK에 기본 포함돼요.

실제로 수동 포팅 방식을 사용했을 때 로딩 시간이 약 20초로 길어 유저 이탈과 CS 문의가 많았던 게임이, Unity SDK로 전환한 뒤 로딩 시간이 1초로 줄어든 사례도 있어요.

자세한 내용은 [Unity SDK 연동하기 가이드](/documentation/unity/integration)를 참고해 주세요.

</details>

<details>

<summary>샌드박스 테스트는 필수인가요?</summary>

SDK 2.x를 사용할 때는 샌드박스 앱에서 테스트해야 해요. React Native는 현재 SDK 2.x만 지원하기 때문에 반드시 샌드박스 앱에서 테스트해야 해요.

WebView에서 SDK 3.x로 마이그레이션했다면 AIT Devtools를 사용해 로컬 브라우저에서 테스트할 수 있어요. (SDK 3.0.1 버전에서 마이그레이션한 경우만 수동 설정이 필요해요.) 아직 SDK 2.x를 사용 중인 WebView 프로젝트는 마찬가지로 샌드박스 앱에서 테스트해야 해요.

자세한 내용은 [가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/development/test/sandbox)와 [SDK 3.x 마이그레이션 가이드](/documentation/integration/sdk-3.x)를 참고해주세요.

</details>

<details>

<summary>`deploymentId를 찾을 수 없어요` 에러가 발생해요.</summary>

가이드 에 맞게 `.ait` 파일을 다시 빌드해주세요.

빌드 과정에서 `deploymentId`가 올바르게 포함되어야 해요.

</details>

<details>

<summary>WebView에서 웹 API 사용이 가능한가요?</summary>

네. WebView 환경에서 일반적인 웹 API 사용은 제한하지 않아요.

</details>

<details>

<summary>Storage에 저장된 데이터는 언제 삭제되나요?</summary>

앱이 삭제되면 Storage에 저장된 데이터도 함께 삭제돼요.

앱 삭제나 기기 변경 후에도 데이터를 유지해야 한다면 자체 서버와 연동해 주세요.

</details>

### 출시 검수

<details>

<summary>반려 사유가 이해되지 않아요. 구체적으로 어떤 부분이 문제인가요?</summary>

반려 시 콘솔에 사유가 안내되며, 해당 사유에 대한 상세 가이드는 개발자센터 검수 가이드라인에서 확인할 수 있어요.

게임 출시 가이드는 [게임 출시 가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/checklist/app-game)를, 비게임 출시 가이드는 [비게임 출시 가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/checklist/app-nongame)를 참고해 주세요.

</details>

<details>

<summary>현재 검수 진행 상태가 어떻게 되나요?</summary>

콘솔 > 앱 관리 > 출시 관리에서 현재 검수 상태(접수/검수 중/승인/반려)를 확인할 수 있어요.

자세한 내용은 미니앱 출시 가이드를 참고해 주세요.

</details>

<details>

<summary>반려된 부분 수정했는데 재검수는 어떻게 요청하나요?</summary>

콘솔에서 반려 사유에 따라 수정 후, 출시 관리에서 '재검수 요청' 버튼을 누르면 돼요.

재검수도 동일하게 영업일 3\~5일 소요돼요.

자세한 내용은 미니앱 출시 가이드를 참고해 주세요.

</details>

<details>

<summary>앱 출시 검수는 무엇을 확인하나요?</summary>

앱 출시 검수는 서비스, 기능, 디자인, 보안을 검토해요.

서비스: 앱인토스 미니앱으로 출시가 가능한 서비스인지 확인해요. 기능: 사용자가 서비스를 정상적으로 이용할 수 있도록 기능이 동작하는지 확인해요. 디자인: 출시 가이드에 따른 정책을 위반하지 않았는지 확인해요. 보안: 앱인토스 보안에 부정적인 영향을 미치는지 확인해요.

게임 출시 가이드는 [게임 출시 가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/checklist/app-game)를, 비게임 출시 가이드는 [비게임 출시 가이드](https://appsintoss.gitbook.io/appsintoss-docs/landing-page/checklist/app-nongame)를 참고해 주세요.

</details>

<details>

<summary>앱 출시 검수는 얼마나 걸리나요?</summary>

검토 완료까지 영업일 기준 3\~5일 소요돼요. 주말·공휴일에는 검수가 진행되지 않아요.

자세한 내용은 미니앱 출시 가이드를 참고해 주세요.

</details>

<details>

<summary>앱 번들 업로드의 용량 제한이 있나요?</summary>

앱 번들은 압축 해제 기준 100MB 이하만 업로드할 수 있어요.

자세한 내용은 미니앱 출시 가이드를 참고해 주세요.

</details>

<details>

<summary>이미 앱 출시 검토 요청을 한 상태에서 앱의 오류를 발견했는데 어떻게 해야하나요?</summary>

현재 앱 출시 검토 요청은 1개의 번들(버전)만 진행할 수 있어요.

오류가 있는 번들의 검토 요청을 취소하고, 재업로드 후에 다시 검토 요청을 하면 돼요.

자세한 내용은 미니앱 출시 가이드를 참고해 주세요.

</details>

<details>

<summary>앱 출시 검수에서 반려되었으면 어떻게 해야하나요?</summary>

앱 출시 검수에서 반려되면 반려사유를 통해 어떤 점을 개선해야 하는지 확인할 수 있어요.

반려사유 기반으로 개선한 후, 다시 검토 요청을 하면 돼요.

자세한 내용은 미니앱 출시 가이드를 참고해 주세요.

</details>

<details>

<summary>반려사유에 대해 문의를 할 수 있나요?</summary>

반려사유는 콘솔 우측 하단의 채널톡의 상담원 연결을 통해 문의할 수 있어요.

[채널톡 링크](https://apps-in-toss.channel.io/user-chats/69352b8e8d373db4ee2f?page=https%3A%2F%2Fapps-in-toss.channel.io%2Fworkflows%2F787658)

</details>

### 마케팅

<details>

<summary>푸시, 알림의 문구를 작성할 때, 주의할 점이 있나요?</summary>

콘솔의 마케팅 도구로서 푸시, 알림은 토스의 앱 자체에서 발송이 이루어지기 때문에, 토스 라이팅 규칙을 지켜야 해요.

관련 가이드는 콘솔 가이드에서 참고해 주세요.

[푸시, 알림의 콘솔 가이드](https://appsintoss.gitbook.io/appsintoss-docs/guide/marketing/intro-1#%EA%B8%B0%EB%B3%B8-%EA%B7%9C%EC%B9%99)

</details>

<details>

<summary>푸시, 알림에서 광고성 메시지와 기능성 메시지의 차이가 무엇인가요?</summary>

광고성 메시지와 기능성 메시지는 목적이 다르며, 활용 방법도 달라요.

광고성 메시지: 할인, 이벤트, 신규 상품 안내처럼 마케팅 목적의 프로모션 메시지를 의미해요. 설정한 세그먼트에게 광고성 메시지를 발송할 수 있어요.

기능성 메시지: 서비스 이용 과정에서 발생하는 주문, 결제, 배송 등 필수 정보를 전달하는 메시지를 의미해요. API를 통해서 기능성 메시지를 발송할 수 있어요.

더욱 자세한 가이드는 [푸시, 알림의 콘솔 가이드](https://appsintoss.gitbook.io/appsintoss-docs/guide/marketing/intro-1#%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%9C%A0%ED%98%95) 를 참고해 주세요.

</details>

<details>

<summary>하루에 광고성 푸시는 몇 명에게 보낼 수 있나요?</summary>

워크스페이스 기준으로 하루에 광고 푸시를 최대 10만 명에게 발송이 가능하며, 최적화 발송을 통해 가장 알맞은 유저에게 도달하게 돼요.

발송 한도 초과 시 다음 날 자정에 초기화돼요.

자세한 내용은 스마트 발송 콘솔 가이드를 참고해 주세요.

</details>

<details>

<summary>푸시 메시지 검수는 얼마나 걸리나요?</summary>

검토 완료까지 영업일 기준 2\~3일 소요돼요.

자세한 내용은 스마트 발송 콘솔 가이드를 참고해 주세요.

</details>

<details>

<summary>알림 수신 동의 문구는 어떻게 작성하나요?</summary>

광고성 알림을 발송하려면 유저의 수신 동의를 받아야 해요.

동의 문구에는 다음 내용을 포함해야 하며, 알림 동의문 SDK를 연동해 주세요.

발송 주체(파트너사명)

알림 내용 요약

수신 동의 및 철회 방법 안내

동의 문구 예시와 자세한 내용은 스마트 발송 콘솔 가이드를 참고해 주세요.

</details>

<details>

<summary>프로모션에서 혜택탭 노출을 설정하면 어디에 노출되나요?</summary>

토스 > 혜택탭 > 새로운 서비스 써보고에 리스트 형태로 노출돼요.

[프로모션의 이해하기 가이드](https://appsintoss.gitbook.io/appsintoss-docs/guide/marketing/intro-2#%ED%94%84%EB%A1%9C%EB%AA%A8%EC%85%98%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80%EC%9A%94) 를 참고해 주세요.

</details>

<details>

<summary>프로모션 지급 테스트를 꼭 해야 하나요?</summary>

프로모션 지급 테스트를 진행해야 '시작하기'를 진행할 수 있어요.

`TEST_{promotionCode}`로 테스트를 완료해야 해요.

자세한 내용은 프로모션 개발하기 가이드를 참고해 주세요.

</details>

<details>

<summary>프로모션에서 중복된 유저에게 지급되지 않도록 막혀있나요?</summary>

중복 지급 차단 로직은 직접 구현해야 해요.

관련 내용은 [프로모션의 개발하기 가이드](https://appsintoss.gitbook.io/appsintoss-docs/documentation/common/growth/promotion) 를 참고해 주세요.

</details>

<details>

<summary>스마트발송 소재 검수가 언제 완료되나요?</summary>

발송 소재 검수는 영업일 기준 1\~2일 소요돼요.

발송 예정일 최소 3영업일 전에 소재를 등록해 주시는 것을 권장해요.

자세한 내용은 스마트 발송 콘솔 가이드를 참고해 주세요.

</details>

<details>

<summary>스마트발송을 설정했는데 0건 발송이에요.</summary>

발송 대상 세그먼트 설정, 발송 시간대, 유저 opt-in 상태를 확인해 주세요.

특히 '기능성 알림'과 '광고성 알림'의 발송 조건이 다르므로, 알림 유형에 맞는 설정이 필요해요.

자세한 내용은 스마트 발송 콘솔 가이드를 참고해 주세요.

</details>

<details>

<summary>대시보드의 데이터는 언제 업데이트 되나요?</summary>

대시보드의 데이터는 익일 오전 8시 정도에 업데이트 돼요.

자세한 내용은 대시보드 가이드를 참고해 주세요.

</details>

### 수익화

<details>

<summary>인앱 광고에서 어떤 유형의 광고를 지원하나요?</summary>

현재 전면형 광고와 보상형 광고를 지원하고 있어요.

전면형 광고: 광고가 전체화면으로 표시되며, 화면 전환 지점에서 보여줘요.

보상형 광고: '광고를 시청하면 보상 지급'의 구조로, 사용자가 원할 때 광고를 볼 수 있어요.

추후 다양한 광고 유형을 지원할 예정이에요.

자세한 내용은 인앱 광고 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>인앱 광고 테스트를 위해서 사용하는 ID가 있나요?</summary>

인앱 광고 테스트를 위해서는 반드시 테스트용 ID를 사용해야 해요.

테스트용 ID는 [인앱 광고의 개발하기 가이드](https://appsintoss.gitbook.io/appsintoss-docs/documentation/common/monetization/iaa) 에서 참고해 주세요.

</details>

<details>

<summary>토스페이 Key 발급은 얼마나 걸리나요?</summary>

서류 제출 후, 영업일 기준 7\~14일 소요돼요.

자세한 내용은 토스페이 콘솔 가이드를 참고해 주세요.

</details>

<details>

<summary>토스페이 테스트 Key로 승인 테스트가 가능한가요?</summary>

테스트 Key로는 결제 생성까지만 활용할 수 있어요.

승인 처리는 불가해요.

자세한 내용은 토스페이 개발하기 가이드를 참고해 주세요.

</details>

<details>

<summary>인앱 결제 가격 제한이 있나요?</summary>

인앱 결제 상품의 가격은 최소 400원, 최대 1,400,000원까지 설정할 수 있어요.

자세한 내용은 인앱 결제 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>유저가 환불 요청했는데 파트너가 직접 처리할 수 있나요?</summary>

네, 콘솔 > 인앱결제 > 결제 내역에서 개별 건의 환불 처리가 가능해요.

환불 정책은 파트너사가 직접 운영하며, 처리 후 유저에게 자동 안내돼요.

자세한 내용은 인앱 결제 콘솔 가이드를 참고해 주세요.

</details>

<details>

<summary>eCPM이 계속 떨어지고 있는데 원인이 뭐가요?</summary>

eCPM은 다양한 요인에 따라 변동돼요. 아래 항목을 점검해 보세요.

(1) DAU 변화: 유저 수 감소 시 광고 경쟁률이 낮아져 eCPM이 하락할 수 있어요.

(2) 광고 지면 수: 지면이 너무 많으면 노출당 단가가 낮아질 수 있어요.

(3) 노출 빈도: 같은 유저에게 반복 노출되면 클릭률이 떨어져요. (4) 광고 위치: 유저 동선과 맞지 않는 위치는 효과가 낮아요.

자세한 내용은 인앱 광고 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>광고가 아예 안 나와요. 테스트에서는 됐는데 라이브에서 안 돼요.</summary>

테스트 키와 라이브 키가 다르게 설정되어 있는지 확인해 주세요.

라이브 환경에서는 광고 지면 승인 완료 후 송출이 시작되며, 승인까지 최대 24시간 소요될 수 있어요.

광고 그룹 ID가 해당 앱에 올바르게 매칭되어 있는지도 확인해 주세요.

자세한 내용은 인앱 광고 개발하기 가이드를 참고해 주세요.

</details>

<details>

<summary>인앱 결제의 환불 처리는 어떻게 진행할 수 있나요?</summary>

앱마켓에 따라 환불 처리 방식이 달라요.

Google Play: 48시간 이내 자동 처리돼요. 이후 수동 요청이 가능해요. App Store: 모든 환불은 Apple이 직접 처리해요.

자세한 내용은 인앱 결제 콘솔 가이드를 참고해 주세요.

</details>

### 정산

<details>

<summary>비즈월렛 충전금과 인보이스는 어떻게 확인하나요?</summary>

비즈월렛은 광고 푸시 및 프로모션 집행을 위한 선충전 금액이에요.

콘솔 > 워크스페이스 > 비즈월렛에서 확인할 수 있어요. 충전, 사용, 환급 내역은 실시간으로 업데이트 돼요. 인보이스는 익월 1~~2일 이내 업데이트가 완료돼요. 환급은 캠페인 종료 후 1~~2일 이내 자동 반영돼요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>앱인토스 수수료율이 어떻게 되나요?</summary>

다만 현재 별도의 수수료는 발생되지 않으며, 추후 발생될 경우 사전 공지해 드릴 예정이에요.

다만 현재 별도의 수수료는 발생 되지 않으며, 추후 발생 될 경우 사전 공지해 드릴 예정이에요.

아래 내용은 참고용으로 확인해 주세요.

(1) 인앱 광고: 광고 수익의 15%가 앱인토스 수수료로 공제 (2) 인앱 결제: 결제 금액 기준 5%가 앱인토스 수수료로 공제 (별도로 앱마켓 수수료 15\~30% 부과) (3) 비즈월렛(광고 푸시/프로모션): 집행 비용 외 별도 수수료 없음 (4) 스마트 발송: 건당 9.9원(vat 포함)

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>간이과세자도 정산을 받을 수 있나요?</summary>

네, 간이과세자도 정산을 받을 수 있어요.

다만 세금계산서 대신 현금영수증을 발행해야 하며, 2회 이상 발급되지 않을 경우 서비스 이용이 제한될 수 있어요.

콘솔 > 워크스페이스 > 내 정보 > 정산 정보에서 사업자 유형을 정확히 등록해 주세요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>광고 푸시 비용은 언제, 어떻게 과금되나요?</summary>

광고 푸시는 아래와 같은 방식으로 과금돼요.

등록 시점: 예상 발송 건수 기준 1차 차감 실제 발송 후: 성공 건수 기준으로 재계산 및 차액 환급

세금계산서는 실발송 기준으로 매월 영업일 3일 이내 발행돼요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>정산을 받기 위해서 어떤 정보를 등록해야 하나요?</summary>

정산을 받기 위해 콘솔 > 워크스페이스 > 파트너 정보 > 정산 정보에 다음 항목을 등록해야 해요.

사업자 정보 거래 계좌 사본 세금계산서 발행용 이메일

모든 정산은 앱 단위가 아니라 사업자 단위로 합산되어 진행돼요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>인앱 광고 정산은 어떻게 이루어지나요?</summary>

인앱 광고는 외부 미디에이션 플랫폼 또는 Toss Ads를 통해 송출되며, 광고 수익에서 앱인토스 수수료 15%가 공제된 금액이 파트너사에게 지급돼요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>인앱 결제 정산은 어떻게 이루어지나요?</summary>

인앱 결제는 앱마켓(애플, 구글)과 앱인토스 각각 수수료가 발생해요.

앱마켓 수수료: 공급가 기준 15% 또는 30% 토스 수수료: 결제가 기준 5% (CBT 0%) 지급일: 익월 말 (영업일 기준) 정산내역 제공: 익월 5일 이내

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>세금계산서 발행은 누가 진행하나요?</summary>

정산 항목에 따라 발행 주체가 달라요.

광고 수익 (파트너사 → 토스 발행)

파트너사가 직접 발행 발행일자: 수익 발생월의 말일 이메일: <yj.jang@toss.im> 기한: 매월 영업일 2일 이내

앱인토스 광고 수수료 (토스 → 파트너사 발행)

토스가 선공제 후 발행 발행일: 매월 영업일 3일 이내

인앱 결제 수수료

애플 수수료: 토스가 대신 발행 토스 수수료: 토스가 발행

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>역발행 세금계산서 승인은 어떻게 하나요? 사업자용 공인인증서가 없어요.</summary>

역발행 승인은 팝빌을 통해 진행되며, 사업자용 공동인증서가 필요해요.

인증서가 없는 경우 이번 달만 정발행으로 처리 가능하며, 다음 달부터는 반드시 사업자용 공인인증서를 발급받아 역발행 승인을 진행해 주세요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>정산금이 입금되지 않았어요 / 금액이 달라요.</summary>

정산은 월 단위로 진행되며, 익월 말일(인앱광고 및 영업일 기준)에 등록된 계좌로 입금돼요.

정산금이 미입금되거나 금액이 다른 경우 아래를 확인해 주세요:

(1) 세금계산서 미승인: 역발행 세금계산서를 기한 내 승인하지 않으면 해당 월 정산이 익월로 이월돼요. (2) 계좌 정보 오류: 예금주명과 사업자 정보가 일치하는지 확인해 주세요. (3) 앱마켓 직권취소: Google/Apple의 직권취소 건은 정산에서 제외돼요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>정산 계좌를 변경하고 싶어요. 어떻게 하나요?</summary>

정산 계좌는 콘솔 > 워크스페이스 > 내 정보 > 정산 정보에서 변경할 수 있어요.

변경 후 익월 정산부터 새 계좌로 적용돼요. 당월 정산이 이미 진행 중이면 기존 계좌로 입금될 수 있어요.

계좌 변경 시 예금주명과 사업자 정보가 일치해야 해요.

문제가 있으면 채널톡으로 문의해 주세요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>사업자 유형이 변경되면 정산에 영향이 있나요?</summary>

네, 사업자 유형(일반과세자/간이과세자/법인)이 변경되면 세금계산서 발행 방식이 달라져요. 변경 사항은 채널톡으로 문의해 주세요.

변경 사항은 익월 정산부터 반영돼요.

개인→법인 변경 등에 따른 사업자번호가 달라지면 워크스페이스를 새로 만들어야 해요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

<details>

<summary>구글 기프트카드로 결제된 건은 어떻게 처리해야 하나요?</summary>

구글 기프트카드 결제는 현금영수증 의무가 있어요.

2025.12.01 이후 토스에서 자동 대행 발행해요. 결제내역에서 기프트카드 여부 확인이 가능해요. 앱마켓 직권취소로 정산에서 제외되어도, 현금영수증을 취소하지 말아야 해요. 익월 초 제공되는 정산 리포트를 참고해서 처리해 주세요.

자세한 내용은 정산 이해하기 가이드를 참고해 주세요.

</details>

### 홍보

<details>

<summary>토스 로고는 어떻게 사용할 수 있나요?</summary>

토스 로고는 보도자료 가이드와 외부 광고 가이드에 따라, 색상과 비율을 변경하지 않고 사용해야 해요.

자세한 내용은 보도자료 가이드를 참고해 주세요.

</details>

<details>

<summary>보도자료에서 \'입점\'이라는 표현을 사용할 수 있나요?</summary>

보도자료에서는 '입점'이 아닌, '토스 제휴'로 표기해야 해요.

그리고 채널톡 문의를 통해 사전 검토 요청 후 승인을 받고 배포해야 해요.

자세한 내용은 보도자료 가이드를 참고해 주세요.

</details>

### 서비스 종료

<details>

<summary>앱 서비스를 종료/삭제하고 싶어요. 절차가 어떻게 되나요?</summary>

채널톡으로 앱 ID와 종료 사유를 알려주시면 운영팀에서 종료 절차를 안내해 드려요.

종료 시 기존 유저 안내, 정산 마무리 등의 후속 절차가 있으니 최소 2주 전에 문의해 주세요.

자세한 내용은 서비스 종료하기 가이드를 참고해 주세요.

</details>


# API & SDK 한 눈에 보기

앱인토스에서 제공하는 주요 기능과 관련 문서를 한 곳에서 확인할 수 있어요. 기능별로 필요한 Client SDK 문서나 서버 API 문서로 바로 이동할 수 있어요.

[샘플 프로젝트 보러가기](https://github.com/toss/apps-in-toss-examples/tree/main)

{% hint style="info" %}
**WebView 프로젝트는 SDK 3.x로 업데이트해 주세요.**

SDK 3.x는 WebView 프로젝트의 구조를 개선한 업데이트예요. 설정 파일 이름과 일부 프로퍼티가 변경되고, 클라이언트 SDK가 도메인 객체 중심으로 정리됐어요.

WebView 프로젝트를 운영 중이라면 [SDK 3.x 마이그레이션](/documentation/integration/sdk-3.x) 문서를 확인해 주세요.
{% endhint %}

| 기능                                                    | 설명                                                         |
| ----------------------------------------------------- | ---------------------------------------------------------- |
| [사용자 식별키 발급](/documentation/sdk/domains-api/user)     | 로그인 없이도 사용자를 구분할 수 있는 익명 사용자 식별키를 발급받을 수 있어요.              |
| [사용자 식별 키 검증](/documentation/api/user-key)            | SDK로 발급받은 익명 사용자 식별 키가 유효한지 서버에서 검증할 수 있어요.                |
| [사용자 정보](/documentation/sdk/domains-api/user)         | 사용자가 동의한 개인정보나 연령대 정보를 조회할 수 있어요.                          |
| [토스 로그인](/documentation/sdk/domains-api/tossauth)     | 토스 계정으로 로그인하고 인가 코드를 받을 수 있어요.                             |
| [토스 로그인 API](/documentation/api/toss-login)           | AccessToken 발급, 사용자 정보 조회, 로그인 연결 끊기 같은 서버 API를 사용할 수 있어요. |
| [토스 인증](/documentation/sdk/domains-api/tossauth)      | 토스 인증서 서명이나 토스 로그인 연동 여부 확인 기능을 사용할 수 있어요.                 |
| [인앱 결제](/documentation/sdk/domains-api/iap)           | 앱스토어 또는 플레이스토어 인앱결제를 진행할 수 있어요.                            |
| [인앱 결제 API](/documentation/api/iap)                   | 인앱결제 주문 상태를 서버에서 조회할 수 있어요.                                |
| [토스페이 결제 인증](/documentation/sdk/domains-api/tosspay)  | 토스페이 결제창을 띄우고 일회성 결제 또는 정기결제 인증을 진행할 수 있어요.                |
| [토스페이 API](/documentation/api/toss-pay)               | 토스페이 결제 실행, 상태 조회, 환불 등 서버 API를 사용할 수 있어요.                 |
| [프로모션 리워드](/documentation/sdk/domains-api/promotion)  | 토스 포인트 리워드 지급이나 친구 초대 기능을 사용할 수 있어요.                       |
| [프로모션 API](/documentation/api/promotion)              | 토스 포인트 지급 키 생성과 리워드 지급 실행 API를 사용할 수 있어요.                  |
| [푸시, 알림 API](/documentation/api/push)                 | 테스트 메시지, 단건 메시지, 대량 메시지를 서버에서 발송할 수 있어요.                   |
| [알림 동의](/documentation/sdk/domains-api/notification)  | 사용자에게 알림 수신 동의를 요청할 수 있어요.                                 |
| [공유](/documentation/sdk/domains-api/share)            | 토스 공유 링크를 만들거나 메시지 공유 창을 띄울 수 있어요.                         |
| [리뷰 요청](/documentation/sdk/domains-api/review)        | 사용자가 만족을 느낄 만한 시점에 앱 리뷰 작성을 요청할 수 있어요.                     |
| [광고](/documentation/sdk/domains-api/ads)              | 전면 광고, 리워드 광고, AdMob 광고 같은 광고 기능을 사용할 수 있어요.               |
| [사용자 행동 기록](/documentation/sdk/domains-api/analytics) | 클릭, 노출, 화면 진입, 사용자 행동 이벤트를 기록할 수 있어요.                      |
| [데이터 SDK API](/documentation/api/sdk)                 | 데이터 SDK 연동에 필요한 서버 API를 확인할 수 있어요.                         |
| [Safe Area](/documentation/sdk/domains-api/safearea)  | 노치나 둥근 모서리에 가려지지 않도록 화면 여백 값을 조회하거나 구독할 수 있어요.             |
| [화면 제어](/documentation/sdk/domains-api/screen)        | 화면 닫기, 방향 설정, 화면 항상 켜짐, 캡처 방지 같은 동작을 제어할 수 있어요.            |
| [디바이스](/documentation/sdk/domains-api/device)         | 외부 URL 열기, 카메라, 앨범, 연락처, 위치, 햅틱 같은 기기 기능을 사용할 수 있어요.       |
| [권한](/documentation/sdk/domains-api/permissions)      | 카메라, 위치, 사진첩, 연락처, 클립보드 같은 기기 권한을 확인하거나 요청할 수 있어요.         |
| [클립보드](/documentation/sdk/domains-api/clipboard)      | 클립보드에 저장된 텍스트를 가져오거나 텍스트를 복사할 수 있어요.                       |
| [파일](/documentation/sdk/domains-api/file)             | Base64 파일을 기기에 저장하거나 PDF 파일을 네이티브 뷰어로 열 수 있어요.             |
| [게임](/documentation/sdk/domains-api/game)             | 게임센터 리더보드, 게임 프로필, 점수 등록 기능을 사용할 수 있어요.                    |
| [Storage](/documentation/sdk/domains-api/storage)     | 사용자 정보를 기기에 저장하고 읽을 수 있어요.                                 |
| [환경](/documentation/sdk/domains-api/environment)      | 실행 환경, 토스 앱 버전, 네트워크 상태, 서버 시간, 언어 같은 정보를 확인할 수 있어요.       |


# 연동 준비


# 시작하기

앱인토스는 **클라이언트 SDK**와 **서버 API** 두 가지 방식으로 연동해요. SDK는 토스 앱 내 미니앱 실행 환경을 구성하고, API는 서버 간 통신으로 로그인·결제 등 핵심 기능을 처리해요.

[샘플 프로젝트 보러가기 ↗](https://github.com/toss/apps-in-toss-examples/tree/main)

[예제 모음 확인하기 ↗](https://github.com/toss/apps-in-toss-examples/tree/main/examples)

{% hint style="info" %}
**주의해 주세요**

iframe은 사용할 수 없어요. iframe을 사용하면 앱인토스 기능이 정상 동작하지 않고, 내부 보안 심사에서도 반려돼요. 단, YouTube 영상 콘텐츠를 삽입하는 용도는 예외적으로 iframe 사용이 가능해요.
{% endhint %}

***

### 앱인토스 아키텍처

앱인토스는 크게 **SDK(클라이언트)** 와 **서버 API** 두 레이어로 구성돼요.

<figure><img src="https://3242303459-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FbbsGTd7OgbyqnSM8Iwcy%2Fuploads%2FcBc5hfPtb6XP9MJNhJf0%2Fimage.png?alt=media&#x26;token=a9f54f5f-5c71-4440-adfc-6ae4ee182791" alt=""><figcaption></figcaption></figure>

SDK는 카메라, 위치정보, 결제 UI 같은 **네이티브 기능**을 미니앱에서 바로 쓸 수 있도록 브릿지 역할을 해요. 서버 API는 **파트너사 서버와 앱인토스 서버 간 통신**을 담당해요. 로그인 토큰 검증, 결제 승인, 스마트 발송처럼 서버에서 처리해야 하는 기능들이 여기에 해당해요.

<figure><img src="https://3242303459-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FbbsGTd7OgbyqnSM8Iwcy%2Fuploads%2FAqXKtJF05AYJew8fbKKb%2Fimage.png?alt=media&#x26;token=9dea6a0d-1764-43db-b4b4-304c1fedb1a7" alt=""><figcaption></figcaption></figure>

#### SDK — 미니앱 실행 환경

파트너사는 **WebView SDK** 또는 **React Native SDK** 중 하나를 선택해 미니앱을 개발해요. 두 SDK 모두 `Granite`을 공통 런타임 레이어로 사용해요.

| SDK              | 설명                                |
| ---------------- | --------------------------------- |
| WebView SDK      | 기존 웹 서비스를 토스 앱에서 빠르게 실행할 수 있어요.   |
| React Native SDK | 네이티브 수준의 성능과 토스 앱 통합이 필요할 때 사용해요. |

파트너사는 SDK만 연동해 빌드 결과물을 업로드하면, 내부 검수 절차 후 바로 출시할 수 있어요. 복잡한 네이티브 개발 없이도 로그인·결제·인증 같은 핵심 기능을 바로 사용할 수 있어요.

#### 서버 API — 서버 간 통신

로그인 토큰 검증, 결제 처리, 스마트 발송 등은 **파트너사 서버 ↔ 앱인토스 서버** 간 API 통신으로 처리해요. 모든 API 통신은 **mTLS(양방향 TLS 인증)** 로 보호돼요.

***

### SDK 소개

앱인토스 SDK의 핵심은 `Granite`이에요. `Granite`은 앱 실행 환경을 초기화하고, 토스 앱과의 통신을 담당하는 공통 런타임 레이어예요.

#### AppsInToss

`AppsInToss.registerApp`은 서비스의 기본 환경을 설정하고, 복잡한 초기 구성 없이 개발을 빠르게 시작할 수 있도록 도와줘요. **`appName`만 전달해도** 아래 기능들을 즉시 사용할 수 있어요.

* **파일 기반 라우팅**: Next.js처럼 경로와 URL이 자동 매핑돼요.
  * 예시: `/my-service/pages/home.ts` → `intoss://my-service/home`
* **쿼리 파라미터 처리**: URL 스킴 파라미터(referrer 등)를 바로 사용 가능해요.
* **뒤로 가기 제어**: 뒤로 가기 이벤트를 가로채 다이얼로그 표시나 화면 닫기를 처리할 수 있어요.
* **화면 가시성 감지**: 화면이 보이거나 가려지는 이벤트에 맞춰 동작을 제어할 수 있어요.

**시그니처**

```typescript
AppsInToss: {
    registerApp(
      AppContainer: ComponentType<PropsWithChildren<InitialProps>>,
      { appName, context, router }: BedrockProps
    ): (initialProps: InitialProps) => JSX.Element;
    readonly appName: string;
}
```

**예제: 앱 등록하기**

```tsx
import { AppsInToss } from '@apps-in-toss/framework';
import { PropsWithChildren } from 'react';
import { InitialProps } from '@granite-js/react-native';
import { context } from '../require.context';

function AppContainer({ children }: PropsWithChildren<InitialProps>) {
  return <>{children}</>;
}

// appName과 context만 전달하면 기본 설정이 완료돼요.
export default AppsInToss.registerApp(AppContainer, { context });
```

#### InitialProps

사용자가 화면으로 진입할 때 네이티브(Android / iOS)가 앱으로 전달하는 **초기 데이터 타입**이에요. 플랫폼에 따라 구조가 달라요.

```typescript
type InitialProps = AndroidInitialProps | IOSInitialProps;
```

**프로퍼티**

* **platform** · 필수 · `'ios' | 'android'`

  현재 앱이 실행 중인 플랫폼이에요.
* **initialColorPreference** · 필수 · `ColorPreference`

  초기 컬러 테마예요. 사용자가 설정한 컬러 테마를 나타내요.
* **networkStatus** · 필수 · `NetworkStatus`

  현재 기기의 네트워크 연결 상태와 연결된 네트워크예요.
* **scheme** · `string`

  현재 화면에 진입하는 데 사용한 URL 스킴이에요.
* **initialFontSize** · 필수 · ``xSmall` | `Small` | `Medium` | `Large` | `xLarge` | `xxLarge` | `xxxLarge` | `A11y_Medium` | `A11y_Large` | `A11y_xLarge` | `A11y_xxLarge` | `A11y_xxxLarge``

  (iOS only) iOS 시스템 폰트 크기예요. 기본값은 `Large`예요.
* **isVisible** · 필수 · `boolean`

  (iOS only) 현재 화면이 보이는 상태인지 여부예요. 초기값은 `true`예요.
* **initialFontScale** · 필수 · `string`

  (Android only) Android 접근성 설정을 반영한 시스템 폰트 스케일이에요.

**예제: 초기 데이터 활용하기**

```tsx
import { AppsInToss } from '@apps-in-toss/framework';
import { PropsWithChildren } from 'react';
import { InitialProps } from '@granite-js/react-native';
import { context } from '../require.context';

function AppContainer({ children, ...initialProps }: PropsWithChildren<InitialProps>) {
  // 화면 진입 시 네이티브가 내려준 초기값을 활용할 수 있어요
  console.log({ initialProps });
  return <>{children}</>;
}

export default AppsInToss.registerApp(AppContainer, { context });
```

***

### API 사용하기

{% hint style="info" %}
**서버 API 연동이 필요한 경우에만 확인해 주세요**

토스 로그인, 토스페이, 스마트 발송 등 서버 간 통신이 필요한 기능을 사용할 때 설정해요. SDK만 사용한다면 이 섹션은 건너뛰어도 돼요.
{% endhint %}

mTLS 인증서 설정, 방화벽 구성, API 공통 규격은 [API 사용하기 문서](/documentation/integration/server-api)에서 확인해 주세요.


# 서버 API 이용하기

앱인토스 API를 이용하기 전에 필요한 서버 통신 설정을 안내해요.

### 서버 mTLS 인증서 발급받기

앱인토스 API를 사용하려면 반드시 **mTLS(mutable Transport Layer Security, 양방향 전송 계층 보안)** 인증서를 설정해야 해요.

mTLS는 클라이언트와 서버가 서로의 신원을 확인하는 방식이에요. 일반 HTTPS는 서버만 인증하지만, mTLS는 **파트너사 서버와 앱인토스 서버가 서로를 인증**해요.

이 인증서를 설정해야 다음을 보장할 수 있어요.

* 통신 구간 암호화
* 허용된 서버만 API 호출 가능
* 위·변조 방지

발급받은 인증서는 다음과 같이 관리하세요.

* 인증서와 키 파일은 유출되지 않도록 안전하게 보관하세요.
* 인증서가 만료되기 전에 재발급하세요.
* 무중단 교체가 필요하면 인증서를 두 개 이상 등록해 둘 수 있어요.

아직 인증서를 준비하지 않았다면 서버 mTLS 인증서 발급받기 문서의 mTLS 인증서 발급 방법을 먼저 확인하세요.

### 통신 방화벽 확인하기

서버에서 Inbound, Outbound 방화벽을 관리하고 있다면 아래 IP와 포트를 반드시 허용해야 해요. 허용하지 않으면 API 호출이 실패하거나 콜백을 받지 못해요.

#### 가맹점이 허용해야 하는 Inbound IP

앱인토스 → 가맹점

| IP                | Port |
| ----------------- | ---- |
| 117.52.3.11       | 443  |
| 211.115.96.11     | 443  |
| 106.249.5.11      | 443  |
| 117.52.3.80\~87   | 443  |
| 211.115.96.80\~87 | 443  |
| 106.249.5.80\~87  | 443  |

앱인토스가 가맹점 서버로 요청을 보낼 때 사용하는 IP예요. 예를 들어 콘솔에 등록한 콜백 URL로 구독 상태 변경 콜백이나 데이터 제공 동의 회수 콜백을 받을 때 필요해요.

#### 가맹점이 허용해야 하는 Outbound IP

가맹점 → 앱인토스

| 기능                        | 도메인                          | IP                                          | Port |
| ------------------------- | ---------------------------- | ------------------------------------------- | ---- |
| 간편 로그인, 메시지 발송, 토스 포인트 지급 | apps-in-toss-api.toss.im     | 117.52.3.192, 211.115.96.192, 106.249.5.192 | 443  |
| 간편 결제                     | pay-apps-in-toss-api.toss.im | 117.52.3.195, 211.115.96.195, 106.249.5.195 | 443  |

가맹점 서버에서 앱인토스 API를 호출할 때 필요한 설정이에요. HTTPS 443 포트를 열어야 정상적으로 통신할 수 있어요.

### CORS 허용하기

SDK 버전에 따라 달라요.

#### SDK 3.x

* `https://<appName>.web.tossmini.com` : 실제 서비스 환경
* `https://<appName>.private-web.tossmini.com` : 콘솔 QR 테스트 환경

#### SDK 1.x \~ 2.x

* `https://<appName>.apps.tossmini.com` : 실제 서비스 환경
* `https://<appName>.private-apps.tossmini.com` : 콘솔 QR 테스트 환경

### API 공통 규격

#### 도메인 정보

* <https://apps-in-toss-api.toss.im>
* <https://pay-apps-in-toss-api.toss.im>

#### API 공통 응답 형식

모든 API는 공통된 응답 구조를 사용해요. `resultType` 값으로 성공 여부를 먼저 확인하세요.

**성공 응답**

```json
{
  "resultType": "SUCCESS",
  "success": {
    "sample": "data"
  }
}
```

* `resultType`이 `"SUCCESS"`이면 요청이 정상 처리된 상태예요.
* 실제 응답 데이터는 `success` 객체 안에 들어 있어요.
* 각 API마다 `success` 내부 구조는 달라요.

**실패 응답**

```json
{
  "resultType": "FAIL",
  "error": {
    "errorCode": "INVALID_PARAMETER",
    "reason": "요청에 실패했습니다."
  }
}
```

* `resultType`이 `"FAIL"`이면 요청 처리에 실패한 상태예요.
* `errorCode`는 오류 유형을 나타내는 코드예요.
* `reason`에는 사람이 읽을 수 있는 오류 설명이 들어 있어요.
* 잘못된 파라미터를 보내면 `INVALID_PARAMETER`와 같은 코드로 에러를 발생시켜요.

응답을 처리할 때는 반드시 `resultType`을 먼저 검사한 뒤, 성공과 실패 로직을 나눠 구현하세요.

### 요청 제한 정책

앱인토스 API는 안정적인 서비스 운영을 위해 요청 수를 제한해요.

#### 기본 제한

* 분당 3,000 QPM
* QPM은 Queries Per Minute의 약자로, 1분 동안 호출할 수 있는 API 요청 수를 의미해요.
* 미니앱 기준으로 분당 최대 3,000건까지 요청할 수 있어요.

이 한도를 초과하면 일정 시간 동안 추가 요청이 차단될 수 있어요.

#### QPM 상향이 필요한 경우

기본 3,000 QPM보다 많은 요청이 필요한 경우 [채널톡](https://apps-in-toss.channel.io/workflows/787658)으로 상향을 요청할 수 있어요.

요청할 때는 다음 정보를 함께 전달하세요.

* 사용 목적
* 예상 트래픽 규모
* 피크 시간대 요청량

비즈니스 목적과 트래픽 규모를 검토한 뒤 한도를 조정해요. 대량 트래픽이 예상된다면 서비스 오픈 전에 미리 협의하는 것이 좋아요.


# WebView의 속성 제어하기

이 문서에서는 WebView의 동작 방식을 제어하기 위해 `apps-in-toss.config.ts`에서 설정할 수 있는 `webView` 옵션을 설명해요.&#x20;

스크롤 동작, 미디어 재생 방식, 제스처 사용 여부처럼 **사용자 경험에 직접적인 영향을 주는 WebView 속성**을 서비스 성격에 맞게 조정할 수 있어요.

{% hint style="info" %}
**지원 환경**

* 실행 환경: WebView, 토스 앱
* SDK 버전: WebView 3.x 이상
  {% endhint %}

## WebView 속성 설정 방법

WebView 속성은 `apps-in-toss.config.ts` 파일의 `webView` 항목에서 설정해요.

```ts
import { defineConfig } from '@apps-in-toss/web-framework/config';

export default defineConfig({
  webView: {
    // WebView 동작 관련 옵션
  },
});
```

{% hint style="warning" %}
SDK 2.x의 `granite.config.ts`와 `webViewProps`는 SDK 3.x에서 각각 `apps-in-toss.config.ts`, `webView`로 이름이 바뀌었어요. 하위 옵션은 동일해요.
{% endhint %}

## 사용 가능한 WebView 속성

`webView`에는 아래 속성을 설정할 수 있어요.

```ts
webView?: {
  allowsInlineMediaPlayback?: boolean;
  bounces?: boolean;
  pullToRefreshEnabled?: boolean;
  overScrollMode?: 'always' | 'content' | 'never';
  mediaPlaybackRequiresUserAction?: boolean;
  allowsBackForwardNavigationGestures?: boolean;
};
```

### 인라인 미디어 재생 허용 (`allowsInlineMediaPlayback`)

HTML5 비디오를 전체 화면이 아닌 WebView 내부에서 인라인으로 재생할지 설정해요.

iOS 전용 속성이며, `true`로 설정한 뒤 `<video>` 태그에 `webkit-playsinline` 속성이 있어야 인라인 재생이 가능해요.

| 항목  | 내용        |
| --- | --------- |
| 타입  | `boolean` |
| 기본값 | `false`   |
| 플랫폼 | iOS       |

### 스크롤 바운스 효과 사용 (`bounces`)

스크롤 영역의 끝에 도달했을 때 튕기는 바운스 효과를 사용할지 설정해요.

iOS 전용 속성이며 기본값은 `true`예요.

| 항목  | 내용        |
| --- | --------- |
| 타입  | `boolean` |
| 기본값 | `true`    |
| 플랫폼 | iOS       |

### 당겨서 새로고침 활성화 (`pullToRefreshEnabled`)

아래로 당겨서 새로고침하는 Pull-to-Refresh 동작을 활성화할지 설정해요.

iOS 전용 옵션이며 기본값은 `true`예요. 이 값을 `true`로 설정하면 `bounces` 옵션도 자동으로 `true`로 설정돼요.

| 항목  | 내용        |
| --- | --------- |
| 타입  | `boolean` |
| 기본값 | `true`    |
| 플랫폼 | iOS       |

### 오버스크롤 동작 방식 설정 (`overScrollMode`)

스크롤 콘텐츠의 끝에 도달했을 때 Android에서 오버스크롤 효과를 어떻게 처리할지 설정해요.

| 항목  | 내용                                 |
| --- | ---------------------------------- |
| 타입  | `'always' \| 'content' \| 'never'` |
| 기본값 | `'always'`                         |
| 플랫폼 | Android                            |

각 값의 의미는 아래와 같아요.

| 값           | 설명                                  |
| ----------- | ----------------------------------- |
| `'always'`  | 콘텐츠 크기와 관계없이 오버스크롤 효과를 허용해요.        |
| `'content'` | 콘텐츠가 WebView보다 클 때만 오버스크롤 효과를 허용해요. |
| `'never'`   | 오버스크롤 효과를 사용하지 않아요.                 |

참고: [Android 공식 문서](https://developer.android.com/reference/android/view/View#OVER_SCROLL_NEVER)

### 미디어 자동 재생 제한 (`mediaPlaybackRequiresUserAction`)

오디오 또는 비디오가 자동으로 재생되지 않도록 제한할지 설정해요.

이 값을 `true`로 설정하면 사용자가 직접 탭해야 미디어가 재생돼요. Android에서는 버전 17 이상에서만 이 옵션이 적용돼요.

| 항목  | 내용           |
| --- | ------------ |
| 타입  | `boolean`    |
| 기본값 | `true`       |
| 플랫폼 | iOS, Android |

참고: [react-native-webview mediaPlaybackRequiresUserAction](https://github.com/react-native-webview/react-native-webview/blob/v13.6.2/docs/Reference.md#mediaplaybackrequiresuseraction)

### 스와이프 뒤로가기/앞으로가기 허용 (`allowsBackForwardNavigationGestures`)

좌우 스와이프 제스처로 뒤로 가기 또는 앞으로 가기 탐색을 허용할지 설정해요.

이 값을 `false`로 설정하면 사용자가 스와이프 제스처로 페이지 이동을 할 수 없어요.

| 항목  | 내용        |
| --- | --------- |
| 타입  | `boolean` |
| 기본값 | `true`    |
| 플랫폼 | iOS       |

참고: [react-native-webview allowsBackForwardNavigationGestures](https://github.com/react-native-webview/react-native-webview/blob/v13.6.2/docs/Reference.md#allowsBackForwardNavigationGestures)

## 설정 예시

아래 예시는 WebView의 스크롤 동작과 미디어 재생 방식을 함께 설정한 예시예요.

```ts
import { defineConfig } from '@apps-in-toss/web-framework/config';

export default defineConfig({
  webView: {
    bounces: true,
    pullToRefreshEnabled: true,
    allowsInlineMediaPlayback: false,
    overScrollMode: 'never',
  },
});
```

## 참고사항

* 일부 WebView 속성은 iOS 또는 Android 전용이에요. 플랫폼별 동작 차이를 꼭 확인해 주세요.
* 사용자 경험에 영향을 크게 주는 옵션은 서비스 성격에 맞춰 신중히 설정하는 것을 권장해요.
* WebView 속성은 런타임이 아닌 설정 단계에서 적용돼요.


# SDK 3.x 마이그레이션

SDK 3.x는 WebView 프로젝트의 구조를 개선한 업데이트예요.\
설정 파일 이름과 일부 프로퍼티가 변경되며, 클라이언트 SDK가 고도화됐어요.\
파라미터와 반환값은 SDK 2.x와 동일하고, SDK 내부 처리 로직을 클라이언트가 아닌 서버에서 처리하도록 변경했어요.

SDK 이슈가 발생해도 파트너사의 재배포 없이 앱인토스 서버에서만 수정하고 반영할 수 있어요.\
SDK 3.x는 미니앱 생태계의 안정성을 높이고, SDK를 안정적인 버전으로 유지하기 위해서예요.

***

### 변경 사항 요약

| 항목             | 변경 전                                     | 변경 후                          |
| -------------- | ---------------------------------------- | ----------------------------- |
| 설정 파일 이름       | `granite.config.ts`                      | `apps-in-toss.config.ts`      |
| `brand` 설정     | `displayName`, `primaryColor`, `icon` 포함 | `primaryColor`만 유지            |
| `webViewProps` | `type` 프로퍼티 포함                           | `webView`로 이름 변경, `type` 삭제   |
| `outdir`       | `outdir`                                 | `webBundleDir`                |
| `web` 설정       | 설정 파일 내 `web.commands` 포함                | 삭제 후 `package.json`으로 이동      |
| 테스트 환경         | 샌드박스 앱 설치·로그인 필요                         | 로컬 브라우저(AIT Devtools)로 바로 테스트 |

***

### 패키지 업데이트

먼저 `@apps-in-toss/web-framework`를 3.x 버전으로 업데이트해요.

{% tabs %}
{% tab title="npm" %}

```sh
npm install @apps-in-toss/web-framework
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn add @apps-in-toss/web-framework
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm add @apps-in-toss/web-framework
```

{% endtab %}
{% endtabs %}

TDS(Toss Design System)를 사용하는 경우, 아래 2가지를 2.4.1 버전으로 업데이트해 주세요.

* `@toss/tds-mobile`
* `@toss/tds-mobile-ait`

{% tabs %}
{% tab title="npm" %}

```sh
npm install @toss/tds-mobile@2.4.1 @toss/tds-mobile-ait@2.4.1
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn add @toss/tds-mobile@2.4.1 @toss/tds-mobile-ait@2.4.1
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm add @toss/tds-mobile@2.4.1 @toss/tds-mobile-ait@2.4.1
```

{% endtab %}
{% endtabs %}

***

### 자동 마이그레이션

아래 명령어를 실행하면 설정 파일 변환과 `package.json` 스크립트 업데이트가 자동으로 처리돼요.

{% tabs %}
{% tab title="npx" %}

```sh
npx ait migrate v3
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn ait migrate v3
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm ait migrate v3
```

{% endtab %}
{% endtabs %}

실행 후 `apps-in-toss.config.ts` 파일이 생성되고, `package.json`의 `dev`, `build` 스크립트가 업데이트돼요.\
마이그레이션이 완료되면 로컬 브라우저에서 미니앱이 정상 동작하는지 확인해 주세요.

***

### 수동 마이그레이션

`apps-in-toss.config.ts`가 생성되지 않거나 값이 올바르지 않으면 수동 마이그레이션 가이드를 확인해 주세요.

#### 1. 설정 파일 이름 변경

`granite.config.ts` 파일 이름을 `apps-in-toss.config.ts`로 변경하세요.

#### 2. `brand` 설정 정리

`brand` 설정에서 `primaryColor`를 제외한 나머지 프로퍼티를 삭제하세요.

```ts
// 변경 전
brand: {
  displayName: '앱 이름',
  primaryColor: '#3182F6',
  icon: 'https://...',
},

// 변경 후
brand: {
  primaryColor: '#3182F6',
},
```

#### 3. `webViewProps` → `webView`로 변경

`webViewProps`의 이름을 `webView`로 바꾸고, `type` 프로퍼티를 삭제하세요.

```ts
// 변경 전
webViewProps: {
  type: 'partner',
},

// 변경 후
webView: {},
```

#### 4. `outdir` → `webBundleDir`로 변경

```ts
// 변경 전
outdir: 'dist',

// 변경 후
webBundleDir: 'dist',
```

#### 5. `web` 설정 삭제 후 `package.json`으로 이동

`web` 설정 블록을 삭제하고, `web.commands`에 있던 커맨드를 `package.json`으로 옮기세요.

* `web.commands.dev` → `package.json`의 `dev` 스크립트에 그대로 옮겨요.
* `web.commands.build` → `package.json`의 `build` 스크립트에 옮기되, `ait build`를 함께 실행하도록 추가해요.

```json
// package.json 변경 예시
{
  "scripts": {
    "dev": "vite --port 3000",
    "build": "vite build && ait build"
  }
}
```

***

### 변경 전후 예시

#### 변경 전(granite.config.ts)

```ts
import { defineConfig } from '@apps-in-toss/web-framework/config';

export default defineConfig({
  appName: 'my-app',
  brand: {
    displayName: '내 앱',
    primaryColor: '#3182F6',
    icon: 'https://...',
  },
  web: {
    host: 'localhost',
    port: 3000,
    commands: {
      dev: 'vite --port 3000',
      build: 'vite build',
    },
  },
  webViewProps: {
    type: 'partner',
  },
  permissions: [],
  outdir: 'dist',
});
```

#### 변경 후(apps-in-toss.config.ts)

```ts
import { defineConfig } from '@apps-in-toss/web-framework/config';

export default defineConfig({
  appName: 'my-app',
  brand: {
    primaryColor: '#3182F6',
  },
  webView: {},
  permissions: [],
  webBundleDir: 'dist',
});
```

***

### 유의사항

#### 1. SDK 3.x 출시 후 롤백이 안돼요

SDK 3.x 이상이 적용된 앱 번들을 출시하면 SDK 2.x 버전으로 롤백할 수 없어요.\
QR코드로 충분히 테스트한 후 출시해 주세요.

#### 2. CORS가 변경돼요

SDK 3.x 버전부터는 CORS(Cross-Origin Resource Sharing)가 아래와 같이 변경돼요.\
Origin 허용 목록에 다음 도메인을 등록하지 않으면 API 요청이 차단될 수 있어요. Origin 허용 목록에 다음 도메인을 등록하세요.

* `https://<appName>.web.tossmini.com` : 실제 서비스 환경
* `https://<appName>.private-web.tossmini.com` : 콘솔 QR 테스트 환경

#### 3. 새로운 테스트 환경이 제공돼요

기존에는 샌드박스 앱을 설치하고 로그인해야 했고, 샌드박스 앱이 수시로 업데이트될 때마다 다시 업데이트해야 하는 번거로움이 있었어요.\
SDK 3.x부터는 이 과정 없이 로컬 브라우저만 띄우면 바로 테스트할 수 있어요.\
설정 방법은 아래 테스트 환경을 참고하세요.

***

### 테스트 환경

새로운 프로젝트를 스캐폴딩했거나, 2.x 버전에서 3.x로 마이그레이션한 경우에는 AIT Devtools가 자동으로 설정돼요.

아래 명령어를 통해 로컬 브라우저로 바로 확인할 수 있어요.\
localhost 링크를 로컬 브라우저로 열어서 미니앱 동작 여부를 확인해 주세요.

{% tabs %}
{% tab title="npm" %}

```shellscript
npm run dev
```

{% endtab %}

{% tab title="pnpm" %}

```shellscript
pnpm run dev
```

{% endtab %}

{% tab title="yarn" %}

```shellscript
yarn dev
```

{% endtab %}
{% endtabs %}

***

### 테스트 환경 수동 세팅

SDK 3.0.1 버전에서 마이그레이션했다면 AIT Devtools를 수동으로 설정해야 해요. \
아래 단계를 따라 설정해 주세요.

#### 1. 패키지 설치

{% tabs %}
{% tab title="npm" %}

```sh
npm install -D @apps-in-toss/devtools
```

{% endtab %}

{% tab title="pnpm" %}

```sh
pnpm add -D @apps-in-toss/devtools
```

{% endtab %}

{% tab title="yarn" %}

```sh
yarn add -D @apps-in-toss/devtools
```

{% endtab %}
{% endtabs %}

#### 2. 번들러 설정

Vite를 사용하는 경우 `vite.config.ts`에 Devtools 플러그인을 추가해요.

다른 번들러를 사용하는 경우에는 `@apps-in-toss/devtools/unplugin`에서 제공하는 해당 번들러용 어댑터를 사용해 설정 파일에 추가해야 해요. 예를 들어 `aitDevtools.vite()`, `aitDevtools.webpack()`처럼 사용할 수 있어요.

아래 코드는 Vite 번들러를 사용할 때의 설정 예시예요.

```ts
import aitDevtools from "@apps-in-toss/devtools/unplugin";

export default defineConfig({
  plugins: [
    aitDevtools.vite(),
    react(),
    babel({ presets: [reactCompilerPreset()] }),
  ],
});
```

#### 3. 테스트하기

서비스를 실행한 뒤 로컬 브라우저로 접근해요. 우측 하단에 AIT Devtools가 보이면 정상적으로 설정된 상태이며, 이 화면에서 바로 미니앱 동작을 테스트할 수 있어요.

{% tabs %}
{% tab title="npm" %}

```shellscript
npm run dev
```

{% endtab %}

{% tab title="pnpm" %}

```shellscript
pnpm run dev
```

{% endtab %}

{% tab title="yarn" %}

```shellscript
yarn dev
```

{% endtab %}
{% endtabs %}

***

### 마이그레이션 체크리스트

* [ ] `granite.config.ts`를 `apps-in-toss.config.ts`로 이름 변경했어요
* [ ] `brand`에서 `primaryColor`만 남기고 나머지를 삭제했어요
* [ ] `webViewProps`를 `webView`로 변경하고 `type`을 삭제했어요
* [ ] `outdir`을 `webBundleDir`로 변경했어요
* [ ] `web` 설정을 삭제하고 커맨드를 `package.json`으로 옮겼어요
* [ ] `build` 스크립트에 `ait build`가 포함되어 있어요
* [ ] 빌드가 정상 동작해요
* [ ] 테스트 환경(AIT Devtools)에서정상 동작을 확인했어요
* [ ] 앱인토스 콘솔에 번들을 업로드했어요
* [ ] 토스앱에서 테스트를 완료했어요

***

### 문의

마이그레이션 관련 문의는 채널톡 또는 커뮤니티를 통해 문의해 주세요.


# API


# 인증

mTLS 인증서와 사용자 인증 헤더 안내

미니앱 파트너 서버 API의 인증 방식을 안내해요.

모든 API는 mTLS(상호 TLS) 클라이언트 인증서로 호출 주체(미니앱)를 식별해요.

mTLS 인증은 TLS 연결 수립 단계에서 이루어지므로 요청 헤더에는 나타나지 않아요. 각 API 문서의 코드 예시(cURL·Python·Node.js)처럼 발급받은 인증서와 개인 키 파일을 요청에 함께 설정해야 해요.

방화벽 IP 허용 목록, mTLS 인증서 발급·관리, 요청 한도 같은 운영 정보는 [서버 API 이용하기](/documentation/integration/server-api) 문서를 참고하세요.

사용자 단위 API는 엔드포인트에 따라 아래 값 중 하나를 함께 보내야 해요.

* `x-toss-user-key` 헤더 — 토스 로그인으로 발급받은 사용자 키 ([사용자 정보 받기](/documentation/common/authentication/toss-login#id-4) API로 획득해요)
* `x-anon-key` 헤더 — 비로그인 사용자 식별 키 (미니앱 SDK [User.getAnonymousKey](/documentation/common/authentication/hash-key) 함수로 발급받아요)
* `Authorization: Bearer {accessToken}` 헤더 — 토스 로그인 Access Token ([AccessToken 받기](https://developers-apps-in-toss.toss.im/documentation/api/pages/FCUMTd5EgKJiIdAljx5H#id-2.-accesstoken) API로 발급받아요, 일부 로그인 API)


# 응답 형식

공통 응답 봉투와 오류 처리 안내

이 문서는 파트너 서버 API의 공통 응답 형식을 설명해요.

모든 응답은 공통 봉투(envelope)로 감싸져요.

성공:

```json
{"resultType": "SUCCESS", "success": { ... }}
```

실패:

```json
{"resultType": "FAIL", "success": null, "error": {"errorType": 0, "errorCode": "4010", "reason": "...", "data": {}, "title": null}}
```

**비즈니스 오류는 HTTP 200으로 응답해요.** `resultType`이 `SUCCESS`인지 확인하고, `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 요청 형식 오류(필드 검증 실패)는 HTTP 400, 분류되지 않은 서버 오류는 HTTP 500으로 응답해요. 요청 한도를 초과하면 HTTP 200에 `errorCode: "4095"`와 `error.data.retryAfterSeconds`가 내려가요.


# 사용자 식별 키

## 익명 사용자 식별 키 검증하기

> x-anon-key 헤더로 전달한 익명 사용자 식별 키가 유효한지 검증해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"파트너 앱에서 사용자 식별 키를 검증할 때 사용하는 API예요.","name":"user-key"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"TossApiSuccessBoolean":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"description":"성공 시 실제 응답 데이터예요.","type":"boolean"}},"required":["resultType","success"],"type":"object"},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/users/anon-key/verify":{"post":{"description":"x-anon-key 헤더로 전달한 익명 사용자 식별 키가 유효한지 검증해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"verifyAnonKey","parameters":[{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessBoolean"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessBoolean"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"익명 사용자 식별 키 검증 결과가 true 또는 false로 돌아와요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 형식이 올바르지 않아요. `error.data`는 빈 객체이고 `error.reason`은 `Unknown Error`로 내려가요. `x-anon-key` 헤더 누락은 이 응답이 아니라 HTTP 200의 비즈니스 오류(`4010`)로 내려가요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"익명 사용자 식별 키 검증하기","tags":["user-key"]}}}}
```


# 토스 로그인

## AccessToken 받기

> Authorization Code로 Access Token과 Refresh Token을 발급하는 API예요. 발급된 Access Token의 유효 시간은 1시간이에요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4050\` | 인증서버에 등록된 미니앱이 아닙니다. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"OAuth2 기반 앱 연동과 사용자 인증을 처리하는 API예요.","name":"toss-login"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"GenerateTokenRequest":{"description":"OAuth2 토큰 발급을 위한 요청 본문이에요.","properties":{"authorizationCode":{"description":"OAuth2 인증 과정을 통해 발급받은 Authorization Code예요. 이 코드는 사용자 인증을 완료한 뒤 리디렉션 URL에 쿼리 파라미터로 전달돼요.","type":"string"},"referrer":{"description":"사용자가 앱에 진입하게 된 유입 경로예요. 예를 들어 딥링크, 푸시 알림, 앱 내 배너 등 어떤 경로로 이 기능을 사용했는지를 의미해요.","type":"string"}},"required":["authorizationCode","referrer"],"type":"object"},"TossApiSuccessGenerateTokenResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/GenerateTokenResponse"}},"required":["resultType","success"],"type":"object"},"GenerateTokenResponse":{"description":"OAuth2 토큰 발급 요청에 대한 응답 정보예요.","properties":{"accessToken":{"description":"API 호출에 성공했을 때 발급되는 액세스 토큰이에요. 이 토큰은 사용자 인증이 필요한 API 요청 시 Authorization 헤더에 넣어서 사용해요.","type":"string"},"expiresIn":{"description":"액세스 토큰의 유효 시간(초)이에요. 이 값이 지나면 리프레시 토큰으로 새 토큰을 발급받아야 해요.","format":"int64","type":"integer"},"refreshToken":{"description":"액세스 토큰이 만료된 뒤 재발급에 사용하는 리프레시 토큰이에요. 이 토큰으로 새 액세스 토큰을 요청할 수 있어요.","type":"string"},"scope":{"description":"토큰으로 수행할 수 있는 작업의 범위를 나타내요. 예를 들어, 사용자 프로필을 조회하거나 메시지를 전송하는 권한을 포함할 수 있어요.","type":"string"},"tokenType":{"description":"토큰의 타입을 나타내요. 일반적으로 'Bearer'로 고정돼 있어요.","type":"string"}},"required":["accessToken","expiresIn","refreshToken","scope","tokenType"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/user/oauth2/generate-token":{"post":{"description":"Authorization Code로 Access Token과 Refresh Token을 발급하는 API예요. 발급된 Access Token의 유효 시간은 1시간이에요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4050` | 인증서버에 등록된 미니앱이 아닙니다. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"generateOauth2Token","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateTokenRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessGenerateTokenResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessGenerateTokenResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청에 성공해서 발급된 토큰 정보가 돌아와요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"AccessToken 받기","tags":["toss-login"]}}}}
```

## 사용자 정보 받기

> Access Token을 사용해서 로그인된 사용자의 정보를 조회하는 API예요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"OAuth2 기반 앱 연동과 사용자 인증을 처리하는 API예요.","name":"toss-login"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"TossApiSuccessLoginMeResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/LoginMeResponse"}},"required":["resultType","success"],"type":"object"},"LoginMeResponse":{"description":"OAuth2 로그인 사용자 정보 응답","properties":{"agreedTerms":{"description":"사용자가 동의한 약관 ID 목록이에요.","items":{"type":"string"},"type":"array"},"birthday":{"description":"생년월일이에요. YYYYMMDD 형식의 8자리 문자열이에요.","type":"string"},"callingCode":{"description":"국가 전화 코드예요. 예를 들어, 한국은 '82'예요.","type":"string"},"ci":{"description":"CI(연계정보) 값이에요. 본인 인증 시 발급돼요.","type":"string"},"di":{"description":"DI(중복가입확인정보) 값이에요. 동일한 사용자인지 확인할 때 사용해요.","type":"string"},"email":{"description":"이메일 주소예요. 제공에 동의하지 않았으면 null이에요.","type":"string"},"gender":{"description":"성별 정보예요. 'M'(남성), 'F'(여성) 중 하나예요.","type":"string"},"name":{"description":"사용자의 이름이에요. 이름 제공에 동의하지 않았으면 null이에요.","type":"string"},"nationality":{"description":"국적 코드예요. 예를 들어, 한국은 'KR'이에요.","type":"string"},"phone":{"description":"전화번호예요. 국가 코드 없이 숫자만 포함돼요.","type":"string"},"scope":{"description":"토큰에 포함된 권한 범위예요. 여러 개의 scope는 공백으로 구분돼요.","type":"string"},"userKey":{"description":"사용자의 고유 식별자예요.","format":"int64","type":"integer"}},"required":["agreedTerms","userKey"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/user/oauth2/login-me":{"get":{"description":"Access Token을 사용해서 로그인된 사용자의 정보를 조회하는 API예요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"loginMe","parameters":[{"description":"Bearer 형식의 Access Token이 담긴 Authorization 헤더 값이에요. Access Token은 [AccessToken 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#post-api-partner-v1-apps-in-toss-user-oauth2-generate-token) API로 발급받아요.","in":"header","name":"Authorization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessLoginMeResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessLoginMeResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청에 성공해서 조회한 사용자 프로필 정보가 응답으로 돌아와요. 일부 항목은 사용자 동의 여부에 따라 `null`일 수 있어요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"`Authorization` 헤더가 없어요. `error.data`는 빈 객체이고 `error.reason`은 `Unknown Error`로 내려가요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"사용자 정보 받기","tags":["toss-login"]}}}}
```

## AccessToken 재발급 받기

> Refresh Token으로 Access Token을 다시 발급받는 API예요. Refresh Token의 유효 시간은 14일이에요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4050\` | 인증서버에 등록된 미니앱이 아닙니다. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"OAuth2 기반 앱 연동과 사용자 인증을 처리하는 API예요.","name":"toss-login"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"RefreshTokenRequest":{"description":"기존에 발급된 refresh token을 사용해 access token을 재발급받는 요청이에요.","properties":{"refreshToken":{"description":"기존에 발급된 refresh token이에요. 이 토큰으로 새 access token을 요청할 수 있어요.","type":"string"}},"required":["refreshToken"],"type":"object"},"TossApiSuccessRefreshTokenResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/RefreshTokenResponse"}},"required":["resultType","success"],"type":"object"},"RefreshTokenResponse":{"description":"OAuth2 토큰 갱신 응답","properties":{"accessToken":{"description":"새로 발급된 access token이에요.","type":"string"},"expiresIn":{"description":"access token의 만료 시간(초 단위)이에요.","format":"int64","type":"integer"},"refreshToken":{"description":"새롭게 발급된 refresh token이에요.","type":"string"},"scope":{"description":"access token의 권한 범위(scope)예요. 예: 사용자 프로필 조회, 메시지 전송 등","type":"string"},"tokenType":{"description":"토큰 타입이에요. 일반적으로 'Bearer'로 고정돼 있어요.","type":"string"}},"required":["accessToken","expiresIn","refreshToken","scope","tokenType"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/user/oauth2/refresh-token":{"post":{"description":"Refresh Token으로 Access Token을 다시 발급받는 API예요. Refresh Token의 유효 시간은 14일이에요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4050` | 인증서버에 등록된 미니앱이 아닙니다. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"refreshOauth2Token","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessRefreshTokenResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessRefreshTokenResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청에 성공해서 재발급된 Access Token 관련 정보가 돌아와요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"AccessToken 재발급 받기","tags":["toss-login"]}}}}
```

## AccessToken으로 로그인 연결 끊기

> Authorization 헤더에 있는 Access Token으로 해당 사용자의 연결을 해제하는 API예요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4050\` | 인증서버에 등록된 미니앱이 아닙니다. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"OAuth2 기반 앱 연동과 사용자 인증을 처리하는 API예요.","name":"toss-login"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"TossApiSuccessDisconnectUserByAccessTokenResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/DisconnectUserByAccessTokenResponse"}},"required":["resultType","success"],"type":"object"},"DisconnectUserByAccessTokenResponse":{"description":"사용자의 OAuth2 Access Token으로 해당 사용자의 연결을 해제해요. 주로 사용자가 직접 연결을 끊으려고 할 때 이 API를 사용해요.","properties":{"userKey":{"description":"연결이 해제된 사용자의 고유한 userKey 값이에요. 이 값은 내부 시스템에서 사용자를 식별하는 데 사용돼요.","format":"int64","type":"integer"}},"required":["userKey"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/user/oauth2/access/remove-by-access-token":{"post":{"description":"Authorization 헤더에 있는 Access Token으로 해당 사용자의 연결을 해제하는 API예요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4050` | 인증서버에 등록된 미니앱이 아닙니다. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"removeByAccessToken","parameters":[{"description":"사용자의 Access Token이 담긴 Authorization 헤더 값이에요. Access Token은 [AccessToken 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#post-api-partner-v1-apps-in-toss-user-oauth2-generate-token) API로 발급받아요.","in":"header","name":"Authorization","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessDisconnectUserByAccessTokenResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessDisconnectUserByAccessTokenResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청에 성공해서 Access Token에 해당하는 사용자의 연결을 성공적으로 해제했어요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"`Authorization` 헤더가 없어요. `error.data`는 빈 객체이고 `error.reason`은 `Unknown Error`로 내려가요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"AccessToken으로 로그인 연결 끊기","tags":["toss-login"]}}}}
```

## userKey로 로그인 연결 끊기

> userKey로 해당 사용자의 로그인 연결을 해제하는 API예요. 응답에는 연결이 해제된 사용자의 \`userKey\`만 포함돼요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4050\` | 인증서버에 등록된 미니앱이 아닙니다. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"OAuth2 기반 앱 연동과 사용자 인증을 처리하는 API예요.","name":"toss-login"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"DisconnectUserByUserKeyRequest":{"description":"UserKey로 OAuth2 사용자 연결 해제 요청","properties":{"userKey":{"description":"연결을 해제할 사용자의 고유 식별자예요. 내부 시스템에서 사용자를 식별할 때 사용해요.","format":"int64","type":"integer"}},"required":["userKey"],"type":"object"},"TossApiSuccessDisconnectUserByUserKeyResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/DisconnectUserByUserKeyResponse"}},"required":["resultType","success"],"type":"object"},"DisconnectUserByUserKeyResponse":{"description":"UserKey로 OAuth2 사용자 연결 해제 응답","properties":{"userKey":{"description":"연결이 해제된 사용자의 고유 식별자예요. 요청에 사용한 값과 동일해요.","format":"int64","type":"integer"}},"required":["userKey"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/user/oauth2/access/remove-by-user-key":{"post":{"description":"userKey로 해당 사용자의 로그인 연결을 해제하는 API예요. 응답에는 연결이 해제된 사용자의 `userKey`만 포함돼요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4050` | 인증서버에 등록된 미니앱이 아닙니다. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"removeByUserKey","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisconnectUserByUserKeyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessDisconnectUserByUserKeyResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessDisconnectUserByUserKeyResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청에 성공해서 `userKey`를 기반으로 연결이 해제된 사용자의 `userKey`가 돌아와요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"userKey로 로그인 연결 끊기","tags":["toss-login"]}}}}
```


# 데이터 SDK

## 동의 기반 사용자 데이터 조회하기

> 미니앱에 등록된 동의 기반 사용자 데이터 항목의 사용자 동의 상태를 확인해요. 사용자가 최신 동의 내용에 동의했다면 실제 데이터 값을 반환하고, 아직 동의하지 않았거나 동의를 거부한 경우에는 데이터 대신 동의 화면으로 이동할 수 있는 URL을 반환해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"파트너가 사용자의 동의를 받은 개인정보를 조회할 때 사용하는 API예요.","name":"sdk"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"DataSdkConsentedUserDataRequest":{"description":"동의 기반 사용자 데이터 조회 요청 본문이에요.","properties":{"consentedUserDataKey":{"description":"조회할 동의 기반 사용자 데이터 항목을 식별하는 키예요. 파트너 워크스페이스 콘솔에서 동의 기반 사용자 데이터 항목을 등록하면 발급돼요.","type":"string"}},"required":["consentedUserDataKey"]},"TossApiSuccessDataSdkConsentedUserDataResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/DataSdkConsentedUserDataResponse"}},"required":["resultType","success"],"type":"object"},"DataSdkConsentedUserDataResponse":{"description":"동의 기반 사용자 데이터 조회 결과예요. `type` 값에 따라 실제로 내려오는 필드가 달라져요.","discriminator":{"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/Provided"},{"$ref":"#/components/schemas/UserNotConsented"},{"$ref":"#/components/schemas/UserDeclined"},{"$ref":"#/components/schemas/TermsNotSet"},{"$ref":"#/components/schemas/Unavailable"}],"properties":{"type":{"description":"응답 결과의 종류를 나타내는 판별자예요.","enum":["PROVIDED","USER_NOT_CONSENTED","USER_DECLINED","TERMS_NOT_SET","UNAVAILABLE"],"type":"string"}},"required":["type"]},"Provided":{"allOf":[{"$ref":"#/components/schemas/DataSdkConsentedUserDataResponse"},{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"동의한 데이터 항목의 실제 값이에요. 키는 데이터 항목(DataScopeKey), 값은 화면에 표시할 문자열이에요.","type":"object"}},"type":"object"}],"description":"사용자가 최신 동의 내용에 동의해서, 요청한 항목의 실제 데이터 값을 제공하는 응답이에요.","required":["data","type"]},"UserNotConsented":{"allOf":[{"$ref":"#/components/schemas/DataSdkConsentedUserDataResponse"},{"properties":{"termsUrl":{"description":"사용자가 동의할 수 있는 동의 화면 URL이에요.","type":"string"}},"type":"object"}],"description":"사용자가 아직 동의 여부를 응답하지 않은 상태예요. `termsUrl`로 동의 화면을 안내해주세요.","required":["termsUrl","type"]},"UserDeclined":{"allOf":[{"$ref":"#/components/schemas/DataSdkConsentedUserDataResponse"},{"properties":{"termsUrl":{"description":"사용자가 다시 동의할 수 있는 동의 화면 URL이에요.","type":"string"}},"type":"object"}],"description":"사용자가 동의를 명시적으로 거부한 상태예요. `termsUrl`로 동의 화면을 다시 안내할 수 있어요.","required":["termsUrl","type"]},"TermsNotSet":{"allOf":[{"$ref":"#/components/schemas/DataSdkConsentedUserDataResponse"}],"description":"동의 화면에 노출할 약관 URL이 아직 설정되지 않아, 동의 여부를 안내할 수 없는 상태예요.","required":["type"]},"Unavailable":{"allOf":[{"$ref":"#/components/schemas/DataSdkConsentedUserDataResponse"}],"description":"일시적인 오류 등으로 사용자의 동의 상태를 확인할 수 없는 상태예요.","required":["type"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/data-sdk/get-consented-user-data":{"post":{"description":"미니앱에 등록된 동의 기반 사용자 데이터 항목의 사용자 동의 상태를 확인해요. 사용자가 최신 동의 내용에 동의했다면 실제 데이터 값을 반환하고, 아직 동의하지 않았거나 동의를 거부한 경우에는 데이터 대신 동의 화면으로 이동할 수 있는 URL을 반환해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4010` | 인증 정보를 찾을 수 없어요. |","operationId":"getConsentedUserData","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataSdkConsentedUserDataRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessDataSdkConsentedUserDataResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessDataSdkConsentedUserDataResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청 처리 결과예요. `resultType` 값으로 성공/실패를 구분하세요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"동의 기반 사용자 데이터 조회하기","tags":["sdk"]}}}}
```


# 인앱 결제

## 결제 상태 조회하기

> 생성된 결제건의 거래 상태와 거래 트랜잭션을 조회할 수 있어요. 상황에 따라, 승인 응답을 수신하지 못한 경우에도 활용할 수 있어요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"인앱결제와 관련된 요청을 처리하는 API예요.","name":"iap"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"GetOrderRequest":{"description":"인앱 결제 주문 상태 조회 요청 본문이에요.","properties":{"orderId":{"description":"조회할 인앱 결제 주문의 ID예요.","type":"string"}},"required":["orderId"]},"TossApiSuccessGetIapOrderPartnerStatusResult":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/GetIapOrderPartnerStatusResult"}},"required":["resultType","success"],"type":"object"},"GetIapOrderPartnerStatusResult":{"description":"파트너가 조회한 인앱 결제 주문의 상태 정보예요.","properties":{"orderId":{"description":"조회한 주문의 고유 식별자예요. 요청에 사용한 orderId와 같아요.","type":"string"},"reason":{"description":"status 값에 대한 사람이 읽을 수 있는 부연 설명이에요. status에 따라 문구가 달라져요.","type":"string"},"sku":{"description":"주문에 연결된 상품의 SKU예요. 주문 상태를 확정하지 못한 경우(status가 MINIAPP_MISMATCH, NOT_FOUND, ERROR일 때)에는 내려주지 않아요.","type":"string"},"status":{"description":"주문 상태예요. MINIAPP_MISMATCH: 요청한 미니앱의 주문이 아니에요, NOT_FOUND: 주문을 찾을 수 없어요, ORDER_IN_PROGRESS: 주문이 아직 진행 중이에요, PAYMENT_COMPLETED: 결제가 완료됐어요, PURCHASED: 구매가 완료됐어요, FAILED: 구매에 실패했어요, REFUNDED: 주문이 환불됐어요, ERROR: 카탈로그 조회 실패 등으로 정상 범위를 벗어난 상태예요.","enum":["MINIAPP_MISMATCH","NOT_FOUND","ORDER_IN_PROGRESS","PAYMENT_COMPLETED","PURCHASED","FAILED","REFUNDED","ERROR"],"type":"string"},"statusDeterminedAt":{"description":"주문 상태가 마지막으로 확정된 시각이에요. 주문 상태를 확정하지 못한 경우(status가 MINIAPP_MISMATCH, NOT_FOUND, ERROR일 때)에는 내려주지 않아요.","format":"date-time","type":"string"}},"required":["orderId","reason","status"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/order/get-order-status":{"post":{"description":"생성된 결제건의 거래 상태와 거래 트랜잭션을 조회할 수 있어요. 상황에 따라, 승인 응답을 수신하지 못한 경우에도 활용할 수 있어요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"getIapOrderStatus","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessGetIapOrderPartnerStatusResult"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessGetIapOrderPartnerStatusResult"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"인앱 결제 상태 조회에 성공했어요"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"결제 상태 조회하기","tags":["iap"]}}}}
```


# 토스 페이

## 결제 생성하기

> 결제를 생성해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"MakePaymentRequest":{"description":"토스페이 결제 생성을 위한 요청 본문이에요.","properties":{"amount":{"description":"총 결제 금액이에요.","format":"int64","maxLength":7,"type":"integer"},"amountServiceFee":{"description":"결제 금액 중 봉사료예요.","format":"int64","maxLength":7,"type":"integer"},"amountTaxFree":{"description":"결제 금액 중 비과세 금액이에요. 과세 상품이면 0으로 보내주세요.","format":"int64","maxLength":7,"type":"integer"},"amountTaxable":{"description":"결제 금액 중 과세 금액이에요. 별도의 과세액을 설정하지 않고 비과세 금액을 0원으로 보내면 토스페이 서버에서 자동으로 과세와 부가세를 계산해요.","format":"int64","maxLength":7,"type":"integer"},"amountVat":{"description":"결제 금액 중 부가세예요. 값이 없으면 환불할 과세 금액을 11로 나눈 후 소수점 첫째 자리에서 올림으로 계산해요.","format":"int64","maxLength":7,"type":"integer"},"cashReceipt":{"description":"현금영수증 발급 가능 여부예요. null일 경우 발급되지 않아요.","type":"boolean"},"cashReceiptTradeOption":{"description":"현금영수증 발급 타입이에요. CULTURE(문화비)/GENERAL(일반, 기본값)/PUBLIC_TP(교통비) 중 하나예요.","maxLength":10,"type":"string"},"enablePayMethods":{"description":"사용 가능한 결제 수단이에요. TOSS_MONEY/CARD 또는 null 값을 사용할 수 있어요.","maxLength":100,"type":"string"},"installment":{"description":"할부 제한 타입이에요. USE(할부 사용, 기본값)/NOT_USE(할부 미사용) 중 하나예요.","maxLength":10,"type":"string"},"isTestPayment":{"description":"샌드박스일 경우 false, 라이브앱일 경우 true예요. true면 실결제가 이루어져요.","type":"boolean"},"orderNo":{"description":"가맹점의 상품 주문번호예요. 숫자, 영문자, 특수문자(_-:.^@)를 사용할 수 있어요.","maxLength":50,"type":"string"},"productDesc":{"description":"상품 설명이에요. 한글이 포함되면 인코딩에 유의해주세요.","maxLength":255,"type":"string"}},"required":["amount","amountTaxFree","isTestPayment","orderNo","productDesc"],"type":"object"},"TossApiSuccessMakePaymentResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/MakePaymentResponse"}},"required":["resultType","success"],"type":"object"},"MakePaymentResponse":{"description":"토스페이 결제 생성 요청 응답이에요.","properties":{"payToken":{"description":"토스페이 토큰이에요. 매회 유니크한 토큰값으로, 가맹점에서 이 값을 반드시 저장하고 관리해야 해요.","maxLength":30,"type":"string"}},"required":["payToken"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/make-payment":{"post":{"description":"결제를 생성해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"makePayment","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakePaymentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessMakePaymentResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessMakePaymentResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"결제 생성 요청이 성공적으로 처리됐어요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"결제 생성하기","tags":["toss-pay"]}}}}
```

## 결제 실행하기

> 결제 인증이 끝난 결제 건의 승인을 요청해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"ExecutePaymentRequest":{"description":"사용자 인증이 된 결제 건에 대한 승인 요청 본문이에요.","properties":{"isTestPayment":{"description":"샌드박스일 경우 false, 라이브앱일 경우 true예요. true면 실결제가 이루어져요.","type":"boolean"},"orderNo":{"description":"가맹점의 상품 주문번호예요. 숫자, 영문자, 특수문자(_-:.^@)를 사용할 수 있어요.","maxLength":50,"type":"string"},"payToken":{"description":"토스페이 토큰이에요. 승인할 결제 건의 토큰값이에요.","maxLength":30,"type":"string"}},"required":["isTestPayment","payToken"],"type":"object"},"TossApiSuccessExecutePaymentResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/ExecutePaymentResponse"}},"required":["resultType","success"],"type":"object"},"ExecutePaymentResponse":{"description":"결제 승인 요청 응답이에요.","properties":{"accountBankCode":{"description":"은행 코드예요. 토스머니(계좌) 결제인 경우에만 내려와요.","type":"string"},"accountBankName":{"description":"은행 이름이에요. 토스머니(계좌) 결제인 경우에만 내려와요.","type":"string"},"accountNumber":{"description":"계좌번호예요. 일부 마스킹되어 있고, 토스머니(계좌) 결제인 경우에만 내려와요.","type":"string"},"amount":{"description":"총 결제 금액이에요.","format":"int32","type":"integer"},"approvalTime":{"description":"결제가 승인된 시간이에요. (yyyy-MM-dd HH:mm:ss 형식)","type":"string"},"cardAuthorizationNo":{"description":"카드 승인 번호예요. 카드 결제인 경우에만 내려와요.","type":"string"},"cardBinNumber":{"description":"카드 BIN 번호예요. 카드사에서 제공한 값이며 마스킹되어 있을 수 있고, 카드 결제인 경우에만 내려와요.","type":"string"},"cardCompanyCode":{"description":"카드사 코드예요. 카드 결제인 경우에만 내려와요.","format":"int32","type":"integer"},"cardCompanyName":{"description":"카드사 이름이에요. 카드 결제인 경우에만 내려와요.","type":"string"},"cardMethodType":{"description":"카드타입이에요. CREDIT(신용카드)/CHECK(체크카드)/PREPAYMENT(선불카드) 중 하나이고, 카드 결제인 경우에만 내려와요.","type":"string"},"cardNum4Print":{"description":"사용자가 선택한 카드의 끝 4자리예요. 카드 결제인 경우에만 내려와요.","type":"string"},"cardNumber":{"description":"마스킹된 카드번호예요. 카드 결제인 경우에만 내려와요.","type":"string"},"cardUserType":{"description":"카드 사용자 구분이에요. PERSONAL(본인 카드)/PERSONAL_FAMILY(가족 카드)/CORP_PERSONAL(법인지정 결제계좌 임직원)/CORP_PRIVATE(법인 공용)/CORP_COMPANY(법인지정 결제계좌 회사(하나카드만)) 중 하나이고, 카드 결제인 경우에만 내려와요.","type":"string"},"cashReceiptMgtKey":{"description":"현금영수증 관리번호 식별값이에요. 토스머니(계좌) 결제인 경우에만 내려와요.","type":"string"},"code":{"description":"결제 승인 처리 결과 코드예요. 성공이면 0, 실패면 -1이에요.","format":"int32","type":"integer"},"discountedAmount":{"description":"할인이 적용된 결제 금액이에요.","format":"int32","type":"integer"},"errorCode":{"description":"결제 실패 시 내려오는 에러 코드예요. code가 -1일 때 내려와요.","type":"string"},"mode":{"description":"결제가 처리된 환경을 나타내요. NORMAL 또는 TEST 값 중 하나예요.","type":"string"},"msg":{"description":"결제 실패 시 실패 사유를 담은 메시지예요. code가 -1일 때 내려와요.","type":"string"},"noInterest":{"description":"무이자 할부 여부예요. 카드 결제인 경우에만 내려와요.","type":"boolean"},"orderNo":{"description":"요청한 가맹점의 상품 주문번호예요.","type":"string"},"paidAmount":{"description":"지불수단으로 실제 승인된 금액이에요.","format":"int32","type":"integer"},"payMethod":{"description":"결제 수단이에요. CARD 또는 TOSS_MONEY 값을 가져요.","type":"string"},"payToken":{"description":"이 결제 건을 식별하는 토큰이에요.","type":"string"},"salesCheckLinkUrl":{"description":"매출전표 확인 URL이에요. 카드 결제인 경우에만 내려와요.","type":"string"},"spreadOut":{"description":"할부 개월 수예요. 0이면 일시불이고, 카드 결제인 경우에만 내려와요.","format":"int32","type":"integer"},"stateMsg":{"description":"결제 처리 상태를 설명하는 메시지예요.","type":"string"},"transactionId":{"description":"거래를 식별하는 트랜잭션 아이디예요.","type":"string"}},"required":["amount","approvalTime","code","discountedAmount","mode","orderNo","paidAmount","payMethod","payToken","stateMsg","transactionId"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/execute-payment":{"post":{"description":"결제 인증이 끝난 결제 건의 승인을 요청해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"executePayment","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecutePaymentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessExecutePaymentResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessExecutePaymentResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"결제 승인 요청이 성공적으로 처리됐어요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"결제 실행하기","tags":["toss-pay"]}}}}
```

## 결제 상태 조회하기

> 사용자가 요청한 결제 상태를 조회할 수 있어요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"PaymentStatusRequest":{"description":"결제 상태 조회 요청 파라미터예요.","properties":{"isTestPayment":{"description":"테스트 결제인지 나타내요.","type":"boolean"},"orderNo":{"description":"주문 번호예요. 요청할 때 이 값과 `payToken` 둘 중 하나는 필수예요.","type":"string"},"payToken":{"description":"결제를 식별하는 키예요. 요청할 때 이 값과 `orderNo` 둘 중 하나는 필수예요.","type":"string"}},"required":["isTestPayment"],"type":"object"},"TossApiSuccessPaymentStatusResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/PaymentStatusResponse"}},"required":["resultType","success"],"type":"object"},"PaymentStatusResponse":{"description":"결제 상태 조회 응답 정보예요.","properties":{"accountBankCode":{"description":"계좌 결제인 경우 은행 코드예요.","type":"string"},"accountBankName":{"description":"계좌 결제인 경우 은행 이름이에요.","type":"string"},"accountNumber":{"description":"계좌 번호예요.","type":"string"},"amount":{"description":"총 결제 금액이에요.","format":"int32","type":"integer"},"amountServiceFee":{"description":"서비스 수수료 금액이에요.","format":"int32","type":"integer"},"amountTaxFree":{"description":"비과세 금액이에요.","format":"int32","type":"integer"},"amountTaxable":{"description":"과세 대상 금액이에요.","format":"int32","type":"integer"},"amountVat":{"description":"부가세 금액이에요.","format":"int32","type":"integer"},"card":{"$ref":"#/components/schemas/CardInfo","description":"카드 결제 정보예요."},"createdTs":{"description":"결제가 생성된 시간이에요 (ISO 8601 형식).","type":"string"},"discountAmountV2":{"description":"최신 할인 시스템에서 적용된 할인 금액이에요.","format":"int32","type":"integer"},"discountedAmount":{"description":"할인 적용 후 금액이에요.","format":"int32","type":"integer"},"disposableCupDeposit":{"description":"일회용 컵 보증금 금액이에요.","format":"int32","type":"integer"},"mode":{"description":"결제 처리 환경을 나타내요. NORMAL 또는 TEST 값 중 하나예요.","type":"string"},"orderNo":{"description":"주문 번호예요.","type":"string"},"paidAmount":{"description":"실제 사용자가 결제한 금액이에요.","format":"int32","type":"integer"},"paidPointV2":{"description":"적립금/포인트로 결제한 금액이에요.","format":"int32","type":"integer"},"paidTs":{"description":"결제가 완료된 시간이에요 (ISO 8601 형식).","type":"string"},"payMethod":{"description":"결제 수단이에요. 예: CARD, ACCOUNT_TRANSFER","type":"string"},"payStatus":{"description":"결제 상태예요. 예: DONE, CANCELLED, FAILED 등","type":"string"},"payToken":{"description":"결제를 식별하는 키예요.","type":"string"},"refundableAmount":{"description":"현재 환불 가능한 금액이에요.","format":"int32","type":"integer"},"transactions":{"description":"결제 처리 단계별 트랜잭션 리스트예요.","items":{"$ref":"#/components/schemas/TransactionInfo"},"type":"array"}},"required":["amount","amountServiceFee","amountTaxFree","amountTaxable","amountVat","createdTs","discountAmountV2","discountedAmount","disposableCupDeposit","mode","orderNo","paidAmount","paidPointV2","paidTs","payMethod","payStatus","payToken","refundableAmount","transactions"]},"CardInfo":{"description":"카드 결제 상세 정보예요.","properties":{"cardAuthorizationNo":{"description":"카드 승인 번호예요.","type":"string"},"cardBinNumber":{"description":"카드 BIN 번호예요.","type":"string"},"cardCompanyCode":{"description":"카드사 코드예요.","format":"int32","type":"integer"},"cardCompanyName":{"description":"카드사 이름이에요.","type":"string"},"cardMethodType":{"description":"카드 결제 방식이에요.","type":"string"},"cardNum4Print":{"description":"사용자가 선택한 카드의 끝 4자리예요.","type":"string"},"cardNumber":{"description":"마스킹된 카드번호예요.","type":"string"},"cardUserType":{"description":"개인/법인 여부예요.","type":"string"},"noInterest":{"description":"무이자 할부 여부예요.","type":"boolean"},"salesCheckLinkUrl":{"description":"매출전표 URL이에요.","type":"string"},"spreadOut":{"description":"할부 개월 수예요.","format":"int32","type":"integer"}},"required":["cardAuthorizationNo","cardBinNumber","cardCompanyCode","cardCompanyName","cardMethodType","cardNum4Print","cardNumber","cardUserType","noInterest","salesCheckLinkUrl","spreadOut"]},"TransactionInfo":{"description":"결제 처리 단계별 트랜잭션 정보예요.","properties":{"discountedAmount":{"description":"해당 단계에서 적용된 할인 금액이에요.","format":"int32","type":"integer"},"paidAmount":{"description":"해당 단계에서 실제 결제된 금액이에요.","format":"int32","type":"integer"},"pointAmount":{"description":"해당 단계에서 사용된 포인트 금액이에요.","format":"int32","type":"integer"},"regTs":{"description":"트랜잭션 등록 시간이에요 (ISO 8601 형식).","type":"string"},"stepType":{"description":"결제 처리 단계예요. 예: INIT, PAID","type":"string"},"transactionAmount":{"description":"해당 단계의 총 결제 금액이에요.","format":"int32","type":"integer"},"transactionId":{"description":"트랜잭션 식별자예요.","type":"string"}},"required":["discountedAmount","paidAmount","pointAmount","regTs","stepType","transactionAmount","transactionId"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/get-payment-status":{"post":{"description":"사용자가 요청한 결제 상태를 조회할 수 있어요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"getPaymentStatus","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentStatusRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessPaymentStatusResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessPaymentStatusResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"결제 상태 정보가 반환돼요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"결제 상태 조회하기","tags":["toss-pay"]}}}}
```

## 결제 환불하기

> 결제 건에 대해 환불을 요청할 수 있어요. 환불 가능 여부와 잔액 조건 등을 사전에 확인해주세요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"RefundPaymentRequest":{"description":"토스페이 환불을 위한 요청 본문이에요.","properties":{"amount":{"description":"환불할 금액이에요. 미입력 시 환불할 결제 건의 남은 전액을 환불 처리해요. 부분환불 시 필수로 amount를 활용해 주세요.","format":"int64","type":"integer"},"isTestPayment":{"description":"샌드박스일 경우 false, 라이브앱일 경우 true예요. true면 실결제가 이루어져요.","type":"boolean"},"payToken":{"description":"토스페이 토큰이에요. 승인할 결제 건의 토큰값이에요.","maxLength":30,"type":"string"},"reason":{"description":"환불 사유예요.","maxLength":55,"type":"string"}},"required":["isTestPayment","payToken"],"type":"object"},"TossApiSuccessRefundPaymentResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/RefundPaymentResponse"}},"required":["resultType","success"],"type":"object"},"RefundPaymentResponse":{"description":"결제 환불 요청 응답이에요.","properties":{"accountBankCode":{"description":"은행 코드예요.","maxLength":3,"type":"string"},"accountBankName":{"description":"은행 이름이에요.","maxLength":20,"type":"string"},"accountNumber":{"description":"계좌번호예요. 일부 마스킹을 포함하고 있어요.","maxLength":30,"type":"string"},"approvalTime":{"description":"결제 건의 환불이 처리된 시간이에요. (yyyy-MM-dd HH:mm:ss 형식)","type":"string"},"cardBinNumber":{"description":"카드 BIN 번호예요. 카드사에서 준 값으로, 마스킹되어 있을 수 있어요.","maxLength":8,"type":"string"},"cardMethodType":{"description":"카드타입이에요. CREDIT(신용카드)/CHECK(체크카드)/PREPAYMENT(선불카드) 중 하나예요.","maxLength":10,"type":"string"},"cardNum4Print":{"description":"사용자가 선택한 카드의 끝 4자리예요.","maxLength":4,"type":"string"},"cardNumber":{"description":"마스킹된 카드번호예요.","maxLength":20,"type":"string"},"cardUserType":{"description":"카드 사용자 구분이에요. PERSONAL(본인 카드)/PERSONAL_FAMILY(가족 카드)/CORP_PERSONAL(법인지정 결제계좌 임직원)/CORP_PRIVATE(법인 공용)/CORP_COMPANY(법인지정 결제계좌 회사(하나카드만)) 중 하나예요.","maxLength":20,"type":"string"},"cashReceiptMgtKey":{"description":"현금영수증 관리번호 식별값이에요.","maxLength":36,"type":"string"},"discountedAmount":{"description":"할인된 금액이에요.","format":"int32","maxLength":7,"type":"integer"},"paidAmount":{"description":"지불수단 승인금액이에요.","format":"int32","maxLength":7,"type":"integer"},"payToken":{"description":"환불된 결제 토큰이에요.","maxLength":30,"type":"string"},"refundNo":{"description":"환불 번호예요.","type":"string"},"refundableAmount":{"description":"환불 가능 금액이에요.","format":"int32","maxLength":7,"type":"integer"},"refundedAmount":{"description":"환불 요청 금액이에요.","format":"int32","maxLength":7,"type":"integer"},"refundedDiscountAmount":{"description":"환불 요청 금액 중 실제 차감된 할인 금액이에요.","format":"int32","maxLength":7,"type":"integer"},"refundedPaidAmount":{"description":"환불 요청 금액 중 실제 차감된 지불수단 금액이에요.","format":"int32","maxLength":7,"type":"integer"},"transactionId":{"description":"거래 트랜잭션 아이디예요.","maxLength":36,"type":"string"}},"required":["approvalTime","discountedAmount","paidAmount","payToken","refundNo","refundableAmount","refundedAmount","refundedDiscountAmount","refundedPaidAmount","transactionId"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/refund-payment":{"post":{"description":"결제 건에 대해 환불을 요청할 수 있어요. 환불 가능 여부와 잔액 조건 등을 사전에 확인해주세요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"refundPayment","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundPaymentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessRefundPaymentResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessRefundPaymentResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"환불 요청이 성공적으로 처리됐어요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"결제 환불하기","tags":["toss-pay"]}}}}
```

## 빌링키 생성하기

> 자동결제를 위한 빌링키를 생성해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"CreateBillingKeyRequest":{"description":"빌링키 생성 요청 본문이에요.","properties":{"isTestPayment":{"description":"테스트 결제 여부예요.","type":"boolean"},"productDesc":{"description":"자동결제 상품명이에요.","type":"string"},"returnFailureUrl":{"description":"인증 실패 시 이동할 URL이에요.","type":"string"},"returnSuccessUrl":{"description":"인증 성공 후 이동할 URL이에요.","type":"string"}},"required":["isTestPayment","productDesc"]},"TossApiSuccessCreateBillingKeyResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/CreateBillingKeyResponse"}},"required":["resultType","success"],"type":"object"},"CreateBillingKeyResponse":{"description":"빌링키 생성 응답이에요.","properties":{"checkoutAndroidUri":{"description":"Android 인증 URI예요.","type":"string"},"checkoutIosUri":{"description":"iOS 인증 URI예요.","type":"string"},"checkoutUri":{"description":"토스 앱 인증 URI예요.","type":"string"},"wrappedToken":{"description":"래핑된 빌링키 토큰이에요.","type":"string"}},"required":["wrappedToken"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/create-billing-key":{"post":{"description":"자동결제를 위한 빌링키를 생성해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"createBillingKey","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBillingKeyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessCreateBillingKeyResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessCreateBillingKeyResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청 처리 결과예요. `resultType` 값으로 성공/실패를 구분하세요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"빌링키 생성하기","tags":["toss-pay"]}}}}
```

## 빌링키 상태 조회하기

> 빌링키의 현재 상태를 조회해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`5006\` | 빌링키를 찾을 수 없어요. |\
> \| \`5005\` | 비활성화된 빌링키에요. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"BillingKeyStatusRequest":{"description":"빌링키 상태 조회 요청 본문이에요.","properties":{"isTestPayment":{"description":"테스트 결제 여부예요.","type":"boolean"},"wrappedToken":{"description":"래핑된 빌링키 토큰이에요.","type":"string"}},"required":["isTestPayment","wrappedToken"]},"TossApiSuccessBillingKeyStatusResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/BillingKeyStatusResponse"}},"required":["resultType","success"],"type":"object"},"BillingKeyStatusResponse":{"description":"빌링키 상태 조회 응답이에요.","properties":{"accountBankCode":{"description":"은행 코드예요. 계좌로 등록된 빌링키인 경우에만 내려와요.","type":"string"},"accountBankName":{"description":"은행 이름이에요. 계좌로 등록된 빌링키인 경우에만 내려와요.","type":"string"},"accountImgUrl":{"description":"은행 이미지 URL이에요. 계좌로 등록된 빌링키인 경우에만 내려와요.","type":"string"},"accountName":{"description":"예금주명이에요. 계좌로 등록된 빌링키인 경우에만 내려와요.","type":"string"},"accountNumber":{"description":"계좌번호예요. 일부 마스킹되어 있고, 계좌로 등록된 빌링키인 경우에만 내려와요.","type":"string"},"billingKeyStatus":{"description":"앱인토스 파트너 API에서 사용하는 빌링키 상태예요.","enum":["CREATED","AUTHENTICATING","ACTIVE","REMOVED","CANCELED","FAILED","UNKNOWN"],"type":"string"},"cardCompanyName":{"description":"카드사 이름이에요. 카드로 등록된 빌링키인 경우에만 내려와요.","type":"string"},"cardCompanyNo":{"description":"카드사 코드예요. 카드로 등록된 빌링키인 경우에만 내려와요.","format":"int32","type":"integer"},"cardImgUrl":{"description":"카드 이미지 URL이에요. 카드로 등록된 빌링키인 경우에만 내려와요.","type":"string"},"cardName":{"description":"카드 이름이에요. 카드로 등록된 빌링키인 경우에만 내려와요.","type":"string"},"cardNumber":{"description":"마스킹된 카드번호예요. 카드로 등록된 빌링키인 경우에만 내려와요.","type":"string"}},"required":["billingKeyStatus"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/get-billing-key-status":{"post":{"description":"빌링키의 현재 상태를 조회해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `5006` | 빌링키를 찾을 수 없어요. |\n| `5005` | 비활성화된 빌링키에요. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"getBillingKeyStatus","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingKeyStatusRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessBillingKeyStatusResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessBillingKeyStatusResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청 처리 결과예요. `resultType` 값으로 성공/실패를 구분하세요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"빌링키 상태 조회하기","tags":["toss-pay"]}}}}
```

## 자동결제 승인하기

> 빌링키를 이용해 결제를 승인해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`5006\` | 빌링키를 찾을 수 없어요. |\
> \| \`5005\` | 비활성화된 빌링키에요. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"ExecuteBillingRequest":{"description":"자동결제 승인 요청 본문이에요.","properties":{"amount":{"description":"결제 금액이에요.","format":"int64","type":"integer"},"amountServiceFee":{"description":"봉사료예요.","format":"int64","type":"integer"},"amountTaxFree":{"description":"비과세 금액이에요.","format":"int64","type":"integer"},"amountTaxable":{"description":"과세 금액이에요.","format":"int64","type":"integer"},"amountVat":{"description":"부가세예요.","format":"int64","type":"integer"},"cashReceipt":{"description":"현금영수증 발급 여부예요. 값이 없으면 true로 처리돼요.","type":"boolean"},"cashReceiptTradeOption":{"description":"현금영수증 발급 타입이에요. GENERAL/CULTURE/PUBLIC_TP 중 하나이고, 값이 없으면 GENERAL로 처리돼요.","type":"string"},"isTestPayment":{"description":"테스트 결제 여부예요.","type":"boolean"},"orderNo":{"description":"주문번호예요.","type":"string"},"productDesc":{"description":"상품 설명이에요.","type":"string"},"sendFailPush":{"description":"결제 실패 시 푸시 발송 여부예요. 값이 없으면 true로 처리돼요.","type":"boolean"},"spreadOut":{"description":"할부 개월 수예요. 0이면 일시불이에요.","format":"int32","type":"integer"},"wrappedToken":{"description":"래핑된 빌링키 토큰이에요.","type":"string"}},"required":["amount","amountTaxFree","isTestPayment","orderNo","productDesc","spreadOut","wrappedToken"]},"TossApiSuccessExecuteBillingResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/ExecuteBillingResponse"}},"required":["resultType","success"],"type":"object"},"ExecuteBillingResponse":{"description":"자동결제 승인 응답이에요.","properties":{"accountBankCode":{"description":"은행 코드예요. 토스머니(계좌) 결제인 경우에만 내려와요.","type":"string"},"accountBankName":{"description":"은행 이름이에요. 토스머니(계좌) 결제인 경우에만 내려와요.","type":"string"},"accountNumber":{"description":"계좌번호예요. 일부 마스킹되어 있고, 토스머니(계좌) 결제인 경우에만 내려와요.","type":"string"},"amount":{"description":"총 결제 금액이에요.","format":"int64","type":"integer"},"approvalTime":{"description":"자동결제가 승인된 시간이에요. (yyyy-MM-dd HH:mm:ss 형식)","type":"string"},"cardAuthorizationNo":{"description":"카드 승인 번호예요. 카드 결제인 경우에만 내려와요.","type":"string"},"cardBinNumber":{"description":"카드 BIN 번호예요. 카드사에서 제공한 값이며 마스킹되어 있을 수 있고, 카드 결제인 경우에만 내려와요.","type":"string"},"cardCompanyCode":{"description":"카드사 코드예요. 카드 결제인 경우에만 내려와요.","format":"int32","type":"integer"},"cardCompanyName":{"description":"카드사 이름이에요. 카드 결제인 경우에만 내려와요.","type":"string"},"cardMethodType":{"description":"카드타입이에요. CREDIT(신용카드)/CHECK(체크카드)/PREPAYMENT(선불카드) 중 하나이고, 카드 결제인 경우에만 내려와요.","type":"string"},"cardNum4Print":{"description":"사용자가 선택한 카드의 끝 4자리예요. 카드 결제인 경우에만 내려와요.","type":"string"},"cardNumber":{"description":"마스킹된 카드번호예요. 카드 결제인 경우에만 내려와요.","type":"string"},"cardUserType":{"description":"카드 사용자 구분이에요. PERSONAL(본인 카드)/PERSONAL_FAMILY(가족 카드)/CORP_PERSONAL(법인지정 결제계좌 임직원)/CORP_PRIVATE(법인 공용)/CORP_COMPANY(법인지정 결제계좌 회사(하나카드만)) 중 하나이고, 카드 결제인 경우에만 내려와요.","type":"string"},"cashReceiptMgtKey":{"description":"현금영수증 관리번호 식별값이에요. 토스머니(계좌) 결제인 경우에만 내려와요.","type":"string"},"code":{"description":"자동결제 승인 처리 결과 코드예요. 성공이면 0, 실패면 -1이에요.","format":"int32","type":"integer"},"discountedAmount":{"description":"할인이 적용된 결제 금액이에요.","format":"int64","type":"integer"},"errorCode":{"description":"자동결제 실패 시 내려오는 에러 코드예요. code가 -1일 때 내려와요.","type":"string"},"mode":{"description":"결제가 처리된 환경을 나타내요. NORMAL 또는 TEST 값 중 하나예요.","type":"string"},"msg":{"description":"자동결제 실패 시 실패 사유를 담은 메시지예요. code가 -1일 때 내려와요.","type":"string"},"noInterest":{"description":"무이자 할부 여부예요. 카드 결제인 경우에만 내려와요.","type":"boolean"},"orderNo":{"description":"요청한 주문번호예요.","type":"string"},"paidAmount":{"description":"지불수단으로 실제 승인된 금액이에요.","format":"int64","type":"integer"},"payMethod":{"description":"결제 수단이에요. CARD 또는 TOSS_MONEY 값을 가져요.","type":"string"},"payToken":{"description":"이 자동결제 건을 식별하는 토큰이에요.","type":"string"},"salesCheckLinkUrl":{"description":"매출전표 확인 URL이에요. 카드 결제인 경우에만 내려와요.","type":"string"},"spreadOut":{"description":"할부 개월 수예요. 0이면 일시불이고, 카드 결제인 경우에만 내려와요.","format":"int32","type":"integer"},"transactionId":{"description":"거래를 식별하는 트랜잭션 아이디예요.","type":"string"}},"required":["code"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/execute-billing":{"post":{"description":"빌링키를 이용해 결제를 승인해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `5006` | 빌링키를 찾을 수 없어요. |\n| `5005` | 비활성화된 빌링키에요. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"executeBilling","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteBillingRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessExecuteBillingResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessExecuteBillingResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청 처리 결과예요. `resultType` 값으로 성공/실패를 구분하세요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"자동결제 승인하기","tags":["toss-pay"]}}}}
```

## 자동결제 환불하기

> 자동결제로 승인된 결제 건을 환불해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"RefundBillingRequest":{"description":"자동결제 환불 요청 본문이에요.","properties":{"isTestPayment":{"description":"테스트 결제 여부예요.","type":"boolean"},"payToken":{"description":"환불할 자동결제 건의 토스페이 토큰이에요.","type":"string"},"reason":{"description":"환불 사유예요.","type":"string"}},"required":["isTestPayment","payToken"]},"TossApiSuccessRefundBillingResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/RefundBillingResponse"}},"required":["resultType","success"],"type":"object"},"RefundBillingResponse":{"description":"자동결제 환불 응답이에요.","properties":{"accountBankCode":{"description":"은행 코드예요.","type":"string"},"accountBankName":{"description":"은행 이름이에요.","type":"string"},"accountNumber":{"description":"계좌번호예요. 일부 마스킹되어 있어요.","type":"string"},"approvalTime":{"description":"환불이 처리된 시간이에요. (yyyy-MM-dd HH:mm:ss 형식)","type":"string"},"cardBinNumber":{"description":"카드 BIN 번호예요. 카드사에서 제공한 값이며 마스킹되어 있을 수 있어요.","type":"string"},"cardMethodType":{"description":"카드타입이에요. CREDIT(신용카드)/CHECK(체크카드)/PREPAYMENT(선불카드) 중 하나예요.","type":"string"},"cardNum4Print":{"description":"사용자가 선택한 카드의 끝 4자리예요.","type":"string"},"cardNumber":{"description":"마스킹된 카드번호예요.","type":"string"},"cardUserType":{"description":"카드 사용자 구분이에요. PERSONAL(본인 카드)/PERSONAL_FAMILY(가족 카드)/CORP_PERSONAL(법인지정 결제계좌 임직원)/CORP_PRIVATE(법인 공용)/CORP_COMPANY(법인지정 결제계좌 회사(하나카드만)) 중 하나예요.","type":"string"},"cashReceiptMgtKey":{"description":"현금영수증 관리번호 식별값이에요.","type":"string"},"discountedAmount":{"description":"할인된 금액이에요.","format":"int32","type":"integer"},"paidAmount":{"description":"지불수단 승인금액이에요.","format":"int32","type":"integer"},"payToken":{"description":"환불된 결제 토큰이에요.","type":"string"},"refundNo":{"description":"환불 번호예요.","type":"string"},"refundableAmount":{"description":"환불 가능 금액이에요.","format":"int32","type":"integer"},"refundedAmount":{"description":"환불 요청 금액이에요.","format":"int32","type":"integer"},"refundedDiscountAmount":{"description":"환불 요청 금액 중 실제 차감된 할인 금액이에요.","format":"int32","type":"integer"},"refundedPaidAmount":{"description":"환불 요청 금액 중 실제 차감된 지불수단 금액이에요.","format":"int32","type":"integer"},"transactionId":{"description":"거래 트랜잭션 아이디예요.","type":"string"}},"required":["approvalTime","discountedAmount","paidAmount","payToken","refundNo","refundableAmount","refundedAmount","refundedDiscountAmount","refundedPaidAmount","transactionId"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/refund-billing":{"post":{"description":"자동결제로 승인된 결제 건을 환불해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"refundBilling","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundBillingRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessRefundBillingResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessRefundBillingResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청 처리 결과예요. `resultType` 값으로 성공/실패를 구분하세요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"자동결제 환불하기","tags":["toss-pay"]}}}}
```

## 빌링키 삭제하기

> 빌링키를 삭제(해지)해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5001\` | 토스페이 청약이 되어 있지 않습니다. |\
> \| \`5006\` | 빌링키를 찾을 수 없어요. |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"토스페이와 관련된 요청을 처리하는 API예요.","name":"toss-pay"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"RemoveBillingKeyRequest":{"description":"빌링키 삭제 요청 본문이에요.","properties":{"isTestPayment":{"description":"테스트 결제 여부예요.","type":"boolean"},"wrappedToken":{"description":"래핑된 빌링키 토큰이에요.","type":"string"}},"required":["isTestPayment","wrappedToken"]},"TossApiSuccessRemoveBillingKeyResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/RemoveBillingKeyResponse"}},"required":["resultType","success"],"type":"object"},"RemoveBillingKeyResponse":{"description":"빌링키 삭제 응답이에요.","properties":{"code":{"description":"빌링키 삭제 처리 결과 코드예요. 성공이면 0이에요.","format":"int32","type":"integer"},"msg":{"description":"빌링키 삭제 실패 시 실패 사유를 담은 메시지예요.","type":"string"}},"required":["code"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/pay/remove-billing-key":{"post":{"description":"빌링키를 삭제(해지)해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5001` | 토스페이 청약이 되어 있지 않습니다. |\n| `5006` | 빌링키를 찾을 수 없어요. |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"removeBillingKey","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveBillingKeyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessRemoveBillingKeyResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessRemoveBillingKeyResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청 처리 결과예요. `resultType` 값으로 성공/실패를 구분하세요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"빌링키 삭제하기","tags":["toss-pay"]}}}}
```


# 푸시, 알림

## 메시지 발송하기

> 이 API는 파트너 앱이 토스 사용자에게 알림 또는 메시지를 전송할 수 있게 해줘요. 사용자 인증 토큰이 필요하며, 사용자에게 메시지를 전송할 수 있는 scope 권한이 포함돼야 해요. 테스트 발송을 포함해 모든 메시지는 문구 검수를 통해 승인 받은 이후 발송 가능해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5004\` | 승인되지 않은 메시지 템플릿이에요. 메시지 발송을 하기 위해서는 템플릿 검토 승인이 필요해요. |\
> \| \`4034\` | 워크스페이스가 없거나 워크스페이스에 접근할 수 있는 권한이 없어요 |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 15,000회, 사용자당 분당 10회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"파트너 앱에서 토스 사용자와 메시지를 처리할 때 사용하는 API예요.","name":"push"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"SendMessageRequest":{"description":"사용자에게 보낼 메시지를 정의해요. 어떤 메시지 템플릿을 사용할지와 템플릿에 들어갈 데이터를 함께 담아요.","properties":{"context":{"additionalProperties":{"type":"object"},"description":"템플릿에서 사용할 변수값들이에요. 예를 들어 사용자 이름이나 인증번호 같은 값을 넣어요.","type":"object"},"templateSetCode":{"description":"사용할 메시지 템플릿 코드예요. 사전에 등록한 템플릿 코드 중 하나를 넣어요.","type":"string"}},"required":["context","templateSetCode"],"type":"object"},"TossApiSuccessSendMessageResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/SendMessageResponse"}},"required":["resultType","success"],"type":"object"},"SendMessageResponse":{"description":"채널별로 몇 건의 메시지가 전송됐는지와 성공/실패한 메시지의 상세 정보가 담겨 있어요.","properties":{"detail":{"$ref":"#/components/schemas/AppendContextResultDetailResponse","description":"성공적으로 전송된 메시지들의 상세 정보예요."},"fail":{"$ref":"#/components/schemas/AppendContextResultDetailResponse","description":"전송에 실패한 메시지들의 상세 정보예요."},"msgCount":{"description":"총 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentAlimtalkCount":{"description":"알림톡으로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentFriendtalkCount":{"description":"친구톡으로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentInboxCount":{"description":"인박스(Inbox)로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentPushCount":{"description":"푸시(Push)로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentSmsCount":{"description":"SMS로 발송된 메시지 수예요.","format":"int32","type":"integer"}},"required":["detail","fail","msgCount","sentAlimtalkCount","sentFriendtalkCount","sentInboxCount","sentPushCount","sentSmsCount"]},"AppendContextResultDetailResponse":{"description":"각 채널별로 전송된 메시지들의 상세 리스트예요. 어떤 메시지가 어떤 채널에서 성공 또는 실패했는지 알 수 있어요.","properties":{"sentAlimtalk":{"description":"알림톡으로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentFriendtalk":{"description":"친구톡으로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentInbox":{"description":"인박스(Inbox)로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentPush":{"description":"푸시(Push)로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentSms":{"description":"SMS로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"}},"required":["sentAlimtalk","sentFriendtalk","sentInbox","sentPush","sentSms"]},"SentContentResponse":{"description":"발송된 메시지 한 건에 대한 정보예요.","properties":{"contentId":{"description":"발송된 메시지의 고유 ID예요.","type":"string"},"reachedFailReason":{"description":"메시지 도달에 실패한 경우 실패 사유가 담겨 있어요.","type":"string"}},"required":["contentId"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/messenger/send-message":{"post":{"description":"이 API는 파트너 앱이 토스 사용자에게 알림 또는 메시지를 전송할 수 있게 해줘요. 사용자 인증 토큰이 필요하며, 사용자에게 메시지를 전송할 수 있는 scope 권한이 포함돼야 해요. 테스트 발송을 포함해 모든 메시지는 문구 검수를 통해 승인 받은 이후 발송 가능해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5004` | 승인되지 않은 메시지 템플릿이에요. 메시지 발송을 하기 위해서는 템플릿 검토 승인이 필요해요. |\n| `4034` | 워크스페이스가 없거나 워크스페이스에 접근할 수 있는 권한이 없어요 |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 15,000회, 사용자당 분당 10회","operationId":"sendMessage","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}},"description":"전송할 메시지의 정보예요","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessSendMessageResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessSendMessageResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"메시지 전송에 성공했어요"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"메시지 발송하기","tags":["push"]}}}}
```

## 대량 메시지 발송하기

> 여러 사용자에게 동일한 템플릿으로 메시지를 대량 발송해요. 최소 50건 이상 대량 발송할때 사용해주세요. 한번 요청 시 최대 2,500건까지 발송 가능해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`5004\` | 승인되지 않은 메시지 템플릿이에요. 메시지 발송을 하기 위해서는 템플릿 검토 승인이 필요해요. |\
> \| \`4034\` | 워크스페이스가 없거나 워크스페이스에 접근할 수 있는 권한이 없어요 |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"파트너 앱에서 토스 사용자와 메시지를 처리할 때 사용하는 API예요.","name":"push"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"BulkSendMessageRequest":{"description":"대량으로 사용자에게 보낼 메시지를 정의해요. 어떤 메시지 템플릿을 사용할지와 템플릿에 들어갈 데이터 목록을 함께 담아요.","properties":{"contextList":{"description":"메시지를 받을 사용자의 userKey 또는 anonKey와 변수값 목록이에요.","items":{"$ref":"#/components/schemas/BulkSendMessageContext"},"type":"array"},"templateSetCode":{"description":"사용할 메시지 템플릿 코드예요. 사전에 등록한 템플릿 코드 중 하나를 넣어요.","type":"string"}},"required":["contextList","templateSetCode"]},"BulkSendMessageContext":{"description":"대량 메시지 발송 시 수신자 한 명에 대한 정보와 템플릿 변수값을 담은 항목이에요.","properties":{"anonKey":{"description":"앱에서 발급받은 익명 사용자 식별자에요.","type":"string"},"context":{"additionalProperties":{"type":"object"},"description":"템플릿에서 사용할 변수값들이에요. 예를 들어 사용자 이름이나 인증번호 같은 값을 넣어요.","type":"object"},"userKey":{"description":"사용자의 고유 식별자에요.","format":"int64","type":"integer"}},"required":["context"]},"TossApiSuccessBulkSendMessageResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/BulkSendMessageResponse"}},"required":["resultType","success"],"type":"object"},"BulkSendMessageResponse":{"description":"채널별로 몇 건의 메시지가 전송됐는지와 성공/실패한 메시지의 상세 정보가 담겨 있어요.","properties":{"detail":{"$ref":"#/components/schemas/AppendContextResultDetailResponse","description":"성공적으로 전송된 메시지들의 상세 정보예요."},"fail":{"$ref":"#/components/schemas/AppendContextResultDetailResponse","description":"전송에 실패한 메시지들의 상세 정보예요."},"msgCount":{"description":"총 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentAlimtalkCount":{"description":"알림톡으로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentFriendtalkCount":{"description":"친구톡으로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentInboxCount":{"description":"인박스(Inbox)로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentPushCount":{"description":"푸시(Push)로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentSmsCount":{"description":"SMS로 발송된 메시지 수예요.","format":"int32","type":"integer"}},"required":["detail","fail","msgCount","sentAlimtalkCount","sentFriendtalkCount","sentInboxCount","sentPushCount","sentSmsCount"]},"AppendContextResultDetailResponse":{"description":"각 채널별로 전송된 메시지들의 상세 리스트예요. 어떤 메시지가 어떤 채널에서 성공 또는 실패했는지 알 수 있어요.","properties":{"sentAlimtalk":{"description":"알림톡으로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentFriendtalk":{"description":"친구톡으로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentInbox":{"description":"인박스(Inbox)로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentPush":{"description":"푸시(Push)로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentSms":{"description":"SMS로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"}},"required":["sentAlimtalk","sentFriendtalk","sentInbox","sentPush","sentSms"]},"SentContentResponse":{"description":"발송된 메시지 한 건에 대한 정보예요.","properties":{"contentId":{"description":"발송된 메시지의 고유 ID예요.","type":"string"},"reachedFailReason":{"description":"메시지 도달에 실패한 경우 실패 사유가 담겨 있어요.","type":"string"}},"required":["contentId"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/messenger/send-bulk-message":{"post":{"description":"여러 사용자에게 동일한 템플릿으로 메시지를 대량 발송해요. 최소 50건 이상 대량 발송할때 사용해주세요. 한번 요청 시 최대 2,500건까지 발송 가능해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `5004` | 승인되지 않은 메시지 템플릿이에요. 메시지 발송을 하기 위해서는 템플릿 검토 승인이 필요해요. |\n| `4034` | 워크스페이스가 없거나 워크스페이스에 접근할 수 있는 권한이 없어요 |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"sendBulkMessage","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkSendMessageRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessBulkSendMessageResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessBulkSendMessageResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"요청 처리 결과예요. `resultType` 값으로 성공/실패를 구분하세요."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"대량 메시지 발송하기","tags":["push"]}}}}
```

## 테스트 메시지 발송하기

> 이 API는 파트너 앱이 메시지를 수신하고 심사 전 번들이 정상동작하는지 확인할 수 있게 해줘요. 사용자 인증 토큰이 필요하며, 사용자에게 메시지를 전송할 수 있는 scope 권한이 포함돼야 해요. 테스트 발송을 포함해 모든 메시지는 문구 검수를 통해 승인 받은 이후 발송 가능해요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> 이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 \`errorCode\`는 실패로 처리하고 \`reason\` 메시지를 참고하세요.\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"파트너 앱에서 토스 사용자와 메시지를 처리할 때 사용하는 API예요.","name":"push"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"TestSendMessageRequest":{"description":"사용자에게 보낼 메시지를 정의해요. 어떤 메시지 템플릿을 사용할지와 템플릿에 들어갈 데이터를 함께 담아요.","properties":{"context":{"additionalProperties":{"type":"object"},"description":"템플릿에서 사용할 변수값들이에요. 예를 들어 사용자 이름이나 인증번호 같은 값을 넣어요.","type":"object"},"deploymentId":{"description":"메시지 발송 테스트 시 사용할 번들의 식별값이에요. UUID 형식으로 앱인토스 콘솔의 앱 출시 메뉴에 업로드한 번들에서 확인할 수 있어요.","type":"string"},"templateSetCode":{"description":"사용할 메시지 템플릿 코드예요. 사전에 등록한 템플릿 코드 중 하나를 넣어요.","type":"string"}},"required":["context","deploymentId","templateSetCode"],"type":"object"},"TossApiSuccessTestSendMessageResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/TestSendMessageResponse"}},"required":["resultType","success"],"type":"object"},"TestSendMessageResponse":{"description":"채널별로 몇 건의 메시지가 전송됐는지와 성공/실패한 메시지의 상세 정보가 담겨 있어요.","properties":{"detail":{"$ref":"#/components/schemas/AppendContextResultDetailResponse","description":"성공적으로 전송된 메시지들의 상세 정보예요."},"fail":{"$ref":"#/components/schemas/AppendContextResultDetailResponse","description":"전송에 실패한 메시지들의 상세 정보예요."},"msgCount":{"description":"총 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentAlimtalkCount":{"description":"알림톡으로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentFriendtalkCount":{"description":"친구톡으로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentInboxCount":{"description":"인박스(Inbox)로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentPushCount":{"description":"푸시(Push)로 발송된 메시지 수예요.","format":"int32","type":"integer"},"sentSmsCount":{"description":"SMS로 발송된 메시지 수예요.","format":"int32","type":"integer"}},"required":["detail","fail","msgCount","sentAlimtalkCount","sentFriendtalkCount","sentInboxCount","sentPushCount","sentSmsCount"]},"AppendContextResultDetailResponse":{"description":"각 채널별로 전송된 메시지들의 상세 리스트예요. 어떤 메시지가 어떤 채널에서 성공 또는 실패했는지 알 수 있어요.","properties":{"sentAlimtalk":{"description":"알림톡으로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentFriendtalk":{"description":"친구톡으로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentInbox":{"description":"인박스(Inbox)로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentPush":{"description":"푸시(Push)로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"},"sentSms":{"description":"SMS로 발송된 메시지 목록이에요.","items":{"$ref":"#/components/schemas/SentContentResponse"},"type":"array"}},"required":["sentAlimtalk","sentFriendtalk","sentInbox","sentPush","sentSms"]},"SentContentResponse":{"description":"발송된 메시지 한 건에 대한 정보예요.","properties":{"contentId":{"description":"발송된 메시지의 고유 ID예요.","type":"string"},"reachedFailReason":{"description":"메시지 도달에 실패한 경우 실패 사유가 담겨 있어요.","type":"string"}},"required":["contentId"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/messenger/send-test-message":{"post":{"description":"이 API는 파트너 앱이 메시지를 수신하고 심사 전 번들이 정상동작하는지 확인할 수 있게 해줘요. 사용자 인증 토큰이 필요하며, 사용자에게 메시지를 전송할 수 있는 scope 권한이 포함돼야 해요. 테스트 발송을 포함해 모든 메시지는 문구 검수를 통해 승인 받은 이후 발송 가능해요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n이 API는 연동된 내부 시스템의 오류 코드를 그대로 전달할 수 있어요. 문서화되지 않은 `errorCode`는 실패로 처리하고 `reason` 메시지를 참고하세요.\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"sendTestMessage","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestSendMessageRequest"}}},"description":"전송할 메시지의 정보예요","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessTestSendMessageResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessTestSendMessageResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"메시지 전송에 성공했어요"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"테스트 메시지 발송하기","tags":["push"]}}}}
```


# 프로모션(토스 포인트)

## 프로모션 리워드 지급 키 생성하기

> 이 API는 파트너 앱이 토스 사용자에게 토스 포인트를 지급하기 위한 키를 발급해줘요.\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"파트너 앱에서 토스 사용자에게 토스 포인트를 지급할 때 사용하는 API예요.","name":"promotion"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"TossApiSuccessGetKeyResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/GetKeyResponse"}},"required":["resultType","success"],"type":"object"},"GetKeyResponse":{"description":"프로모션 리워드 지급 키 생성 응답이에요.","properties":{"key":{"description":"프로모션 리워드 지급에 사용할 암호화된 키예요.","type":"string"}},"required":["key"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/promotion/execute-promotion/get-key":{"post":{"description":"이 API는 파트너 앱이 토스 사용자에게 토스 포인트를 지급하기 위한 키를 발급해줘요.\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"getKey","responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessGetKeyResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessGetKeyResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"프로모션 리워드 지급 키 생성에 성공했어요"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"프로모션 리워드 지급 키 생성하기","tags":["promotion"]}}}}
```

## 프로모션 리워드 지급하기

> 이 API는 파트너 앱이 토스 유저에게 토스 포인트를 지급할 수 있도록 해줘요\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4000\` | 잘못된 요청입니다. |\
> \| \`4100\` | 프로모션 정보를 찾을 수 없어요 |\
> \| \`4108\` | 프로모션이 승인되지 않았어요 |\
> \| \`4109\` | 프로모션이 실행중이 아니에요 |\
> \| \`4114\` | 프로모션에 설정된 1회 지급 금액을 초과해서 지급할 수 없습니다. |\
> \| \`4112\` | 프로모션 머니가 부족해요 |\
> \| \`4105\` | 프로모션이 종료되어 있어요 |\
> \| \`4113\` | 이미 지급/회수된 내역이에요 |\
> \| \`4110\` | 리워드를 지급/회수할 수 없어요 |\
> \| \`4034\` | 워크스페이스가 없거나 워크스페이스에 접근할 수 있는 권한이 없어요 |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회, 사용자당 분당 20회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"파트너 앱에서 토스 사용자에게 토스 포인트를 지급할 때 사용하는 API예요.","name":"promotion"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"ExecutePromotionRequest":{"description":"프로모션 리워드 지급 요청이에요.","properties":{"amount":{"description":"토스 유저에게 지급할 프로모션 금액이에요","format":"int64","type":"integer"},"key":{"description":"프로모션 리워드 지급 키 생성하기 API를 통해 발급받은 리워드 지급 키예요","type":"string"},"promotionCode":{"description":"콘솔을 통해 생성된 프로모션의 프로모션 코드예요","type":"string"}},"required":["amount","key","promotionCode"],"type":"object"},"TossApiSuccessExecutePromotionResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"$ref":"#/components/schemas/ExecutePromotionResponse"}},"required":["resultType","success"],"type":"object"},"ExecutePromotionResponse":{"description":"프로모션 리워드 지급 응답이에요.","properties":{"key":{"description":"프로모션 리워드 지급에 사용된 키예요.","type":"string"}},"required":["key"]},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/promotion/execute-promotion":{"post":{"description":"이 API는 파트너 앱이 토스 유저에게 토스 포인트를 지급할 수 있도록 해줘요\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4000` | 잘못된 요청입니다. |\n| `4100` | 프로모션 정보를 찾을 수 없어요 |\n| `4108` | 프로모션이 승인되지 않았어요 |\n| `4109` | 프로모션이 실행중이 아니에요 |\n| `4114` | 프로모션에 설정된 1회 지급 금액을 초과해서 지급할 수 없습니다. |\n| `4112` | 프로모션 머니가 부족해요 |\n| `4105` | 프로모션이 종료되어 있어요 |\n| `4113` | 이미 지급/회수된 내역이에요 |\n| `4110` | 리워드를 지급/회수할 수 없어요 |\n| `4034` | 워크스페이스가 없거나 워크스페이스에 접근할 수 있는 권한이 없어요 |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n**요청 한도**: 앱당 분당 3,000회, 사용자당 분당 20회","operationId":"executePromotion","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecutePromotionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessExecutePromotionResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessExecutePromotionResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"성공적으로 토스유저에게 토스 포인트를 지급했어요"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"프로모션 리워드 지급하기","tags":["promotion"]}}}}
```

## 프로모션 토스포인트 지급 결과 조회하기

> 이 API는 파트너 앱이 토스 유저에게 지급한 토스포인트를 조회할 수 있도록 해줘요\
> \
> \### 비즈니스 오류 코드\
> \
> 아래 오류는 HTTP 200과 \`resultType: FAIL\`로 응답해요.\
> \
> \| errorCode | 설명 |\
> \| --- | --- |\
> \| \`4000\` | 잘못된 요청입니다. |\
> \| \`4100\` | 프로모션 정보를 찾을 수 없어요 |\
> \| \`4111\` | 리워드 지급내역을 찾을 수 없어요 |\
> \| \`4010\` | 인증 정보를 찾을 수 없어요. |\
> \| \`4095\` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\
> \
> \*\*요청 한도\*\*: 앱당 분당 3,000회

```json
{"openapi":"3.1.0","info":{"title":"Apps in Toss 파트너 API","version":"1.0.0"},"tags":[{"description":"파트너 앱에서 토스 사용자에게 토스 포인트를 지급할 때 사용하는 API예요.","name":"promotion"}],"servers":[{"description":"운영 (간편 로그인·메시지 발송·토스 포인트 지급 등)","url":"https://apps-in-toss-api.toss.im"}],"security":[{"mutualTLS":[]}],"components":{"securitySchemes":{"mutualTLS":{"description":"파트너에게 발급된 클라이언트 인증서 기반 mTLS 인증이에요. 인증서의 CN으로 미니앱을 식별해요. 인증서 발급·관리 방법은 [서버 API 이용하기](https://developers-apps-in-toss.toss.im/documentation/integration/server-api) 문서를 참고하세요.","type":"mutualTLS"}},"schemas":{"GetExecutionResultRequest":{"description":"프로모션 실행 결과 조회 요청이에요.","properties":{"key":{"description":"프로모션 리워드 지급 키 생성하기 API를 통해 발급받은 리워드 지급 키예요","type":"string"},"promotionCode":{"description":"콘솔을 통해 생성된 프로모션의 프로모션 코드예요","type":"string"}},"required":["key","promotionCode"],"type":"object"},"TossApiSuccessGetExecutionResultResponse":{"description":"성공 응답 봉투예요.","properties":{"resultType":{"description":"처리 결과예요. 성공이면 `SUCCESS`예요.","enum":["SUCCESS"],"type":"string"},"success":{"description":"프로모션 실행 결과 조회 응답이에요. PENDING: 리워드 지급 요청이 접수되어 처리 중이에요, SUCCESS: 리워드 지급이 완료됐어요, FAILED: 리워드 지급 요청이 실패해서 사용한 예산이 롤백됐어요.","enum":["PENDING","SUCCESS","FAILED"],"type":"string"}},"required":["resultType","success"],"type":"object"},"TossApiFail":{"description":"실패 응답 봉투예요. 비즈니스 오류는 HTTP 200으로 응답하니 `resultType`을 반드시 확인하세요.","properties":{"error":{"$ref":"#/components/schemas/TossApiError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiValidationFail":{"description":"요청 필드 검증에 실패했을 때의 응답이에요.","properties":{"error":{"$ref":"#/components/schemas/TossApiValidationFailError"},"resultType":{"description":"처리 결과예요. `SUCCESS`가 아닌 값은 모두 실패로 처리하세요. 일반적으로 `FAIL`이에요.","enum":["FAIL","HTTP_TIMEOUT","NETWORK_ERROR","EXECUTION_FAIL","INTERRUPTED","INTERNAL_ERROR"],"type":"string"},"success":{"description":"실패 시 항상 null이에요."}},"required":["error","resultType"],"type":"object"},"TossApiValidationFailError":{"description":"오류 상세 정보예요.","properties":{"data":{"description":"오류 부가 정보예요. 요청 한도 초과 시 `retryAfterSeconds`가 담겨요.","properties":{"errorDetails":{"description":"필드별 검증 실패 상세 목록이에요.","items":{"$ref":"#/components/schemas/TossApiFieldError"},"type":"array"}},"type":"object"},"errorCode":{"description":"오류 코드예요. 각 API의 비즈니스 오류 코드 표를 참고하세요.","type":"string"},"errorType":{"description":"내부 오류 분류 값이에요. 오류 구분에는 `errorCode`를 사용하세요.","format":"int32","type":"integer"},"reason":{"description":"사람이 읽을 수 있는 오류 설명이에요.","type":"string"},"title":{"description":"오류 제목이에요. 대부분 null이에요.","type":"string"}},"required":["errorCode","reason"],"type":"object"},"TossApiFieldError":{"description":"필드별 검증 실패 상세 목록이에요.","properties":{"field":{"description":"검증에 실패한 필드 이름이에요.","type":"string"},"message":{"description":"검증 실패 사유예요.","type":"string"},"rejectedValue":{"description":"거부된 입력 값이에요."}},"type":"object"}}},"paths":{"/api-partner/v1/apps-in-toss/promotion/execution-result":{"post":{"description":"이 API는 파트너 앱이 토스 유저에게 지급한 토스포인트를 조회할 수 있도록 해줘요\n\n### 비즈니스 오류 코드\n\n아래 오류는 HTTP 200과 `resultType: FAIL`로 응답해요.\n\n| errorCode | 설명 |\n| --- | --- |\n| `4000` | 잘못된 요청입니다. |\n| `4100` | 프로모션 정보를 찾을 수 없어요 |\n| `4111` | 리워드 지급내역을 찾을 수 없어요 |\n| `4010` | 인증 정보를 찾을 수 없어요. |\n| `4095` | 요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요. |\n\n**요청 한도**: 앱당 분당 3,000회","operationId":"getExecutionResult","parameters":[{"description":"사용자를 인증하기 위한 키예요. [사용자 정보 받기](https://developers-apps-in-toss.toss.im/documentation/api/toss-login#get-api-partner-v1-apps-in-toss-user-oauth2-login-me) API를 통해 획득할 수 있어요","in":"header","name":"x-toss-user-key","required":false,"schema":{"type":"string"}},{"description":"사용자를 인증하기 위한 키예요. 미니앱 SDK의 [User.getAnonymousKey](https://developers-apps-in-toss.toss.im/documentation/sdk/domains-api/user/user.getanonymouskey) 함수로 발급받을 수 있어요","in":"header","name":"x-anon-key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetExecutionResultRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"discriminator":{"mapping":{"FAIL":"#/components/schemas/TossApiFail","SUCCESS":"#/components/schemas/TossApiSuccessGetExecutionResultResponse"},"propertyName":"resultType"},"oneOf":[{"$ref":"#/components/schemas/TossApiSuccessGetExecutionResultResponse"},{"$ref":"#/components/schemas/TossApiFail"}]}}},"description":"성공적으로 프로모션 지급 결과를 조회했어요"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiValidationFail"}}},"description":"요청 본문이 형식에 맞지 않아요. `error.data.errorDetails`에서 필드별 상세를 확인하세요."},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TossApiFail"}}},"description":"분류되지 않은 서버 오류예요. 계속 실패하면 파트너 지원 채널로 문의해 주세요."}},"summary":"프로모션 토스포인트 지급 결과 조회하기","tags":["promotion"]}}}}
```


# Client SDK

앱인토스 웹 미니앱에서 사용하는 공개 API 표면이에요. 디렉터리 구조가 곧 이 레퍼런스의 목차이고, export되는 API는 전부 같은 depth의 REFERENCE.md로 문서화돼요.

### 구성

| 영역                                         | 설명                                                               |
| ------------------------------------------ | ---------------------------------------------------------------- |
| [domains/](/documentation/sdk/domains-api) | `도메인.동작` 인터페이스 19종 + 각 멤버의 함수형(flat) API                         |
| [events/](/documentation/sdk/events)       | 네이티브 이벤트 구독 버스 (`graniteEvent` · `tdsEvent` · `appsInTossEvent`) |

### 사용 방식

```js
// 도메인 인터페이스
import { Device } from "@apps-in-toss/web-framework";
await Device.openCamera();

// 함수형(flat) API — v2와 동일
import { openCamera } from "@apps-in-toss/web-framework";
await openCamera();
```


# Domains API

함수형(flat) API를 **도메인.동작** 형태로 묶은 공개 인터페이스예요. 함수형 API는 그대로 사용할 수 있고, 도메인 인터페이스는 같은 구현을 도메인 단위로 묶어 추가로 제공해요.

문서 구조는 이 디렉터리 구조를 그대로 따라요 — 도메인 디렉터리의 REFERENCE.md가 개요, `도메인/멤버/REFERENCE.md`가 멤버 상세예요.

### 도메인 목록

| 도메인                                                         | 설명                     |
| ----------------------------------------------------------- | ---------------------- |
| [Ads](/documentation/sdk/domains-api/ads)                   | 전면·AdMob·배너 광고         |
| [Analytics](/documentation/sdk/domains-api/analytics)       | 분석 로그·스크린 로깅           |
| [Clipboard](/documentation/sdk/domains-api/clipboard)       | 클립보드 읽기/쓰기             |
| [Device](/documentation/sdk/domains-api/device)             | 디바이스·권한·위치·미디어         |
| [Environment](/documentation/sdk/domains-api/environment)   | 실행 환경·버전·네트워크          |
| [File](/documentation/sdk/domains-api/file)                 | 파일 저장·PDF 뷰어           |
| [Game](/documentation/sdk/domains-api/game)                 | 게임센터 리더보드·프로필          |
| [IAP](/documentation/sdk/domains-api/iap)                   | 인앱결제                   |
| [Notification](/documentation/sdk/domains-api/notification) | 알림 동의                  |
| [Permissions](/documentation/sdk/domains-api/permissions)   | 디바이스 권한 조회·요청          |
| [Promotion](/documentation/sdk/domains-api/promotion)       | 프로모션 리워드·친구 초대         |
| [Review](/documentation/sdk/domains-api/review)             | 인앱 리뷰 요청               |
| [SafeArea](/documentation/sdk/domains-api/safearea)         | Safe Area insets 조회·구독 |
| [Screen](/documentation/sdk/domains-api/screen)             | 화면 제어                  |
| [Share](/documentation/sdk/domains-api/share)               | 공유 링크·공유 시트            |
| [Storage](/documentation/sdk/domains-api/storage)           | 로컬 저장소                 |
| [TossAuth](/documentation/sdk/domains-api/tossauth)         | 토스 로그인·서명              |
| [TossPay](/documentation/sdk/domains-api/tosspay)           | 토스페이 결제 인증             |
| [User](/documentation/sdk/domains-api/user)                 | 사용자 키·동의 데이터·연령        |

### 함수형 API와의 관계

```js
// 함수형(flat) API — v2와 동일
import { getAnonymousKey } from "@apps-in-toss/web-framework";
await getAnonymousKey();

// 도메인 인터페이스 — 같은 구현을 도메인.동작으로
import { User } from "@apps-in-toss/web-framework";
await User.getAnonymousKey();
```

미지원 토스앱 버전에서 도메인 멤버를 호출하면 `undefined`를 반환하지 않고 `UNSUPPORTED_APP_VERSION` 에러를 **throw**해요(함수형 API는 기존(v2) 동작을 그대로 유지해요). 해당 멤버는 `isSupported()`로 미리 확인할 수 있어요.


# Ads

전면 광고, Google AdMob, 인라인 배너 광고(TossAds) 기능을 제공해요.

### API 목록

| API                                                                                                             | 설명                       |
| --------------------------------------------------------------------------------------------------------------- | ------------------------ |
| [`loadFullScreenAd`](/documentation/sdk/domains-api/ads/loadfullscreenad)                                       | 전면 광고를 미리 불러와요           |
| [`showFullScreenAd`](/documentation/sdk/domains-api/ads/showfullscreenad)                                       | 미리 불러온 전면 광고를 노출해요       |
| [`GoogleAdMob.loadAppsInTossAdMob`](/documentation/sdk/domains-api/ads/googleadmob.loadappsintossadmob)         | Google AdMob 광고를 미리 불러와요 |
| [`GoogleAdMob.showAppsInTossAdMob`](/documentation/sdk/domains-api/ads/googleadmob.showappsintossadmob)         | 미리 불러온 AdMob 광고를 노출해요    |
| [`GoogleAdMob.isAppsInTossAdMobLoaded`](/documentation/sdk/domains-api/ads/googleadmob.isappsintossadmobloaded) | AdMob 광고 로드 여부를 확인해요     |
| [`TossAds`](/documentation/sdk/domains-api/ads/tossads)                                                         | 인라인 배너 광고 SDK예요          |


# GoogleAdMob.isAppsInTossAdMobLoaded

### 기능 설명

해당 광고 그룹의 AdMob 광고가 잘 불러와졌는지 확인해요. 광고를 노출하기 전에 로드 상태를 점검할 때 사용해요.

호출 전에 `GoogleAdMob.isAppsInTossAdMobLoaded.isSupported()`로 지원 여부를 확인할 수 있어요.

### 타입

```ts
GoogleAdMob.isAppsInTossAdMobLoaded(options: IsAdMobLoadedOptions): Promise<boolean>;
```

**Params**

```ts
type IsAdMobLoadedOptions = {
  /** 확인할 광고 그룹 ID예요. */
  adGroupId: string;
};
```

**Response**

광고가 로드돼 있으면 `true`를 반환해요.

### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

async function checkAdLoaded() {
  try {
    const isLoaded = await GoogleAdMob.isAppsInTossAdMobLoaded({
      adGroupId: "AD_GROUP_ID",
    });
    console.log(isLoaded ? "광고 준비 완료" : "광고가 아직 준비되지 않았어요.");
  } catch (error) {
    console.error(error);
  }
}
```


# GoogleAdMob.loadAppsInTossAdMob

### 기능 설명

Google AdMob 광고를 미리 불러와서, 광고가 필요한 시점에 바로 보여줄 수 있도록 준비해요. 광고가 성공적으로 로드되면 `onEvent` 콜백으로 `type: 'loaded'` 이벤트가 전달돼요.

호출 전에 `GoogleAdMob.loadAppsInTossAdMob.isSupported()`로 지원 여부를 확인할 수 있어요.

### 타입

```ts
GoogleAdMob.loadAppsInTossAdMob(params: LoadAdMobParams): () => void;
```

**Params**

```ts
interface LoadAdMobParams {
  options: LoadAdMobOptions;
  onEvent: (event: LoadAdMobEvent) => void;
  onError: (error: unknown) => void;
}

type LoadAdMobOptions = {
  /** 광고 그룹 ID예요. 앱인토스 콘솔에서 발급받은 값을 넣어주세요. */
  adGroupId: string;
  /** 광고 요청 출처를 구분하는 값이에요. */
  referrer?: string | null;
};

type LoadAdMobEvent = {
  type: "loaded";
  data: LoadAdMobResult;
};

type LoadAdMobResult = {
  /** 광고 그룹 ID예요. */
  adGroupId: string;
  /** 광고 ID예요. */
  adUnitId: string;
  /** 광고 로드 응답 정보예요. */
  responseInfo: {
    /** 광고 네트워크 응답 정보 배열이에요. */
    adNetworkInfoArray: AdNetworkResponseInfo[];
    /** 로드된 광고 네트워크 응답 정보예요. */
    loadedAdNetworkInfo: AdNetworkResponseInfo | null;
    /** 광고 응답을 식별하는 ID예요. */
    responseId: string | null;
  };
};

type AdNetworkResponseInfo = {
  /** 광고 소스 ID예요. */
  adSourceId: string;
  /** 광고 소스 이름이에요. */
  adSourceName: string;
  /** 광고 소스 인스턴스 ID예요. */
  adSourceInstanceId: string;
  /** 광고 소스 인스턴스 이름이에요. */
  adSourceInstanceName: string;
  /** 광고 네트워크 클래스 이름이에요. */
  adNetworkClassName: string | null;
};
```

**Response**

구독 해제 함수(`() => void`)를 반환해요. 호출하면 이후 도착한 이벤트와 에러가 콜백으로 전달되지 않아요.

### 에러

광고 로드에 실패하면 `onError` 콜백으로 에러가 전달돼요. 토스앱 웹뷰 환경이 아닌 곳에서 호출하면 호출 시점에 에러가 발생해요.

### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

function loadAd() {
  if (!GoogleAdMob.loadAppsInTossAdMob.isSupported()) {
    console.warn("현재 환경에서는 AdMob 광고를 사용할 수 없어요.");
    return;
  }

  GoogleAdMob.loadAppsInTossAdMob({
    options: { adGroupId: "AD_GROUP_ID" },
    onEvent: (event) => {
      if (event.type === "loaded") {
        console.log("광고 로드 완료:", event.data.responseInfo.responseId);
      }
    },
    onError: (error) => {
      console.error("광고 로드 실패:", error);
    },
  });
}
```


# GoogleAdMob.showAppsInTossAdMob

### 기능 설명

`GoogleAdMob.loadAppsInTossAdMob`으로 미리 불러온 AdMob 광고를 사용자에게 노출해요. 광고 표시 과정에서 발생하는 이벤트가 `onEvent` 콜백으로 순차 전달돼요.

이벤트 스타일로 동작하며, 반환값으로 앱브릿지 콜백을 해제하는 함수를 받아요. 광고가 닫히면 꼭 해제해 주세요.

호출 전에 `GoogleAdMob.showAppsInTossAdMob.isSupported()`로 지원 여부를 확인할 수 있어요.

### 타입

```ts
GoogleAdMob.showAppsInTossAdMob(params: ShowAdMobParams): () => void;
```

**Params**

```ts
interface ShowAdMobParams {
  options: ShowAdMobOptions;
  onEvent: (event: ShowAdMobEvent) => void;
  onError: (error: unknown) => void;
}

type ShowAdMobOptions = {
  /** loadAppsInTossAdMob에서 사용한 것과 같은 광고 그룹 ID예요. */
  adGroupId: string;
};

type ShowAdMobEvent =
  | { type: "requested" }
  | { type: "show" }
  | { type: "impression" }
  | { type: "clicked" }
  | { type: "dismissed" }
  | { type: "failedToShow" }
  | {
      type: "userEarnedReward";
      data: { unitType: string; unitAmount: number };
    };
```

이벤트 `type` 값:

| type               | 설명                          |
| ------------------ | --------------------------- |
| `requested`        | 광고 표시 요청이 성공했어요             |
| `show`             | 광고가 화면에 표시됐어요               |
| `impression`       | 광고 노출이 기록됐어요 (수익 발생 시점)     |
| `clicked`          | 사용자가 광고를 클릭했어요              |
| `dismissed`        | 사용자가 광고를 닫았어요               |
| `failedToShow`     | 광고 표시에 실패했어요                |
| `userEarnedReward` | 사용자가 리워드를 획득했어요 (리워드 광고 전용) |

**Response**

앱브릿지 콜백을 해제하는 함수(`() => void`)를 반환해요.

### 에러

광고 표시에 실패하면 `failedToShow` 이벤트 또는 `onError` 콜백으로 전달돼요. 리워드 광고는 `userEarnedReward` 이벤트가 발생했을 때만 리워드를 지급해야 해요. `dismissed`만으로는 사용자가 광고를 끝까지 봤는지 알 수 없어요.

### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

const button = document.querySelector("#show-ad");

button.addEventListener("click", () => {
  const cleanup = GoogleAdMob.showAppsInTossAdMob({
    options: { adGroupId: "AD_GROUP_ID" },
    onEvent: (event) => {
      switch (event.type) {
        case "userEarnedReward":
          console.log(
            "리워드 획득:",
            event.data.unitType,
            event.data.unitAmount,
          );
          break;
        case "dismissed":
          console.log("광고가 닫혔어요.");
          cleanup();
          break;
        case "failedToShow":
          console.error("광고 표시에 실패했어요.");
          cleanup();
          break;
      }
    },
    onError: (error) => {
      console.error(error);
      cleanup();
    },
  });
});
```


# TossAds

인라인 배너 광고 SDK예요. `initialize`로 초기화한 뒤 `attachBanner`로 슬롯에 배너를 붙여요.

### TossAds.initialize

#### 기능 설명

토스 배너 광고 SDK를 초기화해요. 초기화는 비동기로 진행되며 결과는 콜백으로 전달돼요. 배너를 부착하기 전에 반드시 한 번 호출해야 해요. 이미 초기화된 상태에서 다시 호출하면 중복 초기화 없이 `onInitialized` 콜백이 바로 호출돼요(멱등).

토스앱 `5.239.0` 이상에서 사용할 수 있어요. 호출 전에 `TossAds.initialize.isSupported()`로 지원 여부를 확인할 수 있어요.

#### 타입

```ts
TossAds.initialize(options: TossAdsInitializeOptions): void;
```

**Params**

```ts
interface TossAdsInitializeOptions {
  callbacks?: {
    /** 초기화 성공 시 호출돼요. */
    onInitialized?: () => void;
    /** 초기화 실패 시 호출돼요. */
    onInitializationFailed?: (error: Error) => void;
  };
}
```

**Response**

없음.

#### 에러

SDK 스크립트 로드나 초기화에 실패하면 `onInitializationFailed` 콜백으로 에러가 전달돼요.

#### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

function initializeAds() {
  if (!TossAds.initialize.isSupported()) {
    console.warn("현재 환경에서는 배너 광고를 사용할 수 없어요.");
    return;
  }

  TossAds.initialize({
    callbacks: {
      onInitialized: () => {
        console.log("배너 광고 SDK 초기화 완료");
      },
      onInitializationFailed: (error) => {
        console.error("배너 광고 SDK 초기화 실패:", error);
      },
    },
  });
}
```

### TossAds.attach

> **deprecated**: `TossAds.attach`는 더 이상 권장되지 않아요. `TossAds.attachBanner`를 사용하세요.

#### 기능 설명

특정 DOM 요소에 배너 광고를 부착해요. `TossAds.initialize`를 먼저 호출해서 SDK를 초기화한 후에 사용해야 해요.

토스앱 `5.239.0` 이상에서 사용할 수 있어요. 호출 전에 `TossAds.attach.isSupported()`로 지원 여부를 확인할 수 있어요.

#### 타입

```ts
TossAds.attach(
  adGroupId: string,           // 광고 그룹 ID (앱인토스 콘솔에서 발급)
  target: string | HTMLElement, // DOM 셀렉터 또는 HTMLElement
  options?: TossAdsAttachOptions,
): void;
```

**Params**

```ts
interface TossAdsAttachOptions {
  /** 테마 설정이에요. 생략하면 시스템 기본값을 따라요. */
  theme?: "light" | "dark";
  /** CSS padding 값이에요 (예: '20px', '10px 20px'). List Banner 타입에만 적용돼요. */
  padding?: string;
  /** 배너 이벤트 콜백이에요. */
  callbacks?: TossAdsBannerSlotCallbacks;
}
```

**Response**

없음.

#### 에러

빈 `adGroupId`, 초기화 전 호출, 존재하지 않는 target 등 부착에 실패하면 `callbacks.onAdFailedToRender` 콜백으로 에러가 전달돼요.

#### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

let slotId = null;

TossAds.attach("AD_GROUP_ID", "#banner-container", {
  padding: "20px",
  callbacks: {
    onAdRendered: (payload) => {
      slotId = payload.slotId; // 나중에 TossAds.destroy에 사용
    },
    onAdFailedToRender: (payload) => {
      console.error("광고 렌더링 실패:", payload.error.message);
    },
  },
});
```

### TossAds.attachBanner

#### 기능 설명

스타일 프리셋(배경색, 라운딩, 패딩)이 적용된 배너 광고를 DOM 요소에 부착해요. `TossAds.initialize`를 먼저 호출해서 SDK를 초기화한 후에 사용해야 해요.

같은 요소에 `attachBanner`를 중복 호출하면 새로 부착하지 않고 기존 배너의 핸들을 그대로 반환해요. 다른 옵션으로 다시 부착하려면 기존 핸들의 `destroy()`를 먼저 호출해야 해요.

토스앱 `5.239.0` 이상에서 사용할 수 있어요. 호출 전에 `TossAds.attachBanner.isSupported()`로 지원 여부를 확인할 수 있어요.

#### 타입

```ts
TossAds.attachBanner(
  adGroupId: string,           // 광고 그룹 ID (앱인토스 콘솔에서 발급)
  target: string | HTMLElement, // DOM 셀렉터 또는 HTMLElement
  options?: TossAdsAttachBannerOptions,
): TossAdsAttachBannerResult;
```

**Params**

```ts
interface TossAdsAttachBannerOptions {
  /** 테마 오버라이드예요. 기본값은 'auto'로, 시스템 다크 모드를 따라요. */
  theme?: "auto" | "light" | "dark";
  /** 배경 색상 톤이에요. 기본값은 'blackAndWhite'예요. */
  tone?: "blackAndWhite" | "grey";
  /** 배너 형태예요. 'card'는 라운딩과 좌우 여백이 있는 카드형, 'expanded'는 가로로 꽉 찬 형태예요. 기본값은 'expanded'예요. */
  variant?: "card" | "expanded";
  /** 배너 이벤트 콜백이에요. */
  callbacks?: TossAdsBannerSlotCallbacks;
}

interface TossAdsBannerSlotCallbacks {
  /** 광고가 렌더링됐을 때 호출돼요. slotId를 여기서 받아 저장할 수 있어요. */
  onAdRendered?: (payload: TossAdsBannerSlotEventPayload) => void;
  /** 광고가 화면에 노출됐을 때 호출돼요. */
  onAdViewable?: (payload: TossAdsBannerSlotEventPayload) => void;
  /** 사용자가 광고를 클릭했을 때 호출돼요. */
  onAdClicked?: (payload: TossAdsBannerSlotEventPayload) => void;
  /** 광고 노출이 기록됐을 때 호출돼요. (수익 발생 시점) */
  onAdImpression?: (payload: TossAdsBannerSlotEventPayload) => void;
  /** 광고 렌더링에 실패했을 때 호출돼요. */
  onAdFailedToRender?: (payload: TossAdsBannerSlotErrorPayload) => void;
  /** 표시할 광고가 없을 때 호출돼요. */
  onNoFill?: (payload: {
    slotId: string;
    adGroupId: string;
    adMetadata: Record<string, never>;
  }) => void;
}

interface TossAdsBannerSlotEventPayload {
  slotId: string; // 생성된 슬롯 ID. TossAds.destroy에 전달할 수 있어요.
  adGroupId: string; // 광고 그룹 ID
  adMetadata: {
    creativeId: string;
    requestId: string;
  };
}

interface TossAdsBannerSlotErrorPayload {
  slotId: string;
  adGroupId: string;
  adMetadata: Record<string, never>;
  error: { code: number; message: string; domain?: string };
}
```

**Response**

```ts
interface TossAdsAttachBannerResult {
  /** 부착한 배너와 래퍼 요소를 함께 제거해요. */
  destroy: () => void;
}
```

#### 에러

빈 `adGroupId`, 초기화 전 호출, 존재하지 않는 target 등 부착에 실패하면 `callbacks.onAdFailedToRender` 콜백으로 에러가 전달되고, 아무 동작도 하지 않는 `destroy`를 가진 결과가 반환돼요.

#### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

const container = document.querySelector("#banner-container");

const banner = TossAds.attachBanner("AD_GROUP_ID", container, {
  variant: "card",
  tone: "grey",
  callbacks: {
    onAdRendered: (payload) => {
      console.log("광고 렌더링 완료:", payload.slotId);
    },
    onAdImpression: () => {
      console.log("광고 노출 기록됨 (수익 발생)");
    },
    onNoFill: () => {
      console.warn("표시할 광고가 없어요.");
    },
    onAdFailedToRender: (payload) => {
      console.error("광고 렌더링 실패:", payload.error.message);
    },
  },
});

// 화면을 떠날 때 배너 제거
window.addEventListener("pagehide", () => {
  banner.destroy();
});
```

### TossAds.destroy

#### 기능 설명

특정 슬롯 ID의 배너를 제거해요. `slotId`는 배너 콜백의 `payload.slotId`로 받을 수 있어요. SDK가 초기화되지 않았으면 아무 동작도 하지 않아요.

토스앱 `5.239.0` 이상에서 사용할 수 있어요. 호출 전에 `TossAds.destroy.isSupported()`로 지원 여부를 확인할 수 있어요.

#### 타입

```ts
TossAds.destroy(slotId: string): void;
```

**Params**

제거할 슬롯 ID예요.

**Response**

없음.

#### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

let slotId = null;

TossAds.attachBanner("AD_GROUP_ID", "#banner-container", {
  callbacks: {
    onAdRendered: (payload) => {
      slotId = payload.slotId;
    },
  },
});

// 특정 배너 제거
function removeBanner() {
  if (slotId) {
    TossAds.destroy(slotId);
    slotId = null;
  }
}
```

### TossAds.destroyAll

#### 기능 설명

초기화된 모든 배너 슬롯을 한 번에 제거해요. `attachBanner`로 부착한 배너의 내부 상태도 함께 초기화되므로, 이후 같은 요소에 다시 부착할 수 있어요. SDK가 초기화되지 않았으면 아무 동작도 하지 않아요.

토스앱 `5.239.0` 이상에서 사용할 수 있어요. 호출 전에 `TossAds.destroyAll.isSupported()`로 지원 여부를 확인할 수 있어요.

#### 타입

```ts
TossAds.destroyAll(): void;
```

**Params**

없음

**Response**

없음.

#### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

// 페이지를 떠날 때 모든 배너 제거
window.addEventListener("pagehide", () => {
  TossAds.destroyAll();
});
```


# loadFullScreenAd

### 기능 설명

통합 전면 광고를 미리 불러와요. Toss Ad를 우선 표시하고, 사용할 수 없는 환경에서는 자동으로 AdMob으로 전환돼서 안정적으로 광고를 노출할 수 있어요. 광고를 표시하기 전에 반드시 호출해야 해요.

토스앱 버전에 따라 다르게 동작해요:

| 토스앱 버전                       | 지원 기능                                                |
| ---------------------------- | ---------------------------------------------------- |
| `5.239.0` 이상                 | Toss Ad + AdMob 통합 (Toss Ad 우선, 불가능하면 AdMob으로 자동 전환) |
| `5.227.0` 이상 \~ `5.239.0` 미만 | AdMob 광고만 표시                                         |
| `5.227.0` 미만                 | 미지원                                                  |

호출 전에 `loadFullScreenAd.isSupported()`로 지원 여부를 확인할 수 있어요.

### 타입

```ts
loadFullScreenAd(params: LoadFullScreenAdParams): () => void;
```

**Params**

```ts
type LoadFullScreenAdParams = {
  options: LoadFullScreenAdOptions;
  onEvent: (event: LoadFullScreenAdEvent) => void;
  onError: (error: unknown) => void;
};

type LoadFullScreenAdOptions = {
  /** 광고 그룹 ID예요. 앱인토스 콘솔에서 발급받은 값을 넣어주세요. */
  adGroupId: string;
};

type LoadFullScreenAdEvent = {
  type: "loaded";
};
```

**Response**

앱브릿지 콜백을 해제하는 함수(`() => void`)를 반환해요.

### 에러

광고 로드에 실패하면 `onError` 콜백으로 에러가 전달돼요. 미지원 버전에서 호출하면 `onError`로 에러가 전달되니, 호출 전에 `isSupported()`로 확인하세요.

### 예시 코드

```js
import { Ads } from "@apps-in-toss/web-framework";

let isAdLoaded = false;

function prepareAd() {
  if (!loadFullScreenAd.isSupported()) {
    console.warn("현재 환경에서는 전면 광고를 사용할 수 없어요.");
    return;
  }

  const cleanup = loadFullScreenAd({
    options: { adGroupId: "AD_GROUP_ID" },
    onEvent: (event) => {
      if (event.type === "loaded") {
        console.log("광고 로드 완료");
        isAdLoaded = true;
        cleanup();
      }
    },
    onError: (error) => {
      console.error("광고 로드 실패:", error);
      cleanup();
    },
  });
}
```


# showFullScreenAd

### 기능 설명

`loadFullScreenAd`로 미리 불러온 통합 전면 광고를 사용자에게 노출해요. `loadFullScreenAd`에서 사용한 것과 같은 `adGroupId`를 전달해야 해요. 이미 표시한 광고는 다시 표시할 수 없으므로, 광고가 닫힌 뒤에는 새로 로드해야 해요.

토스앱 `5.227.0` 이상에서 사용할 수 있어요 (`5.239.0` 이상은 Toss Ad + AdMob 통합, 그 미만은 AdMob 전용). 호출 전에 `showFullScreenAd.isSupported()`로 지원 여부를 확인할 수 있어요.

### 타입

```ts
showFullScreenAd(params: ShowFullScreenAdParams): () => void;
```

**Params**

```ts
type ShowFullScreenAdParams = {
  options: ShowFullScreenAdOptions;
  onEvent: (event: ShowFullScreenAdEvent) => void;
  onError: (error: unknown) => void;
};

type ShowFullScreenAdOptions = {
  /** loadFullScreenAd에서 사용한 것과 같은 광고 그룹 ID예요. */
  adGroupId: string;
};

type ShowFullScreenAdEvent =
  | { type: "requested" }
  | { type: "show" }
  | { type: "impression" }
  | { type: "clicked" }
  | { type: "dismissed" }
  | { type: "failedToShow" }
  | {
      type: "userEarnedReward";
      data: { unitType: string; unitAmount: number };
    };
```

이벤트 `type` 값:

| type               | 설명                          |
| ------------------ | --------------------------- |
| `requested`        | 광고 표시 요청이 성공했어요             |
| `show`             | 광고가 화면에 표시됐어요               |
| `impression`       | 광고 노출이 기록됐어요 (수익 발생 시점)     |
| `clicked`          | 사용자가 광고를 클릭했어요              |
| `dismissed`        | 사용자가 광고를 닫았어요               |
| `failedToShow`     | 광고 표시에 실패했어요                |
| `userEarnedReward` | 사용자가 리워드를 획득했어요 (리워드 광고 전용) |

**Response**

앱브릿지 콜백을 해제하는 함수(`() => void`)를 반환해요.

### 에러

광고 표시에 실패하면 `failedToShow` 이벤트 또는 `onError` 콜백으로 전달돼요. 리워드 광고는 `userEarnedReward` 이벤트가 발생했을 때만 리워드를 지급해야 해요.

### 예시 코드

```js
import {
  loadFullScreenAd,
  showFullScreenAd,
} from "@apps-in-toss/web-framework";

const AD_GROUP_ID = "AD_GROUP_ID";
const button = document.querySelector("#show-full-screen-ad");

button.addEventListener("click", () => {
  const cleanup = showFullScreenAd({
    options: { adGroupId: AD_GROUP_ID },
    onEvent: (event) => {
      switch (event.type) {
        case "impression":
          console.log("광고 노출 기록됨 (수익 발생)");
          break;
        case "userEarnedReward":
          console.log(
            "리워드 획득:",
            event.data.unitType,
            event.data.unitAmount,
          );
          break;
        case "dismissed":
          console.log("광고가 닫혔어요. 다음 광고를 새로 로드하세요.");
          cleanup();
          break;
        case "failedToShow":
          console.error("광고 표시에 실패했어요.");
          cleanup();
          break;
      }
    },
    onError: (error) => {
      console.error(error);
      cleanup();
    },
  });
});
```


# Analytics

화면 진입·노출·클릭 등 사용자 행동을 기록하고 분석하는 기능을 제공해요.

### API 목록

| API                    | 설명                      | 상세                                                                         |
| ---------------------- | ----------------------- | -------------------------------------------------------------------------- |
| `Analytics.log`        | 앱에서 발생하는 이벤트를 기록해요      | [REFERENCE](/documentation/sdk/domains-api/analytics/analytics.log)        |
| `Analytics.screen`     | 화면 진입 시점의 분석 로그를 남겨요    | [REFERENCE](/documentation/sdk/domains-api/analytics/analytics.screen)     |
| `Analytics.impression` | 요소가 노출되면 분석 로그를 남겨요     | [REFERENCE](/documentation/sdk/domains-api/analytics/analytics.impression) |
| `Analytics.click`      | 터치나 클릭이 발생하면 분석 로그를 남겨요 | [REFERENCE](/documentation/sdk/domains-api/analytics/analytics.click)      |


# Analytics.click

### 기능 설명

터치나 클릭이 발생하면 분석 로그를 남겨요. 구매 버튼 클릭처럼 중요한 사용자 액션을 기록할 때 사용해요. `event` 유형의 로그에 `event_type: 'click'` 파라미터가 함께 기록돼요.

`log_name`을 지정하지 않으면 현재 경로를 기반으로 `<pathname>::click` 형식의 이름이 자동으로 사용돼요. 로그 파라미터에는 전달한 값 외에도 현재 페이지의 쿼리 스트링(`search`), 진입 경로(`referrer`), 배포 정보(`deployment_id`, `deployment_timestamp`)가 자동으로 포함돼요.

현재 페이지 URL이 `https`가 아니면 로그를 보내지 않아요.

### 타입

```ts
Analytics.click(params?: { log_name?: string } & { [key: string]: LogParam }): Promise<void> | undefined;
```

**Params**

```ts
// log_name 외의 키는 로그 파라미터로 함께 기록돼요.
type LogParam = string | number | boolean | null | undefined;
```

**Response**

현재 페이지 URL이 `https`가 아니면 로그를 보내지 않고 `undefined`를 반환해요.

### 예시 코드

#### JavaScript

```js
import { Analytics } from "@apps-in-toss/web-framework";

const button = document.querySelector("#buy-button");

button.addEventListener("click", () => {
  Analytics.click({ log_name: "buy_button", text: "구매하기" });
});
```


# Analytics.impression

### 기능 설명

요소가 노출되면 분석 로그를 남겨요. 스크롤 아래에 있는 요소가 실제로 화면에 보였을 때 노출 로그를 기록하고 싶을 때 사용해요. `event` 유형의 로그에 `event_type: 'impression'` 파라미터가 함께 기록돼요.

`log_name`을 지정하지 않으면 현재 경로를 기반으로 `<pathname>::impression` 형식의 이름이 자동으로 사용돼요. 로그 파라미터에는 전달한 값 외에도 현재 페이지의 쿼리 스트링(`search`), 진입 경로(`referrer`), 배포 정보(`deployment_id`, `deployment_timestamp`)가 자동으로 포함돼요.

현재 페이지 URL이 `https`가 아니면 로그를 보내지 않아요.

### 타입

```ts
Analytics.impression(params?: { log_name?: string } & { [key: string]: LogParam }): Promise<void> | undefined;
```

**Params**

```ts
// log_name 외의 키는 로그 파라미터로 함께 기록돼요.
type LogParam = string | number | boolean | null | undefined;
```

**Response**

현재 페이지 URL이 `https`가 아니면 로그를 보내지 않고 `undefined`를 반환해요.

### 예시 코드

#### JavaScript

```js
import { Analytics } from "@apps-in-toss/web-framework";

const banner = document.querySelector("#promo-banner");

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      Analytics.impression({ log_name: "banner", text: "프로모션 배너" });
      observer.unobserve(entry.target);
    }
  });
});

observer.observe(banner);
```


# Analytics.log

> 함수형 API `eventLog`로도 같은 기능을 쓸 수 있어요.

### 기능 설명

앱에서 발생하는 이벤트를 기록해요. 디버깅·정보·경고·오류·화면·노출·클릭 등 다양한 유형을 남길 수 있어요. 샌드박스 환경에서는 콘솔에 출력되고, 실환경에서는 로그 시스템에 기록돼요.

`params`에는 사용자 식별용 `anonymous_key`가 기본 파라미터로 자동 포함돼요. 같은 키를 직접 전달하면 전달한 값이 우선해요. `undefined` 값은 전송 전에 제거되고, 나머지 값은 모두 string으로 정규화돼요.

토스앱 Android/iOS `5.208.0` 이상에서 사용할 수 있어요. 미지원 버전에서는 에러 없이 로그가 조용히 무시돼요.

### 타입

```ts
Analytics.log(params: EventLogParams): Promise<void>;
```

**Params**

```ts
type EventLogParams = {
  log_name: string;
  log_type:
    | "debug"
    | "info"
    | "warn"
    | "error"
    | "event"
    | "screen"
    | "impression"
    | "click"
    | "popup";
  params: Record<string, string | number | boolean | null | undefined>;
};
```

**Response**

없음.

### 예시 코드

#### JavaScript

```js
import { Analytics } from "@apps-in-toss/web-framework";

try {
  await Analytics.log({
    log_name: "user_action",
    log_type: "event",
    params: {
      action: "button_click",
      screen: "main",
      userId: 12345,
    },
  });
} catch (error) {
  console.error(error);
}
```


# Analytics.screen

### 기능 설명

화면 진입 시점의 분석 로그를 남겨요. 페이지에 진입했을 때 호출하면 `screen` 유형의 로그가 전송돼요.

`log_name`을 지정하지 않으면 현재 경로를 기반으로 `<pathname>::screen` 형식의 이름이 자동으로 사용돼요. 로그 파라미터에는 전달한 값 외에도 현재 페이지의 쿼리 스트링(`search`), 진입 경로(`referrer`), 문서 제목(`document_title`), 배포 정보(`deployment_id`, `deployment_timestamp`)가 자동으로 포함돼요.

현재 페이지 URL이 `https`가 아니면 로그를 보내지 않아요.

### 타입

```ts
Analytics.screen(params?: { log_name?: string } & { [key: string]: LogParam }): Promise<void> | undefined;
```

**Params**

```ts
// log_name 외의 키는 로그 파라미터로 함께 기록돼요.
type LogParam = string | number | boolean | null | undefined;
```

**Response**

현재 페이지 URL이 `https`가 아니면 로그를 보내지 않고 `undefined`를 반환해요.

### 예시 코드

#### JavaScript

```js
import { Analytics } from "@apps-in-toss/web-framework";

// 페이지 진입 시점에 한 번 호출해요.
Analytics.screen({ entry_point: "home" });
```


# Clipboard

클립보드에 텍스트를 읽고 써요.

### API 목록

| API                 | 설명                  | 상세                                                                      |
| ------------------- | ------------------- | ----------------------------------------------------------------------- |
| `Clipboard.getText` | 클립보드에 저장된 텍스트를 가져와요 | [REFERENCE](/documentation/sdk/domains-api/clipboard/clipboard.gettext) |
| `Clipboard.setText` | 텍스트를 클립보드에 복사해요     | [REFERENCE](/documentation/sdk/domains-api/clipboard/clipboard.settext) |


# Clipboard.getText

> 함수형 API `getClipboardText`으로도 같은 기능을 쓸 수 있어요.

### 기능 설명

클립보드에 저장된 텍스트를 가져와요. 클립보드에 텍스트가 없으면 빈 문자열을 반환해요.

클립보드 읽기 권한이 없으면 `GetClipboardTextPermissionError`가 발생해요. `Clipboard.getText.getPermission()`으로 권한 상태를 확인하고, `Clipboard.getText.openPermissionDialog()`로 권한을 다시 요청할 수 있어요.

### 타입

```ts
Clipboard.getText(): Promise<string>;
```

**Params**

없음

**Response**

클립보드에 저장된 텍스트를 반환해요. 텍스트가 없으면 빈 문자열이에요.

### 에러

에러는 클래스로 던져져요 — `error instanceof GetClipboardTextPermissionError`로 식별해요. 클래스는 `@apps-in-toss/web-framework`에서 import할 수 있어요.

| 에러 클래스                            | 설명                                                                              |
| --------------------------------- | ------------------------------------------------------------------------------- |
| `GetClipboardTextPermissionError` | 클립보드 읽기 권한이 거부된 경우예요. `Clipboard.getText.openPermissionDialog()`로 다시 요청할 수 있어요. |

### 예시 코드

#### JavaScript

```js
import {
  Clipboard,
  GetClipboardTextPermissionError,
} from "@apps-in-toss/web-framework";

try {
  const text = await Clipboard.getText();
  console.log(text);
} catch (error) {
  if (error instanceof GetClipboardTextPermissionError) {
    console.warn("클립보드 읽기 권한이 없어요.");
    return;
  }
  console.error(error);
}
```


# Clipboard.setText

> 함수형 API `setClipboardText`으로도 같은 기능을 쓸 수 있어요.

### 기능 설명

텍스트를 클립보드에 복사해요. 사용자가 다른 앱에서 붙여 넣을 수 있어요.

클립보드 쓰기 권한이 없으면 `SetClipboardTextPermissionError`가 발생해요. `Clipboard.setText.getPermission()`으로 권한 상태를 확인하고, `Clipboard.setText.openPermissionDialog()`로 권한을 다시 요청할 수 있어요.

### 타입

```ts
Clipboard.setText(text: string): Promise<void>;
```

**Params**

클립보드에 복사할 텍스트예요.

**Response**

없음.

### 에러

에러는 클래스로 던져져요 — `error instanceof SetClipboardTextPermissionError`로 식별해요. 클래스는 `@apps-in-toss/web-framework`에서 import할 수 있어요.

| 에러 클래스                            | 설명                                                                              |
| --------------------------------- | ------------------------------------------------------------------------------- |
| `SetClipboardTextPermissionError` | 클립보드 쓰기 권한이 거부된 경우예요. `Clipboard.setText.openPermissionDialog()`로 다시 요청할 수 있어요. |

### 예시 코드

#### JavaScript

```js
import {
  Clipboard,
  SetClipboardTextPermissionError,
} from "@apps-in-toss/web-framework";

try {
  await Clipboard.setText("복사할 텍스트");
} catch (error) {
  if (error instanceof SetClipboardTextPermissionError) {
    console.warn("클립보드 쓰기 권한이 없어요.");
    return;
  }
  console.error(error);
}
```


# Device

디바이스의 로케일·OS, 앨범·카메라·연락처·위치, 햅틱, URL 열기 기능을 제공해요.

### API 목록

| API                        | 설명                               | 상세                                                                                  |
| -------------------------- | -------------------------------- | ----------------------------------------------------------------------------------- |
| `Device.locale`            | 사용자 로케일 정보예요                     | [locale](/documentation/sdk/domains-api/device/device.locale)                       |
| `Device.os`                | 현재 플랫폼(`ios`/`android`) 정보예요     | [os](/documentation/sdk/domains-api/device/device.os)                               |
| `Device.getPhotos`         | 앨범에서 사진 목록을 불러와요                 | [getPhotos](/documentation/sdk/domains-api/device/device.getphotos)                 |
| `Device.getContacts`       | 연락처 목록을 페이지 단위로 가져와요             | [getContacts](/documentation/sdk/domains-api/device/device.getcontacts)             |
| `Device.getLocation`       | 디바이스의 현재 위치 정보를 가져와요             | [getLocation](/documentation/sdk/domains-api/device/device.getlocation)             |
| `Device.openCamera`        | 카메라를 실행해 촬영한 이미지를 반환해요           | [openCamera](/documentation/sdk/domains-api/device/device.opencamera)               |
| `Device.getAlbumItems`     | 앨범에서 사진·동영상을 선택해 가져와요            | [getAlbumItems](/documentation/sdk/domains-api/device/device.getalbumitems)         |
| `Device.triggerHaptic`     | 디바이스에 햅틱 진동을 일으켜요                | [triggerHaptic](/documentation/sdk/domains-api/device/device.triggerhaptic)         |
| `Device.subscribeLocation` | 위치 변경을 계속 감지하고 콜백을 실행해요          | [subscribeLocation](/documentation/sdk/domains-api/device/device.subscribelocation) |
| `Device.openURL`           | 지정한 URL을 기기의 기본 브라우저나 관련 앱에서 열어요 | [openURL](/documentation/sdk/domains-api/device/device.openurl)                     |


# Device.getAlbumItems

> 함수형 API `fetchAlbumItems`로도 같은 기능을 쓸 수 있어요.

### 기능 설명

사용자 앨범에서 사진·동영상을 선택해 가져와요. 선택을 취소하면 빈 배열 `[]`을 반환하고, `types`를 지정하지 않으면 사진만 선택돼요.

토스앱 Android/iOS `5.261.0` 이상에서 사용할 수 있어요. 호출 전에 `Device.getAlbumItems.isSupported()`로 지원 여부를 확인할 수 있어요. 미지원 버전에서 호출하면 `UNSUPPORTED_APP_VERSION` 에러가 발생해요.

### 타입

```ts
Device.getAlbumItems(options?: FetchAlbumItemsOptions): Promise<AlbumItemResponse[]>;
```

**Params**

```ts
type AlbumItemType = "PHOTO" | "VIDEO";

type FetchAlbumItemsOptions = {
  /** 가져올 미디어 유형 목록이에요. 지정하지 않으면 사진만 가져와요. */
  types?: AlbumItemType[];
  /** 가져올 항목의 최대 개수예요. 기본값은 10이에요. */
  maxCount?: number;
  /** 이미지의 최대 폭(px)이에요. 기본값은 1024이에요. */
  maxWidth?: number;
  /** 이미지 dataUri를 Base64로 반환할지 여부예요. 기본값은 false이에요. */
  base64?: boolean;
};
```

**Response**

```ts
type AlbumItemResponse = {
  id: string;
  dataUri: string;
  type: "PHOTO" | "VIDEO";
};
```

### 에러

에러 코드는 catch한 에러의 `error.code` 값이에요.

| 코드                        | 설명                                                                                                                                     |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `NOT_ALLOWED`             | 앨범 접근이 허용되지 않은 경우예요.                                                                                                                   |
| `INVALID_REQUEST`         | 요청 파라미터가 올바르지 않은 경우예요.                                                                                                                 |
| `INVALID_DATA`            | 미디어 데이터가 유효하지 않은 경우예요.                                                                                                                 |
| `UNSUPPORTED_APP_VERSION` | 실행 중인 토스앱 버전이 이 기능을 지원하지 않으면 호출 즉시 발생해요. `Device.getAlbumItems.isSupported()`로 사전 확인하고, 발생 시 `error.message`(업데이트 안내 문구)를 사용자에게 보여주세요. |

### 예시 코드

```js
import { Device } from "@apps-in-toss/web-framework";

async function pickAlbumItems() {
  if (!Device.getAlbumItems.isSupported()) {
    console.warn("현재 토스앱 버전에서는 사용할 수 없어요.");
    return;
  }

  try {
    const items = await Device.getAlbumItems({
      types: ["PHOTO", "VIDEO"],
      maxCount: 5,
      base64: true,
    });

    if (items.length === 0) {
      console.log("선택이 취소되었어요.");
      return;
    }
    console.log(items);
  } catch (error) {
    console.error(error);
  }
}

document.getElementById("pick-media").addEventListener("click", pickAlbumItems);
```


# Device.getContacts

> 함수형 API `fetchContacts`로도 같은 기능을 쓸 수 있어요.

### 기능 설명

사용자의 연락처 목록을 페이지 단위로 가져와요. `size`·`offset`으로 페이지네이션하고, `query.contains`로 이름 포함 검색을 할 수 있어요. `offset`은 첫 호출에 `0`, 이후에는 응답의 `nextOffset`을 사용해요.

연락처 읽기 권한이 없으면 `FetchContactsPermissionError`가 발생해요. `Device.getContacts.getPermission()` / `Device.getContacts.openPermissionDialog()`로 권한을 확인·요청할 수 있어요.

### 타입

```ts
Device.getContacts(options: FetchContactsOptions): Promise<ContactResult>;
```

**Params**

```ts
type FetchContactsOptions = {
  size: number;
  offset: number;
  query?: {
    contains?: string;
  };
};
```

**Response**

```ts
type ContactEntity = {
  name: string;
  phoneNumber: string;
};

type ContactResult = {
  result: ContactEntity[];
  nextOffset: number | null;
  done: boolean;
};
```

### 에러

에러는 클래스로 던져져요 — `error instanceof FetchContactsPermissionError`로 식별해요. 클래스는 `@apps-in-toss/web-framework`에서 import할 수 있어요.

| 에러 클래스                         | 설명                                                                              |
| ------------------------------ | ------------------------------------------------------------------------------- |
| `FetchContactsPermissionError` | 연락처 읽기 권한이 거부된 경우예요. `Device.getContacts.openPermissionDialog()`로 다시 요청할 수 있어요. |

### 예시 코드

```js
import {
  Device,
  FetchContactsPermissionError,
} from "@apps-in-toss/web-framework";

let nextOffset = 0;
let done = false;

async function loadNextContacts() {
  if (done) {
    return;
  }

  try {
    const response = await Device.getContacts({
      size: 10,
      offset: nextOffset ?? 0,
      query: { contains: "김" },
    });

    for (const contact of response.result) {
      console.log(`${contact.name}: ${contact.phoneNumber}`);
    }

    nextOffset = response.nextOffset;
    done = response.done;
  } catch (error) {
    if (error instanceof FetchContactsPermissionError) {
      console.warn("연락처 읽기 권한이 없어요.");
      return;
    }
    console.error(error);
  }
}

document
  .getElementById("load-contacts")
  .addEventListener("click", loadNextContacts);
```


# Device.getLocation

> 함수형 API `getCurrentLocation`으로도 같은 기능을 쓸 수 있어요.

### 기능 설명

디바이스의 현재 위치 정보를 한 번 가져와요. 지도·날씨·매장 찾기처럼 현재 위치가 필요할 때 사용해요. 위치 변경을 계속 감지하려면 `Device.subscribeLocation`을 사용하세요.

위치 권한이 없으면 `GetCurrentLocationPermissionError`가 발생해요. `Device.getLocation.getPermission()` / `Device.getLocation.openPermissionDialog()`로 권한을 확인·요청할 수 있어요.

### 타입

```ts
Device.getLocation(options: GetCurrentLocationOptions): Promise<Location>;
```

**Params**

```ts
enum Accuracy {
  Lowest = 1, // 오차범위 약 3KM 이내
  Low = 2, // 오차범위 약 1KM 이내
  Balanced = 3, // 오차범위 수백 미터 이내
  High = 4, // 오차범위 약 10M 이내
  Highest = 5,
  BestForNavigation = 6,
}

type GetCurrentLocationOptions = {
  accuracy: Accuracy;
};
```

**Response**

```ts
type Location = {
  accessLocation?: "FINE" | "COARSE"; // Android만
  timestamp: number;
  coords: {
    latitude: number;
    longitude: number;
    altitude: number;
    accuracy: number;
    altitudeAccuracy: number;
    heading: number;
  };
};
```

### 에러

에러는 클래스로 던져져요 — `error instanceof GetCurrentLocationPermissionError`로 식별해요. 클래스는 `@apps-in-toss/web-framework`에서 import할 수 있어요.

| 에러 클래스                              | 설명                                                                          |
| ----------------------------------- | --------------------------------------------------------------------------- |
| `GetCurrentLocationPermissionError` | 위치 권한이 거부된 경우예요. `Device.getLocation.openPermissionDialog()`로 다시 요청할 수 있어요. |

### 예시 코드

```js
import {
  Accuracy,
  Device,
  GetCurrentLocationPermissionError,
} from "@apps-in-toss/web-framework";

async function showCurrentPosition() {
  try {
    const location = await Device.getLocation({ accuracy: Accuracy.Balanced });
    console.log(location.coords.latitude, location.coords.longitude);
  } catch (error) {
    if (error instanceof GetCurrentLocationPermissionError) {
      console.warn("위치 권한이 없어요.");
      return;
    }
    console.error(error);
  }
}

document
  .getElementById("get-location")
  .addEventListener("click", showCurrentPosition);
```


# Device.getPhotos

> 함수형 API `fetchAlbumPhotos`로도 같은 기능을 쓸 수 있어요.

### 기능 설명

사용자의 앨범에서 사진 목록을 불러와요. 최대 개수와 해상도를 설정할 수 있고, 갤러리 미리보기·이미지 선택 등에 활용할 수 있어요.

사진첩 읽기 권한이 없으면 `FetchAlbumPhotosPermissionError`가 발생해요. `Device.getPhotos.getPermission()`으로 권한 상태를 확인하고, `Device.getPhotos.openPermissionDialog()`로 권한을 다시 요청할 수 있어요.

### 타입

```ts
Device.getPhotos(options?: FetchAlbumPhotosOptions): Promise<ImageResponse[]>;
```

**Params**

```ts
type FetchAlbumPhotosOptions = {
  /** 가져올 사진의 최대 개수예요. 기본값은 10이에요. */
  maxCount?: number;
  /** 사진의 최대 폭(px)이에요. 기본값은 1024이에요. */
  maxWidth?: number;
  /** 이미지를 base64로 반환할지 여부예요. 기본값은 false예요. */
  base64?: boolean;
};
```

**Response**

```ts
type ImageResponse = {
  id: string;
  dataUri: string;
};
```

### 에러

에러는 클래스로 던져져요 — `error instanceof FetchAlbumPhotosPermissionError`로 식별해요. 클래스는 `@apps-in-toss/web-framework`에서 import할 수 있어요.

| 에러 클래스                            | 설명                                                                            |
| --------------------------------- | ----------------------------------------------------------------------------- |
| `FetchAlbumPhotosPermissionError` | 사진첩 읽기 권한이 거부된 경우예요. `Device.getPhotos.openPermissionDialog()`로 다시 요청할 수 있어요. |

### 예시 코드

```js
import {
  Device,
  FetchAlbumPhotosPermissionError,
} from "@apps-in-toss/web-framework";

const base64 = true;

async function renderAlbumPhotos() {
  try {
    const photos = await Device.getPhotos({
      base64,
      maxCount: 10,
      maxWidth: 360,
    });

    const container = document.getElementById("photo-list");
    for (const photo of photos) {
      const img = document.createElement("img");
      img.src = base64
        ? "data:image/jpeg;base64," + photo.dataUri
        : photo.dataUri;
      container.appendChild(img);
    }
  } catch (error) {
    if (error instanceof FetchAlbumPhotosPermissionError) {
      console.warn("사진첩 읽기 권한이 없어요.");
      return;
    }
    console.error(error);
  }
}

document
  .getElementById("load-photos")
  .addEventListener("click", renderAlbumPhotos);
```


# Device.openCamera

> 함수형 API `openCamera`로도 같은 기능을 쓸 수 있어요.

### 기능 설명

카메라를 실행해서 촬영한 이미지를 반환해요.

카메라 권한이 없으면 `OpenCameraPermissionError`가 발생해요. `Device.openCamera.getPermission()` / `Device.openCamera.openPermissionDialog()`로 권한을 확인·요청할 수 있어요.

### 타입

```ts
Device.openCamera(options?: OpenCameraOptions): Promise<ImageResponse>;
```

**Params**

```ts
type OpenCameraOptions = {
  /** 이미지를 Base64로 반환할지 여부예요. 기본값은 false예요. */
  base64?: boolean;
  /** 이미지의 최대 너비(px)예요. 기본값은 1024예요. */
  maxWidth?: number;
};
```

**Response**

```ts
type ImageResponse = {
  id: string;
  dataUri: string;
};
```

### 에러

에러는 클래스로 던져져요 — `error instanceof OpenCameraPermissionError`로 식별해요. 클래스는 `@apps-in-toss/web-framework`에서 import할 수 있어요.

| 에러 클래스                      | 설명                                                                          |
| --------------------------- | --------------------------------------------------------------------------- |
| `OpenCameraPermissionError` | 카메라 권한이 거부된 경우예요. `Device.openCamera.openPermissionDialog()`로 다시 요청할 수 있어요. |

### 예시 코드

```js
import { Device, OpenCameraPermissionError } from "@apps-in-toss/web-framework";

const base64 = true;

async function captureAndShow() {
  try {
    const image = await Device.openCamera({ base64, maxWidth: 1024 });

    const img = document.getElementById("preview");
    img.src = base64
      ? "data:image/jpeg;base64," + image.dataUri
      : image.dataUri;
  } catch (error) {
    if (error instanceof OpenCameraPermissionError) {
      console.warn("카메라 권한이 없어요.");
      return;
    }
    console.error(error);
  }
}

document
  .getElementById("open-camera")
  .addEventListener("click", captureAndShow);
```


# Device.locale

> 함수형 API `getLocale`로도 같은 기능을 쓸 수 있어요.

### 기능 설명

사용자의 로케일(locale) 정보예요. 네이티브에서 가져올 수 없으면 기본값 `'ko-KR'`이에요. 앱의 현지화·언어 설정에 사용할 수 있어요.

상수 값이라 호출 없이 바로 읽어요.

### 타입

```ts
Device.locale: string;
```

**Params**

없음

**Response**

로케일 문자열이에요. 예: `'ko-KR'`.

### 예시 코드

```js
import { Device } from "@apps-in-toss/web-framework";

console.log(Device.locale);
```




---

[Next Page](/llms-full.txt/1)

