-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: 모집글 예외 처리 및 응답 포맷 통일 #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough이 변경사항은 모집(Recruitment) 관련 API의 응답 형식을 Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant RecruitmentController
participant RecruitmentService
participant OrderClient
participant RecruitmentParticipantRepo
participant UserRepo
participant StoreRepo
Client->>RecruitmentController: 모집 생성 요청(RecruitmentRequestDto)
RecruitmentController->>RecruitmentService: createRecruitment(dto)
RecruitmentService->>UserRepo: 사용자 조회
RecruitmentService->>StoreRepo: 가게 조회
RecruitmentService->>RecruitmentParticipantRepo: 모집 저장
RecruitmentService->>OrderClient: 주문 생성 요청
OrderClient-->>RecruitmentService: 주문 ID 반환
RecruitmentService->>RecruitmentParticipantRepo: 생성자 참여자 등록
RecruitmentService-->>RecruitmentController: 모집 ID 반환
RecruitmentController-->>Client: ApiResponse<모집 ID> 반환
Possibly related PRs
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🔭 Outside diff range comments (1)
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java (1)
81-89: 🛠️ Refactor suggestion참여 메소드의 예외 처리도 개선 필요
joinRecruitment메소드에서도 동일하게RuntimeException대신CustomException을 사용하는 것이 일관성 있는 예외 처리를 위해 바람직합니다.
🧹 Nitpick comments (3)
recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentResponseDto.java (1)
24-32: DTO 구조 설계 승인
RecruitmentResponseDto의 설계가 잘 구성되어 있습니다:
- 엔티티에서 DTO로의 명확한 매핑
- 중첩된
UserDto와StoreDto를 통한 적절한 데이터 캡슐화- Lombok을 활용한 보일러플레이트 코드 감소
다만 null 안전성을 위해 개선을 제안합니다.
public RecruitmentResponseDto(Recruitment recruitment) { this.id = recruitment.getId(); this.title = recruitment.getTitle(); this.description = recruitment.getDescription(); this.status = recruitment.getStatus(); this.deadlineTime = recruitment.getDeadlineTime(); - this.user = new UserDto(recruitment.getUser()); - this.store = new StoreDto(recruitment.getStore()); + this.user = recruitment.getUser() != null ? new UserDto(recruitment.getUser()) : null; + this.store = recruitment.getStore() != null ? new StoreDto(recruitment.getStore()) : null; }recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java (2)
49-49: 로깅 시스템 개선 필요현재
System.err.println을 사용하고 있는데, 프로덕션 환경에서는 적절한 로깅 프레임워크(예: SLF4J + Logback)를 사용하는 것이 바람직합니다.다음과 같이 개선하는 것을 권장합니다:
+ import lombok.extern.slf4j.Slf4j; + @Slf4j @Service public class OrderClient { - System.err.println("Order 서버 호출 중 예외 발생: " + e.getMessage()); + log.error("Order 서버 호출 중 예외 발생", e);
16-16: 하드코딩된 URL 개선 고려서버 URL이 하드코딩되어 있습니다. 환경별로 다른 URL을 사용해야 할 경우를 대비하여
application.properties나 환경 변수로 관리하는 것을 고려해보세요.예시:
@Value("${order.service.url:http://54.66.149.225:8100}") private String serverUrl;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java(3 hunks)recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentDetailDto.java(2 hunks)recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentResponseDto.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/entity/Recruitment.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java(2 hunks)recruitment-service/src/main/java/com/example/recruitment/entity/User.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java(3 hunks)recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java(3 hunks)
🔇 Additional comments (31)
recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java (1)
36-37: 유효성 검증 개선 승인메뉴 필드에
@NotNull과@Size(min = 1)어노테이션을 추가한 것은 좋은 개선사항입니다. 이를 통해 다음과 같은 이점을 얻을 수 있습니다:
- 메뉴 리스트가 null이 되는 것을 방지
- 최소 하나 이상의 메뉴 선택을 강제하여 비즈니스 로직의 무결성 보장
- 클라이언트에게 명확한 검증 메시지 제공
recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java (1)
52-55: Swagger 예외 처리 개선 승인Swagger 관련 요청(
/v3/api-docs,/swagger경로)에 대한 예외를 다시 throw하여 Swagger UI가 정상적으로 작동할 수 있도록 한 것은 훌륭한 개선사항입니다. 이를 통해 API 문서화 기능이 글로벌 예외 핸들러에 의해 방해받지 않게 됩니다.recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java (2)
20-20: 지연 로딩 설정 승인
@ManyToOne관계에 명시적으로fetch = FetchType.LAZY를 설정한 것은 성능 최적화 측면에서 우수한 개선사항입니다. 이를 통해 필요하지 않은 경우 연관 엔티티를 로드하지 않아 N+1 문제를 방지할 수 있습니다.Also applies to: 25-25
38-43: @PrePersist 생명주기 콜백 승인
@PrePersist어노테이션을 사용하여joinedAt필드를 자동으로 설정하는 것은 좋은 접근법입니다. 이를 통해:
- 참여 시간이 자동으로 설정되어 데이터 일관성 보장
- null 체크를 통해 기존 값이 있는 경우 덮어쓰지 않음
- 엔티티 저장 시점의 타임스탬프를 정확히 기록
recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentResponseDto.java (1)
34-46: 중첩 DTO 클래스 설계 승인
UserDto와StoreDto중첩 클래스들이 잘 설계되었습니다:
- static 중첩 클래스로 적절한 캡슐화
- 필요한 필드만 노출하여 데이터 은닉 원칙 준수
- 일관된 생성자 패턴 사용
이러한 구조는 API 응답의 일관성을 높이고 유지보수성을 향상시킵니다.
Also applies to: 48-62
recruitment-service/src/main/java/com/example/recruitment/entity/Recruitment.java (6)
11-12: Lombok 어노테이션 추가 확인됨
@Getter와@Setter어노테이션이 추가되어 보일러플레이트 코드가 줄어들었습니다. 좋은 개선사항입니다.
22-24: ManyToOne 관계 설정이 개선됨
FetchType.LAZY와nullable = false설정이 추가되어 성능과 데이터 무결성이 향상되었습니다. 지연 로딩은 N+1 문제를 방지하는 데 도움이 됩니다.
27-29: Store 관계 설정도 일관성 있게 개선됨User와 동일하게 지연 로딩과 null 불가 제약이 적용되어 일관성 있는 설계가 되었습니다.
33-33: 컬렉션 초기화로 NPE 방지
participants리스트를 빈ArrayList로 초기화하여NullPointerException을 방지했습니다. 이는 좋은 방어적 프로그래밍 관행입니다.
36-49: 필수 필드들에 null 불가 제약 추가모든 필수 필드에
@Column(nullable = false)어노테이션을 추가하여 데이터베이스 레벨에서 데이터 무결성을 보장하게 되었습니다. 특히status필드에 예시 값들이 주석으로 추가된 것도 좋습니다.
57-57: orderIds 컬렉션도 초기화됨
orderIds리스트도 빈ArrayList로 초기화하여 일관성을 유지했습니다.recruitment-service/src/main/java/com/example/recruitment/entity/User.java (4)
8-8: 테이블명 백틱 처리로 예약어 충돌 방지
user는 많은 데이터베이스에서 예약어이므로 백틱으로 감싸는 것이 좋은 방어적 조치입니다.
13-13: Jackson 직렬화 에러 방지
@JsonIgnoreProperties로 Hibernate 프록시 관련 속성들을 무시하도록 설정하여 직렬화 시 발생할 수 있는 에러를 방지했습니다.
17-17: ID 생성 전략 명시적 지정
GenerationType.IDENTITY를 명시적으로 지정하여 의도가 명확해졌습니다.
20-24: 필드 제약 조건 강화
name과nullable = false제약을 추가하고,unique = true까지 추가하여 데이터 무결성이 크게 향상되었습니다.recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java (4)
36-36: 메소드 반환 타입 개선
createRecruitment메소드가 이제 생성된 모집글의 ID를 반환하도록 변경되었습니다. 이는 API 응답에서 생성된 리소스의 식별자를 제공할 수 있게 해주는 좋은 개선사항입니다.
51-51: 엔터티 저장 후 ID 생성 확인
recruitmentRepository.save(recruitment)후 주석으로 "ID 생성됨"을 명시한 것이 좋습니다. 이후 로직에서recruitment.getId()를 사용하므로 이 시점에서 ID가 생성되는 것이 중요합니다.
63-68: 모집자 자동 참여 등록 로직 추가모집자를 자동으로 참여자로 등록하는 로직이 추가되었습니다. 이는 비즈니스 로직상 합리적이며, 트랜잭션 내에서 처리되어 데이터 일관성이 보장됩니다.
69-69: 생성된 ID 반환메소드가 이제 생성된 모집글의 ID를 반환하여 호출자가 생성된 리소스를 식별할 수 있게 되었습니다.
recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java (1)
22-51: 에러 핸들링 대폭 강화됨
createOrder메소드의 에러 핸들링이 크게 개선되었습니다:
- HTTP 응답 상태 코드 검증
- 응답 본문 구조 검증 (
data,orderId필드 존재 확인)orderId의Long변환 검증- 상세한 에러 메시지 제공
이러한 개선으로 외부 서비스 호출의 안정성이 크게 향상되었습니다.
recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentDetailDto.java (4)
3-3: 적절한 import 추가새로운 생성자에서
Recruitment엔티티를 사용하기 위한 import가 올바르게 추가되었습니다.
22-25: 엔티티에서 DTO로의 적절한 변환엔티티를 직접 노출하는 대신 중첩 DTO 클래스를 사용하는 것은 좋은 설계입니다. 이는 다음과 같은 이점을 제공합니다:
- 엔티티와 API 응답의 분리
- 민감한 정보 노출 방지
- 응답 구조의 유연성 확보
새로 추가된
orderIds필드는 참여자의 주문 ID 목록을 제공하여 추가적인 정보를 제공합니다.
27-37: 생성자 구현이 올바르게 작성됨새로운 생성자가 모든 필드를 적절하게 초기화하고 있습니다:
- 기본 필드들은
Recruitment엔티티에서 직접 매핑- 중첩 DTO들은 각각의 생성자를 통해 변환
- 이미 변환된 참여자 목록과 주문 ID 목록을 받아 할당
주석을 통해 participantUsers가 이미 변환된 상태로 전달된다는 것을 명확히 표시한 것도 좋습니다.
39-67: 중첩 DTO 클래스가 적절하게 구현됨
UserDto와StoreDto중첩 클래스들이 잘 설계되었습니다:
- 필요한 필드만 선별적으로 노출하여 보안 강화
- 각 엔티티에서 DTO로 변환하는 생성자 제공
- static 중첩 클래스로 구현하여 응집도 향상
- Lombok 어노테이션으로 보일러플레이트 코드 감소
이러한 구조는 API 응답의 일관성과 유지보수성을 크게 향상시킵니다.
recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java (7)
3-3: 필요한 import문들이 적절하게 추가됨ApiResponse 표준화, 새로운 DTO 클래스, 예외 처리 개선, 그리고 추가 리포지토리 사용을 위한 import문들이 올바르게 추가되었습니다.
Also applies to: 6-6, 10-12, 15-15, 19-19, 25-25
38-43: API 응답 형식이 올바르게 표준화됨모집글 생성 엔드포인트가 다음과 같이 개선되었습니다:
ApiResponse래퍼를 사용한 일관된 응답 형식- HTTP 201 Created 상태 코드 사용
- 생성된 리소스의 ID를 반환하여 RESTful 설계 원칙 준수
이러한 변경은 API의 일관성과 사용성을 크게 향상시킵니다.
46-51: 참여 엔드포인트 개선이 적절함
@Valid어노테이션 추가로 요청 데이터 유효성 검증 강화ApiResponse래퍼를 사용한 일관된 응답 형식 적용- 명확한 성공 메시지 제공
이러한 변경으로 API의 안정성과 일관성이 향상되었습니다.
54-60: 조회 엔드포인트의 응답 구조가 적절하게 개선됨
RecruitmentResponseDto를 사용하여 엔티티 직접 노출 방지- 스트림 API를 활용한 효율적인 변환 로직
- 표준화된 응답 형식으로 일관성 확보
엔티티 대신 DTO를 사용하는 것은 API 보안과 유지보수성 측면에서 모범 사례입니다.
111-157: 상태 관리 엔드포인트들이 일관되게 개선됨모든 상태 업데이트 엔드포인트에서 다음과 같은 개선사항이 적용되었습니다:
CustomException을 사용한 표준화된 예외 처리ApiResponse.fail을 사용한 에러 응답 표준화- 적절한 HTTP 상태 코드 사용 (400 Bad Request, 200 OK)
- 명확한 에러 메시지와 코드 제공
특히 비즈니스 로직 검증 실패 시 적절한 에러 응답을 반환하는 것이 우수합니다.
167-190: 수정 엔드포인트의 보안과 검증이 강화됨다음과 같은 중요한 개선사항들이 적용되었습니다:
- 작성자 권한 검증 유지로 보안 강화
- Store 엔티티 존재 여부 확인으로 데이터 무결성 보장
CustomException을 사용한 일관된 예외 처리- 적절한 HTTP 상태 코드 사용 (403 Forbidden)
StoreRepository를 통한 Store 엔티티 유효성 검사 추가는 특히 중요한 개선사항입니다.
92-108: 나머지 엔드포인트들도 일관되게 개선됨사용자별 조회 엔드포인트와 삭제 엔드포인트에서도 다음과 같은 개선사항이 적용되었습니다:
RecruitmentResponseDto를 사용한 안전한 응답 구조ApiResponse래퍼를 사용한 일관된 응답 형식CustomException을 사용한 표준화된 예외 처리- 명확한 성공/실패 메시지 제공
전체 컨트롤러에 걸쳐 일관된 패턴이 적용되어 유지보수성이 크게 향상되었습니다.
Also applies to: 160-165
| // ❗ IllegalArgumentException 처리 추가 | ||
| @ExceptionHandler(IllegalArgumentException.class) | ||
| public ResponseEntity<ApiResponse<Object>> handleIllegalArgument(IllegalArgumentException e) { | ||
| return ResponseEntity | ||
| .badRequest() | ||
| .body(ApiResponse.fail(4000, e.getMessage(), HttpStatus.BAD_REQUEST)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
IllegalArgumentException 핸들러 추가 승인
IllegalArgumentException에 대한 전용 핸들러를 추가한 것은 좋은 개선사항입니다. ApiResponse.fail() 메서드를 사용하여 일관된 응답 형식을 유지하고 있습니다.
다만 하드코딩된 에러 코드 4000이 다른 에러 코드들과 일관성을 유지하는지 확인이 필요합니다.
🏁 Script executed:
#!/bin/bash
# Description: Check if error code 4000 is consistent with other error codes in the system
# Search for other error codes in the codebase
rg -A 2 -B 2 "ApiResponse\.fail\("
# Search for ErrorCode enum or similar constants
rg -A 5 "class.*ErrorCode|enum.*ErrorCode"Length of output: 5100
IllegalArgumentException 핸들러 추가 – 에러 코드 관리 일관성 필요
IllegalArgumentException 핸들러 구현은 적절하나, 아래와 같이 하드코딩된 4000 코드 관리를 개선할 것을 권장드립니다.
- ErrorCode enum에 신규 상수 추가
- 예:
INVALID_ARGUMENT(4000, HttpStatus.BAD_REQUEST, "잘못된 인자입니다.") - 파일:
recruitment-service/src/main/java/com/example/recruitment/exception/ErrorCode.java
- 예:
- GlobalExceptionHandler에서 하드코딩된
4000→ErrorCode.INVALID_ARGUMENT.getCode()등으로 교체- 파일:
recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java
- 파일:
이렇게 수정하면 에러 코드 정의·관리 일관성을 높일 수 있습니다.
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java
around lines 39 to 45, replace the hardcoded error code 4000 in the
IllegalArgumentException handler with a constant from the ErrorCode enum. First,
add a new constant INVALID_ARGUMENT with code 4000, HttpStatus.BAD_REQUEST, and
a descriptive message in
recruitment-service/src/main/java/com/example/recruitment/exception/ErrorCode.java.
Then update the handler to use ErrorCode.INVALID_ARGUMENT.getCode() and related
properties instead of hardcoded values to ensure consistent error code
management.
| User user = userRepository.findById(dto.getUserId()) | ||
| .orElseThrow(() -> new RuntimeException("사용자 없음")); | ||
| Store store = storeRepository.findById(dto.getStoreId()) | ||
| .orElseThrow(() -> new RuntimeException("가게 없음")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
예외 처리 개선 필요
현재 RuntimeException을 직접 사용하고 있는데, PR 목표에 따르면 CustomException으로 변경해야 합니다. 또한 에러 메시지가 한국어로 되어 있어 국제화 이슈가 있을 수 있습니다.
다음과 같이 개선하는 것을 권장합니다:
- .orElseThrow(() -> new RuntimeException("사용자 없음"));
+ .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));- .orElseThrow(() -> new RuntimeException("가게 없음"));
+ .orElseThrow(() -> new CustomException(ErrorCode.STORE_NOT_FOUND));📝 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.
| User user = userRepository.findById(dto.getUserId()) | |
| .orElseThrow(() -> new RuntimeException("사용자 없음")); | |
| Store store = storeRepository.findById(dto.getStoreId()) | |
| .orElseThrow(() -> new RuntimeException("가게 없음")); | |
| User user = userRepository.findById(dto.getUserId()) | |
| .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); | |
| Store store = storeRepository.findById(dto.getStoreId()) | |
| .orElseThrow(() -> new CustomException(ErrorCode.STORE_NOT_FOUND)); |
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java
around lines 37 to 40, replace the generic RuntimeException with a
CustomException to align with the PR goals. Also, update the error messages from
Korean to English or use a message code for internationalization support. Ensure
the CustomException includes meaningful error codes or messages that can be
localized.
| } | ||
|
|
||
| if (responseBody != null && responseBody.containsKey("data")) { | ||
| Map<String, Object> data = (Map<String, Object>) responseBody.get("data"); | ||
| if (data == null || !data.containsKey("orderId")) { | ||
| throw new RuntimeException("Order 서버 응답 'data'에 'orderId'가 없음"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
예외 처리 일관성 개선
다른 서비스 클래스들과 마찬가지로 RuntimeException 대신 CustomException을 사용하여 예외 처리의 일관성을 유지하는 것이 좋겠습니다.
Also applies to: 45-46, 50-50
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java
around lines 30-35, 45-46, and 50, replace the use of RuntimeException with
CustomException for throwing errors. This change ensures consistent exception
handling across service classes. Locate all RuntimeException throws in these
lines and update them to throw CustomException instead, preserving the original
error messages.
| public ResponseEntity<ApiResponse<RecruitmentDetailDto>> getRecruitmentDetail(@PathVariable Long recruitmentId) { | ||
| Recruitment recruitment = recruitmentRepository.findById(recruitmentId) | ||
| .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); | ||
|
|
||
| List<RecruitmentParticipant> participants = participantRepository.findByRecruitmentId(recruitmentId); | ||
|
|
||
| List<RecruitmentParticipant> participants = | ||
| participantRepository.findByRecruitmentId(recruitmentId); | ||
| List<User> participantUsers = participants.stream() | ||
| .map(RecruitmentParticipant::getUser) | ||
| List<RecruitmentDetailDto.UserDto> participantUsers = participants.stream() | ||
| .map(p -> new RecruitmentDetailDto.UserDto(p.getUser())) | ||
| .toList(); | ||
|
|
||
| RecruitmentDetailDto dto = new RecruitmentDetailDto(); | ||
| dto.setId(recruitment.getId()); | ||
| dto.setTitle(recruitment.getTitle()); | ||
| dto.setDescription(recruitment.getDescription()); | ||
| dto.setStatus(recruitment.getStatus()); | ||
| dto.setDeadlineTime(recruitment.getDeadlineTime()); | ||
| dto.setUser(recruitment.getUser()); | ||
| dto.setStore(recruitment.getStore()); | ||
| dto.setParticipants(participantUsers); | ||
|
|
||
| return ResponseEntity.ok(dto); | ||
| List<Long> orderIds = participants.stream() | ||
| .map(RecruitmentParticipant::getOrderId) | ||
| .toList(); | ||
|
|
||
| RecruitmentDetailDto dto = new RecruitmentDetailDto(recruitment, participantUsers, orderIds); | ||
|
|
||
| return ResponseEntity.ok(ApiResponse.ok(dto, "모집 상세 조회 성공")); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
상세 조회 엔드포인트가 크게 개선됨
다음과 같은 개선사항들이 적용되었습니다:
CustomException을 사용한 표준화된 예외 처리- 참여자 정보를 안전하게 DTO로 변환
- 주문 ID 목록 제공으로 추가 정보 노출
- 일관된 응답 형식 적용
참여자의 getOrderId() 호출 시 null 안전성을 확인해 보겠습니다:
🏁 Script executed:
#!/bin/bash
# Description: RecruitmentParticipant 엔티티의 orderid 필드 null 안전성 확인
# RecruitmentParticipant 클래스에서 orderId 필드와 getter 메서드 확인
ast-grep --pattern 'class RecruitmentParticipant {
$$$
orderId
$$$
}'
# orderId getter 메서드 확인
rg -A 5 "getOrderId" --type javaLength of output: 944
다음 스크립트로 RecruitmentParticipant 엔티티 파일 경로를 찾고, orderId 필드의 선언부와 null 허용 여부를 함께 확인해 보겠습니다:
🏁 Script executed:
#!/bin/bash
# 1) 엔티티 클래스 파일 위치 확인
entity_file=$(rg -l "class\s\+RecruitmentParticipant" --type java)
echo ">> RecruitmentParticipant 엔티티 파일: $entity_file"
# 2) orderId 필드 선언부 및 nullable 설정 확인
rg -n "@Column.*nullable" -C2 "$entity_file"
rg -n "private\s\+Long\s\+orderId" -C2 "$entity_file"
# 3) getter 구현부 확인
rg -n "getOrderId" -C3 "$entity_file"Length of output: 376
다음 스크립트로 RecruitmentParticipant 엔티티 파일 위치를 먼저 찾고, orderId 선언부와 nullable 설정, getter 구현을 확인해 보겠습니다:
🏁 Script executed:
#!/bin/bash
# 1) 엔티티 클래스 파일 경로 검색
entity_file=$(fd RecruitmentParticipant.java)
echo ">> RecruitmentParticipant 엔티티 파일 경로: $entity_file"
# 2) 클래스 선언부 확인
rg -n "class RecruitmentParticipant" -C2 "$entity_file"
# 3) orderId 필드 선언부 및 @Column nullable 설정 확인
rg -n "private\s\+Long\s\+orderId" -C2 "$entity_file"
rg -n "@Column.*nullable" -C2 "$entity_file"
# 4) getOrderId() 구현 확인
rg -n "getOrderId" -C3 "$entity_file"Length of output: 1142
RecruitmentParticipant 엔티티에 orderId 필드 및 getter 미정의로 인한 컴파일 오류
현재 p.getOrderId() 호출 시 대응되는 필드나 메서드가 존재하지 않습니다. 아래 위치를 확인하고 수정이 필요합니다:
- 파일:
recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java
수정 예시:
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class RecruitmentParticipant {
// ... 기존 필드 ...
@Column(name = "order_id", nullable = true)
private Long orderId;
public Long getOrderId() {
return this.orderId;
}
// ... 나머지 메서드 ...
}필드와 getter를 추가하거나, Lombok의 @Getter 어노테이션을 클래스 레벨에 적용해 주세요.
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java
around the declaration of the RecruitmentParticipant class, add a private Long
field named orderId annotated with @Column(name = "order_id", nullable = true)
if it does not exist. Also, add a public getter method getOrderId() that returns
this.orderId. Alternatively, you can add Lombok's @Getter annotation at the
class level to generate the getter automatically. This will fix the compilation
error caused by calling p.getOrderId() in RecruitmentController.java lines
72-89.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java (1)
87-89:⚠️ Potential issueRecruitmentParticipant 엔티티에 getOrderId() 메서드 구현 필요
이전 리뷰에서 지적된 대로
RecruitmentParticipant엔티티에orderId필드와 getter 메서드가 정의되어 있지 않아 컴파일 오류가 발생합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
recruitment-service/src/main/java/com/example/recruitment/config/RestTemplateConfig.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java(3 hunks)recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentDetailDto.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/service/UserClient.java(1 hunks)recruitment-service/src/main/resources/application.properties(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- recruitment-service/src/main/resources/application.properties
- recruitment-service/src/main/java/com/example/recruitment/config/RestTemplateConfig.java
🚧 Files skipped from review as they are similar to previous changes (1)
- recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentDetailDto.java
🔇 Additional comments (3)
recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java (3)
39-45: 모집글 생성 API가 RESTful 원칙에 맞게 개선됨201 Created 상태 코드와 함께 생성된 리소스 ID를 반환하는 방식이 적절합니다.
141-144: 상태 검증 로직과 에러 응답 형식이 일관되게 개선됨CONFIRMED 상태 검증과 함께 명확한 에러 코드(4001)와 메시지를 반환합니다.
178-191: 권한 검증 및 연관 엔티티 검증 로직이 적절히 구현됨작성자 확인과 Store 존재 여부 검증이 추가되어 데이터 무결성이 향상되었습니다.
| public UserDto getUserById(Long userId) { | ||
| String url = userServerUrl + "/api/v1/users/" + userId; | ||
| ResponseEntity<UserDto> response = restTemplate.getForEntity(url, UserDto.class); | ||
| return response.getBody(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
외부 서비스 호출 시 예외 처리 및 null 안전성 개선 필요
RestTemplate 호출 시 네트워크 오류, 타임아웃, 404 응답 등 다양한 예외가 발생할 수 있으며, response body가 null일 수 있습니다.
아래와 같이 예외 처리와 null 체크를 추가하세요:
- public UserDto getUserById(Long userId) {
- String url = userServerUrl + "/api/v1/users/" + userId;
- ResponseEntity<UserDto> response = restTemplate.getForEntity(url, UserDto.class);
- return response.getBody();
- }
+ public UserDto getUserById(Long userId) {
+ try {
+ String url = userServerUrl + "/api/v1/users/" + userId;
+ ResponseEntity<UserDto> response = restTemplate.getForEntity(url, UserDto.class);
+
+ if (response.getBody() == null) {
+ throw new CustomException(ErrorCode.USER_NOT_FOUND);
+ }
+
+ return response.getBody();
+ } catch (RestClientException e) {
+ throw new CustomException(ErrorCode.EXTERNAL_SERVICE_ERROR,
+ "사용자 서비스 호출 실패: " + e.getMessage());
+ }
+ }또한 URL 생성 시 UriComponentsBuilder 사용을 고려하세요:
String url = UriComponentsBuilder.fromHttpUrl(userServerUrl)
.path("/api/v1/users/{userId}")
.buildAndExpand(userId)
.toUriString();🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/UserClient.java
around lines 20 to 24, the current method lacks exception handling for
RestTemplate calls and does not safely handle null response bodies. Refactor the
method to wrap the RestTemplate call in a try-catch block to handle exceptions
like network errors and timeouts, and check if the response body is null before
returning. Also, replace the URL string concatenation with UriComponentsBuilder
to build the URL safely and cleanly.
| RecruitmentDetailDto.UserDto writer = userClient.getUserById(recruitment.getUser().getId()); | ||
|
|
||
| List<RecruitmentParticipant> participants = | ||
| participantRepository.findByRecruitmentId(recruitmentId); | ||
| List<User> participantUsers = participants.stream() | ||
| .map(RecruitmentParticipant::getUser) | ||
| // ✅ 참여자 정보 | ||
| List<RecruitmentParticipant> participants = participantRepository.findByRecruitmentId(recruitmentId); | ||
| List<RecruitmentDetailDto.UserDto> participantUsers = participants.stream() | ||
| .map(p -> userClient.getUserById(p.getUser().getId())) | ||
| .toList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
UserClient 호출 시 예외 처리 필요
UserClient의 getUserById 메서드는 외부 서비스 호출이므로 실패할 수 있습니다. 특히 반복문 내에서 호출 시 일부 사용자 정보 조회 실패로 전체 요청이 실패할 수 있습니다.
부분 실패를 허용하는 방식으로 개선하거나, 최소한 적절한 예외 처리를 추가하세요:
List<RecruitmentDetailDto.UserDto> participantUsers = participants.stream()
- .map(p -> userClient.getUserById(p.getUser().getId()))
+ .map(p -> {
+ try {
+ return userClient.getUserById(p.getUser().getId());
+ } catch (CustomException e) {
+ // 로그 기록 후 기본값 반환 또는 예외 재발생
+ return new RecruitmentDetailDto.UserDto(
+ p.getUser().getId(),
+ "사용자 정보 조회 실패",
+ null
+ );
+ }
+ })
.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.
| RecruitmentDetailDto.UserDto writer = userClient.getUserById(recruitment.getUser().getId()); | |
| List<RecruitmentParticipant> participants = | |
| participantRepository.findByRecruitmentId(recruitmentId); | |
| List<User> participantUsers = participants.stream() | |
| .map(RecruitmentParticipant::getUser) | |
| // ✅ 참여자 정보 | |
| List<RecruitmentParticipant> participants = participantRepository.findByRecruitmentId(recruitmentId); | |
| List<RecruitmentDetailDto.UserDto> participantUsers = participants.stream() | |
| .map(p -> userClient.getUserById(p.getUser().getId())) | |
| .toList(); | |
| RecruitmentDetailDto.UserDto writer = userClient.getUserById(recruitment.getUser().getId()); | |
| // ✅ 참여자 정보 | |
| List<RecruitmentParticipant> participants = participantRepository.findByRecruitmentId(recruitmentId); | |
| List<RecruitmentDetailDto.UserDto> participantUsers = participants.stream() | |
| - .map(p -> userClient.getUserById(p.getUser().getId())) | |
| + .map(p -> { | |
| + try { | |
| + return userClient.getUserById(p.getUser().getId()); | |
| + } catch (CustomException e) { | |
| + // TODO: 로깅 추가 | |
| + return new RecruitmentDetailDto.UserDto( | |
| + p.getUser().getId(), | |
| + "사용자 정보 조회 실패", | |
| + null | |
| + ); | |
| + } | |
| + }) | |
| .toList(); |
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java
around lines 79 to 85, the calls to userClient.getUserById may fail due to
external service issues, especially inside the stream mapping for participants.
To fix this, add exception handling around each getUserById call to catch and
handle failures gracefully, such as returning a default or null user DTO, or
logging the error without failing the entire request. This ensures partial
failures do not cause the whole process to fail.
📌 주요 변경 사항
NOT_FOUND코드로 응답IllegalArgumentException→CustomException전환GlobalExceptionHandler예외 통과 처리RecruitmentDetailDto→ 내부 UserDto/StoreDto 구조 변경 및 null 안전 처리✅ 테스트 방법
/api/v1/recruitments/{id}/swagger-ui/index.html)🔎 기타 참고
ErrorCode.java,CustomException.java,GlobalExceptionHandler.java변경 없음develop브랜치 기준으로 머지 예정Summary by CodeRabbit
신규 기능
개선 사항
버그 수정