Skip to content

Conversation

@unifolio0
Copy link
Contributor

@unifolio0 unifolio0 commented Nov 5, 2025

🚩 Issue

closed #71

🗣️ 리뷰 요구사항 (선택)

Summary by CodeRabbit

  • 주요 기능

    • BFF 전환으로 API 응답 안정성 및 외부 연동 개선 (즐겨찾기·예약 흐름 개선)
    • 페인 클라이언트 기반 통신 도입으로 서비스 통합 데이터 제공 강화
  • 인프라 / 배포

    • CI/CD 파이프라인 및 워크플로우 확장으로 자동 배포 범위 확대
    • 서비스 등록·모니터링 항목(헬스·메트릭스·프로메테우스) 노출 강화
  • 문서

    • 마이그레이션 로드맵 및 로컬/테스트 가이드 추가·정리
  • 테스트

    • 테스트 구조 현대화 및 Flyway 스키마 검증 테스트 추가

@unifolio0 unifolio0 self-assigned this Nov 5, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 5, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

BFF 전환과 마이크로서비스 분리 작업을 포함하는 대규모 리팩터링입니다. 공통 DTO 모듈(common-client) 추가, api-user/api-owner의 도메인 직접 의존을 Feign 클라이언트로 교체, 도메인 서비스(member/owner/reservation/restaurant)의 독립 애플리케이션화, CI/CD·Docker·테스트 재구성 및 관련 문서 추가/정리 작업이 포함됩니다.

Changes

