7주차 미션 [카카]#13
Open
KateteDeveloper wants to merge 22 commits into
Open
Conversation
refactor: DTO 클래스를 record 타입으로 변환 및 Enum 상수 추가 - Home, Mission, Member, Review 도메인의 DTO 클래스(@Getter, @builder)를 Java record로 리팩토링 - DTO 내부 클래스의 불필요한 'Class' 접미사 제거 (예: MyDataReqClass -> MyDataReq) - Controller, Service, Converter 계층에 record 타입 변경 사항(생성자 및 접근자) 반영 - Gender, Term, SocialType Enum에 필요한 상수(MALE, FEMALE, REQUIRED, OPTIONAL 등) 추가 ```
```text
refactor: 리뷰 생성 API 수정 (marketId PathVariable 적용 및 ID 타입 Long 변경)
```
**Breakdown of the changes covered:**
* **Controller**: Updated the review creation endpoint to accept `marketId` as a `@PathVariable` (`/v1/create/{marketId}`).
* **DTO**: Removed `marketId` and `regionId` from `CreateReviewReq` since `marketId` is now handled via the URL path.
* **Entity**: Changed the data types of `marketId` and `regionId` in the `Review` entity from `Integer` to `Long`.
* **Service & Converter**: Updated methods to pass `marketId` from the controller down to the entity builder.
…based on your examples: ```text feat: update CreateReviewRes DTO and ReviewConverter, remove regionId from Review entity ``` **Here is a quick breakdown of the specific changes mapped in this commit:** * **`Review.java`**: Removed the `regionId` field and its corresponding column mapping. * **`ReviewResDTO.java`**: Populated the `CreateReviewRes` record with `reviewId`, `stars`, and `content` fields (previously an empty record). * **`ReviewConverter.java`**: Updated the `toCreateReview` method to properly map the `Review` entity data to the newly updated `CreateReviewRes` DTO instead of returning an empty object.
```text feat: enhance domain entities, introduce BaseEntity, and configure JPA mappings ``` **Breakdown of the specific changes covered in this commit:** * **Global**: * Introduced an abstract `BaseEntity` class with JPA auditing (`createdAt`, `updateAt`, `deleatedAt`) to unify and automate timestamp management across the application. * **Member Domain**: * Updated the `Member` entity to extend `BaseEntity` and added new fields for `detailAddress`, `socialUid`, and `socialType`. * Configured `Food` and `Term` entities, along with `MemberFood` and `MemberTerm` mapping entities to establish many-to-many relationships with the user. * Created the `FoodName` enum and updated the `Term` enum with new constants (AGE, SERVICE, PRIVACY, LOCATION, MARKETING). * **Review Domain**: * Created a new `Reply` entity (extending `BaseEntity`). * Updated the `Review` entity to include a `@OneToOne` relationship with `Reply` and changed its ID generation strategy to `IDENTITY`. * **Mission Domain**: * Refactored the `Mission` entity to extend `BaseEntity`, stripping out redundant explicitly defined timestamp and status fields. * Created the `MemberMission` mapping entity to manage the many-to-many relationship between users and missions, accompanied by a new `MissionStatus` enum (`IDLE`, `CHALLENGING`, `COMPLETE`).
- `Review.java`: 불필요한 `marketId` 필드를 삭제하고 `Store` 엔티티와의 `@ManyToOne` 연관관계 추가 - `StoreRepository.java`, `MemberMissionRepository.java`: 엔티티 조회를 위한 JpaRepository 인터페이스 신규 추가 - `ReviewService.java`, `ReviewConverter.java`: 리뷰 생성 시 전달받은 `storeId`로 `Store` 엔티티를 직접 조회하여 객체에 매핑하도록 로직 수정 - `MissionService.java`, `MissionConverter.java`: 사용자 미션 목록 조회 기준을 `Mission` 엔티티에서 `MemberMission` 매핑 엔티티로 변경 - `MissionResDTO.java`: `MissionList` DTO 내 `status` 필드 타입을 `String`에서 `MissionStatus` Enum 클래스로 변경 ```
- MemberController: 마이페이지 조회 API(`/v1/users/me`)의 HTTP 메서드를 POST에서 GET으로 변경하고, @RequestBody(DTO) 대신 @RequestParam으로 `id`를 받도록 수정 - MemberService: `getInfo` 메서드 파라미터를 `MemberReqDTO.GetInfo` 객체에서 `Long memberId`로 변경 - Member Entity: `socialType` 필드 타입을 String에서 `SocialType` Enum으로 변경 - Term Entity 및 Enum: 기존 `Term` Enum 클래스명을 `TermName`으로 변경하고 엔티티의 `name` 필드 타입에 반영 - MemberTerm, MemberFood Entity: 잘못 임포트된 Spring Data의 `@Id` 어노테이션을 JPA(`jakarta.persistence.Id`)로 수정 및 불필요한 import 제거 - MissionRepository: 사용하지 않는 `findByMemberId` 메서드 삭제 ```
- `MemberMissionRepository`에 `@Query`를 사용한 `findByMemberIdAndStatusIn` 커스텀 쿼리 메서드 추가 - N+1 문제를 방지하기 위해 `Mission`과 `Store` 엔티티를 `JOIN FETCH`로 함께 조회하도록 쿼리 작성 - `MissionService.getMissions()` 메서드에서 진행 중(`CHALLENGING`)이거나 완료(`COMPLETE`)된 미션만 조회하도록 상태 조건 필터링 추가 - `PageRequest` 객체를 사용해 데이터베이스 단에서 페이징 처리가 이루어지도록 로직을 개선하고, `hasNext` 판별 로직 수정 ```
…회 기능 추가 - HomeController: 미션 목록 조회 API(`/v1/missions`)에 특정 지역 필터링을 위한 `@RequestParam Long locationId` 파라미터 추가 - HomeService: 기존 전체 조회(findAll) 임시 로직을 제거하고, `Pageable`과 `locationId`를 활용해 해당 지역의 미션만 조회하도록 로직 변경 - HomeService: 다음 페이지 존재 여부(hasNext) 판단 기준을 `missions.size() == size`로 수정하여 페이지네이션 로직 개선 - MissionRepository: 스토어(store)와 위치(location) 정보를 페치 조인(JOIN FETCH)하여, 전달받은 `locationId`와 일치하는 미션 목록을 최신순(ORDER BY m.id DESC)으로 반환하는 `findByLocationId` 쿼리 메서드 추가 ```
- MissionRepository의 findByLocationId 메서드 반환 타입을 List에서 Slice로 변경 - HomeService의 getMissions 메서드에서 리스트 크기로 다음 페이지 존재 여부를 수동 계산하던 방식을 Slice의 hasNext() 메서드를 활용하도록 수정 ```
- HomeService의 getMyData 메서드 내 임시로 사용하던 RuntimeException("멤버 없음")을 커스텀 예외인 MemberException으로 교체
- 예외 발생 시 MemberErrorCode.MEMBER_NOT_FOUND 에러 코드를 사용하도록 수정
```
- SignUpReqDTO의 SignUpReqBody 레코드 내 필드에 @notblank, @Email, @pattern, @NotNull 등 유효성 검증 어노테이션 추가 - MemberController의 getSignUp 엔드포인트에서 요청 객체를 검증하도록 @Valid 어노테이션 적용 - MemberService에 있던 Member 엔티티 빌더 생성 로직을 MemberConverter.toMember()로 이동하여 계층 역할 분리 - MissionConverter.toCompleteMission() 응답 시 하드코딩된 완료 메시지를 Service 단에서 파라미터로 전달받도록 메서드 시그니처 수정 ```
- MemberMissionRepository: findByMemberIdAndStatusIn 메서드 반환 타입을 List에서 Slice로 변경 - MissionService: Slice 객체의 hasNext()를 활용하도록 페이징 처리 로직 수정 - MissionService: completeMission() 수행 시 완료 상태(COMPLETE)의 MemberMission을 DB에 저장하는 로직 추가 - MissionService: 하드코딩된 성공 응답 메시지를 MissionSuccessCode.OK 상수 값으로 대체 - MissionConverter: Mission 엔티티를 MemberMission으로 변환하는 toMemberMission() 메서드 추가 - Store: location 필드의 @manytoone 연관관계에 지연 로딩(FetchType.LAZY) 속성 적용 ```
- mission 패키지에 위치하던 Store 관련 클래스(Store, Location, StoreRepository)를 store 패키지로 이동 및 관련 import 구문 수정 - ReviewReqDTO.CreateReviewReq DTO에 커스텀 별점 검증(@ValidStars) 및 내용 글자 수 검증(@notblank, @SiZe) 어노테이션 추가 - 별점 입력을 0.5 단위, 0.5~5.0 범위로 검증하는 커스텀 어노테이션(@ValidStars) 및 검증 로직(ValidStarsValidator) 추가 - Review 엔티티에 Member 엔티티와의 @manytoone 연관관계(member_id) 매핑 추가 - Store 전용 커스텀 예외(StoreException) 및 에러 코드(StoreErrorCode)를 추가하고, ReviewService의 Store 조회 로직에 적용 ```
- `Address` Enum에 특별시/광역시 및 서울 주요 구 지역 상수 추가 - `SignUpReqDTO`의 `address` 필드 검증용 `@Pattern` 정규식에 추가된 `Address` 상수 목록 반영 - `Location` 엔티티의 식별자 컬럼명을 `location_id`로 명시하고, 주소 매핑 컬럼명을 `address`에서 `name`으로 변경 및 `nullable = false` 제약조건 추가 - `Store` 엔티티의 식별자 컬럼명을 `store_id`로 명시하고, `managerNumber` 및 `detailAddress` 필드 추가 - `Store` 엔티티의 `name`, `managerNumber`, `detailAddress`, `location_id`(연관관계) 컬럼에 `nullable = false` 제약조건 적용
- MissionController의 기본 URL을 `/api/v1`으로 변경하고, 가게 미션 생성(`POST /stores/{storeId}/missions`) 및 커서 기반 조회(`GET /stores/{storeId}/missions`) API 추가
- MissionService에 `StoreRepository`를 연동하여 새로운 미션을 생성하는 로직과 커서(Cursor) 기반 페이지네이션을 적용한 미션 조회 로직 구현
- MissionRepository에 가게 ID와 커서(ID 내림차순)를 조건으로 미션을 조회하는 메서드(`findMissionsByStore_IdAndIdLessThanOrderByIdDesc` 등) 추가
- MissionReqDTO에 미션 생성 요청용 `CreateMission` record를 추가하고 데이터 유효성 검증(Validation) 어노테이션 적용
- MissionResDTO에 응답용 `GetMissionRes`와 커서 페이징 데이터 처리를 위한 공통 포맷인 `Pagination` record 추가
- MissionConverter에 신규 DTO와 Entity 간의 데이터 변환을 처리하는 매핑 메서드(`toMission`, `toGetMission`, `toPagination` 등) 추가
- MissionErrorCode에 잘못된 쿼리 요청을 처리하는 `QUERY_NOT_VALID` 예외 코드를 추가하고, MissionSuccessCode에 `CREATED` 성공 코드 추가
- `GeneralExceptionAdvice`에 `MethodArgumentNotValidException` 핸들러를 추가하여 필드별 유효성 검증 에러 메시지 반환 로직 구현 - `SignUpReqDTO`, `MissionReqDTO`, `ReviewReqDTO`의 검증 어노테이션(@notblank, @pattern, @SiZe, @NotNull 등)에 구체적인 `message` 속성 추가 - `MissionController`의 가게 미션 조회 API(`getMissions`) 반환 타입을 `List`에서 `Pagination` 구조로 변경 - 프로젝트 전반에 걸쳐 record 기반 DTO 클래스의 빈 중괄호 포맷팅 수정 - 다수의 Controller, Service, Repository, Entity 계층에서 사용하지 않는 불필요한 import 문 제거 및 와일드카드 import(`lombok.*`)를 명시적 선언으로 변경
- `MissionController`에 내가 진행중인 미션 조회 엔드포인트(`/missions/my`) 추가 - `MissionReqDTO`에 회원 ID 및 페이지네이션 정보를 받는 `GetMyMissionsReq` DTO 추가 - `MissionResDTO`에 미션 목록 응답을 처리하는 `MyMissionList`, `MyMissionPage` DTO 추가 - `MissionService`에 `CHALLENGING`, `COMPLETE` 상태의 미션을 페이징하여 반환하는 `getMyMissions` 비즈니스 로직 구현 ``` --- ```text ♻️ refactor(converter): 미션 응답 Converter 파라미터 및 메서드명 수정 - `MissionConverter`의 변환 메서드명을 `toMissionList` -> `toMyMissionList`, `toMissionPage` -> `toMyMissionPage`로 각각 변경 - 응답 변환 로직에서 `List<MemberMission>` 대신 `Slice<MemberMission>`을 직접 파라미터로 받도록 개선하여 불필요한 `hasNext` 인자 제거 - 데이터 리스트 변환 시 `Collectors.toList()` 호출을 `toList()`로 간소화 ``` --- ```text 🗄️ db(entity): Store 엔티티 식별자 매핑 컬럼명 변경 - `Store` 엔티티의 기본키 컬럼 매핑을 `@Column(name = "store_id")`에서 `@Column(name = "id")`로 변경 ```
Collaborator
|
미션 수고하셨습니다! |
yangjiae12
reviewed
May 18, 2026
| Integer page, | ||
|
|
||
| @NotNull(message = "페이지 사이즈는 필수입니다.") | ||
| Integer size |
Member
There was a problem hiding this comment.
page가 음수이거나 size가 0 이하일 경우 예외가 발생할 수 있으므로 @min 검증을 추가하면 좋을 것 같습니다!
| Slice<Mission> missionList; | ||
| String nextCursor; | ||
|
|
||
| if (!cursor.equals("-1")) { |
Member
There was a problem hiding this comment.
cursor가 null로 들어오면 NPE가 발생할 수 있어, 컨트롤러에서 기본값을 주거나 서비스에서 null 처리를 해주면 좋을 것 같습니다.
Comment on lines
+55
to
+63
| case "stars" -> { | ||
| reviews = isFirst | ||
| ? reviewRepository.findByMemberIdOrderByStars(req.memberId(), pageRequest) | ||
| : reviewRepository.findByMemberIdAndStarsLessThan( | ||
| req.memberId(), Float.parseFloat(req.cursor()), pageRequest); | ||
| nextCursor = reviews.hasNext() | ||
| ? String.valueOf(reviews.getContent().getLast().getStars()) | ||
| : null; | ||
| } |
Member
There was a problem hiding this comment.
별점 정렬에서는 동일한 별점의 리뷰가 여러 개 있을 수 있어, stars만 cursor로 사용하면 데이터가 누락될 수 있습니다. stars + id를 함께 cursor로 사용하는 방식이 더 안정적일 것 같습니다.
| ? String.valueOf(reviews.getContent().getLast().getStars()) | ||
| : null; | ||
| } | ||
| default -> throw new RuntimeException("유효하지 않은 정렬 기준입니다."); |
Member
There was a problem hiding this comment.
RuntimeException 대신 ReviewException과 도메인 에러 코드를 사용하면 예외 응답을 일관되게 처리할 수 있을 것 같습니다.
- page 파라미터에 `@Min(value = 0)` 어노테이션을 추가하여 0 이상 값만 허용하도록 설정 - size 파라미터에 `@Min(value = 1)` 어노테이션을 추가하여 1 이상 값만 허용하도록 설정
- `nextCursor` 변수의 초기값을 "-1"로 할당 - `missionList`의 결과가 비어있지 않은 경우에만 `getLast()`를 호출하여 `nextCursor`를 업데이트하도록 조건문 추가 (빈 리스트 접근으로 인한 예외 방지) - `query` 파라미터 검증 로직의 불필요한 `switch` 문을 `equalsIgnoreCase`를 활용한 `if` 문으로 단순화하여 가독성 개선
- ReviewService의 별점 순 정렬 시 커서를 `stars:id` 형태로 파싱하여 동일 별점 데이터도 정상적으로 페이징되도록 로직 수정 - ReviewRepository에 별점 및 ID 복합 조건을 처리하는 `findByMemberIdAndStarsLessThanOrStarsAndIdLessThan` 커스텀 쿼리 메서드 추가 - 유효하지 않은 정렬 기준 요청 시 사용할 `QUERY_NOT_VALID` 에러 코드를 ReviewErrorCode에 추가 - ReviewService에서 잘못된 정렬 기준 처리 시 기존 RuntimeException 대신 추가된 커스텀 예외(ReviewException)를 던지도록 변경
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📌 작업 내용
1. 내가 진행중인 미션 조회 API
POST /api/v1/missions/myCHALLENGING,COMPLETE상태 필터링2. 내가 작성한 리뷰 조회 API
POST /review/reviews/myid순 /stars순 정렬 선택 가능3. @Valid 검증 추가
MissionReqDTO,ReviewReqDTO,SignUpReqDTO검증 어노테이션 추가GeneralExceptionAdvice에MethodArgumentNotValidException핸들러 추가📌 구현 결과
내가 진행중인 미션 조회
Request
`
json { "memberId": 1, "page": 0, "size": 10 } \내가 작성한 리뷰 조회
Request
`
json { "memberId": 1, "pageSize": 10, "cursor": "-1", "query": "id" } \❓ 리뷰 요청
🤔 질문
💬 기타 공유 사항