666 lines
11 KiB
Markdown
666 lines
11 KiB
Markdown
# Zellyy API 명세서
|
|
|
|
이 문서는 Zellyy 프로젝트의 API 엔드포인트와 기능에 대한 상세 명세를 제공합니다.
|
|
|
|
## 기본 정보
|
|
|
|
- **기본 URL**: `https://a11.ism.kr/api`
|
|
- **API 버전**: v1
|
|
- **인증 방식**: JWT 토큰 (Bearer Authentication)
|
|
- **응답 형식**: JSON
|
|
|
|
## 인증 API
|
|
|
|
### 회원가입
|
|
|
|
```
|
|
POST /auth/signup
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "securepassword",
|
|
"username": "username"
|
|
}
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"user": {
|
|
"id": "uuid",
|
|
"email": "user@example.com",
|
|
"username": "username"
|
|
},
|
|
"session": {
|
|
"access_token": "jwt_token",
|
|
"refresh_token": "refresh_token",
|
|
"expires_at": 1672531200
|
|
}
|
|
}
|
|
```
|
|
|
|
### 로그인
|
|
|
|
```
|
|
POST /auth/login
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "securepassword"
|
|
}
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"user": {
|
|
"id": "uuid",
|
|
"email": "user@example.com",
|
|
"username": "username"
|
|
},
|
|
"session": {
|
|
"access_token": "jwt_token",
|
|
"refresh_token": "refresh_token",
|
|
"expires_at": 1672531200
|
|
}
|
|
}
|
|
```
|
|
|
|
### 토큰 갱신
|
|
|
|
```
|
|
POST /auth/refresh
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"refresh_token": "refresh_token"
|
|
}
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"access_token": "new_jwt_token",
|
|
"refresh_token": "new_refresh_token",
|
|
"expires_at": 1672531200
|
|
}
|
|
```
|
|
|
|
### 로그아웃
|
|
|
|
```
|
|
POST /auth/logout
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"message": "Successfully logged out"
|
|
}
|
|
```
|
|
|
|
## 사용자 API
|
|
|
|
### 사용자 정보 조회
|
|
|
|
```
|
|
GET /users/me
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"email": "user@example.com",
|
|
"username": "username",
|
|
"display_name": "Display Name",
|
|
"avatar_url": "https://example.com/avatar.jpg",
|
|
"is_premium": false,
|
|
"premium_until": null,
|
|
"created_at": "2023-01-01T00:00:00Z"
|
|
}
|
|
```
|
|
|
|
### 사용자 정보 업데이트
|
|
|
|
```
|
|
PATCH /users/me
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"display_name": "New Display Name",
|
|
"avatar_url": "https://example.com/new-avatar.jpg"
|
|
}
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"email": "user@example.com",
|
|
"username": "username",
|
|
"display_name": "New Display Name",
|
|
"avatar_url": "https://example.com/new-avatar.jpg",
|
|
"updated_at": "2023-01-02T00:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 카드 API
|
|
|
|
### 카드 생성
|
|
|
|
```
|
|
POST /cards
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"content": "This is a card content",
|
|
"background_color": "#FFFFFF",
|
|
"text_color": "#000000",
|
|
"font_family": "system",
|
|
"font_size": 16,
|
|
"text_align": "center",
|
|
"is_public": false,
|
|
"tags": ["personal", "ideas"]
|
|
}
|
|
```
|
|
|
|
**응답 (201 Created)**:
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"content": "This is a card content",
|
|
"background_color": "#FFFFFF",
|
|
"text_color": "#000000",
|
|
"font_family": "system",
|
|
"font_size": 16,
|
|
"text_align": "center",
|
|
"is_public": false,
|
|
"is_synced": false,
|
|
"created_at": "2023-01-01T00:00:00Z",
|
|
"updated_at": "2023-01-01T00:00:00Z",
|
|
"tags": ["personal", "ideas"]
|
|
}
|
|
```
|
|
|
|
### 카드 목록 조회
|
|
|
|
```
|
|
GET /cards
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**쿼리 파라미터**:
|
|
- `page`: 페이지 번호 (기본값: 1)
|
|
- `limit`: 페이지당 항목 수 (기본값: 20, 최대: 100)
|
|
- `sort`: 정렬 기준 (options: created_at, updated_at, 기본값: created_at)
|
|
- `order`: 정렬 순서 (options: asc, desc, 기본값: desc)
|
|
- `tag`: 태그로 필터링 (선택 사항)
|
|
- `search`: 내용 검색 (선택 사항)
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": "uuid1",
|
|
"content": "Card 1 content",
|
|
"background_color": "#FFFFFF",
|
|
"text_color": "#000000",
|
|
"font_family": "system",
|
|
"font_size": 16,
|
|
"text_align": "center",
|
|
"is_public": false,
|
|
"is_synced": false,
|
|
"created_at": "2023-01-01T00:00:00Z",
|
|
"updated_at": "2023-01-01T00:00:00Z",
|
|
"tags": ["personal"]
|
|
},
|
|
{
|
|
"id": "uuid2",
|
|
"content": "Card 2 content",
|
|
"background_color": "#F0F0F0",
|
|
"text_color": "#333333",
|
|
"font_family": "arial",
|
|
"font_size": 18,
|
|
"text_align": "left",
|
|
"is_public": true,
|
|
"is_synced": true,
|
|
"created_at": "2023-01-02T00:00:00Z",
|
|
"updated_at": "2023-01-02T00:00:00Z",
|
|
"tags": ["ideas", "public"]
|
|
}
|
|
],
|
|
"pagination": {
|
|
"total": 42,
|
|
"page": 1,
|
|
"limit": 20,
|
|
"total_pages": 3
|
|
}
|
|
}
|
|
```
|
|
|
|
### 카드 상세 조회
|
|
|
|
```
|
|
GET /cards/{card_id}
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"content": "This is a card content",
|
|
"background_color": "#FFFFFF",
|
|
"text_color": "#000000",
|
|
"font_family": "system",
|
|
"font_size": 16,
|
|
"text_align": "center",
|
|
"is_public": false,
|
|
"is_synced": false,
|
|
"created_at": "2023-01-01T00:00:00Z",
|
|
"updated_at": "2023-01-01T00:00:00Z",
|
|
"tags": ["personal", "ideas"],
|
|
"share_count": 2,
|
|
"share_platforms": ["facebook", "instagram"]
|
|
}
|
|
```
|
|
|
|
### 카드 업데이트
|
|
|
|
```
|
|
PATCH /cards/{card_id}
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"content": "Updated card content",
|
|
"background_color": "#F0F0F0",
|
|
"is_public": true,
|
|
"tags": ["personal", "ideas", "updated"]
|
|
}
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"content": "Updated card content",
|
|
"background_color": "#F0F0F0",
|
|
"text_color": "#000000",
|
|
"font_family": "system",
|
|
"font_size": 16,
|
|
"text_align": "center",
|
|
"is_public": true,
|
|
"is_synced": false,
|
|
"created_at": "2023-01-01T00:00:00Z",
|
|
"updated_at": "2023-01-02T00:00:00Z",
|
|
"tags": ["personal", "ideas", "updated"]
|
|
}
|
|
```
|
|
|
|
### 카드 삭제
|
|
|
|
```
|
|
DELETE /cards/{card_id}
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**응답 (204 No Content)**
|
|
|
|
## 소셜 공유 API
|
|
|
|
### 소셜 계정 연동
|
|
|
|
```
|
|
POST /social/connect/{platform}
|
|
```
|
|
|
|
**지원 플랫폼**: `facebook`, `instagram`, `twitter`
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"access_token": "platform_access_token",
|
|
"refresh_token": "platform_refresh_token",
|
|
"expires_at": 1672531200
|
|
}
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"platform": "facebook",
|
|
"connected": true,
|
|
"platform_user_id": "platform_user_id",
|
|
"expires_at": 1672531200
|
|
}
|
|
```
|
|
|
|
### 소셜 계정 연동 해제
|
|
|
|
```
|
|
DELETE /social/connect/{platform}
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**응답 (204 No Content)**
|
|
|
|
### 연동된 소셜 계정 목록
|
|
|
|
```
|
|
GET /social/accounts
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"accounts": [
|
|
{
|
|
"platform": "facebook",
|
|
"platform_user_id": "facebook_user_id",
|
|
"connected_at": "2023-01-01T00:00:00Z",
|
|
"expires_at": 1672531200
|
|
},
|
|
{
|
|
"platform": "instagram",
|
|
"platform_user_id": "instagram_user_id",
|
|
"connected_at": "2023-01-02T00:00:00Z",
|
|
"expires_at": 1672617600
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### 카드 소셜 공유
|
|
|
|
```
|
|
POST /cards/{card_id}/share/{platform}
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"message": "Check out my new card!" // 선택적 메시지
|
|
}
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"id": "share_id",
|
|
"card_id": "card_id",
|
|
"platform": "facebook",
|
|
"status": "success",
|
|
"share_url": "https://facebook.com/post/123456",
|
|
"shared_at": "2023-01-01T00:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 구독 API
|
|
|
|
### 구독 플랜 목록
|
|
|
|
```
|
|
GET /subscriptions/plans
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"plans": [
|
|
{
|
|
"id": "monthly",
|
|
"name": "Monthly Premium",
|
|
"description": "Monthly subscription with cloud sync",
|
|
"price": 4.99,
|
|
"currency": "USD",
|
|
"interval": "month",
|
|
"features": [
|
|
"Cloud sync",
|
|
"Unlimited cards",
|
|
"Premium templates"
|
|
]
|
|
},
|
|
{
|
|
"id": "yearly",
|
|
"name": "Yearly Premium",
|
|
"description": "Yearly subscription with cloud sync (save 20%)",
|
|
"price": 47.99,
|
|
"currency": "USD",
|
|
"interval": "year",
|
|
"features": [
|
|
"Cloud sync",
|
|
"Unlimited cards",
|
|
"Premium templates",
|
|
"Priority support"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### 구독 생성
|
|
|
|
```
|
|
POST /subscriptions
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"plan_id": "monthly",
|
|
"payment_method_id": "payment_method_id",
|
|
"payment_provider": "stripe"
|
|
}
|
|
```
|
|
|
|
**응답 (201 Created)**:
|
|
```json
|
|
{
|
|
"id": "subscription_id",
|
|
"plan_id": "monthly",
|
|
"status": "active",
|
|
"start_date": "2023-01-01T00:00:00Z",
|
|
"end_date": "2023-02-01T00:00:00Z",
|
|
"payment_provider": "stripe",
|
|
"payment_id": "payment_id"
|
|
}
|
|
```
|
|
|
|
### 현재 구독 정보
|
|
|
|
```
|
|
GET /subscriptions/current
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"id": "subscription_id",
|
|
"plan_id": "monthly",
|
|
"status": "active",
|
|
"start_date": "2023-01-01T00:00:00Z",
|
|
"end_date": "2023-02-01T00:00:00Z",
|
|
"auto_renew": true,
|
|
"payment_provider": "stripe",
|
|
"payment_id": "payment_id"
|
|
}
|
|
```
|
|
|
|
### 구독 취소
|
|
|
|
```
|
|
POST /subscriptions/cancel
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer jwt_token
|
|
```
|
|
|
|
**응답 (200 OK)**:
|
|
```json
|
|
{
|
|
"id": "subscription_id",
|
|
"status": "canceled",
|
|
"end_date": "2023-02-01T00:00:00Z",
|
|
"message": "Subscription will be active until the end date"
|
|
}
|
|
```
|
|
|
|
## 오류 응답
|
|
|
|
모든 API 엔드포인트는 오류 발생 시 다음과 같은 형식으로 응답합니다:
|
|
|
|
**응답 (4xx/5xx)**:
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "error_code",
|
|
"message": "Error message description",
|
|
"details": {} // 추가 오류 정보 (선택 사항)
|
|
}
|
|
}
|
|
```
|
|
|
|
### 공통 오류 코드
|
|
|
|
- `invalid_request`: 잘못된 요청 형식
|
|
- `authentication_required`: 인증 필요
|
|
- `invalid_credentials`: 잘못된 인증 정보
|
|
- `permission_denied`: 권한 없음
|
|
- `resource_not_found`: 리소스를 찾을 수 없음
|
|
- `rate_limit_exceeded`: 요청 한도 초과
|
|
- `internal_server_error`: 서버 내부 오류
|
|
|
|
## 웹훅 (Webhook)
|
|
|
|
Zellyy는 다음 이벤트에 대한 웹훅을 제공합니다:
|
|
|
|
### 웹훅 등록
|
|
|
|
```
|
|
POST /webhooks
|
|
```
|
|
|
|
**요청 헤더**:
|
|
```
|
|
Authorization: Bearer admin_token
|
|
```
|
|
|
|
**요청 본문**:
|
|
```json
|
|
{
|
|
"url": "https://your-service.com/webhook",
|
|
"events": ["user.created", "subscription.created", "subscription.canceled"],
|
|
"secret": "your_webhook_secret"
|
|
}
|
|
```
|
|
|
|
**응답 (201 Created)**:
|
|
```json
|
|
{
|
|
"id": "webhook_id",
|
|
"url": "https://your-service.com/webhook",
|
|
"events": ["user.created", "subscription.created", "subscription.canceled"],
|
|
"created_at": "2023-01-01T00:00:00Z"
|
|
}
|
|
```
|
|
|
|
### 웹훅 이벤트 형식
|
|
|
|
```json
|
|
{
|
|
"id": "event_id",
|
|
"type": "event.type",
|
|
"created_at": "2023-01-01T00:00:00Z",
|
|
"data": {
|
|
// 이벤트 관련 데이터
|
|
}
|
|
}
|
|
```
|
|
|
|
## 결론
|
|
|
|
이 API 명세서는 Zellyy 프로젝트의 기본 기능을 구현하기 위한 엔드포인트를 정의합니다. 프로젝트가 발전함에 따라 추가 엔드포인트와 기능이 확장될 수 있습니다. |