-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 모집 생성 및 참여 시 주문 생성 기능 구현 #52
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이 변경사항은 모집 서비스에 주문 연동 기능을 도입하고, 비즈니스 로직을 컨트롤러에서 서비스 계층으로 이전합니다. Swagger 테스트 컨트롤러와 주문 관련 DTO, 서비스, 클라이언트가 추가되었으며, 예외 처리 및 엔티티 필드가 확장되었습니다. 빌드 설정과 API 경로도 일부 수정되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant RecruitmentController
participant RecruitmentService
participant OrderClient
participant OrderService
User->>RecruitmentController: 모집글 생성 요청(RecruitmentRequestDto)
RecruitmentController->>RecruitmentService: createRecruitment(dto)
RecruitmentService->>OrderClient: createOrder(OrderRequestDto)
OrderClient->>OrderService: POST /api/v1/orders
OrderService-->>OrderClient: 주문 생성 결과(orderId)
OrderClient-->>RecruitmentService: orderId 반환
RecruitmentService->>RecruitmentService: 모집글에 orderId 저장
RecruitmentService-->>RecruitmentController: 처리 완료
User->>RecruitmentController: 모집 참여 요청(joinRecruitment)
RecruitmentController->>RecruitmentService: joinRecruitment(recruitmentId, userId, orderDto)
RecruitmentService->>OrderClient: createOrder(OrderRequestDto)
OrderClient->>OrderService: POST /api/v1/orders
OrderService-->>OrderClient: 주문 생성 결과(orderId)
OrderClient-->>RecruitmentService: orderId 반환
RecruitmentService->>RecruitmentParticipantRepository: 참여자 저장
RecruitmentService-->>RecruitmentController: 처리 완료
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: 13
🧹 Nitpick comments (6)
recruitment-service/src/main/java/com/example/recruitment/controller/SwaggerTestController.java (1)
7-14: 테스트용 컨트롤러 개선 제안Swagger 테스트용 엔드포인트가 올바르게 구현되었습니다. 다음과 같은 개선사항을 고려해보세요:
- 프로덕션 환경에서는 비활성화 검토
- 프로젝트의 ApiResponse 패턴과 일관성 유지
다음과 같이 개선할 수 있습니다:
+import com.example.recruitment.common.ApiResponse; +import org.springframework.context.annotation.Profile; +import org.springframework.http.ResponseEntity; +@Profile("!prod") // 프로덕션 환경에서 비활성화 @RestController @RequestMapping("/swagger-test") public class SwaggerTestController { @GetMapping - public String test() { - return "Swagger works!"; + public ResponseEntity<ApiResponse<String>> test() { + return ResponseEntity.ok(ApiResponse.success("Swagger works!", "Swagger 테스트가 성공적으로 완료되었습니다.")); } }recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java (1)
39-44: Swagger 요청 처리 로직이 올바르게 구현되었습니다.HttpServletRequest를 통해 요청 URI를 확인하고 Swagger 관련 경로에 대해서는 예외 처리를 우회하는 로직이 적절합니다.
경로 검사 로직을 더욱 견고하게 만들 수 있습니다:
- if (path != null && (path.contains("/v3/api-docs") || path.contains("/swagger"))) { + if (path != null && (path.startsWith("/v3/api-docs") || path.startsWith("/swagger-ui") || path.contains("/swagger"))) { throw e; // Swagger 요청은 예외 처리하지 않고 Spring에게 맡김 }이렇게 하면 더 정확한 경로 매칭이 가능합니다.
recruitment-service/src/main/java/com/example/recruitment/dto/order/OrderResponseDto.java (1)
1-10: 간단하고 명확한 응답 DTO입니다.현재 요구사항에 맞는 간결한 구현입니다. Lombok을 사용하여 보일러플레이트 코드를 줄인 것도 좋습니다.
향후 확장성을 고려한다면 다음과 같은 개선을 고려해볼 수 있습니다:
@Getter @Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor public class OrderResponseDto { + @NotNull private Long orderId; + // 향후 주문 상태, 생성 시간 등 필드 추가 가능 }recruitment-service/src/main/java/com/example/recruitment/entity/Recruitment.java (1)
35-35: 주석의 오타를 수정하세요.주석에 불필요한 "가"가 추가되어 있습니다.
- //카테고리 추가가 + //카테고리 추가recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java (2)
29-34: 서비스 계층으로의 비즈니스 로직 위임이 좋습니다컨트롤러에서 직접 repository를 조작하던 것을
RecruitmentService로 위임한 것은 좋은 아키텍처 개선입니다. 관심사 분리가 잘 이루어졌습니다.반환 타입의 타입 안전성을 개선할 수 있습니다:
-public ResponseEntity<?> createRecruitment(@Valid @RequestBody RecruitmentRequestDto dto) { +public ResponseEntity<String> createRecruitment(@Valid @RequestBody RecruitmentRequestDto dto) {
58-79: 상세 조회 로직이 개선되었습니다불필요한 조인 상태 체크와 엔티티 생성 로직이 제거되어 코드가 더 명확해졌습니다.
DTO 수동 매핑을 개선할 수 있습니다:
- RecruitmentDetailDto dto = new RecruitmentDetailDto(); - dto.setId(recruitment.getId()); - dto.setTitle(recruitment.getTitle()); - // ... 기타 setter 호출들 + RecruitmentDetailDto dto = RecruitmentDetailDto.builder() + .id(recruitment.getId()) + .title(recruitment.getTitle()) + .description(recruitment.getDescription()) + .status(recruitment.getStatus()) + .deadlineTime(recruitment.getDeadlineTime()) + .user(recruitment.getUser()) + .store(recruitment.getStore()) + .participants(participantUsers) + .build();또는 MapStruct 같은 매핑 라이브러리 사용을 고려해보세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
recruitment-service/build.gradle(2 hunks)recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java(4 hunks)recruitment-service/src/main/java/com/example/recruitment/controller/SwaggerTestController.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java(2 hunks)recruitment-service/src/main/java/com/example/recruitment/dto/order/OrderRequestDto.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/dto/order/OrderResponseDto.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/entity/Recruitment.java(2 hunks)recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java(1 hunks)recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java(3 hunks)recruitment-service/src/main/java/com/example/recruitment/repository/RecruitmentParticipantRepository.java(1 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(1 hunks)recruitment-service/src/main/resources/application.properties(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java (1)
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java (1)
Service(17-92)
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java (1)
recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java (1)
Service(10-40)
🔇 Additional comments (14)
recruitment-service/src/main/resources/application.properties (1)
10-10: 화이트스페이스만 변경됨 — 기능에 영향 없음application.properties 파일 끝에 불필요한 빈 줄이 추가되었습니다. 설정 동작에는 영향을 주지 않으므로 이 변경은 무시해도 됩니다.
recruitment-service/build.gradle (2)
31-31: Swagger 의존성 추가가 적절합니다.SpringDoc OpenAPI 의존성이 올바르게 추가되어 Swagger UI 통합을 지원합니다.
3-4:Details
❓ Verification inconclusive
버전 다운그레이드에 대한 검증이 필요합니다.
Spring Boot 버전이 3.4.5에서 3.1.5로, Spring dependency management가 1.1.7에서 1.1.3으로 다운그레이드되었습니다. 일반적으로 최신 버전을 사용하는 것이 보안 및 버그 수정 측면에서 권장됩니다.
다음 스크립트를 통해 버전 호환성과 보안 이슈를 확인해주세요:
🏁 Script executed:
#!/bin/bash # 설명: Spring Boot 버전별 보안 취약점 및 최신 버전 확인 # Spring Boot 최신 버전 확인 curl -s https://api.github.com/repos/spring-projects/spring-boot/releases/latest | jq '.tag_name' # Spring Boot 버전별 보안 공지사항 확인 curl -s "https://api.github.com/repos/spring-projects/spring-boot/security/advisories" | jq '.[] | select(.severity == "high" or .severity == "critical") | {summary, severity, published_at}'Length of output: 366
버전 다운그레이드 호환성 및 보안 검증 필요
Spring Boot 플러그인 버전이
3.4.5→3.1.5로, Spring dependency-management 플러그인이1.1.7→1.1.3으로 낮아졌습니다. 현재 GitHub 릴리스 기준 최신 Spring Boot는v3.5.0이며, 다운그레이드된 버전들은 보안 패치나 API 변경사항에서 뒤처질 수 있습니다. 다음 항목을 확인해주세요:
- recruitment-service/build.gradle (3–4행):
•org.springframework.boot플러그인 버전
•io.spring.dependency-management플러그인 버전- 해당 버전들의 보안 취약점 내역 및 패치 현황
- 버전 변경이 서비스 기능(특히 Spring MVC, AOP, 데이터 바인딩 등)에 미치는 영향
위 사항을 검증하신 후 결과를 공유해 주세요.
recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java (1)
5-5: import 추가가 적절합니다.HttpServletRequest import가 새로운 기능을 위해 올바르게 추가되었습니다.
recruitment-service/src/main/java/com/example/recruitment/repository/RecruitmentParticipantRepository.java (2)
3-3: 필요한 엔티티 import 추가가 적절합니다.새로운 메서드에서 사용할
Recruitment와User엔티티 import가 추가되었습니다.Also applies to: 5-5
24-25: 중복 참여 방지를 위한 메서드 추가가 우수합니다.엔티티 객체를 직접 받는
existsByRecruitmentAndUser메서드 추가가 잘 구현되었습니다. ID 대신 엔티티 객체를 사용하여 타입 안전성을 높였고,exists메서드를 사용하여 성능도 최적화했습니다.recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java (3)
3-3: 주문 DTO import 추가가 적절합니다.주문 연동 기능을 위한
OrderRequestDtoimport가 추가되었습니다.
32-33: 카테고리 필드 추가와 검증이 잘 구현되었습니다.
@NotBlank검증과 함께 카테고리 필드가 추가되어 데이터 무결성이 보장됩니다.
38-45: OrderRequestDto 변환 메서드가 잘 구현되었습니다.필수 필드들을 적절히 매핑하는 깔끔한 변환 메서드입니다. 코드의 가독성과 유지보수성이 좋습니다.
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java (1)
52-52:Details
✅ Verification successful
toOrderRequestDto() 메서드의 존재 여부를 확인하세요.
dto.toOrderRequestDto()메서드가RecruitmentRequestDto에 정의되어 있는지 확인이 필요합니다.다음 스크립트를 실행하여 해당 메서드가 존재하는지 확인하세요:
🏁 Script executed:
#!/bin/bash # Description: RecruitmentRequestDto에 toOrderRequestDto 메서드가 있는지 확인 # toOrderRequestDto 메서드 검색 rg -A 10 "toOrderRequestDto" --type javaLength of output: 2302
toOrderRequestDto() 메서드 존재 확인
RecruitmentRequestDto 클래스에toOrderRequestDto()가 이미 정의되어 있어 별도의 수정이 필요 없습니다.recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java (4)
5-5: 의존성 추가와 API 버전 관리가 적절합니다주문 연동을 위한
OrderRequestDtoimport와 비즈니스 로직 분리를 위한RecruitmentService의존성 추가가 좋습니다. API 버전을 v1으로 명시한 것도 향후 호환성 관리에 도움이 될 것입니다.Also applies to: 11-11, 22-22, 25-25
82-94: 사용자 모집글 조회 메서드들이 일관성 있게 개선되었습니다메서드명과 포맷팅이 일관성 있게 개선되었고, 기존 기능은 잘 유지되었습니다.
96-138: 상태 관리 메서드들이 잘 유지되었습니다기존의 상태 업데이트 로직이 잘 유지되었고, 주석과 포맷팅이 개선되었습니다.
140-146: 삭제 메서드가 일관성 있게 포맷팅되었습니다기존 기능이 잘 유지되고 포맷팅이 일관성 있게 개선되었습니다.
|
|
||
| @Column(name = "order_id") | ||
| private Long 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.
💡 Verification agent
🧩 Analysis chain
주문 ID 필드 추가가 적절합니다.
새로운 주문 연동 기능을 위한 orderId 필드 추가가 잘 구현되었습니다. nullable로 설정하여 기존 데이터와의 호환성도 고려되었습니다.
다만 주문 생성 실패 시 데이터 일관성을 보장하기 위한 처리 로직이 서비스 계층에 구현되어 있는지 확인이 필요합니다.
주문 생성 실패 시나리오에 대한 처리가 적절히 구현되어 있는지 확인해 주세요:
🏁 Script executed:
#!/bin/bash
# 주문 생성 관련 서비스 로직에서 예외 처리 확인
ast-grep --pattern $'class RecruitmentService {
$$$
joinRecruitment($$$) {
$$$
try {
$$$
} catch ($$$) {
$$$
}
$$$
}
$$$
}'Length of output: 164
서비스 계층의 joinRecruitment 메소드와 그 안의 예외 처리 로직을 직접 확인하기 위해 아래 스크립트를 실행해 주세요.
🏁 Script executed:
#!/bin/bash
# RecruitmentService 클래스 정의 위치 확인
rg -n "class RecruitmentService" -A3 .
# joinRecruitment 메소드 구현 내용 및 컨텍스트 확인 (5줄 추가)
rg -n "joinRecruitment" -A5 .
# 해당 파일에서 try/catch 블록 존재 여부 확인
rg -n "try" -A3 src/main/java/com/example/recruitment/service/RecruitmentService.java || echo "try/catch 블록이 없습니다."Length of output: 3100
서비스 계층에 주문 생성 예외 처리 로직 추가 필요
현재 recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java의 joinRecruitment(약 69행) 메소드에는 주문 생성 연동 및 예외 처리 로직이 없습니다. 주문 서비스 호출 실패 시 데이터 일관성이 깨질 수 있으므로 아래 사항을 반영해주세요.
• 파일: recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java
– 메소드: public void joinRecruitment(Long recruitmentId, Long userId, OrderRequestDto orderDto)
– 현황: 주문 생성(orderService 호출) 및 try/catch 블록 부재
제안하는 수정 예시:
public void joinRecruitment(Long recruitmentId, Long userId, OrderRequestDto orderDto) {
Recruitment recruitment = recruitmentRepository.findById(recruitmentId)
- .orElseThrow(() -> new RuntimeException("모집 없음"));
+ .orElseThrow(() -> new RuntimeException("모집 없음"));
User user = userRepository.findById(userId)
- .orElseThrow(() -> new RuntimeException("사용자 없음"));
+ .orElseThrow(() -> new RuntimeException("사용자 없음"));
+ try {
+ Long orderId = orderService.createOrder(orderDto);
+ recruitmentParticipant.setOrderId(orderId);
+ } catch (OrderException e) {
+ // TODO: 롤백 또는 보상 트랜잭션 처리
+ throw new RuntimeException("주문 생성 실패로 참여 처리 취소", e);
+ }
// 나머지 참여 로직
}주문 생성 실패 시에도 참여 데이터가 불완전하게 저장되지 않도록 적절한 예외 처리 및 트랜잭션 관리 코드를 구현해주세요.
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java
around line 69, the joinRecruitment method currently lacks order creation and
exception handling logic. To fix this, add a call to the orderService to create
an order within a try block, and catch any exceptions that occur during order
creation. Ensure that if order creation fails, the recruitment participation
data is not partially saved by rolling back the transaction or handling the
exception appropriately. This will maintain data consistency between recruitment
participation and order creation.
| // 주문용 메뉴 정보 | ||
| private List<OrderRequestDto.MenuDto> menus; |
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
메뉴 필드에 검증 추가를 권장합니다.
메뉴 정보는 주문 생성에 필수적인 데이터이므로 적절한 검증이 필요합니다.
메뉴 필드에 검증 어노테이션을 추가하는 것을 권장합니다:
// 주문용 메뉴 정보
+@NotEmpty(message = "메뉴 정보는 필수입니다.")
+@Valid
private List<OrderRequestDto.MenuDto> menus;🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java
at lines 35-36, the menus field lacks validation annotations. Since menu
information is essential for order creation, add appropriate validation
annotations such as @NotNull and @NotEmpty to the menus field to ensure it is
not null or empty during data binding or request processing.
| public Long createOrder(OrderRequestDto requestDto) { | ||
| String fullUrl = serverUrl + path; | ||
|
|
||
| ResponseEntity<Map> response = restTemplate.postForEntity(fullUrl, requestDto, Map.class); |
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
타입 안전성을 위해 응답 DTO를 사용하세요.
Raw Map 타입 대신 구체적인 응답 DTO를 사용하면 타입 안전성과 코드 가독성이 향상됩니다.
- ResponseEntity<Map> response = restTemplate.postForEntity(fullUrl, requestDto, Map.class);
+ ResponseEntity<OrderResponseDto> response = restTemplate.postForEntity(fullUrl, requestDto, OrderResponseDto.class);그리고 OrderResponseDto 클래스를 생성하세요:
@Getter
@Setter
public class OrderResponseDto {
private OrderDataDto data;
@Getter
@Setter
public static class OrderDataDto {
private Long orderId;
}
}🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java
at line 22, replace the raw Map response type in restTemplate.postForEntity with
a specific response DTO class to improve type safety and readability. Create an
OrderResponseDto class with nested OrderDataDto as described, then update the
method to use ResponseEntity<OrderResponseDto> instead of ResponseEntity<Map>.
Adjust the code to handle the DTO fields accordingly.
| if (response.getStatusCode().is2xxSuccessful()) { | ||
| Map<String, Object> responseBody = response.getBody(); | ||
|
|
||
| if (responseBody != null && responseBody.containsKey("data")) { | ||
| Map<String, Object> data = (Map<String, Object>) responseBody.get("data"); | ||
|
|
||
| if (data != null && data.containsKey("orderId")) { | ||
| return Long.valueOf(data.get("orderId").toString()); | ||
| } | ||
| } | ||
|
|
||
| throw new RuntimeException("응답에 orderId 없음"); | ||
| } | ||
|
|
||
| throw new RuntimeException("주문 생성 실패: " + response.getStatusCode()); |
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
에러 처리와 응답 파싱을 개선하세요.
현재 구조는 중첩된 타입 캐스팅과 null 체크로 인해 복잡하고 오류가 발생하기 쉽습니다. 또한 일반적인 RuntimeException 대신 구체적인 예외를 사용하는 것이 좋습니다.
- if (response.getStatusCode().is2xxSuccessful()) {
- Map<String, Object> responseBody = response.getBody();
-
- if (responseBody != null && responseBody.containsKey("data")) {
- Map<String, Object> data = (Map<String, Object>) responseBody.get("data");
-
- if (data != null && data.containsKey("orderId")) {
- return Long.valueOf(data.get("orderId").toString());
- }
- }
-
- throw new RuntimeException("응답에 orderId 없음");
- }
-
- throw new RuntimeException("주문 생성 실패: " + response.getStatusCode());
+ if (!response.getStatusCode().is2xxSuccessful()) {
+ throw new OrderServiceException("주문 생성 실패: " + response.getStatusCode());
+ }
+
+ OrderResponseDto responseBody = response.getBody();
+ if (responseBody == null || responseBody.getData() == null || responseBody.getData().getOrderId() == null) {
+ throw new OrderServiceException("응답에 orderId가 없습니다");
+ }
+
+ return responseBody.getData().getOrderId();그리고 커스텀 예외 클래스를 생성하세요:
public class OrderServiceException extends RuntimeException {
public OrderServiceException(String message) {
super(message);
}
public OrderServiceException(String message, Throwable cause) {
super(message, cause);
}
}🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java
around lines 24 to 38, the current error handling and response parsing use
nested type casting and null checks, making the code complex and error-prone.
Refactor by introducing a custom exception class named OrderServiceException
extending RuntimeException with constructors for message and cause. Replace the
generic RuntimeException throws with this custom exception to provide clearer,
more specific error handling. Simplify the response parsing by safely extracting
the orderId with proper null checks and type validation to avoid
ClassCastException.
| @Service | ||
| public class OrderClient { | ||
|
|
||
| private final RestTemplate restTemplate = new RestTemplate(); |
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
RestTemplate을 Bean으로 주입하세요.
새로운 RestTemplate 인스턴스를 직접 생성하는 대신, Spring Bean으로 구성하여 주입받는 것이 좋습니다. 이렇게 하면 타임아웃, 인터셉터, 커넥션 풀 등을 중앙에서 관리할 수 있습니다.
- private final RestTemplate restTemplate = new RestTemplate();
+ private final RestTemplate restTemplate;
+
+ public OrderClient(RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ }그리고 Configuration 클래스에서 RestTemplate Bean을 정의하세요:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// 타임아웃 설정
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setReadTimeout(10000);
restTemplate.setRequestFactory(factory);
return restTemplate;
}
}🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java
at line 13, replace the direct instantiation of RestTemplate with a
Spring-managed Bean injection. Remove the line creating a new RestTemplate
instance and instead add a constructor or field injection to receive the
RestTemplate Bean. Additionally, create a separate configuration class annotated
with @Configuration that defines a RestTemplate Bean with appropriate timeout
settings using HttpComponentsClientHttpRequestFactory as shown, so RestTemplate
can be centrally configured and reused.
| * - 생성된 orderId를 모집글에 저장 | ||
| */ | ||
| @Transactional | ||
| public void createRecruitment(RecruitmentRequestDto 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.
🛠️ Refactor suggestion
메서드 매개변수 검증을 추가하세요.
null 값이나 잘못된 매개변수에 대한 검증이 없어서 예상치 못한 오류가 발생할 수 있습니다.
@Transactional
public void createRecruitment(RecruitmentRequestDto dto) {
+ if (dto == null) {
+ throw new IllegalArgumentException("RecruitmentRequestDto는 null일 수 없습니다");
+ }
+ if (dto.getUserId() == null || dto.getStoreId() == null) {
+ throw new IllegalArgumentException("사용자 ID와 가게 ID는 필수입니다");
+ }
@Transactional
public void joinRecruitment(Long recruitmentId, Long userId, OrderRequestDto orderDto) {
+ if (recruitmentId == null || userId == null || orderDto == null) {
+ throw new IllegalArgumentException("모든 매개변수는 null일 수 없습니다");
+ }또는 Bean Validation을 사용하는 것을 고려하세요:
+ @Transactional
+ public void createRecruitment(@Valid RecruitmentRequestDto dto) {
+ @Transactional
+ public void joinRecruitment(@NotNull Long recruitmentId, @NotNull Long userId, @Valid OrderRequestDto orderDto) {Also applies to: 69-69
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java
at lines 35 and 69, the methods lack validation for their parameters, which can
lead to unexpected errors if null or invalid values are passed. Add explicit
null checks and validation logic for the method parameters to ensure they meet
expected criteria before proceeding. Alternatively, consider using Bean
Validation annotations on the DTO classes and enable validation in the service
methods to automatically enforce parameter correctness.
| User user = userRepository.findById(dto.getUserId()) | ||
| .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 대신 구체적인 예외 클래스를 사용하면 예외 처리가 더 명확해지고 클라이언트에서 적절한 응답을 할 수 있습니다.
- 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 EntityNotFoundException("사용자를 찾을 수 없습니다: " + dto.getUserId()));
+ Store store = storeRepository.findById(dto.getStoreId())
+ .orElseThrow(() -> new EntityNotFoundException("가게를 찾을 수 없습니다: " + dto.getStoreId()));
- Recruitment recruitment = recruitmentRepository.findById(recruitmentId)
- .orElseThrow(() -> new RuntimeException("모집 없음"));
- User user = userRepository.findById(userId)
- .orElseThrow(() -> new RuntimeException("사용자 없음"));
+ Recruitment recruitment = recruitmentRepository.findById(recruitmentId)
+ .orElseThrow(() -> new EntityNotFoundException("모집을 찾을 수 없습니다: " + recruitmentId));
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new EntityNotFoundException("사용자를 찾을 수 없습니다: " + userId));
- throw new RuntimeException("이미 참여한 모집입니다.");
+ throw new BusinessException("이미 참여한 모집입니다.");커스텀 예외 클래스들을 생성하세요:
public class EntityNotFoundException extends RuntimeException {
public EntityNotFoundException(String message) {
super(message);
}
}
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}Also applies to: 38-39, 70-71, 72-73
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java
around lines 36-37, replace the generic RuntimeException thrown when a user is
not found with a more specific custom exception like EntityNotFoundException.
Create custom exception classes such as EntityNotFoundException and
BusinessException as suggested, and use them in all similar cases including
lines 38-39, 70-71, and 72-73 to improve exception clarity and handling.
| Recruitment recruitment = new Recruitment(); | ||
| recruitment.setUser(user); | ||
| recruitment.setStore(store); | ||
| recruitment.setTitle(dto.getTitle()); | ||
| recruitment.setDescription(dto.getDescription()); | ||
| recruitment.setDeadlineTime(dto.getDeadlineTime()); | ||
| recruitment.setStatus("RECRUITING"); | ||
| recruitment.setCategory(dto.getCategory()); | ||
| recruitmentRepository.save(recruitment); | ||
|
|
||
| // 주문 서버에 주문 생성 요청 | ||
| OrderRequestDto orderDto = dto.toOrderRequestDto(); | ||
| orderDto.setGroupId(recruitment.getId()); | ||
| Long orderId = orderClient.createOrder(orderDto); | ||
|
|
||
| // orderId를 모집글에 저장 | ||
| recruitment.setOrderId(orderId); | ||
| recruitmentRepository.save(recruitment); |
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
트랜잭션 일관성을 개선하세요.
현재 구조는 모집글을 먼저 저장한 후 주문을 생성하고 다시 저장하는데, 이는 주문 생성이 실패할 경우 불일치 상태를 만들 수 있습니다.
- Recruitment recruitment = new Recruitment();
- recruitment.setUser(user);
- recruitment.setStore(store);
- recruitment.setTitle(dto.getTitle());
- recruitment.setDescription(dto.getDescription());
- recruitment.setDeadlineTime(dto.getDeadlineTime());
- recruitment.setStatus("RECRUITING");
- recruitment.setCategory(dto.getCategory());
- recruitmentRepository.save(recruitment);
-
- // 주문 서버에 주문 생성 요청
- OrderRequestDto orderDto = dto.toOrderRequestDto();
- orderDto.setGroupId(recruitment.getId());
- Long orderId = orderClient.createOrder(orderDto);
-
- // orderId를 모집글에 저장
- recruitment.setOrderId(orderId);
- recruitmentRepository.save(recruitment);
+ // 주문 서버에 주문 생성 요청을 위한 임시 모집글 생성
+ Recruitment recruitment = new Recruitment();
+ recruitment.setUser(user);
+ recruitment.setStore(store);
+ recruitment.setTitle(dto.getTitle());
+ recruitment.setDescription(dto.getDescription());
+ recruitment.setDeadlineTime(dto.getDeadlineTime());
+ recruitment.setStatus("RECRUITING");
+ recruitment.setCategory(dto.getCategory());
+ recruitment = recruitmentRepository.save(recruitment);
+
+ try {
+ // 주문 서버에 주문 생성 요청
+ OrderRequestDto orderDto = dto.toOrderRequestDto();
+ orderDto.setGroupId(recruitment.getId());
+ Long orderId = orderClient.createOrder(orderDto);
+
+ // orderId를 모집글에 저장
+ recruitment.setOrderId(orderId);
+ recruitmentRepository.save(recruitment);
+ } catch (Exception e) {
+ // 주문 생성 실패 시 모집글 삭제 또는 상태 변경
+ recruitmentRepository.delete(recruitment);
+ throw new OrderCreationException("주문 생성 중 오류가 발생했습니다", 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.
| Recruitment recruitment = new Recruitment(); | |
| recruitment.setUser(user); | |
| recruitment.setStore(store); | |
| recruitment.setTitle(dto.getTitle()); | |
| recruitment.setDescription(dto.getDescription()); | |
| recruitment.setDeadlineTime(dto.getDeadlineTime()); | |
| recruitment.setStatus("RECRUITING"); | |
| recruitment.setCategory(dto.getCategory()); | |
| recruitmentRepository.save(recruitment); | |
| // 주문 서버에 주문 생성 요청 | |
| OrderRequestDto orderDto = dto.toOrderRequestDto(); | |
| orderDto.setGroupId(recruitment.getId()); | |
| Long orderId = orderClient.createOrder(orderDto); | |
| // orderId를 모집글에 저장 | |
| recruitment.setOrderId(orderId); | |
| recruitmentRepository.save(recruitment); | |
| // 주문 서버에 주문 생성 요청을 위한 임시 모집글 생성 | |
| Recruitment recruitment = new Recruitment(); | |
| recruitment.setUser(user); | |
| recruitment.setStore(store); | |
| recruitment.setTitle(dto.getTitle()); | |
| recruitment.setDescription(dto.getDescription()); | |
| recruitment.setDeadlineTime(dto.getDeadlineTime()); | |
| recruitment.setStatus("RECRUITING"); | |
| recruitment.setCategory(dto.getCategory()); | |
| recruitment = recruitmentRepository.save(recruitment); | |
| try { | |
| // 주문 서버에 주문 생성 요청 | |
| OrderRequestDto orderDto = dto.toOrderRequestDto(); | |
| orderDto.setGroupId(recruitment.getId()); | |
| Long orderId = orderClient.createOrder(orderDto); | |
| // orderId를 모집글에 저장 | |
| recruitment.setOrderId(orderId); | |
| recruitmentRepository.save(recruitment); | |
| } catch (Exception e) { | |
| // 주문 생성 실패 시 모집글 삭제 또는 상태 변경 | |
| recruitmentRepository.delete(recruitment); | |
| throw new OrderCreationException("주문 생성 중 오류가 발생했습니다", e); | |
| } |
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java
around lines 41 to 58, the current code saves the recruitment entity before
creating the order, then updates and saves it again after order creation. To
improve transaction consistency and avoid data inconsistency if order creation
fails, refactor the code to create the order first, then set the orderId on the
recruitment entity, and finally save the recruitment once. This ensures
atomicity and prevents partial updates.
| if (dto.getStoreId() != null) { | ||
| recruitment.setStore(recruitment.getStore()); | ||
| } |
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.
치명적인 로직 버그가 있습니다
새로운 storeId가 제공되었을 때 기존 store를 다시 설정하고 있어서 store 변경이 전혀 적용되지 않습니다.
다음과 같이 수정해야 합니다:
if (dto.getStoreId() != null) {
- recruitment.setStore(recruitment.getStore());
+ Store store = storeRepository.findById(dto.getStoreId())
+ .orElseThrow(() -> new EntityNotFoundException("Store not found"));
+ recruitment.setStore(store);
}이를 위해 StoreRepository 의존성을 다시 주입받아야 합니다.
📝 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.
| if (dto.getStoreId() != null) { | |
| recruitment.setStore(recruitment.getStore()); | |
| } | |
| if (dto.getStoreId() != null) { | |
| Store store = storeRepository.findById(dto.getStoreId()) | |
| .orElseThrow(() -> new EntityNotFoundException("Store not found")); | |
| recruitment.setStore(store); | |
| } |
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java
around lines 161 to 163, the code incorrectly resets the store to the existing
one when a new storeId is provided, so the store change is not applied. Fix this
by injecting StoreRepository into the controller, then fetch the new Store
entity using the provided storeId from dto and set it on the recruitment object
instead of resetting the existing store.
| //모집글 참여 (Order 서버에 주문 생성 포함) | ||
| @PostMapping("/{recruitmentId}/join") | ||
| public ResponseEntity<?> joinRecruitment(@PathVariable Long recruitmentId, | ||
| @RequestParam Long userId, | ||
| @RequestBody OrderRequestDto orderRequestDto) { | ||
| recruitmentService.joinRecruitment(recruitmentId, userId, orderRequestDto); | ||
| return ResponseEntity.ok("모집글 참여 완료"); | ||
| } |
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
모집글 참여 기능이 잘 구현되었습니다
새로운 참여 엔드포인트가 RESTful 원칙에 맞게 구현되었고, 서비스 계층으로의 위임도 적절합니다.
보안상 userId를 URL 파라미터 대신 인증된 사용자 정보에서 가져오는 것을 권장합니다:
-public ResponseEntity<?> joinRecruitment(@PathVariable Long recruitmentId,
- @RequestParam Long userId,
- @RequestBody OrderRequestDto orderRequestDto) {
+public ResponseEntity<?> joinRecruitment(@PathVariable Long recruitmentId,
+ @RequestBody OrderRequestDto orderRequestDto,
+ Authentication authentication) {
+ Long userId = getUserIdFromAuthentication(authentication);📝 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.
| //모집글 참여 (Order 서버에 주문 생성 포함) | |
| @PostMapping("/{recruitmentId}/join") | |
| public ResponseEntity<?> joinRecruitment(@PathVariable Long recruitmentId, | |
| @RequestParam Long userId, | |
| @RequestBody OrderRequestDto orderRequestDto) { | |
| recruitmentService.joinRecruitment(recruitmentId, userId, orderRequestDto); | |
| return ResponseEntity.ok("모집글 참여 완료"); | |
| } | |
| //모집글 참여 (Order 서버에 주문 생성 포함) | |
| @PostMapping("/{recruitmentId}/join") | |
| public ResponseEntity<?> joinRecruitment(@PathVariable Long recruitmentId, | |
| @RequestBody OrderRequestDto orderRequestDto, | |
| Authentication authentication) { | |
| Long userId = getUserIdFromAuthentication(authentication); | |
| recruitmentService.joinRecruitment(recruitmentId, userId, orderRequestDto); | |
| return ResponseEntity.ok("모집글 참여 완료"); | |
| } |
🤖 Prompt for AI Agents
In
recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java
around lines 36 to 43, the joinRecruitment method currently accepts userId as a
request parameter, which is a security risk. Modify the method to remove the
userId parameter and instead obtain the authenticated user's ID from the
security context or authentication principal. This ensures the userId is
securely retrieved from the logged-in user's session rather than from client
input.
Summary by CodeRabbit
신규 기능
기능 개선
버그 수정