[FEAT] owner 서버 분리#68
Conversation
|
Caution Review failedThe pull request is closed. Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughOwner 도메인 마이크로서비스 초기 분리 구현을 추가했습니다: domain-owner 모듈(컨트롤러, 애플리케이션/도메인 서비스, DTO, 예외 처리), Dockerfile 및 docker-compose 서비스, 빌드/설정 파일과 마이그레이션 계획 문서 업데이트가 포함됩니다. Changes
Sequence DiagramsequenceDiagram
participant Client
participant OwnerController
participant OwnerApplicationService
participant OwnerDomainService
participant OwnerRepository
participant DB
rect rgb(220,245,220)
Note right of OwnerController: CreateOwner (POST /api/owners)
Client->>OwnerController: POST CreateOwnerRequest
OwnerController->>OwnerApplicationService: createOwner(name,email)
OwnerApplicationService->>OwnerRepository: save(new Owner)
OwnerRepository->>DB: INSERT
DB-->>OwnerRepository: Owner(with id)
OwnerRepository-->>OwnerApplicationService: Owner
OwnerApplicationService-->>OwnerController: OwnerResponse (201)
OwnerController-->>Client: 201 Created
end
rect rgb(245,245,220)
Note right of OwnerController: GetOwner (GET /api/owners/{id})
Client->>OwnerController: GET /api/owners/{id}
OwnerController->>OwnerApplicationService: getOwnerById(id)
OwnerApplicationService->>OwnerDomainService: getById(id)
OwnerDomainService->>OwnerRepository: findById(id)
OwnerRepository->>DB: SELECT
DB-->>OwnerRepository: Owner | empty
OwnerRepository-->>OwnerDomainService: Owner
OwnerDomainService-->>OwnerApplicationService: Owner
OwnerApplicationService-->>OwnerController: OwnerResponse (200)
OwnerController-->>Client: 200 OK
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 분
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
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. Comment |
Summary of ChangesHello @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! 이 PR은 마이크로서비스 전환 전략에 따라 Highlights
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
Code Review
이 PR은 owner 도메인을 마이크로서비스로 분리하는 작업을 담고 있으며, Docker 설정, REST 컨트롤러 및 관련 서비스를 추가했습니다. 전반적으로 구조는 잘 잡혀있지만, 몇 가지 중요한 이슈가 있습니다. 가장 시급한 문제는 서비스의 메인 애플리케이션 클래스가 주석 처리되어 있어 애플리케이션을 실행할 수 없다는 점입니다. 또한, 예외 처리 시 로깅을 추가하여 디버깅 용이성을 높여야 하며, 프로덕션 환경을 위한 데이터베이스 스키마 관리 전략에 대한 고려가 필요합니다. 이 외에도 계층형 아키텍처를 강화하고 코드 일관성을 높이기 위한 몇 가지 리팩토링을 제안했습니다.
| //package com.wellmeet.domain; | ||
| // | ||
| //import org.springframework.boot.SpringApplication; | ||
| //import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| // | ||
| //@SpringBootApplication | ||
| //public class OwnerServiceApplication { | ||
| // | ||
| // public static void main(String[] args) { | ||
| // SpringApplication.run(OwnerServiceApplication.class, args); | ||
| // } | ||
| //} |
There was a problem hiding this comment.
애플리케이션의 메인 클래스가 주석 처리되어 있어 서비스가 빌드 및 실행될 수 없습니다. 이는 마이크로서비스 분리라는 PR의 핵심 목표를 달성하지 못하게 하는 심각한 문제입니다.
문서에 언급된 '빈 스캔 문제'는 @SpringBootApplication의 위치와 관련이 있을 가능성이 높습니다. 현재 com.wellmeet.domain 패키지에 위치해 있는데, 이는 다른 도메인 모듈을 의도치 않게 스캔하여 문제를 일으킬 수 있습니다.
이 클래스를 com.wellmeet.domain.owner 패키지로 이동하고 주석을 해제하여 문제를 해결하는 것을 제안합니다. 이렇게 하면 컴포넌트 스캔 범위가 owner 도메인으로 적절히 제한됩니다. 파일 위치도 domain-owner/src/main/java/com/wellmeet/domain/owner/OwnerServiceApplication.java로 변경해야 합니다.
| //package com.wellmeet.domain; | |
| // | |
| //import org.springframework.boot.SpringApplication; | |
| //import org.springframework.boot.autoconfigure.SpringBootApplication; | |
| // | |
| //@SpringBootApplication | |
| //public class OwnerServiceApplication { | |
| // | |
| // public static void main(String[] args) { | |
| // SpringApplication.run(OwnerServiceApplication.class, args); | |
| // } | |
| //} | |
| package com.wellmeet.domain.owner; | |
| import org.springframework.boot.SpringApplication; | |
| import org.springframework.boot.autoconfigure.SpringBootApplication; | |
| @SpringBootApplication | |
| public class OwnerServiceApplication { | |
| public static void main(String[] args) { | |
| SpringApplication.run(OwnerServiceApplication.class, args); | |
| } | |
| } |
| @ExceptionHandler(Exception.class) | ||
| public ResponseEntity<ErrorResponse> handleException(Exception e) { | ||
| ErrorResponse errorResponse = ErrorResponse.of( | ||
| HttpStatus.INTERNAL_SERVER_ERROR.value(), | ||
| "Internal server error occurred" | ||
| ); | ||
| return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); | ||
| } |
There was a problem hiding this comment.
일반적인 Exception을 처리할 때, 예외의 상세 정보를 로깅하는 것이 매우 중요합니다. 현재 구현은 클라이언트에게 일반적인 오류 메시지만 반환하고 예외 스택 트레이스나 원인에 대한 정보를 기록하지 않아, 프로덕션 환경에서 문제가 발생했을 때 원인 파악이 거의 불가능합니다. @Slf4j 어노테이션을 클래스에 추가하고 log.error()를 사용하여 예외 정보를 로깅하는 코드를 추가하는 것을 제안합니다.
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error("Internal server error occurred", e);
ErrorResponse errorResponse = ErrorResponse.of(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"Internal server error occurred"
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
}|
|
||
| jpa: | ||
| hibernate: | ||
| ddl-auto: update |
There was a problem hiding this comment.
spring.jpa.hibernate.ddl-auto: update 설정은 개발 환경에서는 편리하지만, 프로덕션 환경에서는 데이터 손실이나 의도치 않은 스키마 변경을 유발할 수 있어 위험합니다. 프로덕션 프로필에서는 validate나 none으로 설정하고, 데이터베이스 스키마 변경은 Flyway나 Liquibase 같은 마이그레이션 도구를 사용하여 관리하는 것을 강력히 권장합니다. 이 PR의 다른 모듈(domain-reservation)에서는 이미 Flyway를 사용하고 있으므로, 일관성을 위해 domain-owner 모듈에도 Flyway를 도입하는 것을 고려해볼 수 있습니다.
| public List<Owner> findAllByIds(List<String> ownerIds) { | ||
| return ownerRepository.findAllById(ownerIds); | ||
| } | ||
| } |
There was a problem hiding this comment.
도메인 서비스의 역할을 강화하고 애플리케이션 서비스의 책임을 명확히 하기 위해, Owner를 생성하고 삭제하는 로직을 OwnerDomainService로 옮기는 것을 제안합니다. 이렇게 하면 OwnerApplicationService는 OwnerRepository에 직접 의존하지 않고 도메인 서비스에만 의존하게 되어 더 계층화된 아키텍처를 유지할 수 있습니다.
| } | |
| public List<Owner> findAllByIds(List<String> ownerIds) { | |
| return ownerRepository.findAllById(ownerIds); | |
| } | |
| @Transactional | |
| public Owner create(String name, String email) { | |
| Owner owner = new Owner(name, email); | |
| return ownerRepository.save(owner); | |
| } | |
| @Transactional | |
| public void delete(String ownerId) { | |
| Owner owner = getById(ownerId); | |
| ownerRepository.delete(owner); | |
| } | |
| } |
| @RequestMapping("/api/owners") | ||
| public class OwnerController { | ||
|
|
||
| private final OwnerApplicationService ownerApplicationService; | ||
|
|
||
| public OwnerController(OwnerApplicationService ownerApplicationService) { | ||
| this.ownerApplicationService = ownerApplicationService; | ||
| } |
There was a problem hiding this comment.
프로젝트의 다른 서비스 및 컨트롤러 클래스에서는 lombok.RequiredArgsConstructor를 사용하여 생성자 주입을 간결하게 처리하고 있습니다. 일관성을 유지하고 보일러플레이트 코드를 줄이기 위해 이 클래스에도 @RequiredArgsConstructor를 사용하는 것을 권장합니다. import lombok.RequiredArgsConstructor; 구문도 추가해야 합니다.
| @RequestMapping("/api/owners") | |
| public class OwnerController { | |
| private final OwnerApplicationService ownerApplicationService; | |
| public OwnerController(OwnerApplicationService ownerApplicationService) { | |
| this.ownerApplicationService = ownerApplicationService; | |
| } | |
| @RequestMapping("/api/owners") | |
| @lombok.RequiredArgsConstructor | |
| public class OwnerController { | |
| private final OwnerApplicationService ownerApplicationService; |
| private final OwnerDomainService ownerDomainService; | ||
| private final OwnerRepository ownerRepository; | ||
|
|
||
| @Transactional | ||
| public OwnerResponse createOwner(String name, String email) { | ||
| Owner owner = new Owner(name, email); | ||
| Owner saved = ownerRepository.save(owner); | ||
| return OwnerResponse.from(saved); | ||
| } | ||
|
|
||
| public OwnerResponse getOwnerById(String ownerId) { | ||
| Owner owner = ownerDomainService.getById(ownerId); | ||
| return OwnerResponse.from(owner); | ||
| } | ||
|
|
||
| public List<OwnerResponse> getOwnersByIds(List<String> ownerIds) { | ||
| return ownerDomainService.findAllByIds(ownerIds).stream() | ||
| .map(OwnerResponse::from) | ||
| .toList(); | ||
| } | ||
|
|
||
| @Transactional | ||
| public void deleteOwner(String ownerId) { | ||
| Owner owner = ownerDomainService.getById(ownerId); | ||
| ownerRepository.delete(owner); | ||
| } |
There was a problem hiding this comment.
애플리케이션 서비스는 도메인 로직을 직접 다루기보다 도메인 서비스를 통해 작업을 위임하는 것이 좋습니다. OwnerRepository에 직접 의존하는 대신, OwnerDomainService로 생성 및 삭제 로직을 옮기고 OwnerApplicationService에서는 OwnerDomainService만 호출하도록 리팩토링하는 것을 제안합니다. 이 변경은 OwnerDomainService에 create 및 delete 메소드를 추가하는 다른 제안과 함께 적용되어야 합니다.
private final OwnerDomainService ownerDomainService;
@Transactional
public OwnerResponse createOwner(String name, String email) {
Owner saved = ownerDomainService.create(name, email);
return OwnerResponse.from(saved);
}
public OwnerResponse getOwnerById(String ownerId) {
Owner owner = ownerDomainService.getById(ownerId);
return OwnerResponse.from(owner);
}
public List<OwnerResponse> getOwnersByIds(List<String> ownerIds) {
return ownerDomainService.findAllByIds(ownerIds).stream()
.map(OwnerResponse::from)
.toList();
}
@Transactional
public void deleteOwner(String ownerId) {
ownerDomainService.delete(ownerId);
}There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (6)
domain-owner/src/main/resources/application.yml (2)
8-12: 데이터베이스 자격 증명 하드코딩
password: password가 하드코딩되어 있습니다. 로컬 개발용이지만, 프로덕션 배포 시 환경 변수를 사용하도록 준비하세요:spring: datasource: url: ${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3308/wellmeet_owner} username: ${SPRING_DATASOURCE_USERNAME:root} password: ${SPRING_DATASOURCE_PASSWORD:password}docker-compose.yml에서 이미 환경 변수를 전달하고 있으므로, 이 설정으로 변경하면 로컬/프로덕션 모두 대응 가능합니다.
44-47: Debug 로깅 레벨 확인
com.wellmeet와org.hibernate.SQL모두 DEBUG 레벨로 설정되어 있어 로그가 매우 많이 생성될 수 있습니다. 로컬 개발에는 유용하지만, 프로덕션 환경에서는 INFO 이상으로 설정하는 것을 권장합니다.domain-owner/src/main/java/com/wellmeet/domain/owner/service/OwnerApplicationService.java (2)
17-18: 계층 분리를 고려해보세요.Application Service가
OwnerDomainService와OwnerRepository를 모두 주입받고 있습니다. 일반적으로 Application Service는 Domain Service를 통해서만 데이터 접근을 하는 것이 계층 분리 원칙에 더 부합합니다. Repository 직접 접근이 필요한 경우 Domain Service에 해당 메서드를 추가하는 것을 고려해보세요.
21-25: 생성 로직에 검증을 추가하는 것을 고려해보세요.
Owner엔티티를 직접 생성하고 있는데, name과 email에 대한 비즈니스 검증이 필요한 경우 Domain Service에서 처리하는 것이 좋습니다. 예를 들어,OwnerErrorCode.OWNER_NAME_INVALID가 정의되어 있는 것으로 보아 이름 검증 로직이 필요할 수 있습니다.domain-owner/src/main/java/com/wellmeet/domain/owner/exception/OwnerExceptionHandler.java (2)
20-27: 모든 검증 에러를 포함하는 것을 고려해보세요.현재는 첫 번째 필드 에러만 반환하고 있습니다. 여러 필드에서 검증이 실패한 경우 클라이언트가 모든 오류를 한 번에 확인할 수 있도록 모든 필드 에러를 포함하는 것이 사용자 경험에 더 좋습니다.
다음과 같이 모든 검증 에러를 수집할 수 있습니다:
@ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException e) { - FieldError fieldError = e.getBindingResult().getFieldError(); - String message = fieldError != null ? fieldError.getDefaultMessage() : "Validation failed"; + String message = e.getBindingResult().getFieldErrors().stream() + .map(FieldError::getDefaultMessage) + .collect(Collectors.joining(", ")); + if (message.isEmpty()) { + message = "Validation failed"; + } ErrorResponse errorResponse = ErrorResponse.of(HttpStatus.BAD_REQUEST.value(), message); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse); }
35-42: 예외 로깅을 추가해주세요.일반 예외 처리 시 실제 오류 정보가 숨겨지고 있습니다. 디버깅과 모니터링을 위해 예외를 로그로 남기는 것이 중요합니다. 다른 핸들러들에도 적절한 로그 레벨로 예외를 기록하는 것을 권장합니다.
다음과 같이 로깅을 추가할 수 있습니다:
+import lombok.extern.slf4j.Slf4j; + +@Slf4j @RestControllerAdvice public class OwnerExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleException(Exception e) { + log.error("Unexpected error occurred", e); ErrorResponse errorResponse = ErrorResponse.of( HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal server error occurred" ); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
CLAUDE.md(2 hunks)claudedocs/microservices-migration-plan.md(3 hunks)docker-compose.yml(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/java/com/wellmeet/domain/owner/OwnerDomainService.java(1 hunks)domain-owner/src/main/java/com/wellmeet/domain/owner/controller/OwnerController.java(1 hunks)domain-owner/src/main/java/com/wellmeet/domain/owner/dto/CreateOwnerRequest.java(1 hunks)domain-owner/src/main/java/com/wellmeet/domain/owner/dto/ErrorResponse.java(1 hunks)domain-owner/src/main/java/com/wellmeet/domain/owner/dto/OwnerIdsRequest.java(1 hunks)domain-owner/src/main/java/com/wellmeet/domain/owner/dto/OwnerResponse.java(1 hunks)domain-owner/src/main/java/com/wellmeet/domain/owner/exception/OwnerErrorCode.java(1 hunks)domain-owner/src/main/java/com/wellmeet/domain/owner/exception/OwnerExceptionHandler.java(1 hunks)domain-owner/src/main/java/com/wellmeet/domain/owner/service/OwnerApplicationService.java(1 hunks)domain-owner/src/main/resources/application.yml(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
domain-owner/src/main/java/com/wellmeet/domain/owner/service/OwnerApplicationService.java (1)
domain-owner/src/main/java/com/wellmeet/domain/owner/OwnerDomainService.java (1)
Service(12-27)
domain-owner/src/main/java/com/wellmeet/domain/owner/OwnerDomainService.java (1)
domain-owner/src/main/java/com/wellmeet/domain/owner/service/OwnerApplicationService.java (1)
Service(12-43)
🪛 markdownlint-cli2 (0.18.1)
claudedocs/microservices-migration-plan.md
399-399: 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). (1)
- GitHub Check: build-and-push
🔇 Additional comments (15)
domain-owner/Dockerfile (1)
17-17: JVM 메모리 설정 검토 필요
-Xms256m -Xmx512m설정은 Spring Boot 애플리케이션에 다소 제한적일 수 있습니다. 서비스 시작 시 메모리 부족으로 인한 OOM 또는 성능 저하가 발생할 수 있습니다.초기 부하 테스트를 통해 적절한 메모리 설정을 확인하고, 필요시 최소
-Xmx768m이상으로 조정을 고려하세요.CLAUDE.md (1)
128-183: 문서 업데이트가 현재 구현 상태를 정확히 반영합니다마이그레이션 계획 문서가 Phase 1-2의 완료 상태와 Phase 3의 진행 상황을 명확히 표시하고 있으며, 도메인 서비스 포트 할당 테이블이 유용합니다. 알려진 이슈도 잘 문서화되어 있습니다.
domain-owner/build.gradle (1)
10-12: 의존성 추가가 적절합니다
spring-boot-starter-web,spring-boot-starter-actuator,spring-cloud-starter-netflix-eureka-client추가가 domain-restaurant 및 domain-member 모듈과 일관되며, 독립 마이크로서비스 구성에 필요한 의존성을 올바르게 포함하고 있습니다.domain-owner/src/main/java/com/wellmeet/domain/owner/dto/CreateOwnerRequest.java (2)
7-15: DTO 검증 패턴이 일관성 있게 구현되었습니다Jakarta Bean Validation 어노테이션을 사용한 입력 검증이 적절하게 구현되어 있으며, 사용자 친화적인 한글 에러 메시지도 포함되어 있습니다.
8-10: 비즈니스 요구사항과의 일치성 확인 필요코드베이스 검증 결과:
- Owner.java 엔티티에서
MAX_NAME_LENGTH = 10이 상수로 명시적으로 정의됨- 검증이 DTO 계층(@SiZe)과 엔티티 계층(validateName())에서 이중으로 적용됨
- OwnerTest.java에서 이 제한이 의도적으로 테스트됨
그러나 이 제한의 근거가 문서화되어 있지 않습니다. 10자 제한이 실제 비즈니스 요구사항에 기반한 것인지 기술적 결정일 뿐인지 확인이 필요합니다.
실무에서 사업자 이름은:
- 개인명: 3-5자 (예: "김철수")
- 상호명: 9자 이상 자주 사용 (예: "맛있는 한식당" = 9자, "Seoul BBQ Restaurant" = 20자 이상)
확인 필요 사항: 요구사항 문서를 검토하여 10자 제한이 실제 비즈니스 요구사항을 반영하는지 재검토하세요. 필요시 제한값을 조정하세요.
claudedocs/microservices-migration-plan.md (3)
288-304: Phase 1-2 완료 상태가 명확히 문서화되었습니다코드 구현 완료 상태와 실행 검증 보류 상태를 명확히 구분하여 문서화했으며, 80% 완료도 표시가 현 상황을 정확히 반영합니다.
773-784: 포트 할당 테이블이 유용합니다각 도메인 서비스의 포트와 현재 상태를 한눈에 파악할 수 있어 인프라 관리에 도움이 됩니다. domain-restaurant-service가 당초 계획의 8081이 아닌 8083 포트를 사용한다는 주의사항도 명확합니다.
394-403: 알려진 이슈가 명확히 문서화되었습니다Phase 1-2 공통 이슈(Application 클래스 주석 처리, 빈 스캔 문제, Docker 실행 검증 보류)가 체크리스트 형태로 잘 정리되어 있어, 다음 작업 우선순위를 쉽게 파악할 수 있습니다.
domain-owner/src/main/java/com/wellmeet/domain/owner/dto/ErrorResponse.java (1)
1-13: 잘 구현되었습니다.에러 응답 DTO가 명확하고 간결하게 구현되어 있습니다. 정적 팩토리 메서드를 통해 timestamp를 자동으로 설정하는 방식이 적절합니다.
domain-owner/src/main/java/com/wellmeet/domain/owner/dto/OwnerIdsRequest.java (1)
1-10: 배치 조회 요청 DTO가 적절합니다.
@NotEmpty검증을 통해 빈 리스트 요청을 방지하고 있어 좋습니다.domain-owner/src/main/java/com/wellmeet/domain/owner/exception/OwnerErrorCode.java (1)
8-8: 에러 코드가 명확하게 정의되었습니다.404 상태 코드와 함께 적절한 한글 메시지가 설정되어 있습니다.
domain-owner/src/main/java/com/wellmeet/domain/owner/OwnerDomainService.java (1)
19-22: 단건 조회 로직이 적절합니다.존재하지 않는 Owner에 대해 명확하게 예외를 발생시키고 있어 좋습니다.
domain-owner/src/main/java/com/wellmeet/domain/owner/dto/OwnerResponse.java (1)
5-21: 응답 DTO와 매핑 로직이 잘 구현되었습니다.Entity를 DTO로 변환하는 정적 팩토리 메서드가 명확하고 모든 필드가 적절히 매핑되어 있습니다.
domain-owner/src/main/java/com/wellmeet/domain/owner/service/OwnerApplicationService.java (1)
27-30: 조회 및 삭제 로직이 적절합니다.Domain Service를 통해 Owner를 조회하고 응답으로 변환하는 패턴이 잘 구현되어 있습니다. 특히 삭제 시 존재 여부를 먼저 확인하는 것이 좋습니다.
Also applies to: 32-36, 39-42
domain-owner/src/main/java/com/wellmeet/domain/owner/controller/OwnerController.java (1)
19-57: REST 컨트롤러가 잘 구현되었습니다.
- 적절한 HTTP 메서드와 상태 코드 사용 (POST→201, GET→200, DELETE→204)
@Valid어노테이션을 통한 요청 검증- 명시적 생성자 주입으로 테스트 용이성 확보
- RESTful 엔드포인트 설계가 명확함
| # Owner Service | ||
| owner-service: | ||
| build: | ||
| context: . | ||
| dockerfile: domain-owner/Dockerfile | ||
| container_name: owner-service | ||
| depends_on: | ||
| - mysql-owner | ||
| - discovery-server | ||
| ports: | ||
| - "8084:8084" | ||
| environment: | ||
| SPRING_PROFILES_ACTIVE: local | ||
| SPRING_DATASOURCE_URL: jdbc:mysql://mysql-owner:3306/wellmeet_owner | ||
| SPRING_DATASOURCE_USERNAME: root | ||
| SPRING_DATASOURCE_PASSWORD: password | ||
| EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: http://discovery-server:8761/eureka/ | ||
| networks: | ||
| - wellmeet-network | ||
| healthcheck: | ||
| test: ["CMD", "curl", "-f", "http://localhost:8084/actuator/health"] | ||
| interval: 30s | ||
| timeout: 3s | ||
| start_period: 40s | ||
| retries: 3 |
There was a problem hiding this comment.
🧩 Analysis chain
owner-service 구성이 기존 서비스 패턴과 일관성 있습니다
owner-service가 restaurant-service 및 member-service와 동일한 패턴으로 설정되어 있습니다. 다만, 다음 사항을 확인해야 합니다:
- Healthcheck 실행 실패:
curl이 컨테이너에 없어 healthcheck가 실패합니다 (Dockerfile 리뷰 참고) - 서비스 시작 불가:
OwnerServiceApplication.java가 주석 처리되어 있어 컨테이너가 시작되지 않습니다
Application 클래스와 Dockerfile의 healthcheck를 수정한 후 다음 명령으로 검증하세요:
🏁 Script executed:
#!/bin/bash
# owner-service 독립 실행 검증
docker-compose up -d owner-service
# 컨테이너 로그 확인
docker-compose logs -f owner-service
# Eureka 등록 확인
curl http://localhost:8761/eureka/apps/DOMAIN-OWNER-SERVICE
# Health check 확인
curl http://localhost:8084/actuator/healthLength of output: 920
🏁 Script executed:
#!/bin/bash
# Verify concerns about owner-service configuration
# 1. Check domain-owner/Dockerfile for curl availability
echo "=== Checking domain-owner/Dockerfile for curl ==="
fd -t f "Dockerfile" domain-owner/ && cat domain-owner/Dockerfile
# 2. Check OwnerServiceApplication.java
echo -e "\n=== Checking OwnerServiceApplication.java ==="
fd -t f "OwnerServiceApplication.java" && cat domain-owner/src/main/java/com/wellmeet/owner/OwnerServiceApplication.java
# 3. Check other service Dockerfiles for comparison (restaurant and member)
echo -e "\n=== Checking restaurant-service Dockerfile for curl ==="
fd -t f "Dockerfile" domain-restaurant/ | head -1 | xargs cat
echo -e "\n=== Checking member-service Dockerfile for curl ==="
fd -t f "Dockerfile" domain-member/ | head -1 | xargs catLength of output: 1778
🏁 Script executed:
#!/bin/bash
# Find and check OwnerServiceApplication.java content
echo "=== Finding OwnerServiceApplication.java ==="
fd -t f "OwnerServiceApplication.java" domain-owner/
echo -e "\n=== Checking content of OwnerServiceApplication.java ==="
cat domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java
echo -e "\n=== Checking if curl is available in openjdk:21-jdk-slim ==="
# Check common base images for curl availability
echo "openjdk:21-jdk-slim base image does not include curl or wget by default"
echo "This affects all services using this base image"
# List all service Dockerfiles to see the pattern
echo -e "\n=== All service Dockerfiles using healthcheck with curl ==="
fd -t f "Dockerfile" domain-*/Length of output: 858
owner-service 설정에서 두 가지 중요한 문제 확인됨
검증 결과, 원본 리뷰의 우려사항이 모두 확인되었습니다:
-
OwnerServiceApplication.java 완전 주석 처리: 파일의 모든 코드가 주석 처리되어 있어 서비스가 시작되지 않습니다 (
domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java) -
Healthcheck curl 실패:
openjdk:21-jdk-slim기본 이미지에는 curl이 포함되지 않습니다. 이는 owner-service뿐 아니라 restaurant-service와 member-service도 동일한 문제를 갖고 있습니다.
필수 수정 사항:
OwnerServiceApplication.java주석 해제- 모든 서비스의 Dockerfile에서 healthcheck 실패 문제 해결 (curl 설치 또는 대체 방법 적용)
🤖 Prompt for AI Agents
In docker-compose.yml around lines 157 to 181: the review found two bugs
affecting owner-service startup and healthchecks—(1)
domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java is
completely commented out so the Spring Boot app won't start; uncomment or
restore the original class content so the application has a valid main class and
can boot, and (2) the base image used by owner, restaurant, and member services
(openjdk:21-jdk-slim) lacks curl so the healthcheck fails; update each service
Dockerfile to install a lightweight HTTP client (e.g., apt-get update && apt-get
install -y curl or use wget or busybox-extras) or switch the healthcheck to use
a JVM-aware check (e.g., java -jar readiness probe) so the docker healthcheck
command succeeds.
| FROM gradle:8.5-jdk21 AS build | ||
| WORKDIR /app | ||
| COPY . . | ||
| RUN gradle :domain-owner:bootJar --no-daemon |
There was a problem hiding this comment.
🧩 Analysis chain
bootJar 빌드가 실패할 수 있습니다
OwnerServiceApplication.java가 전체 주석 처리되어 있어 bootJar 빌드가 실패합니다. Application 클래스의 주석을 해제한 후 Dockerfile을 테스트하세요.
🏁 Script executed:
#!/bin/bash
# Find OwnerServiceApplication.java file
fd -i "OwnerServiceApplication.java" -type fLength of output: 170
🏁 Script executed:
#!/bin/bash
# Search for the application class file with broader patterns
fd "Application\.java" -type f | grep -i ownerLength of output: 170
🏁 Script executed:
#!/bin/bash
# Check the structure of domain-owner module
find . -path "*/domain-owner/*" -name "*.java" -type f | head -20Length of output: 1661
🏁 Script executed:
#!/bin/bash
# View contents of OwnerServiceApplication.java
cat ./domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.javaLength of output: 358
OwnerServiceApplication.java 주석 해제 필요
검증 결과, OwnerServiceApplication.java 파일이 완전히 주석 처리되어 있습니다. @SpringBootApplication 애노테이션과 main 메서드가 모두 주석 처리된 상태에서는 gradle :domain-owner:bootJar 빌드가 실패합니다. Dockerfile 빌드를 진행하기 전에 이 파일의 주석을 해제해야 합니다.
🤖 Prompt for AI Agents
In domain-owner/Dockerfile around line 5, the docker build runs "gradle
:domain-owner:bootJar" but the build fails because OwnerServiceApplication.java
is fully commented out; open
domain-owner/src/main/java/.../OwnerServiceApplication.java and remove the
comment markers to restore the @SpringBootApplication annotation and the public
static void main(String[] args) method (and any required imports), save the
file, and then re-run the docker build so gradle can produce the bootJar.
| HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \ | ||
| CMD curl -f http://localhost:8084/actuator/health || exit 1 |
There was a problem hiding this comment.
Healthcheck 실행 실패 가능성
openjdk:21-jdk-slim 이미지에는 curl이 기본 설치되어 있지 않아 healthcheck가 실패합니다.
다음 중 하나를 선택하세요:
방법 1 (권장): wget 사용
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
- CMD curl -f http://localhost:8084/actuator/health || exit 1
+ CMD wget --no-verbose --tries=1 --spider http://localhost:8084/actuator/health || exit 1방법 2: curl 설치 추가
FROM openjdk:21-jdk-slim
WORKDIR /app
+RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
COPY --from=build /app/domain-owner/build/libs/*.jar app.jar방법 1이 이미지 크기를 줄이고 빌드 시간을 단축시킵니다.
📝 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.
| HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \ | |
| CMD curl -f http://localhost:8084/actuator/health || exit 1 | |
| RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* |
| public List<Owner> findAllByIds(List<String> ownerIds) { | ||
| return ownerRepository.findAllById(ownerIds); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
배치 조회 시 누락된 ID에 대한 처리를 검토해주세요.
findAllById는 존재하지 않는 ID를 조용히 생략합니다. 호출자가 요청한 모든 ID에 대한 결과를 기대할 수 있으므로, 누락된 ID가 있을 때의 동작을 명확히 하거나 검증 로직을 추가하는 것을 고려해보세요.
다음 스크립트로 이 메서드의 사용처를 확인하여 누락된 Owner 처리가 필요한지 검토해주세요:
🏁 Script executed:
#!/bin/bash
# Description: Check how findAllByIds is used in the codebase
# Search for usages of findAllByIds
rg -n -A 5 "findAllByIds" --type javaLength of output: 8208
추가 검증을 위해 Map 조회 후 null 처리 여부를 확인하겠습니다.
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Check null handling in ReservationService and FavoriteService after Map.get() calls
# Check ReservationService (api-owner)
echo "=== api-owner/ReservationService around line 45 ==="
rg -n -B 5 -A 10 "membersById\.get" api-owner/src/main/java/com/wellmeet/reservation/ReservationService.java
echo -e "\n=== api-user/ReservationService around line 41 ==="
rg -n -B 5 -A 10 "restaurantsById\.get\|availableDatesById\.get" api-user/src/main/java/com/wellmeet/reservation/ReservationService.java
echo -e "\n=== FavoriteService around line 41 ==="
rg -n -B 5 -A 10 "restaurantsById\.get" api-user/src/main/java/com/wellmeet/favorite/FavoriteService.javaLength of output: 1849
업스트림 검증이 있는지 확인하겠습니다.
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Check if there's validation before calling findAllByIds
echo "=== Check ReservationService getReservations method for validation ==="
rg -n -B 15 "memberDomainService.findAllByIds" api-owner/src/main/java/com/wellmeet/reservation/ReservationService.java | head -40
echo -e "\n=== Check FavoriteService for validation ==="
rg -n -B 15 "restaurantDomainService.findAllByIds" api-user/src/main/java/com/wellmeet/favorite/FavoriteService.java | head -40
echo -e "\n=== Check if there are any error/exception handlers ==="
rg -n "Optional\|orElse\|orElseThrow\|ifPresent" api-owner/src/main/java/com/wellmeet/reservation/ReservationService.javaLength of output: 1798
배치 조회 시 누락된 ID 처리 - 검증 완료, 개선 필요
OwnerDomainService.findAllByIds의 호출 패턴을 검증한 결과, 실제로 문제가 있습니다:
- OwnerApplicationService (33-35줄): findAllByIds 결과를 직접 stream().map()하며 null 체크 없음
- api-owner/ReservationService (40-55줄): memberDomainService.findAllByIds 후 member.getName() 등을 null 체크 없이 호출
- api-user/FavoriteService (36-42줄): restaurantDomainService.findAllByIds 후 restaurant.getId()를 null 체크 없이 호출
findAllById는 존재하지 않는 ID를 조용히 생략하므로, 데이터 일관성 문제(외래키 참조 대상이 삭제된 경우)가 발생하면 NullPointerException이 발생합니다.
필요한 조치:
- findAllByIds 호출 후 반환된 크기와 요청 ID 크기 검증 또는
- 호출자에서 null 체크 추가 또는
- 데이터 정합성 보장 로직 추가
🤖 Prompt for AI Agents
In domain-owner/src/main/java/com/wellmeet/domain/owner/OwnerDomainService.java
around lines 24 to 26, the method returns repository.findAllById(ownerIds) which
silently omits missing IDs; update the method to validate returned list against
the requested ownerIds (e.g., compare sizes or compute missing IDs) and either
throw a clear exception listing missing IDs or return a result object that
signals which IDs were not found (or at minimum log a warning), so callers are
not exposed to unexpected nulls when they dereference entities.
| //package com.wellmeet.domain; | ||
| // | ||
| //import org.springframework.boot.SpringApplication; | ||
| //import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| // | ||
| //@SpringBootApplication | ||
| //public class OwnerServiceApplication { | ||
| // | ||
| // public static void main(String[] args) { | ||
| // SpringApplication.run(OwnerServiceApplication.class, args); | ||
| // } | ||
| //} |
There was a problem hiding this comment.
Application 클래스가 주석 처리되어 독립 실행 불가
전체 Application 클래스가 주석 처리되어 있어 다음 문제가 발생합니다:
bootJar빌드 실패 (main 메서드 없음)- Docker 컨테이너 시작 불가
- Eureka 서버 등록 불가
- 독립 서비스로 실행 검증 불가
Phase 1-2와 동일한 빈 스캔 문제로 보이며, 다음을 시도해보세요:
-//package com.wellmeet.domain;
-//
-//import org.springframework.boot.SpringApplication;
-//import org.springframework.boot.autoconfigure.SpringBootApplication;
-//
-//@SpringBootApplication
-//public class OwnerServiceApplication {
-//
-// public static void main(String[] args) {
-// SpringApplication.run(OwnerServiceApplication.class, args);
-// }
-//}
+package com.wellmeet.domain;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication(scanBasePackages = "com.wellmeet")
+public class OwnerServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(OwnerServiceApplication.class, args);
+ }
+}scanBasePackages를 명시적으로 지정하면 빈 스캔 문제가 해결될 수 있습니다.
🤖 Prompt for AI Agents
In domain-owner/src/main/java/com/wellmeet/domain/OwnerServiceApplication.java
around lines 1 to 12, the entire Application class is commented out causing no
main method (bootJar, Docker start, Eureka registration failures); restore an
executable Spring Boot application by uncommenting or re-creating the class with
the package declaration, @SpringBootApplication annotation, and public static
void main(...) that calls SpringApplication.run(...); to fix the bean-scan
issues also set @SpringBootApplication(scanBasePackages =
{"com.wellmeet.domain","com.wellmeet.shared","<other-needed-packages>"}) so
Spring explicitly scans required packages, then rebuild and verify bootJar and
runtime startup succeed.
| jpa: | ||
| hibernate: | ||
| ddl-auto: update | ||
| properties: | ||
| hibernate: | ||
| format_sql: true | ||
| show-sql: false | ||
| open-in-view: false |
There was a problem hiding this comment.
ddl-auto: update 설정 검토 필요
ddl-auto: update는 로컬 개발 환경에는 편리하지만, 프로덕션 환경에서는 예상치 못한 스키마 변경이나 데이터 손실을 유발할 수 있습니다.
환경별 프로파일 설정을 권장합니다:
spring:
config:
activate:
on-profile: local
jpa:
hibernate:
ddl-auto: update
---
spring:
config:
activate:
on-profile: prod
jpa:
hibernate:
ddl-auto: validate # 프로덕션: validate만 사용또는 domain-reservation처럼 Flyway를 도입하여 스키마 변경을 버전 관리하는 것도 고려하세요.
🤖 Prompt for AI Agents
In domain-owner/src/main/resources/application.yml around lines 14 to 21, the
current setting uses jpa.hibernate.ddl-auto: update which is unsafe for
production; refactor configuration to move ddl-auto: update into a local/dev
profile and set the production profile to use ddl-auto: validate (or omit
ddl-auto entirely) via profile-specific YAML sections, and add instructions to
introduce Flyway (or another migration tool) for schema versioning in production
so schema changes are managed explicitly rather than auto-applied.
🚩 Issue
closed #60
🗣️ 리뷰 요구사항 (선택)
Summary by CodeRabbit
신규 기능
문서화
Chores