서비스 아키텍처 모듈화 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
아키텍쳐 다이어그램
아키텍쳐 페이지
1. 아키텍처 구조
app/
├── main.py
├── api/
│ ├── main.py
│ ├── endpoints/
│ │ ├── album_embedding_router.py # [앨범 임베딩 요청]
│ ├── album_duplicate_router.py # [중복 판별]
│ │ ├── album_quality_router.py # [이미지 품질 측정(흐린 사진 등)]
│ │ ├── album_category_router.py # [이미지 분류 요청]
│ │ ├── album_score_router.py # [하이라이트 점수 측정]
│ │ ├── album_people_router.py # [인물 클러스터링]
│ └── style_router.py # [스타일 변환 요청]
│ └── controllers/
│ ├── album_embedding_controller.py
│ ├── album_duplicate_controller.py
│ ├── album_quality_controller.py
│ ├── album_category_controller.py
│ ├── album_score_controller.py
│ ├── album_people_controller.py
│ └── style_controller.py
├── models/
│ ├── clip_loader.py # CLIP 모델 로드 및 관리
│ └── face_loader.py # 얼굴 임베딩 모델 로드
├── core/ # 서비스 로직에서 공통적으로 사용되는 기능
│ ├── image_loader.py # 이미지 불러오기
│ ├── image_cropper.py # 얼굴 crop 전처리
│ ├── cache.py # Redis or 메모리 기반 캐싱 유틸
│ ├── similarity.py # 벡터 유사도 비교 함수
│ └── text_embedding_loader.py # 태그/카테고리용 텍스트 임베딩 로더
├── services/
│ ├── album/
│ │ ├── clip/
│ │ │ ├── preprocess.py # clip용 preprocess
│ │ │ ├── embedding.py # 이미지 임베딩 추출
│ │ │ ├── duplicate.py # 중복 사진 판별
│ │ │ ├── quality.py # 사진 품질 측정
│ │ │ ├── category.py # 카테고리 분류
│ │ │ ├── score.py # 하이라이트 점수 추출
│ │ ├── face_recog/
│ │ │ ├── crop_face.py # 얼굴 crop 처리
│ │ │ └── embedding.py # 얼굴 임베딩 생성
│ │ │ └── people.py # 인물 클러스터링
│ └── thumbnail/
│ ├── prompt_generator.py # 프롬프트 생성 역할 (Gemini)
│ └── style_transformer.py # 스타일 변환 역할 (GPU 호출)
├── schemas/
│ ├── album_schema.py # Pydantic: 앨범 생성 요청/응답
│ └── style_schema.py # Pydantic: 스타일 변환 요청/응답
├── utils/ # 단순한 보조 함수
│ ├── image_converter.py # base64 <-> PIL 변환 등
│ ├── validation.py # url 유효성 검사 / 이미지 형식 검증 등
│ ├── logger.py # log 담당
│ └── error_handler.py # error 처리
│
│ -------------------- 아래는 V2부터 반영 --------------------
│
├── workers/
│ ├── duplicate_worker.py # 메시지 큐 소비자 (중복 판별)
│ ├── quality_worker.py # 메시지 큐 소비자 (품질 분석)
│ ├── category_worker.py # 메시지 큐 소비자 (카테고리 분류)
│ ├── score_worker.py # 메시지 큐 소비자 (하이라이트 스코어)
│ └── utils.py # 공통 로직: 메시지 큐 consume, 로깅 등
├── queues/
│ ├── publisher.py # 메시지 큐 발행 로직 (controller → queue)
│ └── consumer_base.py # 워커가 상속받는 공통 큐 소비자 클래스
│
└── config/
└── settings.py # 환경 변수, 큐 연결 설정, 모델 경로 등
---------------------- 아래는 V3부터 반영 ---------------------
embedding_service/
├── main.py # Entrypoint (FastAPI app)
│
├── endpoints/
│ └── embed_router.py # /embed, /face-embed 등 엔드포인트 정의
│
├── controllers/
│ └── embed_controller.py # 요청 처리 → image_loader → embedding 호출
│
├── services/
│ ├── image_loader.py # 이미지 URL/base64 로딩 및 전처리
│ ├── model_loader/
│ │ ├── clip_loader.py # CLIP 모델 로딩 및 지연 캐싱
│ │ └── face_recog_loader.py # 얼굴 임베딩 모델 로딩
│ └── embedding/
│ ├── clip_embedder.py # CLIP 임베딩 실행 로직
│ └── face_embedder.py # 얼굴 임베딩 실행 로직
│
├── schemas/
│ └── request_schema.py # 요청 파라미터 검증용 Pydantic 모델
│
├── config/
│ └── settings.py # 모델 경로, 로깅, 디바이스 설정 등
│
└── utils/
└── timer.py / logger.py # 로깅, 추론 시간 측정 등
2. 각 모듈의 책임과 기능
api/endpoints/
a. - RESTful API 엔드포인트를 정의
- 책임: 외부와 내부 모듈의 인터페이스
- 분리 이유: 명확한 API 관리 및 버저닝
api/controllers/
b. - 요청 검증 후 service 계층에 작업 위임
- 책임: API 계층과 서비스 계층 간의 중간 조율자 역할
- 분리 이유: 비즈니스 로직과 API 논리의 관심사 분리
models/
c. - service를 수행할 모델을 load
- 책임: 모델 로딩, 디바이스 할당, 모델 버전 관리
- 분리 이유: 비즈니스 로직을 수행하는 모델 관리의 책임을 분산
services/
d. - 실제 기능 수행 로직 (이미지 임베딩, 품질 평가 등)
album/clip
/album/facemodel
/thumbnail
로 하위 기능을 도메인별로 나눔- 책임: AI 모델 실행, 전처리, 후처리, 비즈니스 로직
- 분리 이유: 독립적 서비스 기능 확장을 위한 명확한 책임 분리
core/
e. - 전역적으로 재사용되는 모듈 모음
- 예: 이미지 로더, 유사도 측정, 캐시, 텍스트 임베딩 로더
- 책임: 서비스 간 공통 유틸리티
- 분리 이유: 중복 코드 제거, 공통화, 유지보수성 확보
utils/
f. - 단순 기능: base64 변환, 유효성 검사, 로깅, 에러 핸들링
- 책임: 코어나 서비스에 포함될 정도는 아니지만 반복되는 도우미 함수
- 분리 이유: 기타 로직과 명확히 구분하고 가볍게 유지
workers/
(V2)
g. - 메시지 큐를 통해 전달된 태스크를 소비하고 처리하는 실행 단위
- 예: 중복 판별 워커, 품질 평가 워커, 하이라이트 스코어 워커, 스타일 변환 워커
- 책임: 각 태스크별 비동기 연산을 병렬로 수행
- 분리 이유: 컨트롤러와 서비스 레이어에서 무거운 연산을 분리하고, 메시지 기반 처리 구조 확립
queues/
(V2)
h. - 메시지 큐 발행/구독 및 큐 라우팅 관련 로직 집합
- 예:
publish_duplicate_task()
,consume_from_queue()
,BaseWorker
- 책임: Controller나 Worker가 사용할 수 있는 큐 인터페이스 제공
- 분리 이유: 메시지 큐 의존성을 캡슐화하고, 큐 구현체 변경(Redis → Kafka 등)에 유연하게 대응
config/
(V2)
i. - 환경 변수, 경로, 하이퍼파라미터 등 설정 관리
- 예: 모델 경로, 디바이스(
cpu
,cuda
), Redis 호스트, 큐 이름, 로깅 레벨 - 책임: 서비스 전반의 설정을 통합적으로 관리
- 분리 이유: 코드 내 하드코딩 제거, 설정의 일관성과 변경 용이성 확보
2. 모듈간 인터페이스 설계
a. 데이터 포맷
- 모든 API는
application/json
기반 - 요청/응답은 모두
schemas/
에 정의된 Pydantic 모델 기반
가. 예시: 이미지 임베딩 요청
POST /api/v1/albums/embeddings
Content-Type: application/json
{
"images": ["http://server:8000/img1.jpg", "http://server:8000/img2.jpg"]
}
{
"message": "success",
"data": null
}
나. 내부 처리 흐름(V1)
[endpoint]
↓
[controller]
↓
[services/album/clip/embedding.py] # CLIP embedding 수행
↓
[core/image_loader + core/cache] # 이미지 로드, embedding 캐시 저장
↓
[services/album/clip/*.py] # 중복 판별, 점수 계산, 품질 측정, 카테고리 분류
[services/album/face_recog/*.py] # 얼굴 crop 및 클러스터링
[services/thumbnail/style_transformer.py] # 스타일 변환
- 모든 내부 호출은 동기 함수 기반, 향후 비동기 리팩토링 여지 확보
다. 내부 처리 흐름 (v2)
[endpoint]
↓
[controller]
↓
[core/cache] # 캐시에 embedding 있는지 확인
↓
[services/album/clip/embedding.py] # embedding 직접 수행
↓
[core/cache] # embedding 저장
↓
[queues/publisher] # 중복 판별, 점수 계산 등 작업을 메시지 큐에 발행
↓
[message queue]
↓
[workers/*_worker.py] # 큐 소비 → 태스크 처리
↓
[core/cache] # embedding 불러오기
↓
[services/album/clip/*.py] # duplicate.py, score.py, category.py, quality.py
[services/album/face_recog/*.py] # people.py (얼굴 클러스터링)
[services/thumbnail/style_transformer.py] # 스타일 변환
라. v1과 v2의 주요 차이점
항목 | v1 | v2 |
---|---|---|
태스크 처리 방식 | 동기적으로 한 번에 수행 | embedding 이후는 비동기 큐 처리 |
워커 사용 여부 | 없음 | 있음 (duplicate, score 등 분리 처리) |
embedding 위치 | services 내부 | 여전히 내부에 있음 (단, 큐와 분리 구조로 정리됨) |
embedding 재사용 | 암묵적 재사용 | 명시적으로 캐시에 저장하고 조회 |
3. 모듈화의 기대 효과 및 장점
항목 | 기대 효과 |
---|---|
개발 효율성 | 팀원이 각 서비스 기능을 독립적으로 개발 및 테스트 가능 |
유지보수 용이성 | 특정 기능 변경 시 관련 모듈만 수정하면 됨 (예: quality.py 만 수정) |
확장성 확보 | 새로운 기능이나 모델 추가 시 기존 코드에 영향 없음 |
재사용성 증가 | core/ , utils/ 내 기능은 모든 서비스에서 공통 활용 가능 |
CLIP embedding 재사용 | |
배포 최적화 | 향후 병목현상 등 발생 시, AI 서비스별로 마이크로서비스 구조로 이관 가능 |
4. 팀 서비스 시나리오와의 적합성 근거
a. 시나리오 기반 예시 1: 모델 교체 혹은 추가
- 예: 현재 CLIP 모델 외에 Mistral 기반 텍스트 태깅 모델 추가
- ➜
services/album/clip/
과 병행하여services/album/mistral/
디렉토리만 추가하면 됨 - ➜ API/Controller/Core는 변경할 필요 없음
b. 시나리오 기반 예시 2: 품질 평가 기준 변경
- 흐림 뿐만 아니라 밝기나 노이즈 기준 추가 요청 발생
- ➜
quality.py
내부의 품질 판단 함수만 변경하면 전체 시스템 반영 완료 - ➜ API는 그대로 유지되므로 사용자/프론트엔드는 영향 없음
c. 시나리오 기반 예시 3: 하이라이트 점수 개선 로직
- 새 점수 기준을 A/B 테스트하고 싶은 경우
- ➜
score.py
내부 로직만 분기처리하면 테스트 가능
5. 흐름 요약
a. services/album/
POST /api/v1/albums/embeddings
)
가. 이미지 임베딩 요청 (-
album_embedding_router.py
→album_embedding_controller.py
→
embedding.py
-
임베딩해서 캐시에 저장(
image_loader.py
→preprocess.py
→embedding.py
→cache.py
) -
클라이언트에 임베딩 성공했음을 알림. 임베딩 성공 후, 캐싱된 임베딩으로태깅&중복 판별&인물 클러스터링이 비동기로 진행됨.
POST /api/v1/albums/duplicates, POST /api/v1/albums/quality
)
나. 중복/저품질 이미지 판별 요청 (-
album_duplicate_router.py
→album_duplicate_controller.py
→
duplicate.py
→cache.py
→similarity.py
-
album_quality_router.py
→album_quality_controller.py
→
quality.py
→cache.py
→quality.py
-
유사도 기반으로 중복 이미지 / 저품질(흐린) 이미지 그룹화
POST /api/v1/albums/categories
, POST /api/v1/albums/scores
, POST /api/v1/albums/people
)
다. 이미지 분류/하이라이트 점수/인물 클러스터링 요청 (-
album_category_router.py
→album_category_controller.py
→
category.py
→cache.py
→text_embedding_loader.py
→similarity.py
-
album_score_router.py
→album_score_controller.py
→score.py
→cache.py
→score.py
-
album_people_router.py
→album_people_controller.py
→
people.py
→facemodel/embedding.py
→crop_face.py
→people.py
b. services/thumbnail/
POST /api/v1/style
)
가. 스타일 변환 요청 (-
style_router.py
→style_controller.py
→
image_downloader.py
(이미지 다운로드)→
gemini_service.py
(이미지 기반 프롬프트 생성)→
image_converter.py
(PIL → base64 변환)→
gpu_server_service.py
(base64 + 프롬프트 전달)→
s3_uploader.py
(결과 이미지 S3 업로드 후 URL 반환)