Cohort / File(s) 변경 요약
GitHub Actions 워크플로우
.github/workflows/*
CD 워크플로우 추가/수정(Dev_API_Owner_CD.yml, Dev_API_User_CD.yml 등), 경로 필터 조정, 배포 러너명 변경, Dev_CI.yml에 MySQL 서비스 분리 및 Flyway 검증 스텝 추가
공통 DTO 모듈 (common-client)
common-client/build.gradle, common-client/src/main/java/com/wellmeet/common/dto/*
공통 레코드 DTO들(ReservationDTO, RestaurantDTO, MemberDTO, OwnerDTO, AvailableDateDTO 등) 추가 및 java-library 설정
BFF: api-user 모듈 (구현·설정·테스트)
api-user/build.gradle, api-user/src/main/java/com/wellmeet/client/*FeignClient.java, api-user/src/main/java/com/wellmeet/*User*Bff*.java, api-user/src/main/resources/application-*.yml, api-user/src/test/java/com/wellmeet/**/*
도메인 서비스 호출을 Feign 클라이언트로 전환, UserBffService/Controller 도입, Feign 설정/에러 디코더 추가, application- 설정 변경(Eureka/Feign/로그), 테스트를 MockMvc/Mockito 기반으로 재작성
BFF: api-owner 모듈 (구현·정적분석)
api-owner/build.gradle, api-owner/src/main/java/com/wellmeet/client/*FeignClient.java, api-owner/src/main/java/com/wellmeet/*Owner*Bff*.java, api-owner/src/main/resources/*, api-owner/src/test/java/...
api-owner 쪽 Feign 도입 및 BFF 서비스/컨트롤러 리네이밍, 로컬 DTO 삭제 및 common-client로 통합, 테스트 재구성
Favorite/Reservation/Restaurant 서비스 내부 리팩터링
api-user/src/main/java/com/wellmeet/{favorite,reservation,restaurant}/*.java
내부 구현을 도메인 엔티티 사용에서 공통 DTO + Feign 클라이언트 호출 방식으로 변경(로직 오케스트레이션 이동, Redis 분산 잠금 사용)
도메인 모듈 분리 및 Flyway 마이그레이션 추가
domain-member/*, domain-owner/*, domain-reservation/*, domain-restaurant/*
각 도메인 모듈에 Spring Boot 엔트리 활성화, build.gradle에 spring-boot 플러그인 및 common-client/ Flyway 의존 추가, application-*.yml 및 Flyway V1__init_schema.sql 추가/정리
Docker / Compose / 이미지 변경
discovery-server/Dockerfile, domain-*/Dockerfile, docker-compose.yml, infra-kafka/docker/docker-compose.yml
런타임 베이스 이미지 openjdk → eclipse-temurin 변경, 일부 Dockerfile 간소화, root docker-compose 및 Kafka 설정 조정(일부 파일 삭제)
배치 / 설정 정리
batch-reminder/src/main/resources/*
불필요한 배치 환경 설정 파일 제거
테스트 인프라·클린업 도구 개선
*/src/test/java/**/DataBaseCleaner.java, Base*Test 변경
데이터베이스 이름 동적 추출 추가, Base 테스트 계층에서 fixture 제거 및 Mockito 기반 테스트 전환
문서화 추가/갱신
CLAUDE.md, claudedocs/*
마이그레이션 로드맵, BFF 가이드, 테스트 가이드, 로컬 개발/인프라 가이드 등 문서 대폭 추가·재구성

Sequence Diagram(s)

sequenceDiagram
    actor User as 사용자
    participant BFF as API BFF (api-user)
    participant Redis as Redis Lock
    participant FeignR as ReservationFeignClient
    participant FeignA as AvailableDateFeignClient
    participant FeignM as MemberFeignClient
    participant FeignRest as RestaurantFeignClient
    participant Event as EventPublishService
    User->>BFF: 예약 생성 요청 (memberId, 요청)
    activate BFF
    BFF->>Redis: Lock 획득 시도
    Redis-->>BFF: Lock 획득
    BFF->>FeignR: 기존 예약 확인 (memberId)
    FeignR-->>BFF: 예약 목록(DTO)
    BFF->>FeignM: 회원 조회
    BFF->>FeignRest: 레스토랑 조회
    BFF->>FeignA: 가능한 시간 조회
    BFF->>FeignA: 수용인원 감소 (decreaseCapacity)
    FeignA-->>BFF: 성공
    BFF->>FeignR: 예약 생성 (POST)
    FeignR-->>BFF: ReservationDTO (created)
    BFF->>Event: ReservationCreatedEvent 발행
    Event-->>BFF: 발행 완료
    BFF->>Redis: Lock 해제
    BFF-->>User: 생성 응답 (CreateReservationResponse)
    deactivate BFF
Loading

(색상 강조: 없음 — 흐름 중심의 간단한 시퀀스)

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120+ minutes

  • 이유: 대규모 아키텍처 전환(도메인 → BFF/Feign), 공통 DTO 도입으로 인한 API 계약 변경, 여러 모듈에 걸친 동시 변경, 테스트 방식 전면 교체, CI/CD 및 인프라 설정 수정 등 복합적이고 위험도가 높은 변경이 포함되어 있습니다.

주의할 파일/영역:

  • FeignConfig / FeignErrorDecoder (타임아웃·재시도·에러 맵핑)
  • Reservation/AvailableDate의 분산 잠금 및 용량 증감 시나리오(경합/예외 보상)
  • common-client DTO와 각 모듈 간 필드/열거형(ReservationStatus) 일치성
  • Flyway 마이그레이션 SQL (문법·인덱스·제약 조건)
  • CI/CD 워크플로우 경로 필터와 배포 러너 매핑
  • 테스트 전환(통합 → Mock 기반)으로 인한 커버리지/회귀 가능성

Possibly related PRs

Suggested labels

feat

Suggested reviewers

  • ezcolin2

Poem

🐰
DTO 한 장 들고 뛰어왔네,
Feign 다리 놓아 멀리 손을 내밀고,
Redis 잠금으로 약속하며,
도메인들은 독립의 숲으로 떠나고,
BFF 밭엔 새 길이 반짝이네 ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive PR 설명이 템플릿 구조를 따르지만, 이슈 번호('#71')는 명시되어 있으나 리뷰 요구사항 섹션이 비어있어 필수 정보가 부족합니다. 리뷰 요구사항 섹션을 채우거나, 구체적인 테스트 범위, 마이그레이션 검증 항목 등 추가 컨텍스트를 제공하세요.
Out of Scope Changes check ❓ Inconclusive 대부분의 변경사항이 api-user BFF 전환과 관련이 있으나, domain-member/owner/reservation/restaurant의 db 마이그레이션, Dockerfile 기본 이미지 변경, 광범위한 문서 추가 등이 포함되어 있습니다. db 마이그레이션, infra 변경, 문서 추가가 api-user BFF 전환과의 관계를 명확히 하거나, 별도 PR로 분리하는 것을 검토하세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목 '[FEAT] api-user BFF 전환'은 핵심 변경사항인 api-user 모듈의 BFF 아키텍처 전환을 명확하고 간결하게 요약하고 있습니다.
Linked Issues check ✅ Passed 이슈 #71은 BFF 전환 목표만 명시되어 있고 구체적인 수락 기준이 없으나, 코드 변경사항이 Feign 클라이언트 도입, DTO 통합, 테스트 마이그레이션을 포함하여 BFF 전환 요구사항을 충족합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#71

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @unifolio0, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 Pull Request는 api-user 모듈을 백엔드 포 프론트엔드(BFF) 레이어로 전환하는 중요한 아키텍처 변경을 포함합니다. api-user가 더 이상 도메인 서비스의 내부 구현에 직접적으로 의존하지 않고, Spring Cloud OpenFeign을 활용하여 다른 마이크로서비스들과 견고하고 선언적인 방식으로 통신하도록 재구성되었습니다. 이 변화는 모듈성을 강화하고, api-user의 역할을 명확히 하며, 애플리케이션의 확장 가능한 마이크로서비스 아키텍처를 위한 기반을 마련합니다.

Highlights

  • api-user 모듈 BFF 전환: api-user 모듈이 백엔드 포 프론트엔드(BFF) 아키텍처로 전환되었습니다. 이는 api-user가 다른 도메인 서비스의 직접적인 구현에 의존하지 않고, 선언적 HTTP 클라이언트인 Feign을 통해 통신하도록 변경되었음을 의미합니다.
  • Spring Cloud OpenFeign 도입: api-user 모듈에 Spring Cloud OpenFeign이 도입되어 domain-member, domain-owner, domain-reservation, domain-restaurant 서비스와 통신하기 위한 클라이언트 인터페이스들이 추가되었습니다. 이를 통해 서비스 간의 결합도를 낮추고 유지보수성을 향상시켰습니다.
  • 도메인 서비스 직접 의존성 제거: api-user 모듈에서 기존에 존재하던 domain-common, domain-reservation, domain-member, domain-owner, domain-restaurant 프로젝트에 대한 직접적인 의존성이 제거되었습니다. 이는 각 서비스의 독립성을 강화하고 BFF의 역할을 명확히 합니다.
  • 환경 설정 및 Eureka 구성 개선: 여러 서비스(api-owner, api-user, discovery-server, domain-member, domain-owner, domain-reservation, domain-restaurant)의 application.yml 파일들이 개발, 로컬, 테스트 환경에 맞춰 세분화되고, Eureka 클라이언트/서버 설정 및 민감 정보 관리를 위한 dev-secret.yml 파일이 추가되었습니다.
  • 테스트 코드 리팩토링: api-user 모듈의 테스트 코드가 BFF 전환에 맞춰 리팩토링되었습니다. 기존의 도메인 서비스 직접 호출 방식에서 Feign 클라이언트를 Mocking하는 방식으로 변경되었으며, 불필요한 데이터베이스 클리너 및 도메인 관련 테스트 픽스처 의존성이 제거되었습니다.
Ignored Files
  • Ignored by pattern: .github/workflows/** (8)
    • .github/workflows/Dev_API_Owner_CD.yml
    • .github/workflows/Dev_API_User_CD.yml
    • .github/workflows/Dev_CI.yml
    • .github/workflows/Dev_Discovery_CD.yml
    • .github/workflows/Dev_Domain_Member_CD.yml
    • .github/workflows/Dev_Domain_Owner_CD.yml
    • .github/workflows/Dev_Domain_Reservation_CD.yml
    • .github/workflows/Dev_Domain_Restaurant_CD.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist
Copy link

Summary of Changes

Hello @unifolio0, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

본 Pull Request는 api-user 서비스를 BFF 아키텍처로 전환하고, MSA 환경에서의 서비스 간 통신을 위해 OpenFeign 클라이언트를 도입하는 것을 목표로 합니다. 또한, 설정 파일 관리 및 테스트 환경을 개선하여 전체적인 시스템 유지보수성을 향상시킵니다.

Highlights

  • BFF 전환: api-user 서비스를 BFF (Backend For Frontend) 아키텍처로 전환하여, 클라이언트의 요구사항에 맞는 API를 제공하고 MSA 환경에서의 유연성을 확보합니다.
  • OpenFeign 클라이언트 도입: Spring Cloud OpenFeign을 사용하여 다른 도메인 서비스와의 통신을 단순화하고, 선언적인 방식으로 API 클라이언트를 구현합니다.
  • 설정 변경: application 설정 파일에서 domain 관련 설정을 제거하고 Eureka 서버 URL을 secret yml 파일에서 관리하도록 변경하여 보안을 강화합니다.
  • 테스트 환경 개선: 테스트 관련 fixture 생성 코드를 제거하고, DataBaseCleaner 클래스를 제거하여 테스트 환경을 간소화합니다.
Ignored Files
  • Ignored by pattern: .github/workflows/** (8)
    • .github/workflows/Dev_API_Owner_CD.yml
    • .github/workflows/Dev_API_User_CD.yml
    • .github/workflows/Dev_CI.yml
    • .github/workflows/Dev_Discovery_CD.yml
    • .github/workflows/Dev_Domain_Member_CD.yml
    • .github/workflows/Dev_Domain_Owner_CD.yml
    • .github/workflows/Dev_Domain_Reservation_CD.yml
    • .github/workflows/Dev_Domain_Restaurant_CD.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 api-user 모듈을 BFF(Backend-For-Frontend)로 전환하고 도메인 모듈들을 독립적인 마이크로서비스로 변환하는 중요한 아키텍처 변경을 구현합니다. 서비스 디스커버리를 위한 Eureka와 서비스 간 통신을 위한 Feign을 사용하는 Spring Cloud 기반 구조는 잘 적용되었습니다. 전반적인 구조는 견고하지만, 분산 트랜잭션, 성능, 오류 처리와 관련하여 해결해야 할 몇 가지 중요하고 심각도가 높은 이슈들이 있습니다.

Comment on lines +17 to +23
return switch (response.status()) {
case 400 -> new IllegalArgumentException("잘못된 요청입니다: " + response.reason());
case 404 -> new IllegalArgumentException("요청한 리소스를 찾을 수 없습니다: " + response.reason());
case 500 -> new RuntimeException("서버 내부 오류가 발생했습니다: " + response.reason());
case 503 -> new RuntimeException("서비스를 사용할 수 없습니다: " + response.reason());
default -> defaultDecoder.decode(methodKey, response);
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

HTTP 404 상태 코드를 IllegalArgumentException으로 매핑하는 것은 의미상 올바르지 않습니다. '리소스를 찾을 수 없음' 오류는 '잘못된 인수' 오류와 다릅니다. 이는 오류 처리 로직에서 혼란을 야기할 수 있습니다. 404 오류에 대해 ResourceNotFoundException과 같은 더 구체적인 커스텀 예외를 발생시키거나, Feign의 기본 디코더가 FeignException.NotFound를 던지도록 하는 것이 좋습니다.

Comment on lines 30 to 42
public List<NearbyRestaurantResponse> findWithNearbyRestaurant(double latitude, double longitude) {
return restaurantDomainService.findWithBoundBox(latitude, longitude)
return restaurantClient.getAllRestaurants()
.stream()
.filter(restaurant -> {
double distance = DistanceCalculator.calculateDistance(
latitude, longitude,
restaurant.getLatitude(), restaurant.getLongitude()
);
return distance <= SEARCH_RADIUS_M;
})
.map(restaurant -> getNearbyRestaurantResponse(restaurant, latitude, longitude))
.toList();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

findWithNearbyRestaurant 메서드는 restaurantClient.getAllRestaurants()를 통해 모든 식당 정보를 가져온 후 BFF의 메모리에서 거리를 기준으로 필터링합니다. 이는 식당 수가 증가함에 따라 성능 문제를 야기할 수 있는 매우 비효율적인 방식입니다. domain-restaurant-service는 좌표와 반경을 인자로 받아 서비스 측에서(가급적 데이터베이스의 공간 인덱스를 사용하여) 효율적으로 검색을 수행하는 엔드포인트를 제공해야 합니다.

Comment on lines 46 to 50
private FavoriteRestaurantResponse getFavoriteRestaurantResponse(RestaurantDTO restaurant) {
Double rating = restaurantClient.getAverageRating(restaurant.getId());
double ratingValue = (rating != null) ? rating : 0.0;
return new FavoriteRestaurantResponse(restaurant, ratingValue);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

getFavoriteRestaurants 메서드 내에서 각 즐겨찾기 항목에 대해 getFavoriteRestaurantResponse가 호출되면서 N+1 조회 문제가 발생합니다. 각 즐겨찾기 식당의 평균 평점을 가져오기 위해 restaurantClient.getAverageRating()을 개별적으로 호출하고 있습니다. 이를 최적화하기 위해 getRestaurantsByIds 배치 호출 시 반환되는 RestaurantDTO에 평균 평점 정보를 포함하도록 domain-restaurant-service를 수정하는 것을 고려해 보세요.

Comment on lines 105 to 119
return reservations.stream()
.map(reservation -> {
var restaurant = restaurantsById.get(reservation.getRestaurantId());
var availableDate = availableDatesById.get(reservation.getAvailableDateId());
return new SummaryReservationResponse(reservation, restaurant.getName(), availableDate);
RestaurantDTO restaurant = restaurantsById.get(reservation.getRestaurantId());
// AvailableDate는 각 Restaurant에서 개별 조회
AvailableDateDTO availableDate = restaurantClient.getAvailableDate(
reservation.getRestaurantId(),
reservation.getAvailableDateId()
);
return new SummaryReservationResponse(
reservation,
restaurant.getName(),
availableDate
);
})
.toList();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

getReservations 메서드에 N+1 조회 문제가 있습니다. 각 예약을 순회하면서 restaurantClient.getAvailableDate()를 개별적으로 호출하여 AvailableDateDTO를 가져옵니다. 이는 예약 수가 증가함에 따라 성능 저하를 초래할 것입니다. 이 문제를 해결하려면 AvailableDateClientgetAvailableDatesByIds와 같은 배치(batch) 엔드포인트를 추가하고 이를 사용하여 필요한 모든 AvailableDateDTO 객체를 단일 네트워크 호출로 가져와야 합니다.

Comment on lines +31 to +35
@GetMapping("/api/restaurants/{restaurantId}/available-dates/{availableDateId}")
AvailableDateDTO getAvailableDate(
@PathVariable("restaurantId") String restaurantId,
@PathVariable("availableDateId") Long availableDateId
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

getAvailableDate 메서드가 RestaurantClient에 위치해 있지만, AvailableDate 리소스와 관련이 있습니다. 코드 구성과 관심사 분리를 개선하기 위해 이 메서드를 AvailableDate 관련 작업을 담당하는 AvailableDateClient로 이동하는 것이 더 논리적입니다. 이렇게 하면 클라이언트 인터페이스의 응집도가 높아집니다.

import org.springframework.http.HttpStatus;
import org.springframework.test.context.bean.override.mockito.MockitoBean;

class RestaurantControllerTest extends BaseControllerTest {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

컨트롤러 테스트 전략에 일관성이 없습니다. 이 테스트는 @SpringBootTest와 함께 RestAssured를 사용하는 반면, FavoriteControllerTestReservationControllerTest@WebMvcTest와 함께 MockMvc를 사용합니다. 테스트 스위트의 이해와 유지보수를 용이하게 하려면 일관된 단일 접근 방식을 사용하는 것이 좋습니다. MockMvc는 더 빠르고 전체 서버를 구동할 필요가 없기 때문에 컨트롤러 슬라이스 테스트에 일반적으로 선호됩니다.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 api-user 서비스를 BFF(Backend-For-Frontend) 패턴으로 성공적으로 리팩터링하여, 도메인 모듈과의 결합도를 낮추고 서비스 간 통신을 위해 Feign 클라이언트를 도입했습니다. 이는 중요한 아키텍처 개선입니다. 변경 사항에는 마이크로서비스 환경에 필요한 의존성, Feign 클라이언트, DTO 및 구성 추가가 포함됩니다. 테스트 또한 새로운 서비스/클라이언트 경계를 반영하여 업데이트되었습니다.

하지만 몇 가지 개선할 점이 있습니다:

  • 심각한 성능 문제: 주변 식당 검색 기능이 모든 식당을 가져와 메모리에서 필터링하므로 확장성이 떨어집니다.
  • N+1 문제: 예약 목록 조회 시 각 예약의 예약 가능 정보를 얻기 위해 여러 번의 API 호출이 발생합니다.
  • 설정: Feign 클라이언트 설정이 외부 설정 파일이 아닌 코드에 하드코딩되어 있습니다.
  • 오류 처리: 사용자 정의 Feign 오류 디코더가 더 구체적인 예외를 던지도록 개선할 수 있습니다.
  • 코드 일관성: DTO 구현(record vs class)에 일관성이 부족합니다.

Comment on lines 30 to 41
public List<NearbyRestaurantResponse> findWithNearbyRestaurant(double latitude, double longitude) {
return restaurantDomainService.findWithBoundBox(latitude, longitude)
return restaurantClient.getAllRestaurants()
.stream()
.filter(restaurant -> {
double distance = DistanceCalculator.calculateDistance(
latitude, longitude,
restaurant.getLatitude(), restaurant.getLongitude()
);
return distance <= SEARCH_RADIUS_M;
})
.map(restaurant -> getNearbyRestaurantResponse(restaurant, latitude, longitude))
.toList();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

현재 findWithNearbyRestaurant 메서드는 restaurant-service로부터 모든 식당 목록을 가져온 후 BFF에서 거리를 기준으로 필터링합니다. 이는 식당 수가 증가함에 따라 확장성이 매우 떨어지는 비효율적인 방식입니다. 메모리 사용량과 네트워크 트래픽이 크게 증가할 것입니다.

restaurant-service에서 위도, 경도, 검색 반경을 파라미터로 받아 데이터베이스 수준에서 필터링을 수행하는 엔드포인트를 제공해야 합니다. RestaurantClient는 이 새로운 엔드포인트를 호출하도록 수정되어야 합니다.
예를 들어, restaurant-service는 다음과 같은 엔드포인트를 노출할 수 있습니다:
GET /api/restaurants/nearby?latitude={lat}&longitude={lon}&radius={radius}

이렇게 하면 성능이 크게 향상됩니다.

Comment on lines 48 to 55
List<ReservationDTO> memberReservations = reservationClient.getReservationsByMember(memberId);
boolean alreadyReserved = memberReservations.stream()
.anyMatch(r -> r.getRestaurantId().equals(request.getRestaurantId())
&& r.getAvailableDateId().equals(request.getAvailableDateId())
&& r.getStatus().equals("CONFIRMED"));
if (alreadyReserved) {
throw new IllegalStateException("이미 예약된 날짜입니다.");
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

reserve 메서드 내의 중복 예약 확인 로직이 회원의 모든 예약 정보를 가져와 BFF에서 필터링하고 있습니다. 회원이 많은 예약을 가지고 있을 경우 이는 비효율적일 수 있습니다.

더 효율적인 방법은 이 확인 로직을 domain-reservation-service에 위임하는 것입니다. 이를 위한 전용 엔드포인트를 만들 수 있습니다.

ReservationClient 예시:

@GetMapping("/check-duplicate")
boolean hasDuplicateReservation(@RequestParam String memberId, @RequestParam String restaurantId, @RequestParam Long availableDateId);

이렇게 하면 비즈니스 로직이 책임이 있는 서비스로 이동하고 네트워크를 통해 전송되는 데이터 양을 줄일 수 있습니다.

Comment on lines 105 to 119
return reservations.stream()
.map(reservation -> {
var restaurant = restaurantsById.get(reservation.getRestaurantId());
var availableDate = availableDatesById.get(reservation.getAvailableDateId());
return new SummaryReservationResponse(reservation, restaurant.getName(), availableDate);
RestaurantDTO restaurant = restaurantsById.get(reservation.getRestaurantId());
// AvailableDate는 각 Restaurant에서 개별 조회
AvailableDateDTO availableDate = restaurantClient.getAvailableDate(
reservation.getRestaurantId(),
reservation.getAvailableDateId()
);
return new SummaryReservationResponse(
reservation,
restaurant.getName(),
availableDate
);
})
.toList();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

getReservations 메서드에 N+1 API 호출 문제가 있습니다. 예약 목록을 가져온 후, 각 예약을 순회하며 restaurantClient.getAvailableDate를 개별적으로 호출하고 있습니다. 사용자가 많은 예약을 가지고 있을 경우, 이는 수많은 네트워크 호출을 유발하여 성능 저하의 원인이 됩니다.

이 문제를 해결하기 위해 restaurant-service는 여러 AvailableDate 객체를 ID로 한 번에 조회할 수 있는 배치(batch) 엔드포인트를 제공해야 합니다.
예를 들어:

  1. 필요한 모든 (restaurantId, availableDateId) 쌍을 수집합니다.
  2. restaurant-service에 단일 배치 호출을 하여 필요한 모든 AvailableDateDTO를 가져옵니다.
  3. 메모리에서 가져온 AvailableDateDTO들을 예약 정보와 매핑합니다.

RestaurantClient에 추가될 수 있는 새로운 엔드포인트 예시:

@PostMapping("/api/restaurants/available-dates/batch")
List<AvailableDateDTO> getAvailableDatesInBatch(@RequestBody AvailableDateBatchRequest request);

여기서 AvailableDateBatchRequest는 식별자 목록을 포함합니다.

Comment on lines 10 to 22
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AvailableDateDTO {

private Long id;
private LocalDate date;
private LocalTime time;
private int maxCapacity;
private boolean isAvailable;
private String restaurantId;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

DTO 구현에 일관성이 부족합니다. 일부는 Java record로 정의되어 있고(FavoriteRestaurantDTO, MenuDTO 등), 다른 일부는 Lombok 어노테이션을 사용하는 일반 클래스(AvailableDateDTO, MemberDTO 등)로 되어 있습니다. 두 방식 모두 기능적으로는 문제없지만, 일관된 스타일을 사용하면 코드 가독성과 유지보수성이 향상됩니다.

불변(immutable) 데이터 전송 객체의 경우, 간결하고 데이터 운반체로서의 의도를 명확하게 표현하는 Java record 사용을 권장합니다. 이 DTO와 같은 클래스 기반 DTO를 record로 변환하는 것을 고려해 보세요.

public record AvailableDateDTO(
    Long id,
    LocalDate date,
    LocalTime time,
    int maxCapacity,
    boolean isAvailable,
    String restaurantId
) {
}

Comment on lines +19 to +35
@Bean
public Request.Options requestOptions() {
return new Request.Options(
5000, TimeUnit.MILLISECONDS, // connectTimeout
5000, TimeUnit.MILLISECONDS, // readTimeout
true // followRedirects
);
}

@Bean
public Retryer retryer() {
return new Retryer.Default(
100, // period (초기 대기 시간)
1000, // maxPeriod (최대 대기 시간)
3 // maxAttempts (최대 재시도 횟수)
);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Feign 클라이언트의 연결/읽기 시간 초과 및 재시도 파라미터가 FeignConfig 클래스에 하드코딩되어 있습니다. 이로 인해 코드를 변경하지 않고는 다른 환경(예: local, dev, prod)에 맞게 이 값들을 변경하기 어렵습니다. application.yml에 이미 관련 설정이 있지만, 여기서 정의된 Bean이 우선 적용되어 YAML 설정이 무시되고 있습니다.

requestOptions()retryer() Bean 정의를 FeignConfig.java에서 제거하고 application.yml의 설정에 의존하는 것이 좋습니다. Spring Boot의 Feign 자동 설정이 이 속성들을 자동으로 인식하여 적용해 줄 것입니다. 이렇게 하면 코드가 단순해지고 구성이 더 유연해집니다.

Comment on lines +17 to +23
return switch (response.status()) {
case 400 -> new IllegalArgumentException("잘못된 요청입니다: " + response.reason());
case 404 -> new IllegalArgumentException("요청한 리소스를 찾을 수 없습니다: " + response.reason());
case 500 -> new RuntimeException("서버 내부 오류가 발생했습니다: " + response.reason());
case 503 -> new RuntimeException("서비스를 사용할 수 없습니다: " + response.reason());
default -> defaultDecoder.decode(methodKey, response);
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

현재 FeignErrorDecoder는 400과 404 상태 코드 모두에 대해 IllegalArgumentException을 발생시킵니다. 404는 "Not Found" 오류를 의미하므로 이는 오해의 소지가 있습니다. 또한 5xx 오류에 대해 일반적인 RuntimeException을 사용하면 서버 측 문제의 구체적인 성격이 가려집니다.

HTTP 상태에 매핑되는 더 구체적인 사용자 정의 예외를 사용하는 것이 좋습니다. 이러한 예외는 전역 예외 핸들러(@ControllerAdvice)에서 처리하여 BFF의 클라이언트에게 적절한 응답을 반환할 수 있습니다.

Suggested change
return switch (response.status()) {
case 400 -> new IllegalArgumentException("잘못된 요청입니다: " + response.reason());
case 404 -> new IllegalArgumentException("요청한 리소스를 찾을 수 없습니다: " + response.reason());
case 500 -> new RuntimeException("서버 내부 오류가 발생했습니다: " + response.reason());
case 503 -> new RuntimeException("서비스를 사용할 수 없습니다: " + response.reason());
default -> defaultDecoder.decode(methodKey, response);
};
return switch (response.status()) {
case 400 -> new IllegalArgumentException("잘못된 요청입니다: " + response.reason()); // Or a custom BadRequestException
case 404 -> new RuntimeException("요청한 리소스를 찾을 수 없습니다: " + response.reason()); // Or a custom ResourceNotFoundException
case 500 -> new RuntimeException("서버 내부 오류가 발생했습니다: " + response.reason());
case 503 -> new RuntimeException("서비스를 사용할 수 없습니다: " + response.reason());
default -> defaultDecoder.decode(methodKey, response);
};

this.id = reservation.getId();
this.restaurantName = restaurantName;
this.status = reservation.getStatus();
this.status = ReservationStatus.valueOf(reservation.getStatus());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

StringReservationStatus enum으로 변환하기 위해 ReservationStatus.valueOf(reservation.getStatus())를 사용하고 있습니다. 이 방식은 DTO의 문자열이 enum 상수 이름과 정확히 일치하지 않으면 IllegalArgumentException을 발생시켜 불안정합니다. 서비스 간에 enum 이름이 달라질 경우 문제가 발생할 수 있습니다. 이 문제는 ReservationResponse, SummaryReservationResponse에도 동일하게 적용됩니다.

더 안정적인 접근 방식은 ReservationDTOReservationStatus enum 타입을 직접 사용하도록 하는 것입니다. 만약 서비스 경계를 넘어 enum을 직접 사용하는 것이 어렵다면, 대소문자를 구분하지 않는 등 보다 안전한 변환 메서드를 추가하는 것을 고려해야 합니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (1)

22-29: ReservationStatus.valueOf() 호출 시 예외 처리 필요

검증 결과, ReservationDTOstatus 필드는 String 타입이며, 현재 구현에서는 데이터베이스 제약과 엔티티 계층의 제어된 메서드(confirm(), cancel())를 통해 PENDING/CONFIRMED/CANCELED 값만 저장됩니다. 그러나 DTO는 다양한 출처(캐시, 외부 API 등)에서 로드될 수 있으므로, ReservationStatus.valueOf(reservation.getStatus()) 호출 시 유효하지 않은 값으로 인한 IllegalArgumentException이 발생할 수 있습니다.

권장사항:

  • valueOf() 호출을 try-catch로 감싸거나 사전 검증 추가
  • 또는 잘못된 상태 값에 대한 명확한 에러 메시지 제공

동일한 패턴이 api-owner/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java:43에도 존재하므로 함께 개선 권장

♻️ Duplicate comments (2)
.github/workflows/Dev_Discovery_CD.yml (1)

62-62: Self-hosted 러너 레이블 설정

이 파일도 커스텀 러너 레이블(dev-discovery-server)을 사용합니다. Dev_Domain_Member_CD.yml 파일에 대한 이전 코멘트에서 언급한 actionlint.yaml 설정에 이 레이블도 함께 추가하세요.

.github/workflows/Dev_Domain_Owner_CD.yml (1)

63-63: Self-hosted 러너 레이블 설정

이 파일도 커스텀 러너 레이블(dev-domain-owner)을 사용합니다. Dev_Domain_Member_CD.yml 파일에 대한 이전 코멘트에서 언급한 actionlint.yaml 설정에 이 레이블도 함께 추가하세요.

🧹 Nitpick comments (16)
domain-reservation/src/main/resources/application-test.yml (1)

13-13: 테스트 환경 JPA 설정이 적절합니다.

ddl-auto: create-dropshow-sql: true 설정은 테스트 각 실행 시 깨끗한 스키마를 제공하고 쿼리 검증을 용이하게 합니다. 다만 성능 영향이 있을 수 있으므로, 테스트 속도가 중요한 경우 조건부 활성화를 고려하세요.

Also applies to: 18-18

domain-reservation/Dockerfile (1)

17-17: 개선: JVM 메모리 설정을 환경 변수로 변경

JVM 메모리 플래그(-Xms256m -Xmx512m)가 하드코딩되어 있어 런타임 시 조정할 수 없습니다. 환경 변수를 사용하여 유연성을 높이고 다양한 배포 환경에 대응할 수 있도록 하십시오.

다음 diff를 고려하십시오:

-ENTRYPOINT ["java", "-Xms256m", "-Xmx512m", "-XX:+UseG1GC", "-jar", "app.jar"]
+ENV JAVA_HEAP_MIN=256m
+ENV JAVA_HEAP_MAX=512m
+ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -Xms${JAVA_HEAP_MIN} -Xmx${JAVA_HEAP_MAX} -XX:+UseG1GC -jar app.jar"]

이렇게 하면 배포 시 다음과 같이 값을 재정의할 수 있습니다:

docker run -e JAVA_HEAP_MIN=512m -e JAVA_HEAP_MAX=1024m <image>
api-user/src/main/java/com/wellmeet/client/dto/request/DecreaseCapacityRequest.java (1)

3-4: 유효성 검증 어노테이션 추가 권장

클라이언트 요청 DTO에 @NotNull 검증을 추가하면 잘못된 요청을 조기에 방지할 수 있습니다. 도메인 레이어의 유사한 DTO는 검증을 포함합니다.

 package com.wellmeet.client.dto.request;

+import jakarta.validation.constraints.NotNull;
+
-public record DecreaseCapacityRequest(Long availableDateId, int partySize) {
+public record DecreaseCapacityRequest(
+    @NotNull Long availableDateId,
+    @NotNull Integer partySize
+) {
 }
api-user/src/main/java/com/wellmeet/client/dto/request/IncreaseCapacityRequest.java (1)

3-4: 유효성 검증 어노테이션 추가 권장

도메인 레이어의 IncreaseCapacityRequest에는 @NotNull 검증이 있지만, 이 클라이언트 버전에는 없습니다. 일관성을 위해 검증을 추가하세요.

Based on learnings

 package com.wellmeet.client.dto.request;

+import jakarta.validation.constraints.NotNull;
+
-public record IncreaseCapacityRequest(Long availableDateId, int partySize) {
+public record IncreaseCapacityRequest(
+    @NotNull Long availableDateId,
+    @NotNull Integer partySize
+) {
 }
api-user/src/main/java/com/wellmeet/client/dto/BusinessHourDTO.java (1)

1-22: 코드 중복: api-owner 모듈에 동일한 DTO가 존재합니다.

BusinessHourDTOapi-owner/src/main/java/com/wellmeet/client/dto/BusinessHourDTO.java에도 동일하게 정의되어 있습니다. 이는 코드 중복이며 두 모듈 간의 불일치를 초래할 수 있습니다.

공통 DTO를 domain-common 또는 별도의 공유 모듈로 추출하여 DRY 원칙을 준수하는 것을 권장합니다.

.github/workflows/Dev_Domain_Member_CD.yml (1)

63-63: Self-hosted 러너 레이블을 actionlint 설정에 추가하세요.

dev-domain-member 레이블은 커스텀 self-hosted 러너를 나타내는 것으로 보입니다. actionlint의 경고를 해결하기 위해 프로젝트 루트에 actionlint.yaml 파일을 생성하고 커스텀 러너 레이블을 명시적으로 선언하는 것을 권장합니다.

예시:

self-hosted-runner:
  labels:
    - dev-domain-member
    - dev-discovery-server
    - dev-domain-owner
    # 기타 커스텀 러너 레이블들...
api-owner/src/main/resources/dev-secret.yml (1)

1-19: 시크릿 파일 관리 방식을 확인하세요.

api-user의 dev-secret.yml과 동일한 구조로 플레이스홀더를 사용하고 있습니다. 일관성 있는 구성이지만, 다음 사항을 확인해주세요:

  1. 이 파일이 .gitignore에 포함되어 실제 시크릿이 노출되지 않는지 확인
  2. 여러 서비스에서 동일한 Kafka producer 설정을 사용하는 것이 의도된 것인지 검토 (향후 중앙화된 설정 관리 고려 가능)
api-user/src/main/java/com/wellmeet/client/dto/ReviewDTO.java (1)

3-10: rating 필드의 primitive 타입 사용 검토

rating 필드가 primitive double로 선언되어 있어 null 값을 표현할 수 없습니다. 리뷰 데이터에서 평점이 아직 설정되지 않은 경우를 처리해야 한다면 Double 래퍼 타입 사용을 고려하세요. 또한, rating 범위 검증을 위한 @Min, @Max 등의 validation 어노테이션 추가를 권장합니다.

api-user/src/main/java/com/wellmeet/client/dto/FavoriteRestaurantDTO.java (1)

3-8: DTO validation 추가 권장

memberIdrestaurantId에 대한 @NotNull 또는 @NotBlank 같은 validation 어노테이션 추가를 고려하세요. 이는 Feign 클라이언트 호출 시 잘못된 데이터가 전송되는 것을 조기에 방지할 수 있습니다.

api-user/src/main/java/com/wellmeet/client/dto/request/MemberIdsRequest.java (1)

9-16: 배치 요청 DTO에 validation 추가 권장

memberIds 리스트에 대한 @NotNull, @NotEmpty 등의 validation 어노테이션 추가를 고려하세요. 빈 리스트나 null 값으로 인한 불필요한 네트워크 호출을 방지할 수 있습니다.

api-user/src/main/java/com/wellmeet/client/dto/request/UpdateReservationDTO.java (1)

1-16: 유효성 검증과 빌더 패턴 추가를 고려하세요.

  1. 다른 DTO들(CreateReservationDTO, ReservationDTO)과 달리 @Builder 어노테이션이 없어 일관성이 떨어집니다.
  2. partySize에 대한 유효성 검증(@Positive, @Min)이 없습니다.
  3. 필수 필드(restaurantId, availableDateId)에 대한 @NotNull 검증이 없습니다.

일관성과 데이터 무결성을 위해 다음 변경을 적용하세요:

 package com.wellmeet.client.dto.request;

+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
 import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Getter;
 import lombok.NoArgsConstructor;

 @Getter
+@Builder
 @NoArgsConstructor
 @AllArgsConstructor
 public class UpdateReservationDTO {

+    @NotNull
     private String restaurantId;
+    @NotNull
     private Long availableDateId;
+    @Min(1)
     private int partySize;
     private String specialRequest;
 }
api-user/src/main/java/com/wellmeet/client/dto/request/CreateReservationDTO.java (1)

1-19: 유효성 검증 추가를 권장합니다.

필수 필드와 partySize에 대한 유효성 검증이 없습니다. 잘못된 요청 데이터로부터 보호하기 위해 검증 어노테이션을 추가하세요.

 package com.wellmeet.client.dto.request;

+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Getter;
 import lombok.NoArgsConstructor;

 @Getter
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
 public class CreateReservationDTO {

+    @NotNull
     private String restaurantId;
+    @NotNull
     private Long availableDateId;
+    @NotNull
     private String memberId;
+    @Min(1)
     private int partySize;
     private String specialRequest;
 }
api-user/src/main/java/com/wellmeet/config/FeignErrorDecoder.java (1)

17-23: 추가 HTTP 상태 코드 처리 고려

현재 400, 404, 500, 503에 대한 처리만 구현되어 있습니다. 인증/인가 관련 상태 코드(401 Unauthorized, 403 Forbidden)에 대한 명시적인 처리도 고려해보시기 바랍니다.

예시:

 return switch (response.status()) {
     case 400 -> new IllegalArgumentException("잘못된 요청입니다: " + response.reason());
+    case 401 -> new IllegalArgumentException("인증이 필요합니다: " + response.reason());
+    case 403 -> new IllegalArgumentException("접근 권한이 없습니다: " + response.reason());
     case 404 -> new IllegalArgumentException("요청한 리소스를 찾을 수 없습니다: " + response.reason());
     case 500 -> new RuntimeException("서버 내부 오류가 발생했습니다: " + response.reason());
     case 503 -> new RuntimeException("서비스를 사용할 수 없습니다: " + response.reason());
     default -> defaultDecoder.decode(methodKey, response);
 };
api-user/src/main/java/com/wellmeet/restaurant/RestaurantService.java (1)

32-53: 거리 계산 중복 호출 최소화 제안

DistanceCalculator.calculateDistance가 필터 단계와 getNearbyRestaurantResponse 내부에서 각각 한 번씩 호출되어 레스토랑마다 두 번 계산됩니다. 스트림에서 거리를 한 번 계산해 보관한 뒤 재사용하면 불필요한 연산을 줄이고, 호출당 성능을 조금이나마 절약할 수 있습니다. 현재 비용이 크지 않다면 유지해도 되지만, 향후 레스토랑 수가 많아질 경우를 대비해 구조화해 두는 편을 권장드립니다.

api-user/src/main/java/com/wellmeet/client/dto/request/UpdateOperatingHoursDTO.java (2)

23-23: dayOfWeek 필드의 타입 안정성을 개선하세요.

dayOfWeekString 대신 DayOfWeek enum 또는 표준 java.time.DayOfWeek를 사용하면 타입 안정성이 향상되고 잘못된 값 입력을 컴파일 타임에 방지할 수 있습니다.

+import java.time.DayOfWeek;
+
 public static class DayHoursDTO {
-    private String dayOfWeek;
+    private DayOfWeek dayOfWeek;
     private boolean isOperating;

22-29: 운영 시간 검증 로직 추가를 권장합니다.

영업 시작 시간이 종료 시간보다 빠른지, 휴게 시간이 영업 시간 내에 있는지 등의 비즈니스 규칙 검증이 필요합니다.

DTO에 @AssertTrue 메서드를 추가하거나, 별도의 validator를 고려하세요:

@AssertTrue(message = "영업 종료 시간은 시작 시간보다 늦어야 합니다")
private boolean isValidOperatingHours() {
    if (!isOperating || open == null || close == null) {
        return true;
    }
    return open.isBefore(close);
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a84551e and 84bd7b4.

📒 Files selected for processing (107)
  • .github/workflows/Dev_API_Owner_CD.yml (2 hunks)
  • .github/workflows/Dev_API_User_CD.yml (2 hunks)
  • .github/workflows/Dev_CI.yml (1 hunks)
  • .github/workflows/Dev_Discovery_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Member_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Owner_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Reservation_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Restaurant_CD.yml (1 hunks)
  • api-owner/src/main/resources/application-dev.yml (1 hunks)
  • api-owner/src/main/resources/dev-secret.yml (1 hunks)
  • api-owner/src/test/java/com/wellmeet/DataBaseCleaner.java (0 hunks)
  • api-user/build.gradle (1 hunks)
  • api-user/src/main/java/com/wellmeet/ApiUserApplication.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/AvailableDateClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/FavoriteRestaurantClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/MemberClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/OwnerClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/ReservationClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/RestaurantClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/BusinessHourDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/FavoriteRestaurantDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/MemberDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/MenuDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/OwnerDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/ReservationDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/ReviewDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/request/CreateReservationDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/request/DecreaseCapacityRequest.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/request/IncreaseCapacityRequest.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/request/MemberIdsRequest.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/request/RestaurantIdsRequest.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/request/UpdateOperatingHoursDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/request/UpdateReservationDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/dto/request/UpdateRestaurantDTO.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/config/FeignConfig.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/config/FeignErrorDecoder.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/favorite/FavoriteService.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/global/event/event/ReservationCanceledEvent.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/global/event/event/ReservationCreatedEvent.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/global/event/event/ReservationUpdatedEvent.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/ReservationService.java (3 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationRequest.java (0 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (3 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/dto/ReservationStatus.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/dto/SummaryReservationResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/RestaurantService.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/AvailableDateResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/NearbyRestaurantResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RepresentativeMenuResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RepresentativeReviewResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RestaurantResponse.java (2 hunks)
  • api-user/src/main/resources/application-dev.yml (1 hunks)
  • api-user/src/main/resources/application-local.yml (1 hunks)
  • api-user/src/main/resources/application-test.yml (1 hunks)
  • api-user/src/main/resources/dev-secret.yml (1 hunks)
  • api-user/src/test/java/com/wellmeet/BaseControllerTest.java (0 hunks)
  • api-user/src/test/java/com/wellmeet/BaseServiceTest.java (0 hunks)
  • api-user/src/test/java/com/wellmeet/DataBaseCleaner.java (0 hunks)
  • api-user/src/test/java/com/wellmeet/favorite/FavoriteControllerTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/favorite/FavoriteServiceTest.java (6 hunks)
  • api-user/src/test/java/com/wellmeet/global/controller/HealthCheckControllerTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/global/event/EventPublishServiceTest.java (4 hunks)
  • api-user/src/test/java/com/wellmeet/global/event/listener/ReservationEventListenerTest.java (4 hunks)
  • api-user/src/test/java/com/wellmeet/reservation/ReservationControllerTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/reservation/ReservationServiceTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/restaurant/DomainRestaurantControllerTest.java (0 hunks)
  • api-user/src/test/java/com/wellmeet/restaurant/RestaurantControllerTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/restaurant/RestaurantServiceTest.java (8 hunks)
  • batch-reminder/src/main/resources/application-dev.yml (0 hunks)
  • batch-reminder/src/main/resources/application-local.yml (0 hunks)
  • batch-reminder/src/main/resources/application-test.yml (0 hunks)
  • discovery-server/Dockerfile (1 hunks)
  • discovery-server/src/main/resources/application-dev.yml (1 hunks)
  • discovery-server/src/main/resources/application-local.yml (1 hunks)
  • domain-member/Dockerfile (1 hunks)
  • domain-member/build.gradle (1 hunks)
  • domain-member/src/main/java/com/wellmeet/domain/MemberServiceApplication.java (1 hunks)
  • domain-member/src/main/resources/application-dev.yml (1 hunks)
  • domain-member/src/main/resources/application-domain-test.yml (0 hunks)
  • domain-member/src/main/resources/application-local.yml (1 hunks)
  • domain-member/src/main/resources/application-test.yml (1 hunks)
  • domain-member/src/main/resources/dev-secret.yml (1 hunks)
  • domain-member/src/test/java/com/wellmeet/domain/member/BaseRepositoryTest.java (1 hunks)
  • domain-owner/Dockerfile (1 hunks)
  • domain-owner/build.gradle (1 hunks)
  • domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java (1 hunks)
  • domain-owner/src/main/resources/application-dev.yml (1 hunks)
  • domain-owner/src/main/resources/application-local.yml (1 hunks)
  • domain-owner/src/main/resources/application-test.yml (1 hunks)
  • domain-owner/src/main/resources/dev-secret.yml (1 hunks)
  • domain-owner/src/test/java/com/wellmeet/domain/owner/BaseRepositoryTest.java (1 hunks)
  • domain-reservation/Dockerfile (1 hunks)
  • domain-reservation/build.gradle (1 hunks)
  • domain-reservation/src/main/java/com/wellmeet/domain/ReservationServiceApplication.java (1 hunks)
  • domain-reservation/src/main/resources/application-dev.yml (1 hunks)
  • domain-reservation/src/main/resources/application-domain-dev.yml (0 hunks)
  • domain-reservation/src/main/resources/application-domain-local.yml (0 hunks)
  • domain-reservation/src/main/resources/application-domain-test.yml (0 hunks)
  • domain-reservation/src/main/resources/application-local.yml (1 hunks)
  • domain-reservation/src/main/resources/application-test.yml (1 hunks)
  • domain-reservation/src/main/resources/dev-secret.yml (1 hunks)
  • domain-reservation/src/test/java/com/wellmeet/BaseRepositoryTest.java (1 hunks)
  • domain-restaurant/Dockerfile (1 hunks)
⛔ Files not processed due to max files limit (8)
  • domain-restaurant/build.gradle
  • domain-restaurant/src/main/java/com/wellmeet/domain/RestaurantServiceApplication.java
  • domain-restaurant/src/main/resources/application-dev.yml
  • domain-restaurant/src/main/resources/application-domain-test.yml
  • domain-restaurant/src/main/resources/application-local.yml
  • domain-restaurant/src/main/resources/application-test.yml
  • domain-restaurant/src/main/resources/dev-secret.yml
  • domain-restaurant/src/test/java/com/wellmeet/BaseRepositoryTest.java
💤 Files with no reviewable changes (13)
  • domain-reservation/src/main/resources/application-domain-local.yml
  • domain-reservation/src/main/resources/application-domain-test.yml
  • batch-reminder/src/main/resources/application-local.yml
  • api-user/src/test/java/com/wellmeet/BaseServiceTest.java
  • batch-reminder/src/main/resources/application-dev.yml
  • api-user/src/test/java/com/wellmeet/BaseControllerTest.java
  • domain-reservation/src/main/resources/application-domain-dev.yml
  • api-user/src/test/java/com/wellmeet/DataBaseCleaner.java
  • batch-reminder/src/main/resources/application-test.yml
  • api-user/src/test/java/com/wellmeet/restaurant/DomainRestaurantControllerTest.java
  • api-owner/src/test/java/com/wellmeet/DataBaseCleaner.java
  • api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationRequest.java
  • domain-member/src/main/resources/application-domain-test.yml
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-10T02:52:48.337Z
Learnt from: unifolio0
Repo: Team-soft-macaron/WellMeet-Backend PR: 14
File: src/main/java/com/wellmeet/recommend/dto/RecommendRestaurantResponse.java:3-3
Timestamp: 2025-07-10T02:52:48.337Z
Learning: The Restaurant class is located at `com.wellmeet.restaurant.domain.Restaurant` after the package refactoring in the WellMeet-Backend project, not in a nested model package structure.

Applied to files:

  • api-user/src/main/java/com/wellmeet/client/dto/MenuDTO.java
  • api-user/src/main/java/com/wellmeet/client/dto/request/UpdateRestaurantDTO.java
  • api-user/src/main/java/com/wellmeet/restaurant/RestaurantService.java
  • api-user/src/main/java/com/wellmeet/client/dto/request/RestaurantIdsRequest.java
  • api-user/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RepresentativeMenuResponse.java
  • api-user/src/main/java/com/wellmeet/client/FavoriteRestaurantClient.java
  • api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java
  • api-user/src/main/java/com/wellmeet/client/RestaurantClient.java
  • api-user/src/test/java/com/wellmeet/restaurant/RestaurantServiceTest.java
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RestaurantResponse.java
  • api-user/src/main/java/com/wellmeet/client/dto/FavoriteRestaurantDTO.java
  • api-user/src/main/java/com/wellmeet/restaurant/dto/NearbyRestaurantResponse.java
  • api-user/src/test/java/com/wellmeet/favorite/FavoriteServiceTest.java
  • api-user/src/main/java/com/wellmeet/reservation/ReservationService.java
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RepresentativeReviewResponse.java
  • api-user/src/main/java/com/wellmeet/favorite/FavoriteService.java
  • api-user/src/main/java/com/wellmeet/client/ReservationClient.java
  • api-user/src/test/java/com/wellmeet/restaurant/RestaurantControllerTest.java
🧬 Code graph analysis (28)
api-user/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java (2)
api-user/src/main/java/com/wellmeet/restaurant/dto/AvailableDateResponse.java (1)
  • Getter (9-26)
api-owner/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java (1)
  • Getter (10-22)
api-user/src/main/java/com/wellmeet/client/dto/request/UpdateRestaurantDTO.java (1)
api-user/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java (1)
  • Getter (8-21)
api-user/src/main/java/com/wellmeet/restaurant/RestaurantService.java (1)
api-user/src/main/java/com/wellmeet/common/util/DistanceCalculator.java (1)
  • DistanceCalculator (3-18)
domain-member/src/main/java/com/wellmeet/domain/MemberServiceApplication.java (3)
domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java (1)
  • SpringBootApplication (6-12)
domain-reservation/src/main/java/com/wellmeet/domain/ReservationServiceApplication.java (1)
  • SpringBootApplication (7-14)
domain-restaurant/src/main/java/com/wellmeet/domain/RestaurantServiceApplication.java (1)
  • SpringBootApplication (6-12)
api-user/src/main/java/com/wellmeet/client/dto/request/RestaurantIdsRequest.java (1)
domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/dto/RestaurantIdsRequest.java (1)
  • RestaurantIdsRequest (6-10)
api-user/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java (3)
api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java (1)
  • Getter (7-26)
api-user/src/main/java/com/wellmeet/restaurant/dto/RestaurantResponse.java (1)
  • Getter (8-43)
api-user/src/main/java/com/wellmeet/restaurant/dto/NearbyRestaurantResponse.java (1)
  • Getter (7-26)
api-user/src/main/java/com/wellmeet/client/dto/request/MemberIdsRequest.java (4)
api-user/src/main/java/com/wellmeet/client/dto/MemberDTO.java (1)
  • Getter (8-23)
api-user/src/main/java/com/wellmeet/client/dto/request/CreateReservationDTO.java (1)
  • Getter (8-19)
api-user/src/main/java/com/wellmeet/client/dto/request/UpdateOperatingHoursDTO.java (2)
  • Getter (10-30)
  • Getter (18-29)
api-user/src/main/java/com/wellmeet/client/dto/request/UpdateRestaurantDTO.java (1)
  • Getter (8-19)
domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java (3)
domain-member/src/main/java/com/wellmeet/domain/MemberServiceApplication.java (1)
  • SpringBootApplication (6-12)
domain-reservation/src/main/java/com/wellmeet/domain/ReservationServiceApplication.java (1)
  • SpringBootApplication (7-14)
domain-restaurant/src/main/java/com/wellmeet/domain/RestaurantServiceApplication.java (1)
  • SpringBootApplication (6-12)
api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java (1)
api-user/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java (1)
  • Getter (8-21)
api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (2)
api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (1)
  • Getter (10-41)
api-user/src/main/java/com/wellmeet/reservation/dto/SummaryReservationResponse.java (1)
  • Getter (9-28)
api-user/src/main/java/com/wellmeet/client/dto/OwnerDTO.java (1)
api-user/src/main/java/com/wellmeet/client/dto/MemberDTO.java (1)
  • Getter (8-23)
api-user/src/main/java/com/wellmeet/reservation/dto/SummaryReservationResponse.java (3)
api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (1)
  • Getter (9-30)
api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (1)
  • Getter (10-41)
api-user/src/main/java/com/wellmeet/restaurant/dto/AvailableDateResponse.java (1)
  • Getter (9-26)
api-user/src/main/java/com/wellmeet/client/dto/request/CreateReservationDTO.java (2)
api-user/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java (1)
  • Getter (10-22)
api-user/src/main/java/com/wellmeet/client/dto/ReservationDTO.java (1)
  • Getter (9-24)
api-user/src/main/java/com/wellmeet/client/dto/request/UpdateOperatingHoursDTO.java (2)
api-user/src/main/java/com/wellmeet/client/dto/BusinessHourDTO.java (1)
  • Getter (9-22)
api-owner/src/main/java/com/wellmeet/client/dto/request/UpdateOperatingHoursDTO.java (2)
  • Getter (18-29)
  • Getter (10-30)
api-user/src/main/java/com/wellmeet/client/dto/MemberDTO.java (2)
api-user/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java (1)
  • Getter (10-22)
api-user/src/main/java/com/wellmeet/client/dto/OwnerDTO.java (1)
  • Getter (8-19)
api-user/src/main/java/com/wellmeet/client/dto/ReservationDTO.java (3)
api-user/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java (1)
  • Getter (10-22)
api-user/src/main/java/com/wellmeet/client/dto/request/CreateReservationDTO.java (1)
  • Getter (8-19)
api-owner/src/main/java/com/wellmeet/client/dto/ReservationDTO.java (1)
  • Getter (9-24)
api-user/src/test/java/com/wellmeet/reservation/ReservationServiceTest.java (3)
api-user/src/test/java/com/wellmeet/global/event/listener/ReservationEventListenerTest.java (4)
  • Nested (32-50)
  • Nested (52-70)
  • Nested (72-90)
  • ExtendWith (23-105)
api-user/src/test/java/com/wellmeet/global/event/EventPublishServiceTest.java (4)
  • Nested (27-42)
  • Nested (44-59)
  • Nested (61-76)
  • ExtendWith (18-91)
api-owner/src/test/java/com/wellmeet/reservation/ReservationServiceTest.java (1)
  • ExtendWith (28-161)
api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (4)
api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (1)
  • Getter (9-30)
api-user/src/main/java/com/wellmeet/reservation/dto/SummaryReservationResponse.java (1)
  • Getter (9-28)
api-user/src/main/java/com/wellmeet/restaurant/dto/AvailableDateResponse.java (1)
  • Getter (9-26)
api-owner/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (2)
  • Getter (13-67)
  • Getter (49-66)
api-user/src/main/java/com/wellmeet/client/dto/BusinessHourDTO.java (2)
api-user/src/main/java/com/wellmeet/client/dto/request/UpdateOperatingHoursDTO.java (2)
  • Getter (10-30)
  • Getter (18-29)
api-owner/src/main/java/com/wellmeet/client/dto/BusinessHourDTO.java (1)
  • Getter (9-22)
api-user/src/main/java/com/wellmeet/client/dto/request/IncreaseCapacityRequest.java (1)
domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/dto/IncreaseCapacityRequest.java (1)
  • IncreaseCapacityRequest (5-12)
api-user/src/main/java/com/wellmeet/config/FeignConfig.java (1)
api-owner/src/main/java/com/wellmeet/config/FeignConfig.java (1)
  • Configuration (11-41)
domain-reservation/src/main/java/com/wellmeet/domain/ReservationServiceApplication.java (3)
domain-member/src/main/java/com/wellmeet/domain/MemberServiceApplication.java (1)
  • SpringBootApplication (6-12)
domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java (1)
  • SpringBootApplication (6-12)
domain-restaurant/src/main/java/com/wellmeet/domain/RestaurantServiceApplication.java (1)
  • SpringBootApplication (6-12)
api-user/src/test/java/com/wellmeet/global/controller/HealthCheckControllerTest.java (2)
api-user/src/test/java/com/wellmeet/favorite/FavoriteControllerTest.java (4)
  • Nested (30-55)
  • Nested (57-79)
  • Nested (81-96)
  • WebMvcTest (21-107)
api-owner/src/test/java/com/wellmeet/global/controller/HealthCheckControllerTest.java (1)
  • HealthCheckControllerTest (8-20)
api-user/src/main/java/com/wellmeet/favorite/FavoriteService.java (3)
api-user/src/main/java/com/wellmeet/restaurant/RestaurantService.java (1)
  • Service (20-83)
api-user/src/main/java/com/wellmeet/reservation/ReservationService.java (1)
  • Service (32-233)
domain-member/src/main/java/com/wellmeet/domain/member/FavoriteRestaurantDomainService.java (1)
  • Service (11-37)
api-user/src/main/java/com/wellmeet/client/ReservationClient.java (1)
api-owner/src/main/java/com/wellmeet/client/ReservationClient.java (1)
  • FeignClient (10-21)
api-user/src/test/java/com/wellmeet/restaurant/RestaurantControllerTest.java (2)
api-user/src/test/java/com/wellmeet/restaurant/RestaurantServiceTest.java (3)
  • Nested (42-99)
  • Nested (101-169)
  • Nested (171-213)
api-user/src/test/java/com/wellmeet/restaurant/DomainRestaurantControllerTest.java (1)
  • DomainRestaurantControllerTest (18-88)
api-user/src/test/java/com/wellmeet/favorite/FavoriteControllerTest.java (3)
api-user/src/test/java/com/wellmeet/favorite/FavoriteServiceTest.java (3)
  • Nested (34-76)
  • Nested (78-100)
  • Nested (102-114)
api-user/src/test/java/com/wellmeet/reservation/ReservationControllerTest.java (6)
  • Nested (42-103)
  • Nested (105-140)
  • Nested (142-172)
  • Nested (174-209)
  • Nested (211-226)
  • WebMvcTest (30-227)
api-user/src/test/java/com/wellmeet/global/controller/HealthCheckControllerTest.java (1)
  • WebMvcTest (12-27)
api-user/src/test/java/com/wellmeet/reservation/ReservationControllerTest.java (3)
api-user/src/test/java/com/wellmeet/favorite/FavoriteControllerTest.java (4)
  • Nested (30-55)
  • Nested (57-79)
  • Nested (81-96)
  • WebMvcTest (21-107)
api-user/src/test/java/com/wellmeet/reservation/ReservationServiceTest.java (4)
  • Nested (61-117)
  • Nested (119-165)
  • Nested (167-209)
  • Nested (211-255)
api-user/src/test/java/com/wellmeet/global/controller/HealthCheckControllerTest.java (1)
  • WebMvcTest (12-27)
🪛 actionlint (1.7.8)
.github/workflows/Dev_Discovery_CD.yml

62-62: label "dev-discovery-server" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Member_CD.yml

63-63: label "dev-domain-member" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Restaurant_CD.yml

63-63: label "dev-domain-restaurant" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_API_User_CD.yml

69-69: label "dev-api-user" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_API_Owner_CD.yml

69-69: label "dev-api-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Reservation_CD.yml

63-63: label "dev-domain-reservation" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Owner_CD.yml

63-63: label "dev-domain-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-push
🔇 Additional comments (73)
domain-reservation/build.gradle (1)

1-4: Spring Boot 플러그인 추가가 적절합니다.

org.springframework.boot 플러그인 추가는 필요하고 올바른 위치에 배치되었습니다. 파일에 이미 선언된 Spring Boot 의존성(lines 12-16)들을 활용하기 위해 필수적인 플러그인입니다. 플러그인은 버전 관리, Fat JAR 생성, 그 외 Spring Boot 빌드 기능을 담당합니다.

domain-member/build.gradle (1)

2-2: Spring Boot 플러그인 추가 승인

org.springframework.boot 플러그인 추가가 적절합니다. 파일에 이미 Spring Boot starter 의존성들(data-jpa, validation, web, actuator)이 선언되어 있으므로, 해당 플러그인을 통해 Spring Boot 규칙과 기능(Fat JAR 생성, 리패키징 등)을 활용할 수 있습니다.

domain-owner/build.gradle (1)

2-2: Spring Boot 플러그인 추가 승인

org.springframework.boot 플러그인 추가가 적절합니다. domain-member 모듈과의 일관성 있는 변경이며, 파일의 기존 Spring Boot starter 의존성들과 함께 잘 통합됩니다.

domain-member/src/main/java/com/wellmeet/domain/MemberServiceApplication.java (1)

6-12: 원본 리뷰 코멘트는 부정확합니다. 수정이 불필요합니다.

검증 결과, domain-member 서비스는 서비스 디스커버리에 올바르게 구성되어 있습니다:

  • spring-cloud-starter-netflix-eureka-client 의존성 포함 ✓
  • application-local.ymlapplication-dev.yml에서 Eureka 등록 설정 완료 (register-with-eureka: true) ✓
  • Eureka Server URL 정상 구성 ✓

프로젝트의 마이그레이션 문서에 명시된 대로, Spring Cloud 2020.0.0 이상에서는 @EnableDiscoveryClient 어노테이션이 불필요하며 application.yml의 설정만으로 자동 등록됩니다. ReservationServiceApplication@EnableDiscoveryClient는 중복된 어노테이션입니다.

현재 구현은 최신 Spring Cloud 표준을 따르고 있으므로 변경이 필요 없습니다.

Likely an incorrect or invalid review comment.

domain-member/Dockerfile (1)

8-8: ---

eclipse-temurin:21-jre-jammy 이미지 변경 승인됨

검증 결과:

  • ✓ eclipse-temurin:21-jre-jammy는 활발하게 유지보수 중 (2025년 릴리스, 최근 Docker 업데이트)
  • ✓ 현재 이미지 크기: 285.44 MB (Nov 4, 2025 기준)
  • ✓ JDK에서 JRE로의 변경은 런타임 전용 컨테이너에 최적 (빌드 도구 제거, 보안 개선, 이미지 크기 감소)
  • ✓ slim에서 jammy로의 변경은 OS 라이브러리 지원 개선으로 바람직함

배포 환경에서의 런타임 호환성은 배포 테스트를 통해 표준적으로 검증되므로 승인 가능한 상태입니다.

domain-reservation/src/main/resources/application-test.yml (2)

24-25: 서버 포트 설정 검증 요청.

포트 8085가 선택된 근거를 확인해 주세요. 다른 도메인 서비스들과의 포트 충돌이 없는지, 그리고 로컬 테스트 환경에서 일관된 포트 정책을 따르고 있는지 확인이 필요합니다.


21-22: Flyway 비활성화 설정이 명확합니다.

테스트 환경에서 Flyway를 비활성화하고 create-drop을 사용하는 것은 일관된 설정입니다. 이는 마이그레이션 관리와 테스트 상태 격리를 적절히 구분하는 좋은 실천입니다.

domain-owner/src/test/java/com/wellmeet/domain/owner/BaseRepositoryTest.java (1)

15-15: LGTM! 일관된 프로필 변경입니다.

domain-member와 동일하게 테스트 프로필이 "test"로 변경되어 도메인 모듈 간 일관성이 유지됩니다.

domain-reservation/src/test/java/com/wellmeet/BaseRepositoryTest.java (1)

15-15: LGTM! 프로필 통일 작업 완료.

세 번째 도메인 모듈에서도 동일한 테스트 프로필 변경이 적용되어 전체 도메인 모듈의 테스트 설정이 통일되었습니다.

domain-member/src/test/java/com/wellmeet/domain/member/BaseRepositoryTest.java (1)

15-15: 테스트 프로필 변경이 올바르게 적용되었습니다.

검증 결과:

  • ✅ "test" 프로필이 application-test.yml을 통해 여러 모듈에서 제대로 설정됨
  • ✅ 모든 domain 모듈의 BaseRepositoryTest가 @ActiveProfiles("test")로 통일됨 (domain-member, domain-owner, domain-reservation, domain-restaurant)
  • ✅ "domain-test" 프로필에 대한 코드 참조가 완전히 제거됨 (CLAUDE.md 문서만 남아있으며, 이는 코드베이스 아님)

변경사항이 일관되고 안전합니다.

domain-reservation/Dockerfile (1)

14-15: 헬스체크 엔드포인트 검증 완료

검증 결과, domain-reservation 서비스의 헬스체크 설정이 모두 올바르게 구현되어 있습니다:

  • spring-boot-starter-actuator 의존성이 build.gradle에 포함되어 있음
  • ✓ 모든 application 설정 파일에서 /actuator/health 엔드포인트가 노출됨 (management.endpoints.web.exposure.include: health,info,metrics)
  • management.endpoint.health.show-details: always 설정으로 상세 정보 제공
  • ✓ 포트 8085 설정이 Dockerfile의 헬스체크 URL과 일치

타임아웃 설정: start-period=40s, interval=30s, timeout=3s는 데이터베이스 연결, Eureka 등록, Flyway 마이그레이션을 포함한 Spring Boot 서비스 시작 시간에 적절한 값입니다.

domain-restaurant/Dockerfile (1)

8-8: 리뷰 코멘트는 부정확한 기술적 가정을 기반으로 함

실제 검증 결과, eclipse-temurin:21-jre-jammy 이미지는 기본적으로 curl을 포함하고 있습니다. 따라서 라인 14-15의 HEALTHCHECK에서 curl 명령이 정상적으로 작동하며, 추가 설치나 수정이 필요하지 않습니다.

베이스 이미지 변경은 적절하게 구현되었습니다:

  • OpenJDK에서 Eclipse Temurin으로의 마이그레이션 ✓
  • JDK에서 JRE로의 변경 ✓
  • HEALTHCHECK 의존성 문제 없음 ✓

Likely an incorrect or invalid review comment.

.github/workflows/Dev_API_Owner_CD.yml (1)

69-69: dev-api-owner 라벨은 유효함 — 리뷰 코멘트 철회

코드베이스를 검증한 결과, dev-api-owner 라벨은 다른 모든 워크플로우 파일에서 사용되는 self-hosted 러너 라벨 패턴과 일치합니다. Dev_API_User_CD.yml도 동일 구조에서 dev-api-user 라벨을 사용하고, Dev_Domain_*_CD.yml 파일들도 dev-domain-[module] 형식으로 일관되게 사용 중입니다. actionlint 설정 파일이 없어 경고가 발생할 수 있으나, 라벨 자체는 조직의 self-hosted runner 등록 정책에 따른 유효한 라벨입니다.

Likely an incorrect or invalid review comment.

.github/workflows/Dev_API_User_CD.yml (2)

7-14: 경로 필터 업데이트가 BFF 아키텍처와 잘 맞습니다.

domain-common, infra-redis, infra-kafka 모듈 추가는 BFF 패턴의 의존성을 올바르게 반영합니다.


69-69: 자체 호스팅 러너 라벨 구성 확인 필요

dev-api-user 러너가 올바르게 설정되어 있는지 확인하세요. actionlint 경고를 억제하려면 저장소 루트에 actionlint.yaml 파일을 추가하세요.

self-hosted-runner:
  labels:
    - dev-api-user
    - dev-domain-reservation
    - dev-domain-restaurant
api-user/src/main/java/com/wellmeet/client/dto/OwnerDTO.java (1)

8-19: LGTM!

DTO 구조가 올바르며 MemberDTO와 일관된 패턴을 따릅니다.

api-user/src/main/java/com/wellmeet/client/OwnerClient.java (1)

8-13: LGTM!

Feign 클라이언트가 올바르게 구성되었으며 다른 클라이언트들과 일관된 패턴을 따릅니다.

.github/workflows/Dev_Domain_Reservation_CD.yml (1)

1-79: 워크플로우 구조가 올바릅니다.

도메인 예약 모듈에 대한 CD 파이프라인이 다른 도메인 워크플로우와 일관된 패턴을 따릅니다. 자체 호스팅 러너 dev-domain-reservation이 인프라에 올바르게 구성되어 있는지 확인하세요.

.github/workflows/Dev_Domain_Restaurant_CD.yml (1)

1-79: 워크플로우 구조가 올바릅니다.

도메인 레스토랑 모듈에 대한 CD 파이프라인이 다른 도메인 워크플로우와 일관된 패턴을 따릅니다. 자체 호스팅 러너 dev-domain-restaurant이 인프라에 올바르게 구성되어 있는지 확인하세요.

api-user/src/main/java/com/wellmeet/client/dto/request/UpdateRestaurantDTO.java (1)

12-19: UpdateRestaurantDTO의 설계가 올바릅니다. 식당 ID는 경로 변수로 전달되고 있습니다.

검증 결과, 식당 ID는 HTTP 요청의 경로 변수(@PathVariable)로 전달되고 있습니다. RestaurantController에서 @PathVariable String restaurantId로 수신하고, 이를 RestaurantService의 updateRestaurant(String restaurantId, ...) 메서드에 전달한 후, RestaurantClient의 FeignClient에서 @PutMapping("/api/restaurants/{id}")로 경로 변수로 사용합니다. UpdateRestaurantDTO는 수정할 데이터만 포함하는 설계로, 이는 REST API 표준 패턴입니다.

Likely an incorrect or invalid review comment.

api-user/src/main/java/com/wellmeet/restaurant/dto/RepresentativeMenuResponse.java (1)

3-3: 도메인 엔티티에서 DTO로의 전환이 적절하게 구현되었습니다.

Menu 엔티티 대신 MenuDTO를 사용하는 방식은 BFF 아키텍처 패턴에 부합합니다. MenuDTO가 record 타입이므로 name(), price() 접근자가 자동으로 제공되어 코드가 정확히 작동합니다.

Also applies to: 14-16

.github/workflows/Dev_CI.yml (1)

22-73: 마이크로서비스 아키텍처에 맞게 MySQL 서비스를 적절히 분리했습니다.

각 도메인 서비스별로 독립적인 MySQL 인스턴스를 구성한 것은 마이크로서비스 아키텍처의 데이터베이스 분리 원칙에 부합합니다. 포트 매핑과 데이터베이스 이름이 명확하게 구분되어 있습니다.

api-owner/src/main/resources/application-dev.yml (1)

17-17: 시크릿 기반 설정으로의 전환이 적절합니다.

Eureka 서버 URL을 하드코딩된 값에서 시크릿 기반 설정(${secret.eureka.server-url})으로 변경한 것은 보안 모범 사례에 부합하며, 환경별 설정 관리를 개선합니다.

domain-owner/src/main/resources/application-local.yml (1)

20-21: 설정 키 재정렬이 적절합니다.

server.port 블록을 spring 블록 이후로 이동한 것은 설정 파일의 일관성을 개선하는 변경입니다. 포트 값(8084)은 동일하게 유지되어 동작에는 영향이 없습니다.

api-user/src/main/java/com/wellmeet/restaurant/dto/RepresentativeReviewResponse.java (1)

14-17: DTO 전환이 올바르게 구현되었습니다.

도메인 엔티티 대신 ReviewDTO를 사용하도록 변경이 잘 되었으며, BFF 아키텍처로의 전환 목표에 부합합니다.

api-user/src/main/java/com/wellmeet/reservation/dto/ReservationStatus.java (1)

1-8: 예약 상태 enum 정의가 명확합니다.

표준적인 예약 상태 값들이 잘 정의되어 있으며, BFF 레이어에서의 상태 관리에 적합합니다.

api-user/src/test/java/com/wellmeet/global/controller/HealthCheckControllerTest.java (1)

12-25: MockMvc 기반 테스트로 올바르게 마이그레이션되었습니다.

BaseControllerTest 대신 @WebMvcTestMockMvc를 사용하는 표준 Spring MVC 테스트 방식으로 전환되었으며, PR의 다른 컨트롤러 테스트들과 일관성 있는 패턴입니다.

api-user/src/main/java/com/wellmeet/ApiUserApplication.java (1)

5-10: Feign 클라이언트 활성화가 올바르게 구성되었습니다.

BFF 아키텍처로의 전환을 위한 @EnableFeignClients 어노테이션 추가가 적절하며, 도메인 서비스와의 통신을 위한 Feign 클라이언트 사용 준비가 완료되었습니다.

api-user/src/main/resources/application-test.yml (1)

1-34: BFF 아키텍처에 맞는 테스트 구성으로 올바르게 업데이트되었습니다.

주요 변경사항이 BFF 패턴에 부합합니다:

  • DataSource 및 JPA 자동 구성 제외로 직접 DB 접근 제거
  • Feign 클라이언트 설정 추가로 도메인 서비스 호출 지원
  • Eureka 비활성화로 테스트 환경 단순화
api-user/src/main/java/com/wellmeet/config/FeignConfig.java (1)

19-26: 타임아웃 설정이 적절하게 구성되었습니다.

5초의 연결 및 읽기 타임아웃은 서비스 간 통신에 합리적인 값입니다. followRedirects 활성화도 적절합니다.

api-user/src/main/java/com/wellmeet/global/event/event/ReservationCreatedEvent.java (1)

21-32: 검증 완료: 상태 값 일관성 확인됨

ReservationDTO.getStatus()가 반환하는 문자열 값이 ReservationStatus enum의 이름(PENDING, CONFIRMED, CANCELED)과 정확히 일치합니다. 다른 응답 DTO들(ReservationResponse, SummaryReservationResponse, CreateReservationResponse)에서 ReservationStatus.valueOf(reservation.getStatus())로 성공적으로 변환되고 있으며, ReservationCreatedEvent가 상태를 문자열로 저장하는 것은 이벤트 도메인 모델의 일반적인 설계 패턴입니다.

api-user/src/main/java/com/wellmeet/restaurant/dto/NearbyRestaurantResponse.java (1)

3-3: 도메인 엔티티에서 DTO로의 전환이 적절합니다.

도메인 엔티티 Restaurant 대신 RestaurantDTO를 사용하도록 변경하여 BFF 패턴에 맞게 계층 분리가 잘 이루어졌습니다. 생성자 로직도 동일하게 유지되어 기존 동작에 영향을 주지 않습니다.

Also applies to: 18-25

domain-reservation/src/main/java/com/wellmeet/domain/ReservationServiceApplication.java (1)

7-13: Spring Boot 애플리케이션 설정이 올바릅니다.

@SpringBootApplication@EnableDiscoveryClient 어노테이션을 추가하여 마이크로서비스 아키텍처에 맞게 Eureka 서비스 등록이 활성화되었습니다. 다른 도메인 서비스들과 일관된 구조를 유지하고 있습니다.

api-user/src/main/java/com/wellmeet/client/dto/MenuDTO.java (1)

3-10: 간결하고 적절한 DTO 설계입니다.

Java record를 사용하여 불변 DTO를 구현했으며, 필드 구성이 명확합니다. price 필드에 primitive int 타입을 사용한 것은 원화 표시에 적합한 선택입니다. restaurantId를 포함하여 BFF 패턴에서 메뉴-레스토랑 관계를 잘 표현하고 있습니다.

domain-owner/src/main/resources/application-test.yml (1)

1-41: 테스트 환경 설정이 적절합니다.

테스트 환경에 맞는 합리적인 구성입니다:

  • MySQL 포트 3308 사용으로 기본 포트와 격리
  • JPA create-drop으로 테스트 간 데이터 독립성 보장
  • Eureka 클라이언트 비활성화로 외부 의존성 제거
  • 디버그 로깅과 SQL 출력으로 테스트 디버깅 용이
api-user/src/main/resources/dev-secret.yml (1)

1-19: dev-secret.yml 파일들이 플레이스홀더 값만 포함하고 있어 보안 위험은 없습니다.

검증 결과:

  • 플레이스홀더 확인: 모든 dev-secret.yml 파일(api-owner, api-user, domain-member, domain-owner, domain-reservation, domain-restaurant)에서 [EC2-PRIVATE-IP], [CORS-ORIGIN], [REDIS-HOST], [RDS-PASSWORD] 등 플레이스홀더 값만 확인됨. 실제 민감 정보는 커밋되지 않음.
  • .gitignore 상태: dev-secret.yml 또는 secret 관련 패턴이 .gitignore에 등록되지 않아 파일들이 git에 추적 중. 다만 파일이 템플릿 역할을 하므로 의도적 설계일 수 있음.
  • 배포 프로세스: 플레이스홀더 치환 프로세스의 문서화 여부는 스크립트로 확인 불가능하므로 수동 검증 필요.

권장 사항:

  1. .gitignore**/dev-secret.yml 또는 **/*-secret.yml 패턴 추가 고려 (템플릿이 아닌 실제 설정파일 보호)
  2. 배포 시 플레이스홀더 치환 프로세스가 README 또는 DEPLOYMENT 문서에 명확히 기록되어 있는지 개발팀과 확인

Kafka producer 설정은 적절합니다 (idempotent producer, 재시도, 압축 등).

api-user/src/main/resources/application-dev.yml (1)

18-24: Feign 타임아웃 설정 검증 권장

기본 타임아웃(연결 5초, 읽기 5초)이 설정되었습니다. 실제 downstream 서비스들의 평균 응답 시간을 기준으로 이 값들이 적절한지 검증하세요. 특히 데이터베이스 집약적인 작업이나 외부 API 호출이 포함된 엔드포인트는 더 긴 타임아웃이 필요할 수 있습니다.

domain-member/src/main/resources/application-test.yml (1)

1-41: 테스트 환경 설정 적절함

테스트 프로파일 설정이 잘 구성되었습니다. ddl-auto: create-drop, Eureka 비활성화, 상세 로깅 등 테스트 환경에 적합한 설정들입니다.

api-user/src/main/resources/application-local.yml (1)

1-26: 로컬 환경 설정 적절함

로컬 개발 환경 설정이 잘 구성되었습니다. Feign 타임아웃 설정은 application-dev.yml과 동일하므로, 동일한 검증이 필요합니다.

domain-reservation/src/main/resources/application-local.yml (1)

31-42: Eureka 클라이언트 명시적 활성화 적절함

eureka.client.enabled: true를 명시적으로 설정하여 서비스 디스커버리 활성화 의도가 명확해졌습니다. 인스턴스 ID 및 갱신 간격 설정도 적절합니다.

api-user/build.gradle (1)

5-21: BFF 전환을 위한 의존성 변경 적절함

domain 모듈 직접 의존성을 제거하고 Spring Cloud OpenFeign 및 Eureka 클라이언트를 추가한 것은 BFF 패턴 전환에 적합합니다. Feign을 통한 서비스 간 통신으로 결합도가 낮아졌습니다. 기존 기능이 Feign 클라이언트를 통해 정상적으로 동작하는지 통합 테스트로 검증하세요.

api-user/src/test/java/com/wellmeet/global/event/EventPublishServiceTest.java (2)

5-76: 도메인 엔티티에서 DTO로의 전환이 적절합니다.

테스트가 ReservationDTO를 사용하도록 올바르게 업데이트되었으며, 이벤트 발행 검증 로직이 정확합니다.


78-90: 헬퍼 메서드 구현이 적절합니다.

빌더 패턴을 사용하여 테스트용 ReservationDTO를 생성하는 방식이 명확하고 유지보수하기 좋습니다.

api-user/src/main/java/com/wellmeet/restaurant/dto/RestaurantResponse.java (1)

3-42: DTO 기반 응답으로의 전환이 올바릅니다.

RestaurantDTO를 사용하도록 변경되었으며, 필드 매핑이 정확하게 유지되고 있습니다. BFF 아키텍처 전환에 부합합니다.

api-user/src/main/java/com/wellmeet/client/dto/MemberDTO.java (1)

1-23: DTO 구조가 일관성 있고 적절합니다.

다른 클라이언트 DTO들(OwnerDTO, AvailableDateDTO)과 동일한 패턴을 따르고 있으며, Lombok을 통한 보일러플레이트 제거가 적절합니다.

domain-owner/src/main/resources/application-dev.yml (1)

1-50: 개발 환경 설정이 적절합니다.

  • JPA의 ddl-auto: validateopen-in-view: false 설정이 모범 사례를 따릅니다.
  • 민감한 정보를 dev-secret.yml로 외부화한 것이 보안상 적절합니다.
  • Eureka 클라이언트 설정과 헬스 체크 엔드포인트 구성이 올바릅니다.
api-user/src/main/java/com/wellmeet/global/event/event/ReservationUpdatedEvent.java (1)

3-32: DTO 기반 이벤트로의 전환이 정확합니다.

ReservationDTO를 사용하도록 업데이트되었으며, 상태 필드 처리가 DTO의 String 타입에 맞게 조정되었습니다. createdAt 초기화가 추가되어 더 완전한 이벤트 데이터를 제공합니다.

domain-member/src/main/resources/application-dev.yml (1)

1-50: LGTM!

dev 환경 설정이 적절하게 구성되어 있습니다. Secret 관리, Eureka 연동, JPA 설정 모두 올바르게 설정되었습니다.

api-user/src/main/java/com/wellmeet/client/MemberClient.java (1)

12-19: LGTM!

Feign 클라이언트가 올바르게 구현되었습니다. contextId를 명시하여 빈 이름 충돌을 방지하고 있으며, 엔드포인트 매핑도 적절합니다.

domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java (1)

6-11: @EnableDiscoveryClient 어노테이션 확인 필요

ReservationServiceApplication은 @EnableDiscoveryClient를 사용하고 있지만, 이 서비스에는 없습니다. Eureka 서비스 디스커버리를 사용할 예정이라면 일관성을 위해 추가하는 것을 고려해보시기 바랍니다. 최신 Spring Cloud 버전에서는 선택사항이지만, 명시적으로 표시하는 것이 코드 의도를 명확히 합니다.

domain-owner 서비스가 Eureka 클라이언트로 등록되어야 하는지 확인하고, 필요하다면 다음과 같이 추가하세요:

 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

 @SpringBootApplication
+@EnableDiscoveryClient
 public class OwnerServiceApplication {
api-user/src/test/java/com/wellmeet/favorite/FavoriteControllerTest.java (2)

21-28: LGTM!

@WebMvcTest를 사용한 테스트 리팩토링이 올바르게 수행되었습니다. MockMvc와 @MockitoBean을 적절히 활용하여 컨트롤러 레이어만 격리하여 테스트하고 있습니다.


98-106: 테스트 헬퍼 메서드 잘 구현됨

Builder 패턴을 사용한 테스트 데이터 생성 헬퍼 메서드가 깔끔하게 구현되어 테스트 코드의 가독성을 높이고 있습니다.

domain-reservation/src/main/resources/application-dev.yml (1)

1-69: LGTM!

domain-reservation 서비스의 dev 환경 설정이 상세하고 적절하게 구성되어 있습니다. Flyway 마이그레이션, Prometheus 메트릭, graceful shutdown 등 프로덕션 준비를 위한 설정들이 잘 포함되어 있습니다.

api-user/src/test/java/com/wellmeet/restaurant/RestaurantControllerTest.java (3)

37-70: LGTM!

주변 레스토랑 조회 테스트가 잘 작성되었습니다. 서비스 레이어를 적절히 모킹하고, RestAssured를 사용하여 HTTP 응답을 검증하고 있습니다.


77-126: LGTM!

레스토랑 상세 조회 테스트가 적절하게 구현되었습니다. DTO 기반 테스트 데이터 구성과 응답 검증이 명확합니다.


133-168: LGTM!

예약 가능 시간 조회 테스트가 올바르게 작성되었습니다. DTO 빌더를 활용하여 테스트 데이터를 생성하고 응답을 검증하고 있습니다.

api-user/src/test/java/com/wellmeet/restaurant/RestaurantServiceTest.java (4)

30-40: LGTM!

도메인 서비스에서 Feign 클라이언트로의 전환이 올바르게 이루어졌습니다. 테스트 설정이 BFF 아키텍처에 맞게 적절히 구성되었습니다.


45-85: LGTM!

주변 식당 조회 테스트가 DTO 기반으로 잘 마이그레이션되었습니다. 클라이언트 모킹과 응답 검증이 적절합니다.


104-138: LGTM!

식당 상세 정보 조회 테스트가 클라이언트 기반 아키텍처에 맞게 올바르게 업데이트되었습니다.


174-200: LGTM!

예약 가능 날짜 조회 테스트가 AvailableDateClient를 사용하도록 적절히 변경되었습니다.

api-user/src/test/java/com/wellmeet/global/event/listener/ReservationEventListenerTest.java (2)

92-104: LGTM!

ReservationDTO 생성 헬퍼 메서드가 테스트 가독성을 향상시킵니다. 도메인 엔티티 대신 DTO를 사용하여 테스트가 더 간결해졌습니다.


36-49: LGTM!

예약 생성 이벤트 리스너 테스트가 DTO 기반으로 올바르게 전환되었습니다. Kafka 메시지 발송 검증이 적절합니다.

api-user/src/main/java/com/wellmeet/favorite/FavoriteService.java (3)

20-21: LGTM!

도메인 서비스에서 Feign 클라이언트로의 전환이 BFF 패턴에 맞게 올바르게 구현되었습니다.


23-44: 트랜잭션 경계 손실에 유의하세요.

이전에는 @Transactional로 보호되던 메서드가 이제 여러 원격 클라이언트 호출로 분리되었습니다. 부분 실패 시 데이터 일관성 문제가 발생할 수 있으므로, 필요하다면 보상 트랜잭션이나 재시도 로직을 고려해야 합니다.

BFF 아키텍처에서는 이것이 예상되는 동작이지만, 특히 addFavoriteRestaurantremoveFavoriteRestaurant 메서드에서 실패 시나리오를 문서화하거나 처리하는 것을 권장합니다.


46-50: LGTM!

Rating null 처리가 적절하며, RestaurantDTO 기반으로 응답을 구성하는 로직이 명확합니다.

api-user/src/test/java/com/wellmeet/reservation/ReservationControllerTest.java (7)

30-40: LGTM!

WebMvcTest 기반 컨트롤러 테스트 설정이 올바릅니다. MockMvc를 사용한 격리된 컨트롤러 테스트 패턴이 적절히 구현되었습니다.


46-74: LGTM!

예약 생성 테스트가 MockMvc를 사용하여 잘 작성되었습니다. 요청/응답 검증과 서비스 호출 확인이 적절합니다.


77-102: LGTM!

입력 검증 테스트가 포함되어 있어 좋습니다. null 값에 대한 적절한 400 Bad Request 응답을 검증하고 있습니다.


109-139: LGTM!

예약 목록 조회 테스트가 적절히 구현되었습니다. 배열 응답과 jsonPath를 사용한 검증이 명확합니다.


146-171: LGTM!

예약 상세 조회 테스트가 올바르게 작성되었습니다. PathVariable과 QueryParam 처리가 적절합니다.


178-208: LGTM!

예약 업데이트 테스트가 잘 구현되었습니다. PUT 요청과 요청 본문 직렬화가 올바르게 처리되고 있습니다.


215-225: LGTM!

예약 취소 테스트가 적절합니다. PATCH 메서드와 204 No Content 응답 검증이 올바릅니다.

api-user/src/main/java/com/wellmeet/client/ReservationClient.java (1)

15-15: 경로 설정의 실제 상태를 확인했습니다.

api-owner의 ReservationClient는 /api/reservations (복수형)을 사용하지 않습니다. 실제로 path 속성이 정의되지 않았습니다:

  • api-user: @FeignClient(name = "domain-reservation-service", path = "/api/reservation")
  • api-owner: @FeignClient(name = "domain-reservation-service") (path 없음)

원본 리뷰 의견의 전제가 정확하지 않습니다. api-owner는 명시적인 경로를 지정하지 않으므로, 실제 일관성 문제는 다를 수 있습니다.

Likely an incorrect or invalid review comment.

Comment on lines 10 to 22
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AvailableDateDTO {

private Long id;
private LocalDate date;
private LocalTime time;
private int maxCapacity;
private boolean isAvailable;
private String restaurantId;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

DTO 중복 확인 필요

구현은 올바르지만, api-owner/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java에 동일한 구조의 DTO가 존재합니다. 모듈 간 분리를 위한 의도적인 중복인지, 아니면 공통 모듈로 추출해야 하는지 확인이 필요합니다.

다음 스크립트로 두 DTO의 일관성을 확인하세요:


🏁 Script executed:

#!/bin/bash
# api-user와 api-owner 모듈의 AvailableDateDTO 비교
echo "=== api-user AvailableDateDTO ==="
cat api-user/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java

echo ""
echo "=== api-owner AvailableDateDTO ==="
cat api-owner/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java

Length of output: 1001



🏁 Script executed:

#!/bin/bash
# Find all references to AvailableDateDTO in both modules
echo "=== References to AvailableDateDTO in api-user ==="
rg -n "AvailableDateDTO" api-user/ --type java

echo ""
echo "=== References to AvailableDateDTO in api-owner ==="
rg -n "AvailableDateDTO" api-owner/ --type java

echo ""
echo "=== Module structure check ==="
ls -la api-*/pom.xml 2>/dev/null | head -20

