-
Notifications
You must be signed in to change notification settings - Fork 2
feat: Implement real sandwich recommendation #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough이번 변경에서는 샌드위치 휴가 추천 및 AI 기반 휴가 생성 기능이 전면적으로 확장되었습니다. 주말 데이터 도메인 및 저장소가 신설되었고, 클라이언트-서버-DB 계층 전체에 걸쳐 데이터 모델, API, 예외 처리, 프롬프트 생성, AI 상호작용 로직이 대폭 보강되었습니다. 기존 샌드위치 추천 API의 반환 타입이 복수 기간 리스트로 변경되었으며, AI를 활용한 맞춤형 휴가 생성 API와 주말 데이터 자동 스케줄러가 추가되었습니다. Changes
Sequence Diagram(s)1. 샌드위치 휴가 추천(BridgeVacationPeriod 리스트 반환)sequenceDiagram
participant Client
participant RecommendController
participant RecommendServiceImpl
participant HolidayReader
participant WeekendReader
participant RecommendClient
participant MCP
Client->>RecommendController: getSandwich(userId)
RecommendController->>RecommendServiceImpl: getSandwich(userId)
RecommendServiceImpl->>HolidayReader: findRemainingHolidays
RecommendServiceImpl->>WeekendReader: getUpcomingWeekends
RecommendServiceImpl->>RecommendClient: getSandwich(SandwichRequest)
RecommendClient->>MCP: getSandwich API 호출
MCP-->>RecommendClient: List<BridgeVacationPeriod>
RecommendClient-->>RecommendServiceImpl: List<BridgeVacationPeriod>
RecommendServiceImpl-->>RecommendController: SandwichApiResponse
RecommendController-->>Client: ApiResponse<SandwichApiResponse>
2. AI 기반 휴가 생성sequenceDiagram
participant Client
participant RecommendController
participant RecommendServiceImpl
participant RecommendClient
participant MCP
Client->>RecommendController: generateVacation(userId, request)
RecommendController->>RecommendServiceImpl: generateVacation(userId, request)
RecommendServiceImpl->>RecommendClient: generateVacation(AiGenerateVacationRequest)
RecommendClient->>MCP: generateVacation API 호출
MCP-->>RecommendClient: AiGenerateVacationResponse
RecommendClient-->>RecommendServiceImpl: AiGenerateVacationResponse
RecommendServiceImpl-->>RecommendController: AiGenerateVacationApiResponse
RecommendController-->>Client: ApiResponse<AiGenerateVacationApiResponse>
3. 주말 데이터 연간 자동 생성sequenceDiagram
participant Scheduler
participant WeekendScheduler
participant WeekendWriter
participant WeekendRepository
Scheduler->>WeekendScheduler: generateThisYearWeekends (매년 1월 1일 0시)
WeekendScheduler->>WeekendWriter: register(Weekend) (연중 모든 주말)
WeekendWriter->>WeekendRepository: register(Weekend)
Possibly related PRs
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 18
🔭 Outside diff range comments (2)
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendClient.kt (1)
97-107: 로깅 메시지의 복사-붙여넣기 오류를 수정해야 합니다.104라인의 로그 메시지에서 메서드명이 잘못되었습니다.
- log.error("[getSandwich] 예기치 못한 예외, empty 반환.", e) + log.error("[generateVacation] 예기치 못한 예외, empty 반환.", e)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/service/ChatbotService.kt (1)
62-62: Thread.sleep 대신 비동기 처리 고려
Thread.sleep은 스레드를 블로킹하여 성능 저하를 일으킬 수 있습니다.다음 대안을 고려하세요:
@Async와CompletableFuture사용ScheduledExecutorService활용- Spring의
TaskScheduler사용- Kotlin 코루틴의
delay()함수// 코루틴 예시 import kotlinx.coroutines.delay suspend fun retryWithDelay(delayMs: Long) { delay(delayMs) }Also applies to: 207-207
🧹 Nitpick comments (22)
noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendRepository.kt (2)
5-9: 메서드 명세 모호성 정리 필요
findAfterDate메서드가 기준 날짜를 포함하는지(≥) 배제하는지(>) 명확하지 않습니다.
도메인 용어(예:findFromvsfindAfterExclusive)로 의도를 드러내거나 KDoc 주석을 추가해 주세요.
5-9: 네이밍 일관성 확인
register대신 일반적으로 사용하는save나add같은 표현을 고려해 보세요. 동일 계층의 다른 리포지터리와 용어를 맞추면 가독성이 향상됩니다.noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/response/BridgeVacationPeriod.kt (1)
5-8: 불변 객체로의 활용 검토
startDate,endDate만 가지는 단순 DTO라면@JvmInline value class나record(Kotlin 1.9+) 도 고려해 볼 수 있습니다. 다만 직렬화 라이브러리 호환성을 먼저 검토하세요.noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/SandwichRequest.kt (1)
5-10: 입력 유효성 검사 및 문서화 고려가 필요합니다.데이터 클래스의 구조는 적절하지만 다음 사항들을 고려해보세요:
- 유효성 검사:
remainingAnnualLeave에 대한 최소값 검증, 날짜 리스트의 null 체크 등- 문서화: 각 속성의 역할과 제약사항에 대한 KDoc 추가
다음과 같은 개선을 제안합니다:
+import javax.validation.constraints.Min +import javax.validation.constraints.NotNull + +/** + * 샌드위치 휴가 추천 요청 데이터 + */ data class SandwichRequest( + /** 사용자 생일 */ + @field:NotNull val birthDay: LocalDate, + /** 공휴일 목록 */ + @field:NotNull val holidays: List<LocalDate>, + /** 남은 연차 일수 */ + @field:Min(0) val remainingAnnualLeave: Int, + /** 주말 목록 */ + @field:NotNull val weekends: List<LocalDate>, )noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendWriter.kt (1)
6-15: 깔끔한 구조이지만 검증 및 로깅 추가를 고려하세요.단일 책임 원칙을 잘 따르는 깔끔한 구조입니다. 하지만 운영 환경에서의 안정성을 위해 다음 사항들을 고려해보세요:
+import org.slf4j.LoggerFactory + @Component @Transactional class WeekendWriter( private val weekendRepository: WeekendRepository, ) { + private val logger = LoggerFactory.getLogger(WeekendWriter::class.java) fun register(weekend: Weekend) { + logger.debug("Registering weekend: {}", weekend) + require(weekend.id.isNotBlank()) { "Weekend ID cannot be blank" } weekendRepository.register(weekend) + logger.info("Successfully registered weekend: {}", weekend.id) } }noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/weekend/WeekendScheduler.kt (2)
33-33: 개별 등록 대신 일괄 등록으로 성능 개선
forEach를 사용한 개별 등록은 성능상 비효율적입니다. 일괄 등록을 고려하세요.-weekends.forEach { weekendWriter.register(it) } +weekendWriter.registerAll(weekends)
WeekendWriter에registerAll메서드 추가가 필요합니다.
18-31: 날짜 반복 로직을 더 간결하게 개선현재 while 루프 방식보다 더 함수형 접근 방식을 사용할 수 있습니다.
-val year = LocalDate.now().year -val start = LocalDate.of(year, 1, 1) -val end = LocalDate.of(year, 12, 31) -var date = start -val weekends = mutableListOf<Weekend>() - -while (!date.isAfter(end)) { - if (date.dayOfWeek == DayOfWeek.SATURDAY || date.dayOfWeek == DayOfWeek.SUNDAY) { - weekends.add( - Weekend.generate( - date = date, - dayOfWeek = date.dayOfWeek, - ), - ) - } - date = date.plusDays(1) -} +val start = LocalDate.of(year, 1, 1) +val end = LocalDate.of(year, 12, 31) + +val weekends = generateSequence(start) { it.plusDays(1) } + .takeWhile { !it.isAfter(end) } + .filter { it.dayOfWeek == DayOfWeek.SATURDAY || it.dayOfWeek == DayOfWeek.SUNDAY } + .map { Weekend.generate(it, it.dayOfWeek) } + .toList()noweekend-mcp/mcp-host/src/main/resources/application.yml (2)
8-12: 재시도 설정이 과도할 수 있음최대 5회 재시도와 3분까지의 백오프는 과도할 수 있습니다. 특히 사용자 대면 서비스에서는 응답 시간이 중요합니다.
- max-attempts: 5 + max-attempts: 3 backoff: - initial-interval: 10s + initial-interval: 5s multiplier: 2 - max-interval: 3m + max-interval: 1m
64-64: 환경 변수 문서화 필요
JASYPT_ENCRYPTOR_PASSWORD환경 변수가 필수이지만 문서화가 부족합니다.README 파일이나 배포 문서에 다음 환경 변수 설정을 추가하시겠습니까?
JASYPT_ENCRYPTOR_PASSWORD: API 키 암호화/복호화를 위한 Jasypt 암호화 키noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/holiday/HolidayServiceImpl.kt (1)
37-37: 연도별 휴일 조회 성능 고려사항
findAllByYear메서드가 이전 직접 레포지토리 접근보다 성능상 이점을 제공하는지 검토가 필요합니다.대량의 휴일 데이터 처리 시 다음을 고려하세요:
- 인덱스 확인 (year 컬럼)
- 캐싱 전략 적용
- 페이징 처리 (필요한 경우)
noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendEntity.kt (1)
20-21: 날짜 조회 성능을 위한 인덱스 추가를 고려해주세요.
date컬럼은 범위 검색이 빈번할 것으로 예상되므로 데이터베이스 인덱스 추가를 권장합니다.@Column(name = "date", nullable = false) +@Index(name = "idx_weekend_date") val date: LocalDate,noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/holiday/HolidayReader.kt (1)
17-21: 메모리 필터링 대신 데이터베이스 쿼리 최적화를 고려해주세요.현재 구현은 연도별 모든 공휴일을 메모리에 로드한 후 필터링하고 있습니다. 데이터가 많아질 경우 성능 이슈가 발생할 수 있습니다.
HolidayRepository에 월별 조회 메서드를 추가하는 것을 권장합니다:
// HolidayRepository에 추가 fun findByYearAndMonth(year: Int, month: Int): List<Holiday> // HolidayReader에서 사용 fun findMonthHolidays(year: Int, month: Int): List<Holiday> { return holidayRepository.findByYearAndMonth(year, month) .sortedBy { it.day } }noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendReader.kt (1)
13-16: 현재 연도 로직의 테스트 가능성을 개선해주세요.
LocalDate.now()가 하드코딩되어 있어 테스트가 어려울 수 있습니다.시간을 주입 가능하도록 개선하는 것을 권장합니다:
@Component @Transactional(readOnly = true) class WeekendReader( private val weekendRepository: WeekendRepository, private val clock: Clock = Clock.systemDefaultZone() ) { fun getAllThisYearWeekends(): List<Weekend> { val year = LocalDate.now(clock).year return weekendRepository.findByYear(year) } }noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/AiGenerateVacation.kt (1)
5-29: 문서화 추가 권장각 필드의 목적과 예상 값에 대한 KDoc 주석을 추가하면 API 사용성이 향상됩니다.
+/** + * AI 기반 휴가 생성 요청 + * @property days 휴가 일수 + * @property travelStyleOptionLabels 여행 스타일 옵션 목록 + * @property chosenTravelStyleLabel 선택된 여행 스타일 + * ... + */ data class AiGenerateVacationRequest(noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/service/ChatbotService.kt (2)
43-43: 재시도 지연 시간 설정 가능하도록 개선백오프 시간이 하드코딩되어 있습니다. 설정 파일로 외부화하면 환경별 조정이 용이합니다:
+@Value("\${ai.retry.backoff-intervals:1000,2000,4000,8000,16000}") +private lateinit var backoffIntervals: List<Long> + -val backoff = listOf(1000L, 2000L, 4000L, 8000L, 16000L) +val backoff = backoffIntervals
260-264: 사용하지 않는 메서드 제거
printRaw메서드는 디버깅 용도로 보이며 프로덕션 코드에 남아있습니다. 로거를 사용하거나 제거하는 것이 좋습니다.-private fun printRaw(text: String) { - println("------------------------------------------------\n") - println(text) - println("------------------------------------------------\n") -} +private fun debugLog(text: String) { + logger.debug("Raw response: {}", text) +}noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/Prompt.kt (2)
14-59: 프롬프트 상수 추출 고려프롬프트 문자열이 매우 길어서 가독성이 떨어집니다. 별도의 리소스 파일로 관리하면 유지보수가 용이합니다:
@Value("classpath:prompts/weather-prompt.txt") private lateinit var weatherPromptResource: Resource val WEATHER_PROMPT: String by lazy { weatherPromptResource.inputStream.bufferedReader().use { it.readText() } }
149-220: detailedPlanPrompt 메서드 매개변수가 과다함5개의 매개변수는 과도합니다. 데이터 클래스로 묶어서 전달하면 가독성이 향상됩니다:
data class DetailedPlanContext( val req: AiGenerateVacationRequest, val dates: List<String>, val offset: Int, val totalDays: Int, val prevUsed: List<String> ) fun detailedPlanPrompt(context: DetailedPlanContext): String { // ... implementation }noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendServiceImpl.kt (4)
245-247: 유틸리티 메서드는 별도 클래스로 분리
generateDateRange는 도메인 로직과 무관한 유틸리티 메서드입니다. 별도의 유틸리티 클래스로 분리하는 것이 좋습니다:object DateRangeUtils { fun generateDateRange(start: LocalDate, end: LocalDate): List<LocalDate> { return (0..ChronoUnit.DAYS.between(start, end)).map { start.plusDays(it) } } }
298-314: solveIcon 로직을 맵 기반으로 개선중첩된 if 문 대신 맵을 사용하면 가독성과 확장성이 향상됩니다:
private val iconStyleMap = mapOf( ActivityType.AT_HOME to IconStyle.HOUSE, ActivityType.OUTDOOR to mapOf( TravelStyle.PLANNER to IconStyle.PLANE, TravelStyle.SPONTANEOUS to IconStyle.TRAIN ) ) private fun solveIcon(request: GenerateVacationRequest): IconStyle { if (request.activityType == ActivityType.AT_HOME) { return IconStyle.HOUSE } val outdoorMap = iconStyleMap[ActivityType.OUTDOOR] as? Map<TravelStyle, IconStyle> return outdoorMap?.get(request.travelStyle) ?: IconStyle.STAR }
258-262: 공휴일 조회 범위 확인 필요15일 이내의 공휴일만 조회하고 있는데, 사용자가 더 긴 휴가를 계획할 경우 문제가 될 수 있습니다.
-val endDate = today.plusDays(15) +val endDate = today.plusDays(Math.max(request.days * 2, 30))또한 이 값을 설정으로 관리하는 것도 고려해보세요.
240-242: 예외 처리 개선 가능
McpNotRespondingException을 catch하고 있지만, 로깅이 없습니다. 디버깅을 위해 로그를 추가하는 것이 좋습니다:} catch (e: McpNotRespondingException) { + logger.error("MCP server not responding for sandwich request", e) throw CoreException(ErrorType.MCP_SERVER_INTERNAL_ERROR) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (33)
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/McpNotRespondingException.kt(1 hunks)noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendApi.kt(2 hunks)noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendClient.kt(2 hunks)noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/model/AiGenerateVacation.kt(1 hunks)noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/model/Sandwich.kt(1 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/api/controller/v1/RecommendController.kt(2 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/api/controller/v1/docs/RecommendControllerDocs.kt(5 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/holiday/HolidayServiceImpl.kt(2 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendService.kt(1 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendServiceImpl.kt(4 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/weekend/WeekendScheduler.kt(1 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/support/error/ErrorType.kt(1 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/holiday/HolidayReader.kt(2 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/Weekend.kt(1 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendReader.kt(1 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendRepository.kt(1 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendWriter.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/McpHostApplication.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/LangGraphConfig.kt(0 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/MyAgentState.kt(0 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/RestTemplateConfig.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/ChatbotController.kt(2 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/Prompt.kt(3 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/AiGenerateVacation.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/SandwichRequest.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/WeatherRequest.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/response/BridgeVacationPeriod.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/service/ChatbotService.kt(5 hunks)noweekend-mcp/mcp-host/src/main/resources/application.yml(2 hunks)noweekend-mcp/mcp-server/src/main/kotlin/noweekend/mcpserver/controller/WeatherController.kt(0 hunks)noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendCoreRepository.kt(1 hunks)noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendEntity.kt(1 hunks)noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendJpaRepository.kt(1 hunks)
💤 Files with no reviewable changes (3)
- noweekend-mcp/mcp-server/src/main/kotlin/noweekend/mcpserver/controller/WeatherController.kt
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/LangGraphConfig.kt
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/MyAgentState.kt
🧰 Additional context used
🪛 detekt (1.23.8)
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/McpNotRespondingException.kt
[warning] 5-5: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
🔇 Additional comments (32)
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/McpHostApplication.kt (1)
12-13: 의미 없는 EOF 개행기능적 변화가 없는 단순 개행 추가입니다. 팀 컨벤션(예: POSIX-style EOF 개행 여부)에 맞춰 정리해 주세요.
noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendRepository.kt (1)
5-9: LGTM도메인 레이어에 의존성 없는 인터페이스 선언으로 깔끔하게 분리되었습니다.
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/WeatherRequest.kt (1)
3-6: 트레일링 콤마 사용 시 Kotlin 버전 확인파라미터 마지막에 있는 트레일링 콤마는 Kotlin 1.4 이상에서만 컴파일됩니다.
CI 환경의 Kotlin 버전이 이를 지원하는지 확인 부탁드립니다.noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/response/BridgeVacationPeriod.kt (1)
5-8: LGTM간결한 DTO 정의로 목적이 분명합니다.
noweekend-core/core-api/src/main/kotlin/noweekend/core/support/error/ErrorType.kt (1)
23-23: 개선된 에러 메시지가 사용자 경험을 향상시킵니다.MCP 서버 내부 에러에 대한 메시지가 더 구체적이고 사용자 친화적으로 변경되었습니다. "추천할 수 있는 상황이 아닙니다"라는 표현이 일시적인 서비스 불가 상태를 명확히 전달합니다.
noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendService.kt (1)
14-14: generateVacation 메서드 선언·구현 정상 확인 및 승인
noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendService.kt
–fun generateVacation(userId: String, request: GenerateVacationRequest): AiGenerateVacationApiResponse선언 확인noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendServiceImpl.kt
–override fun generateVacation(...)구현부 확인
–GenerateVacationRequest→AiGenerateVacationRequest매핑 로직
– 외부 AI 클라이언트 호출 및AiGenerateVacationApiResponse반환 로직 정상 동작 예상AI 기반 휴가 생성 기능 확장에 부합하며, 추가 수정 사항이 없습니다. 승인합니다.
noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/holiday/HolidayServiceImpl.kt (3)
14-14: 읽기 전용 작업 분리로 아키텍처 개선
holidayRepository대신holidayReader를 사용하여 읽기 작업을 분리한 것은 좋은 아키텍처 개선입니다.CQRS 패턴을 따라 읽기와 쓰기 작업을 명확히 분리하였습니다.
27-28: 남은 휴일 필터링 로직 수동 확인 필요
findRemainingHolidays메서드 구현이 오늘 이후의 날짜만 정확히 필터링하는지 확인이 불가해 스크립트 검증이 실패했습니다. 아래 항목을 직접 검토해 주세요.
- HolidayReader(또는 HolidayServiceImpl)이
findRemainingHolidays를 어떻게 구현하는지 파일 위치 확인- 해당 메서드 내부에서
today이후의 휴일만 필터링하는 날짜 비교 로직 검토
21-21: findMonthHolidays의 월별 필터링 구현 확인 완료
HolidayReader 인터페이스의 기본 구현에서.filter { it.month == month }로 월별 필터링이 적용되어 있어, 별도 추가 작업이 필요 없습니다.
- core-domain/src/main/kotlin/noweekend/core/domain/holiday/HolidayReader.kt:
fun findMonthHolidays(year: Int, month: Int): List {
…
.filter { it.month == month }
…
}noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendEntity.kt (2)
28-40: 도메인/엔티티 변환 함수 구현이 깔끔합니다.확장 함수를 활용한 변환 로직이 간결하고 명확합니다. 도메인과 영속성 계층 간의 분리가 잘 구현되어 있습니다.
16-18: ID 생성 전략 확인 완료도메인 모델의
Weekend.generate()에서IdGenerator.generate()를 통해 타임스탬프+UUID 기반 고유 ID를 생성하도록 구현되어 있으므로, JPA 엔티티에@GeneratedValue없이도 영속화 시 정상 작동합니다. 추가 수정이나 전략 명시가 필요하지 않습니다.noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendApi.kt (2)
50-55: 새로운 AI 휴가 생성 API 추가가 적절합니다.
generateVacation메서드 추가로 AI 기반 휴가 추천 기능이 확장되었습니다. 메서드 시그니처와 매핑이 일관성 있게 구현되어 있습니다.
48-48: client-mcp API 반환 타입 변경에 따른 버전 관리 필요
RecommendApi.getSandwich의 반환 타입이SandwichResponse→List<BridgeVacationPeriod>로 변경되었습니다.
내부RecommendServiceImpl등은 이미 새로운 타입에 맞춰 정상 동작하지만, 외부 소비자(다른 서비스·모듈)에서는 컴파일 오류나 런타임 에러가 발생할 수 있습니다.조치 사항:
- client-mcp 라이브러리의 MAJOR 버전을 올려(v1 → v2) 배포
- CHANGELOG 및 릴리스 노트에 변경점 명시
- 필요 시 하위 호환용 래퍼 메서드 제공 또는 마이그레이션 가이드 작성
외부 소비자들이 새 시그니처로 마이그레이션했는지 확인해주세요.
noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendReader.kt (1)
18-21: 메서드 구현이 간결하고 명확합니다.
getUpcomingWeekends메서드가 저장소에 적절히 위임하고 있으며, 로직이 명확합니다.noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendJpaRepository.kt (1)
6-9: JPA 저장소 구현이 Spring Data 규칙을 잘 따르고 있습니다.쿼리 메서드 명명 규칙과 매개변수 타입이 적절하며, Spring Data JPA의 쿼리 파생 기능을 효과적으로 활용하고 있습니다.
성능 향상을 위해
date컬럼에 인덱스 추가를 고려해주세요:CREATE INDEX idx_weekend_date ON weekend(date);noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/ChatbotController.kt (2)
7-10: 새로운 데이터 모델 import가 적절하게 추가되었습니다.AI 기반 휴가 생성과 샌드위치 휴가 추천 기능을 위한 request/response 모델들이 올바르게 import되었습니다.
42-45: getSandwich 엔드포인트가 올바르게 구현되었습니다.요청을 서비스 레이어로 적절히 위임하고 있으며, 반환 타입이 새로운 데이터 모델과 일치합니다.
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendClient.kt (1)
4-7: 새로운 데이터 모델과 예외 처리를 위한 import가 추가되었습니다.AI 휴가 생성 기능과 향상된 샌드위치 휴가 추천을 위한 필요한 import들이 적절하게 추가되었습니다.
noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendCoreRepository.kt (4)
1-11: 리포지토리 구조와 의존성 주입이 적절하게 구현되었습니다.Spring의
@Repository어노테이션과 생성자 주입을 사용하여 JPA 리포지토리를 래핑하는 구조가 올바르게 구현되었습니다.
12-14: 도메인 객체 등록 메서드가 간결하게 구현되었습니다.
register메서드에서 도메인 객체를 엔티티로 변환 후 저장하는 로직이 적절합니다.
16-20: 연도별 주말 조회 로직이 올바르게 구현되었습니다.연도의 시작일(1월 1일)과 마지막일(12월 31일)을 사용한 날짜 범위 검색이 정확하며, 엔티티를 도메인 객체로 변환하는 매핑도 적절합니다.
22-25: 특정 날짜 이후 주말 조회 기능이 적절하게 구현되었습니다.
findAfterDate메서드에서 JPA 쿼리 결과를 도메인 객체로 변환하는 로직이 올바르게 구현되었습니다.noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/model/Sandwich.kt (4)
8-10: SandwichRequest에 필요한 필드들이 적절히 추가되었습니다.남은 연차 일수(
remainingAnnualLeave)와 주말 날짜 목록(weekends)이 추가되어 더 정확한 휴가 추천이 가능해졌습니다.
12-15: BridgeVacationPeriod 데이터 클래스가 명확하게 정의되었습니다.연속 휴가 구간을 나타내는 시작일과 종료일 필드가 적절하게 구성되었습니다.
20-22: SandwichResponse에 유용한 정보 필드들이 추가되었습니다.실제 사용 연차 일수(
useAnnualLeave)와 총 휴가 일수(totalVacationDays)가 추가되어 사용자에게 더 상세한 정보를 제공할 수 있습니다.
24-26: SandwichApiResponse 래퍼 클래스가 적절하게 구현되었습니다.여러 개의 휴가 추천 결과를 담을 수 있는 구조로 API 응답 형태가 향상되었습니다.
noweekend-core/core-api/src/main/kotlin/noweekend/core/api/controller/v1/docs/RecommendControllerDocs.kt (4)
9-9: import 변경이 새로운 응답 모델과 일치합니다.
SandwichResponse에서SandwichApiResponse로 변경되어 새로운 다중 휴가 구간 응답 구조를 반영합니다.
252-256: API 설명이 향상된 기능을 정확히 반영합니다.샌드위치 휴가 추천 기능의 새로운 특징들(남은 연차, 주말 고려, 다중 휴가 구간, 상세 정보 제공)이 잘 설명되어 있습니다.
272-285: 예제 응답이 새로운 데이터 모델과 일치합니다.다중 휴가 구간을 보여주는 예제 응답이
SandwichApiResponse구조와 정확히 일치하며, 각 구간별로useAnnualLeave와totalVacationDays정보가 포함되어 있습니다.
323-325: 메서드 반환 타입이 새로운 응답 모델로 올바르게 업데이트되었습니다.
ApiResponse<SandwichApiResponse>로 변경되어 다중 휴가 구간 응답을 지원합니다.noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/Prompt.kt (1)
124-147: JSON 인젝션 방지 확인 필요사용자 입력이 프롬프트에 직접 포함되므로 JSON 인젝션 가능성을 확인해야 합니다.
ObjectMapper를 사용하여 JSON을 생성하고 있어 기본적인 이스케이핑은 되지만, 추가 검증이 필요할 수 있습니다:
// 입력 검증 예시 require(req.days in 1..365) { "Invalid days range" } require(req.upcomingHolidays.all { it.matches(datePattern) }) { "Invalid date format" }noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendServiceImpl.kt (1)
219-220: 주말과 공휴일 중복 제거 로직 확인
holidayOrWeekendSet을 생성할 때 Set으로 변환하여 중복을 제거하고 있습니다. 이는 적절한 접근입니다.코드가 명확하고 효율적으로 작성되었습니다.
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/McpNotRespondingException.kt
Show resolved
Hide resolved
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/RestTemplateConfig.kt
Show resolved
Hide resolved
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/RestTemplateConfig.kt
Show resolved
Hide resolved
noweekend-core/core-api/src/main/kotlin/noweekend/core/api/controller/v1/RecommendController.kt
Show resolved
Hide resolved
noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/Weekend.kt
Show resolved
Hide resolved
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendClient.kt
Show resolved
Hide resolved
...kend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/AiGenerateVacation.kt
Show resolved
Hide resolved
...lients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/model/AiGenerateVacation.kt
Show resolved
Hide resolved
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/service/ChatbotService.kt
Show resolved
Hide resolved
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/service/ChatbotService.kt
Show resolved
Hide resolved
ec31046 to
9ffaa72
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (3)
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/McpNotRespondingException.kt (1)
5-5: 불필요한 기본 생성자 및 메시지 전달 누락이전 리뷰 댓글에서 이미 지적된 것과 동일한 문제입니다. 빈 기본 생성자를 제거하고 의미 있는 메시지나 cause를 전달할 수 있는 생성자를 추가해야 합니다.
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/service/ChatbotService.kt (2)
152-235: generateVacation 메서드 복잡도 개선 필요이전 리뷰에서 지적된 것과 동일한 문제입니다. 이 메서드가 여전히 너무 많은 책임을 담당하고 있습니다:
- 샌드위치 날짜 계산
- 청크 단위 일정 생성
- 사용된 항목 추적
- 요약 생성
각 단계를 별도 메서드로 분리하여 가독성과 테스트 용이성을 향상시켜야 합니다.
310-310: getSandwich 메서드의 재시도 횟수가 과도함이전 리뷰에서 지적된 것과 동일한 문제입니다. 10회 재시도는 여전히 과도하며, 재시도 간격이 없어 API 서버에 부담을 줄 수 있습니다.
🧹 Nitpick comments (1)
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/Prompt.kt (1)
149-222: detailedPlanPrompt 메서드 복잡도 높음이 메서드는 매우 복잡하고 많은 책임을 담당하고 있습니다. 프롬프트 생성 로직을 더 작은 단위로 분리하는 것을 고려해보세요.
fun detailedPlanPrompt( req: AiGenerateVacationRequest, dates: List<String>, offset: Int, totalDays: Int, prevUsed: List<String>, ): String { val headers = generateDayHeaders(dates, offset) val usedClause = generateUsedClause(prevUsed) val profileJson = generateProfileJson(req) val instructions = generateInstructions(dates.size, totalDays, profileJson, usedClause) return "$instructions\n\nHEADERS:\n$headers\n\nNow generate the itinerary." } private fun generateDayHeaders(dates: List<String>, offset: Int): String { ... } private fun generateUsedClause(prevUsed: List<String>): String { ... } private fun generateProfileJson(req: AiGenerateVacationRequest): String { ... } private fun generateInstructions(daysCount: Int, totalDays: Int, profileJson: String, usedClause: String): String { ... }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (31)
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/McpNotRespondingException.kt(1 hunks)noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendApi.kt(2 hunks)noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendClient.kt(2 hunks)noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/model/AiGenerateVacation.kt(1 hunks)noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/model/Sandwich.kt(1 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/api/controller/v1/RecommendController.kt(2 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/api/controller/v1/docs/RecommendControllerDocs.kt(5 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/holiday/HolidayServiceImpl.kt(2 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendService.kt(1 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendServiceImpl.kt(4 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/weekend/WeekendScheduler.kt(1 hunks)noweekend-core/core-api/src/main/kotlin/noweekend/core/support/error/ErrorType.kt(1 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/holiday/HolidayReader.kt(2 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/Weekend.kt(1 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendReader.kt(1 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendRepository.kt(1 hunks)noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendWriter.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/LangGraphConfig.kt(0 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/MyAgentState.kt(0 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/RestTemplateConfig.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/ChatbotController.kt(2 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/Prompt.kt(4 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/AiGenerateVacation.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/SandwichRequest.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/response/BridgeVacationPeriod.kt(1 hunks)noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/service/ChatbotService.kt(5 hunks)noweekend-mcp/mcp-host/src/main/resources/application.yml(2 hunks)noweekend-mcp/mcp-server/src/main/kotlin/noweekend/mcpserver/controller/WeatherController.kt(0 hunks)noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendCoreRepository.kt(1 hunks)noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendEntity.kt(1 hunks)noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendJpaRepository.kt(1 hunks)
💤 Files with no reviewable changes (3)
- noweekend-mcp/mcp-server/src/main/kotlin/noweekend/mcpserver/controller/WeatherController.kt
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/MyAgentState.kt
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/LangGraphConfig.kt
✅ Files skipped from review due to trivial changes (1)
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/SandwichRequest.kt
🚧 Files skipped from review as they are similar to previous changes (23)
- noweekend-core/core-api/src/main/kotlin/noweekend/core/support/error/ErrorType.kt
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/config/RestTemplateConfig.kt
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/response/BridgeVacationPeriod.kt
- noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendWriter.kt
- noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/holiday/HolidayServiceImpl.kt
- noweekend-core/core-api/src/main/kotlin/noweekend/core/api/controller/v1/RecommendController.kt
- noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/weekend/WeekendScheduler.kt
- noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendEntity.kt
- noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/Weekend.kt
- noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendClient.kt
- noweekend-mcp/mcp-host/src/main/resources/application.yml
- noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/holiday/HolidayReader.kt
- noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendReader.kt
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/ChatbotController.kt
- noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendService.kt
- noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/model/AiGenerateVacation.kt
- noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendJpaRepository.kt
- noweekend-core/core-api/src/main/kotlin/noweekend/core/api/controller/v1/docs/RecommendControllerDocs.kt
- noweekend-core/core-domain/src/main/kotlin/noweekend/core/domain/weekend/WeekendRepository.kt
- noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/RecommendApi.kt
- noweekend-storage/db-core/src/main/kotlin/noweekend/storage/db/core/weekend/WeekendCoreRepository.kt
- noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/request/AiGenerateVacation.kt
- noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/recommend/model/Sandwich.kt
🧰 Additional context used
🪛 detekt (1.23.8)
noweekend-clients/client-mcp/src/main/kotlin/noweekend/client/mcp/McpNotRespondingException.kt
[warning] 5-5: An empty default constructor can be removed.
(detekt.empty-blocks.EmptyDefaultConstructor)
🔇 Additional comments (8)
noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/service/ChatbotService.kt (4)
6-6: Prompt 의존성 주입으로 개선됨정적 상수 대신 Prompt 컴포넌트를 주입하여 프롬프트 관리를 개선한 것은 좋은 변경사항입니다.
43-43: 백오프 전략 개선재시도 간격을 지수적으로 증가시키는 백오프 전략을 도입한 것은 좋은 개선사항입니다.
125-125: 재시도 횟수 증가재시도 횟수가 2회에서 5회로 증가했습니다. 이는 합리적인 범위 내에서 안정성을 높이는 변경사항입니다.
337-337: 로그 메시지 오류재시도 횟수가 10회인데 로그 메시지에서는 "5"로 하드코딩되어 있습니다.
-println("getSandwich retry ${attempt + 1}/5: ${e.message}") +println("getSandwich retry ${attempt + 1}/10: ${e.message}")Likely an incorrect or invalid review comment.
noweekend-core/core-api/src/main/kotlin/noweekend/core/domain/recommend/RecommendServiceImpl.kt (3)
211-243: getSandwich 메서드 로직 개선메서드 시그니처 변경과 주말 데이터 통합, 브릿지 휴가 기간 계산 로직이 잘 구현되었습니다. 예외 처리도 적절합니다.
298-314: solveIcon 메서드 로직 개선아이콘 결정 로직이 명확하고 잘 구현되었습니다. 기본값 처리도 적절합니다.
245-247: generateDateRange 메서드 검증 완료
테스트 결과, 시작일과 종료일을 모두 포함하여 정상적으로 작동함을 확인했습니다.noweekend-mcp/mcp-host/src/main/kotlin/noweekend/mcphost/controller/Prompt.kt (1)
10-13: Spring 컴포넌트로 전환 개선정적 상수에서 Spring 컴포넌트로 전환하여 ObjectMapper 주입이 가능해진 것은 좋은 설계 변경입니다.
요약(개요)
작업 내용
집중해서 리뷰해야 하는 부분
기타 전달 사항 및 참고 자료(선택)
application.yml내 Jasypt 암호화 관련 환경변수 세팅 주의Summary by CodeRabbit
신규 기능
버그 수정 및 개선
문서
리팩터 및 기타