[FEAT] 사장님 & 푸드트럭 최초 등록 API 구현#50
Conversation
|
Warning Rate limit exceeded@ksg1227 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 11 minutes and 25 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
Walkthrough이 PR은 사장님을 위한 푸드트럭 최초 등록 기능을 구현합니다. 새로운 엔티티 모델(FoodTruckDocument)을 도입하고, 승인 워크플로우를 ON/OFF에서 APPROVED 상태로 재구조화하며, 문서 처리 기능이 있는 새로운 API 엔드포인트를 추가합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Owner as 사장님
participant Controller as OwnerController
participant OwnerService
participant MyFoodTruckService
participant FoodTruckRepo as FoodTruckRepository
participant DocRepo as FoodTruckDocumentRepository
Owner->>Controller: POST /owners (FoodTruckCreateRequest)
activate Controller
Controller->>OwnerService: createNewFoodTruck(ownerId, request)
activate OwnerService
OwnerService->>OwnerService: validateAndGetOwner(ownerId)
OwnerService->>MyFoodTruckService: createNewFoodTruck(owner, request)
activate MyFoodTruckService
MyFoodTruckService->>FoodTruckRepo: save(createEmptyFoodTruck)
activate FoodTruckRepo
FoodTruckRepo-->>MyFoodTruckService: FoodTruck(PENDING)
deactivate FoodTruckRepo
rect rgb(200, 220, 240)
note over MyFoodTruckService: 사업자등록증 서류 생성
MyFoodTruckService->>DocRepo: save(BUSINESS_REGISTRATION document)
activate DocRepo
DocRepo-->>MyFoodTruckService: saved document
deactivate DocRepo
end
rect rgb(200, 220, 240)
note over MyFoodTruckService: 추가 서류 생성 (5개)
MyFoodTruckService->>DocRepo: save(OTHER documents)
activate DocRepo
DocRepo-->>MyFoodTruckService: saved documents
deactivate DocRepo
end
MyFoodTruckService-->>OwnerService: completed
deactivate MyFoodTruckService
OwnerService-->>Controller: void
deactivate OwnerService
Controller-->>Owner: BaseResponse<Void>
deactivate Controller
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 분 변경 사항의 범위가 광범위하며, 도메인 모델 변경(FoodTruckStatus ON/OFF → APPROVED)이 여러 서비스 계층에 계단식으로 영향을 미칩니다. 새로운 엔티티(FoodTruckDocument), 저장소, 서비스 메서드, API 엔드포인트 등 이질적인 변경이 많고, 승인 워크플로우 로직 변경으로 인한 영향 범위 검토가 필요합니다. Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
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 |
buzz0331
left a comment
There was a problem hiding this comment.
확인했습니다~
한가지 궁금한점이 관리자를 위한 현재 서비스 내에 존재하는 모든 푸드트럭을 조회하는 api가 구현된 상태인가요? 혹시 다른 api에서 파라미터를 통해 분기처리를 해둔 것인지 궁금해서 여쭤봅니다~
| @Column(name = "rejection_reason", length = 30) | ||
| private String rejectionReason; |
There was a problem hiding this comment.
오 저도 이런 컬럼이 필요하다고 생각했는데 추가해주셨네요 !
이 부분은 아직 구현되어있지 않습니다! |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (8)
src/main/java/konkuk/chacall/domain/owner/presentation/OwnerController.java (1)
289-301: 엔드포인트 경로를 기존 패턴과 일관되게 조정하는 것을 권장합니다.새로운 푸드트럭 등록 엔드포인트가
POST /owners로 정의되어 있는데, 기존의 다른 owner 리소스 엔드포인트들이 모두/owners/me/*패턴을 따르고 있습니다 (예:/owners/me/food-trucks,/owners/me/bank-accounts등).REST API 일관성을 위해
POST /owners/me/food-trucks로 변경하는 것을 고려해보세요. 현재GET /owners/me/food-trucks는 목록 조회이므로, 같은 경로에 POST를 추가하면 RESTful 규칙과도 더 잘 맞습니다.다음과 같이 수정할 수 있습니다:
- @PostMapping + @PostMapping("/me/food-trucks") public BaseResponse<Void> createNewFoodTruck(src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckDocumentRepository.java (1)
1-7: LGTM — 기본 CRUD 리포지토리 적절합니다.현재 요구사항엔 충분해 보입니다. 다만 문서 개수/중복 검증을 서비스에서 자주 하게 된다면 파생 메서드 추가를 고려해 주세요(예: countByFoodTruck_FoodTruckIdAndType, findAllByFoodTruck_FoodTruckId).
src/main/java/konkuk/chacall/domain/owner/presentation/dto/request/FoodTruckCreateRequest.java (1)
3-7: URL 유효성 및 길이 제약 보강 제안.DB 컬럼은 documentUrl length 500입니다. DTO에도 길이 상한과 URL 형식을 함께 검증하면 일관성이 올라갑니다.
Hibernate Validator 사용 시 @url, 또는 @pattern 대안 사용을 권장합니다.import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; +import org.hibernate.validator.constraints.URL; ... @Schema(description = "사업자등록증 url", example = "https://cdn.chacall.com/foodtrucks/osori/business-license.jpg") - @NotBlank(message = "사업자등록증 url 은 필수입니다.") - String businessRegistrationUrl, + @NotBlank(message = "사업자등록증 URL은 필수입니다.") + @Size(max = 500, message = "URL은 최대 500자까지 허용됩니다.") + @URL(message = "유효한 URL 형식이 아닙니다.") + String businessRegistrationUrl, ... - @Size(min = 5, max = 5, message = "기타 서류는 반드시 5장까지 업로드 가능합니다.") - List<@NotBlank String> otherDocumentUrls + @Size(min = 5, max = 5, message = "기타 서류는 정확히 5장을 업로드해야 합니다.") + List< + @NotBlank(message = "각 URL은 비어 있을 수 없습니다.") + @Size(max = 500, message = "URL은 최대 500자까지 허용됩니다.") + @URL(message = "유효한 URL 형식이 아닙니다.") + String + > otherDocumentUrls추가로 중복 URL 업로드를 방지하려면 @UniqueElements(hibernate-validator) 또는 서비스 단 distinct 검증을 고려하세요.
Also applies to: 16-23
src/main/java/konkuk/chacall/domain/owner/application/myfoodtruckmenu/MyFoodTruckMenuService.java (1)
44-46: 승인 검증 로직 중복 — 헬퍼 메서드로 추출 권장.동일 조건 검증이 5회 반복됩니다. 중복 제거로 가독성과 유지보수성을 높이세요.
public class MyFoodTruckMenuService { @@ - if (!foodTruckRepository.existsByFoodTruckIdAndOwnerIdAndFoodTruckStatus(foodTruckId, ownerId, FoodTruckStatus.APPROVED)) { - throw new BusinessException(ErrorCode.FOOD_TRUCK_NOT_APPROVED); - } + assertApprovedOwner(ownerId, foodTruckId); @@ - FoodTruck foodTruck = foodTruckRepository.findByFoodTruckIdAndOwnerIdAndFoodTruckStatus( - foodTruckId, ownerId, FoodTruckStatus.APPROVED) + FoodTruck foodTruck = foodTruckRepository.findByFoodTruckIdAndOwnerIdAndFoodTruckStatus( + foodTruckId, ownerId, FoodTruckStatus.APPROVED) .orElseThrow(() -> new BusinessException(ErrorCode.FOOD_TRUCK_NOT_APPROVED)); @@ - if (!foodTruckRepository.existsByFoodTruckIdAndOwnerIdAndFoodTruckStatus(foodTruckId, ownerId, FoodTruckStatus.APPROVED)) { - throw new BusinessException(ErrorCode.FOOD_TRUCK_NOT_APPROVED); - } + assertApprovedOwner(ownerId, foodTruckId); @@ - if (!foodTruckRepository.existsByFoodTruckIdAndOwnerIdAndFoodTruckStatus(foodTruckId, ownerId, FoodTruckStatus.APPROVED)) { - throw new BusinessException(ErrorCode.FOOD_TRUCK_NOT_APPROVED); - } + assertApprovedOwner(ownerId, foodTruckId); @@ - if (!foodTruckRepository.existsByFoodTruckIdAndOwnerIdAndFoodTruckStatus(foodTruckId, ownerId, FoodTruckStatus.APPROVED)) { - throw new BusinessException(ErrorCode.FOOD_TRUCK_NOT_APPROVED); - } + assertApprovedOwner(ownerId, foodTruckId); @@ + private void assertApprovedOwner(Long ownerId, Long foodTruckId) { + if (!foodTruckRepository.existsByFoodTruckIdAndOwnerIdAndFoodTruckStatus( + foodTruckId, ownerId, FoodTruckStatus.APPROVED)) { + throw new BusinessException(ErrorCode.FOOD_TRUCK_NOT_APPROVED); + } + }Also applies to: 63-65, 80-82, 95-97, 109-111
src/main/java/konkuk/chacall/domain/foodtruck/domain/model/FoodTruckDocument.java (2)
19-21: 삭제 시 참조 무결성 전략 확인(부모 삭제 → 문서 정리).현재 ManyToOne만 있고 부모(FoodTruck)에서 하위 문서 컬렉션 매핑/캐스케이드는 없습니다. 푸드트럭 삭제 시 문서가 남거나 삭제가 막힐 수 있습니다.
권장 중 하나:
- FoodTruck 쪽에 일대다 컬렉션 추가 + cascade = ALL, orphanRemoval = true
- 또는 DB 레벨 ON DELETE CASCADE 인덱스/제약 설정
- 서비스 계층에서 명시적 문서 삭제
예시(FoodTruck 엔티티에 추가):
@OneToMany(mappedBy = "foodTruck", cascade = CascadeType.ALL, orphanRemoval = true) private List<FoodTruckDocument> documents = new ArrayList<>();
7-10: 조회 성능 및 검증 일관성 — 인덱스/길이 제약 정합화.
- 빈번한 조회에 대비해 (food_truck_id), (food_truck_id, type) 인덱스를 추천합니다.
- DTO에서도 URL 길이 500 제약을 맞추면(검증 추가) 프리밸리데이션이 됩니다.
-@Table(name = "food_truck_documents") +@Table( + name = "food_truck_documents", + indexes = { + @Index(name = "idx_ft_docs_truck", columnList = "food_truck_id"), + @Index(name = "idx_ft_docs_truck_type", columnList = "food_truck_id,type") + } +)Also applies to: 23-28
src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckRepository.java (1)
12-12: 사용되지 않는 import 정리 권장.java.util.Collection은 더 이상 사용되지 않습니다. 정리해 주세요.
-import java.util.Collection;src/main/java/konkuk/chacall/domain/foodtruck/domain/model/FoodTruck.java (1)
63-64: 거부 사유 길이 제약이 너무 짧음
rejectionReason필드의 최대 길이가 30자로 설정되어 있습니다. 관리자가 푸드트럭 등록을 거부하는 상세한 사유를 입력하기에는 너무 짧을 수 있습니다. 예를 들어, "사업자등록증 이미지가 불명확하고 필수 항목이 누락되어 있습니다"와 같은 문장도 30자를 초과합니다.최소 100자 이상으로 늘리는 것을 권장합니다:
-@Column(name = "rejection_reason", length = 30) +@Column(name = "rejection_reason", length = 200) private String rejectionReason;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
src/main/java/konkuk/chacall/domain/foodtruck/domain/model/FoodTruck.java(4 hunks)src/main/java/konkuk/chacall/domain/foodtruck/domain/model/FoodTruckDocument.java(1 hunks)src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckDocumentRepository.java(1 hunks)src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckRepository.java(1 hunks)src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/infra/FoodTruckSearchRepositoryImpl.java(1 hunks)src/main/java/konkuk/chacall/domain/foodtruck/domain/value/DocumentType.java(1 hunks)src/main/java/konkuk/chacall/domain/foodtruck/domain/value/FoodTruckStatus.java(1 hunks)src/main/java/konkuk/chacall/domain/owner/application/OwnerService.java(1 hunks)src/main/java/konkuk/chacall/domain/owner/application/myfoodtruck/MyFoodTruckService.java(3 hunks)src/main/java/konkuk/chacall/domain/owner/application/myfoodtruckmenu/MyFoodTruckMenuService.java(5 hunks)src/main/java/konkuk/chacall/domain/owner/presentation/OwnerController.java(1 hunks)src/main/java/konkuk/chacall/domain/owner/presentation/dto/request/FoodTruckCreateRequest.java(1 hunks)src/main/java/konkuk/chacall/domain/owner/presentation/dto/response/MyFoodTruckResponse.java(1 hunks)src/main/java/konkuk/chacall/domain/region/application/query/RegionQueryService.java(1 hunks)src/main/java/konkuk/chacall/domain/region/application/search/RegionSearchService.java(1 hunks)src/main/java/konkuk/chacall/domain/region/presentation/dto/response/RegionResponse.java(1 hunks)src/main/java/konkuk/chacall/domain/user/presentation/dto/request/ApproveFoodTruckStatusRequest.java(1 hunks)src/main/java/konkuk/chacall/global/common/swagger/SwaggerResponseDescription.java(1 hunks)src/main/resources/application-local.yml(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/konkuk/chacall/domain/foodtruck/domain/model/FoodTruckDocument.java (1)
src/main/java/konkuk/chacall/domain/foodtruck/domain/model/FoodTruck.java (1)
Builder(16-151)
src/main/java/konkuk/chacall/domain/foodtruck/domain/value/DocumentType.java (2)
src/main/java/konkuk/chacall/domain/owner/application/myfoodtruck/MyFoodTruckService.java (1)
RequiredArgsConstructor(30-144)src/main/java/konkuk/chacall/domain/owner/application/OwnerService.java (1)
RequiredArgsConstructor(23-203)
🔇 Additional comments (17)
src/main/java/konkuk/chacall/domain/region/application/search/RegionSearchService.java (1)
22-22: RegionResponse 팩토리 메서드 호출이 올바르게 업데이트되었습니다.변경된
RegionResponse.of()시그니처에 맞춰regionId를 두 번째 파라미터로 올바르게 전달하고 있습니다. 검색 컨텍스트에서getFullName()을 사용하는 것도 적절합니다.src/main/java/konkuk/chacall/domain/region/presentation/dto/response/RegionResponse.java (1)
9-10: RegionResponse에 id 필드가 올바르게 추가되었습니다.새로운
id필드와 업데이트된 팩토리 메서드가 일관되게 구현되었습니다. 스키마 설명도 명확하게 PK 값임을 나타내고 있습니다.Also applies to: 15-19
src/main/java/konkuk/chacall/domain/region/application/query/RegionQueryService.java (1)
28-28: 변경 사항 검증 완료:RegionResponse.of()API 호출이 모두 올바르게 업데이트되었습니다.코드베이스 전체에서
RegionResponse.of()호출 2건을 확인했습니다:
RegionQueryService.java:28- 3개 파라미터로 올바르게 호출 중RegionSearchService.java:22- 3개 파라미터로 올바르게 호출 중구버전의 2개 파라미터 호출 패턴은 남아있지 않으며, 모든 호출에서
regionId가 두 번째 파라미터로 올바르게 전달되고 있습니다.src/main/java/konkuk/chacall/domain/owner/presentation/dto/response/MyFoodTruckResponse.java (1)
12-19: DTO의 nullable 메타데이터 업데이트가 올바르게 구성되었습니다.검증 결과:
description:FoodTruck엔티티에서 nullable로 정의됨 ✓activeTime:FoodTruck엔티티에서 nullable로 정의됨 ✓imageUrl:foodTruckPhotoList에서getMainPhotoUrl()로 추출되는 파생 필드로,foodTruckPhotoList자체가 null 가능하므로 nullable 표시가 적절함 ✓세 필드 모두 초기 푸드트럭 등록 단계에서 미입력 상태를 반영하는 것으로 타당합니다.
src/main/java/konkuk/chacall/global/common/swagger/SwaggerResponseDescription.java (1)
128-131: 잘 구현되었습니다!새로운 푸드트럭 등록 API에 대한 Swagger 응답 설명이 기존 패턴과 일관되게 추가되었습니다.
src/main/java/konkuk/chacall/domain/foodtruck/domain/value/DocumentType.java (1)
1-14: 깔끔한 구현입니다!문서 타입을 명확하게 구분하는 enum이 잘 정의되었습니다. 사업자 등록증과 기타 서류를 구분하는 로직이 간결하고 이해하기 쉽습니다.
src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/infra/FoodTruckSearchRepositoryImpl.java (1)
106-106: 상태 모델 리팩토링과 일관되게 변경되었습니다.ON/OFF 상태를 APPROVED 단일 상태로 대체하는 변경사항이 올바르게 반영되었습니다.
src/main/java/konkuk/chacall/domain/user/presentation/dto/request/ApproveFoodTruckStatusRequest.java (1)
8-8: 문서가 새로운 상태 모델과 일치하도록 업데이트되었습니다.Swagger 예제 값이 올바르게 변경되었습니다.
src/main/java/konkuk/chacall/domain/owner/application/OwnerService.java (1)
195-202: 기존 패턴을 잘 따르는 구현입니다!사장님 검증 후 하위 서비스로 위임하는 파사드 패턴이 일관되게 적용되었고, 트랜잭션 관리도 적절합니다.
src/main/java/konkuk/chacall/domain/foodtruck/domain/value/FoodTruckStatus.java (1)
10-10: 리뷰 코멘트는 현재 코드베이스 상태와 부정확합니다.검증 결과, 현재
FoodTruckStatusenum에는PENDING,APPROVED,REJECTED만 있으며 ON/OFF 상태가 존재하지 않습니다. 또한 코드베이스 전체를 검색한 결과FoodTruckStatus.ON또는FoodTruckStatus.OFF에 대한 참조가 없습니다. 검색되는 "OFF"는FoodTruckViewedStatus(푸드트럭 표시 여부)로 별도의 enum이며, 이 PR의 변경과는 무관합니다.
FoodTruckStatus.APPROVED상태는MyFoodTruckService,FoodTruckRepository, 쿼리 구현체 등 여러 곳에서 일관되게 사용 중이며, 상태 전이 로직(PENDING → APPROVED/REJECTED,REJECTED → APPROVED)도FoodTruck.approveFoodTruck()메서드에 명확하게 구현되어 있습니다. 마이그레이션 계획이 필요한 ON/OFF 상태는 현재 코드에 존재하지 않습니다.Likely an incorrect or invalid review comment.
src/main/java/konkuk/chacall/domain/foodtruck/domain/model/FoodTruckDocument.java (1)
30-36: 정적 팩토리 메서드 깔끔합니다.생성 경로가 명확해져서 테스트/유지보수에 유리합니다.
src/main/java/konkuk/chacall/domain/foodtruck/domain/repository/FoodTruckRepository.java (2)
28-34: 단일 상태(equal) 기반으로 정리한 쿼리 — 방향성 적절합니다.APPROVED 단일 상태 모델로의 전환에 맞춘 리포지토리 시그니처/JPQL 변경이 일관됩니다.
Also applies to: 40-46
30-34: StatusIn 메서드 전환 완료됨 — 추가 조치 불필요검증 결과, 기존
StatusIn메서드 호출이 코드베이스 전역에서 완전히 제거되었으며, 모든 호출 부분이 이미 신규 메서드명(findByFoodTruckIdAndOwnerIdAndFoodTruckStatus,existsByFoodTruckIdAndOwnerIdAndFoodTruckStatus)으로 전환된 상태입니다. Collection 파라미터 사용도 없으므로 빌드 실패 위험은 없습니다.src/main/java/konkuk/chacall/domain/owner/application/myfoodtruckmenu/MyFoodTruckMenuService.java (1)
117-122: 원본 리뷰 의견은 부정확합니다 - 필드명 불일치 문제가 없습니다.검증 결과 다음과 같이 필드명 매핑이 일관성 있게 구성되어 있습니다:
- 요청 DTO:
photoUrl필드명 사용- Menu 엔티티:
imageUrl필드명 사용- updateMenu 메서드:
this.imageUrl = imageUrl;로 명확히 업데이트업데이트 흐름(
request.photoUrl()→updateMenu(photoUrl)→this.imageUrl = imageUrl)이 정상 작동하므로, 삭제 시menu.getImageUrl()은 저장된 이미지 URL을 올바르게 반환합니다. 이미지 정리 누락 가능성은 없습니다.Likely an incorrect or invalid review comment.
src/main/java/konkuk/chacall/domain/foodtruck/domain/model/FoodTruck.java (2)
123-135: 승인 로직 업데이트가 올바르게 구현됨
FoodTruckStatus의 변경사항(ON/OFF제거,APPROVED추가)이 올바르게 반영되었습니다. 상태 전이 로직이 명확하고 비즈니스 규칙을 잘 표현하고 있습니다:
PENDING→APPROVED또는REJECTEDREJECTED→APPROVED
40-40: 원본 리뷰 의견이 부정확합니다.검증 결과,
timeDiscussRequired필드는 선언(40행)과 팩토리 메서드(91행)에서만 나타나며, 코드베이스 어디에서도 실제로 읽혀지지 않습니다.getTimeDiscussRequired()호출이 없으므로 null 값으로 인한 NullPointerException이 현재 코드에서 발생하지 않습니다.Boolean 래퍼 타입으로의 변경은 설계상 의도적이며(팩토리 메서드에서 명시적으로 null 할당), 해당 필드는 쓰기 전용 상태입니다.
Likely an incorrect or invalid review comment.
src/main/java/konkuk/chacall/domain/owner/application/myfoodtruck/MyFoodTruckService.java (1)
111-111: 리뷰 댓글의 가정이 코드베이스와 맞지 않습니다.현재 저장소를 검토한 결과:
findByFoodTruckIdAndOwnerIdAndFoodTruckStatusIn메서드는 존재하지 않으며, 변경 이력도 없습니다.FoodTruckRepository에는 오직findByFoodTruckIdAndOwnerIdAndFoodTruckStatus(단수형) 메서드만 있고, 단일FoodTruckStatus status파라미터를 사용합니다.- git diff 결과가 비어 있어 최근에 이 영역에 변경사항이 없음을 시사합니다.
따라서 "여러 상태 조회에서 단일 상태로 변경되었다"는 리뷰 댓글의 전제가 실제 코드베이스 증거와 일치하지 않습니다. 코드는 처음부터
APPROVED상태만 조회하도록 설계되었던 것으로 보입니다.Likely an incorrect or invalid review comment.
| public static FoodTruck createEmptyFoodTruck(User owner, String name) { | ||
| return FoodTruck.builder() | ||
| .owner(owner) | ||
| .name(name) | ||
| .description(null) | ||
| .phoneNumber(null) | ||
| .activeTime(null) | ||
| .timeDiscussRequired(null) | ||
| .foodTruckPhotoList(null) | ||
| .menuCategoryList(null) | ||
| .availableQuantity(null) | ||
| .needElectricity(null) | ||
| .paymentMethod(null) | ||
| .operatingInfo(null) | ||
| .option(null) | ||
| .rejectionReason(null) | ||
| .foodTruckStatus(FoodTruckStatus.PENDING) | ||
| .foodTruckViewedStatus(FoodTruckViewedStatus.OFF) | ||
| .ratingInfo(RatingInfo.createInitial()) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
팩토리 메서드의 명시적 null 설정이 불필요하고 위험함
이 팩토리 메서드는 다음과 같은 문제점이 있습니다:
-
불필요한 명시적 null 설정: 대부분의 필드를 명시적으로
null로 설정하고 있는데, 이는 Java의 기본 동작이므로 불필요합니다. 코드만 길어지고 가독성이 떨어집니다. -
NPE 위험: 많은 필드가
null인 상태로 엔티티가 생성되므로, 이 엔티티를 사용하는 다른 메서드들이 null 체크 없이 필드에 접근하면NullPointerException이 발생할 수 있습니다. -
데이터 무결성: JPA
@Column어노테이션에nullable=false로 설정된 필드들(예: description, phoneNumber 등)이 실제로는 null로 설정되고 있어 DB 저장 시 제약 조건 위반이 발생할 수 있습니다.
다음과 같이 리팩토링하는 것을 권장합니다:
public static FoodTruck createEmptyFoodTruck(User owner, String name) {
return FoodTruck.builder()
.owner(owner)
.name(name)
- .description(null)
- .phoneNumber(null)
- .activeTime(null)
- .timeDiscussRequired(null)
- .foodTruckPhotoList(null)
- .menuCategoryList(null)
- .availableQuantity(null)
- .needElectricity(null)
- .paymentMethod(null)
- .operatingInfo(null)
- .option(null)
- .rejectionReason(null)
+ // 명시적 null 설정 제거 (기본값이 null임)
.foodTruckStatus(FoodTruckStatus.PENDING)
.foodTruckViewedStatus(FoodTruckViewedStatus.OFF)
.ratingInfo(RatingInfo.createInitial())
.build();
}또한, 이 "비어있는" 푸드트럭을 사용하는 코드에서 null 체크가 필요한지 검증해야 합니다.
| public void createNewFoodTruck(User owner, FoodTruckCreateRequest request) { | ||
| // 1. 비어 있는 푸드트럭 엔티티 생성 (null 필드 초기화) | ||
| FoodTruck foodTruck = FoodTruck.createEmptyFoodTruck(owner, request.name()); | ||
| foodTruckRepository.save(foodTruck); | ||
|
|
||
| // 2. 서류 등록 로직 (사업자등록증 + 기타 5장) | ||
| FoodTruckDocument business = FoodTruckDocument.create(foodTruck, DocumentType.BUSINESS_REGISTRATION, request.businessRegistrationUrl()); | ||
| foodTruckDocumentRepository.save(business); | ||
|
|
||
| List<FoodTruckDocument> otherDocs = request.otherDocumentUrls().stream() | ||
| .map(url -> FoodTruckDocument.create(foodTruck, DocumentType.OTHER, url)) | ||
| .toList(); | ||
|
|
||
| foodTruckDocumentRepository.saveAll(otherDocs); | ||
| } |
There was a problem hiding this comment.
트랜잭션 경계 및 검증 로직 추가 필요
이 메서드에는 다음과 같은 중요한 문제들이 있습니다:
-
트랜잭션 관리:
@Transactional어노테이션이 없어서 푸드트럭과 서류 저장이 원자적으로 처리되지 않습니다. 푸드트럭은 저장되었지만 서류 저장 중 실패하면 데이터 불일치가 발생할 수 있습니다. -
서류 개수 검증: PR 설명에는 "기타 서류 5가지"를 입력받는다고 명시되어 있지만,
otherDocumentUrls의 크기가 정확히 5개인지 검증하는 로직이 없습니다. -
중복 등록 방지: 동일한 사장님이 같은 이름의 푸드트럭을 중복 등록할 수 있는지 확인하는 비즈니스 검증이 없습니다.
-
저장 로직 일관성: 사업자등록증은 개별 저장(
save)하고 기타 서류는 일괄 저장(saveAll)하는데, 모든 서류를 모아서 한 번에saveAll로 저장하는 것이 더 효율적입니다.
다음과 같이 수정하는 것을 권장합니다:
+@Transactional
public void createNewFoodTruck(User owner, FoodTruckCreateRequest request) {
+ // 0. 서류 개수 검증
+ if (request.otherDocumentUrls().size() != 5) {
+ throw new BusinessException(ErrorCode.INVALID_DOCUMENT_COUNT);
+ }
+
// 1. 비어 있는 푸드트럭 엔티티 생성 (null 필드 초기화)
FoodTruck foodTruck = FoodTruck.createEmptyFoodTruck(owner, request.name());
foodTruckRepository.save(foodTruck);
- // 2. 서류 등록 로직 (사업자등록증 + 기타 5장)
+ // 2. 서류 등록 로직 (사업자등록증 + 기타 서류)
FoodTruckDocument business = FoodTruckDocument.create(foodTruck, DocumentType.BUSINESS_REGISTRATION, request.businessRegistrationUrl());
- foodTruckDocumentRepository.save(business);
List<FoodTruckDocument> otherDocs = request.otherDocumentUrls().stream()
.map(url -> FoodTruckDocument.create(foodTruck, DocumentType.OTHER, url))
.toList();
- foodTruckDocumentRepository.saveAll(otherDocs);
+ // 모든 서류를 한 번에 저장
+ List<FoodTruckDocument> allDocs = new ArrayList<>();
+ allDocs.add(business);
+ allDocs.addAll(otherDocs);
+ foodTruckDocumentRepository.saveAll(allDocs);
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/konkuk/chacall/domain/owner/application/myfoodtruck/MyFoodTruckService.java
around lines 118-132, add transactional and validation/duplication checks and
consolidate document saves: annotate the method (or class) with @Transactional,
first validate request.otherDocumentUrls() has exactly 5 entries and that
request.businessRegistrationUrl() is present (throw a meaningful runtime
exception if not), check via the repository (e.g. existsByOwnerAndName or
equivalent) that the owner does not already have a FoodTruck with the same name
and throw a business exception on duplicate, create the FoodTruck entity but do
not persist documents separately — collect the business registration document
plus the 5 other documents into a single List<FoodTruckDocument> and call
foodTruckDocumentRepository.saveAll(documents) after
foodTruckRepository.save(foodTruck) so all operations occur within the
transaction and documents are saved in one batch.
| jpa: | ||
| hibernate: | ||
| ddl-auto: update | ||
| ddl-auto: create |
There was a problem hiding this comment.
로컬 환경에서 ddl-auto: create 설정은 데이터 손실을 유발합니다.
애플리케이션을 재시작할 때마다 모든 테이블이 삭제되고 재생성되므로, 로컬 개발 중 작업한 테스트 데이터가 모두 손실됩니다.
로컬 환경에서는 update 또는 validate를 사용하여 데이터를 보존하는 것을 권장합니다.
다음 diff를 적용하여 기존 설정으로 복원하세요:
- ddl-auto: create
+ ddl-auto: update📝 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.
| ddl-auto: create | |
| ddl-auto: update |
🤖 Prompt for AI Agents
In src/main/resources/application-local.yml around line 13 the property ddl-auto
is set to create which drops and recreates all tables on restart; change it to a
safer value for local development such as update (or validate) to preserve
existing data, i.e. replace "ddl-auto: create" with "ddl-auto: update" (or
"ddl-auto: validate") and save the file so restarts no longer wipe the local
database.
아하 확인했습니다~ |
#️⃣연관된 이슈
📝작업 내용
사장님 등록 & 푸드트럭을 최초 등록 API 를 구현하였습니다.
푸드트럭 최초 등록
푸드트럭을 최초 등록할 시 푸드트럭의 이름, 사업자 등록증, 기타서류 5가지를 입력받아야합니다.
이 때 사업자 등록증과 기타서류를 푸드트럭 테이블이 아닌 별도의 테이블로 분리해서 관리하는 것이 더 나을 것 같다고 합의되었기에,
푸드트럭 서류테이블을 새롭게 만들어서 푸드트럭과 1 : N 관계를 맺어주었습니다.또한 사업자 등록증과 기타 서류를 서로 구분해주기 위해서 DocumentType 이라는 ENUM 을 만들어두었습니다.

이건 실제로 푸드트럭을 등록하고, 관리자로부터 승인된 이후의 모습입니다.
이 외에 구현상의 별다른 이슈는 없었습니다.
스크린샷 (선택)
💬리뷰 요구사항(선택)
ddl-auto update 로 설정하면, 기존에 nullable=false 로 설정되어있던 컬럼들을 nullable=true 로 바꾸는 변경사항이 제대로 DB 에 반영이 되지 않아서 ddl-auto create 으로 설정해두었으니 확인 부탁드립니다
Summary by CodeRabbit
릴리즈 노트
새로운 기능
개선 사항