echo ""
echo "=== Check for common module ==="
find . -maxdepth 2 -name "pom.xml" -o -name "common*" -type d 2>/dev/null | head -20

Length of output: 7863


🏁 Script executed:

#!/bin/bash
# Alternative: check root structure
echo "=== Root directory structure ==="
ls -d */ 2>/dev/null | grep -E "(common|shared|client|api)" | head -20

Length of output: 68



🏁 Script executed:

#!/bin/bash
# Check domain-common module structure
echo "=== domain-common module structure ==="
find domain-common -type f -name "*.java" | grep -i "dto\|client" | head -20

echo ""
echo "=== domain-common pom.xml ==="
cat domain-common/pom.xml 2>/dev/null | head -30

echo ""
echo "=== api-user dependencies ==="
grep -A 20 "<dependencies>" api-user/pom.xml 2>/dev/null | head -30

echo ""
echo "=== api-owner dependencies ==="
grep -A 20 "<dependencies>" api-owner/pom.xml 2>/dev/null | head -30

Length of output: 132



🏁 Script executed:

#!/bin/bash
# Direct exploration of module structures
echo "=== domain-common contents ==="
ls -la domain-common/ 2>/dev/null

echo ""
echo "=== Check for gradle or maven ==="
ls -la | grep -E "(pom|gradle)" | head -10

echo ""
echo "=== api-user pom.xml content (full) ==="
head -60 api-user/pom.xml 2>/dev/null

echo ""
echo "=== Check modules interdependency ==="
grep -r "domain-common" . --include="pom.xml" 2>/dev/null | head -10

Length of output: 682



🏁 Script executed:

#!/bin/bash
# Gradle project analysis
echo "=== settings.gradle ==="
cat settings.gradle

echo ""
echo "=== domain-common/build.gradle ==="
cat domain-common/build.gradle

echo ""
echo "=== domain-common/src structure ==="
find domain-common/src -type f -name "*.java" 2>/dev/null | head -20

echo ""
echo "=== api-user/build.gradle (dependencies section) ==="
grep -A 30 "dependencies" api-user/build.gradle 2>/dev/null | head -40

echo ""
echo "=== api-owner/build.gradle (dependencies section) ==="
grep -A 30 "dependencies" api-owner/build.gradle 2>/dev/null | head -40

Length of output: 2193


AvailableDateDTO를 domain-common 모듈로 통합하고 양쪽 API 모듈의 의존성 수정 필요

확인 결과, 두 DTO는 바이트 단위로 동일하며 의도적인 모듈 분리가 아닙니다. 두 모듈 모두 같은 외부 API (RestaurantClient.getAvailableDate)의 응답을 나타내기 위해 사용되고 있습니다.

필수 조치:

  • AvailableDateDTO를 domain-common/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java로 이동
  • api-user/build.gradleapi-owner/build.gradleimplementation project(':domain-common') 의존성 추가 (현재 없음)
  • 각 모듈의 import 문을 도메인-공통 모듈을 가리키도록 유지 (패키지명 동일하게 유지 가능)
  • 중복 파일 제거

Comment on lines +5 to +6
public record RestaurantIdsRequest(List<String> restaurantIds) {
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

요청 DTO에 입력 검증이 누락되었습니다.

도메인 모듈의 RestaurantIdsRequest에는 @NotEmpty 검증 어노테이션이 있지만, 이 BFF 레이어의 버전에는 검증이 없습니다. 빈 리스트가 전달되면 불필요한 API 호출이나 예상치 못한 동작이 발생할 수 있습니다.

다음 diff를 적용하여 검증을 추가하세요:

 package com.wellmeet.client.dto.request;

