모델 API 설계 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
API 명세 페이지
보안 정책
외부 클라이언트(브라우저 등)에서의 직접 접근은 차단되며, 허용된 도메인(예: https://backend.example.com
)을 통해 프록시된 요청만 처리
1. 엔드포인트 목록 및 기능 설명
기능 | 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 등)로 이미지 변환 요청 |
2. API 요청/응답 예시
/api/albums/embeddings
▸ - Request
{
"images": [
"http://server:8000/img1.jpg",
"http://server:8000/img2.jpg"
]
}
- Response
{
"message": "success",
"data": null
}
/api/albums/duplicates
▸ - Request
{
"images": [
"http://server:8000/img1.jpg",
"http://server:8000/img2.jpg",
"http://server:8000/img3.jpg",
"http://server:8000/img4.jpg"
]
}
- Response
{
"message": "success",
"data": [
["http://server:8000/img1.jpg", "http://server:8000/img3.jpg"],
["http://server:8000/img2.jpg", "http://server:8000/img4.jpg"]
]
}
/api/albums/quality
▸ - Request
{
"images": [
"http://server:8000/img1.jpg",
"http://server:8000/img2.jpg",
"http://server:8000/img3.jpg",
"http://server:8000/img4.jpg"
]
}
- Response
{
"message": "success",
"data": [
"http://server:8000/img2.jpg",
"http://server:8000/img3.jpg"
]
}
/api/albums/categories
▸ - Request
{
"images": [
"http://server:8000/img1.jpg",
"http://server:8000/img2.jpg",
"http://server:8000/img3.jpg",
"http://server:8000/img4.jpg"
]
}
- Response
{
"message": "success",
"data": [
{
"category": "food",
"images": ["http://server:8000/img1.jpg"]
},
{
"category": "beach",
"images": ["http://server:8000/img2.jpg"]
},
{
"category": "others",
"images": ["http://server:8000/img3.jpg", "http://server:8000/img4.jpg"]
}
]
}
/api/albums/scores
▸ - Request
{
"categories": [
{
"category": "food",
"images": ["http://server:8000/img1.jpg"]
},
{
"category": "beach",
"images": ["http://server:8000/img2.jpg"]
}
]
}
- Response
{
"message": "success",
"data": [
{
"category": "food",
"images": [
{ "image": "http://server:8000/img1.jpg", "score": 0.91 }
]
},
{
"category": "beach",
"images": [
{ "image": "http://server:8000/img2.jpg", "score": 0.86 }
]
}
]
}
/api/albums/people
▸ - Request
{
"images": [
"http://server:8000/img1.jpg",
"http://server:8000/img2.jpg",
"http://server:8000/img3.jpg",
"http://server:8000/img4.jpg"
]
}
- Response
{
"message": "success",
"data": [
["http://server:8000/img1.jpg", "http://server:8000/img3.jpg"],
["http://server:8000/img2.jpg"]
]
}
/api/style
▸ - Request
{
"image": "http://server:8000/img1.jpg",
"style": "ghibli"
}
- Response
{
"message": "success",
"data": "http://server:8000/img1_styled.jpg"
}
▸ 실패 응답 예시
-
400 Bad Request – 필드 누락 등 잘못된 요청 형식
{ "message": "invalid_request", "data": null }
-
403 Forbidden – 인가되지 않은 접근
{ "message": "unauthorized_server", "data": null }
-
422 Unprocessable Entity – URL 유효성 실패
{ "message" : "invalid_image_url", "data" : [ "http://server:8000/img4.jpg", "http://server:8000/img6.jpg" ] }
-
428 Precondition Required – 이미지 임베딩이 캐시되어 있지 않음
{ "message" : "invalid_image_url", "data" : [ "http://server:8000/img4.jpg", "http://server:8000/img6.jpg" ] }
-
500 Internal Server Error – 서버 처리 중 예기치 못한 오류
{ "message": "internal_server_error", "data": null }
3. 입력/출력 스키마
▸ 공통 입력 스키마
from pydantic import BaseModel, HttpUrl, constr
from typing import List
# /embeddings, /duplicates, /quality, /categories, /people 등에서 공통 사용
class ImageListRequest(BaseModel):
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)
▸ 공통 출력 스키마
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
▸ 예외 응답 스키마
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]
▸ 각 API와 연결 예시
API Endpoint | Request Model | Success Response | Error Response |
---|---|---|---|
/api/albums/embeddings |
ImageListRequest |
EmptySuccessResponse |
UnauthorizedServerResponse , |
InvalidImageUrlResponse , ErrorResponse |
|||
/api/albums/duplicates |
ImageListRequest |
ClusteredImageResponse |
UnauthorizedServerResponse , InvalidImageUrlResponse , EmbeddingRequiredResponse , ErrorResponse |
/api/albums/quality |
ImageListRequest |
QualityResponse |
UnauthorizedServerResponse , InvalidImageUrlResponse , EmbeddingRequiredResponse , ErrorResponse |
/api/albums/categories |
ImageListRequest |
CategoryResponse |
UnauthorizedServerResponse , InvalidImageUrlResponse , EmbeddingRequiredResponse , ErrorResponse |
/api/albums/scores |
CategoryScoreRequest |
ScoreResponse |
UnauthorizedServerResponse , InvalidImageUrlResponse , EmbeddingRequiredResponse , ErrorResponse |
/api/albums/people |
ImageListRequest |
ClusteredImageResponse |
UnauthorizedServerResponse , InvalidImageUrlResponse , EmbeddingRequiredResponse , ErrorResponse |
/api/style |
StyleRequest |
StyleResponse |
UnauthorizedServerResponse , InvalidImageUrlResponse , ErrorResponse |
4. 서비스 구조에서의 역할 및 연동 관계
아키텍처 요약
(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