API 설계 ‐ 1단계 과제 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
1. API 명세 페이지
2. 보안 정책
- 외부 클라이언트(브라우저 등)에서의 직접 접근은 차단되며, 허용된 도메인(예:
https://backend.example.com)을 통해 프록시된 요청만 처리
3. 엔드포인트 목록 및 기능 설명
| 기능 | Method | Endpoint | 설명 |
|---|---|---|---|
| 앨범 생성 (임베딩) | POST |
/api/albums/embeddings |
입력 이미지들을 받아 CLIP 기반 임베딩을 생성하고 캐싱 |
| 앨범 생성 (중복 판별) | POST |
/api/albums/duplicates |
입력 이미지들의 임베딩을 바탕으로 유사도를 계산하고 중복 이미지 그룹화 |
| 앨범 생성 (품질 측정) | POST |
/api/albums/quality |
이미지 품질(흐림 등)을 평가하고 낮은 품질 이미지를 필터링 |
| 앨범 생성 (카테고리 분류) | POST |
/api/albums/categories |
입력 이미지들을 사전에 정의된 카테고리로 분류 |
| 앨범 생성 (하이라이트 점수) | POST |
/api/albums/scores |
카테고리 내 이미지들의 대표성/품질 기반 하이라이트 점수 계산 |
| 앨범 생성 (인물 클러스터링) | POST |
/api/albums/people |
얼굴 임베딩을 통해 인물별로 이미지 클러스터링 수행 |
| 사진 스타일 변환 | POST |
/api/style |
지정한 스타일(e.g., ghibli, pixel, comic 등)로 이미지 변환 요청 |
4. API 요청/응답 예시
a. /api/albums/embeddings
가. Request
{
"images": [
"img1.jpg",
"img2.jpg"
]
}
나. Response
{
"message": "success",
"data": null
}
b. /api/albums/duplicates
가. Request
{
"images": [
"img1.jpg",
"img2.jpg",
"img3.jpg",
"img4.jpg"
]
}
나. Response
{
"message": "success",
"data": [
["img1.jpg", "img3.jpg"],
["img2.jpg", "img4.jpg"]
]
}
c. /api/albums/quality
가. Request
{
"images": [
"img1.jpg",
"img2.jpg",
"img3.jpg",
"img4.jpg"
]
}
나. Response
{
"message": "success",
"data": [
"img2.jpg",
"img3.jpg"
]
}
d. /api/albums/categories
가. Request
{
"concepts": [
"concept1",
"concept2"
],
"images": [
"img1.jpg",
"img2.jpg",
"img3.jpg",
"img4.jpg"
]
}
나. Response
{
"message": "success",
"data": [
{
"category": "food",
"images": ["img1.jpg"]
},
{
"category": "beach",
"images": ["img2.jpg"]
},
{
"category": "others",
"images": ["img3.jpg", "img4.jpg"]
}
]
}
e. /api/albums/score
가. Request
{
"categories": [
{
"category": "food",
"images": ["img1.jpg"]
},
{
"category": "beach",
"images": ["img2.jpg"]
}
]
}
나. Response
{
"message": "success",
"data": [
{
"category": "food",
"images": [
{ "image": "img1.jpg", "score": 0.91 }
]
},
{
"category": "beach",
"images": [
{ "image": "img2.jpg", "score": 0.86 }
]
}
]
}
f. /api/albums/people
가. Request
{
"images": [
"img1.jpg",
"img2.jpg",
"img3.jpg",
"img4.jpg"
]
}
나. Response
{
"message": "success",
"data": [
{
"images": ["img1.jpg", "img3.jpg"],
"representative_face": {
"image": "img1.jpg",
"bbox": [x1, y1, x2, y2] // 첫 번째 이미지 기준 얼굴 좌표
}
},
{
"images": ["img2.jpg", "img4.jpg"],
"representative_face": {
"image": "img2.jpg",
"bbox": [x1, y1, x2, y2]
}
}
]
}
g. /api/style
가. Request
{
"image": "img1.jpg",
"style": "ghibli"
}
나. Response
{
"message": "success",
"data": "img1_styled.jpg"
}
h. 실패 응답 예시
가.400 Bad Request – 필드 누락 등 잘못된 요청 형식
{
"message": "invalid_request",
"data": null
}
나. 403 Forbidden – 인가되지 않은 접근
{
"message": "unauthorized_server",
"data": null
}
다. 422 Unprocessable Entity – URL 유효성 실패
{
"message" : "invalid_image_url",
"data" : [
"img4.jpg",
"img6.jpg"
]
}
라. 428 Precondition Required – 이미지 임베딩이 캐시되어 있지 않음
{
"message" : "invalid_image_url",
"data" : [
"img4.jpg",
"img6.jpg"
]
}
마. 500 Internal Server Error – 서버 처리 중 예기치 못한 오류
{
"message": "internal_server_error",
"data": null
}
5. 입력/출력 스키마
a. 공통 입력 스키마
from pydantic import BaseModel, HttpUrl, constr, Field
from typing import List
# /embeddings, /duplicates, /quality, /people 등에서 공통 사용
class ImageRequest(BaseModel):
images: List[HttpUrl]
# /category 요청용
class ImageConceptRequest(BaseModel):
concepts: list[str] = Field(default_factory=list)
images: List[HttpUrl]
# /scores 요청용
class ImageCategoryGroup(BaseModel):
category: str
images: List[HttpUrl]
class CategoryScoreRequest(BaseModel):
categories: List[ImageCategoryGroup]
# /style 요청용
class StyleRequest(BaseModel):
image: HttpUrl
style: constr(strip_whitespace=True, min_length=1)
b. 공통 출력 스키마
from typing import Optional, List, Union
from pydantic import BaseModel, HttpUrl
from typing_extensions import Literal
# 공통 메시지 포함
class BaseMessage(BaseModel):
message: str
# /embeddings
class EmptySuccessResponse(BaseMessage):
data: None
# /quality
class QualityResponse(BaseMessage):
data: List[HttpUrl]
# /duplicates, /people
class ClusteredImageResponse(BaseMessage):
data: List[List[HttpUrl]]
# /categories
class CategoryGroup(BaseModel):
category: str
images: List[HttpUrl]
class CategoryResponse(BaseMessage):
data: List[CategoryGroup]
# /scores
class ScoredImage(BaseModel):
image: HttpUrl
score: float
class ScoredCategory(BaseModel):
category: str
images: List[ScoredImage]
class ScoreResponse(BaseMessage):
data: List[ScoredCategory]
# /style
class StyleResponse(BaseMessage):
data: HttpUrl
c. 예외 응답 스키마
from typing import Optional, List, Dict
from pydantic import BaseModel, HttpUrl
from typing_extensions import Literal
# 400, 500용
class ErrorResponse(BaseModel):
message: str
data: Optional[None] = None
# 403: 인가되지 않은 요청
class UnauthorizedServerResponse(BaseModel):
message: Literal["unauthorized_server"]
data: Optional[None] = None
# 422: URL 유효성 오류
class InvalidImageUrlResponse(BaseModel):
message: Literal["invalid_image_url"]
data: List[HttpUrl]
# 428: 캐시 미존재로 인한 선행 임베딩 필요
class EmbeddingRequiredResponse(BaseModel):
message: Literal["embedding_required"]
data: List[HttpUrl]
d. 각 API와 연결 예시
| API Endpoint | Request Model | Success Response | Error Response |
|---|---|---|---|
/api/albums/embeddings |
ImageRequest |
EmptySuccessResponse |
UnauthorizedServerResponse, |
InvalidImageUrlResponse, ErrorResponse |
|||
/api/albums/duplicates |
ImageRequest |
ClusteredImageResponse |
UnauthorizedServerResponse, InvalidImageUrlResponse, EmbeddingRequiredResponse, ErrorResponse |
/api/albums/quality |
ImageRequest |
QualityResponse |
UnauthorizedServerResponse, InvalidImageUrlResponse, EmbeddingRequiredResponse, ErrorResponse |
/api/albums/categories |
ImageConceptRequest |
CategoryResponse |
UnauthorizedServerResponse, InvalidImageUrlResponse, EmbeddingRequiredResponse, ErrorResponse |
/api/albums/score |
CategoryScoreRequest |
ScoreResponse |
UnauthorizedServerResponse, InvalidImageUrlResponse, EmbeddingRequiredResponse, ErrorResponse |
/api/albums/people |
ImageRequest |
ClusteredImageResponse |
UnauthorizedServerResponse, InvalidImageUrlResponse, EmbeddingRequiredResponse, ErrorResponse |
/api/style |
StyleRequest |
StyleResponse |
UnauthorizedServerResponse, InvalidImageUrlResponse, ErrorResponse |
6. 서비스 구조에서의 역할 및 연동 관계
a. 아키텍처 요약
(Client)
│
▼
[FastAPI Endpoint] ──> [Controller] ──> [Service Logic]
│
├─> Embedding
├─> Duplicate Detection
├─> Quality Assessment
├─> Category Classification
├─> Highlight Scoring
└─> People Clustering
↕ ↕
[core/cache] [core/image_loader]
-
공통 데이터 흐름:
- 사용자로부터 이미지 URL 리스트 수신
- 서비스에 해당하는
controller로 전달 - 임베딩 → 캐싱 → 후속 연산 등 수행
- 각 서비스는
services/내 모듈 호출 services/내 모듈은 필요시core/,utils/내 모듈 호출- 결과는
controller를 통해 응답 JSON으로 변환
-
동기/비동기 흐름 정리:
embedding ──> category ──> score ├─> duplicate └─> quality ────────────> people ────────────> style -
공통 에러 응답
Status message data 예시 400 "invalid_request" null 403 “unauthorized_server” null 422 "invalid_image_url" { "images": ["invalid_url1", "url2"] }428 "embedding_required" { "missing": ["url1", "url2", ...] }500 "internal_server_error" null