이번 작업은 냉파마스터 2차 프로젝트의 리팩토링 및 확장 작업이다.
1차 프로젝트에서는 냉장고 재료 관리, 장보기 목록 관리, 사전 재료 검색, 관리자 사전 재료 관리 기능을 중심으로 기본 CRUD 흐름을 구현했다.
2차 프로젝트에서는 여기서 한 단계 확장해, 사용자의 냉장고와 장보기 상태를 기반으로 AI가 필요한 재료를 추천하고, 추천 결과를 사용자가 직접 승인해 장보기 목록에 추가할 수 있는 구조를 구현했다.
이번 작업 범위는 다음과 같다.
- #238 AI 장보기 추천 MVP 구현
- #239 AI 대화 세션 및 메시지 기록 구현
- #240 LLM 사용량 로그 저장 구현
- #316 프론트 AI 장보기 추천 화면 연동
- #317 관리자 LLM 사용량 전체 조회 API 구현
- #318 관리자 LLM 사용량 로그 화면 구현
1. 작업 배경
기존 장보기 기능은 사용자가 직접 재료를 검색하고 장보기 목록에 추가하는 방식이었다.
하지만 냉장고에 이미 있는 재료, 장보기 목록에 이미 담긴 재료, 사용자가 못 먹는 재료 등을 매번 직접 고려해서 장보기 목록을 구성하는 것은 번거롭다.
그래서 이번 작업에서는 AI Agent를 활용해 다음과 같은 흐름을 만들고자 했다.
사용자의 냉장고 상태 조회
→ 장보기 목록 조회
→ 못 먹는 재료와 선호 음식 정보 반영
→ 추천 후보 재료 생성
→ AI 추천 결과 반환
→ 사용자가 선택한 항목만 장보기 목록에 추가
중요한 점은 AI가 추천 결과를 바로 DB에 저장하지 않도록 한 것이다.
AI 추천은 어디까지나 제안이고, 실제 장보기 목록에 반영할지는 사용자가 직접 선택해야 한다고 판단했다.
2. 전체 구조
이번 AI 추천 기능은 Backend와 FastAPI Agent 서버를 분리해서 구성했다.
Backend는 기존 회원 인증, 냉장고, 장보기, 사전 재료 데이터를 관리한다.
FastAPI Agent는 Backend에서 전달받은 사용자 상태와 후보 재료를 기반으로 LLM 추천 결과를 생성한다.
전체 흐름은 다음과 같다.
- 사용자가 프론트에서 AI 추천 버튼 클릭
- 프론트가 Backend의 AI 추천 API 호출
- Backend가 로그인한 사용자의 냉장고/장보기/못 먹는 재료/선호 음식 정보 조회
- Backend가 추천 제외 상품 ID 목록 생성
- Backend가 추천 후보 상품 목록을 Agent 서버에 전달
- Agent가 LLM 또는 fallback 로직으로 추천 결과 생성
- Backend가 추천 결과를 사용자에게 반환
- Backend가 AI 대화 기록과 LLM 사용량 로그 저장
- 사용자가 원하는 추천 항목만 장보기 목록에 추가
3. #238 AI 장보기 추천 MVP 구현
먼저 AI 장보기 추천 API를 구현했다.
관련 API는 다음과 같다.
- POST /api/v1/agent/shopping-recommendations
이 API는 사용자의 냉장고와 장보기 상태를 기반으로 추천 재료 목록을 반환한다.
추천에서 제외해야 하는 데이터는 다음과 같다.
- 이미 냉장고에 있는 재료
- 아직 구매하지 않은 장보기 항목
- 사용자가 못 먹는 재료로 등록한 재료
- 재추천 시 이전 추천 화면에 이미 노출된 재료
처음에는 냉장고와 장보기 목록만 제외했지만, 이후 사용자가 “재추천을 눌러도 같은 추천이 다시 나온다”는 문제를 발견했다.
그래서 프론트에서 현재 화면에 떠 있는 추천 재료의 productId를 excludeProductIds로 Backend에 전달하도록 수정했다.
Backend는 이 값을 기존 제외 목록에 합쳐 추천 후보에서 제거한다.
즉, 최종 제외 목록은 다음 기준으로 만들어진다.
- 냉장고 보유 재료 productId
- 미구매 장보기 항목 productId
- 못 먹는 재료 productId
- 화면에 이미 노출된 추천 productId
이렇게 해서 재추천 시 같은 항목이 반복 노출되는 문제를 줄였다.
4. #239 AI 대화 세션 및 메시지 기록 구현
AI 추천은 단순히 결과만 보여주고 끝나는 기능이 아니라, 사용자가 나중에 이전 추천 내용을 다시 확인할 수 있어야 한다고 판단했다.
그래서 AI 대화 세션과 메시지 기록을 저장하는 구조를 추가했다.
구현한 주요 테이블은 다음과 같다.
- conversation_sessions
- conversation_messages
conversation_sessions는 AI 추천 요청 단위의 세션 정보를 저장한다.
conversation_messages는 해당 세션 안에서 사용자 요청 메시지와 AI 응답 메시지를 저장한다.
관련 API는 다음과 같다.
- GET /api/v1/ai/conversation-sessions
- GET /api/v1/ai/conversation-sessions/{sessionId}/messages
이 API들은 로그인한 사용자의 데이터만 조회할 수 있도록 구성했다.
본인 세션이 아닌 경우에는 조회되지 않도록 소유자 검증도 함께 처리했다.
이 작업을 통해 AI 추천 결과를 단발성 응답이 아니라, 사용자별 기록 데이터로 관리할 수 있게 되었다.
5. #240 LLM 사용량 로그 저장 구현
LLM 기능은 호출량, 실패 여부, 토큰 사용량, 예상 비용을 추적하는 것이 중요하다.
특히 추후 구독 정책이나 사용량 제한, 비용 분석을 고려한다면 LLM 사용 로그는 반드시 필요하다.
그래서 LLM 사용량 로그 저장 기능을 구현했다.
저장하는 주요 정보는 다음과 같다.
- memberId
- modelName
- promptTokens
- completionTokens
- totalTokens
- estimatedCost
- status
- failureMessage
- createdAt
현재는 FastAPI Agent에서 usage 값을 내려주고, Backend가 해당 값을 저장한다.
실제 OpenAI 연동 전 fallback 또는 rule-based 추천이 동작하는 경우에는 modelName을 rule-based-mvp로 저장하고, 토큰 수와 비용은 0으로 처리했다.
실제 gpt-4.1-mini 사용 시에는 입력 토큰과 출력 토큰 단가를 기준으로 예상 비용을 계산하도록 구성했다.
Prompt는 입력 토큰, Completion은 출력 토큰, Total은 두 값을 합친 전체 토큰 수다.
6. #316 프론트 AI 장보기 추천 화면 연동
Backend와 Agent API가 준비된 뒤, 기존 장보기 화면에 AI 추천 기능을 연결했다.
기존 화면의 디자인과 사용자 흐름을 해치지 않는 것이 중요했기 때문에, 장보기 목록 화면 안에 AI 추천 버튼과 추천 결과 카드만 추가했다.
프론트에서 구현한 흐름은 다음과 같다.
- 사용자가 AI 추천 버튼 클릭
- 추천 API 호출
- 추천 결과를 카드 형태로 표시
- 추천 결과는 자동 저장하지 않음
- 사용자가 담기 버튼을 누른 항목만 장보기 목록에 추가
- 추가된 항목은 추천 목록에서 제거
- 기존 장보기 추가/수정/삭제/체크 흐름은 그대로 유지
특히 AI 추천 결과를 바로 DB에 저장하지 않고 화면 상태로만 관리했다.
이렇게 해야 사용자가 원하지 않는 재료가 자동으로 장보기 목록에 들어가는 문제를 막을 수 있다.
7. #317 관리자 LLM 사용량 전체 조회 API 구현
LLM 사용량은 일반 사용자 본인 조회뿐 아니라, 관리자 관점에서도 확인할 필요가 있다.
그래서 관리자 전용 전체 LLM 사용량 조회 API를 추가했다.
관련 API는 다음과 같다.
- GET /api/v1/admin/ai/usage-logs
관리자 응답에는 다음 정보를 포함했다.
- memberId
- nickname
- modelName
- promptTokens
- completionTokens
- totalTokens
- estimatedCost
- status
- failureMessage
- createdAt
일반 사용자는 접근할 수 없고, 관리자 권한을 가진 사용자만 전체 로그를 조회할 수 있다.
로그는 최신순으로 정렬해 운영자가 최근 AI 호출 상태를 빠르게 확인할 수 있도록 했다.
8. #318 관리자 LLM 사용량 로그 화면 구현
관리자 API를 만든 뒤에는 프론트 관리자 화면에서도 LLM 사용량 로그를 확인할 수 있도록 화면을 추가했다.
관리자 화면에서는 다음 정보를 확인할 수 있다.
- 사용자 이메일
- 닉네임
- 모델명
- Prompt 토큰
- Completion 토큰
- Total 토큰
- 예상 비용
- 성공/실패 상태
- 실패 메시지
- 호출 일시
예상 비용은 달러 기준으로 저장되지만, 화면에서는 원화로도 볼 수 있도록 환율 API를 이용해 KRW 변환 표시를 추가했다.
다만 실제 운영 환경에서는 환율 API 장애나 변동성을 고려해 fallback 환율도 함께 두었다.
9. 트러블슈팅: Flyway Migration이 적용되지 않던 문제
이번 작업 중 가장 크게 막혔던 부분은 Flyway migration이었다.
AI 대화 세션과 LLM 사용량 로그를 저장하기 위해 새로운 테이블을 추가했고, 이를 Flyway migration으로 관리하려고 했다.
하지만 V4, V5 migration 파일을 추가했는데도 flyway_schema_history 테이블에 버전이 등록되지 않는 문제가 있었다.
처음에는 SQL 파일명 문제라고 생각했지만, 실제로는 DB 연결 정보와 migration 적용 대상 DB를 함께 확인해야 하는 문제였다.
발생 상황
Backend를 실행했는데 신규 migration이 적용되지 않았다.
flyway_schema_history를 조회해도 기대했던 V4, V5가 보이지 않았다.
또한 dev 브랜치를 병합한 뒤에는 Backend 실행 자체가 실패하는 상황도 발생했다.
확인한 쿼리
먼저 현재 Flyway가 어떤 migration을 적용했는지 확인했다.
SELECT installed_rank, version, description, script, success
FROM flyway_schema_history
ORDER BY installed_rank;
이 쿼리로 Flyway가 실제로 어떤 파일까지 실행했는지 확인할 수 있다.
다음으로 현재 내가 접속한 DB가 Backend가 바라보는 DB와 같은지 확인했다.
SELECT current_database();
현재 schema도 확인했다.
SELECT current_schema();
그리고 migration으로 생성되어야 하는 테이블이 실제로 존재하는지도 확인했다.
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name IN (
'conversation_sessions',
'conversation_messages',
'llm_usage_logs'
);
원인 분석
원인은 크게 두 가지로 나눠 볼 수 있었다.
첫 번째는 application-secret.env의 DB 접속 정보가 현재 실행하려는 DB와 맞지 않았던 점이다.
두 번째는 Flyway가 바라보는 DB와 내가 직접 쿼리로 확인하던 DB가 일치하지 않을 가능성이 있었다는 점이다.
즉, migration 파일은 존재하지만 Backend 실행 시점에 다른 DB를 보고 있으면 flyway_schema_history에는 기대한 버전이 보이지 않는다.
또한 dev 브랜치를 병합하면서 환경 설정이나 migration 파일 순서가 바뀌면, 기존에 정상 실행되던 Backend도 다시 실패할 수 있다.
해결 과정
먼저 Backend 실행 로그에서 실제 DB URL을 확인했다.
그 다음 application-secret.env의 DB_URL, DB_USERNAME, DB_PASSWORD 값을 점검했다.
이후 현재 접속 DB와 schema를 SQL로 확인했다.
마지막으로 flyway_schema_history와 information_schema.tables를 조회해 migration 적용 여부와 실제 테이블 생성 여부를 함께 확인했다.
정리하면 확인 순서는 다음과 같다.
- Backend 실행 로그에서 실제 DB URL 확인
- application-secret.env의 DB 접속 정보 확인
- current_database()로 현재 접속 DB 확인
- current_schema()로 현재 schema 확인
- flyway_schema_history에서 V4, V5 적용 여부 확인
- information_schema.tables로 실제 테이블 생성 여부 확인
- Backend 재실행 후 migration 정상 반영 확인
최종적으로 DB 연결 정보를 정리하고 Backend를 재실행하자 migration이 정상 반영되었다.
배운 점
Flyway 문제를 볼 때는 migration 파일만 보면 안 된다.
파일명이 맞는지, SQL 문법이 맞는지도 중요하지만, 그보다 먼저 “내가 지금 어느 DB를 보고 있는지”를 확인해야 한다.
이번 경험을 통해 Flyway migration을 점검할 때는 다음 기준이 중요하다는 것을 알게 되었다.
- Backend가 실제로 연결한 DB 확인
- 내가 직접 쿼리로 확인하는 DB 확인
- flyway_schema_history 확인
- 실제 테이블 생성 여부 확인
- dev 병합 후 설정 변경 여부 확인
결국 Flyway 문제는 단순히 SQL 파일 문제가 아니라, DB 연결 정보와 migration 이력, 실제 schema 상태를 함께 확인해야 해결할 수 있었다.
10. 테스트 및 검증
이번 작업에서 확인한 내용은 다음과 같다.
Backend
- AI 추천 API 200 응답 확인
- 냉장고 보유 재료 추천 제외 확인
- 미구매 장보기 항목 추천 제외 확인
- 못 먹는 재료 추천 제외 확인
- excludeProductIds 기반 재추천 제외 확인
- AI 대화 세션 저장 확인
- AI 메시지 기록 저장 확인
- LLM 사용량 로그 저장 확인
- 관리자 전체 LLM 로그 조회 확인
Frontend
- 장보기 화면 AI 추천 버튼 표시 확인
- 추천 결과 카드 표시 확인
- 추천 항목 장보기 담기 확인
- 기존 장보기 CRUD 영향 없음 확인
- 관리자 LLM 사용량 화면 표시 확인
- 빌드 성공 확인
Agent
- FastAPI Agent 서버 추천 응답 확인
- OpenAI API Key 없는 경우 fallback 동작 확인
- 사용량 응답 구조 확인
- 선호 음식 기반 prompt 전달 확인
11. 이번 작업에서 신경 쓴 점
이번 작업에서 가장 신경 쓴 부분은 AI가 직접 DB를 수정하지 않도록 한 것이다.
AI 추천 결과를 바로 장보기 목록에 저장하면 편해 보일 수 있지만, 사용자가 원하지 않는 재료가 자동으로 추가될 위험이 있다.
그래서 추천과 저장을 분리했다.
추천 API는 추천 결과만 반환하고, 실제 장보기 추가는 사용자가 선택한 항목만 별도 API로 저장한다.
또한 기존 장보기 중복 등록 방지 정책을 그대로 재사용했다.
이렇게 하면 일반 장보기 추가와 AI 추천 장보기 추가가 서로 다른 정책으로 동작하지 않고, 동일한 서비스 로직을 기준으로 관리된다.
12. 남은 개선 방향
현재 AI 장보기 추천 기능은 MVP 수준으로 동작한다.
다만 추천 품질과 속도 측면에서는 추가 개선 여지가 있다.
앞으로 개선할 수 있는 부분은 다음과 같다.
- 추천 후보 재료 수 최적화
- LLM prompt 길이 축소
- 추천 이유 문장 길이 제한
- 같은 조건의 추천 캐싱
- 재추천 시 더 다양한 결과 제공
- 사용자 선호 음식과 식단 패턴 반영 강화
- FREE/PREMIUM 권한 정책 적용
- AI 대화 기록 화면 고도화
- LLM 비용 통계 대시보드 개선
특히 추천 속도는 Agent에 넘기는 후보 재료 수와 prompt 길이에 영향을 많이 받는다.
따라서 다음 단계에서는 후보군을 DB에서 먼저 줄이고, Agent에는 꼭 필요한 정보만 전달하는 방향으로 개선할 예정이다.
13. 마무리
이번 작업을 통해 냉파마스터의 장보기 기능을 단순 CRUD에서 AI 기반 추천 흐름으로 확장했다.
기존 냉장고, 장보기, 못 먹는 재료, 선호 음식 데이터를 활용해 추천 후보를 만들고, FastAPI Agent와 LLM을 연결할 수 있는 기반을 구성했다.
또한 추천 결과를 저장하지 않고 사용자 승인 후 장보기 목록에 추가하는 방식으로 안전한 사용자 흐름을 유지했다.
이번 작업은 단순히 AI API를 붙이는 것이 아니라, 기존 서비스 정책과 AI 추천 흐름을 어떻게 연결할지 고민한 작업이었다.
앞으로는 추천 품질, 응답 속도, 구독 정책, 비용 관리까지 확장해 더 실사용에 가까운 AI 장보기 Agent로 발전시킬 수 있을 것 같다.
'개발 프로젝트 > [팀 프로젝트] 냉파마스터' 카테고리의 다른 글
| 냉파마스터 2차 프로젝트: 장보기 리팩토링과 AI Agent 확장 기반 만들기 (0) | 2026.08.04 |
|---|---|
| 냉장고 유통기한 자동 반영 보완 (0) | 2026.07.02 |
| 장보기 연동과 사전 재료 관리자 기능 정리 (0) | 2026.06.30 |
| 장보기 도메인 API 구현 정리 (0) | 2026.06.29 |
| 냉장고 유통기한 조회와 프론트 연동, 그리고 검색 성능 개선 정리 (0) | 2026.06.28 |