 import java.util.List;
+import jakarta.validation.constraints.NotEmpty;

-public record RestaurantIdsRequest(List<String> restaurantIds) {
+public record RestaurantIdsRequest(
+        @NotEmpty
+        List<String> restaurantIds
+) {
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public record RestaurantIdsRequest(List<String> restaurantIds) {
}
package com.wellmeet.client.dto.request;
import java.util.List;
import jakarta.validation.constraints.NotEmpty;
public record RestaurantIdsRequest(
@NotEmpty
List<String> restaurantIds
) {
}
🤖 Prompt for AI Agents
In
api-user/src/main/java/com/wellmeet/client/dto/request/RestaurantIdsRequest.java
around lines 5-6, the record lacks validation so an empty restaurantIds list can
be submitted; add the appropriate validation annotation to the record component
(e.g., annotate the List<String> restaurantIds parameter with @NotEmpty) and
import the correct validation package used in the project
(jakarta.validation.constraints.NotEmpty or
javax.validation.constraints.NotEmpty), ensuring the class is eligible for
validation (no further code changes needed if validation is enabled globally).

Comment on lines 1 to 24
package com.wellmeet.client.dto;

import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReservationDTO {

private Long id;
private String status;
private String restaurantId;
private Long availableDateId;
private String memberId;
private int partySize;
private String specialRequest;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

DTO 구조는 적절하나 중복 및 타입 고려사항이 있습니다.

  1. api-owner 모듈에 동일한 ReservationDTO가 존재합니다. 공통 모듈로 추출하여 중복을 제거하는 것을 권장합니다.
  2. status 필드가 String 타입입니다. 타입 안정성을 위해 enum 사용을 고려하거나, 최소한 문서화를 추가하세요.

다음 스크립트로 DTO 중복을 확인하세요:


🏁 Script executed:

#!/bin/bash
# ReservationDTO 중복 확인
fd -t f "ReservationDTO.java" --exec echo "File: {}" \; --exec cat {} \;

Length of output: 2677


🏁 Script executed:

#!/bin/bash
# Search for any existing status-related enums
fd -t f ".*Status.*\.java" --exec echo "File: {}" \; --exec cat {} \;

Length of output: 829


🏁 Script executed:

#!/bin/bash
# Search for enum definitions that might be related to reservation status
rg -n "enum.*Status" --type java -A 5

Length of output: 2255


🏁 Script executed:

#!/bin/bash
# Check if there are any status-related constants or enums in the codebase
rg -n "status.*=.*\"" --type java -C 2 | head -50

Length of output: 0


DTO 구조 및 타입 안정성 개선 필요

검증 결과:

  1. DTO 중복 확인됨: ReservationDTOapi-ownerapi-user 모듈에 동일하게 존재합니다. 공통 모듈로 추출하여 중복을 제거하세요.

  2. status 필드 타입 개선: status 필드가 String 타입이지만, ReservationStatus enum이 이미 코드베이스에 존재합니다 (com.wellmeet.reservation.dto.ReservationStatus). 현재 DTO에서 이 enum을 사용하도록 수정하세요.

  3. 추가 주의사항: ReservationStatus enum 자체도 여러 모듈에서 중복 정의되어 있습니다. DTO 추출 시 enum 통합도 함께 고려하세요.

🤖 Prompt for AI Agents
In api-user/src/main/java/com/wellmeet/client/dto/ReservationDTO.java lines
1-24, the DTO is duplicated across modules and uses a String for status; extract
the DTO into a shared/common module (e.g., api-common or model-common), replace
the status String with the existing
com.wellmeet.reservation.dto.ReservationStatus enum (update package/imports and
builder/getter annotations accordingly), move or centralize the
ReservationStatus enum into the same common module to avoid cross-module
duplicates, and update all module dependencies and imports in api-user and
api-owner to reference the new shared DTO/enum.

Comment on lines 17 to 18
private double latitude;
private double longitude;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

좌표 필드의 null 처리를 검토하세요.

latitudelongitude 필드가 primitive double 타입으로 선언되어 있어 null 값을 허용하지 않습니다. 만약 레스토랑 데이터에서 좌표 정보가 누락될 수 있다면, Feign 클라이언트가 응답을 역직렬화할 때 기본값 0.0이 할당되어 데이터 무결성 문제가 발생할 수 있습니다.

좌표가 선택적 데이터라면 Double 래퍼 타입 사용을 고려하세요.

좌표 데이터의 필수 여부를 확인하기 위해 다음 스크립트를 실행하세요:


🏁 Script executed:

#!/bin/bash
# Description: 레스토랑 도메인 엔티티에서 좌표 필드의 nullable 여부 확인

# 레스토랑 엔티티 클래스에서 latitude/longitude 필드 정의 검색
rg -A 2 -B 2 "latitude|longitude" --type java -g '**/Restaurant.java'

Length of output: 4670


[/run_scripts]


🏁 Script executed:

#!/bin/bash
# Find validation constants for coordinate ranges
rg "MINIMUM_LATITUDE|MAXIMUM_LATITUDE|MINIMUM_LONGITUDE|MAXIMUM_LONGITUDE" --type java -A 1 -B 1

Length of output: 5228


🏁 Script executed:

#!/bin/bash
# Check for @NotNull, @Nullable, or other validation annotations on RestaurantDTO
rg -A 5 "class RestaurantDTO|private double latitude|private double longitude" --type java -g '**/RestaurantDTO.java'

Length of output: 1782


🏁 Script executed:

#!/bin/bash
# Check if there are Jackson deserialization configurations or null handling
rg "RestaurantDTO|@JsonProperty|DeserializationFeature" --type java -A 2 -B 2 -g '**Feign*' -g '**Config*'

Length of output: 0


[/run_scripts]


🏁 Script executed:

#!/bin/bash
# Search for @JsonProperty, @JsonField, or Jackson required configurations on RestaurantDTO
cat api-user/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java

Length of output: 427


🏁 Script executed:

#!/bin/bash
# Search for API endpoints using RestaurantDTO and any validation annotations
rg "@PostMapping|@GetMapping|@PutMapping|RequestBody" --type java -A 5 -B 5 | head -100

Length of output: 13356


🏁 Script executed:

#!/bin/bash
# Search for @Validated, @Valid, validation annotations on controller/service methods
rg "@Validated|@Valid|@NotNull.*RestaurantDTO|BindingResult" --type java -B 3 -A 3

Length of output: 15706


좌표 필드 null 처리 개선 필수

RestaurantDTO의 latitude, longitude가 primitive double로 선언되어 있어, Feign 클라이언트가 외부 API 응답에서 좌표 데이터를 받지 못할 경우 Jackson이 자동으로 0.0을 할당합니다. 문제는 0.0이 Restaurant 엔티티의 검증 범위(위도: [-90.0, 90.0], 경도: [-180.0, 180.0]) 내에 있어 검증을 통과한다는 점입니다. 결과적으로 누락된 데이터가 유효한 값으로 오인되어 데이터 무결성이 훼손됩니다.

개선안:

  • Double 래퍼 타입 사용으로 null 명시화
  • 좌표 필드에 @NotNull 검증 추가 (필수) 또는 선택적이면 적절한 기본값 처리
🤖 Prompt for AI Agents
In api-user/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java around
lines 17 to 18, the latitude and longitude are declared as primitive doubles so
missing values deserialize to 0.0 and can wrongly pass validation; change both
fields to the wrapper type Double and annotate them with @NotNull (or
document/handle as optional with a clear default) and add the necessary import
for javax.validation.constraints.NotNull so missing coordinates are represented
as null and properly validated.

Comment on lines +28 to +35
@Bean
public Retryer retryer() {
return new Retryer.Default(
100, // period (초기 대기 시간)
1000, // maxPeriod (최대 대기 시간)
3 // maxAttempts (최대 재시도 횟수)
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

재시도 정책 검토 필요: 비멱등 작업에 대한 고려

현재 재시도 설정이 모든 Feign 클라이언트에 전역으로 적용됩니다. POST, PUT, DELETE와 같은 비멱등 작업을 재시도하면 중복 요청이 발생할 수 있습니다.

다음을 고려하세요:

  • 멱등성이 보장되는 GET 요청에만 재시도 적용
  • 비멱등 작업에는 개별 클라이언트 설정에서 재시도 비활성화
  • 추후 서킷 브레이커 패턴(Resilience4j) 도입 검토

클라이언트별 재시도 비활성화 예시:

// 특정 Feign 클라이언트에서 재시도 비활성화
@FeignClient(name = "reservation-service", configuration = NoRetryConfig.class)
public interface ReservationClient {
    // ...
}

@Configuration
class NoRetryConfig {
    @Bean
    public Retryer retryer() {
        return Retryer.NEVER_RETRY;
    }
}
🤖 Prompt for AI Agents
In api-user/src/main/java/com/wellmeet/config/FeignConfig.java around lines
28-35, the global Retryer bean applies retries to all Feign calls (including
non-idempotent POST/PUT/DELETE) which can cause duplicate side-effects; remove
or stop exposing a global Retryer and instead apply retries only to idempotent
GET clients (keep a Retryer bean in a dedicated config used by GET-only clients)
and create a NoRetryConfig (bean returning Retryer.NEVER_RETRY) to assign to
clients that must not retry (e.g., reservation/payment clients) via
@FeignClient(configuration = NoRetryConfig.class); optionally plan for
Resilience4j/circuit-breaker integration for more advanced retry/fallback
control.

Comment on lines 1 to 7
secret:
eureka:
server-url: http://[EC2-PRIVATE-IP]:8761/eureka/
database:
url: jdbc:mysql://[RDS-ENDPOINT]:3307/wellmeet_member
username: root
password: [RDS-PASSWORD]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

데이터베이스 포트 확인 및 비밀 파일 버전 관리 제외 확인

두 가지 사항을 확인해주세요:

  1. MySQL 포트 3307: 표준 포트는 3306이며, 3307은 커스텀 설정입니다. 이것이 의도된 설정인지, 아니면 domain-reservation/dev-secret.yml의 3306과 일치해야 하는지 확인하십시오.

  2. 비밀 파일 버전 관리: dev-secret.yml 파일이 .gitignore에서 제외되어 있는지 확인하십시오.



🏁 Script executed:

#!/bin/bash
# Step 1: Check .gitignore for dev-secret.yml exclusion
echo "=== Checking .gitignore for secret file patterns ==="
if [ -f ".gitignore" ]; then
  cat .gitignore | grep -E "(secret|dev-secret)" || echo "No secret patterns found"
else
  echo ".gitignore not found"
fi

echo ""
echo "=== Checking domain-reservation dev-secret.yml port configuration ==="
# Step 2: Check domain-reservation's dev-secret.yml for database port
if [ -f "domain-reservation/src/main/resources/dev-secret.yml" ]; then
  grep -n "port\|[0-9]\{4\}" domain-reservation/src/main/resources/dev-secret.yml || echo "No port found"
else
  echo "domain-reservation dev-secret.yml not found"
fi

echo ""
echo "=== Checking for database configuration properties ==="
# Step 3: Search for any database port configuration in application properties
fd -e "properties" -e "yml" -e "yaml" --type f | xargs grep -l "3307\|3306" 2>/dev/null || echo "No matching port configurations found"

Length of output: 722


🏁 Script executed:

#!/bin/bash
# Step 4: Search for any documentation or comments about port configuration
echo "=== Checking for RDS or database configuration documentation ==="
fd -e "md" -e "txt" -e "README" --type f | xargs grep -i "rds\|port\|3307\|3306" 2>/dev/null | head -20 || echo "No documentation found"

Length of output: 1198


🏁 Script executed:

#!/bin/bash
# 1. .gitignore에서 secret 파일 패턴 확인
echo "=== .gitignore 검사 (secret 파일 제외 확인) ==="
if [ -f ".gitignore" ]; then
  cat .gitignore | grep -i "secret\|dev-secret" || echo "Secret 패턴 없음"
else
  echo ".gitignore 파일 없음"
fi

echo ""
echo "=== domain-reservation dev-secret.yml 포트 설정 확인 ==="
# 2. domain-reservation의 dev-secret.yml 포트 확인
if [ -f "domain-reservation/src/main/resources/dev-secret.yml" ]; then
  cat domain-reservation/src/main/resources/dev-secret.yml
else
  echo "domain-reservation dev-secret.yml 파일 없음"
fi

echo ""
echo "=== 데이터베이스 포트 관련 모든 설정 검색 ==="
# 3. 전체 포트 설정 검색
fd -e "properties" -e "yml" -e "yaml" --type f -x grep -l "3307\|3306" {} \; 2>/dev/null || true

Length of output: 741


🏁 Script executed:

#!/bin/bash
# 4. yml/yaml 파일에서 포트 설정 상세 확인
echo "=== yml/yaml 파일의 포트 설정 상세 ==="
fd -e "yml" -e "yaml" --type f -x grep -nP "(port|url):" {} + 2>/dev/null | grep -E "(3306|3307|url)" || true

Length of output: 3448


dev-secret.yml을 .gitignore에 추가하여 버전 관리 제외 필수

포트 3307 설정은 의도된 설정입니다 (CLAUDE.md 문서에 명시, 각 도메인이 고유 포트 사용). 하지만 더 중요한 보안 문제가 있습니다:

.gitignoredev-secret.yml 패턴이 없어 민감한 정보(RDS-PASSWORD, RDS-ENDPOINT)가 버전 관리에 노출될 위험이 있습니다. .gitignore**/dev-secret.yml 또는 **/dev-secret.*.yml 패턴을 추가하십시오.

🤖 Prompt for AI Agents
In domain-member/src/main/resources/dev-secret.yml lines 1-7: this file contains
sensitive RDS credentials and is not ignored; add a gitignore entry for patterns
like **/dev-secret.yml and **/dev-secret.*.yml to prevent future commits, then
remove the tracked file from the repository index (git rm --cached <path>) and
commit the change so it’s no longer versioned; finally move these secrets to a
safe store (env vars or a secrets manager) and rotate any exposed credentials.

Comment on lines 1 to 7
secret:
eureka:
server-url: http://[EC2-PRIVATE-IP]:8761/eureka/
database:
url: jdbc:mysql://[RDS-ENDPOINT]:3308/wellmeet_owner
username: root
password: [RDS-PASSWORD]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

보안 문제: 시크릿 파일이 리포지토리에 커밋되었습니다.

dev-secret.yml 파일은 민감한 설정 정보를 포함하므로 리포지토리에 커밋되어서는 안 됩니다. 플레이스홀더를 사용하더라도 실제 개발 환경에서 실제 값으로 대체될 수 있어 보안 위험이 있습니다.

추가 권장사항:

  • root 사용자 대신 제한된 권한을 가진 전용 데이터베이스 사용자를 사용하세요.
  • MySQL 표준 포트(3306) 대신 3308을 사용하는 이유를 문서화하세요.

다음 스크립트로 .gitignore 설정을 확인하세요:

#!/bin/bash
# dev-secret.yml 파일이 .gitignore에 포함되어 있는지 확인
rg -n 'dev-secret\.yml' .gitignore

# 실제로 커밋된 secret 파일들 찾기
fd -t f 'secret\.yml$' --no-ignore

.gitignore에 다음을 추가하세요:

+**/dev-secret.yml
+**/prod-secret.yml
+**/*-secret.yml
🤖 Prompt for AI Agents
In domain-owner/src/main/resources/dev-secret.yml lines 1-7, a secrets file
containing sensitive configuration was committed; remove the file from the
repository history and change the repo to stop tracking it: delete the committed
file from git (and from history if necessary), add dev-secret.yml to .gitignore,
and commit a non-sensitive template (e.g., dev-secret.example.yml) with
placeholders instead. Immediately rotate any credentials exposed (DB password,
any keys) and create a limited-permission DB user instead of root; document why
port 3308 is used if not standard. Finally, scan the repo for other committed
secret files, ensure CI/secrets managers are used for runtime config, and verify
the change by running the provided checks to confirm dev-secret.yml is ignored
and no secret files remain tracked.

Comment on lines +8 to 10
FROM eclipse-temurin:21-jre-jammy
WORKDIR /app

COPY --from=build /app/domain-reservation/build/libs/*.jar app.jar
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

보안: USER 선언 추가 필요

런타임 이미지에서 root 사용자로 컨테이너가 실행됩니다. 보안 모범 사례에 따라 비-root 사용자를 생성하고 사용해야 합니다.

다음 diff를 적용하여 전용 애플리케이션 사용자를 추가하십시오:

 # Stage 2: Runtime
 FROM eclipse-temurin:21-jre-jammy
 WORKDIR /app
 COPY --from=build /app/domain-reservation/build/libs/*.jar app.jar
 
+RUN groupadd -r appgroup && useradd -r -g appgroup appuser
+USER appuser
+
 EXPOSE 8085
🤖 Prompt for AI Agents
In domain-reservation/Dockerfile around lines 8 to 10, the image runs as root;
create a non-root app user, change ownership of /app to that user, and switch to
it with USER. Add steps: create group and user (e.g., groupadd/appuser and
useradd with no-login), chown -R /app to that user (or set --chown on COPY if
preferred), set HOME if needed, and add USER appuser (or UID/GID) before
container start so the JVM runs as the non-root account.

Comment on lines +8 to +9
username: root
password: password
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

데이터베이스 자격증명이 소스 코드에 하드코딩되어 있습니다.

테스트 설정이더라도 저장소에 평문의 데이터베이스 자격증명(root/password)이 노출되는 것은 보안 위험입니다. 다음 중 하나의 방식으로 개선을 권장합니다:

  1. 환경 변수를 통해 주입
  2. Spring Profiles를 활용한 분리 (예: application-test-local.yml - .gitignore에 포함)
  3. GitHub Secrets 또는 CI/CD 환경 변수 활용

예제 다이프:

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/wellmeet_reservation?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
-   username: root
-   password: password
+   username: ${DB_USERNAME:root}
+   password: ${DB_PASSWORD}

또는 .gitignore에 다음을 추가하고 로컬 설정 파일을 사용:

application-test-local.yml

프로젝트의 다른 테스트 설정 파일들과 자격증명 관리 방식의 일관성을 확인해 주세요.



---

🏁 Script executed:

```shell
#!/bin/bash
# 대상 파일 내용 확인
cat domain-reservation/src/main/resources/application-test.yml 2>/dev/null || echo "File not found"

Length of output: 822


🏁 Script executed:

#!/bin/bash
# 프로젝트의 다른 application-*.yml 파일 검색
find . -name "application-*.yml" -type f | head -20

Length of output: 1122


🏁 Script executed:

#!/bin/bash
# .gitignore 파일 확인
cat .gitignore 2>/dev/null || echo ".gitignore not found"

Length of output: 451


🏁 Script executed:

#!/bin/bash
# 프로젝트의 properties/yml 파일에서 자격증명 패턴 검색
rg -n "username|password" --type yaml --type properties -B2 -A2 2>/dev/null | head -50

Length of output: 0


🏁 Script executed:

#!/bin/bash
# domain-reservation 모듈의 src 구조 확인
find domain-reservation/src -type f \( -name "*.yml" -o -name "*.yaml" -o -name "*.properties" \) 2>/dev/null | sort

Length of output: 229



---

🏁 Script executed:

```shell
#!/bin/bash
# 다른 모듈의 test.yml 파일 샘플 확인 (api-user)
cat ./api-user/src/main/resources/application-test.yml 2>/dev/null | grep -A2 "datasource:" | head -10

Length of output: 0


🏁 Script executed:

#!/bin/bash
# dev-secret.yml 파일 구조 확인
cat ./domain-reservation/src/main/resources/dev-secret.yml 2>/dev/null || echo "File not found or empty"

Length of output: 243


🏁 Script executed:

#!/bin/bash
# application-dev.yml에서 자격증명 확인
cat ./domain-reservation/src/main/resources/application-dev.yml 2>/dev/null | grep -A5 "datasource:"

Length of output: 178


🏁 Script executed:

#!/bin/bash
# 프로젝트의 README 또는 설정 가이드 확인
find . -maxdepth 2 -name "README*" -o -name "*.md" | grep -E "(README|SETUP|CONFIG)" | head -10

Length of output: 11


테스트 설정 파일의 평문 데이터베이스 자격증명을 제거하세요.

저장소에 노출된 평문 자격증명(root/password)은 보안 위험입니다. 프로젝트가 dev-secret.yml로 민감 정보를 분리하는 패턴을 이미 사용 중이므로, 테스트 환경도 동일한 방식으로 일관성 있게 수정해야 합니다.

  1. application-test.yml에서 자격증명을 환경 변수 참조로 변경:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/wellmeet_reservation?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
-   username: root
-   password: password
+   username: ${secret.database.username}
+   password: ${secret.database.password}
  1. domain-reservation/src/main/resources/ 디렉터리에 test-secret.yml 생성:
secret:
  database:
    username: root
    password: password
  1. .gitignore에 추가하여 로컬 테스트 자격증명이 버전 컨트롤에 포함되지 않도록:
*-secret.yml
test-secret.yml

이렇게 수정하면 application-dev.yml과 동일한 패턴으로 보안이 개선되고 개발/테스트/운영 환경 간 일관성이 유지됩니다.

🤖 Prompt for AI Agents
In domain-reservation/src/main/resources/application-test.yml around lines 8 to
9, remove the hard-coded plaintext DB credentials and replace them with
environment variable references (e.g., reference SPRING_DATASOURCE_USERNAME and
SPRING_DATASOURCE_PASSWORD) so the file does not contain secrets; add a new file
domain-reservation/src/main/resources/test-secret.yml containing the test-only
secret values under a secret.database section, and add an entry to .gitignore to
exclude *-secret.yml and test-secret.yml so local test credentials are not
committed.

Comment on lines 1 to 7
secret:
eureka:
server-url: http://[EC2-PRIVATE-IP]:8761/eureka/
database:
url: jdbc:mysql://[RDS-ENDPOINT]:3306/wellmeet_reservation?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
username: root
password: [RDS-PASSWORD]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

비밀 설정 파일 버전 관리 제외 확인 필요

dev-secret.yml 파일에 플레이스홀더 사용으로 즉각적인 노출 위험은 적지만, 이러한 비밀 설정 파일은 반드시 .gitignore에서 제외되어야 합니다.

.gitignore**/*-secret.yml 또는 **/dev-secret.yml 패턴이 포함되어 있는지 확인하십시오.


🏁 Script executed:

#!/bin/bash
# .gitignore 파일 찾기 및 dev-secret.yml 관련 패턴 확인
find . -name ".gitignore" -type f | head -20

Length of output: 12


🏁 Script executed:

#!/bin/bash
# 루트 및 domain-reservation 디렉토리의 .gitignore 내용 확인
echo "=== Root .gitignore ==="
if [ -f ".gitignore" ]; then
  cat .gitignore | grep -i secret
else
  echo ".gitignore 파일 없음"
fi

echo -e "\n=== domain-reservation/.gitignore ==="
if [ -f "domain-reservation/.gitignore" ]; then
  cat domain-reservation/.gitignore | grep -i secret
else
  echo "domain-reservation/.gitignore 파일 없음"
fi

Length of output: 98


🏁 Script executed:

#!/bin/bash
# dev-secret.yml이 git에 추적되고 있는지 확인
echo "=== Git 추적 상태 ==="
git ls-files | grep -i "dev-secret.yml"

Length of output: 304


🏁 Script executed:

#!/bin/bash
# .gitignore의 전체 내용 확인 (민감한 파일 패턴 파악)
echo "=== Root .gitignore 전체 내용 ==="
if [ -f ".gitignore" ]; then
  cat .gitignore
fi

echo -e "\n=== domain-reservation/.gitignore 전체 내용 ==="
if [ -f "domain-reservation/.gitignore" ]; then
  cat domain-reservation/.gitignore
fi

Length of output: 526


.gitignoredev-secret.yml 패턴 추가 필수

검증 결과 dev-secret.yml 파일이 현재 git에 추적되고 있습니다. 루트 .gitignore에 이 파일을 제외하는 패턴이 없어 6개 모듈(api-owner, api-user, domain-member, domain-owner, domain-reservation, domain-restaurant)의 비밀 설정 파일이 모두 버전 관리에 포함되어 있습니다.

루트 .gitignore에 다음 패턴을 추가하십시오:

**/*-secret.yml

또는 더 구체적으로:

**/dev-secret.yml
**/prod-secret.yml
🤖 Prompt for AI Agents
In domain-reservation/src/main/resources/dev-secret.yml lines 1-7:
dev-secret.yml (and other *-secret.yml files) are currently tracked in git;
update the repository root .gitignore to stop committing secret files by adding
ignore patterns for secret YAMLs — add a pattern to ignore all files matching
**/*-secret.yml or, alternatively, add the two more specific patterns
**/dev-secret.yml and **/prod-secret.yml; commit the .gitignore change and
remove already-tracked secret files from git history or at least from the index
(git rm --cached) so they are no longer tracked.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (5)
.github/workflows/Dev_Domain_Owner_CD.yml (1)

1-1: 워크플로우 이름의 일관성을 확인하세요.

워크플로우 이름이 dev-owner-domain-cd로 설정되어 있지만, Dev_Domain_Member_CD.yml 파일의 이름은 dev-member-cd입니다. 일관성을 위해 다음 중 하나로 수정을 검토하세요:

  1. dev-domain-owner-cd (Dev_Domain_Member_CD.yml과 일치하도록 접두사 패턴 적용)
  2. 또는 Dev_Domain_Member_CD.yml을 dev-domain-member-cd로 변경
.github/workflows/Dev_Discovery_CD.yml (1)

44-45: mv 명령의 glob 패턴을 더욱 견고하게 처리하세요.

여러 개의 JAR 파일이 생성되거나 glob 패턴이 일치하지 않는 경우 명령이 실패할 수 있습니다. 빌드 출력을 검증하고 예상치 못한 경우를 처리하도록 개선하세요.

제안하는 변경사항:

-      - name: Change artifact file name
-        run: mv discovery-server/build/libs/*.jar discovery-server/build/libs/app.jar
+      - name: Change artifact file name
+        run: |
+          jar_file=$(ls -1 discovery-server/build/libs/*.jar | head -1)
+          if [ -z "$jar_file" ]; then
+            echo "Error: No JAR file found"
+            exit 1
+          fi
+          mv "$jar_file" discovery-server/build/libs/app.jar
.github/workflows/Dev_Domain_Reservation_CD.yml (3)

13-13: 경로 필터 수정 권장

경로 필터에서 워크플로우 파일 자체를 참조할 때는 전체 상대 경로를 사용하는 것이 더 명확합니다.

      - 'domain-reservation/**'
      - 'domain-common/**'
      - 'build.gradle'
      - 'settings.gradle'
-     - 'Dev_Domain_Reservation_CD.yml'
+     - '.github/workflows/Dev_Domain_Reservation_CD.yml'

48-60: 아티팩트 보존 정책 설정 권장

아티팩트 업로드 시 보존 기간(retention)을 명시적으로 설정하는 것이 좋습니다. 기본값은 30일이지만, 배포 정책에 맞게 조정할 수 있습니다.

      - name: Upload artifact file
        uses: actions/upload-artifact@v4
        with:
          name: app-artifact
          path: ./domain-reservation/build/libs/app.jar
+         retention-days: 5
          if-no-files-found: error

      - name: Upload deploy scripts
        uses: actions/upload-artifact@v4
        with:
          name: deploy-scripts
          path: ./scripts/dev/
+         retention-days: 5
          if-no-files-found: error

62-80: 배포 작업에 에러 처리 및 롤백 전략 추가 권장

현재 배포 작업에는 실패 시 명시적인 에러 처리나 롤백 전략이 없습니다. 스크립트 실패 시 대응 방법을 정의하는 것이 좋습니다.

다음 개선을 고려하세요:

  1. 배포 스크립트 실행 실패 시 이전 버전으로 복구하는 롤백 단계
  2. 배포 실패 알림 설정
  3. 배포 상태 검증 단계

예시 구조:

      - name: Replace application to latest
        run: sudo sh ~/app/scripts/replace-new-version.sh
        
      - name: Verify deployment
        run: # 배포 성공 여부 확인 스크립트
        
      - name: Rollback on failure
        if: failure()
        run: sudo sh ~/app/scripts/rollback.sh
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84bd7b4 and 8cef2a2.

📒 Files selected for processing (8)
  • .github/workflows/Dev_API_Owner_CD.yml (2 hunks)
  • .github/workflows/Dev_API_User_CD.yml (2 hunks)
  • .github/workflows/Dev_Discovery_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Member_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Owner_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Reservation_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Restaurant_CD.yml (1 hunks)
  • domain-reservation/src/main/java/com/wellmeet/domain/reservation/controller/ReservationController.java (1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.8)
.github/workflows/Dev_API_Owner_CD.yml

69-69: label "dev-api-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Reservation_CD.yml

64-64: label "dev-domain-reservation" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Owner_CD.yml

64-64: label "dev-domain-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_API_User_CD.yml

69-69: label "dev-api-user" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Discovery_CD.yml

63-63: label "dev-discovery-server" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Member_CD.yml

64-64: label "dev-domain-member" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Restaurant_CD.yml

64-64: label "dev-domain-restaurant" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
🔇 Additional comments (9)
.github/workflows/Dev_API_User_CD.yml (1)

69-69: Self-hosted runner label 검증 필요: dev-api-user 러너가 실제로 프로비저닝되어 있는지 확인하세요.

actionlint에서 dev-api-user 레이블을 알 수 없는 것으로 표시하고 있습니다. 이는 자체 호스팅 러너 레이블입니다. 다음 중 하나를 수행해야 합니다:

  1. .github/actionlint.yaml 파일에 이 커스텀 러너 레이블을 등록하거나
  2. 해당 레이블을 가진 자체 호스팅 러너가 실제로 GitHub Actions에 등록되어 있는지 확인하세요.

워크플로우 실행 중 이 러너를 찾을 수 없으면 배포 작업이 실패합니다.

.github/workflows/Dev_API_Owner_CD.yml (1)

69-69: Self-hosted runner label 검증 필요: dev-api-owner 러너가 실제로 프로비저닝되어 있는지 확인하세요.

actionlint에서 dev-api-owner 레이블을 알 수 없는 것으로 표시하고 있습니다. Dev_API_User_CD.yml과 마찬가지로 다음을 확인하세요:

  1. .github/actionlint.yaml 파일에 이 커스텀 러너 레이블을 등록하거나
  2. 해당 레이블을 가진 자체 호스팅 러너가 GitHub Actions에 등록되어 있는지 확인하세요.
.github/workflows/Dev_Domain_Member_CD.yml (2)

64-64: Self-hosted runner label 검증 필요: dev-domain-member 러너가 실제로 프로비저닝되어 있는지 확인하세요.

actionlint에서 dev-domain-member 레이블을 알 수 없는 것으로 표시하고 있습니다. 다음을 확인하세요:

  1. .github/actionlint.yaml 파일에 이 커스텀 러너 레이블을 등록하거나
  2. 해당 레이블을 가진 자체 호스팅 러너가 GitHub Actions에 등록되어 있는지 확인하세요.

29-60: 비밀 설정 단계가 누락되었습니다.

Dev_API_User_CD.ymlDev_API_Owner_CD.yml 워크플로우에는 "Setting dev-secret.yml" 단계가 있지만, 이 워크플로우에는 없습니다.

도메인 서비스가 개발 환경 시크릿이 필요하지 않은 경우는 문제없지만, 필요한 경우 다음 단계를 추가해야 합니다:

- name: Setting dev-secret.yml
  run: |
    echo "${{ secrets.DEV_SECRET_YML }}" > ./domain-member/src/main/resources/dev-secret.yml

이것이 의도된 것인지 확인해 주세요.

.github/workflows/Dev_Domain_Owner_CD.yml (2)

64-64: Self-hosted runner label 검증 필요: dev-domain-owner 러너가 실제로 프로비저닝되어 있는지 확인하세요.

actionlint에서 dev-domain-owner 레이블을 알 수 없는 것으로 표시하고 있습니다. 다음을 확인하세요:

  1. .github/actionlint.yaml 파일에 이 커스텀 러너 레이블을 등록하거나
  2. 해당 레이블을 가진 자체 호스팅 러너가 GitHub Actions에 등록되어 있는지 확인하세요.

29-60: 비밀 설정 단계가 누락되었습니다.

Dev_API_User_CD.ymlDev_API_Owner_CD.yml 워크플로우에는 "Setting dev-secret.yml" 단계가 있지만, 이 워크플로우에는 없습니다.

도메인 서비스가 개발 환경 시크릿이 필요하지 않은 경우는 문제없지만, 필요한 경우 다음 단계를 추가해야 합니다:

- name: Setting dev-secret.yml
  run: |
    echo "${{ secrets.DEV_SECRET_YML }}" > ./domain-owner/src/main/resources/dev-secret.yml

이것이 의도된 것인지 확인해 주세요.

.github/workflows/Dev_Domain_Restaurant_CD.yml (1)

62-65: 자격 호스트 러너 레이블 구성 - 검증 완료

검증 결과, 원래의 리뷰 코멘트는 부정확합니다.

  • actionlint.yaml 파일은 저장소에 존재하지 않습니다. 따라서 레이블을 이 파일에 등록하는 제안은 적용할 수 없습니다.
  • dev-domain-restaurant 레이블은 저장소의 표준 관행입니다. 동일한 패턴의 자격 호스트 러너 레이블(dev-api-owner, dev-api-user, dev-discovery-server, dev-domain-member, dev-domain-owner, dev-domain-reservation)이 여러 워크플로우에서 일관되게 사용되고 있습니다.
  • 표준 러너(예: ubuntu-latest)로 변경하는 제안은 저장소의 확립된 자격 호스트 인프라 설정과 맞지 않습니다.

현재 코드는 저장소의 기존 패턴을 따르고 있으며, 리뷰 코멘트의 우려사항은 저장소의 실제 구성과 부합하지 않습니다.

Likely an incorrect or invalid review comment.

domain-reservation/src/main/java/com/wellmeet/domain/reservation/controller/ReservationController.java (1)

22-22: 클래스 이름 변경이 올바르게 완료되었습니다.

검증 결과, 이전 클래스 이름 DomainReservationController에 대한 참조가 활성 코드에서는 완전히 제거되었습니다. 문서 파일(마이그레이션 플랜)에만 존재하며, 이는 문제가 되지 않습니다. 새로운 클래스명 ReservationController로 올바르게 선언되어 있고, 모든 관련 파일이 일관되게 업데이트되었습니다.

.github/workflows/Dev_Domain_Reservation_CD.yml (1)

64-64: 리뷰 의견 철회 - 의도된 설정 패턴 확인됨

dev-domain-reservation 러너 레이블은 리포지토리 전체에 걸친 의도된 아키텍처 패턴입니다. 검증 결과:

  • 8개 CD 워크플로우 모두 동일한 패턴 적용: dev-{service-name} 규칙 준수
  • 다른 워크플로우들도 동일한 커스텀 러너 사용 (dev-domain-owner, dev-api-user, dev-discovery-server 등)
  • 각 서비스별 전용 자체 호스팅 러너를 위한 명확한 명명 규칙
  • GitHub Actions 모범 사례에 부합하는 다중 서비스 배포 구조

runs-on: dev-domain-reservation은 올바르게 작성되었으며, actionlint가 플래그한 "미지의 레이블"은 조직별 커스텀 러너를 인식하지 못하기 때문입니다. 이는 정상적인 동작입니다.

추가 변경이나 검증이 필요하지 않습니다.


deploy:
needs: build
runs-on: dev-discovery-server
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

자체 호스팅 러너 라벨을 명시적으로 구성해야 합니다.

dev-discovery-server 러너 라벨은 GitHub Actions의 표준 라벨이 아닙니다. 이것이 자체 호스팅 러너라면, .github/actionlint.yaml 파일에 구성하여 actionlint에 알려야 하며, 실제로 이 라벨이 배포 환경에서 사용 가능한지 확인해야 합니다.

.github/actionlint.yaml 파일이 존재하고 dev-discovery-server 라벨이 등록되어 있는지 확인해주세요:


🏁 Script executed:

#!/bin/bash
# Check if actionlint.yaml exists and contains the custom runner label
if [ -f .github/actionlint.yaml ]; then
  echo "actionlint.yaml exists:"
  cat .github/actionlint.yaml
else
  echo "actionlint.yaml does not exist"
fi

# Check for self-hosted runner configuration
grep -r "dev-discovery-server" .github/ || echo "Label not found in .github directory"

Length of output: 103


추가 검증이 필요합니다. 자체 호스팅 러너 구성에 대한 문서나 다른 워크플로우 파일이 있는지 확인하겠습니다.


🏁 Script executed:

#!/bin/bash
# Check for documentation about self-hosted runners
find .github -type f \( -name "*.md" -o -name "*.yaml" -o -name "*.yml" -o -name "*.json" \) -exec grep -l "self-hosted\|runner" {} \;

# Check all workflow files for custom runner usage
echo "=== All workflow files with runner configuration ==="
grep -r "runs-on:" .github/workflows/ --include="*.yml" --include="*.yaml"

# Check for any actionlint config (different name variations)
ls -la .github/ | grep -i action

Length of output: 1108


GitHub Actions 실행기 구성이 일관되게 누락되어 있습니다.

모든 마이크로서비스 워크플로우(dev-api-owner, dev-api-user, dev-discovery-server 등)가 커스텀 러너 라벨을 사용하지만, .github/actionlint.yaml 파일이 존재하지 않아 actionlint 검증이 실패합니다. 다음 중 하나를 수행하십시오:

  • .github/actionlint.yaml을 생성하여 모든 커스텀 러너 라벨을 등록하거나
  • actionlint 검증을 건너뛰도록 워크플로우를 구성하세요

이 커스텀 러너들이 GitHub 조직 설정에서 실제로 구성되어 있는지도 확인하십시오.

🧰 Tools
🪛 actionlint (1.7.8)

63-63: label "dev-discovery-server" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🤖 Prompt for AI Agents
In .github/workflows/Dev_Discovery_CD.yml around line 63, the workflow uses a
custom runner label ("dev-discovery-server") but there is no
.github/actionlint.yaml to register custom runners which causes actionlint
failures; fix by either adding a .github/actionlint.yaml that lists all custom
runner labels used across workflows (dev-api-owner, dev-api-user,
dev-discovery-server, etc.) or modify this workflow to skip actionlint (e.g.,
set a workflow-level flag or add actionlint skip metadata) so validation ignores
custom runners; also verify those custom runner labels actually exist in the
GitHub org settings.

path: ~/app/scripts

- name: Replace application to latest
run: sudo sh ~/app/scripts/replace-new-version.sh
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

배포 스크립트 실행 시 권한 관리 검증 필수

sudo를 사용하여 배포 스크립트를 실행합니다. 자체 호스팅 러너에서 sudo 명령이 비밀번호 없이 실행되도록 안전하게 설정되었는지 확인하세요. 또한 replace-new-version.sh 스크립트의 내용과 권한 요구사항을 검토하여 보안 위험이 없는지 확인해야 합니다.

다음을 확인하세요:

  • 자체 호스팅 러너의 sudoers 파일에서 암호 없는 sudo 설정
  • 배포 스크립트가 필요한 최소 권한만 요청하는지
  • 스크립트 소유권과 권한 설정

생성할 필요한 정보를 수집하기 위해 저장소를 검토하겠습니다.


🏁 Script executed:

#!/bin/bash
# Find the workflow file and examine the context around line 80
find . -name "Dev_Domain_Reservation_CD.yml" -type f 2>/dev/null | head -5

Length of output: 49


🏁 Script executed:

#!/bin/bash
# Search for the deployment script referenced in the workflow
find . -name "replace-new-version.sh" -type f 2>/dev/null

Length of output: 36


🏁 Script executed:

#!/bin/bash
# Look for sudoers or runner configuration files
find . -name "sudoers*" -o -name "*runner*" -o -name "*setup*" 2>/dev/null | grep -E "(sudoers|runner|setup)" | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for any documentation or scripts related to self-hosted runner setup
fd -e md -e txt -e sh | xargs grep -l "sudo\|sudoers\|self-hosted" 2>/dev/null | head -10

Length of output: 34


🏁 Script executed:

#!/bin/bash
# Examine the workflow file context around line 80
cat ./.github/workflows/Dev_Domain_Reservation_CD.yml | head -90 | tail -20

Length of output: 480


🏁 Script executed:

#!/bin/bash
# View the complete deployment script
cat ./scripts/dev/replace-new-version.sh

Length of output: 882


🏁 Script executed:

#!/bin/bash
# Check script permissions
ls -la ./scripts/dev/replace-new-version.sh

Length of output: 85


🏁 Script executed:

#!/bin/bash
# Search for README or documentation about self-hosted runner setup
find . -name "README*" -o -name "SETUP*" -o -name "*.md" | xargs grep -l "self-hosted\|runner\|deploy" 2>/dev/null | head -5

Length of output: 0


배포 스크립트의 실제 권한 관리 문제 확인됨

검증 결과, 다음의 보안 문제가 확인되었습니다:

  • 스크립트 파일 권한이 644로 설정되어 있어 모든 사용자가 배포 스크립트를 읽을 수 있습니다 (./scripts/dev/replace-new-version.sh)
  • 워크플로우에서 sudo sh 실행 후 스크립트 내부에서 다시 sudo nohup java 사용하여 불필요한 중복 sudo 실행 발생
  • 최소 권한 원칙이 적용되지 않음

필수 조치:

  • 스크립트 파일 권한을 750 이상으로 변경하여 불필요한 사용자의 읽기 접근 제한
  • 워크플로우의 sudo sh 또는 스크립트 내부의 중복된 sudo를 제거하여 단일 권한 요청만 사용
  • 자체 호스팅 러너 설정 문서에서 암호 없는 sudo 설정 요구사항 명시
  • 배포에 필요한 최소 권한만 sudoers에 등록하도록 구성
🤖 Prompt for AI Agents
.github/workflows/Dev_Domain_Reservation_CD.yml around line 80: the workflow
runs the deployment script with `sudo sh ~/app/scripts/replace-new-version.sh`,
causing unnecessary privilege escalation and the script file is world-readable
(644); update to ensure least-privilege by (1) change the script file
permissions to 750 (or tighter) in the repo/infra so only owner/group can
read/execute, (2) remove the `sudo sh` wrapper in the workflow and instead run
the script as the intended user or call the script directly so only one sudo
elevation is used if absolutely required, (3) remove redundant `sudo`
invocations inside the script (use a single controlled elevation point), (4) add
documentation for self-hosted runner setup that describes the passwordless sudo
requirement if needed and explicitly restrict allowed sudo commands in sudoers
to only the deployment commands required.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

♻️ Duplicate comments (11)
.github/workflows/Dev_Domain_Reservation_CD.yml (1)

84-84: 배포 스크립트 실행 시 권한 관리 재검증 권장

이전 리뷰에서 sudo 권한 관리에 대해 지적되었습니다. 배포 스크립트가 최소 권한 원칙을 따르는지, 그리고 sudoers 설정이 올바른지 재확인하세요. 스크립트 파일 권한과 중복된 sudo 호출도 검토하시기 바랍니다.

.github/workflows/Dev_Domain_Restaurant_CD.yml (1)

3-13: 워크플로우 트리거에서 임시 피처 브랜치 및 경로 필터 수정 필요

이전 리뷰에서 지적한 대로 feat/#71 브랜치와 상대 경로를 수정해야 합니다. PR 병합 후 develop 브랜치만 유지하고, 경로 필터를 전체 경로로 업데이트하세요.

  on:
    push:
      branches:
        - "develop"
-       - "feat/#71"
      paths:
        - 'domain-restaurant/**'
        - 'domain-common/**'
        - 'build.gradle'
        - 'settings.gradle'
-       - 'Dev_Domain_Restaurant_CD.yml'
+       - '.github/workflows/Dev_Domain_Restaurant_CD.yml'
.github/workflows/Dev_Domain_Owner_CD.yml (1)

3-13: 워크플로우 트리거 설정 수정 필요

feat/#71 브랜치를 제거하고 경로 필터를 전체 경로로 업데이트하세요. 다른 도메인 CD 워크플로우와 동일한 패턴을 적용하세요.

  on:
    push:
      branches:
        - "develop"
-       - "feat/#71"
      paths:
        - 'domain-owner/**'
        - 'domain-common/**'
        - 'build.gradle'
        - 'settings.gradle'
-       - 'Dev_Domain_Owner_CD.yml'
+       - '.github/workflows/Dev_Domain_Owner_CD.yml'
api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java (1)

9-24: Lombok 어노테이션 import 일관성 정리 필요

Line 9-10에서 여전히 @lombok.Builder, @lombok.AllArgsConstructor를 직접 사용하고 있습니다. 상단의 @Getter, @NoArgsConstructor와 동일하게 import하여 사용하는 편이 가독성과 일관성에 맞습니다. 이전 리뷰 코멘트도 동일 사항을 지적했으므로 이번에 반영 부탁드립니다.

 import lombok.Getter;
 import lombok.NoArgsConstructor;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 
 @Getter
 @NoArgsConstructor
-@lombok.Builder
-@lombok.AllArgsConstructor
+@Builder
+@AllArgsConstructor
 public class FavoriteRestaurantResponse {
api-user/src/main/java/com/wellmeet/restaurant/UserRestaurantBffService.java (1)

30-52: 전국 식당 풀 스캔은 스케일 불가
이전에 지적된 그대로, getAllRestaurants()로 전체 식당 목록을 끌어온 뒤 BFF 메모리에서 거리 필터링하면 식당 수가 늘어날수록 네트워크 트래픽·메모리 사용량이 폭증합니다. 반경 검색은 도메인 서비스나 DB에서 처리해야 합니다. 위도·경도·반경을 쿼리 파라미터로 넘기는 전용 엔드포인트를 추가하고, BFF에서는 그 결과만 받아 활용하도록 바꿔 주세요.

api-user/src/main/java/com/wellmeet/reservation/UserReservationBffService.java (6)

49-56: 중복 예약 확인 로직의 비효율성 (이전 리뷰에서 지적됨)

회원의 모든 예약을 가져와서 BFF에서 필터링하는 방식은 예약 건수가 많을 경우 비효율적입니다. 이 로직을 domain-reservation-service로 위임하는 전용 엔드포인트 추가를 고려하세요.


65-77: 용량 감소 후 보상 로직 누락 (이전 리뷰에서 지적됨)

Line 66-67에서 용량을 감소시킨 직후 예약 생성(line 77)이 실패하면 좌석이 영구적으로 줄어듭니다. 예약 생성 실패 시 용량을 복구하는 보상 트랜잭션을 try-catch로 추가해야 합니다.


106-120: N+1 조회 문제 (이전 리뷰에서 지적됨)

각 예약마다 restaurantClient.getAvailableDate()를 개별 호출하여 N+1 문제가 발생합니다. restaurant-service에 배치 조회 엔드포인트를 추가하여 한 번의 호출로 모든 AvailableDateDTO를 가져오도록 개선이 필요합니다.


176-194: 수정 플로우의 용량 롤백 누락 (이전 리뷰에서 지적됨)

기존 용량 복구(line 181-182) 후 새 슬롯 감소(line 183-184)와 예약 업데이트(line 193)를 수행하는데, 예외 발생 시 용량 불일치가 발생합니다. 새 슬롯 확보 여부를 추적하고 실패 시 양쪽 모두 원복하는 보상 로직이 필요합니다.


195-203: 업데이트 후 식당 정보가 갱신되지 않음 (이전 리뷰에서 지적됨)

Line 197에서 reservation.getRestaurantId()로 식당 정보를 조회하면, 예약이 다른 식당으로 변경된 경우에도 이전 식당 이름이 노출됩니다. updatedReservation.getRestaurantId()를 사용해야 합니다.


219-225: 예약 취소 시 선행 용량 복구로 인한 이중 예약 위험 (이전 리뷰에서 지적됨)

Line 220-221에서 취소 전에 용량을 복구합니다. 취소가 실패하면 예약은 살아있는데 좌석은 풀려 이중 예약이 발생할 수 있습니다. 취소 성공 후에 용량을 복구하도록 순서를 변경해야 합니다.

🧹 Nitpick comments (26)
domain-owner/src/main/resources/application-dev.yml (1)

60-67: 로깅 레벨 설정 - 개발환경에 적합하나 성능 영향 검토 권장

org.hibernate.type.descriptor.sql.BasicBinder에 TRACE 레벨, Hibernate SQL 및 Spring 컴포넌트에 DEBUG 레벨을 설정하면 상세한 로깅이 가능합니다. 개발 환경에서는 유용하지만, 본격적인 통합 테스트 시 I/O 성능 저하가 발생할 수 있습니다.

필요에 따라 프로파일별로 로깅 레벨을 조정하는 것을 고려하세요:

# application-local.yml: 더 상세한 로깅
# application-dev.yml: 현재 설정 유지
# application-test.yml: INFO 레벨로 축소
common-client/build.gradle (1)

10-11: 공통 DTO 모듈의 의존성 구성 확인 필요.

현재 dependencies 블록이 비어있습니다. 제공하는 DTO 클래스들이 @NotNull, @Valid 같은 Jakarta Validation 애노테이션이나 Lombok을 사용한다면 해당 의존성을 추가해야 합니다. DTO 계약(contract)을 명확히 하기 위해 필수 의존성을 명시적으로 선언하세요.

domain-owner/build.gradle (1)

16-17: Flyway 데이터베이스 마이그레이션 설정이 일관되게 적용되고 있습니다.

Flyway 의존성이 적절히 추가되었으며, MySQL 방언도 함께 포함되어 있습니다. 다만 Flyway 버전이 명시되지 않은 경우 상위 프로젝트의 BOM에서 버전 관리가 이루어지는지 확인해주세요.

claudedocs/microservices-migration-plan.md (9)

291-291: 강조 대신 제목 구조 사용 (MD036)

**코드 구현 (완료)**:는 강조 텍스트이지만, 섹션 헤더로 기능합니다. 더 명확한 구조를 위해 마크다운 제목(####)을 사용하는 것이 권장됩니다.

-**코드 구현 (완료)**:
+#### 코드 구현 (완료)

296-296: 강조 대신 제목 구조 사용 (MD036)

**클래스 네이밍 규칙 적용 완료 (2025-11-05)**도 동일하게 제목 구조로 개선 가능합니다.

-**클래스 네이밍 규칙 적용 완료 (2025-11-05)**
+#### 클래스 네이밍 규칙 적용 완료 (2025-11-05)

287-307: Phase 1 완료 상태 설명 명확히 하기

Line 287에서 "✅ 완료 (2025-11-05)"로 표시되어 있으나, 실행 검증(Line 297~304)은 여전히 "보류" 상태입니다. 이 불일치로 인해 실제 완료 수준이 모호합니다.

다음과 같이 상태 설명을 명확히 하는 것을 권장합니다:

  • "코드 완성" 대 "독립 서버 검증 완료"를 명확히 구분
  • Line 305의 완료도 설명("코드 완성, 독립 실행 검증은 Phase 6 이후")이 좋은 예시

375-407: Phase 2 완료 상태 일관성 확보

Phase 1과 유사하게 Phase 2도 "✅ 완료 (2025-11-05)"로 표시되어 있지만, 실행 검증은 보류 상태입니다. Line 395의 "90% (코드 완성, 독립 실행 검증은 Phase 6 이후 수행 예정)" 설명이 명확하나, 헤더의 상태 표시와 일관성을 유지하도록 권장합니다.


410-462: Phase 3 상태 기록 누락

Phase 3 (line 410~462)에서:

  • Line 414: "완료 일자: 2025-11-05" 기록됨
  • 하지만 Line 455 이후의 완료 기준 체크리스트 구조가 Phase 1, 2와 달리 상세하지 않음
  • "완료도" 퍼센티지가 명시되지 않음

Phase 1-2와 동일한 수준의 상세 상태 기록을 추가할 것을 권장합니다.


465-812: Phase 4 실행 검증 상태 명확히

Phase 4는 가장 광범위한 문서화가 되어 있으나, Line 797~804에서도 동일한 패턴이 보입니다:

  • 코드 구현 항목들: [x] 체크됨
  • 실행 검증 항목들: 보류 상태

Line 806~810의 "중요" 섹션이 이를 잘 설명하고 있으나, 헤더 상태와의 일관성을 명확히 할 것을 권장합니다.


975-993: Phase 5 완료 상태 및 성과 기록 우수

Phase 5 섹션 (Line 975~993)은 매우 명확한 완료 기준, 주요 성과, 구체적인 수치를 포함하고 있습니다. 다음과 같은 점이 우수합니다:

  • ✅ 세부 체크리스트 제공
  • ✅ 정량적 성과 (테스트 속도 3-5배 개선)
  • ✅ 구현 아티팩트 명시 (10개 FeignClient 인터페이스, 15개 DTO)

이 형식을 Phase 1-4에도 일관되게 적용할 것을 권장합니다.


1222-1231: 2025-11-05 변경 이력 항목 구조 개선

새 항목은 좋은 세부 사항을 포함하지만, 다음 개선을 권장합니다:

  1. 49개 파일 변경 정보는 영외 조건입니다. PR 맥락에서는 이 파일만 검토되고 있습니다.
  2. "Phase 1, 2 완료도 90%로 업데이트"와 "Phase 3, 4, 5 완료 상태 유지"의 의미가 명확하지 않습니다.
  3. "참고: Phase 5 BFF 전환 완료로 domain-* 독립 실행은 선택사항이 됨" → 이 중요한 설명을 본문(Line 307, 397 등)에도 반영하여 일관성 확보를 권장합니다.
~- ✅ Phase 1-5 전체 완료 표시 업데이트
~- ✅ 클래스 네이밍 규칙 적용 완료 기록 (49개 파일: 38 프로덕션 + 11 테스트)
~- ✅ Phase 1, 2 완료도 90%로 업데이트 (코드 완성, 독립 실행은 Phase 6 이후)
~- ✅ Phase 3, 4, 5 완료 상태 유지
+- ✅ Phase 1-5 완료 상태 표시 업데이트: 코드 구현 완료, 독립 서버 검증은 Phase 6 이후
+- ✅ 클래스 네이밍 규칙 적용 완료 (컨트롤러, 애플리케이션 서비스, FeignClient)
+- ✅ Phase 5 BFF 전환으로 인해 domain-* 독립 실행이 선택사항화됨을 명시

287-307: 전체 문서: 완료 상태 정의 명확화

현재 문서에서 "완료"의 의미가 모호합니다:

  • 현재 해석: Phase 1-5까지 코드 레벨 구현은 완료되었으나, 독립 서버 실행 검증은 Phase 6 이후 예정
  • 문서상 표시: 각 Phase 헤더에서 "✅ 완료 (날짜)"로 표기

개선 제안: 문서 상단에 명확한 정의를 추가합니다:

## 완료 상태 정의

- **✅ 코드 완성**: 컨트롤러, 서비스, DTO, 설정 파일 구현 완료
- **⚠️ 실행 검증 보류**: Phase 6 Bean 스캔 문제 해결 후 bootJar 빌드 및 독립 서버 실행 검증 예정
- **❌ 미구현**: 아직 구현되지 않은 Phase

이렇게 하면 각 Phase의 상태가 명확해집니다.

Also applies to: 375-407, 410-462, 465-812, 975-993

domain-member/src/main/resources/db/migration/V1__init_schema.sql (3)

17-27: ID 생성 전략의 일관성 검토 필요

member 테이블은 VARCHAR(255) 기반 ID(UUID로 추정)를 사용하지만, favorite_restaurant 테이블은 BIGINT AUTO_INCREMENT를 사용합니다. 같은 마이크로서비스 내에서 ID 생성 전략이 다르면 코드 복잡도가 증가하고 유지보수가 어려워집니다.

또한 분산 마이크로서비스 환경에서는 AUTO_INCREMENT보다 UUID 기반 ID가 더 적합합니다. 데이터베이스 복제나 샤딩 시나리오에서 AUTO_INCREMENT는 충돌 위험이 있기 때문입니다.

다음 중 하나를 선택하여 일관성을 유지하세요:

  1. favorite_restaurant의 id도 UUID 기반(VARCHAR(255))으로 변경
  2. 또는 BIGINT를 유지하되, 도메인 설계 문서에서 이유를 명시

권장 수정안:

- CREATE TABLE favorite_restaurant
- (
-     id            BIGINT AUTO_INCREMENT PRIMARY KEY,
+CREATE TABLE favorite_restaurant
+(
+    id            VARCHAR(255) PRIMARY KEY,
     member_id     VARCHAR(255) NOT NULL,

1-27: 파일 끝에 개행 문자 추가 필요

SQL 마이그레이션 파일은 개행 문자로 종료하는 것이 표준 관행입니다(POSIX 정의). 파일 끝에 개행이 없으면 버전 관리 시스템에서 경고가 발생할 수 있습니다.

27번 줄 끝에 개행 문자를 추가하세요.

     INDEX idx_favorite_restaurant_member (member_id),
     INDEX idx_favorite_restaurant_restaurant (restaurant_id)
 );
+

16-27: 마이크로서비스 환경에서 데이터 무결성 검증 전략 확인 필요

물리적 외래키 제약을 제거한 설계는 마이크로서비스 패턴으로 적절하지만, 데이터베이스 레벨에서 무결성이 보장되지 않습니다. 따라서 다음이 필수입니다:

  1. 애플리케이션 계층의 검증: BFF 오케스트레이션 레이어에서 member_id와 restaurant_id 존재 여부를 Feign 클라이언트로 명시적으로 확인
  2. 고아 레코드 방지: 즐겨찾기 추가 시 두 리소스 모두 존재하는지 확인
  3. 삭제 처리 전략: 회원이나 레스토랑 삭제 시 해당 favorite_restaurant 레코드를 정리하는 이벤트 기반 처리 또는 배치 작업

마이그레이션 자체는 올바른 설계이지만, BFF 계층의 구현에서 이러한 검증이 완전히 커버되어야 합니다.

BFF 서비스 계층(api-user 또는 api-owner)에서 즐겨찾기 관련 데이터 무결성 검증이 구현되었는지 확인하여 댓글로 알려주세요. 특히 다음 메서드들을 점검해주세요:

  • 즐겨찾기 추가 시 member, restaurant 존재 확인
  • 즐겨찾기 삭제 시 처리
  • 회원 또는 레스토랑 삭제 시 favorite_restaurant 정리 로직
api-user/src/main/resources/application-dev.yml (1)

56-63: 로깅 레벨 설정을 검토하세요.

com.wellmeetorg.springframework.web, org.springframework.cloud의 DEBUG 레벨 설정은 개발 환경에서는 적절하지만, 로그 볼륨이 상당할 수 있습니다. 특히 Feign 클라이언트 호출이 증가하면 org.springframework.cloud 로그가 매우 많아질 수 있으므로, 필요에 따라 INFO 레벨로 낮추거나 프로필별로 조정하는 것을 고려하세요.

CLAUDE.md (1)

258-269: 레이어별 접미사 정리 테이블이 명확하게 정리되어 있습니다.

표의 내용이 앞의 상세한 설명과 일관되게 정렬되어 있으며, 각 계층의 역할이 명확합니다. 다만 한 가지 제안사항:

라인 260-268의 테이블에서 "위치(위치)" 컬럼의 path 표기가 더 구체적일 수 있습니다. 예를 들어:

  • domain-* 대신 domain-{entity}/src/main/java/...
  • api-* 대신 api-user|api-owner/src/main/java/...

이렇게 하면 개발자들이 파일을 생성할 때 정확한 디렉토리 구조를 바로 파악할 수 있을 것 같습니다.

common-client/src/main/java/com/wellmeet/common/dto/request/UpdateRestaurantDTO.java (1)

3-10: 원시 타입 대신 래퍼 타입 사용을 고려하세요.

latitudelongitude에 원시 타입 double을 사용하면 부분 업데이트 시나리오에서 유연성이 제한될 수 있습니다. 좌표를 변경하지 않는 업데이트의 경우, null 값을 전달할 수 없어 항상 기존 값을 명시적으로 제공해야 합니다.

필요시 다음과 같이 래퍼 타입으로 변경할 수 있습니다:

 public record UpdateRestaurantDTO(
         String name,
         String address,
-        double latitude,
-        double longitude,
+        Double latitude,
+        Double longitude,
         String thumbnail
 ) {
 }
api-owner/src/test/java/com/wellmeet/restaurant/OwnerRestaurantBffControllerTest.java (1)

140-214: 테스트 픽스처 생성 방식 개선 고려

리플렉션을 사용해 private 필드를 설정하는 방식은 취약할 수 있습니다. 필드명 변경 시 컴파일 타임에 감지되지 않고 런타임에 실패합니다.

가능하다면 응답 객체에 테스트용 빌더나 정적 팩토리 메서드를 제공하는 것을 고려해보세요.

common-client/src/main/java/com/wellmeet/common/dto/AvailableDateDTO.java (1)

7-17: 공통 DTO에 검증 및 문서화 고려

서비스 간 통신에 사용되는 공통 DTO에 다음 사항을 고려해보세요:

  • 필드 검증 애노테이션 추가 (예: @NotNull, @Positive for maxCapacity)
  • JavaDoc을 통한 필드 의미 문서화

다만, 검증 로직이 서비스 경계에서 수행된다면 현재 구조도 적절합니다.

domain-member/src/main/java/com/wellmeet/domain/member/service/MemberApplicationService.java (1)

45-59: 매핑 로직이 완전하고 정확합니다

toDTO 헬퍼 메서드가 모든 필드를 올바르게 매핑하고 있습니다. 만약 다른 클래스에서도 유사한 매핑이 필요하다면, 공통 매퍼 유틸리티로 추출하는 것을 고려해보세요.

common-client/src/main/java/com/wellmeet/common/dto/BusinessHourDTO.java (1)

7-19: 비즈니스 규칙 검증 고려

영업 시간 DTO에 다음을 고려해보세요:

  • openTime < closeTime 같은 비즈니스 규칙 검증
  • breakStartTimebreakEndTime이 영업 시간 내에 있는지 검증
  • 필드에 대한 JavaDoc 문서화

이러한 검증이 서비스 레이어에서 수행된다면 현재 구조도 적절합니다.

api-user/src/test/java/com/wellmeet/favorite/UserFavoriteRestaurantBffControllerTest.java (1)

21-106: 컨트롤러 테스트가 잘 구성되어 있습니다.

MockMvc를 사용한 웹 계층 테스트가 적절하게 작성되었습니다. 세 가지 주요 엔드포인트(조회, 추가, 삭제)에 대한 테스트가 포함되어 있으며, 응답 상태 코드와 JSON 구조를 검증하고 있습니다.

테스트 커버리지를 더 강화하려면 다음 시나리오를 추가로 고려해보세요:

  • 존재하지 않는 레스토랑 추가 시도
  • 이미 즐겨찾기에 있는 레스토랑 중복 추가
  • 존재하지 않는 즐겨찾기 삭제 시도
  • memberId가 누락된 경우의 400 에러 처리
api-user/src/main/java/com/wellmeet/client/RestaurantAvailableDateFeignClient.java (1)

19-23: 용량 조정 메서드의 반환 타입을 고려하세요.

decreaseCapacityincreaseCapacity 메서드가 void를 반환하도록 정의되어 있습니다. 이는 응답 본문이 필요 없다는 것을 의미하지만, 작업 결과를 확인하거나 오류를 처리해야 하는 경우에는 적절한 DTO를 반환하도록 변경하는 것을 고려해보세요.

만약 용량 조정 후 업데이트된 AvailableDateDTO를 반환받아야 한다면:

 @PutMapping("/decrease-capacity")
-void decreaseCapacity(@RequestBody DecreaseCapacityRequest request);
+AvailableDateDTO decreaseCapacity(@RequestBody DecreaseCapacityRequest request);

 @PutMapping("/increase-capacity")
-void increaseCapacity(@RequestBody IncreaseCapacityRequest request);
+AvailableDateDTO increaseCapacity(@RequestBody IncreaseCapacityRequest request);
api-user/src/test/java/com/wellmeet/favorite/UserFavoriteRestaurantBffServiceTest.java (1)

116-132: DTO 필드 정의를 확인했으며, null 값 전달은 기술적으로 유효하지만 설계 개선이 필요합니다.

FavoriteRestaurantDTORestaurantDTO 모두 LocalDateTime createdAtupdatedAt 필드에 @Nullable 또는 @NotNull 애너테이션이 없습니다. Java 레코드는 Optional이나 명시적 애너테이션 없이도 null을 허용하므로, 테스트 헬퍼 메서드의 null 전달은 런타임 오류를 발생시키지 않습니다.

다만 테스트 시나리오에서 이 필드들에 접근하지 않으므로 현재 기능상 문제는 없습니다. 그러나 null이 의도된 설계임을 명확히 하려면:

  • Optional<LocalDateTime> createdAt 사용, 또는
  • @Nullable 애너테이션 추가

를 권장합니다.

api-user/src/main/java/com/wellmeet/client/RestaurantFeignClient.java (1)

3-10: DTO import 경로 정리 필요

공통 DTO 계층으로 묶은 다른 모듈들과 달리 이 파일은 ReviewDTORestaurantIdsRequest만 여전히 com.wellmeet.client... 경로를 바라보고 있습니다. 공통 모듈이 소스 오브 트루스로 사용되고 기존 경로가 정리되면 컴파일 실패나 중복 정의가 발생할 수 있으니, 동일하게 com.wellmeet.common... 경로를 사용하도록 맞춰 주세요.

-import com.wellmeet.client.dto.ReviewDTO;
-import com.wellmeet.client.dto.request.RestaurantIdsRequest;
+import com.wellmeet.common.dto.ReviewDTO;
+import com.wellmeet.common.dto.request.RestaurantIdsRequest;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cef2a2 and 206dbc0.

📒 Files selected for processing (107)
  • .github/workflows/Dev_CI.yml (1 hunks)
  • .github/workflows/Dev_Domain_Member_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Owner_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Reservation_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Restaurant_CD.yml (1 hunks)
  • CLAUDE.md (2 hunks)
  • api-owner/build.gradle (1 hunks)
  • api-owner/src/main/java/com/wellmeet/client/MemberFeignClient.java (2 hunks)
  • api-owner/src/main/java/com/wellmeet/client/OwnerFeignClient.java (1 hunks)
  • api-owner/src/main/java/com/wellmeet/client/ReservationFeignClient.java (1 hunks)
  • api-owner/src/main/java/com/wellmeet/client/RestaurantFeignClient.java (2 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/BusinessHourDTO.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/MemberDTO.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/OwnerDTO.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/ReservationDTO.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/request/MemberIdsRequest.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/request/UpdateOperatingHoursDTO.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/client/dto/request/UpdateRestaurantDTO.java (0 hunks)
  • api-owner/src/main/java/com/wellmeet/global/event/OwnerEventPublishBffService.java (1 hunks)
  • api-owner/src/main/java/com/wellmeet/global/event/event/ReservationConfirmedEvent.java (2 hunks)
  • api-owner/src/main/java/com/wellmeet/reservation/OwnerReservationBffController.java (1 hunks)
  • api-owner/src/main/java/com/wellmeet/reservation/OwnerReservationBffService.java (4 hunks)
  • api-owner/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (2 hunks)
  • api-owner/src/main/java/com/wellmeet/restaurant/OwnerRestaurantBffController.java (1 hunks)
  • api-owner/src/main/java/com/wellmeet/restaurant/OwnerRestaurantBffService.java (3 hunks)
  • api-owner/src/main/java/com/wellmeet/restaurant/dto/OperatingHoursResponse.java (3 hunks)
  • api-owner/src/main/resources/application-dev.yml (2 hunks)
  • api-owner/src/test/java/com/wellmeet/reservation/OwnerReservationBffControllerTest.java (1 hunks)
  • api-owner/src/test/java/com/wellmeet/reservation/OwnerReservationBffServiceTest.java (4 hunks)
  • api-owner/src/test/java/com/wellmeet/restaurant/OwnerRestaurantBffControllerTest.java (1 hunks)
  • api-owner/src/test/java/com/wellmeet/restaurant/OwnerRestaurantBffServiceTest.java (5 hunks)
  • api-user/build.gradle (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/MemberFavoriteRestaurantFeignClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/MemberFeignClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/OwnerFeignClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/ReservationFeignClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/RestaurantAvailableDateFeignClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/client/RestaurantFeignClient.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/favorite/UserFavoriteRestaurantBffController.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/favorite/UserFavoriteRestaurantBffService.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/global/event/UserEventPublishBffService.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/global/event/event/ReservationCanceledEvent.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/global/event/event/ReservationCreatedEvent.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/global/event/event/ReservationUpdatedEvent.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/UserReservationBffController.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/UserReservationBffService.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/reservation/dto/SummaryReservationResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/UserRestaurantBffController.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/UserRestaurantBffService.java (1 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/AvailableDateResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/NearbyRestaurantResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RepresentativeMenuResponse.java (2 hunks)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RestaurantResponse.java (2 hunks)
  • api-user/src/main/resources/application-dev.yml (1 hunks)
  • api-user/src/test/java/com/wellmeet/favorite/UserFavoriteRestaurantBffControllerTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/favorite/UserFavoriteRestaurantBffServiceTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/global/event/UserEventPublishBffServiceTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/global/event/listener/ReservationEventListenerTest.java (4 hunks)
  • api-user/src/test/java/com/wellmeet/reservation/UserReservationBffControllerTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/reservation/UserReservationBffServiceTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/restaurant/UserRestaurantBffControllerTest.java (1 hunks)
  • api-user/src/test/java/com/wellmeet/restaurant/UserRestaurantBffServiceTest.java (8 hunks)
  • claudedocs/microservices-migration-plan.md (5 hunks)
  • common-client/build.gradle (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/AvailableDateDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/BusinessHourDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/FavoriteRestaurantDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/MemberDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/MenuDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/OwnerDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/ReservationDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/ReservationStatus.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/RestaurantDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/request/CreateReservationDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/request/MemberIdsRequest.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/request/UpdateOperatingHoursDTO.java (1 hunks)
  • common-client/src/main/java/com/wellmeet/common/dto/request/UpdateRestaurantDTO.java (1 hunks)
  • docker-compose.yml (1 hunks)
  • domain-member/build.gradle (2 hunks)
  • domain-member/src/main/java/com/wellmeet/domain/member/controller/MemberDomainController.java (3 hunks)
  • domain-member/src/main/java/com/wellmeet/domain/member/controller/MemberFavoriteRestaurantController.java (3 hunks)
  • domain-member/src/main/java/com/wellmeet/domain/member/dto/FavoriteRestaurantResponse.java (0 hunks)
  • domain-member/src/main/java/com/wellmeet/domain/member/dto/MemberResponse.java (0 hunks)
  • domain-member/src/main/java/com/wellmeet/domain/member/service/MemberApplicationService.java (3 hunks)
  • domain-member/src/main/java/com/wellmeet/domain/member/service/MemberFavoriteRestaurantApplicationService.java (3 hunks)
  • domain-member/src/main/resources/application-dev.yml (1 hunks)
  • domain-member/src/main/resources/application-local.yml (1 hunks)
  • domain-member/src/main/resources/db/migration/V1__init_schema.sql (1 hunks)
  • domain-owner/build.gradle (2 hunks)
  • domain-owner/src/main/java/com/wellmeet/domain/owner/controller/OwnerDomainController.java (2 hunks)
  • domain-owner/src/main/java/com/wellmeet/domain/owner/dto/OwnerResponse.java (0 hunks)
  • domain-owner/src/main/java/com/wellmeet/domain/owner/service/OwnerApplicationService.java (3 hunks)
  • domain-owner/src/main/resources/application-dev.yml (1 hunks)
  • domain-owner/src/main/resources/application-local.yml (1 hunks)
  • domain-owner/src/main/resources/db/migration/V1__init_schema.sql (1 hunks)
  • domain-reservation/build.gradle (1 hunks)
  • domain-reservation/src/main/java/com/wellmeet/domain/reservation/controller/ReservationDomainController.java (2 hunks)
  • domain-reservation/src/main/java/com/wellmeet/domain/reservation/dto/ReservationResponse.java (0 hunks)
  • domain-reservation/src/main/java/com/wellmeet/domain/reservation/service/ReservationApplicationService.java (5 hunks)
  • domain-reservation/src/main/resources/application-dev.yml (1 hunks)
  • domain-reservation/src/main/resources/application-local.yml (2 hunks)
  • domain-reservation/src/main/resources/application-test.yml (1 hunks)
⛔ Files not processed due to max files limit (23)
  • domain-reservation/src/main/resources/db/migration/V1__init_schema.sql
  • domain-restaurant/build.gradle
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/RestaurantApplicationService.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/RestaurantDomainController.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/controller/RestaurantAvailableDateController.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/controller/RestaurantBusinessHourController.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/controller/RestaurantMenuController.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/controller/RestaurantReviewController.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/dto/AvailableDateResponse.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/dto/BusinessHourResponse.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/dto/BusinessHoursResponse.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/dto/MenuResponse.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/dto/RestaurantResponse.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/BusinessHourService.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/MenuService.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/RestaurantAvailableDateApplicationService.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/RestaurantBusinessHourApplicationService.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/RestaurantMenuApplicationService.java
  • domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/RestaurantReviewApplicationService.java
  • domain-restaurant/src/main/resources/application-dev.yml
  • domain-restaurant/src/main/resources/application-local.yml
  • domain-restaurant/src/main/resources/db/migration/V1__init_schema.sql
  • settings.gradle
💤 Files with no reviewable changes (13)
  • api-owner/src/main/java/com/wellmeet/client/dto/request/UpdateOperatingHoursDTO.java
  • api-owner/src/main/java/com/wellmeet/client/dto/MemberDTO.java
  • api-owner/src/main/java/com/wellmeet/client/dto/OwnerDTO.java
  • api-owner/src/main/java/com/wellmeet/client/dto/request/MemberIdsRequest.java
  • domain-reservation/src/main/java/com/wellmeet/domain/reservation/dto/ReservationResponse.java
  • api-owner/src/main/java/com/wellmeet/client/dto/BusinessHourDTO.java
  • domain-member/src/main/java/com/wellmeet/domain/member/dto/MemberResponse.java
  • domain-member/src/main/java/com/wellmeet/domain/member/dto/FavoriteRestaurantResponse.java
  • api-owner/src/main/java/com/wellmeet/client/dto/RestaurantDTO.java
  • api-owner/src/main/java/com/wellmeet/client/dto/ReservationDTO.java
  • api-owner/src/main/java/com/wellmeet/client/dto/request/UpdateRestaurantDTO.java
  • domain-owner/src/main/java/com/wellmeet/domain/owner/dto/OwnerResponse.java
  • api-owner/src/main/java/com/wellmeet/client/dto/AvailableDateDTO.java
🚧 Files skipped from review as they are similar to previous changes (6)
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RepresentativeMenuResponse.java
  • domain-reservation/src/main/resources/application-dev.yml
  • domain-reservation/src/main/resources/application-test.yml
  • api-user/build.gradle
  • domain-owner/src/main/resources/application-local.yml
  • domain-reservation/build.gradle
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-10T02:52:48.337Z
Learnt from: unifolio0
Repo: Team-soft-macaron/WellMeet-Backend PR: 14
File: src/main/java/com/wellmeet/recommend/dto/RecommendRestaurantResponse.java:3-3
Timestamp: 2025-07-10T02:52:48.337Z
Learning: The Restaurant class is located at `com.wellmeet.restaurant.domain.Restaurant` after the package refactoring in the WellMeet-Backend project, not in a nested model package structure.

Applied to files:

  • api-user/src/main/java/com/wellmeet/favorite/UserFavoriteRestaurantBffController.java
  • api-user/src/main/java/com/wellmeet/favorite/UserFavoriteRestaurantBffService.java
  • api-user/src/test/java/com/wellmeet/restaurant/UserRestaurantBffControllerTest.java
  • api-user/src/test/java/com/wellmeet/restaurant/UserRestaurantBffServiceTest.java
  • api-user/src/main/java/com/wellmeet/client/RestaurantAvailableDateFeignClient.java
  • api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java
  • api-owner/src/test/java/com/wellmeet/restaurant/OwnerRestaurantBffControllerTest.java
  • domain-member/src/main/java/com/wellmeet/domain/member/controller/MemberFavoriteRestaurantController.java
  • api-user/src/main/java/com/wellmeet/reservation/UserReservationBffService.java
  • api-owner/src/main/java/com/wellmeet/restaurant/OwnerRestaurantBffService.java
  • common-client/src/main/java/com/wellmeet/common/dto/FavoriteRestaurantDTO.java
  • api-user/src/main/java/com/wellmeet/restaurant/UserRestaurantBffController.java
  • api-user/src/main/java/com/wellmeet/client/ReservationFeignClient.java
  • api-user/src/main/java/com/wellmeet/restaurant/UserRestaurantBffService.java
  • api-user/src/main/java/com/wellmeet/client/RestaurantFeignClient.java
  • api-user/src/main/java/com/wellmeet/restaurant/dto/RestaurantResponse.java
  • api-owner/src/main/java/com/wellmeet/restaurant/OwnerRestaurantBffController.java
  • common-client/src/main/java/com/wellmeet/common/dto/MenuDTO.java
  • api-owner/src/main/java/com/wellmeet/restaurant/dto/OperatingHoursResponse.java
  • domain-reservation/src/main/java/com/wellmeet/domain/reservation/controller/ReservationDomainController.java
  • common-client/src/main/java/com/wellmeet/common/dto/RestaurantDTO.java
  • common-client/src/main/java/com/wellmeet/common/dto/request/UpdateRestaurantDTO.java
  • api-owner/src/main/java/com/wellmeet/client/RestaurantFeignClient.java
  • domain-member/src/main/java/com/wellmeet/domain/member/service/MemberFavoriteRestaurantApplicationService.java
  • api-user/src/main/java/com/wellmeet/restaurant/dto/NearbyRestaurantResponse.java
  • api-owner/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java
  • api-user/src/main/java/com/wellmeet/client/MemberFavoriteRestaurantFeignClient.java
  • api-owner/src/main/java/com/wellmeet/reservation/OwnerReservationBffService.java
  • api-owner/src/test/java/com/wellmeet/restaurant/OwnerRestaurantBffServiceTest.java
  • api-owner/src/test/java/com/wellmeet/reservation/OwnerReservationBffServiceTest.java
🧬 Code graph analysis (18)
api-user/src/main/java/com/wellmeet/favorite/UserFavoriteRestaurantBffService.java (5)
api-user/src/main/java/com/wellmeet/reservation/UserReservationBffService.java (1)
  • Service (33-234)
api-user/src/main/java/com/wellmeet/restaurant/UserRestaurantBffService.java (1)
  • Service (20-83)
domain-member/src/main/java/com/wellmeet/domain/member/service/MemberFavoriteRestaurantApplicationService.java (1)
  • Service (11-52)
domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/RestaurantApplicationService.java (1)
  • Service (12-56)
domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/RestaurantReviewApplicationService.java (1)
  • Service (9-29)
api-user/src/test/java/com/wellmeet/favorite/UserFavoriteRestaurantBffServiceTest.java (2)
api-user/src/test/java/com/wellmeet/restaurant/UserRestaurantBffServiceTest.java (1)
  • ExtendWith (27-226)
api-user/src/test/java/com/wellmeet/favorite/FavoriteServiceTest.java (4)
  • ExtendWith (25-144)
  • Test (86-105)
  • Nested (83-106)
  • Nested (40-81)
api-user/src/test/java/com/wellmeet/restaurant/UserRestaurantBffControllerTest.java (2)
api-owner/src/test/java/com/wellmeet/reservation/OwnerReservationBffControllerTest.java (1)
  • Nested (23-46)
api-user/src/test/java/com/wellmeet/restaurant/DomainRestaurantControllerTest.java (1)
  • DomainRestaurantControllerTest (18-88)
api-user/src/main/java/com/wellmeet/client/OwnerFeignClient.java (1)
api-owner/src/main/java/com/wellmeet/client/OwnerClient.java (1)
  • FeignClient (8-13)
api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java (3)
api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (1)
  • Getter (10-41)
api-user/src/main/java/com/wellmeet/restaurant/dto/NearbyRestaurantResponse.java (1)
  • Getter (7-26)
api-user/src/main/java/com/wellmeet/restaurant/dto/RestaurantResponse.java (1)
  • Getter (8-43)
api-user/src/test/java/com/wellmeet/reservation/UserReservationBffControllerTest.java (2)
api-user/src/test/java/com/wellmeet/reservation/UserReservationBffServiceTest.java (1)
  • Nested (62-118)
api-user/src/test/java/com/wellmeet/favorite/UserFavoriteRestaurantBffControllerTest.java (1)
  • WebMvcTest (21-107)
api-user/src/main/java/com/wellmeet/reservation/UserReservationBffService.java (3)
domain-reservation/src/main/java/com/wellmeet/domain/reservation/service/ReservationApplicationService.java (1)
  • Service (13-113)
api-user/src/main/java/com/wellmeet/global/event/UserEventPublishBffService.java (1)
  • Service (10-27)
domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/RestaurantAvailableDateApplicationService.java (1)
  • Service (12-62)
api-user/src/test/java/com/wellmeet/global/event/UserEventPublishBffServiceTest.java (2)
api-user/src/test/java/com/wellmeet/reservation/UserReservationBffServiceTest.java (5)
  • Nested (62-118)
  • Nested (120-166)
  • Nested (168-210)
  • Nested (212-256)
  • ExtendWith (38-364)
api-user/src/test/java/com/wellmeet/global/event/listener/ReservationEventListenerTest.java (1)
  • ExtendWith (24-106)
api-user/src/main/java/com/wellmeet/reservation/dto/SummaryReservationResponse.java (3)
api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (1)
  • Getter (9-30)
api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (1)
  • Getter (10-41)
api-user/src/main/java/com/wellmeet/restaurant/dto/AvailableDateResponse.java (1)
  • Getter (9-26)
api-user/src/test/java/com/wellmeet/reservation/UserReservationBffServiceTest.java (2)
api-owner/src/test/java/com/wellmeet/reservation/OwnerReservationBffServiceTest.java (3)
  • Nested (46-72)
  • Nested (74-107)
  • ExtendWith (28-167)
api-user/src/test/java/com/wellmeet/global/event/UserEventPublishBffServiceTest.java (4)
  • Nested (28-43)
  • Nested (45-60)
  • Nested (62-77)
  • ExtendWith (19-92)
api-user/src/test/java/com/wellmeet/favorite/UserFavoriteRestaurantBffControllerTest.java (2)
api-user/src/test/java/com/wellmeet/favorite/UserFavoriteRestaurantBffServiceTest.java (3)
  • Nested (34-76)
  • Nested (78-100)
  • Nested (102-114)
api-user/src/test/java/com/wellmeet/favorite/FavoriteControllerTest.java (1)
  • FavoriteControllerTest (15-85)
api-user/src/main/java/com/wellmeet/client/ReservationFeignClient.java (1)
api-owner/src/main/java/com/wellmeet/client/ReservationClient.java (1)
  • FeignClient (10-21)
api-user/src/main/java/com/wellmeet/restaurant/UserRestaurantBffService.java (3)
api-user/src/main/java/com/wellmeet/common/util/DistanceCalculator.java (1)
  • DistanceCalculator (3-18)
api-user/src/main/java/com/wellmeet/favorite/UserFavoriteRestaurantBffService.java (1)
  • Service (16-61)
domain-restaurant/src/main/java/com/wellmeet/domain/restaurant/service/RestaurantAvailableDateApplicationService.java (1)
  • Service (12-62)
api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (7)
api-owner/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (2)
  • Getter (13-67)
  • Getter (49-66)
api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java (1)
  • Getter (7-26)
api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (1)
  • Getter (9-30)
api-user/src/main/java/com/wellmeet/reservation/dto/SummaryReservationResponse.java (1)
  • Getter (9-28)
api-user/src/main/java/com/wellmeet/restaurant/dto/AvailableDateResponse.java (1)
  • Getter (9-26)
api-user/src/main/java/com/wellmeet/restaurant/dto/NearbyRestaurantResponse.java (1)
  • Getter (7-26)
api-user/src/main/java/com/wellmeet/restaurant/dto/RestaurantResponse.java (1)
  • Getter (8-43)
api-user/src/main/java/com/wellmeet/client/RestaurantFeignClient.java (1)
api-owner/src/main/java/com/wellmeet/client/RestaurantClient.java (1)
  • FeignClient (15-41)
api-user/src/main/java/com/wellmeet/reservation/dto/CreateReservationResponse.java (4)
api-owner/src/main/java/com/wellmeet/global/event/event/ReservationConfirmedEvent.java (1)
  • Getter (7-33)
api-user/src/main/java/com/wellmeet/favorite/dto/FavoriteRestaurantResponse.java (1)
  • Getter (7-26)
api-user/src/main/java/com/wellmeet/reservation/dto/ReservationResponse.java (1)
  • Getter (10-41)
api-user/src/main/java/com/wellmeet/reservation/dto/SummaryReservationResponse.java (1)
  • Getter (9-28)
common-client/src/main/java/com/wellmeet/common/dto/request/UpdateRestaurantDTO.java (1)
api-owner/src/main/java/com/wellmeet/client/dto/request/UpdateRestaurantDTO.java (1)
  • Getter (8-19)
api-owner/src/main/java/com/wellmeet/client/RestaurantFeignClient.java (1)
api-owner/src/main/java/com/wellmeet/client/RestaurantClient.java (1)
  • FeignClient (15-41)
🪛 actionlint (1.7.8)
.github/workflows/Dev_Domain_Reservation_CD.yml

68-68: label "dev-domain-reservation" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Restaurant_CD.yml

68-68: label "dev-domain-restaurant" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Owner_CD.yml

68-68: label "dev-domain-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Member_CD.yml

68-68: label "dev-domain-member" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🪛 markdownlint-cli2 (0.18.1)
claudedocs/microservices-migration-plan.md

291-291: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


296-296: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


796-796: Bare URL used

(MD034, no-bare-urls)


797-797: Bare URL used

(MD034, no-bare-urls)


798-798: Bare URL used

(MD034, no-bare-urls)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push

Comment on lines +15 to +37
@FeignClient(name = "domain-reservation-service", path = "/api/reservation")
public interface ReservationFeignClient {

@PostMapping
ReservationDTO createReservation(@RequestBody CreateReservationDTO request);

@GetMapping("/{id}")
ReservationDTO getReservation(@PathVariable("id") Long id);

@GetMapping("/restaurant/{restaurantId}")
List<ReservationDTO> getReservationsByRestaurant(@PathVariable("restaurantId") String restaurantId);

@GetMapping("/member/{memberId}")
List<ReservationDTO> getReservationsByMember(@PathVariable("memberId") String memberId);

@PutMapping("/{id}")
ReservationDTO updateReservation(
@PathVariable("id") Long id,
@RequestBody UpdateReservationDTO request
);

@PatchMapping("/{id}/cancel")
void cancelReservation(@PathVariable("id") Long id);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

도메인 예약 API 경로 불일치로 404 발생 위험
@FeignClient에 설정한 path = "/api/reservation" 때문에 실제 호출 경로가 /api/reservation/...으로 만들어지는데, 동일 도메인을 사용하는 api-owner 모듈의 ReservationClient는 여전히 /api/reservations/...(복수형) 경로를 호출합니다. 도메인 서비스가 경로를 바꾸지 않았다면 현재 구현은 전부 404를 맞을 가능성이 매우 높습니다. 다중 호출 지점 중 한 곳만 경로가 달라지면 운영 장애가 발생하니, 여기서도 반드시 복수형 경로로 맞춰 주세요.

다음 수정안을 적용해 주세요:

-@FeignClient(name = "domain-reservation-service", path = "/api/reservation")
+@FeignClient(name = "domain-reservation-service", path = "/api/reservations")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@FeignClient(name = "domain-reservation-service", path = "/api/reservation")
public interface ReservationFeignClient {
@PostMapping
ReservationDTO createReservation(@RequestBody CreateReservationDTO request);
@GetMapping("/{id}")
ReservationDTO getReservation(@PathVariable("id") Long id);
@GetMapping("/restaurant/{restaurantId}")
List<ReservationDTO> getReservationsByRestaurant(@PathVariable("restaurantId") String restaurantId);
@GetMapping("/member/{memberId}")
List<ReservationDTO> getReservationsByMember(@PathVariable("memberId") String memberId);
@PutMapping("/{id}")
ReservationDTO updateReservation(
@PathVariable("id") Long id,
@RequestBody UpdateReservationDTO request
);
@PatchMapping("/{id}/cancel")
void cancelReservation(@PathVariable("id") Long id);
@FeignClient(name = "domain-reservation-service", path = "/api/reservations")
public interface ReservationFeignClient {
@PostMapping
ReservationDTO createReservation(@RequestBody CreateReservationDTO request);
@GetMapping("/{id}")
ReservationDTO getReservation(@PathVariable("id") Long id);
@GetMapping("/restaurant/{restaurantId}")
List<ReservationDTO> getReservationsByRestaurant(@PathVariable("restaurantId") String restaurantId);
@GetMapping("/member/{memberId}")
List<ReservationDTO> getReservationsByMember(@PathVariable("memberId") String memberId);
@PutMapping("/{id}")
ReservationDTO updateReservation(
@PathVariable("id") Long id,
@RequestBody UpdateReservationDTO request
);
@PatchMapping("/{id}/cancel")
void cancelReservation(@PathVariable("id") Long id);
🤖 Prompt for AI Agents
In api-user/src/main/java/com/wellmeet/client/ReservationFeignClient.java around
lines 15 to 37, the Feign client path is set to "/api/reservation" (singular)
which will mismatch the rest of the codebase that calls "/api/reservations"
(plural) and likely cause 404s; change the FeignClient path to
"/api/reservations" and ensure all method-level mappings remain consistent with
the plural base (no extra "/reservations" prefixes), so the constructed URLs
match the domain service and the api-owner module.

Comment on lines +38 to +43
return favoriteRestaurants.stream()
.map(favoriteRestaurant -> {
RestaurantDTO restaurant = restaurantsById.get(favoriteRestaurant.restaurantId());
return getFavoriteRestaurantResponse(restaurant);
})
.toList();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

삭제된 식당 즐겨찾기에서 NPE 발생 가능

즐겨찾기와 식당 데이터가 비동기적으로 동기화되기 때문에, 이미 삭제된 식당이 즐겨찾기에 남아 있으면 restaurantsById.get(...)null을 돌려줄 수 있습니다. 이 상태에서 getFavoriteRestaurantResponse를 호출하면 restaurant.id() 접근 시 즉시 NullPointerException이 발생하고 전체 즐겨찾기 조회가 실패합니다. 일시적인 데이터 불일치만으로도 재현 가능한 문제이므로, null을 필터링하거나 별도 fallback 처리를 넣어 방어해 주세요.

+import java.util.Objects;
@@
-        return favoriteRestaurants.stream()
-                .map(favoriteRestaurant -> {
-                    RestaurantDTO restaurant = restaurantsById.get(favoriteRestaurant.restaurantId());
-                    return getFavoriteRestaurantResponse(restaurant);
-                })
+        return favoriteRestaurants.stream()
+                .map(favoriteRestaurant -> restaurantsById.get(favoriteRestaurant.restaurantId()))
+                .filter(Objects::nonNull)
+                .map(this::getFavoriteRestaurantResponse)
                 .toList();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return favoriteRestaurants.stream()
.map(favoriteRestaurant -> {
RestaurantDTO restaurant = restaurantsById.get(favoriteRestaurant.restaurantId());
return getFavoriteRestaurantResponse(restaurant);
})
.toList();
return favoriteRestaurants.stream()
.map(favoriteRestaurant -> restaurantsById.get(favoriteRestaurant.restaurantId()))
.filter(Objects::nonNull)
.map(this::getFavoriteRestaurantResponse)
.toList();
🤖 Prompt for AI Agents
In
api-user/src/main/java/com/wellmeet/favorite/UserFavoriteRestaurantBffService.java
around lines 38 to 43, the current mapping assumes
restaurantsById.get(favoriteRestaurant.restaurantId()) is non-null which can
cause an NPE if the restaurant was deleted; update the stream to guard against
nulls by either filtering out favorites whose corresponding restaurant is null
before mapping, or map nulls to a safe fallback FavoriteRestaurantResponse
(e.g., a stub/placeholder or a response marking the restaurant as deleted) so
getFavoriteRestaurantResponse is never called with a null restaurant; ensure the
chosen approach preserves ordering and returns a stable, non-exceptional list.

Comment on lines 2 to 13
CREATE TABLE member
(
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
nickname VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
phone_number VARCHAR(255) NOT NULL,
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NOT NULL,
UNIQUE KEY unique_email (email),
UNIQUE KEY unique_phone_number (phone_number)
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

문자 집합(charset) 및 정렬 순서(collation) 명시 필요

테이블 정의에서 CHARACTER SET과 COLLATE가 명시되지 않았습니다. 한글 데이터를 지원하려면 utf8mb4 charset과 적절한 collation을 명시해야 합니다. 이를 지정하지 않으면 데이터베이스 기본 설정에 의존하게 되어 환경 간 불일치 위험이 있습니다.

또한 email 필드는 국제표준상 최대 254자이므로, VARCHAR(255)보다는 VARCHAR(254)로 제한하는 것이 더 정확합니다.

마이그레이션을 다음과 같이 수정하세요:

- CREATE TABLE member
+ CREATE TABLE member (
+ CHARSET utf8mb4
+ COLLATE utf8mb4_unicode_ci
- (
     id           VARCHAR(255) PRIMARY KEY,
     name         VARCHAR(255) NOT NULL,
     nickname     VARCHAR(255) NOT NULL,
-    email        VARCHAR(255) NOT NULL,
+    email        VARCHAR(254) NOT NULL,
     phone_number VARCHAR(255) NOT NULL,

또는 데이터베이스 수준에서 기본 charset/collation을 utf8mb4로 설정하고 있다면, 마이그레이션 파일 최상단에 명시적으로 선언하세요:

SET NAMES utf8mb4;
SET CHARACTER_SET_CLIENT = utf8mb4;
SET CHARACTER_SET_CONNECTION = utf8mb4;
🤖 Prompt for AI Agents
In domain-member/src/main/resources/db/migration/V1__init_schema.sql around
lines 2 to 13, the table definition omits CHARACTER SET and COLLATE and uses
EMAIL VARCHAR(255); update the migration to explicitly set utf8mb4 charset and a
utf8mb4 collation (e.g. utf8mb4_unicode_ci) for the table (or add the SET
NAMES/CHARACTER_SET_* statements at the top if you prefer DB-level), and change
the email column to VARCHAR(254); ensure the UNIQUE KEY declarations remain and
that created_at/updated_at types are unchanged.

Comment on lines 1 to 10
-- Owner 테이블
CREATE TABLE owner
(
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NOT NULL,
UNIQUE KEY unique_email (email)
); No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

데이터베이스 문자 집합 명시 필요

한국어를 포함한 다국어 텍스트 지원을 위해 테이블 레벨에서 명시적으로 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci를 지정해야 합니다. 현재 기본 설정에서는 한글 문자가 제대로 저장되지 않을 수 있습니다.

다음과 같이 수정하세요:

-- Owner 테이블
 CREATE TABLE owner
 (
     id         VARCHAR(255) PRIMARY KEY,
     name       VARCHAR(255) NOT NULL,
     email      VARCHAR(255) NOT NULL,
     created_at DATETIME(6) NOT NULL,
     updated_at DATETIME(6) NOT NULL,
     UNIQUE KEY unique_email (email)
-);
+) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-- Owner 테이블
CREATE TABLE owner
(
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NOT NULL,
UNIQUE KEY unique_email (email)
);
-- Owner 테이블
CREATE TABLE owner
(
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NOT NULL,
UNIQUE KEY unique_email (email)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
🤖 Prompt for AI Agents
In domain-owner/src/main/resources/db/migration/V1__init_schema.sql lines 1-10,
the CREATE TABLE owner statement lacks an explicit character set and collation;
update the table definition to specify CHARACTER SET utf8mb4 COLLATE
utf8mb4_unicode_ci at the table level (e.g., append the CHARACTER SET/COLLATE
clause after the closing parenthesis of the CREATE TABLE) so all VARCHAR columns
use utf8mb4/utf8mb4_unicode_ci; ensure the migration uses this clause for any
future text tables as well.

Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (3)
domain-owner/src/test/java/com/wellmeet/domain/owner/DataBaseCleaner.java (1)

22-24: 비원자적 초기화 패턴

check-then-set 패턴이 원자적이지 않아 여러 스레드가 동시에 extractDatabaseName을 호출할 수 있습니다. 병렬 테스트 실행 시 중복 초기화가 발생할 수 있습니다.

동기화 또는 double-checked locking 패턴 적용을 고려해보세요:

     if (databaseName == null) {
-        extractDatabaseName(context);
+        synchronized (this) {
+            if (databaseName == null) {
+                extractDatabaseName(context);
+            }
+        }
     }
domain-member/src/test/java/com/wellmeet/domain/member/DataBaseCleaner.java (2)

29-36: 에러 메시지에 원인 정보를 추가하는 것을 권장합니다.

리소스 관리는 try-with-resources로 올바르게 처리되었습니다. 다만, 디버깅을 용이하게 하기 위해 RuntimeException에 SQLException의 메시지를 포함하는 것을 고려해주세요.

-            throw new RuntimeException("Failed to extract database name", e);
+            throw new RuntimeException("Failed to extract database name: " + e.getMessage(), e);

59-64: SQL 인젝션 위험을 방어적으로 처리해주세요.

databaseName이 SQL 쿼리에 직접 포맷팅되고 있습니다. 비록 테스트 환경이고 데이터베이스 이름이 Connection.getCatalog()에서 가져온 값이지만, 방어적 코딩을 위해 데이터베이스 이름 검증이나 식별자 이스케이핑을 추가하는 것을 권장합니다.

다음과 같이 데이터베이스 이름을 검증하는 로직을 추가할 수 있습니다:

extractDatabaseName 메서드에 검증 로직 추가:

 private void extractDatabaseName(ApplicationContext context) {
     DataSource dataSource = context.getBean(DataSource.class);
     try (Connection conn = dataSource.getConnection()) {
         databaseName = conn.getCatalog();
+        if (databaseName == null || !databaseName.matches("^[a-zA-Z0-9_]+$")) {
+            throw new IllegalStateException("Invalid database name: " + databaseName);
+        }
     } catch (SQLException e) {
         throw new RuntimeException("Failed to extract database name", e);
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 206dbc0 and ddddb7a.

📒 Files selected for processing (7)
  • domain-member/src/main/resources/application-test.yml (1 hunks)
  • domain-member/src/test/java/com/wellmeet/domain/member/DataBaseCleaner.java (3 hunks)
  • domain-owner/src/main/resources/application-test.yml (1 hunks)
  • domain-owner/src/test/java/com/wellmeet/domain/owner/DataBaseCleaner.java (3 hunks)
  • domain-reservation/src/test/java/com/wellmeet/DataBaseCleaner.java (3 hunks)
  • domain-restaurant/src/main/resources/application-test.yml (1 hunks)
  • domain-restaurant/src/test/java/com/wellmeet/DataBaseCleaner.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • domain-owner/src/main/resources/application-test.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
🔇 Additional comments (18)
domain-member/src/main/resources/application-test.yml (4)

11-18: 테스트 환경 JPA 설정 검증

JPA 설정이 테스트 환경에 적합하게 구성되어 있습니다:

  • ddl-auto: create-drop: 각 테스트마다 깨끗한 상태 보장
  • show-sql: trueformat_sql: true: 테스트 로그 추적에 유용
  • open-in-view: false: 테스트 환경에서 권장

20-21: Flyway 설정 확인

테스트 환경에서 Flyway가 비활성화되어 있습니다. 이는 Hibernate ddl-auto: create-drop 설정과 함께 동작하므로 적절합니다. 다만, 본 코드베이스에서 Flyway와 Hibernate DDL 전략 간의 충돌 가능성이 있는지 확인하시기 바랍니다.


26-30: 테스트 환경 Eureka 클라이언트 설정 적절

Eureka가 비활성화되어 있으므로 테스트 환경에서 서비스 디스커버리에 의존하지 않습니다. 테스트 환경 설정으로는 적절합니다.


32-39: 관리 엔드포인트 노출 설정 검토

health, info, metrics 엔드포인트가 노출되어 있으며, health 세부 정보가 항상 표시되도록 설정되어 있습니다. 테스트 환경에는 적절하지만, 이러한 엔드포인트가 개발 또는 스테이징 환경에도 동일하게 노출되고 있는지 확인하시기 바랍니다.

domain-restaurant/src/main/resources/application-test.yml (2)

1-45: 전반적인 테스트 설정 구조는 적절합니다

나머지 설정들은 테스트 환경에 맞게 잘 구성되어 있습니다:

  • Eureka 클라이언트 비활성화 (테스트 환경에서 불필요)
  • JPA ddl-auto: create-drop (각 테스트 실행 시 스키마 신규 생성)
  • Flyway 비활성화
  • Jackson 시간대를 Asia/Seoul로 일관성 있게 설정
  • Management 엔드포인트 적절히 노출

위의 보안 이슈가 해결되면 승인 가능합니다.


7-7: 포트 3309는 의도된 설정 - 재검토 불필요

docker-compose.yml에 명시적으로 문서화되어 있습니다. 각 마이크로서비스는 독립적인 MySQL 인스턴스를 고유한 호스트 포트로 운영하도록 설계되었습니다 (mysql-restaurant: 3309, mysql-member: 3307, mysql-owner: 3308, mysql-reservation: 3310). 주석 "# MySQL - Domain별 분리(Domain Separation)"는 이 패턴의 의도를 명확히 합니다. application-test.yml의 포트 3309 설정은 이 아키텍처와 올바르게 정렬되어 있으며, 주석 추가나 포트 변경은 불필요합니다.

Likely an incorrect or invalid review comment.

domain-restaurant/src/test/java/com/wellmeet/DataBaseCleaner.java (3)

4-7: LGTM!

새로운 데이터베이스 이름 추출 기능을 위해 필요한 임포트가 올바르게 추가되었습니다.


16-16: LGTM!

데이터베이스 이름을 캐싱하는 지연 초기화 패턴이 적절하게 구현되었습니다. 여러 테스트 메서드에서 불필요한 반복 조회를 방지합니다.

Also applies to: 22-24


29-36: 리소스 관리가 적절합니다.

try-with-resources를 사용하여 Connection이 올바르게 닫히도록 보장했습니다. 테스트 유틸리티에서 SQLException을 RuntimeException으로 래핑하는 것도 적절합니다.

참고: MySQL에서는 catalog와 schema가 동일하지만, 다른 데이터베이스 시스템에서는 다를 수 있습니다. 현재 MySQL을 사용하는 것으로 보이므로 문제없습니다.

domain-reservation/src/test/java/com/wellmeet/DataBaseCleaner.java (4)

4-7: LGTM!

데이터베이스 이름을 동적으로 추출하는 새로운 기능에 필요한 임포트가 적절하게 추가되었습니다.


16-16: LGTM!

데이터베이스 이름을 저장하기 위한 필드가 적절하게 선언되었습니다.


22-24: LGTM!

지연 초기화 패턴이 적절하게 적용되어 반복적인 데이터베이스 이름 추출을 방지합니다. JUnit 확장 라이프사이클 내에서 안전하게 작동합니다.


59-64: 하드코딩된 스키마 이름 제거는 좋은 개선입니다

하드코딩된 'test' 스키마 대신 동적으로 추출된 데이터베이스 이름을 사용하는 것은 테스트 유연성을 향상시킵니다. extractDatabaseName에서 null 체크가 적절히 추가되면 이 구현은 안전하게 작동할 것입니다.

참고: databaseName이 JDBC 연결의 카탈로그에서 가져온 값이므로 SQL 인젝션 위험은 없습니다.

domain-owner/src/test/java/com/wellmeet/domain/owner/DataBaseCleaner.java (2)

29-36: 잘 구현되었습니다!

데이터베이스 이름 추출 로직이 적절합니다:

  • try-with-resources로 Connection 리소스를 안전하게 관리
  • 명확한 에러 메시지로 SQLException을 래핑

16-16: 스레드 안전성 우려는 타당하지 않습니다

검증 결과, DataBaseCleaner@ExtendWith(DataBaseCleaner.class)로 각 테스트 클래스에 등록되어 JUnit 5에 의해 테스트 클래스마다 독립적인 인스턴스가 생성됩니다. databaseName 필드는 각 테스트 메서드의 beforeEach()에서 초기화되므로, 공유되는 정적 필드가 아니며 동시 접근의 위험이 없습니다.

domain-member/src/test/java/com/wellmeet/domain/member/DataBaseCleaner.java (3)

4-7: 필요한 임포트가 올바르게 추가되었습니다.

새로운 데이터베이스 이름 추출 기능을 위해 필요한 임포트들이 적절하게 추가되었습니다.


22-24: 지연 초기화 패턴이 적절합니다.

null 체크를 통한 지연 초기화로 데이터베이스 이름을 한 번만 추출하도록 최적화되어 있습니다.


16-16: 병렬 테스트 실행 설정이 없어 이 우려사항은 해당되지 않습니다.

프로젝트에 junit-platform.properties 파일이 없고, pom.xmlbuild.gradle에서도 병렬 실행 설정을 찾을 수 없습니다. JUnit 5의 기본 동작은 순차 실행이므로, databaseName 필드는 각 테스트 클래스마다 하나의 extension 인스턴스 내에서만 접근됩니다.

현재 구현에서 databaseName은 첫 번째 테스트 메서드에서 한 번만 초기화되고 이후에는 읽기만 수행되므로, 순차 실행 환경에서는 스레드 안전성 문제가 없습니다. 이 필드 설계는 현재 프로젝트의 실행 환경에 적합합니다.

Likely an incorrect or invalid review comment.

Comment on lines +5 to +9
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/wellmeet_member
username: root
password: password
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

데이터베이스 자격증명을 소스 코드에 하드코딩하지 않기

데이터베이스 사용자명과 비밀번호가 소스 코드에 평문으로 노출되어 있습니다. 테스트 환경이더라도 보안 모범 사례에 따라 민감한 자격증명은 환경 변수 또는 외부 설정 시스템을 통해 주입되어야 합니다.

다음과 같이 수정하시기 바랍니다:

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3307/wellmeet_member
-    username: root
-    password: password
+    username: ${DATASOURCE_USERNAME:root}
+    password: ${DATASOURCE_PASSWORD:password}

대안으로, application-test.yml에는 placeholder를 유지하고 테스트 실행 시 @TestPropertySource 또는 환경 변수를 통해 실제 자격증명을 주입하는 방식도 권장됩니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/wellmeet_member
username: root
password: password
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/wellmeet_member
username: ${DATASOURCE_USERNAME:root}
password: ${DATASOURCE_PASSWORD:password}
🤖 Prompt for AI Agents
In domain-member/src/main/resources/application-test.yml around lines 5 to 9,
the datasource username and password are hardcoded in plain text; replace them
with placeholders that read from environment variables or external test
properties (e.g., use ${DB_USERNAME:default} and ${DB_PASSWORD:default} style
placeholders) and remove the literal credentials, and ensure tests inject real
credentials via @TestPropertySource or CI/test environment variables so no
secrets are stored in source.

Comment on lines 59 to 64
String tableNameSelectQuery = String.format("""
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'test'
WHERE TABLE_SCHEMA = '%s'
AND TABLE_TYPE = 'BASE TABLE'
""";
""", databaseName);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

SQL 인젝션 위험 인지 필요

String.format으로 databaseName을 직접 쿼리에 삽입하고 있습니다. databaseNameconn.getCatalog()에서 가져온 값이라 위험은 제한적이지만, 문자열 포매팅 방식은 일반적으로 권장되지 않습니다.

다만 INFORMATION_SCHEMA 쿼리에서 데이터베이스 이름은 파라미터화가 불가능하므로 현재 접근 방식이 불가피합니다. AI 요약에 따르면 다른 모듈에서도 동일한 패턴을 사용하고 있어 일관성은 유지되고 있습니다.

추가 안전장치로 데이터베이스 이름 검증을 고려해보세요:

 private void extractDatabaseName(ApplicationContext context) {
     DataSource dataSource = context.getBean(DataSource.class);
     try (Connection conn = dataSource.getConnection()) {
         databaseName = conn.getCatalog();
+        if (databaseName == null || !databaseName.matches("[a-zA-Z0-9_]+")) {
+            throw new IllegalStateException("Invalid database name: " + databaseName);
+        }
     } catch (SQLException e) {
         throw new RuntimeException("Failed to extract database name", e);
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +29 to +36
private void extractDatabaseName(ApplicationContext context) {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection conn = dataSource.getConnection()) {
databaseName = conn.getCatalog();
} catch (SQLException e) {
throw new RuntimeException("Failed to extract database name", e);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

getCatalog() 반환값에 대한 null 체크 추가 필요

Connection.getCatalog()은 일부 JDBC 드라이버나 데이터베이스 설정에서 null을 반환할 수 있습니다. 만약 null이 반환되면 databaseName 필드가 null로 설정되고, 이후 findTableNames 메서드에서 SQL 쿼리에 문자열 "null"이 삽입되어 테이블을 찾지 못하거나 쿼리가 실패할 수 있습니다.

다음과 같이 null 체크를 추가하는 것을 권장합니다:

 private void extractDatabaseName(ApplicationContext context) {
     DataSource dataSource = context.getBean(DataSource.class);
     try (Connection conn = dataSource.getConnection()) {
         databaseName = conn.getCatalog();
+        if (databaseName == null || databaseName.isBlank()) {
+            throw new RuntimeException("Database catalog name is null or empty");
+        }
     } catch (SQLException e) {
         throw new RuntimeException("Failed to extract database name", e);
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void extractDatabaseName(ApplicationContext context) {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection conn = dataSource.getConnection()) {
databaseName = conn.getCatalog();
} catch (SQLException e) {
throw new RuntimeException("Failed to extract database name", e);
}
}
private void extractDatabaseName(ApplicationContext context) {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection conn = dataSource.getConnection()) {
databaseName = conn.getCatalog();
if (databaseName == null || databaseName.isBlank()) {
throw new RuntimeException("Database catalog name is null or empty");
}
} catch (SQLException e) {
throw new RuntimeException("Failed to extract database name", e);
}
}
🤖 Prompt for AI Agents
In domain-reservation/src/test/java/com/wellmeet/DataBaseCleaner.java around
lines 29 to 36, add a null check after calling conn.getCatalog(): if
getCatalog() returns null, attempt to obtain a fallback (e.g., conn.getSchema()
or DatabaseMetaData.getURL()/getUserName as appropriate) and use that as
databaseName; if no valid name can be resolved, throw a RuntimeException with a
clear message so downstream SQL won't receive the string "null". Ensure the
chosen value is trimmed and non-empty before assigning to databaseName.

Comment on lines +8 to +9
username: root
password: password
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

테스트 환경에서도 데이터베이스 자격증명을 하드코딩하지 말 것

테스트 설정에 평문으로 작성된 데이터베이스 자격증명(username: root, password: password)은 버전 관리 시스템에 저장되면 안 됩니다. 이는 보안 위험입니다.

권장사항:

  • 환경 변수(${DB_USERNAME}, ${DB_PASSWORD}) 사용
  • 또는 TestContainers 같은 테스트 컨테이너 활용
  • 또는 @TestPropertySource 또는 @DynamicPropertySource로 테스트 클래스에서 정의
datasource:
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3309/wellmeet_restaurant
  username: ${DB_USERNAME:root}
  password: ${DB_PASSWORD:password}
🤖 Prompt for AI Agents
In domain-restaurant/src/main/resources/application-test.yml around lines 8 to
9, the test config hardcodes DB credentials (username: root, password:
password); replace these with environment-backed properties and/or test-specific
overrides. Update the YAML to read username and password from env vars (e.g.,
${DB_USERNAME} and ${DB_PASSWORD} with optional safe defaults) or remove
credentials and switch the tests to use TestContainers or define credentials via
@TestPropertySource / @DynamicPropertySource in test classes so no plaintext
secrets are committed.

Comment on lines +59 to +64
String tableNameSelectQuery = String.format("""
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'test'
WHERE TABLE_SCHEMA = '%s'
AND TABLE_TYPE = 'BASE TABLE'
""";
""", databaseName);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

데이터베이스 이름에 대한 검증을 추가하세요.

databaseName이 검증 없이 SQL 쿼리에 직접 포맷되고 있습니다. 테스트 코드이고 DataSource가 신뢰할 수 있는 소스이긴 하지만, 보안 모범 사례를 위해 데이터베이스 이름이 유효한 식별자 문자만 포함하는지 검증하는 것이 좋습니다.

다음 diff를 적용하여 검증을 추가할 수 있습니다:

 private void extractDatabaseName(ApplicationContext context) {
     DataSource dataSource = context.getBean(DataSource.class);
     try (Connection conn = dataSource.getConnection()) {
         databaseName = conn.getCatalog();
+        if (databaseName == null || !databaseName.matches("^[a-zA-Z0-9_]+$")) {
+            throw new IllegalStateException("Invalid database name: " + databaseName);
+        }
     } catch (SQLException e) {
         throw new RuntimeException("Failed to extract database name", e);
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In domain-restaurant/src/test/java/com/wellmeet/DataBaseCleaner.java around
lines 59 to 64, databaseName is inserted directly into a SQL string without
validation; add a check that databaseName is non-null, non-empty, trimmed, and
matches a safe identifier pattern (e.g. only letters, digits and underscores,
optionally enforce a max length), and throw an IllegalArgumentException if it
fails; after validation you can keep using String.format to build the query, or
alternatively escape/reject unsafe values — do not attempt to parameterize the
schema identifier via PreparedStatement.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
.github/workflows/Dev_Domain_Reservation_CD.yml (1)

6-7: ** 임시 피처 브랜치 제거 필요**

feat/#71 브랜치를 트리거 조건에서 제거하세요. 이는 PR 병합 후 의도하지 않은 배포를 방지하기 위함입니다.

  on:
    push:
      branches:
        - "develop"
-       - "feat/#71"
.github/workflows/Dev_Domain_Restaurant_CD.yml (1)

6-7: ** 임시 피처 브랜치 제거 필요**

feat/#71 브랜치를 트리거 조건에서 제거하세요.

  on:
    push:
      branches:
        - "develop"
-       - "feat/#71"
.github/workflows/Dev_Domain_Member_CD.yml (1)

6-7: ** 임시 피처 브랜치 제거 필요**

feat/#71 브랜치를 트리거 조건에서 제거하세요.

  on:
    push:
      branches:
        - "develop"
-       - "feat/#71"
.github/workflows/Dev_Domain_Owner_CD.yml (1)

6-7: ** 임시 피처 브랜치 제거 필요**

feat/#71 브랜치를 트리거 조건에서 제거하세요.

  on:
    push:
      branches:
        - "develop"
-       - "feat/#71"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ddddb7a and 393072a.

📒 Files selected for processing (6)
  • .github/workflows/Dev_API_Owner_CD.yml (2 hunks)
  • .github/workflows/Dev_API_User_CD.yml (2 hunks)
  • .github/workflows/Dev_Domain_Member_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Owner_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Reservation_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Restaurant_CD.yml (1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.8)
.github/workflows/Dev_Domain_Owner_CD.yml

69-69: label "dev-domain-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Reservation_CD.yml

69-69: label "dev-domain-reservation" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_API_Owner_CD.yml

70-70: label "dev-api-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_API_User_CD.yml

70-70: label "dev-api-user" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Member_CD.yml

69-69: label "dev-domain-member" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Restaurant_CD.yml

69-69: label "dev-domain-restaurant" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
🔇 Additional comments (8)
.github/workflows/Dev_API_Owner_CD.yml (1)

70-70: 커스텀 러너 라벨이 actionlint에서 경고로 표시됨

dev-api-owner 라벨이 표준 GitHub Actions 라벨로 인식되지 않습니다. 이는 자체 호스팅 러너에 할당된 커스텀 라벨일 수 있습니다. 해당 러너가 실제로 이 라벨로 구성되었는지 확인하고, 필요시 actionlint 설정 파일에 커스텀 라벨을 등록하세요.

.github/workflows/Dev_Domain_Reservation_CD.yml (1)

69-69: 커스텀 러너 라벨 확인 필요

dev-domain-reservation 라벨이 자체 호스팅 러너에 올바르게 구성되었는지 확인하세요.

.github/workflows/Dev_Domain_Restaurant_CD.yml (1)

69-69: 커스텀 러너 라벨 확인 필요

dev-domain-restaurant 라벨이 자체 호스팅 러너에 올바르게 구성되었는지 확인하세요.

.github/workflows/Dev_Domain_Member_CD.yml (1)

69-69: 커스텀 러너 라벨 확인 필요

dev-domain-member 라벨이 자체 호스팅 러너에 올바르게 구성되었는지 확인하세요.

.github/workflows/Dev_Domain_Owner_CD.yml (1)

69-69: 커스텀 러너 라벨 확인 필요

dev-domain-owner 라벨이 자체 호스팅 러너에 올바르게 구성되었는지 확인하세요.

.github/workflows/Dev_API_User_CD.yml (3)

10-12: 새로운 경로 필터가 BFF 전환 범위와 일치하는지 확인하세요.

새로운 경로 필터들('common-client/', 'infra-redis/', 'infra-kafka/**')이 추가되었습니다. 이는 api-user 서비스가 이제 이러한 모듈들에 의존하고 있음을 나타냅니다.

몇 가지 확인이 필요합니다:

  • 이 필터들이 실제로 필요한 모든 의존성을 포함하는지 확인하세요.
  • 다른 모듈(예: domain 관련 모듈)에 대한 필터는 제거되었는지 확인하세요 - 이것이 의도된 것인지 검증하세요.

7-7: feat/#71 브랜치 트리거가 적절한지 확인하세요.

개발 브랜치 외에 'feat/#71' 피처 브랜치에서도 이 워크플로우가 실행되도록 추가되었습니다. 이는 개발 중에 유용하지만, PR이 병합된 후 이 트리거를 제거하거나 조정할 계획이 있는지 확인하세요.


70-70: dev-api-user 커스텀 러너의 설정 상태를 확인하세요.

Line 70의 runs-on: dev-api-user는 커스텀 자체 호스팅 러너 레이블입니다. 이 워크플로우가 정상 작동하려면 GitHub 리포지토리의 Settings > Runners에서 'dev-api-user' 레이블로 등록된 자체 호스팅 러너가 실제로 존재하고 정상 상태인지 확인이 필요합니다.

참고: 리포지토리 내에 actionlint 설정 파일이 발견되지 않았으므로, 원본 리뷰 의견의 actionlint 관련 세부사항은 검증할 수 없습니다. 핵심은 커스텀 러너 설정이 제대로 구성되어 있는지 확인하는 것입니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
domain-owner/src/main/resources/db/migration/V1__init_schema.sql (1)

1-12: 이전 리뷰에서 지적된 문자 집합 명시 문제가 여전히 해결되지 않음

한국어를 포함한 다국어 텍스트를 안정적으로 지원하기 위해, 테이블 수준에서 명시적으로 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci를 지정해야 합니다. 현재 설정에서는 한글 문자가 제대로 저장되지 않을 수 있으며, 데이터베이스 기본 설정에 의존하는 것은 이식성 및 일관성 문제를 야기할 수 있습니다.

다음과 같이 수정하세요:

-- Owner 테이블
 CREATE TABLE owner
 (
     id                  VARCHAR(255) PRIMARY KEY,
     name                VARCHAR(255) NOT NULL,
     email               VARCHAR(255) NOT NULL,
     reservation_enabled BOOLEAN      NOT NULL DEFAULT TRUE,
     review_enabled      BOOLEAN      NOT NULL DEFAULT TRUE,
     created_at          DATETIME(6) NOT NULL,
     updated_at          DATETIME(6) NOT NULL,
     UNIQUE KEY unique_email (email)
-);
+) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
domain-member/src/main/resources/db/migration/V1__init_schema.sql (1)

1-31: 문자 집합 및 이메일 필드 길이 지정 필요 (이전 검토 피드백 미적용)

이전 리뷰에서 지적했던 두 가지 사항이 여전히 해결되지 않았습니다:

  1. CHARACTER SET 및 COLLATE 미지정: 한글 데이터를 올바르게 지원하려면 테이블 정의 또는 파일 상단에서 utf8mb4 charset을 명시적으로 지정해야 합니다. 현재 설정이 없으면 데이터베이스 기본값에 의존하여 환경 간 불일치가 발생할 수 있습니다.

  2. 이메일 필드 길이: RFC 5321 표준에 따라 이메일 주소의 최대 길이는 254자입니다. 현재 VARCHAR(255) (7번 줄)를 VARCHAR(254)로 수정해야 합니다.

다음과 같이 수정하세요:

+ -- 파일 상단에 문자 집합 설정 추가
+ SET NAMES utf8mb4;
+ SET CHARACTER_SET_CLIENT = utf8mb4;
+ SET CHARACTER_SET_CONNECTION = utf8mb4;
+
  -- Member 테이블
  CREATE TABLE member
  (
      id                  VARCHAR(255) PRIMARY KEY,
      name                VARCHAR(255) NOT NULL,
      nickname            VARCHAR(255) NOT NULL,
-     email               VARCHAR(255) NOT NULL,
+     email               VARCHAR(254) NOT NULL,
      phone               VARCHAR(255) NOT NULL,
      reservation_enabled BOOLEAN      NOT NULL DEFAULT TRUE,
      remind_enabled      BOOLEAN      NOT NULL DEFAULT TRUE,
      review_enabled      BOOLEAN      NOT NULL DEFAULT TRUE,
      is_vip              BOOLEAN      NOT NULL DEFAULT FALSE,
      created_at          DATETIME(6) NOT NULL,
      updated_at          DATETIME(6) NOT NULL,
      UNIQUE KEY unique_email (email),
      UNIQUE KEY unique_phone (phone)
- );
+ ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

또는 테이블 정의에서 charset/collation을 추가하되 파일 상단의 SET 문은 생략해도 괜찮습니다. 선택한 방식을 일관되게 적용하세요.

🧹 Nitpick comments (3)
domain-restaurant/src/main/resources/db/migration/V1__init_schema.sql (3)

1-18: 마이크로서비스 아키텍처 일관성 확인 필요: owner_id 논리적 참조의 데이터 무결성 보장 방식 명시

owner_id를 논리적 참조로 설정하여 물리적 외래키 제약을 제거했으나, 애플리케이션 계층에서 이를 어떻게 관리할지 명확하지 않습니다. 특히 domain-owner 서비스에서 소유자가 삭제될 때 고아 레코드(orphaned records) 방지 메커니즘이 필요합니다.

또한 thumbnail 필드에 대한 데이터 검증이 없습니다(예: URL 형식, NULL 허용성). 필요한 경우 CHECK 제약을 추가하거나 애플리케이션 단계에서 검증을 강화하시기 바랍니다.

domain-owner와의 참조 무결성 관리 방식(예: 비동기 이벤트 처리, 정기적 검증 배치)을 아키텍처 문서에 명시하실 수 있을까요?


36-52: 영업 시간 필드 간 논리적 검증 부재

time 필드들 간의 논리적 제약이 없어 다음과 같은 부정확한 데이터가 저장될 수 있습니다:

  • open_time >= close_time
  • break_start_time >= break_end_time
  • break 시간이 영업 시간 범위를 벗어남

데이터베이스 수준에서 이를 완벽히 제어하기는 어렵지만, 최소한 애플리케이션 레이어에서 엄격한 검증을 강화하고, 마이그레이션 후 기존 데이터 검증 배치를 실행할 것을 권장합니다.

또한 break_start_time과 break_end_time이 모두 NULL이거나 하나만 NULL인 경우에 대한 정책(양쪽 모두 존재하거나 모두 NULL)을 명시하실 수 있을까요?


68-85: 마이크로서비스 아키텍처: 참조 무결성 전략의 혼합 사용 명확화 필요

review 테이블에서:

  • restaurant_id: 물리적 외래키 제약 (ON DELETE CASCADE)
  • member_id: 논리적 참조 (FK 제약 없음)

이러한 불일치가 의도된 설계인지 확인이 필요합니다. 만약 의도라면:

  1. domain-restaurant: 같은 서비스 내 테이블이므로 물리적 FK 사용
  2. domain-member: 별도 마이크로서비스이므로 논리적 참조 사용

이러한 구분이 타당하다면, 향후 모든 테이블 설계에서 이를 명시적으로 문서화하여 일관성을 유지해야 합니다.

또한 restaurant_id에 ON DELETE CASCADE를 사용하면 식당 삭제 시 모든 리뷰가 함께 삭제되는데, 리뷰 데이터의 보존 필요성(감사 추적, 통계 등)을 고려하여 이 정책의 적절성을 재검토해주시기 바랍니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 393072a and e3dc8dd.

📒 Files selected for processing (3)
  • domain-member/src/main/resources/db/migration/V1__init_schema.sql (1 hunks)
  • domain-owner/src/main/resources/db/migration/V1__init_schema.sql (1 hunks)
  • domain-restaurant/src/main/resources/db/migration/V1__init_schema.sql (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-25T08:56:15.148Z
Learnt from: ezcolin2
Repo: Team-soft-macaron/WellMeet-Backend PR: 4
File: src/main/java/com/wellmeet/restaurant/domain/crawlingreview/domain/Atmosphere.java:15-16
Timestamp: 2025-06-25T08:56:15.148Z
Learning: CrawlingReview와 Restaurant 엔티티는 외부에서 수집한 식별자를 직접 사용하므로 GeneratedValue 없이 수동으로 ID를 설정하는 것이 적합하다.

Applied to files:

  • domain-restaurant/src/main/resources/db/migration/V1__init_schema.sql
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
🔇 Additional comments (1)
domain-restaurant/src/main/resources/db/migration/V1__init_schema.sql (1)

20-34: max_capacity 검증 부재는 유효하나, 아키텍처 우려는 오류입니다

max_capacity 검증 문제는 실제: max_capacity 필드에 CHECK 제약이 없어 0 이하의 값이 저장될 수 있습니다. 다만 이 문제는 price, available_date, available_time 등 다른 numeric 필드들에도 동일하게 적용되므로, 전체 스키마의 데이터 검증 일관성을 개선하는 것이 더 적절합니다.

아키텍처 우려는 오류: restaurant 테이블은 owner_id(다른 도메인)에만 논리적 참조를 유지하며, available_date는 같은 도메인 내 테이블이므로 물리적 FK 사용이 설계상 정상입니다. 스키마 주석에서도 이를 명시하고 있습니다. 따라서 아키텍처 혼합 우려는 근거가 없습니다.

권장사항:

  • max_capacity > 0 외에도 price > 0, 시간 필드 범위 검증 등을 일관성 있게 추가 검토
  • 아키텍처 관련 우려사항은 제거

Likely an incorrect or invalid review comment.

Comment on lines +54 to +66
-- Menu 테이블
CREATE TABLE menu
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
price INT NOT NULL,
restaurant_id VARCHAR(255) NOT NULL,
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NOT NULL,
CONSTRAINT fk_menu_restaurant FOREIGN KEY (restaurant_id) REFERENCES restaurant (id) ON DELETE CASCADE,
INDEX idx_menu_restaurant (restaurant_id)
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

가격(price) 필드 타입 및 검증 부재

price 필드가 INT로 정의되어 있으나 다음과 같은 문제가 있습니다:

  1. 통화 정밀도: 화폐 데이터는 DECIMAL(10, 2) 또는 NUMERIC(10, 2)를 사용하여 부동소수점 연산 오류를 방지해야 합니다.
  2. 음수 방지: CHECK 제약으로 price > 0 또는 price >= 0를 명시해야 합니다.

다음과 같이 수정을 권장합니다:

price DECIMAL(10, 2) NOT NULL,
...
CHECK (price >= 0)

또한 description은 현재 NULL을 허용하는데, 메뉴 설명이 필수인지 선택인지 비즈니스 요구사항을 명확히 하여 NOT NULL 제약 추가 여부를 결정해야 합니다.

🤖 Prompt for AI Agents
In domain-restaurant/src/main/resources/db/migration/V1__init_schema.sql around
lines 54 to 66, the menu.price column is currently INT and lacks validation;
change its type to a fixed-point monetary type (e.g., DECIMAL(10,2) or
NUMERIC(10,2)) and add a CHECK constraint to prevent negative values (e.g.,
CHECK (price >= 0)); also review whether menu.description should be nullable per
business rules and, if description must be present, add NOT NULL to that column
(or leave as-is if optional).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3dc8dd and 5648e75.

📒 Files selected for processing (3)
  • docker-compose.yml (2 hunks)
  • domain-restaurant/src/main/resources/db/migration/V1__init_schema.sql (1 hunks)
  • infra-kafka/docker/docker-compose.yml (0 hunks)
💤 Files with no reviewable changes (1)
  • infra-kafka/docker/docker-compose.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • domain-restaurant/src/main/resources/db/migration/V1__init_schema.sql
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
  • GitHub Check: test / build-and-push
🔇 Additional comments (2)
docker-compose.yml (2)

69-69: 좋은 개선: Zookeeper 명시적 설정

hostname 추가와 ZOOKEEPER_CLIENT_PORT, ZOOKEEPER_TICK_TIME 환경 변수 명시는 Docker 환경에서 DNS 해석과 설정 명확성을 개선합니다.

Also applies to: 74-75


12-12: 검토 코멘트 부적절 - 포트 설정이 이미 일관되게 반영됨

mysql-reservation 포트 3310은 이미 domain-reservation/src/main/resources/application-local.ymlapplication-test.yml에서 일관되게 설정되어 있습니다. docker-compose.yml의 변경사항이 모든 관련 설정 파일과 완벽히 일치하므로 추가 검증이 필요하지 않습니다.

Likely an incorrect or invalid review comment.


kafka:
image: confluentinc/cp-kafka:7.5.0
hostname: kafka
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Kafka 이중 리스너 설정 검증 필요

Kafka 리스너 설정이 크게 변경되었습니다:

  • 기존: PLAINTEXT://kafka:9092
  • 변경: 내부(29092) + 외부(9092) 이중 리스너 설정

이 변경은 Docker 내부/외부 통신 분리를 위한 것으로 보이나, 클라이언트 구성이 새로운 설정과 호환되는지 확인이 필요합니다.

Also applies to: 86-86, 90-95


🏁 Script executed:

#!/bin/bash
# Description: Kafka 클라이언트 구성에서 PLAINTEXT_HOST://localhost:9092 호환성 확인

# Kafka 부트스트랩 주소 참조 검색
rg -n "bootstrap.servers|KAFKA_BROKERS|kafka.*9092|kafka.*29092" --type=yaml --type=properties --type=java -A 2 -B 2

# Spring Cloud 스트림 또는 Kafka 클라이언트 설정 확인
rg -n "kafka:|spring.kafka" --type=yaml --type=properties -A 5

Length of output: 69


추가 검증을 위해 더 나은 스크립트로 다시 검색하겠습니다.

[/run_scripts]


🏁 Script executed:

#!/bin/bash
# Kafka 클라이언트 설정 검색 (파일타입 지정 제거)

echo "=== docker-compose.yml Kafka 설정 확인 ==="
rg -n "kafka|KAFKA" docker-compose.yml -A 3 -B 1

echo -e "\n=== Kafka 부트스트랩/브로커 설정 검색 ==="
rg -n "bootstrap\.servers|KAFKA_BROKERS|localhost:9092|kafka:9092|kafka:29092" -A 2 -B 2

echo -e "\n=== Spring Kafka 설정 파일 검색 ==="
fd -e yml -e yaml -e properties | head -20

echo -e "\n=== application 설정 파일에서 Kafka 관련 설정 검색 ==="
rg -n "spring.kafka|kafka:" -A 5

Length of output: 8168


Docker Compose 환경에서 다른 서비스들의 Kafka 설정을 더 확인하겠습니다.

[/run_scripts]


🏁 Script executed:

#!/bin/bash
# 각 모듈의 Kafka 부트스트랩 설정 확인

echo "=== 모든 application 설정에서 kafka bootstrap-servers 검색 ==="
fd -e yml -e yaml | xargs rg -n "bootstrap-servers:|bootstrap.servers" -A 1 | grep -E "^\S+|bootstrap"

echo -e "\n=== 도커 환경 외부/내부 통신 설정 확인 ==="
find . -name "*.yml" -o -name "*.yaml" | xargs rg -n "kafka:|localhost:9092|kafka:29092" -B 1 -A 1 | head -50

Length of output: 2631


Docker Compose 내 다른 서비스들의 환경 설정을 확인하겠습니다.

[/run_scripts]


🏁 Script executed:

#!/bin/bash
# Docker Compose의 다른 서비스들이 어떤 환경에서 실행되는지 확인

echo "=== Docker Compose 서비스 목록 ==="
rg -n "services:|^  [a-z]" docker-compose.yml | head -30

echo -e "\n=== Docker Compose 내 SPRING_PROFILES_ACTIVE 설정 ==="
rg -n "SPRING_PROFILES_ACTIVE" docker-compose.yml -B 2 -A 2

echo -e "\n=== 실제 Docker 실행 시 로컬 프로필 사용 여부 확인 ==="
rg -n "application-infra-kafka-local|application-local" . -A 2 -B 1 | head -40

Length of output: 2170


각 서비스의 Docker 프로필 Kafka 설정을 확인하겠습니다.

[/run_scripts]


🏁 Script executed:

#!/bin/bash
# Docker 실행 시 사용되는 프로필별 Kafka 설정 확인

echo "=== application-docker.yml 파일들의 Kafka 설정 ==="
find . -name "application-docker.yml" -o -name "application-docker*.yml" | xargs cat 2>/dev/null | grep -A 5 "kafka:" | head -30

echo -e "\n=== 각 서비스의 application-local.yml 내용 (Kafka 설정 부분) ==="
rg -n "kafka:" api-owner/src/main/resources/application-local.yml -A 8
rg -n "kafka:" api-user/src/main/resources/application-local.yml -A 8

echo -e "\n=== Docker 환경에서 실제 사용될 Kafka 호스트 검증 ==="
rg -n "localhost|docker|kafka" docker-compose.yml | grep -E "(restaurant-service|member-service|owner-service|SPRING_PROFILES|JAVA_OPTS)" -A 2 -B 2

Length of output: 395


각 서비스의 실제 Kafka 사용 여부를 확인하겠습니다.

[/run_scripts]


🏁 Script executed:

#!/bin/bash
# 각 서비스가 Kafka를 실제로 사용하는지 확인

echo "=== 각 서비스의 build.gradle에서 spring-kafka 의존성 ==="
for dir in api-owner api-user api-restaurant discovery-server domain-*; do
  if [ -f "$dir/build.gradle" ]; then
    kafka_dep=$(grep -c "spring-kafka" "$dir/build.gradle" 2>/dev/null || echo 0)
    if [ "$kafka_dep" -gt 0 ]; then
      echo "$dir: spring-kafka 사용"
    fi
  fi
done

echo -e "\n=== Kafka 클라이언트 코드 (Producer/Consumer) 검색 ==="
rg -l "@KafkaListener|@EnableKafka|KafkaTemplate|@SendTo" --type=java | head -20

echo -e "\n=== discovery-server가 Kafka를 사용하는지 ==="
grep -r "kafka" discovery-server/src/main/resources/ 2>/dev/null || echo "discovery-server는 Kafka 설정 없음"

echo -e "\n=== restaurant-service, member-service, owner-service의 Kafka 사용 여부 ==="
for svc in restaurant member owner; do
  echo "--- $svc-service ---"
  rg -n "kafka:" api-${svc}/src/main/resources/ 2>/dev/null | head -3
done

Length of output: 895


Docker Compose에서 Kafka 클라이언트 호환성 문제 확인됨 - 수정 필요

Kafka 리스너 설정이 내부(29092, PLAINTEXT://kafka:29092) + 외부(9092, PLAINTEXT_HOST://localhost:9092) 이중 구성으로 변경되었습니다.

검증 결과, 실제 호환성 문제가 있습니다:

  • infra-kafka 모듈의 클라이언트 설정이 bootstrap-servers: localhost:9092로 지정되어 있습니다
  • Docker Compose 내 서비스들이 SPRING_PROFILES_ACTIVE: local로 실행되어 application-infra-kafka-local.yml을 로드합니다
  • Docker 컨테이너 환경에서 localhost:9092는 컨테이너 내부를 가리키므로 Kafka 접근 실패 위험이 있습니다
  • Docker 네트워크 내 통신은 kafka:29092 (내부 리스너)를 사용해야 합니다

필요한 수정:

  1. infra-kafka/src/main/resources/application-infra-kafka-docker.yml 생성하여 bootstrap-servers: kafka:29092 설정
  2. Docker Compose의 서비스들을 SPRING_PROFILES_ACTIVE: docker 또는 새로운 복합 프로필로 변경
  3. 또는 application-infra-kafka-local.yml을 Docker 환경 인식 가능하도록 수정
🤖 Prompt for AI Agents
In docker-compose.yml around line 81, the Kafka hostname/ports are set for dual
listeners but containers using the local Spring profile still point to
bootstrap-servers: localhost:9092 which fails inside Docker; fix by creating
infra-kafka/src/main/resources/application-infra-kafka-docker.yml that sets
bootstrap-servers: kafka:29092, update docker-compose services to set
SPRING_PROFILES_ACTIVE=docker (or a combined profile that includes the docker
kafka config), or alternatively modify application-infra-kafka-local.yml to
detect Docker and use kafka:29092 when running in containers so services use the
container-accessible kafka:29092 instead of localhost:9092.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
domain-owner/src/test/java/com/wellmeet/DataBaseCleaner.java (1)

4-36: 동적 데이터베이스 이름 추출 접근 방식 승인, 코드 중복 해결 필요

런타임 데이터베이스 이름을 동적으로 추출하는 로직이 올바르게 구현되었습니다. Try-with-resources 사용과 예외 처리가 적절합니다.

그러나 이 코드가 domain-member/src/test/java/com/wellmeet/DataBaseCleaner.java와 동일하게 중복되어 있습니다. AI 요약에 따르면 domain-reservation 모듈에도 유사한 패턴이 있습니다.

공통 테스트 유틸리티 모듈로 추출하여 중복을 제거하세요:

// common-test/src/main/java/com/wellmeet/test/DataBaseCleaner.java
package com.wellmeet.test;

// 단일 구현을 여기에 배치하고 모든 도메인 모듈에서 재사용

각 도메인 모듈의 테스트 의존성에 추가:

testImplementation project(':common-test')
♻️ Duplicate comments (10)
.github/workflows/Dev_API_User_CD.yml (1)

69-69: 커스텀 러너 라벨 구성 필요 (전체 워크플로우 검토 필요)

dev-api-user 러너 라벨도 마찬가지로 GitHub Actions 표준 라벨이 아닙니다. .github/actionlint.yaml에 등록되어 있거나 GitHub 조직 설정에서 구성되어야 합니다.

.github/workflows/Dev_Discovery_CD.yml (1)

62-62: 커스텀 러너 라벨 구성 필요 (전체 워크플로우 검토 필요)

dev-discovery-server 러너 라벨이 GitHub Actions 표준 라벨이 아닙니다. 자체 호스팅 러너 구성이 필요합니다.

.github/workflows/Dev_Domain_Reservation_CD.yml (1)

68-68: 커스텀 러너 라벨 구성 필요 (전체 워크플로우 검토 필요)

dev-domain-reservation 러너 라벨이 GitHub Actions 표준 라벨이 아닙니다.

.github/workflows/Dev_Domain_Restaurant_CD.yml (2)

68-68: 커스텀 러너 라벨 구성 필요 (전체 워크플로우 검토 필요)

dev-domain-restaurant 러너 라벨이 GitHub Actions 표준 라벨이 아닙니다.


84-84: 배포 스크립트 실행 시 권한 관리 검증

sudo sh 명령으로 배포 스크립트를 실행합니다.

.github/workflows/Dev_Domain_Owner_CD.yml (2)

68-68: 커스텀 러너 라벨 구성 필요 (전체 워크플로우 검토 필요)

dev-domain-owner 러너 라벨이 GitHub Actions 표준 라벨이 아닙니다.


84-84: 배포 스크립트 실행 시 권한 관리 검증

sudo sh 명령으로 배포 스크립트를 실행합니다.

.github/workflows/Dev_Domain_Member_CD.yml (2)

68-68: 커스텀 러너 라벨 구성 필요 (전체 워크플로우 검토 필요)

dev-domain-member 러너 라벨이 GitHub Actions 표준 라벨이 아닙니다.


84-84: 배포 스크립트 실행 시 권한 관리 검증

sudo sh 명령으로 배포 스크립트를 실행합니다.

domain-member/src/test/java/com/wellmeet/DataBaseCleaner.java (1)

4-36: 동일한 코드 중복 문제

이 파일은 domain-owner/src/test/java/com/wellmeet/DataBaseCleaner.java와 동일한 코드입니다. 구현 자체는 올바르지만, 앞서 리뷰한 파일에서 언급한 것처럼 공통 테스트 모듈로 추출해야 합니다.

🧹 Nitpick comments (9)
domain-member/src/main/resources/application-flyway.yml (1)

1-10: Flyway 설정이 올바르게 구성되었습니다.

ddl-auto: validatebaseline-on-migrate: true 설정이 스키마 검증에 적절합니다. 다만 이 설정이 4개 도메인 모듈에 동일하게 반복되고 있습니다.

선택적으로, 공통 설정을 application-flyway.yml 파일로 추출하여 중복을 제거할 수 있습니다.

claudedocs/microservices-migration-plan.md (1)

110-110: 마크다운 강조 형식 수정 필요 (MD037)

라인 110, 149, 227, 458에서 마크다운린트 경고가 발생했습니다. 강조 마커(**) 내부의 공백 문제로 보입니다.

예시:

# 수정 전
-**Phase 5 BFF 전환 완료로 domain-* 모듈의 독립 실행은 선택사항

# 수정 후
-**Phase 5 BFF 전환 완료로** domain-* 모듈의 독립 실행은 선택사항

Also applies to: 149-149, 227-227, 458-458

claudedocs/guides/bff-transaction-strategy.md (1)

46-46: 펜스 코드 블록에 언어 지정 필요 (MD040)

라인 46, 63의 텍스트 다이어그램 블록에 언어가 미지정되었습니다. 마크다운 베스트 프랙티스에 따라 언어를 명시해주세요.

# 수정 전

수정 후

또는




Also applies to: 63-63

</blockquote></details>
<details>
<summary>claudedocs/guides/infrastructure-integration.md (1)</summary><blockquote>

`47-53`: **파일 구조 코드 블록에 언어 지정 필요 (MD040)**

라인 47-53의 디렉토리 트리 구조 블록에 언어를 지정해주세요.



```markdown
# 수정 전

db/migration/

수정 후

db/migration/

또는

db/migration/

</blockquote></details>
<details>
<summary>claudedocs/guides/local-development.md (1)</summary><blockquote>

`92-94`: **URL 마크다운 링크 형식 권장 (MD034)**

라인 92-94의 맨 URL을 마크다운 링크 형식으로 감싸는 것을 권장합니다.



```markdown
# 수정 전
- **Dashboard**: http://localhost:8761

# 수정 후
- **Dashboard**: [http://localhost:8761](http://localhost:8761)

또는 더 간결하게:

- **Dashboard**: [localhost:8761](http://localhost:8761)
claudedocs/guides/naming-conventions.md (2)

33-196: 파일 구조 코드 블록에 언어 지정 필요 (MD040)

라인 33-196의 파일/디렉토리 구조 블록들에 언어를 지정해주세요.

# 수정 전

domain-restaurant/

수정 후

domain-restaurant/

또는

domain-restaurant/

---

`233-252`: **섹션 제목 마크다운 형식 개선 (MD036)**

라인 233-252에서 강조 형식(`**텍스트**`)으로 사용된 항목들을 제목(`####`) 형식으로 변경하는 것을 권장합니다.



```markdown
# 수정 전
**Phase 1: domain-restaurant (10개)**

# 수정 후
#### Phase 1: domain-restaurant (10개)
claudedocs/guides/test-infrastructure.md (1)

183-192: 디렉토리 구조 코드 블록에 언어 지정 필요 (MD040)

라인 183-192의 디렉토리 구조 블록에 언어를 지정해주세요.

# 수정 전

domain-member/

수정 후

domain-member/

또는

domain-member/

</blockquote></details>
<details>
<summary>claudedocs/README.md (1)</summary><blockquote>

`46-50`: **코드 블록의 언어 지정자 추가 필요**

마크다운 코드 블록에 언어 지정자가 누락되어 있습니다. 마크다운린트 권장사항(MD040)에 따라 언어를 명시하세요.

- 라인 46: ` ``` ` → ` ```yaml ` 또는 ` ```text `
- 라인 63: ` ``` ` → ` ```text `

제안된 수정:

```diff
-| Phase 1-4 | 10-14주 | ✅ 완료 | 2025-11-05 |
+| Phase 1-4 | 10-14주 | ✅ 완료 | 2025-11-05 |

Also applies to: 63-89

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5648e75 and 0dad413.

📒 Files selected for processing (34)
  • .github/workflows/Dev_API_Owner_CD.yml (2 hunks)
  • .github/workflows/Dev_API_User_CD.yml (2 hunks)
  • .github/workflows/Dev_CI.yml (2 hunks)
  • .github/workflows/Dev_Discovery_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Member_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Owner_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Reservation_CD.yml (1 hunks)
  • .github/workflows/Dev_Domain_Restaurant_CD.yml (1 hunks)
  • CLAUDE.md (2 hunks)
  • claudedocs/README.md (1 hunks)
  • claudedocs/guides/bff-transaction-strategy.md (1 hunks)
  • claudedocs/guides/infrastructure-integration.md (1 hunks)
  • claudedocs/guides/local-development.md (1 hunks)
  • claudedocs/guides/module-test-strategies.md (1 hunks)
  • claudedocs/guides/naming-conventions.md (1 hunks)
  • claudedocs/guides/test-infrastructure.md (1 hunks)
  • claudedocs/guides/test-layer-guide.md (1 hunks)
  • claudedocs/guides/test-writing-rules.md (1 hunks)
  • claudedocs/microservices-migration-plan.md (4 hunks)
  • claudedocs/phase5-bff-migration-plan.md (0 hunks)
  • domain-member/src/main/resources/application-flyway.yml (1 hunks)
  • domain-member/src/test/java/com/wellmeet/BaseRepositoryTest.java (2 hunks)
  • domain-member/src/test/java/com/wellmeet/DataBaseCleaner.java (3 hunks)
  • domain-member/src/test/java/com/wellmeet/TestConfiguration.java (1 hunks)
  • domain-member/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1 hunks)
  • domain-owner/src/main/resources/application-flyway.yml (1 hunks)
  • domain-owner/src/test/java/com/wellmeet/BaseRepositoryTest.java (2 hunks)
  • domain-owner/src/test/java/com/wellmeet/DataBaseCleaner.java (3 hunks)
  • domain-owner/src/test/java/com/wellmeet/TestConfiguration.java (1 hunks)
  • domain-owner/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1 hunks)
  • domain-reservation/src/main/resources/application-flyway.yml (1 hunks)
  • domain-reservation/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1 hunks)
  • domain-restaurant/src/main/resources/application-flyway.yml (1 hunks)
  • domain-restaurant/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1 hunks)
💤 Files with no reviewable changes (1)
  • claudedocs/phase5-bff-migration-plan.md
✅ Files skipped from review due to trivial changes (1)
  • domain-member/src/test/java/com/wellmeet/TestConfiguration.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/Dev_CI.yml
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-10T02:52:48.337Z
Learnt from: unifolio0
Repo: Team-soft-macaron/WellMeet-Backend PR: 14
File: src/main/java/com/wellmeet/recommend/dto/RecommendRestaurantResponse.java:3-3
Timestamp: 2025-07-10T02:52:48.337Z
Learning: The Restaurant class is located at `com.wellmeet.restaurant.domain.Restaurant` after the package refactoring in the WellMeet-Backend project, not in a nested model package structure.

Applied to files:

  • domain-owner/src/test/java/com/wellmeet/TestConfiguration.java
🧬 Code graph analysis (4)
domain-owner/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (3)
domain-member/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-reservation/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-restaurant/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-member/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (3)
domain-owner/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-reservation/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-restaurant/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-reservation/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (3)
domain-member/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-owner/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-restaurant/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-restaurant/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (3)
domain-member/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-owner/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
domain-reservation/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java (1)
  • SpringBootTest (7-15)
🪛 actionlint (1.7.8)
.github/workflows/Dev_Domain_Reservation_CD.yml

68-68: label "dev-domain-reservation" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_API_Owner_CD.yml

69-69: label "dev-api-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Restaurant_CD.yml

68-68: label "dev-domain-restaurant" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_API_User_CD.yml

69-69: label "dev-api-user" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Discovery_CD.yml

62-62: label "dev-discovery-server" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Member_CD.yml

68-68: label "dev-domain-member" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

.github/workflows/Dev_Domain_Owner_CD.yml

68-68: label "dev-domain-owner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xl", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xl", "macos-14-xlarge", "macos-14-large", "macos-14", "macos-13-xl", "macos-13-xlarge", "macos-13-large", "macos-13", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🪛 markdownlint-cli2 (0.18.1)
claudedocs/guides/bff-transaction-strategy.md

46-46: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


63-63: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

claudedocs/microservices-migration-plan.md

110-110: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)


149-149: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)


227-227: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)


458-458: Spaces inside emphasis markers

(MD037, no-space-in-emphasis)

claudedocs/guides/naming-conventions.md

33-33: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


64-64: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


95-95: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


127-127: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


150-150: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


174-174: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


196-196: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


233-233: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


237-237: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


242-242: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


247-247: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


252-252: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

claudedocs/guides/infrastructure-integration.md

46-46: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


63-63: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

claudedocs/guides/local-development.md

92-92: Bare URL used

(MD034, no-bare-urls)


93-93: Bare URL used

(MD034, no-bare-urls)


94-94: Bare URL used

(MD034, no-bare-urls)

claudedocs/README.md

46-46: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


63-63: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

claudedocs/guides/test-infrastructure.md

183-183: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-push
🔇 Additional comments (16)
.github/workflows/Dev_API_Owner_CD.yml (1)

69-69: 커스텀 러너 라벨 구성 필요 (전체 워크플로우 검토 필요)

dev-api-owner 러너 라벨이 GitHub Actions 표준 라벨이 아닙니다. 이는 자체 호스팅 러너라면, .github/actionlint.yaml에 구성되어 있거나, 조직의 GitHub Actions 러너 설정에서 이 라벨이 정의되어 있어야 합니다.

다음을 확인해주세요:

  • .github/actionlint.yaml 파일이 존재하고 dev-api-owner 라벨을 포함하는지
  • 또는 GitHub 조직 설정에서 이 자체 호스팅 러너가 실제로 구성되어 있는지
.github/workflows/Dev_Domain_Reservation_CD.yml (1)

84-84: 배포 스크립트 실행 시 권한 관리 검증

sudo sh 명령으로 배포 스크립트를 실행합니다. 자체 호스팅 러너에서 비밀번호 없는 sudo 설정 및 replace-new-version.sh 스크립트의 최소 권한 원칙 적용을 확인해주세요.

다음을 확인해주세요:

  • 자체 호스팅 러너의 sudoers 파일에서 암호 없는 sudo 설정 여부
  • 배포 스크립트가 필요한 최소 권한만 요청하는지
  • 스크립트 파일의 소유권 및 권한 설정 (권장: 750 이상)
domain-owner/src/test/java/com/wellmeet/DataBaseCleaner.java (1)

59-64: String.format 사용에 대한 SQL 인젝션 우려

databaseNameconn.getCatalog()에서 가져온 신뢰할 수 있는 소스이고 INFORMATION_SCHEMA 쿼리는 스키마 이름 파라미터화를 지원하지 않으므로 현재 접근 방식은 불가피합니다.

동일한 패턴이 domain-member 모듈에서도 사용되고 있으며, 이전 리뷰에서 방어적 검증 추가 제안이 있었습니다.

데이터베이스 이름 검증을 추가하여 안전성을 강화하는 것을 고려하세요:

 private void extractDatabaseName(ApplicationContext context) {
     DataSource dataSource = context.getBean(DataSource.class);
     try (Connection conn = dataSource.getConnection()) {
         databaseName = conn.getCatalog();
+        if (databaseName == null || !databaseName.matches("[a-zA-Z0-9_]+")) {
+            throw new IllegalStateException("Invalid database name: " + databaseName);
+        }
     } catch (SQLException e) {
         throw new RuntimeException("Failed to extract database name", e);
     }
 }
domain-member/src/test/java/com/wellmeet/DataBaseCleaner.java (1)

59-64: 이전 리뷰 코멘트 확인

이전 리뷰에서 제안된 데이터베이스 이름 검증은 방어적 프로그래밍 관점에서 유효합니다. getCatalog()에서 반환되는 값이 일반적으로 안전하지만, 추가 검증 레이어를 두는 것이 좋습니다.

domain-reservation/src/main/resources/application-flyway.yml (1)

1-10: Flyway 설정이 올바르게 구성되었습니다.

설정 내용이 적절합니다. domain-member와 동일한 구성입니다.

domain-owner/src/main/resources/application-flyway.yml (1)

1-10: Flyway 설정이 올바르게 구성되었습니다.

다른 도메인 모듈과 일관된 Flyway 구성입니다.

domain-restaurant/src/main/resources/application-flyway.yml (1)

1-10: Flyway 설정이 올바르게 구성되었습니다.

다른 도메인 모듈과 일관된 Flyway 구성입니다.

domain-member/src/test/java/com/wellmeet/BaseRepositoryTest.java (2)

1-1: 패키지 변경은 검증되었습니다 - 추가 조치 불필요합니다.

검증 결과, BaseRepositoryTest 패키지 변경(com.wellmeet.domain.ownercom.wellmeet)은 올바르고 안전합니다. domain-member 모듈 내의 MemberDomainServiceTest와 FavoriteRestaurantDomainServiceTest가 모두 새로운 패키지에서 BaseRepositoryTest를 성공적으로 상속받고 있으며, 어떤 breaking changes도 발생하지 않았습니다. 이전 패키지명은 파일 위치와 맞지 않았던 것이 올바르게 수정되었습니다.


15-15: 테스트 프로파일 변경 검증 완료 - 이슈 없음

검증 결과, domain-test에서 test로의 프로파일 변경이 올바르게 진행되었습니다:

  • application-test.yml이 리소스 폴더에 존재하며, @activeprofiles('test') 어노테이션과 함께 작동합니다
  • domain-member 모듈의 모든 테스트 파일(BaseRepositoryTest, FlywaySchemaValidationTest)이 새로운 "test" 프로파일을 일관되게 사용 중
  • 이전 "domain-test" 프로파일 설정 파일은 제거됨
domain-owner/src/test/java/com/wellmeet/TestConfiguration.java (2)

3-7: 최소 구성의 테스트 설정입니다. 테스트 요구사항 충족 여부를 확인하세요.

@SpringBootApplication 어노테이션만으로 구성된 최소한의 테스트 설정 클래스입니다. BFF 마이그레이션 과정에서 Feign 클라이언트나 특정 빈들의 모킹이 필요한 경우, 이 구성으로 충분한지 확인이 필요합니다.


1-1: TestConfiguration은 어떤 테스트에서도 사용되지 않습니다. 원본 리뷰 의견은 부정확합니다.

TestConfiguration은 정의되어 있지만 domain-owner 모듈의 어떤 테스트 클래스에서도 임포트되거나 참조되지 않습니다. 현재 테스트 설정은 다음과 같습니다:

  • BaseRepositoryTest: @DataJpaTest@Import(JpaAuditingConfig.class) 사용
  • FlywaySchemaValidationTest: @SpringBootTest 사용 (TestConfiguration 참조 없음)

패키지 변경(com.wellmeet.domain.membercom.wellmeet)이 실제 테스트에 미치는 영향은 없습니다. 이 클래스가 사용되지 않으므로 컴포넌트 스캔 범위 검증은 필요하지 않습니다.

Likely an incorrect or invalid review comment.

domain-owner/src/test/java/com/wellmeet/BaseRepositoryTest.java (1)

15-15: 테스트 프로파일 변경 검증 완료 - 문제 없음.

@ActiveProfiles("test") 프로파일 변경이 전체 코드베이스에서 일관되게 적용되었습니다. 모든 domain 모듈(domain-member, domain-owner, domain-reservation, domain-restaurant)의 BaseRepositoryTest에서 동일하게 "test" 프로파일을 사용하고 있으며, 각 모듈에 필요한 application-test.yml 설정 파일이 존재합니다. 구 "domain-test" 프로파일에 대한 참조는 코드베이스 어디에도 남아있지 않습니다.

claudedocs/README.md (1)

13-13: Phase 5 완료 기준의 명확성 개선 필요

라인 13과 74에서 "Phase 5 ✅ 완료"를 선언하고 있으나, 실제 상태(라인 69-72)는 도메인 서비스들이 "⚠️ 코드 완성 | 독립 실행 검증 보류" 상태입니다. 또한 라인 91의 설명이 다소 오도적입니다.

문제점:

  • "Phase 5 BFF 전환 완료로 microservices 아키텍처 목표는 이미 달성되었다"는 명제가 부정확합니다
  • BFF 패턴 도입 ≠ 마이크로서비스 아키텍처 완성
  • 실제 마이크로서비스 목표: 각 도메인 서비스의 독립 배포 가능성

권장사항:

  • Phase 5를 "BFF 전환 및 Feign 클라이언트 도입 완료"로 명확히 표현
  • 독립 배포 가능성은 별도 Phase(예: Phase 6)로 구분
  • 각 서비스의 실제 가능 상태를 명확히 구분 (코드 완성 vs 검증 완료 vs 실행 중)

Also applies to: 69-91

claudedocs/guides/test-layer-guide.md (2)

147-232: 테스트 가이드의 베이스 클래스 참조 검증 필수

문서에서 여러 베이스 클래스를 참조하고 있습니다. 실제 구현 상황을 확인하세요:

  • BaseRepositoryTest (라인 147, 230): @DataJpaTest, @ExtendWith(DataBaseCleaner.class) 사용 선언
  • BaseServiceTest (라인 254, 384, 461, 484): 문서에는 설명이 없으나 예제에서 사용됨
  • BaseControllerTest (라인 576, 668-684): @SpringBootTest, RestAssured 사용 선언

각 베이스 클래스가 프로젝트에 실제 존재하는지, 그리고 문서의 구현 형태와 일치하는지 확인이 필요합니다. 특히 BaseServiceTest의 구체적인 정의가 누락되어 있습니다.

Also applies to: 254-254, 384-384, 576-576, 668-668, 708-708, 962-962


502-523: 테스트 데이터 생성 유틸리티 참조 확인 필요

테스트 코드 예제에서 다음 생성자 패턴을 사용하고 있습니다:

  • memberGenerator.generate() (라인 512, 539)
  • ownerGenerator.generate() (라인 502, 527)
  • restaurantGenerator.generate() (라인 503, 528)
  • availableDateGenerator.generate() (라인 505, 530)

BaseControllerTest나 BaseServiceTest가 이러한 Generator 인스턴스를 제공하는지 확인 필요합니다. 생성자 패턴이 문서와 실제 구현 간 일치하는지 검증하세요.

Also applies to: 538-552

CLAUDE.md (1)

14-25: 추출된 가이드 문서 검증 완료 - 모든 링크 및 파일 정상

검증 결과 CLAUDE.md의 문서 분리가 정상적으로 완료되었습니다:

모든 8개 가이드 파일 존재 확인

  • claudedocs/guides/ 디렉토리에 8개 파일 모두 정상 배치

링크 형식 및 경로 검증

  • 모든 markdown 링크가 올바른 상대 경로 사용 (./claudedocs/guides/{filename}.md)
  • CLAUDE.md 14-26줄의 링크 정상 작동

이전 리뷰 지적사항 해결 확인

  • ApplicationService 네이밍 패턴이 naming-conventions.md에 명확하게 문서화됨
  • 올바른 패턴: {Domain}{Entity}ApplicationService
  • 구체적 예시 제공: RestaurantApplicationService 등

인프라 문서 구성 확인

  • infrastructure-integration.md에서 Flyway, AWS MSK(Kafka) 통합 가이드 정상 작성

Comment on lines +287 to +305
**마지막 업데이트**: 2025-11-06

## 변경 이력

**2025-11-06 (문서 재구성)**:

- CLAUDE.md 대폭 간소화 (2,682줄 → 300줄, 89% 감소)
- 8개 상세 가이드 문서 분리 (`claudedocs/guides/` 디렉토리):
- `naming-conventions.md` (254줄) - 클래스 네이밍 규칙
- `bff-transaction-strategy.md` (231줄) - BFF 패턴 및 분산 트랜잭션
- `local-development.md` (120줄) - Docker Compose, Eureka Server
- `test-layer-guide.md` (1,013줄) - 8개 테스트 레이어 상세
- `module-test-strategies.md` (103줄) - 모듈별 테스트 전략
- `test-writing-rules.md` (141줄) - 테스트 작성 규칙
- `test-infrastructure.md` (306줄) - Gradle, testFixtures 설정
- `infrastructure-integration.md` (214줄) - Flyway, AWS MSK 통합
- 문서 간 링크 구조 개선 (메인 문서 → 상세 가이드)
- 각 가이드 문서에 독립적인 목차(TOC) 추가
- Claude 컨텍스트 로딩 효율 개선 (~25,000 토큰 절약)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

문서 재구성 상태의 정확성 검증

CLAUDE.md의 대폭적인 간소화(2,682줄 → 300줄, 89% 감소)를 보고하고 있습니다. 다음을 확인하세요:

  1. 추출 완결성: 8개 가이드 문서가 모두 완성되었는지 확인
  2. 컨텍스트 손실 방지: 핵심 정보가 실제로 추출된 가이드에 포함되었는지 확인
  3. 호환성: 기존 링크와 참조가 새로운 문서 구조에서 여전히 유효한지 확인

특히 이전 리뷰에서 지적된 네이밍 규칙 오류(ApplicationService 패턴, RedisService, KafkaProducerService, DomainController)가 올바르게 수정되어 추출된 가이드에 포함되었는지 검증 필요합니다.


🏁 Script executed:

#!/bin/bash
# Description: Verify extracted guide completeness and content quality

echo "=== Checking line counts of extracted guides ==="
wc -l claudedocs/guides/*.md 2>/dev/null | tail -1

echo ""
echo "=== Verifying no duplicate content between CLAUDE.md and guides ==="
echo "Checking if key sections were properly removed from CLAUDE.md..."
if grep -q "Domain Service Layer" CLAUDE.md; then
  echo "⚠️ WARNING: Domain Service Layer section still in CLAUDE.md (should be extracted)"
fi

echo ""
echo "=== Checking if README.md links are referenced correctly ==="
grep -n "microservices-migration-plan.md\|CLAUDE.md" claudedocs/README.md

Length of output: 421


검증을 계속 진행하겠습니다. 개별 가이드 파일의 존재 여부와 이전 리뷰에서 지적된 네이밍 규칙 문제가 올바르게 수정되었는지 확인하겠습니다.

[/run_scripts]


🏁 Script executed:

#!/bin/bash
# Verify individual guide files and check naming conventions content

echo "=== Individual guide file line counts ==="
for file in claudedocs/guides/*.md; do
  if [ -f "$file" ]; then
    lines=$(wc -l < "$file")
    echo "$(basename $file): $lines"
  fi
done

echo ""
echo "=== Checking naming-conventions.md for previously flagged patterns ==="
if [ -f "claudedocs/guides/naming-conventions.md" ]; then
  echo "✓ naming-conventions.md exists"
  echo ""
  echo "Searching for guidance on Application/Service/Controller patterns:"
  grep -i "application\|service\|controller" claudedocs/guides/naming-conventions.md | head -20
else
  echo "✗ naming-conventions.md NOT FOUND"
fi

Length of output: 1590



CLAUDE.md의 추출 가이드 라인 수 수정 필요

8개 가이드 문서의 추출은 완성되었으며 이전에 지적된 네이밍 규칙 이슈(ApplicationService, DomainController 패턴)가 naming-conventions.md에 정확히 문서화되어 있습니다. 다만 CLAUDE.md (287-305줄)에 기재된 각 가이드의 라인 수가 실제 파일과 일치하지 않습니다:

문서 기재된 줄 수 실제 줄 수 차이
naming-conventions.md 254 271 +17
bff-transaction-strategy.md 231 256 +25
local-development.md 120 134 +14
test-layer-guide.md 1,013 1,033 +20
test-infrastructure.md 306 321 +15
infrastructure-integration.md 214 228 +14
module-test-strategies.md 103 123 +20
test-writing-rules.md 141 158 +17
합계 2,382 2,524 +142

각 파일의 실제 라인 수로 변경 이력을 정정해주세요.

🤖 Prompt for AI Agents
In CLAUDE.md around lines 287-305, the recorded line counts for the 8 extracted
guide files are outdated; update each entry to the actual line counts and
correct the total: set naming-conventions.md to 271, bff-transaction-strategy.md
to 256, local-development.md to 134, test-layer-guide.md to 1,033,
test-infrastructure.md to 321, infrastructure-integration.md to 228,
module-test-strategies.md to 123, test-writing-rules.md to 158, and update the
summed total from 2,382 to 2,524; ensure the markdown table and the bolded total
are replaced with these corrected values.

Comment on lines +11 to +14
@Test
void Flyway_마이그레이션과_Entity가_일치해야_한다() {

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

테스트 메서드가 비어 있습니다.

테스트 메서드에 검증 로직이 구현되어 있지 않습니다. 현재는 Spring 컨텍스트가 flyway 프로필로 로드되는지만 암묵적으로 검증합니다. 이 패턴이 4개 도메인 모듈에 동일하게 반복되고 있습니다.

명시적인 검증 로직을 추가하거나, 의도적인 스캐폴딩이라면 테스트 메서드에 주석으로 명확히 표시하는 것을 권장합니다. 예를 들어:

 @Test
 void Flyway_마이그레이션과_Entity가_일치해야_한다() {
-
+    // Spring 컨텍스트 로드 시 ddl-auto: validate로 인해
+    // Flyway 마이그레이션과 Entity의 불일치가 있으면 테스트 실패
 }
🤖 Prompt for AI Agents
In
domain-member/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java
around lines 11 to 14, the test method Flyway_마이그레이션과_Entity가_일치해야_한다() is
empty; either implement explicit validation logic (e.g., obtain Flyway and run
validate(), or bootstrap JPA/Hibernate and trigger schema validation/assert no
mismatches) to fail the test on schema/entity drift, or if this is intentional
scaffolding, add a clear comment in the method body explaining it is a
placeholder and why; ensure every domain module follows the same approach so the
pattern is consistent.

Comment on lines +11 to +14
@Test
void Flyway_마이그레이션과_Entity가_일치해야_한다() {

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

테스트 메서드가 비어 있습니다.

다른 도메인 모듈과 동일한 패턴입니다. 검증 로직 추가 또는 의도를 명시하는 주석 추가를 권장합니다.

🤖 Prompt for AI Agents
In
domain-owner/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java
around lines 11 to 14 the test method is empty; either implement the Flyway vs
JPA entity validation logic or document the intent. Add a real assertion that
runs Flyway validation against the JPA entities (or boots the Spring context and
invokes Flyway.validate()/migrate() and fails on discrepancies), or if the test
is intentionally omitted add a clear comment explaining why and/or annotate with
@Disabled and a brief reason.

Comment on lines +11 to +14
@Test
void Flyway_마이그레이션과_Entity가_일치해야_한다() {

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

테스트 메서드가 비어 있습니다.

다른 도메인 모듈과 동일한 패턴입니다. 검증 로직 추가 또는 의도를 명시하는 주석 추가를 권장합니다.

🤖 Prompt for AI Agents
In
domain-reservation/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java
around lines 11 to 14, the test method Flyway_마이그레이션과_Entity가_일치해야_한다() is
empty; either implement the schema validation logic (e.g., run Flyway
migrate/validate against the JPA entities as done in other domain modules or
load ApplicationContext and assert Flyway.validate() succeeds) or make the
intent explicit by adding a brief comment or annotating the test with
@Disabled("reason") explaining why it is empty; update the method to perform the
same verification pattern used in other modules or to document the intentional
no-op.

Comment on lines +11 to +14
@Test
void Flyway_마이그레이션과_Entity가_일치해야_한다() {

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

테스트 메서드가 비어 있습니다.

domain-member와 동일한 패턴입니다. 검증 로직 추가 또는 의도를 명시하는 주석 추가를 권장합니다.

🤖 Prompt for AI Agents
In
domain-restaurant/src/test/java/com/wellmeet/domain/FlywaySchemaValidationTest.java
around lines 11 to 14 the test method is empty; implement the intended Flyway vs
Entity validation by either (A) loading the test ApplicationContext (e.g.,
@SpringBootTest), autowiring the Flyway bean and calling flyway.validate() (or
flyway.migrate() and assert no exceptions) so the test fails on mismatches, or
(B) if the emptiness is intentional, add a short clarifying comment explaining
why the test is intentionally blank and reference the analogous implementation
in domain-member.

@unifolio0 unifolio0 closed this Nov 6, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
domain-member/src/test/java/com/wellmeet/domain/member/MemberDomainServiceTest.java (2)

23-42: 테스트 케이스가 성공/실패 경로를 모두 커버합니다.

정상 조회와 예외 발생 시나리오를 모두 테스트하고 있어 좋습니다.

선택적 개선사항: 예외 테스트에서 예외 메시지나 에러 코드도 검증하면 더 명확한 테스트가 됩니다.

다음과 같이 예외 상세 정보를 검증할 수 있습니다:

 @Test
 void 존재하지_않는_회원_조회_시_예외가_발생한다() {
     String nonExistentId = "non-existent-id";
 
     assertThatThrownBy(() -> memberDomainService.getById(nonExistentId))
-            .isInstanceOf(MemberException.class);
+            .isInstanceOf(MemberException.class)
+            .hasMessageContaining("존재하지 않는 회원");
 }

45-48: 헬퍼 메서드가 간결하고 명확합니다.

현재 테스트에는 충분하지만, 향후 다양한 테스트 케이스가 추가될 경우 빌더 패턴이나 테스트 픽스처를 고려해보세요.

예시:

private Member createAndSaveMember(String name) {
    Member member = Member.builder()
            .name(name)
            .nickname("nickname")
            .email("email@example.com")
            .phone("010-1234-5678")
            .build();
    return memberRepository.save(member);
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0dad413 and bf3fb23.

📒 Files selected for processing (2)
  • domain-member/src/test/java/com/wellmeet/domain/member/FavoriteRestaurantDomainServiceTest.java (1 hunks)
  • domain-member/src/test/java/com/wellmeet/domain/member/MemberDomainServiceTest.java (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • domain-member/src/test/java/com/wellmeet/domain/member/FavoriteRestaurantDomainServiceTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-push
🔇 Additional comments (1)
domain-member/src/test/java/com/wellmeet/domain/member/MemberDomainServiceTest.java (1)

14-22: 테스트 클래스 구조가 잘 설계되었습니다!

BaseRepositoryTest를 확장하고 @Import로 필요한 서비스만 로드하는 방식은 가볍고 효율적인 테스트 설정입니다. 필요한 의존성도 적절하게 주입되어 있습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] api-user BFF 전환

2 participants