AI‐3단계‐ 서비스 아키텍처 모듈화 - 100-hours-a-week/KTB4-5th-wiki GitHub Wiki
스마트 냉장고의 AI 기능은 이미지 분석과 레시피 추천이라는 성격이 다른 두 흐름으로 구성됩니다. 재료 원본은 백엔드 MySQL이 관리하고 AI는 요청으로 받은 재고 스냅샷만 사용합니다. 레시피 원본은 AI PostgreSQL이 관리하며 백엔드 MySQL의 레시피 테이블은 동기화 사본입니다.
따라서 사용자 서비스, 이미지 분석, 레시피 추천, 모델 운영을 분리합니다. 단, 초기부터 모든 기능을 독립 서버로 배포하는 것은 현재 예산에 맞지 않습니다. 코드와 책임은 모듈로 분리하고, 배포는 트래픽에 맞춰 단계적으로 분리합니다.
여기서 모듈화는 파일이나 클래스 이름만 나누는 것을 뜻하지 않습니다. 한 클래스가 요청 검증, 모델 호출, 결과 판정, 데이터 저장까지 모두 처리하지 않도록 책임과 데이터 수정 권한을 분리합니다. 현재 실행은 단일 FastAPI 인스턴스의 인메모리 큐와 소비자 코루틴 2개를 사용하고, 논리 모듈은 공개 인터페이스로 협력합니다.
flowchart TB
APP[모바일 PWA] --> API[서비스 백엔드]
API --> MY[(백엔드 MySQL<br/>사용자·냉장고·재료 원본<br/>레시피 사본)]
APP --> OBJ[(S3 이미지)]
API --> GATE[단일 FastAPI AI 서버]
GATE -->|허용된 객체 읽기| OBJ
GATE --> Q[인메모리 asyncio.Queue]
Q --> ORCH[소비자 코루틴·오케스트레이터]
ORCH --> QUALITY[입력 분류]
QUALITY --> RECEIPT[영수증 OCR]
QUALITY --> PRODUCT[실물 분석]
RECEIPT --> NORM[보강·정규화·검증]
PRODUCT --> NORM
NORM --> MEM[인메모리 상태·결과]
API -->|재고 스냅샷| RECIPE[레시피 검색·추천]
RECIPE --> PG[(AI PostgreSQL<br/>레시피 원본)]
API -->|커서 기반 변경 조회| GATE
GATE --> PG
API -->|트랜잭션 동기화| MY
GATE --> OBS[로그·메트릭]
GATE -->|분석 완료 콜백| API
EC2 FastAPI는 인메모리 큐·소비자 2개·LangGraph 재시도·후처리·완료 콜백을 담당합니다. RunPod GPU Worker는 MobileNetV3·PP-OCRv5·YOLO-World·Gemma를 실행하고 좌표 기반 상품행과 실물 다중 포장 후보를 반환합니다. PostgreSQL은 상품 카탈로그·별칭·소비기한 규칙·레시피 원본을 관리하며 OpenTelemetry·Phoenix·Prometheus·Grafana가 추론과 서비스 지표를 관측합니다. Qwen-VL, vLLM, 벡터 검색은 품질·처리량 기준을 충족하지 못할 때 검토하는 대체 기술입니다.
외부 영수증 21장 최초 평가의 종합점수는 87.8%였고, OCR부터 후처리까지 전체 95% 완료시간은 18.44초였습니다. 이후 기존 OCR 결과로 세 번 반복한 후처리 비교에서는 정밀도 95.1%, 재현율 73.6%, 종합점수 83.0%였으며 후처리만의 95% 완료시간은 약 11.9~15.7초였습니다. 두 실험의 시간 측정 범위는 다르며 실제 EC2 측정값도 아닙니다. 현재 결과는 RECOGNIZED 후보 승인 기준을 통과하지 못했으므로, 아키텍처가 완성되더라도 별도 촬영 평가 자료와 실제 EC2 측정을 통과하기 전에는 해당 상태의 적용 범위를 확대하지 않습니다. 승인 후에도 사용자 확인 전 재고 저장은 허용하지 않습니다.
| 원칙 | 우리 서비스 적용 방식 |
|---|---|
| 하나의 변경 이유 | 문자 인식, 상품행 구성, 정규화, 날짜 검증, 상태 판정을 서로 다른 모듈로 둡니다. |
| 작은 공개 인터페이스 | 다른 모듈은 내부 모델 객체나 테이블에 접근하지 않고 classify, extract, normalize, evaluate 같은 공개 기능만 호출합니다. |
| 의존성 역전 | 분석 오케스트레이터는 PP-OCRv5와 Gemma의 구체 구현이 아니라 TextExtractor, FoodCandidateClassifier에 의존하고, 추천 오케스트레이터는 공급자 SDK 대신 RecipeSourcePort에 의존합니다. |
| 명령과 조회 분리 | 분석 실행·피드백 저장처럼 상태를 바꾸는 기능과 결과·모델 상태를 읽는 기능을 분리합니다. |
| 데이터 소유권 분리 | 재료 원본 수정은 백엔드만, 레시피 원본 수정은 AI만, 백엔드 레시피 사본 갱신은 백엔드 동기화 작업만 수행합니다. 분석 작업·결과는 FastAPI 메모리에만 둡니다. |
| 실패 격리 | 한 모듈의 실패를 예외로 전체에 전파하지 않고 정해진 실패 결과로 변환하여 확인 필요 또는 직접 입력으로 전환합니다. |
| 외부 의존성 격리 | S3, MySQL, PostgreSQL, Gemma와 레시피 공급자 API는 어댑터 뒤에 두어 핵심 규칙이 특정 제품의 SDK에 묶이지 않게 합니다. |
| 읽기 쉬운 이름과 짧은 함수 | 함수는 한 작업만 수행하고, processData 같은 이름 대신 extractReceiptLines, rejectNonFoodItem처럼 의도를 드러냅니다. |
다음 항목은 코드 검토에서 God Class·God Object 신호로 판단합니다.
- 생성자 의존성이 7개 이상으로 계속 증가하는 클래스
- 서로 다른 도메인의 저장소를 두 개 이상 직접 수정하는 클래스
- 분류·문자 인식·정규화·상태 판정을 한 메서드에서 모두 수행하는 코드
- 모델 종류를 확인하는
if또는switch가 여러 모듈에 반복되는 구조 - 요청·응답 객체가 모델 실행 객체와 데이터베이스 객체를 동시에 겸하는 구조
- 한 클래스의 변경이 영수증, 실물, 추천 기능의 테스트를 모두 깨뜨리는 구조
위 신호가 나타나면 단순히 메서드만 잘게 나누지 않고, 책임과 데이터 소유권을 기준으로 클래스를 분리합니다. 다만 클래스 줄 수만으로 기계적으로 쪼개거나 인터페이스를 하나의 구현마다 만들지는 않습니다. 실제로 교체 가능성이 있거나 외부 시스템과 경계를 이루는 지점에 인터페이스를 둡니다.
책임
- 사용자 인증과 냉장고 접근 권한 확인
- 이미지 업로드와 분석 요청 생성
- AI 결과를 사용자 화면 형식으로 변환
- 사용자 수정값을 재고와 AI 피드백으로 전달
분리 이유
AI API에 사용자 인증과 가족 권한을 중복 구현하지 않기 위해 서비스 경계를 유지합니다. AI는 식재료 분석에 집중하고 사용자가 어떤 냉장고에 접근 가능한지는 서비스가 결정합니다.
책임
- AI 내부 API의 단일 진입점
- 요청 스키마 검증과 멱등성 보장
- 작업 상태 및 모델 버전 추적
- 요청량 제한 적용
- 자체 분석 결과를 공통 API 형식으로 변환
AI Gateway는 이미지 내용을 분석하거나 상품명을 정규화하지 않습니다. 사용자 재고도 직접 수정하지 않습니다.
분리 이유
백엔드가 MobileNetV3, PP-OCRv5와 Gemma의 호출법을 알지 않도록 합니다. 내부 모델이 바뀌어도 서비스 API 계약은 유지됩니다.
책임
- 인메모리 큐에서 작업을 하나씩 꺼내 입력 유형에 따라 영수증·실물·미인식 경로 실행
- 파이프라인 버전에 따른 단계 순서와 허용 가능한 다음 상태 정의
- 프로세스 수명 안의 요청 멱등성, 단계별 타임아웃과 제한된 재시도 관리
- 분석 결과와 추적 정보 결합
오케스트레이터는 파이프라인 상태와 허용 가능한 다음 단계를 정의하고 각 모듈의 공개 인터페이스를 순서대로 호출합니다. 문자 인식 규칙, 식품 판별 규칙, 날짜 계산식과 상태 임계값을 내부에 넣지 않습니다.
분리 이유
영수증과 실물 사진은 필요한 모델이 다릅니다. 분기 로직을 각 모델 안에 넣지 않고 오케스트레이터에서 관리해야 경로별 비용과 성공률을 측정할 수 있습니다.
책임
- 흐림, 어두움, 반사, 잘림 확인
-
RECEIPT,PRODUCT,OTHER분류 - 처리 불가능한 이미지를 조기에 차단
- 추가 학습한 MobileNetV3-Small의 버전과 신뢰도 기록
분리 이유
사용할 수 없는 이미지를 비싼 모델에 전달하지 않고 즉시 재촬영을 요청합니다. 이 모듈은 비교적 빠르게 자체 모델로 발전시킬 수 있습니다.
책임
- 영수증 영역 보정과 전체 문맥 보존
- PP-OCRv5로 문자·신뢰도·좌표 추출
- 카드·전화·주소·회원번호 등 민감정보를 중간 저장·Gemma·외부 API 전달 전에 마스킹
- 헤더·합계 구간과 가격·수량 좌표로 상품행 후보 구성
- 키워드 비식품 규칙과 PostgreSQL
receipt_ai상품·별칭 자료 우선 조회 - 사전에 없는 후보는 같은 작업 실행 안에서 Gemma 보완 모듈로 전달
- OCR 원문과 선택 결과의 일치 여부를 검증할 자료 생성
분리 이유
영수증은 객체 인식보다 문서 구조와 한국어 문자 인식 이후의 상품행 판별이 중요합니다. 21장 반복 확인에서 정밀도 95.1%에 비해 재현율이 73.6%였고 긴 영수증 누락이 집중됐습니다. 그러나 고정 길이 분할과 상품 영역만 선제 입력하는 방식은 정확도를 낮췄으므로, 실물 분석과 분리해 종이 영역 보정과 전체 문맥을 유지한 좌표·가격·수량 관계 구성을 독립적으로 개선합니다.
여기서 전체 문맥은 마스킹 후 남은 상품명·가격·수량의 배치 관계를 뜻하며 카드번호·전화번호·주소 같은 개인정보 원문을 보존한다는 의미가 아닙니다.
책임
- 패키지 문자와 시각 특징 인식. 바코드 해독·상품 DB 조회는 보류
- 비포장 식재료 및 복수 객체 후보 탐지
- 상품명·수량·날짜 영역 후보 생성
- 자체 경로가 확정하지 못한 어려운 사례는
NEEDS_REVIEW또는 직접 입력으로 전환
분리 이유
포장 식품과 비포장 재료는 시각적 특징이 중요합니다. OCR 실패와 상품 인식 실패를 구분해야 필요한 학습 데이터를 정확히 수집할 수 있습니다.
실물의 포장 문자·시각 후보도 상품·별칭 사전을 조회합니다. 사전에 없는 실물은 Gemma 영수증 상품행 경로로 보내지 않습니다. 자체 실물 경로가 확정하지 못하고 단계 6의 조건을 통과하면 외부 API로 보완하며, 허용되지 않거나 보완 결과도 부족하면 확인 필요 또는 미인식으로 종료합니다.
책임
- 상품명과 OCR 문자열을 표준 식재료명으로 변환
- 고정된 열한 개 카테고리 중 하나로 분류
- ERD의
measure_type에 따라COUNT개수와WEIGHT측정량을 배타적으로 만들고 측정량을G또는ML로 표준화 - 동의어·브랜드명·축약 상품명 사전 관리
- 식재료 카테고리의
OTHER(기타 식재료), 입력 유형의OTHER(분석 대상 아님), 식별 실패를 구분
분리 이유
이미지 분석 과정이 반환한 자유로운 상품명을 재고와 레시피에서 바로 비교하면 같은 재료가 여러 이름으로 처리됩니다. 현재 ERD에는 ingredients와 recipe_ingredients를 잇는 표준 식재료 ID가 없으므로, 공통 ID가 있다고 가정하지 않고 이름 정규화 버전과 원문을 함께 유지합니다. 표준 식재료 ID는 ERD가 확장된 뒤에만 공통 계약으로 도입합니다.
책임
- OCR 날짜 형식과 미래·과거 범위 검증
- 제조일, 소비기한, 유통기한의 의미 구분
- 표시 날짜가 없을 때 PostgreSQL
receipt_ai.shelf_life_rules의 승인된 보관기간 자료 조회 - 보관조건과 자료 버전이 있는 추정 기간 반환
분리 이유
이미지에서 읽은 날짜와 지식으로 예상한 날짜는 신뢰 수준이 다릅니다. 이 모듈에서 출처와 가정을 강제하여 UI와 알림에서 혼동되지 않게 합니다.
책임
- 모델 신뢰도, 필수값, 화질, 날짜 규칙을 종합
-
RECOGNIZED,UNRECOGNIZED,NEEDS_REVIEW,AI_ESTIMATED결정 - 사용자에게 보여줄 확인 사유 생성
- 모델 버전별 임계값 관리
분리 이유
모델이 스스로 자신을 인식 성공으로 판정하게 두지 않습니다. 서비스의 안전 기준을 모델 밖의 정책으로 두면 모델 교체 후에도 동일한 사용자 경험을 유지할 수 있습니다.
책임
- 재고 스냅샷과 사용자 조건 검증
- 서비스 백엔드가 검증해 전달한 제외 재료 선제 제외
- AI PostgreSQL의 레시피 원본에서 후보 조회
- 임박 재료 활용도 기반 랭킹
- 정해진 가중치와 템플릿으로 추천 이유와 부족 재료 반환
분리 이유
이미지 분석은 재고 등록 시 발생하지만 레시피 추천은 사용자가 식사를 고민할 때 반복됩니다. 초기에는 같은 배포 안의 독립 모듈로 두고, 트래픽이나 변경 주기가 실제로 달라졌을 때만 별도 서비스로 분리합니다. 초기 출시에서는 벡터 데이터베이스나 생성 모델을 필수 구성으로 두지 않습니다.
책임
- 모델, 프롬프트, 데이터, 정책 버전 관리
- 고정 평가 세트 결과 보관
- 배포 승인 기준 확인
- 카나리와 롤백 대상 관리
분리 이유
자체 모델·프롬프트·사전·정책 버전의 결과를 같은 기준으로 비교하고, 정확도 저하 없이 승인·롤백하기 위해 필요합니다.
각 AI 모듈은 다음 네 영역을 기본 구조로 사용합니다. 작은 모듈은 디렉터리를 억지로 모두 만들지 않아도 되지만 의존 방향은 유지합니다.
module
├─ domain # 식품 후보, 소비기한, 상태 판정 등 순수 규칙
├─ application # 한 가지 사용 흐름을 실행하는 기능
├─ ports # 필요한 저장소·모델·외부 서비스 인터페이스
└─ adapters # PP-OCRv5, Gemma, MySQL, PostgreSQL, S3, 레시피 공급자 API 구현
의존 방향은 adapters → ports ← application → domain을 따릅니다. domain은 AWS SDK, 웹 프레임워크, ORM, Ollama 같은 실행 기술을 import하지 않습니다. 이 구조를 사용하면 PP-OCRv5나 PostgreSQL 조회 구현을 바꿔도 상품 판정 규칙과 테스트를 유지할 수 있습니다.
대표 인터페이스는 다음과 같이 작게 유지합니다.
InputClassifier.classify(image) -> InputTypeResult
TextExtractor.extract(image) -> OcrDocument
ReceiptRowBuilder.build(document) -> ReceiptRowCandidate[]
ProductCatalog.find(candidate) -> ProductMatch
FoodCandidateClassifier.classify(candidate) -> FoodCandidateResult
IngredientNormalizer.normalize(candidate) -> NormalizedIngredient
ExpirationPolicy.evaluate(candidate, knowledge) -> ExpirationResult
RecognitionPolicy.evaluate(context) -> RecognitionStatus
어떤 구현도 다음 단계의 내부 객체를 직접 수정하지 않습니다. 각 단계는 변경 불가능한 결과를 반환하고 오케스트레이터는 허용된 상태 전환과 버전만 검증합니다.
| 데이터 | 읽을 수 있는 모듈 | 수정할 수 있는 모듈 | 제한 이유 |
|---|---|---|---|
| 사용자·가족 권한 | 서비스 API | 인증·권한 모듈 | AI 코드가 사용자 권한을 우회하지 못하게 함 |
| 냉장고 재고 | 서비스 API, 추천 모듈 | 재고 모듈 | 모델 결과가 사용자 확인 없이 재고를 변경하지 못하게 함 |
| 원본 이미지 | 이미지 분석 작업자 | 서비스 API의 업로드·삭제 기능 | 모델과 일반 로그가 원본을 임의 보관하지 못하게 함 |
| 분석 작업·중간 결과·최종 응답 | FastAPI 런타임 | 해당 작업 실행 | 프로세스 수명 동안 메모리에만 보관하고 재시작 복구를 보장하지 않음 |
| 상품·별칭·비식품 사전 | 분석 모듈 | 정규화 관리 기능 | 여러 모델이 서로 다른 표준명을 직접 등록하지 못하게 함 |
| 보관기간 자료 | 소비기한 모듈, 추천 모듈 | 승인된 지식 관리 기능 | 추정 날짜의 근거와 버전을 유지 |
| 사용자 확정값 | 평가 모듈 | 피드백 수집 기능 | 저장소·동의·보관·철회 정책 승인 전에는 수집하지 않음 |
| 모델·정책 버전 | 오케스트레이터, 평가 모듈 | 모델 레지스트리 | 실행 코드가 승인 없이 운영 모델을 교체하지 못하게 함 |
AI 서버는 백엔드 MySQL에 직접 접속해 재료나 레시피 사본을 수정하지 않습니다. 백엔드가 재고 스냅샷을 API로 전달하고, 백엔드 동기화 작업이 AI API에서 레시피 변경분을 읽어 자기 MySQL에 반영합니다. AI PostgreSQL 계정은 레시피 원본에 필요한 권한만 갖습니다. 보조 자료 저장소가 정해지면 별도 최소 권한을 설계합니다.
AI는 별도 PostgreSQL에서 레시피 원본을 관리합니다. 레시피 검색 공급자는 식품안전나라, TheMealDB, Tavily 순서이며 상품·별칭·보관기간 보조 자료와 레시피 원본은 AI PostgreSQL에서 스키마를 분리해 관리합니다. 분석 작업·결과는 PostgreSQL에 저장하지 않고 FastAPI 메모리에만 둡니다.
| 송신 | 수신 | 방식 | 주요 데이터 |
|---|---|---|---|
| 모바일 PWA | 서비스 API | HTTPS REST | 업로드 주소 요청, 분석 요청, 사용자 확인값 |
| 모바일 PWA | Amazon S3 | HTTPS PUT | 크기를 조절·압축한 이미지 파일 |
| 서비스 API | AI Gateway | 내부 HTTPS REST | 이미지 참조, 요청 ID, 로케일 |
| AI Gateway | 인메모리 큐 | asyncio.Queue |
분석 ID |
| 소비자 코루틴 | 추론 오케스트레이터 | 내부 호출 | 분석 ID, S3 객체 키, 파이프라인 버전 |
| 오케스트레이터 | 분석 모듈 | 공개 인터페이스 | 단계별 입력과 변경 불가능한 결과 |
| AI Gateway | 서비스 API | REST 조회 또는 완료 이벤트 | 상태, 식재료 후보, 근거, 모델 추적 |
| 서비스 API | 레시피 추천 모듈 | 초기에는 내부 함수, 분리 후 HTTPS REST | 냉장고 ID, 재고 지문, ERD 형식 재고, 검증된 제외 재료 |
| 추천 모듈 | AI PostgreSQL | SQL 질의 | 레시피 원본과 보조 자료 |
| 서비스 백엔드 동기화 작업 | AI Gateway | 내부 HTTPS REST | cursor, limit |
| 서비스 백엔드 동기화 작업 | MySQL | 트랜잭션 |
recipes, recipe_ingredients, recipe_steps 사본 |
| 서비스 API | AI Gateway | 내부 HTTPS REST | 사용자 확정값과 모델 개선 동의. 피드백 저장 정책 승인 전에는 비활성 |
정규화·날짜 검증은 같은 프로세스의 함수 호출로 실행합니다. CPU 추론은 이벤트 루프를 막지 않는 전용 실행기로 분리하되, 스레드와 자식 프로세스 중 어느 방식인지는 실측 후 확정합니다.
AI Gateway는 검증된 요청 전체를 인메모리 저장소에 만들고 asyncio.Queue에는 analysisId만 넣습니다. 단계 결과는 같은 작업 객체에 연결하되 일반 로그에는 전체 OCR이나 이미지 바이트를 남기지 않습니다.
{
"schemaVersion": "1.0",
"analysisId": "ana_01JEXAMPLE",
"requestId": "req_01JEXAMPLE",
"objectKey": "uploads/user-scope/2026/09/02/image-001.webp",
"inputHint": "AUTO",
"locale": "ko-KR",
"pipelineVersion": "image-analysis-1.0.0",
"stage": "QUEUED",
"attempt": 1
}작업 객체에 사용자 인증 토큰이나 냉장고 수정 권한을 넣지 않습니다. 재고 반영 여부는 서비스 API가 사용자 확인 후 결정합니다. 프로세스 재시작 시 작업·멱등성 기록·결과가 모두 사라지며 복구하지 못합니다.
- 단계 1의
/ai/v1요청·응답 계약을 모듈 간 외부 경계에서도 사용합니다. - 기존 필드의 의미와 자료형을 바꾸지 않고 새로운 선택 필드만 추가합니다.
- 호환되지 않는 변경은 새 API 버전과 새
schemaVersion으로 분리합니다. - 소비자는 알 수 없는 선택 필드를 무시하고, 필수 버전을 지원하지 못하면 처리하지 않습니다.
- 같은 프로세스 수명 안에서만
requestId와 요청 내용 해시로 중복 접수를 방지합니다. - 내부 예외 문자열을 전달하지 않고
INVALID_INPUT,MODEL_TIMEOUT,DEPENDENCY_UNAVAILABLE처럼 합의된 오류 코드로 변환합니다. - 모델·규칙·상품 사전 버전은 분석 시작 시 고정하고 결과에도 같은 버전을 기록합니다.
- 시간 초과와 재시도 횟수는 오케스트레이터가 관리하고, 각 모델 어댑터는 성공 또는 명시적인 실패 결과만 반환합니다.
{
"ingredientId": 101,
"name": "우유",
"category": "DAIRY",
"storageType": "REFRIGERATED",
"measureType": "WEIGHT",
"quantity": null,
"weightValue": 1000,
"weightUnit": "ML",
"expirationDate": "2026-09-10"
}COUNT이면 quantity만 사용하고, WEIGHT이면 weightValue와 weightUnit만 사용합니다. 두 측정 방식을 한 재고 행에 동시에 저장하지 않습니다. ingredientId는 ERD의 BIGINT에 맞춘 JSON 정수이며 expirationDate는 재고 저장 시 필수입니다. 상품 별칭과 표준화 후보는 현재 ERD의 ingredients 행에 섞어 저장하지 않고 별도 AI 보조 스키마가 승인될 때까지 추적 결과로만 유지합니다.
{
"analysisId": "ana_01JEXAMPLE",
"pipelineVersion": "image-analysis-1.0.0",
"route": ["quality-0.2", "receipt-ocr-0.4", "normalizer-0.3"],
"latencyMs": 2840,
"externalApiCostKrw": 0
}추천 결과는 점수만 주지 않고 사용한 임박 재료, 보유 재료, 부족 재료, 제외 조건, 출처를 포함해야 합니다. 그래야 사용자가 추천 이유를 이해하고 백엔드가 장보기 목록을 만들 수 있습니다.
2026-10-08 갱신: 모델 추론은 EC2가 아니라 원격 GPU Worker(RunPod)로 분리했습니다. EC2 FastAPI는 소비자 2개로 큐를 처리하며 이미지 바이트 대신 S3 서명 URL을 Worker에 전달합니다. 아래 목록의 CPU 실행기 관련 항목은 초기 설계 기록입니다.
- 서비스 API와 AI Gateway는 배포 단위를 분리합니다.
- PWA는 S3에 이미지를 직접 업로드하고 서비스 API에는 이미지 위치만 전달합니다.
- 단일 FastAPI 인스턴스가 인메모리 큐·상태·결과를 소유하고 Uvicorn worker를 1개로 제한합니다.
- 소비자 코루틴 2개가 작업 단위 파이프라인을 실행하며 모델 추론은 원격 GPU Worker로 넘깁니다.
- MobileNetV3-Small, PP-OCRv5, AI PostgreSQL의 승인된 보조 자료, Ollama
gemma4:e2b와 결정 규칙을 사용합니다. - 재료 원본은 백엔드 MySQL, 레시피 원본은 AI PostgreSQL, 백엔드 레시피는 동기화 사본으로 유지합니다.
- 서비스 제공 시간에는 AI 서버를 실행 상태로 유지하며 중지 중 요청 보관과 자동 복구를 보장하지 않습니다.
현재 팀 규모와 예산에는 이 구성이 적합합니다. 처음부터 모듈마다 인스턴스를 만들지 않습니다.
| 논리 모듈 | 초기 배포 단위 | 독립 분리·확장 조건 | 독립 배포 시 유지할 계약 |
|---|---|---|---|
| 서비스 API | 백엔드 서버 | 사용자 요청 증가 |
/ai/v1 요청·조회 계약 |
| FastAPI 런타임 | AI EC2 단일 인스턴스·Uvicorn worker 1개 | 영속 복구·무중단 배포·다중 인스턴스가 필요 | 분석 ID와 작업 상태 계약을 유지하며 재설계 |
| 입력 유형 판별 | 같은 AI 런타임의 논리 모듈 | 실제 단계 실행시간이 병목 | InputTypeResult |
| 영수증 분석 | 같은 AI 런타임의 논리 모듈 | OCR 실행시간·메모리가 병목 | OCR 문서와 상품행 후보 스키마 |
| 실물 분석 | 같은 AI 런타임의 논리 모듈 | 실물 처리시간·직접 입력 전환율 증가 | 실물 분석 결과 스키마 |
| Gemma 보완 | 같은 AI 런타임의 논리 모듈 | 모델 실행시간·메모리가 병목 |
FoodCandidateClassifier 인터페이스 |
| 결과 검증 | 같은 프로세스의 논리 모듈 | 검증 시간이 병목 | 공통 분석 결과 스키마 |
| 레시피 공급자 어댑터 | 추천 오케스트레이터가 호출하는 AI 내부 어댑터 | 공급자별 한도·장애 격리가 필요 |
RecipeSourceCandidate 계약 |
| 레시피 추천·원본 저장 | AI 서버와 AI PostgreSQL | 추천 트래픽이나 데이터 크기 증가 | 안정 원천 식별자와 추천 응답 계약 |
| 레시피 사본 동기화 | 백엔드 주기 작업 | 동기화 지연·실패 증가 | 커서 기반 변경 조회 계약 |
초기 구조는 상태를 메모리에 보관하므로 인스턴스를 그대로 여러 대로 늘릴 수 없습니다. 영속 복구나 수평 확장이 실제 요구가 되면 작업 상태 저장과 분산 큐를 다시 선택해야 하며, 현재 문서에서는 제품을 미리 정하지 않습니다.
- 영수증 문자 인식과 실물 분석 작업자를 독립 배포합니다.
- 레시피 추천 서비스를 독립 확장합니다.
- 레시피 공급자 어댑터는 추천 트래픽과 장애 영향이 커질 때 독립 배포를 검토합니다.
- 논리 모듈의 입력·출력 계약을 유지하면서 병목 작업자를 별도 실행 환경으로 옮깁니다.
이 단계는 초기 출시부터 적용하지 않습니다. 인메모리 큐 대기시간, 메모리 사용량, 요청 비율과 장애 영향 범위를 측정해 단일 인스턴스로 감당하기 어렵다는 근거가 생겼을 때 작업 영속화부터 다시 설계합니다.
- 이미지 이해 모델이 꼭 필요하다는 품질 근거와 운영 예산이 마련됐을 때만 별도 모델 서빙을 검토합니다.
- 그래픽 처리장치 모델은 초기 범위에 넣지 않고 필요성과 예산 근거가 생기면 별도 설계에서 검토합니다.
- Qwen-VL이나 vLLM은 현재 확정 구성이 아니며 후보 모델과 서빙 도구로만 평가합니다.
- 모델별 카나리 배포와 자동 롤백을 구성합니다.
- GPU 작업자는 향후 선택한 작업 전달 구조의 부하 제한을 따릅니다.
| 장애 | 영향 범위 | 대체 동작 |
|---|---|---|
| OCR 워커 장애 | 영수증과 날짜 인식 | 작업 재시도 후 수동 등록 제공 |
| 실물 분석 작업자 장애 | 실물 사진 인식 | 바코드·상품 검색 또는 직접 입력 제공 |
| 보관기간 보조 자료 조회 장애 | AI 예상 날짜 | 추정하지 않고 날짜 미입력 상태 유지 |
| AI PostgreSQL 장애 | 신규 레시피 검색·동기화 | 기존 백엔드 MySQL 레시피 사본 유지, 재료 기능은 계속 제공 |
| 동기화 작업 장애 | 백엔드 레시피 갱신 지연 | cursor를 전진시키지 않고 같은 페이지부터 재시도 |
| 레시피 추천 모듈 장애 | 신규 추천 | 최근 캐시 결과 제공 또는 잠시 후 재시도 |
| 모델 성능 저하 | 특정 버전의 오인식 증가 | 이전 모델·정책 버전으로 롤백 |
- 정규화·카테고리 모듈과 API enum 버전을 변경합니다.
- 고정 평가 세트와 프론트 표시명을 추가합니다.
- 문자 인식과 실물 분석 모델은 원본 이름 후보만 반환하므로 즉시 재학습할 필요는 없습니다.
-
RecipeSourcePort구현과 공급자 설정을 추가하거나 교체합니다. - 추천 API와 공통 레시피 후보 계약은 유지합니다.
- 식품안전나라 → TheMealDB → Tavily 우선순위 변경은 품질·지연·비용·이용 조건을 검증한 뒤 적용합니다.
- 레시피 랭커와 추천 정책 버전만 변경합니다.
- 이미지 분석과 냉장고 재고 API는 영향을 받지 않습니다.
- 기존·신규 점수를 오프라인 평가하거나 A/B 테스트할 수 있습니다.
- 승인된 저장소에 보관기간 자료를 도입한 경우 해당 버전과 관련 캐시만 갱신합니다.
- 실제 OCR 날짜는 변경하지 않습니다.
- 어떤 지식 버전으로 추정했는지 추적할 수 있습니다.
모든 분석 요청에는 requestId, analysisId, pipelineVersion을 연결합니다.
- 영수증·실물·기타 입력 비율
- 파이프라인별 p50·p95 지연시간
- 자체 모델 처리율과 사용자 확인·직접 입력 전환율
- 모델·필드별 사용자 수정률
- 상태값 분포와 재촬영률
- 레시피 공급자별 호출 비용과 AWS 비용
- 레시피 추천의 보유 재료 활용률
- 임박 재료 추천 반영률
- 호출자가 전달한 제외 재료 필터 차단 건수
원본 이미지와 OCR 전문은 일반 로그에 남기지 않습니다. 오류 분석이 필요한 샘플은 사용자 동의와 제한된 접근권한 아래 별도 보관합니다.
- 이미지 모델을 교체해도 프론트와 냉장고 재고 코드를 수정하지 않습니다.
- 영수증 OCR과 실물 인식을 각기 다른 데이터와 지표로 개선할 수 있습니다.
- 외부 API 의존도를 모듈별로 단계적으로 낮출 수 있습니다.
- 이미지 분석 장애가 레시피 조회나 기존 재고 확인까지 확산되지 않습니다.
- 초기 출시에서는 그래픽 처리장치를 운영하지 않아 AWS 예산을 통제하고, 이후 필요성이 입증된 모델만 AI 예산 안에서 별도로 검토할 수 있습니다.
- 사용자 수정 데이터가 어느 모델의 어떤 오류인지 추적되어 재학습에 활용됩니다.