-
🚀 [NestJS] Saga 패턴의 시작: RabbitMQ와 함께하는 비동기 메뉴 분석 API 구현기프로젝트/개인프로젝트 2026. 3. 8. 12:59
안녕하세요! 오늘은 스마트 식단 관리 에이전트 프로젝트의 핵심, 메뉴판 분석 요청 API를 개발하며 겪은 우여곡절과 기술적 고민들을 정리해 보려고 합니다. 🥗✨
이번 작업의 핵심은 "무거운 AI 분석 작업을 어떻게 사용자 대기 시간 없이 처리할 것인가?"였습니다.
🏗️ 1. 아키텍처 설계: 왜 Saga 패턴인가?
사용자가 메뉴판 이미지를 업로드하면 OCR과 AI 분석이 돌아가야 하는데, 이 작업은 수 초 이상 걸릴 수 있습니다. 이를 동기(Sync) 방식으로 처리하면 유저는 화면이 멈춘 듯한 경험을 하게 되죠.
그래서 저는 Saga 패턴의 첫 단계를 도입했습니다.
- NestJS: 이미지 업로드 즉시 DB에 태스크를 저장하고 taskId를 반환 (비동기 처리 시작).
- RabbitMQ: 분석에 필요한 정보를 메시지 큐에 담아 FastAPI 분석 엔진으로 전달.
🛠️ 2. 트러블슈팅: 내가 만난 에러와 해결책
❌ Issue 1: "나 분명히 만들었는데?" (404 Not Found)
새로운 모듈을 생성하고 포스트맨으로 쐈는데 404 Not Found가 떴습니다.
원인: NestJS의 중앙 관리실인 AppModule에 신규 모듈인 AnalysisModule을 등록하지 않아 서버가 해당 경로를 인식하지 못했습니다.
해결: app.module.ts의 imports 배열에 모듈을 추가하여 해결!❌ Issue 2: FileTypeValidator의 배신 (400 Bad Request)
이미지 파일만 받으려고 FileTypeValidator를 썼는데, 계속해서 검증 실패 에러가 났습니다.
원인: 저희 서버는 메모리 효율을 위해 파일을 디스크에 바로 저장하는 diskStorage를 사용합니다. 하지만 NestJS의 기본 Validator는 메모리에 있는 file.buffer를 필요로 하기 때문에 디스크 저장 시에는 버퍼가 비어있어 검증에 실패한 것이죠.
해결: 컨트롤러에서 file.mimetype을 직접 대조하는 수동 검증 로직으로 교체하여 해결했습니다. 💪// mimetype 직접 검증 const allowedMimeTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']; if (!allowedMimeTypes.includes(file.mimetype)) { throw new BadRequestException('이미지 파일만 업로드 가능합니다. (jpeg, jpg, png, gif)'); }
💻 3. 핵심 코드 구현
📁 분석 요청 컨트롤러 (Swagger 적용)
파일 업로드 형식을 명시하여 스웨거 문서에서도 편리하게 테스트할 수 있도록 구성했습니다.
@Post('analyze') @UseInterceptors(FileInterceptor('image')) @ApiConsumes('multipart/form-data') async analyzeMenu(@UploadedFile() file: Express.Multer.File) { // 1. mimetype 직접 검증 const allowedMimeTypes = ['image/jpeg', 'image/png']; if (!allowedMimeTypes.includes(file.mimetype)) throw new BadRequestException('이미지만 가능!'); // 2. 서비스 호출 (DB 저장 및 RabbitMQ 발행) return this.analysisService.createAnalysisTask(file.path); }
📸 4. 테스트 결과 및 사진 설명
① 포스트맨 404 에러 상황
- 설명: 모듈 등록 전, 서버가 경로를 찾지 못해 404 응답을 내뱉는 당황스러운 순간입니다. 이 과정을 통해 NestJS 모듈 시스템의 중요성을 다시 한번 깨달았죠.
② 분석 요청 성공 (201 Created)

- 설명: 트러블슈팅 후, 드디어 이미지가 성공적으로 업로드된 모습입니다! 응답으로 전달된 taskId와 status: PENDING은 비동기 작업이 정상적으로 시작되었음을 알려줍니다.
- DTO 추가 후 응답 body

③ 분석 태스크 상태 조회 (200 OK)

- 설명: 발급받은 taskId로 상태를 조회한 결과입니다. DB에 저장된 유저의 targetCalories 정보가 분석 태스크와 결합되어 안전하게 관리되고 있습니다.
④ RabbitMQ 관리자 대시보드 (Overview)

- 설명: 메시지 큐 전광판에 찍힌 신호입니다! NestJS 서버(Connections: 1)가 기차역에 접속해 메시지를 성공적으로 발행(Publish)한 흔적을 그래프로 확인할 수 있습니다.
⑤ 메시지 통로(Exchange) 설정 완료

- 설명: 우리가 코드로 정의한 menu_analysis_exchange가 리스트에 예쁘게 등록되었습니다. 이제 이곳을 통해 분석 데이터들이 FastAPI로 슝슝 날아갈 예정이에요! 🚀
🏁 마치며
오늘은 NestJS에서 이미지 업로드를 처리하고 RabbitMQ를 통해 다른 서비스와 통신하는 첫 단추를 끼워보았습니다. 인프라 설정부터 타입 에러까지 쉽지 않았지만, 한 단계씩 뚫어내며 시스템이 견고해지는 것을 느끼니 정말 뿌듯하네요!
다음 포스팅에서는 이 메시지를 받아 실제 분석을 수행할 FastAPI 분석 엔진 구현기로 돌아오겠습니다! 🎀🌻
'프로젝트 > 개인프로젝트' 카테고리의 다른 글
[FastAPI] 분석 엔진에 '눈', EasyOCR 연동 및 트러블슈팅 (Saga Step 2) (0) 2026.03.10 [FastAPI] 파이썬 분석 엔진 기지 건설! RabbitMQ 메시지 수신 성공기 (feat. zsh 트러블슈팅) (0) 2026.03.10 [NestJS] 회원가입부터 사용자 취향 설정까지: 인증 및 유저 모듈 통합기 (F-03, F-04) (0) 2026.03.07 🥗 [NestJS/Prisma] 식단 관리 서비스의 꽃, 주간 리포트 API 구현 및 트러블슈팅 (0) 2026.03.05 Meal API Troubleshooting(API 보안 강화) (0) 2026.03.04