---
title: "앱 심사 품질 요건 | Tajo"
description: "Stripe 앱이 심사를 통과하기 위해 충족해야 하는 품질, 보안, UX, 법적 기준"
url: https://tajo.io/kr/docs/stripe-apps/review-requirements/
markdown: https://tajo.io/kr/docs/stripe-apps/review-requirements.md
lang: ko-KR
author: "Set Noa"
author_url: https://tajo.io/kr/set-noa/
robots: noindex
---

Tajo 사전 이용 신청

이름과 회사 이메일 주소를 입력하세요. Tajo 이용 신청을 확인할 수 있는 링크를 이메일로 보내드립니다.

# 앱 심사 품질 요건

[Set Noa](https://tajo.io/kr/set-noa/)

Stripe는 마켓플레이스에 제출된 모든 앱을 포괄적인 품질 요건에 따라 심사합니다. 개발에 착수하기 전에 이 기준을 이해해 두면 시간을 아끼고 심사 반복 횟수를 줄일 수 있습니다.

Caution

**상태: 예정, 아직 구축되지 않음.** Tajo에는 Stripe App이 없으므로 이 섹션의 어떤 것도 현재 설치할 수 없습니다. 이 페이지에 나오는 앱 식별자, URL, 자격 증명은 실제로 동작하는 엔드포인트가 아니라 예시용 자리표시자입니다.

## 개요

Stripe 앱 심사는 다음 여섯 가지 핵심 영역에서 앱을 평가합니다.

1. **투명한 요금제**, 비용에 대한 명확한 안내
2. **앱 기능**, 안정성과 완성도
3. **개발자 표준**, 코드 품질과 API 사용 방식
4. **UX 품질**, 사용자 인터페이스와 경험 기준
5. **보안**, 데이터 보호와 안전한 관행
6. **법적 준수**, 개인정보 보호 및 규제 요건

## 투명한 요금제

앱은 모든 비용을 사용자에게 명확하게 알려야 합니다.

- **요금 고지**: 모든 가격은 마켓플레이스 등록 정보에 미리 명시되어야 합니다
- **숨은 비용 금지**: 사용자가 설치 후 예상치 못한 청구를 겪어서는 안 됩니다
- **체험 조건**: 체험을 제공한다면 기간과 종료 후 처리 방식을 분명히 밝혀야 합니다
- **업그레이드 흐름**: 업셀이나 업그레이드 안내는 방해가 되지 않아야 하며 선택 사항임이 분명해야 합니다
- **통화**: 가능한 경우 사용자의 현지 통화로 가격을 표시합니다

Caution

가격을 모호하게 표시하거나 명확한 동의 없이 사용자에게 청구하는 앱은 즉시 거절됩니다.

## 날짜와 시간 표기

앱에 표시되는 모든 날짜와 시간은 Stripe 대시보드 관례를 따라야 합니다.

- 사용 가능한 경우 사용자의 로케일에 맞춰 날짜를 표기합니다
- 시간은 사용자의 현지 시간대로 표시합니다
- 최근 이벤트에는 상대 시간을 사용합니다 (예: “2 hours ago”)
- 오래된 이벤트에는 전체 날짜와 시간을 담은 절대 시간을 사용합니다
- API에 노출되는 날짜 필드는 ISO 8601을 따릅니다

```typescript
// Good: Use Stripe's date formatting utilities
import { formatDate, formatRelativeTime } from '@stripe/ui-extension-sdk/utils';

const formattedDate = formatDate(timestamp); // Locale-aware
const relativeTime = formatRelativeTime(timestamp); // "2 hours ago"
```

## 앱 설정

앱에 구성이 필요한 경우 다음을 지켜야 합니다.

- 앱 뷰포트에서 접근할 수 있는 전용 **설정 화면**을 제공합니다
- 가능한 경우 합리적인 기본값을 미리 채워 둡니다
- 모든 사용자 입력을 검증하고 명확한 오류 메시지를 보여 줍니다
- 앱을 다시 설치하지 않고도 설정을 변경할 수 있게 합니다
- Stripe Secret Store API로 설정을 세션 간에 유지합니다

## 샌드박스 지원

앱은 Stripe 샌드박스(테스트) 모드에서도 정상 동작해야 합니다.

- **테스트 모드 호환성**: 모든 기능이 테스트 모드에서 작동해야 합니다
- **테스트 데이터**: 앱 기능을 잘 보여 주는 현실적인 테스트 데이터를 사용합니다
- **샌드박스에 라이브 데이터 금지**: 테스트 모드에서 운영 데이터를 절대 노출하지 않습니다
- **정중한 처리**: 샌드박스에서 사용할 수 없는 기능이라면 그 이유를 설명하는 명확한 메시지를 표시합니다
- 매니페스트에 `sandbox_install_compatible: true`를 설정합니다

```json
{
  "sandbox_install_compatible": true
}
```

## 앱 기능

### 안정성

- 앱은 일반적인 사용 중에 충돌하거나 멈추지 않아야 합니다
- 광고한 모든 기능이 설명대로 작동해야 합니다
- 네트워크 오류는 재시도 옵션과 함께 매끄럽게 처리해야 합니다
- 백그라운드 작업 중에도 앱은 계속 반응해야 합니다

### 완성도

- 자리표시자 콘텐츠, “coming soon” 기능, 깨진 링크가 없어야 합니다
- 모든 UI 요소가 동작해야 하며, 눌리지 않는 버튼이나 비활성 컨트롤이 없어야 합니다
- 도움말 텍스트와 문서 링크는 유효한 페이지로 연결되어야 합니다
- 앱을 제거하면 모든 앱 데이터와 웹훅이 깔끔하게 정리되어야 합니다

### 성능

- UI는 일반적인 연결 환경에서 3초 이내에 렌더링되어야 합니다
- 백그라운드 동기화 작업이 UI를 막아서는 안 됩니다
- 대용량 데이터는 페이지네이션이나 지연 로딩을 사용해야 합니다
- 속도 제한에 걸리지 않도록 API 호출을 최소화합니다

## 개발자 표준

### API 사용

- Stripe API의 최신 안정 버전을 사용합니다
- 페이지네이션, 오류 처리, 멱등성에 대해 Stripe API 모범 사례를 따릅니다
- 속도 제한을 초과하지 말고, 재시도에는 지수 백오프를 구현합니다
- 폴링 대신 웹훅으로 이벤트 기반 업데이트를 처리합니다

### 코드 품질

- 프로덕션 빌드에 콘솔 오류나 경고가 없어야 합니다
- 제출 전에 모든 디버그 로깅을 제거합니다
- 모든 예외 상황(빈 상태, 누락된 데이터, 네트워크 장애)을 처리합니다
- 일관된 UI를 위해 Stripe 컴포넌트 라이브러리 패턴을 따릅니다

### 버전 관리

- 시맨틱 버저닝(MAJOR.MINOR.PATCH)을 사용합니다
- 버전 업데이트에서 하위 호환성을 깨는 변경을 문서화합니다
- 가능한 경우 하위 호환성을 유지합니다

## UX 품질

### 광고

- **광고 금지**: 앱은 어떤 종류의 광고도 표시해서는 안 됩니다
- **교차 홍보 금지**: 앱 UI 안에서 다른 제품이나 서비스를 홍보하지 않습니다
- **브랜드 콘텐츠**: Brevo 같은 연동 파트너를 제외하고, 서드파티 브랜드가 아닌 자사 브랜드 아이덴티티만 표시합니다

### 언어와 콘텐츠

- **일관된 표현**: 앱 전반에서 일관된 용어를 사용합니다
- **전문적인 톤**: Stripe 대시보드의 전문적이고 간결한 커뮤니케이션 방식에 맞춥니다
- **전문 용어 지양**: 판매자가 이해하기 어려운 기술 용어를 피합니다
- **맞춤법 검사**: 모든 텍스트에 철자 및 문법 오류가 없도록 합니다
- **영어 전용**: 마켓플레이스 앱의 모든 사용자 노출 텍스트는 영어여야 합니다

### 확인 대화상자

파괴적이거나 중대한 작업에는 사용자 확인을 요구해야 합니다.

```typescript
// Good: Confirm before destructive actions
const handleDisconnect = async () => {
  const confirmed = await showConfirmation({
    title: 'Disconnect Brevo Integration',
    message: 'This will stop syncing customer data to Brevo. You can reconnect at any time.',
    confirmLabel: 'Disconnect',
    cancelLabel: 'Cancel',
    destructive: true,
  });

  if (confirmed) {
    await disconnectIntegration();
  }
};
```

확인이 필요한 작업입니다.

- 연동 해제
- 동기화된 데이터 삭제
- 데이터 흐름에 영향을 주는 설정 변경
- 구성을 기본값으로 초기화

### 로딩 상태

비동기 작업에는 항상 로딩 표시를 보여 주어야 합니다.

```typescript
// Good: Show loading state during data fetch
const CustomerSyncStatus = () => {
  const { data, isLoading, error } = useSyncStatus();

  if (isLoading) {
    return <Spinner label="Loading sync status..." />;
  }

  if (error) {
    return <Banner type="critical" title="Failed to load sync status">
      {error.message}
    </Banner>;
  }

  return <SyncStatusDisplay data={data} />;
};
```

요구 사항은 다음과 같습니다.

- 데이터를 불러오는 동안 스피너나 스켈레톤 화면을 표시합니다
- 폼 제출 중에는 버튼을 비활성화합니다
- 오래 걸리는 작업에는 진행 표시기를 보여 줍니다
- 로딩 중에 빈 화면을 보여 주어서는 안 됩니다

### 오류 메시지

명확하고 실행 가능한 오류 메시지를 제공합니다.

```typescript
// Bad: Generic error
"Something went wrong"

// Good: Specific and actionable
"Unable to sync customer data to Brevo. Please verify your Brevo API key
in Settings and try again."
```

오류 메시지 가이드라인입니다.

- 무슨 일이 일어났는지 쉬운 말로 설명합니다
- 사용자가 문제를 해결하기 위해 취할 수 있는 구체적인 조치를 제안합니다
- 실패한 작업을 다시 시도할 방법을 제공합니다
- 디버깅용 상세 오류 정보를 기록합니다 (사용자에게는 보여 주지 않습니다)
- 해당하는 경우 지원 문의에 쓸 오류 코드를 포함합니다

## 보안

### Secret Store API

민감한 데이터 저장에는 모두 Stripe Secret Store API를 사용합니다.

```typescript
import { createHttpClient, STRIPE_API_KEY } from '@stripe/ui-extension-sdk/http_client';

// Good: Store secrets using the Secret Store API
const storeBrevoApiKey = async (apiKey: string) => {
  const stripe = createHttpClient(STRIPE_API_KEY);
  await stripe.apps.secrets.create({
    name: 'brevo_api_key',
    payload: apiKey,
    scope: { type: 'account' },
  });
};

// Good: Retrieve secrets from the Secret Store
const getBrevoApiKey = async () => {
  const stripe = createHttpClient(STRIPE_API_KEY);
  const secret = await stripe.apps.secrets.find({
    name: 'brevo_api_key',
    scope: { type: 'account' },
  });
  return secret.payload;
};
```

다음 위치에는 민감한 데이터를 **절대** 저장하지 마세요.

- 로컬 스토리지 또는 세션 스토리지
- 쿠키
- URL 파라미터
- 소스 코드에 하드코딩된 값
- 평문 구성 파일

### 암호화

- **자체 암호화 금지**: 직접 만든 암호화 알고리즘을 구현하지 않습니다
- Stripe의 내장 보안 요소(Secret Store, 서명 시크릿)를 사용합니다
- 모든 외부 API 호출에 HTTPS를 사용합니다
- 처리 전에 모든 웹훅 서명을 검증합니다

### 데이터 취급

- 앱에 실제로 필요한 권한만 요청합니다
- 기능에 필요한 범위를 넘어서는 Stripe 데이터를 저장하지 않습니다
- 개인정보처리방침에 맞춘 데이터 보관 정책을 시행합니다
- 사용자가 데이터 삭제를 요청할 수 있는 수단을 제공합니다

## 법적 준수

### 개인정보처리방침

앱에는 다음 내용을 담은, 공개적으로 접근 가능한 개인정보처리방침이 있어야 합니다.

- 앱이 Stripe에서 수집하는 데이터
- 데이터를 저장, 처리, 공유하는 방식
- 데이터 보관 및 삭제 정책
- 데이터에 관한 사용자의 권리
- 개인정보 문의를 위한 연락처
- 관련 규정 준수 여부 (GDPR, CCPA 등)

### 서비스 약관

- 앱에 대한 명확한 서비스 약관을 제공합니다
- Stripe 서비스 약관과 충돌하는 조항을 포함하지 않습니다
- 사용상의 제한이나 제약을 분명히 밝힙니다

### 규제 준수

- 적용되는 모든 데이터 보호 규정을 준수합니다
- 적절한 데이터 처리 계약을 체결합니다
- 데이터 이동권과 삭제 요청을 지원합니다
- 데이터 접근 및 처리에 대한 감사 로그를 유지합니다

## 심사 진행 기간

| 단계 | 소요 기간 |
| --- | --- |
| 최초 제출 | 영업일 기준 5~10일 |
| 수정본 심사 | 영업일 기준 3~7일 |
| 최종 승인 | 영업일 기준 1~2일 |
| 게시 | 승인 직후 |

Tip

심사 주기를 여러 번 거치지 않으려면 모든 심사 피드백을 한 번의 수정본에서 처리하세요. Stripe 팀은 발견된 각 문제에 대해 구체적이고 실행 가능한 피드백을 제공합니다.

## 자주 발생하는 거절 사유

1. **오류 처리 누락**, 네트워크 오류나 예상치 못한 데이터에서 앱이 충돌
2. **부족한 로딩 상태**, 데이터를 불러오는 동안 빈 화면 노출
3. **불명확한 요금제**, 등록 정보에 가격이 충분히 공개되지 않음
4. **과도한 권한**, 앱에 필요하지 않은 권한을 요청
5. **샌드박스 모드 미작동**, 테스트 모드에서 앱이 동작하지 않음
6. **보안 문제**, Secret Store API 밖에 시크릿을 저장
7. **개인정보처리방침 누락**, 접근 가능한 개인정보처리방침 URL이 없음
8. **미완성 기능**, “coming soon” 기능이나 자리표시자 콘텐츠
