[feat] 오늘의 한마디 삭제 api 개발#278
Conversation
- soft delete 대상이므로 jpa entity에 @SQLDelete 코드 추가
- soft delete 대상이므로 jpa entity 조회 시 status 조건 추가해야함
- 오늘의 한마디 작성자를 검증하는 메서드 단위 테스트 코드 추가
- BaseJpaEntity 의 status 필드를 private로 수정 & status의 상태변경 메서드를 protected로 뚫어놓는게 더 좋을 듯 해서 TODO 주석 추가 - 추후 수정할 예정
Walkthrough오늘의 한마디(AttendanceCheck) 삭제 기능을 추가하고 관련 포트·서비스·퍼시스턴스(소프트 삭제)·도메인 검증·에러·스웨거 항목 및 통합/단위 테스트를 추가했으며 테스트용 토큰 엔드포인트와 보안 예외 경로, 일부 트랜잭션 어노테이션과 주석이 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User as 사용자
participant Ctrl as RoomPostCommandController
participant UC as AttendanceCheckDeleteUseCase
participant Val as RoomParticipantValidator
participant Port as AttendanceCheckCommandPort
participant Repo as AttendanceCheckJpaRepository
User->>Ctrl: DELETE /rooms/{roomId}/daily-greeting/{attendanceCheckId} (userId)
Ctrl->>UC: delete(userId, roomId, attendanceCheckId)
UC->>Val: validateMember(userId, roomId)
Val-->>UC: ok
UC->>Port: findById(attendanceCheckId)
Port->>Repo: findByAttendanceCheckIdAndStatus(id, ACTIVE)
alt not found
Repo-->>Port: empty
Port-->>UC: throw EntityNotFound(ATTENDANCE_CHECK_NOT_FOUND)
UC-->>Ctrl: 404
else found
Port-->>UC: AttendanceCheck
UC->>UC: attendanceCheck.validateCreator(userId)
alt creator mismatch
UC-->>Ctrl: InvalidState(ATTENDANCE_CHECK_CAN_NOT_DELETE) -> 403
else ok
UC->>Port: delete(attendanceCheck)
Port->>Repo: delete(jpaEntity)
Note right of Repo: @SQLDelete => UPDATE ... SET status='INACTIVE'
Repo-->>Port: done
UC-->>Ctrl: roomId
Ctrl-->>User: 200 {roomId}
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
Test Results431 tests 431 ✅ 36s ⏱️ Results for commit 8ddcf61. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (16)
src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java (1)
61-67: Enum 비교는 == 사용, Base 리팩터 대비 변경 지점 최소화
- Java enum 비교는
equals보다==가 명확하고 NPE 걱정도 없습니다.- Base에서
status를 private+protected 변경 메서드로 바꿀 예정이므로, 여기서도 그 메서드를 호출하는 형태로 틀을 잡아두면 이후 변경 파급을 최소화할 수 있습니다.아래처럼 수정 제안합니다.
- // TODO : Base Entity 의 status를 private 로 변경 & 상태변경을 위한 protected method 추가 후 수정 public void softDelete() { - if(this.status.equals(INACTIVE)){ + if (status == INACTIVE) { throw new InvalidStateException(POST_ALREADY_DELETED); } - this.status = INACTIVE; + this.status = INACTIVE; // Base 리팩터 이후: changeStatus(INACTIVE); }추가 메모:
- 현재 양방향 연관에
cascade = REMOVE, orphanRemoval = true가 걸려 있습니다(댓글/좋아요). 실수로 JPAdelete()가 호출되면 연쇄 물리 삭제가 작동할 수 있습니다. 소프트 딜리트 일관성을 위해 실제 삭제 경로가 막혀 있는지(레포지토리 레벨에서 delete 차단, @SQLDelete(+ @where) 사용 등) 점검을 권장합니다.src/main/java/konkuk/thip/roompost/adapter/in/web/response/AttendanceCheckDeleteResponse.java (1)
1-9: Response DTO는 primitive로 null 불가를 명시하세요
roomId는 응답에서 null일 수 없는 값으로 보입니다. primitivelong을 쓰면 불필요한 오토박싱을 피하고 스키마 의도가 명확해집니다. Swagger 문서화(@Schema)도 함께 고려해 주세요.-public record AttendanceCheckDeleteResponse( - Long roomId -) { - public static AttendanceCheckDeleteResponse of(Long roomId) { +public record AttendanceCheckDeleteResponse( + long roomId +) { + public static AttendanceCheckDeleteResponse of(long roomId) { return new AttendanceCheckDeleteResponse(roomId); } }선택 사항:
@Schema(description = "...", example = "123")추가로 API 문서 가독성 향상.src/main/java/konkuk/thip/roompost/application/port/out/AttendanceCheckCommandPort.java (1)
21-21: delete 메서드 구현 확인 및 JavaDoc 추가 제안AttendanceCheckCommandPersistenceAdapter에서
delete(AttendanceCheck attendanceCheck)가 포트 시그니처와 일치하게 구현된 것을 확인했습니다.
이제 해당 메서드가 “소프트 삭제(상태를 INACTIVE로 변경)”한다는 의미와 멱등성, 예외 계약을 JavaDoc으로 명확히 문서화해 주세요. 선택적으로 아래와 같은 주석을 추가하여 가독성과 유지보수성을 높일 수 있습니다:/** * Soft-delete the given AttendanceCheck (set status to INACTIVE). * <p> * Idempotent: no-op if already INACTIVE. * </p> * <p> * Throws: * - EntityNotFoundException when the AttendanceCheck with ACTIVE status is not found * - InvalidStateException when the caller is not authorized to delete * </p> */ void delete(AttendanceCheck attendanceCheck);
- 소프트 삭제 기반(@SQLDelete 혹은 엔티티 내부 상태 변경)임을 명시
- 이미 INACTIVE인 경우 멱등 처리(no-op)
- 존재하지 않거나 권한 검증 실패 시 던지는 예외 종류와 계약을 문서화
src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/attendancecheck/AttendanceCheckJpaRepository.java (1)
21-21: 카운트 쿼리의 상태조건 추가는 타당 — 인덱스/성능 고려 권장
status파라미터를 포함해 소프트 삭제 레코드 배제를 보장한 점 좋습니다. 일 단위 카운트가 트래픽 경로라면 다음을 검토해 주세요.
- 컬럼 인덱스:
(user_id, room_id, status, created_at)복합 인덱스 권장. created_at 범위 조건과 status 동시 선택성 확보.- 캐시/서머리 테이블: TODO 주석대로 Redis 카운터 또는 일별 집계테이블로 read 경로 비용 상한 설정.
src/test/java/konkuk/thip/roompost/domain/AttendanceCheckTest.java (2)
50-63: 테스트 시그니처 단순화 및 스타일 통일
throws Exception는 불필요합니다. 또한 본 파일 내에서 JUnit 단언 스타일을 통일하면 가독성이 좋아집니다(아래 실패 케이스 코멘트 참고).제안 diff:
- void validate_creator_success() throws Exception { + void validate_creator_success() {
65-80: 실패 케이스 단언 스타일 통일 + 메시지 의존도 최소화현재
assertThatThrownBy를 사용하고, 메시지 문자열에 직접 의존하고 있습니다. 동일 파일에서 사용 중인 JUnit 스타일(AssertThrows)로 통일하고, 메시지 검증은 유지하되 예외 인스턴스를 받아 최소화된 의존으로 테스트해 주세요.제안 diff:
- assertThatThrownBy(() -> ac.validateCreator(2L)) - .isInstanceOf(InvalidStateException.class) - .hasMessage(ATTENDANCE_CHECK_CAN_NOT_DELETE.getMessage()); + InvalidStateException ex = + assertThrows(InvalidStateException.class, () -> ac.validateCreator(2L)); + assertEquals(ATTENDANCE_CHECK_CAN_NOT_DELETE.getMessage(), ex.getMessage());추가 제안(선택):
InvalidStateException에getErrorCode()가 존재한다면 메시지 대신 코드로 검증하세요.assertEquals(ATTENDANCE_CHECK_CAN_NOT_DELETE, ex.getErrorCode());엣지 케이스 보강(선택):
creatorId == null,userId == null입력에 대한 NPE 미발생/예외 메시지 확인 테스트 추가 가능.src/main/java/konkuk/thip/roompost/adapter/out/jpa/AttendanceCheckJpaEntity.java (1)
16-16: AttendanceCheckJpaEntity @SQLDelete 최적화 제안검증 결과
- BaseJpaEntity의
status필드는@Enumerated(EnumType.STRING)로 매핑되어 있어 문자열 상수'INACTIVE'사용과 정확히 일치합니다.- 도메인 조회 로직(AttendanceCheckCommandPersistenceAdapter)에서는
findByAttendanceCheckIdAndStatus(..., StatusType.ACTIVE)를 사용해 이미 비활성화된 항목을 자동으로 제외하고 있습니다.선택적 리팩토링 제안
- @SQLDelete 구문에
AND status <> 'INACTIVE'조건을 추가하여, 이미 비활성화된 레코드에 대한 불필요한 UPDATE를 방지할 수 있습니다.- 엔티티 단 전역 필터(@where(clause = "status = 'ACTIVE'") 또는 Hibernate @filter) 적용을 검토해, 각 리포지토리마다 status 조건을 반복하는 수고를 줄이세요.
updated_at자동 갱신이 필요하다면 DB 트리거나 엔티티 리스너를 활용해 보완할 수 있습니다.제안 diff:
- @SQLDelete(sql = "UPDATE attendance_checks SET status = 'INACTIVE' WHERE attendancecheck_id = ?") + @SQLDelete(sql = "UPDATE attendance_checks SET status = 'INACTIVE' WHERE attendancecheck_id = ? AND status <> 'INACTIVE'")src/main/java/konkuk/thip/roompost/application/port/in/AttendanceCheckDeleteUseCase.java (1)
3-7: UseCase 반환 타입에 대한 선택 재고(선택 사항)현재 Long(roomId) 반환은 컨트롤러 응답 DTO와 결합도가 생깁니다. 도메인 관점에서는 void 또는 별도 결과 VO(예: DeleteResult.of(roomId))가 더 명확합니다. 유지보수성과 테스트 대역 구성에도 이점이 있습니다.
원하시면 DeleteResult VO 초안과 매핑 변경(diff)까지 같이 제안드리겠습니다.
src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java (1)
52-58: DB 라운드트립 축소 제안: 조건부 소프트 삭제 단일 쿼리화(선택 사항)서비스에서 getByIdOrThrow(SELECT) 후 여기서 다시 SELECT→DELETE(@SQLDelete Update)로 최소 2번의 DB 접근이 발생합니다. JPA @Modifying UPDATE로
status = INACTIVE를 조건부 변경하면 1쿼리로 끝낼 수 있어 지연시간·부하가 줄고 TOCTOU 가능성도 낮아집니다.예시(리포지토리에 추가):
@Modifying(clearAutomatically = true, flushAutomatically = true) @Query("update AttendanceCheckJpaEntity a set a.status = konkuk.thip.common.entity.StatusType.INACTIVE " + "where a.attendanceCheckId = :id and a.status = konkuk.thip.common.entity.StatusType.ACTIVE") int softDeleteIfActive(@Param("id") Long id);적용 예(본 어댑터):
- AttendanceCheckJpaEntity attendanceCheckJpaEntity = attendanceCheckJpaRepository... - attendanceCheckJpaRepository.delete(attendanceCheckJpaEntity); + int updated = attendanceCheckJpaRepository.softDeleteIfActive(attendanceCheck.getAttendanceCheckId()); + if (updated == 0) { + throw new EntityNotFoundException(ATTENDANCE_CHECK_NOT_FOUND); + }효과:
- 쿼리 수: 2→1
- 일관성: 상태 조건을 DB가 원자적으로 보장
src/test/java/konkuk/thip/roompost/adapter/in/web/AttendanceCheckDeleteApiTest.java (4)
61-87: 행복 경로 테스트 훌륭합니다 + 경로 변수 변환 간소화 제안기능 검증이 명확합니다. 다만
intValue()로 불필요한 다운캐스팅을 하고 있어 Long 그대로 전달하면 가독성이 좋아집니다.적용 제안(diff):
- mockMvc.perform(delete("/rooms/{roomId}/daily-greeting/{attendanceCheckId}", room.getRoomId().intValue(), ac1.getAttendanceCheckId().intValue()) + mockMvc.perform(delete("/rooms/{roomId}/daily-greeting/{attendanceCheckId}", room.getRoomId(), ac1.getAttendanceCheckId()) .requestAttr("userId", me.getUserId())) .andExpect(status().isOk()) .andExpect(jsonPath("$.data.roomId", is(room.getRoomId().intValue())));
84-87: 삭제 검증을 상태 스코프 메소드로 직접 확인(선택 사항)향후 엔티티에 @where가 추가되면
findById로는 INACTIVE 레코드 접근이 막힐 수 있습니다. 상태 조건 메소드로 바로 검증하는 편이 견고합니다.적용 제안(diff):
- AttendanceCheckJpaEntity deleted = attendanceCheckJpaRepository.findById(ac1.getAttendanceCheckId()).orElse(null); - Assertions.assertNotNull(deleted); - assertThat(deleted.getStatus()).isEqualTo(INACTIVE); + AttendanceCheckJpaEntity deleted = attendanceCheckJpaRepository + .findByAttendanceCheckIdAndStatus(ac1.getAttendanceCheckId(), INACTIVE) + .orElse(null); + assertThat(deleted).isNotNull();
45-45: 미사용 필드 정리 제안ObjectMapper가 사용되지 않습니다. 제거하면 경고 감소 및 가독성 향상됩니다.
적용 제안(diff):
- @Autowired private ObjectMapper objectMapper;
61-87: 부가 시나리오 테스트 추가 권장(선택 사항)
- 이미 삭제된 항목 재삭제 → 404(ATTENDANCE_CHECK_NOT_FOUND) 기대
- 존재하지 않는 ID 삭제 → 404 기대
실제 운영 이슈 재현에 유용합니다. 원하시면 테스트 케이스 템플릿을 드리겠습니다.src/main/java/konkuk/thip/roompost/application/service/AttendanceCheckDeleteService.java (2)
21-31: 삭제 플로우 구성 적절 + 주석 오탈자
- 방 참여자 검증 → 소유자 검증 → 삭제의 순서가 명확합니다.
- 주석 오탈자 수정 권장: “creator 겁증” → “creator 검증”.
적용 제안(diff):
- // 2. creator 겁증 + // 2. creator 검증
18-31: 쿼리 수 2→1로 줄이는 아키텍처 옵션(선택 사항)현재 흐름은 (1) getByIdOrThrow(SELECT) → (2) delete(SELECT+UPDATE)로 최소 2회의 DB 접근이 필요합니다. 퍼시스턴스 어댑터에 “creatorId까지 조건으로 소프트 삭제”를 캡슐화하면 단일 UPDATE로 끝낼 수 있습니다.
예:
- Adapter:
deleteIfCreator(attendanceCheckId, creatorId)추가- Repository:
update ... set status=INACTIVE where attendanceCheckId=:id and creator.userId=:creatorId and status=ACTIVE- 영향: 네트워크 RTT·락 점유 시간 감소, 경합 환경에서 TOCTOU 축소
원하시면 구체 diff(리포지토리/포트/서비스)까지 제안드리겠습니다.
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java (1)
138-151: DELETE 응답 설계에 대한 선택 사항: 200 + payload vs 204 No Content현재는 roomId를 반환(200)합니다. API 일관성이 200+payload라면 유지 OK. 만약 삭제는 204를 표준으로 쓰는 컨벤션이라면 204로 맞추고 응답 본문을 제거하는 것도 고려 가능합니다. 스웨거 문서에 응답 예시(성공/실패)를 추가하면 클라이언트 구현이 더 쉬워집니다.
원하시면 204 스타일로의 변경 diff(BaseResponse, 컨트롤러, 테스트)도 제공 가능합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
src/main/java/konkuk/thip/common/entity/BaseJpaEntity.java(1 hunks)src/main/java/konkuk/thip/common/exception/code/ErrorCode.java(1 hunks)src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java(1 hunks)src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java(1 hunks)src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java(2 hunks)src/main/java/konkuk/thip/roompost/adapter/in/web/response/AttendanceCheckDeleteResponse.java(1 hunks)src/main/java/konkuk/thip/roompost/adapter/out/jpa/AttendanceCheckJpaEntity.java(1 hunks)src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java(3 hunks)src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/attendancecheck/AttendanceCheckJpaRepository.java(2 hunks)src/main/java/konkuk/thip/roompost/application/port/in/AttendanceCheckDeleteUseCase.java(1 hunks)src/main/java/konkuk/thip/roompost/application/port/out/AttendanceCheckCommandPort.java(1 hunks)src/main/java/konkuk/thip/roompost/application/service/AttendanceCheckDeleteService.java(1 hunks)src/main/java/konkuk/thip/roompost/domain/AttendanceCheck.java(2 hunks)src/test/java/konkuk/thip/roompost/adapter/in/web/AttendanceCheckDeleteApiTest.java(1 hunks)src/test/java/konkuk/thip/roompost/domain/AttendanceCheckTest.java(2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#195
File: src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java:0-0
Timestamp: 2025-08-13T05:22:32.287Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/service/AttendanceCheckCreateService.java:22-40
Timestamp: 2025-08-14T09:15:31.371Z
Learning: seongjunnoh는 기술적 완벽성보다 실제 비즈니스 시나리오와 사용자 행동 패턴을 우선 고려하며, 발생 가능성이 낮은 엣지 케이스보다는 실용적인 구현을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
📚 Learning: 2025-08-14T07:19:27.310Z
Learnt from: buzz0331
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/port/out/AttendanceCheckQueryPort.java:5-5
Timestamp: 2025-08-14T07:19:27.310Z
Learning: AttendanceCheck 엔티티는 BaseJpaEntity를 상속받아 status 필드(StatusType.ACTIVE/INACTIVE/EXPIRED)로 소프트 딜리트를 구현하므로, 카운트 쿼리에서 status = 'ACTIVE' 조건을 포함해야 삭제된 레코드를 제외할 수 있음.
Applied to files:
src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.javasrc/main/java/konkuk/thip/roompost/application/port/out/AttendanceCheckCommandPort.javasrc/main/java/konkuk/thip/common/entity/BaseJpaEntity.javasrc/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/attendancecheck/AttendanceCheckJpaRepository.javasrc/main/java/konkuk/thip/roompost/adapter/out/jpa/AttendanceCheckJpaEntity.javasrc/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java
📚 Learning: 2025-08-14T09:27:36.450Z
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/adapter/out/persistence/AttendanceCheckQueryPersistenceAdapter.java:20-24
Timestamp: 2025-08-14T09:27:36.450Z
Learning: AttendanceCheckJpaRepository의 countByUserIdAndCreatedAtBetween 메서드는 메서드명과 달리 사용자 정의 JPQL 쿼리를 사용한다. "a.createdAt >= :startOfDay AND a.createdAt < :endOfDay"로 반열림구간 [startOfDay, endOfDay)을 정확히 구현하고 있어서 경계값 버그가 없다. 메서드명만으로 Spring Data JPA의 기본 Between 동작을 가정하면 안 된다.
Applied to files:
src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/attendancecheck/AttendanceCheckJpaRepository.java
📚 Learning: 2025-08-14T09:27:36.450Z
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/adapter/out/persistence/AttendanceCheckQueryPersistenceAdapter.java:20-24
Timestamp: 2025-08-14T09:27:36.450Z
Learning: AttendanceCheckJpaRepository에서 countByUserIdAndCreatedAtBetween 메서드는 실제로는 사용자 정의 JPQL 쿼리 "createdAt >= :startOfDay AND createdAt < :endOfDay"를 사용하여 반열림구간 [startOfDay, endOfDay)을 구현하고 있다. 메서드 이름만으로 Spring Data JPA의 기본 Between 동작을 가정하면 안 된다.
Applied to files:
src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/attendancecheck/AttendanceCheckJpaRepository.java
🧬 Code graph analysis (1)
src/test/java/konkuk/thip/roompost/adapter/in/web/AttendanceCheckDeleteApiTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(30-394)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (11)
src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java (1)
314-318: 검증 스크립트 실행 완료 후 요약–
AttendanceCheckDeleteService에서는
roomParticipantValidator.validateUserIsRoomMember(roomId, creatorId)호출로 방 참여 여부를 404(ROOM_NOT_FOUND)가 아닌 403(ROOM_ACCESS_FORBIDDEN)로 처리AttendanceCheckCommandPort.delete(...)호출 시 존재하지 않는attendanceCheck는EntityNotFoundException(ATTENDANCE_CHECK_NOT_FOUND)으로 404 처리- 작성자 불일치는 도메인의
validateCreator에서InvalidStateException(ATTENDANCE_CHECK_CAN_NOT_DELETE)로 403 처리– 컨트롤러(
RoomPostCommandController)는@ExceptionDescription(ATTENDANCE_CHECK_DELETE)하나로 위 예외들을 매핑
– 테스트(AttendanceCheckDeleteApiTest) 시나리오
• 정상 삭제 → 200
• 타인 글 삭제 요청 → 403
• 비활성 상태 등 잘못된 삭제 요청 → 400결론 및 제안 정리
- 에러 매핑 구성 적절
SwaggerResponseDescription의ATTENDANCE_CHECK_DELETE는
ROOM_ACCESS_FORBIDDEN(403), ATTENDANCE_CHECK_NOT_FOUND(404), ATTENDANCE_CHECK_CAN_NOT_DELETE(403) 세 가지로 구성돼 있어 서비스 흐름과 일치합니다.- 상태코드 변경 시 점검 불필요
ATTENDANCE_CHECK_CAN_NOT_DELETE를 403으로 바꿔도 열거 상수만 변경되므로 스웨거 매핑은 그대로 유효합니다.- 추가 검토 사항 (선택)
방이 존재하지 않을 때 404(ROOM_NOT_FOUND)를 내보낼 수 있다면, 다른 Delete API와 일관성을 위해ATTENDANCE_CHECK_DELETE에ROOM_NOT_FOUND가 포함된 구성이 필요한지 확인해 보세요. 테스트 시나리오에서 불필요하다면 그대로 두셔도 무방합니다.src/main/java/konkuk/thip/roompost/domain/AttendanceCheck.java (1)
8-8: 에러코드 의존성 추가 적절삭제 권한 검증 용도로
ATTENDANCE_CHECK_CAN_NOT_DELETE를 명시적 static import 한 점, 도메인 의도와 일치합니다.src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/attendancecheck/AttendanceCheckJpaRepository.java (2)
10-10: Optional import 추가 OK새로운 finder 메서드 반환형과 일치합니다.
23-23: 기존findById호출 경로 없음 — 추가 검증 불필요
attendanceCheckJpaRepository.findById호출이 전혀 존재하지 않음을 확인했습니다.- 직접 작성된 JPQL(count 쿼리)에도
status필터가 포함되어 있어 삭제된(비활성) 레코드를 제외하고 있습니다.- 따라서
findByAttendanceCheckIdAndStatus추가에 따른 혼용 우려가 없으며, 별도의 전환 누락 점검은 필요하지 않습니다.src/test/java/konkuk/thip/roompost/domain/AttendanceCheckTest.java (1)
7-8: 정확한 예외 메시지 검증을 위한 상수 import 👍테스트 가독성 향상에 도움이 됩니다.
src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java (2)
47-49: 소프트 삭제에 따른 ACTIVE 스코프 조회 적용, 적절합니다findById가 ACTIVE 상태만 조회하도록 바뀐 점이 일관성 확보와 버그 예방에 유리합니다. 동일한 제약을 기대하는 호출부(특히 카운트/리스트 쿼리)도 점진적으로 정렬하는 것이 좋습니다.
향후 조회 전체를 상태 조건으로 통일할 계획이라면 체크리스트에 추가해 주세요.
52-58: 리뷰 코멘트 무시:getId()가 올바른 도메인 식별자 접근자입니다.도메인
AttendanceCheck엔티티는BaseDomainEntity를 상속받아 식별자를getId()로 제공하며, JPA 어댑터의findByAttendanceCheckIdAndStatus(attendanceCheck.getId(), …)호출은 도메인 ID를 JPA 엔티티의attendanceCheckId와 매핑하여 정상 동작합니다. 도메인에getAttendanceCheckId()메서드는 존재하지 않아 제안된 변경은 컴파일 에러를 발생시킵니다.Likely an incorrect or invalid review comment.
src/test/java/konkuk/thip/roompost/adapter/in/web/AttendanceCheckDeleteApiTest.java (2)
89-112: 비참여자 403 시나리오 테스트 적절합니다접근 제어와 에러 메시지 단언이 명확합니다. 유지하세요.
114-139: 작성자 불일치 400 시나리오 테스트 적절합니다도메인 소유권 검증의 실패 흐름이 잘 드러납니다.
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java (2)
38-38: UseCase 의존성 주입 추가 LGTM필드 주입이 아닌 생성자 주입 패턴 유지되어 일관적입니다.
129-136: createAttendanceCheck 엔드포인트 명세 정리 LGTM메서드명과 설명이 기능과 일치합니다. 기존 클라이언트가 메서드명에 의존하지 않으므로 호환성 문제도 없습니다.
|
|
||
| // TODO : status를 private로 변경, status를 변경하는 protected 메서드 추가 |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
status를 private 전환 시, 변경 경로 단일화/검증 훅을 함께 설계하세요
TODO 방향성에 동의합니다. 다음을 함께 반영하면 도메인 일관성과 오용 방지에 도움이 됩니다.
- 변경 경로 단일화: protected final 메서드로만 상태 전이를 허용하고, 외부에서 직접 필드 접근을 금지.
- 전이 검증: 동일 상태 재전이 방지, 삭제 불가 상태(예: EXPIRED 등) 사전 검증 훅 제공.
- 감사/감시 포인트: 상태 전이 시점에 도메인 이벤트/감사 로그를 연결하기 쉬워집니다.
아래와 같은 형태를 고려해 보세요(파일 밖 변경 예시):
// BaseJpaEntity
protected final void changeStatus(StatusType next) {
if (this.status == next) return; // idempotent
// 전이 검증 훅 (override 가능)
validateStatusTransition(this.status, next);
this.status = next;
// onStatusChanged(next); // 이벤트 훅이 필요하면
}
protected void validateStatusTransition(StatusType current, StatusType next) {
// 기본 구현: 항상 허용, 하위 클래스에서 제약 가능
}또한, 소프트 딜리트 누락을 줄이기 위해
- 각 엔티티에
@Where(clause = "status = 'ACTIVE'")적용(공통화가 어려우면 최소한 조회량이 많은 엔티티부터), - 혹은 Hibernate Filter(편의성과 성능 사이 절충) 도입
을 검토해 주세요. findByIdAndStatus 패턴 반복을 줄이고, 쿼리 누락 위험을 낮춥니다. 상태 컬럼 인덱스(또는 복합 인덱스 status + FK)도 함께 고려하면 조회 성능이 안정적입니다.
소프트 딜리트 조건 누락 지점을 빠르게 점검하려면 아래 스크립트를 실행해 보세요.
🏁 Script executed:
#!/bin/bash
# 상태 조건 없이 findById, getReferenceById 등을 사용하는 지점 탐색
rg -nP -C2 --type=java '\b(findById|getReferenceById|getById)\s*\('
# 상태 포함 메서드 패턴 점검
rg -nP -C1 --type=java '\bfindByIdAndStatus\b|\bfind.*Status\b'Length of output: 105947
status 필드 접근 제어 및 soft delete 필터 일괄 적용 필수
TODO에 제안된 방향성에 동의합니다. 다음 필수 조치를 함께 반영해 주세요:
- 접근 제어 및 상태 전이 메서드 통합
BaseJpaEntity의status를private으로 변경하고, 오직 아래와 같은protected final changeStatus(StatusType next)메서드를 통해서만 상태 전이를 허용하세요.// BaseJpaEntity protected final void changeStatus(StatusType next) { if (this.status == next) return; // idempotent validateStatusTransition(this.status, next); // 전이 검증 hook this.status = next; // onStatusChanged(next); // 이벤트 hook 필요 시 } protected void validateStatusTransition(StatusType current, StatusType next) { // 기본 구현: 항상 허용, 하위 클래스에서 override로 제약 가능 }
- soft delete 필터 일괄 적용
BaseJpaEntity(또는 각 엔티티) 클래스 상단에
@Where(clause = "status = 'ACTIVE'")
또는 Hibernate Filter를 도입해, 모든 조회에 자동으로status = 'ACTIVE'조건이 포함되도록 구성하세요. - 서비스·퍼시스턴스 레이어 조회 메서드 점검
스크립트 실행 결과, FeedShowUserInfoService·FeedShowSingleService·RoomJoinService 등에서
userCommandPort.findById,bookCommandPort.findById,roomCommandPort.findById등
상태 필터 없이 직접 조회하는 지점이 다수 발견되었습니다.
해당 호출부를 status 필터가 적용된 커스텀 메서드 또는 필터링 로직을 거치는 형태로 수정해 주세요. - 상태 컬럼 인덱스 검토
조회 성능 안정화를 위해status컬럼 단독 또는 FK와 복합 인덱스 생성을 고려하세요.
위 항목들을 반영한 후 다시 리뷰 요청 부탁드립니다.
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/common/entity/BaseJpaEntity.java around lines
38-39: change the status field to private and add a protected final
changeStatus(StatusType next) method that enforces idempotency, calls a
protected validateStatusTransition(current,next) hook (default no-op) and then
sets the status; add an optional protected onStatusChanged hook if needed;
annotate the base entity (or each entity) with a global soft-delete filter such
as @Where(clause = "status = 'ACTIVE'") or configure a Hibernate Filter so reads
automatically exclude non-active rows; update service/repository lookup points
(e.g., userCommandPort.findById, bookCommandPort.findById,
roomCommandPort.findById and the callers in FeedShowUserInfoService,
FeedShowSingleService, RoomJoinService) to use status-aware find methods or
apply the filter before returning; and consider adding an index on the status
column (or composite index with relevant FKs) for read performance.
| public void validateCreator(Long userId) { | ||
| if (!creatorId.equals(userId)) { | ||
| throw new InvalidStateException(ATTENDANCE_CHECK_CAN_NOT_DELETE); | ||
| } | ||
| } |
There was a problem hiding this comment.
NPE 방지: Long 비교는 Objects.equals 사용 권장
creatorId 또는 인자 userId가 null일 경우 creatorId.equals(userId)에서 NPE 위험이 있습니다. Objects.equals로 치환해 주세요.
아래 수정 제안:
- public void validateCreator(Long userId) {
- if (!creatorId.equals(userId)) {
- throw new InvalidStateException(ATTENDANCE_CHECK_CAN_NOT_DELETE);
- }
- }
+ public void validateCreator(Long userId) {
+ if (!java.util.Objects.equals(creatorId, userId)) {
+ throw new InvalidStateException(ATTENDANCE_CHECK_CAN_NOT_DELETE);
+ }
+ }파일 상단에 import를 별도로 두고 싶다면:
import java.util.Objects;그리고 본문은 Objects.equals(creatorId, userId)로 치환해 주세요.
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/roompost/domain/AttendanceCheck.java around lines
41 to 45, the method uses creatorId.equals(userId) which can throw NPE if either
value is null; replace the comparison with Objects.equals(creatorId, userId) and
negate it (if (!Objects.equals(creatorId, userId)) throw ...), and add the
import java.util.Objects at the top of the file.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
src/main/java/konkuk/thip/TestTokenController.java (3)
15-18: HTTP GET으로 토큰 발급 + 평문 문자열 반환은 운영 습관과 다릅니다 — POST와 JSON 응답으로 정렬하세요.민감 토큰 발급은 일반적으로 POST이고, 응답은 JSON이 표준적입니다. 테스트 코드/도구의 파싱 안정성도 좋아집니다.
제안 변경안:
-import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import java.util.Map; @@ - @GetMapping("/api/test/token/access") - public String generateAccessToken(@RequestParam Long userId) { - return jwtUtil.createAccessToken(userId); + @PostMapping("/api/test/token/access") + public Map<String, String> generateAccessToken(@RequestParam Long userId) { + return Map.of("accessToken", jwtUtil.createAccessToken(userId)); }참고: GET 유지가 꼭 필요하면 그대로 두셔도 되지만, JSON 응답으로 바꾸는 것만이라도 권장합니다.
15-17: 파라미터 최소 검증 추가 권장 — 음수/0 userId 차단.로컬 테스트에서도 잘못된 입력을 초기에 걸러두면 디버깅 비용이 내려갑니다. 간단히 Bean Validation을 적용하세요.
예시:
+import org.springframework.validation.annotation.Validated; +import jakarta.validation.constraints.Min; @@ -@RestController -@RequiredArgsConstructor +@RestController +@RequiredArgsConstructor +@Validated public class TestTokenController { @@ - public Map<String, String> generateAccessToken(@RequestParam Long userId) { + public Map<String, String> generateAccessToken(@RequestParam @Min(1) Long userId) { return Map.of("accessToken", jwtUtil.createAccessToken(userId)); }추가로, 필요 시 존재하지 않는 사용자에 대한 토큰 발급을 막도록
JwtUtil앞단에서 사용자 존재 여부 확인도 고려해 주세요.
15-18: 테스트 토큰의 오남용 방지: 짧은 TTL 또는 “source=test” 클레임 부여 후 비테스트 프로파일에서 거부.운영 키/검증 로직을 공유한다면 테스트 토큰의 악용을 더 줄여야 합니다. 생성 토큰에 별도 클레임(
source=test)을 넣고, 필터에서 prod 프로파일일 때 해당 클레임이 있으면 거부하도록 하세요. 또는 테스트 토큰 TTL을 1~5분으로 제한하세요.
JwtUtil에 손대야 하므로 여기서는 개념 제안만 남깁니다. 구현 필요하시면 말씀 주세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/main/java/konkuk/thip/TestTokenController.java(1 hunks)src/main/java/konkuk/thip/config/SecurityConfig.java(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#195
File: src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java:0-0
Timestamp: 2025-08-13T05:22:32.287Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/service/AttendanceCheckCreateService.java:22-40
Timestamp: 2025-08-14T09:15:31.371Z
Learning: seongjunnoh는 기술적 완벽성보다 실제 비즈니스 시나리오와 사용자 행동 패턴을 우선 고려하며, 발생 가능성이 낮은 엣지 케이스보다는 실용적인 구현을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
| "/api/test/**", // for test | ||
|
|
There was a problem hiding this comment.
프로덕션에서 /api/test/ 무인증 허용은 치명적 — 누구나 임의 userId로 액세스 토큰을 발급받을 수 있습니다.**
이 PR에 포함된 TestTokenController와 결합되면, 프로덕션에서도 /api/test/token/access?userId=... 호출만으로 유효 JWT를 생성할 수 있습니다(서명 키가 동일하다는 가정). 이는 계정 탈취로 직결되는 보안 이슈입니다. 최소한 프로파일/프로퍼티로 비활성화하고 기본 화이트리스트에서는 제거해야 합니다.
해결책 1(권장): 기본 화이트리스트에서 제거
- "/api/test/**", // for test해결책 2: 프로퍼티 기반 조건부 허용(코드 외 추가 제안)
- 필드 추가:
@Value("${thip.security.enable-test-endpoints:false}") private boolean enableTestEndpoints;
- authorize 섹션에서 조건부로 허용:
.authorizeHttpRequests(auth -> { auth.requestMatchers(WHITELIST).permitAll(); if (enableTestEndpoints) { auth.requestMatchers("/api/test/**").permitAll(); } auth.anyRequest().authenticated(); })
- application-local.yml 등에만
thip.security.enable-test-endpoints: true설정, prod에는 생략 또는 false 유지.
또는, TestTokenController 자체를 @Profile({"local","dev","test"})로 제한하면 SecurityConfig에 남아 있어도 prod에서는 404로 차단됩니다(아래 컨트롤러 코멘트 참고).
🤖 Prompt for AI Agents
src/main/java/konkuk/thip/config/SecurityConfig.java around lines 57-58, the
WHITELIST currently includes "/api/test/**" which allows unauthenticated JWT
minting in prod; remove "/api/test/**" from the default WHITELIST and instead
either (A) conditionally permit it via a property: add a boolean field (e.g.
@Value("${thip.security.enable-test-endpoints:false}") private boolean
enableTestEndpoints;) and in the authorize section only call
auth.requestMatchers("/api/test/**").permitAll() when that flag is true, or (B)
remove it entirely and annotate TestTokenController with a non-prod profile
(e.g. @Profile("local|dev|test")) so the endpoint is not available in
production.
| package konkuk.thip; | ||
|
|
||
| import konkuk.thip.common.security.util.JwtUtil; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| public class TestTokenController { | ||
|
|
||
| private final JwtUtil jwtUtil; | ||
|
|
||
| @GetMapping("/api/test/token/access") | ||
| public String generateAccessToken(@RequestParam Long userId) { | ||
| return jwtUtil.createAccessToken(userId); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
검증 스크립트: 테스트 엔드포인트가 실제로 프로덕션에서 비활성화되는지 확인해 주세요.
아래 스크립트로 컨트롤러 게이팅 어노테이션/프로퍼티와 SecurityConfig 화이트리스트 상태를 점검할 수 있습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== TestTokenController 게이팅 어노테이션 확인(@Profile/@ConditionalOnProperty) =="
rg -nP '@Profile|@ConditionalOnProperty' src/main/java/konkuk/thip/TestTokenController.java || {
echo "경고: 게이팅 어노테이션이 없습니다."
}
echo "== SecurityConfig에 /api/test 화이트리스트 존재 여부 =="
rg -nP '/api/test/\*\*' src/main/java/konkuk/thip/config/SecurityConfig.java || echo "OK: SecurityConfig 화이트리스트에 없음"
echo "== application* 설정에서 테스트 엔드포인트 프로퍼티 존재 여부 =="
rg -n --glob 'application*.yml' 'thip\.security\.enable-test-endpoints' -n -C2 || echo "참고: 프로퍼티 미정의(기본 false 가정)"Length of output: 929
TestTokenController에 프로파일/프로퍼티 기반 게이팅이 없고, SecurityConfig에 화이트리스트가 등록되어 있어 실제 운영 환경에서도 엔드포인트가 활성화됩니다. 아래 수정 사항을 반영해주세요:
- src/main/java/konkuk/thip/TestTokenController.java
@Profile("test")또는
@ConditionalOnProperty(prefix = "thip.security", name = "enable-test-endpoints", havingValue = "true")어노테이션 추가
- src/main/java/konkuk/thip/config/SecurityConfig.java
"/api/test/**"경로를 화이트리스트에서 제거하거나,
프로퍼티/프로파일 조건부로 등록하도록 변경
- application.yml (및 application-*.yml)
thip.security.enable-test-endpoints프로퍼티 정의 (기본값false, 테스트 환경에서만true)
예시 diff:
@@ -7,6 +7,8 @@
@RestController
@RequiredArgsConstructor
+@ConditionalOnProperty(
+ prefix = "thip.security", name = "enable-test-endpoints", havingValue = "true", matchIfMissing = false
+)
public class TestTokenController {
private final JwtUtil jwtUtil;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/TestTokenController.java around lines 1-19, the
test-only controller is unguarded and exposed in production; add gating by
annotating the class with either @Profile("test") or
@ConditionalOnProperty(prefix = "thip.security", name = "enable-test-endpoints",
havingValue = "true") so it only loads when intended; in
src/main/java/konkuk/thip/config/SecurityConfig.java remove "/api/test/**" from
the whitelist or register that whitelist entry conditionally based on the same
property/profile; and add thip.security.enable-test-endpoints: false to
application.yml (override to true in test-specific yml) so the endpoint is
disabled by default in production.
There was a problem hiding this comment.
@coderabbitai 너의 리뷰처럼 yml 파일에서의 property 값에 따라서 TestTokenController 의 visibility 를 설정할 수 있도록 코드를 수정하였어.
There was a problem hiding this comment.
🧩 Analysis chain
@seongjunnoh 수정사항을 확인해보겠습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== TestTokenController 현재 상태 확인 =="
cat src/main/java/konkuk/thip/TestTokenController.java
echo -e "\n== SecurityConfig WHITELIST 현재 상태 =="
rg -nP -A 10 -B 5 'WHITELIST.*=' src/main/java/konkuk/thip/config/SecurityConfig.java
echo -e "\n== yml 파일들에서 테스트 관련 프로퍼티 확인 =="
find . -name 'application*.yml' -exec echo "=== {} ===" \; -exec cat {} \; 2>/dev/null || echo "yml 파일 없음"
Length of output: 1936
좋습니다! TestTokenController에 @ConditionalOnProperty 어노테이션이 정상적으로 추가된 것을 확인했습니다. 추가로 yml 설정 파일들을 확인해보겠습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== resources 폴더에서 yml 파일들 찾기 =="
find . -path "*/resources/*" -name '*.yml' -exec echo "=== {} ===" \; -exec cat {} \;
echo -e "\n== properties 파일들도 확인 =="
find . -path "*/resources/*" -name '*.properties' -exec echo "=== {} ===" \; -exec cat {} \;
echo -e "\n== thip.test-api 관련 프로퍼티 검색 =="
rg -r --type yaml --type properties "thip\.test-api" . || echo "thip.test-api 프로퍼티 미발견"
Length of output: 572
설정 파일들을 더 광범위하게 검색해보겠습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== 전체 폴더에서 yml/yaml 파일들 검색 =="
find . -name '*.yml' -o -name '*.yaml' | head -10
echo -e "\n== application 관련 설정 파일들 검색 =="
find . -name 'application*' | head -10
echo -e "\n== 모든 파일에서 thip 관련 프로퍼티 검색 =="
rg -l "thip\." . 2>/dev/null | head -5 || echo "thip 프로퍼티 설정 파일 미발견"
echo -e "\n== SecurityConfig에서 조건부 화이트리스트 설정 확인 =="
rg -n -A 3 -B 3 "api/test" src/main/java/konkuk/thip/config/SecurityConfig.java
Length of output: 1904
@seongjunnoh 좋습니다! TestTokenController에 @ConditionalOnProperty(name = "thip.test-api.enabled", havingValue = "true") 어노테이션을 추가하신 것을 확인했습니다.
하지만 아직 완전하지 않은 부분이 있습니다:
✅ 완료된 부분:
- TestTokenController에 조건부 빈 생성 로직 추가
❌ 남은 보안 이슈:
SecurityConfig의 화이트리스트에 /api/test/**가 여전히 하드코딩되어 있어서, 컨트롤러가 비활성화되어도 해당 경로는 인증 없이 접근 가능합니다.
추가 수정 필요사항:
// SecurityConfig.java에서 조건부 화이트리스트 등록
@ConditionalOnProperty(name = "thip.test-api.enabled", havingValue = "true")
@Bean
public String[] getTestWhitelist() {
return new String[]{"/api/test/**"};
}또는 SecurityConfig 자체에서 프로퍼티를 주입받아 동적으로 화이트리스트를 구성하는 방법도 있습니다.
현재 상태로는 컨트롤러는 비활성화되지만 경로는 여전히 열려있어 보안상 완전하지 않습니다. 이 부분도 함께 수정해주시겠어요?
🧠 Learnings used
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#195
File: src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java:0-0
Timestamp: 2025-08-13T05:22:32.287Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/service/AttendanceCheckCreateService.java:22-40
Timestamp: 2025-08-14T09:15:31.371Z
Learning: seongjunnoh는 기술적 완벽성보다 실제 비즈니스 시나리오와 사용자 행동 패턴을 우선 고려하며, 발생 가능성이 낮은 엣지 케이스보다는 실용적인 구현을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
There was a problem hiding this comment.
@coderabbitai 음 그런데 controller가 비활성화되면 해당 api 엔드포인트로 접근이 가능하더라도 404 error 가 발생하니 문제없는거 아닌가??
There was a problem hiding this comment.
@seongjunnoh 맞습니다! 기술적으로 정확한 지적입니다.
@ConditionalOnProperty로 TestTokenController 빈이 생성되지 않으면 Spring이 해당 엔드포인트를 전혀 등록하지 않기 때문에, SecurityConfig의 화이트리스트에 /api/test/**가 있어도 실제로는 404 에러가 발생합니다.
보안상 실질적인 위험은 없습니다:
- 컨트롤러 빈이 없으면 → 엔드포인트 매핑이 없음 → 404 응답
- 인증을 우회해도 처리할 핸들러가 존재하지 않음
SecurityConfig에서 해당 경로를 제거하는 것은 코드 정리 차원의 문제일 뿐, 보안상 필수는 아니네요. 현재 구현으로도 충분히 안전합니다.
실용적인 관점에서 현재 상태로 두시는 것이 합리적입니다! 👍
✏️ Learnings added
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#278
File: src/main/java/konkuk/thip/TestTokenController.java:0-0
Timestamp: 2025-08-24T09:33:52.961Z
Learning: seongjunnoh는 Spring의 ConditionalOnProperty 동작 원리를 정확히 이해하고 있으며, 보안 이슈에 대해서도 실질적인 위험성을 기준으로 판단하는 실용적 접근을 선호한다.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#195
File: src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java:0-0
Timestamp: 2025-08-13T05:22:32.287Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/service/AttendanceCheckCreateService.java:22-40
Timestamp: 2025-08-14T09:15:31.371Z
Learning: seongjunnoh는 기술적 완벽성보다 실제 비즈니스 시나리오와 사용자 행동 패턴을 우선 고려하며, 발생 가능성이 낮은 엣지 케이스보다는 실용적인 구현을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
- 테스트 api 용 controller는 property의 값이 true 인 경우에만 접근 가능
| @RestController | ||
| @RequiredArgsConstructor | ||
| @ConditionalOnProperty(name = "thip.test-api.enabled", havingValue = "true") | ||
| public class TestTokenController { | ||
|
|
||
| private final JwtUtil jwtUtil; | ||
|
|
||
| @GetMapping("/api/test/token/access") | ||
| public String generateAccessToken(@RequestParam Long userId) { | ||
| return jwtUtil.createAccessToken(userId); | ||
| } |
| @Column(nullable = false) | ||
| protected StatusType status = StatusType.ACTIVE; | ||
|
|
||
| // TODO : status를 private로 변경, status를 변경하는 protected 메서드 추가 |
There was a problem hiding this comment.
저번에 posts 관련 jpa entity 에서 soft delete 를 처리할때 BaseJpaEntity 의 status 필드를 private로 유지하고, 대신 BaseJpaEntity 내부에 status 의 상태변경을 위한 메서드를 뚫어놓는 식으로 수정하는게 좋을것 같다고 얘기 나눴던것 같아서 TODO로 추가해보았습니다!
| @Test | ||
| @DisplayName("오늘의 한마디 작성자가 맞을 경우, 예외를 터뜨리지 않는다.") | ||
| void validate_creator_success() throws Exception { | ||
| //given | ||
| AttendanceCheck ac = AttendanceCheck.builder() | ||
| .id(1L) | ||
| .creatorId(1L) | ||
| .todayComment("오늘의 한마디!!!") | ||
| .roomId(1L) | ||
| .build(); | ||
|
|
||
| //when //then | ||
| assertDoesNotThrow(() -> ac.validateCreator(1L)); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("오늘의 한마디 작성자가 아닐 경우, InvalidStateException 을 터뜨린다.") | ||
| void validate_creator_fail() throws Exception { | ||
| //given | ||
| AttendanceCheck ac = AttendanceCheck.builder() | ||
| .id(1L) | ||
| .creatorId(1L) | ||
| .todayComment("오늘의 한마디!!!") | ||
| .roomId(1L) | ||
| .build(); | ||
|
|
||
| //when //then | ||
| assertThatThrownBy(() -> ac.validateCreator(2L)) | ||
| .isInstanceOf(InvalidStateException.class) | ||
| .hasMessage(ATTENDANCE_CHECK_CAN_NOT_DELETE.getMessage()); | ||
| } |
…eeting-remove # Conflicts: # src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java
| @RestController | ||
| @RequiredArgsConstructor | ||
| @ConditionalOnProperty(name = "thip.test-api.enabled", havingValue = "true") | ||
| public class TestTokenController { | ||
|
|
||
| private final JwtUtil jwtUtil; | ||
|
|
||
| @GetMapping("/api/test/token/access") | ||
| public String generateAccessToken(@RequestParam Long userId) { | ||
| return jwtUtil.createAccessToken(userId); | ||
| } |
|
|
||
| // 2. creator 겁증 | ||
| AttendanceCheck attendanceCheck = attendanceCheckCommandPort.getByIdOrThrow(attendanceCheckId); | ||
| attendanceCheck.validateCreator(creatorId); |
| } | ||
|
|
||
| @Test | ||
| @DisplayName("다른 사람이 작성한 오늘의 한마디의 삭제 요청을 할 경우, 400 error 를 반환한다.") |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java (1)
120-131: Swagger 예외 설명 상수 오타(VOTE 삭제인데 RECORD_DELETE 사용)문서화/스웨거 응답 설명이 잘못 매핑되어 소비자 혼선을 야기합니다. VOTE_DELETE로 교체해 주세요.
적용 diff:
- @ExceptionDescription(RECORD_DELETE) + @ExceptionDescription(VOTE_DELETE)
🧹 Nitpick comments (4)
src/main/java/konkuk/thip/roompost/application/service/RoomPostUpdateService.java (2)
41-55: Vote 수정도 동일한 권한 검증 필요Vote.updateVote에서도 작성자 검증이 도메인/서비스 중 한 곳에서 확실히 이뤄지는지 확인 부탁드립니다. 위 스크립트로 함께 점검 가능합니다.
24-38: 서비스 레이어에서 작성자 검증 중복 제거 및 도메인 검증 확인
- Record.updateRecord 메서드 내부에서 이미 작성자 검증(
validateCreator(userId))이 수행되고 있습니다 (src/main/java/konkuk/thip/roompost/domain/Record.java:106)- 따라서 서비스 레이어에 별도
record.validateCreator(command.userId())검증 코드는 중복이므로 제거해도 무방합니다선택 권장 사항:
- JPA
@Version애노테이션을 활용한 낙관적 락을 도입하여 동시 수정 충돌을 방지하고, 충돌 발생 시 HTTP 409 또는 비즈니스 예외로 매핑하세요src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java (2)
168-180: 오늘의 한마디 삭제 엔드포인트 추가 👍소프트 삭제 흐름에 맞는 API 설계로 보입니다. BaseResponse로 200 OK 응답을 유지하는 현재 컨벤션과도 일치합니다. 권한/작성자 검증 실패(403/404) 케이스에 대한 통합 테스트도 추가 부탁드립니다.
원하시면 삭제·권한 실패·이미 삭제된 경우(멱등) 커버리지를 포함한 API 통합 테스트 케이스 템플릿을 드리겠습니다.
69-85: 업데이트 엔드포인트 테스트 보강 제안
- 작성자가 아닌 사용자의 수정 시 403
- 존재하지 않거나 비활성(소프트 삭제)된 게시물 수정 시 404
- 성공 케이스 200 + 응답 페이로드 필드 검증
현재 PR 요지(삭제)와 직접적이진 않으나, 이번에 함께 추가된 엔드포인트의 회귀를 막기 위해 최소 케이스 권장드립니다.
Also applies to: 133-149
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java(1 hunks)src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java(4 hunks)src/main/java/konkuk/thip/roompost/application/service/RoomPostUpdateService.java(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#195
File: src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java:0-0
Timestamp: 2025-08-13T05:22:32.287Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/service/AttendanceCheckCreateService.java:22-40
Timestamp: 2025-08-14T09:15:31.371Z
Learning: seongjunnoh는 기술적 완벽성보다 실제 비즈니스 시나리오와 사용자 행동 패턴을 우선 고려하며, 발생 가능성이 낮은 엣지 케이스보다는 실용적인 구현을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#278
File: src/main/java/konkuk/thip/common/exception/code/ErrorCode.java:210-211
Timestamp: 2025-08-24T09:40:48.561Z
Learning: seongjunnoh는 HTTP 상태 코드 선택에 대해 기술적 근거와 코드베이스 내 일관성을 중요하게 생각하며, 구체적인 사례 분석을 통한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#278
File: src/main/java/konkuk/thip/TestTokenController.java:0-0
Timestamp: 2025-08-24T09:33:52.961Z
Learning: seongjunnoh는 Spring의 ConditionalOnProperty 동작 원리를 정확히 이해하고 있으며, 보안 이슈에 대해서도 실질적인 위험성을 기준으로 판단하는 실용적 접근을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (4)
src/main/java/konkuk/thip/roompost/application/service/RoomPostUpdateService.java (1)
13-13: 트랜잭션 경계 추가 적절합니다쓰기 연산인 updateRecord/updateVote에 @transactional을 부여한 선택 좋습니다. 메서드 단위 적용으로 의도도 명확합니다.
Also applies to: 24-24, 41-41
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java (3)
34-34: 삭제 유스케이스 DI 추가 👍AttendanceCheckDeleteUseCase 주입으로 책임이 명확해졌습니다.
159-159: 메서드명(createAttendanceCheck)으로 의도 명확화 👍엔드포인트 역할이 분명해졌습니다.
69-85: 검증 결과: @PathVariable 및 도메인 권한 검증 모두 적절히 처리됨 확인됨
@PathVariable이름 미지정 사례가 스크립트로 탐지되지 않았습니다. 컨트롤러 메서드들의 파라미터 이름이 URI 템플릿({roomId},{recordId})과 일치하여 암묵적 바인딩이 정상적으로 동작합니다.- 도메인 계층(
Vote,Record,AttendanceCheck,Feed,Comment등)에서validateCreator(...)메서드를 통해 작성자 검증 로직이 이미 구현되어 있어, 컨트롤러 레벨에서 추가 검증 없이도 권한이 보장됩니다.위 두 사항 모두 문제없음이 확인되었으므로 추가 조치 불필요합니다.
| @Operation( | ||
| summary = "기록 수정", | ||
| description = "사용자가 방 기록을 수정합니다. (기록 내용만 수정 가능)" | ||
| ) | ||
| @PatchMapping("/rooms/{roomId}/records/{recordId}") | ||
| @ExceptionDescription(RECORD_UPDATE) | ||
| public BaseResponse<RecordUpdateResponse> updateRecord( | ||
| @Parameter(hidden = true) @UserId Long userId, | ||
| @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId, | ||
| @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable Long recordId, | ||
| @RequestBody @Valid final RecordUpdateRequest request | ||
| ) { | ||
| return BaseResponse.ok(RecordUpdateResponse.of( | ||
| roomPostUpdateUseCase.updateRecord(request.toCommand(userId, roomId, recordId)) | ||
| )); | ||
| } | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
@PathVariable 이름 미지정으로 바인딩 실패 가능성 — 명시화 권장
현재 컴파일 시 -parameters 미설정이면 파라미터 이름 소실로 바인딩 실패 위험이 있습니다. 기존 메서드들과도 일관되게 변수명을 명시해 주세요.
적용 diff:
- public BaseResponse<RecordUpdateResponse> updateRecord(
- @Parameter(hidden = true) @UserId Long userId,
- @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId,
- @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable Long recordId,
+ public BaseResponse<RecordUpdateResponse> updateRecord(
+ @Parameter(hidden = true) @UserId Long userId,
+ @Parameter(description = "수정할 방 ID", example = "1") @PathVariable("roomId") Long roomId,
+ @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable("recordId") Long recordId,
@RequestBody @Valid final RecordUpdateRequest request또한 생성(POST /rooms/{roomId}/record)과 수정(PATCH /rooms/{roomId}/records/{recordId})의 단수/복수 경로가 불일치합니다. 레거시와의 호환을 고려해야 하므로 즉시 변경은 권하지 않지만, 차기 버전에서 REST 리소스 명 복수형으로의 통일을 고려해 주세요.
마지막으로, RoomPostUpdateService.updateRecord가 roomId를 반환하는데, RecordUpdateResponse.of의 생성 인자와 의미가 일치하는지 확인 부탁드립니다.
검증을 위해 아래 스크립트를 실행하겠습니다.
해당 스크립트는
- 빌드 설정에서
-parameters옵션 사용 여부 확인 - 컨트롤러의 POST/ PATCH 매핑 경로 일관성 확인
roomPostUpdateUseCase.updateRecord반환 타입과RecordUpdateResponse.of()인자 일치 여부 확인
를 수행합니다.
🏁 Script executed:
#!/bin/bash
set -e
# 1. 빌드 설정에서 -parameters 옵션 사용 여부 확인
echo "==> pom.xml 또는 build.gradle 확인"
fd pom.xml build.gradle* -c never || true
if [ -f pom.xml ]; then
echo "--> pom.xml 내 maven-compiler-plugin 설정:"
grep -R "<parameters>" -n pom.xml || echo "parameters 설정 없음"
fi
if ls build.gradle* 1> /dev/null 2>&1; then
echo "--> build.gradle* 내 compileJava.compilerArgs 설정:"
grep -R "compilerArgs" -n build.gradle* || echo "compilerArgs 설정 없음"
fi
# 2. 컨트롤러 매핑 경로 일관성 확인
echo "==> RoomPostCommandController의 매핑 확인"
rg -n "@PostMapping" -C1 src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java || true
rg -n "@PatchMapping" -C1 src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java || true
# 전체 컨트롤러에서 POST 경로 검색 (singular vs plural)
echo "==> 전체 @PostMapping 경로 목록:"
rg -n "@PostMapping" -C0 src/main/java/konkuk/thip/roompost/adapter/in/web || true
echo "==> 전체 @PatchMapping 경로 목록:"
rg -n "@PatchMapping" -C0 src/main/java/konkuk/thip/roompost/adapter/in/web || true
# 3. use case 반환 타입 및 of() 인자 일치 여부 확인
echo "==> useCase 필드 및 타입 확인"
rg -n "roomPostUpdateUseCase" -n src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java || true
echo "==> updateRecord 메서드 구현 위치 찾기"
rg -n "updateRecord" -n src/main/java/konkuk/thip/roompost || true
echo "==> RecordUpdateResponse.of 선언부 확인"
rg -n "class RecordUpdateResponse" -C3 -n src/main/java/konkuk/thip/roompost || trueLength of output: 4284
검증을 위해 아래 스크립트를 실행하겠습니다:
🏁 Script executed:
#!/usr/bin/env bash
set -e
# 1. updateRecord 구현 상세 확인
echo "==> RoomPostUpdateService.updateRecord 구현 (클래스/리턴/return) 확인"
grep -R "public Long updateRecord" -n -C3 src/main/java/konkuk/thip/roompost/application/service/RoomPostUpdateService.java || true
grep -R "return" -n -C3 src/main/java/konkuk/thip/roompost/application/service/RoomPostUpdateService.java || true
# 2. RecordUpdateResponse 클래스 및 of() 메서드 위치 확인
echo "==> RecordUpdateResponse 클래스/메서드 위치 확인"
grep -R "RecordUpdateResponse" -n -C5 src || trueLength of output: 4210
룸 기록 수정 API: @PathVariable 명시·경로 일관성·반환 값 검증 요청
검증 결과, 아래 사항들이 확인되었습니다. 런타임 오류 방지 및 API 일관성 유지를 위해 조치가 필요합니다.
- 빌드 설정(pom.xml·build.gradle)에
-parameters옵션이 설정되어 있지 않아, 파라미터 이름이 제거될 경우@PathVariable이름 바인딩이 실패할 수 있습니다.
→@PathVariable("roomId"),@PathVariable("recordId")로 이름을 명시해 주세요. - 생성 API는
/rooms/{roomId}/record(단수), 수정 API는/rooms/{roomId}/records/{recordId}(복수)를 사용하고 있어 경로가 불일치합니다. 레거시 호환성을 유지해야 하므로 즉시 변경은 권장하지 않지만, 차기 버전에서 REST 리소스 명을 복수형으로 통일하는 방안을 검토해 주세요. RoomPostUpdateService.updateRecord()가command.roomId()를 반환하고,RecordUpdateResponse.of()도roomId만 받도록 설계되어 있습니다. 응답에 포함해야 할 값(예: 갱신된recordId등)과 의미가 일치하는지 다시 한 번 확인 부탁드립니다.
적용 제안 diff:
@@ src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java
- @PatchMapping("/rooms/{roomId}/records/{recordId}")
+ @PatchMapping("/rooms/{roomId}/records/{recordId}")
@ExceptionDescription(RECORD_UPDATE)
public BaseResponse<RecordUpdateResponse> updateRecord(
@Parameter(hidden = true) @UserId Long userId,
- @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId,
- @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable Long recordId,
+ @Parameter(description = "수정할 방 ID", example = "1") @PathVariable("roomId") Long roomId,
+ @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable("recordId") Long recordId,
@RequestBody @Valid final RecordUpdateRequest request
) {
return BaseResponse.ok(RecordUpdateResponse.of(
roomPostUpdateUseCase.updateRecord(
- request.toCommand(userId, roomId, recordId)
+ request.toCommand(userId, roomId, recordId)
)
));
}- 위
@PathVariable이름 명시 적용 - 차기 버전 REST 리소스 명 복수형 통일 검토
- 반환 값(
roomId)이 의도한 응답 데이터와 일치하는지 확인
📝 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.
| @Operation( | |
| summary = "기록 수정", | |
| description = "사용자가 방 기록을 수정합니다. (기록 내용만 수정 가능)" | |
| ) | |
| @PatchMapping("/rooms/{roomId}/records/{recordId}") | |
| @ExceptionDescription(RECORD_UPDATE) | |
| public BaseResponse<RecordUpdateResponse> updateRecord( | |
| @Parameter(hidden = true) @UserId Long userId, | |
| @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId, | |
| @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable Long recordId, | |
| @RequestBody @Valid final RecordUpdateRequest request | |
| ) { | |
| return BaseResponse.ok(RecordUpdateResponse.of( | |
| roomPostUpdateUseCase.updateRecord(request.toCommand(userId, roomId, recordId)) | |
| )); | |
| } | |
| @Operation( | |
| summary = "기록 수정", | |
| description = "사용자가 방 기록을 수정합니다. (기록 내용만 수정 가능)" | |
| ) | |
| @PatchMapping("/rooms/{roomId}/records/{recordId}") | |
| @ExceptionDescription(RECORD_UPDATE) | |
| public BaseResponse<RecordUpdateResponse> updateRecord( | |
| @Parameter(hidden = true) @UserId Long userId, | |
| @Parameter(description = "수정할 방 ID", example = "1") @PathVariable("roomId") Long roomId, | |
| @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable("recordId") Long recordId, | |
| @RequestBody @Valid final RecordUpdateRequest request | |
| ) { | |
| return BaseResponse.ok(RecordUpdateResponse.of( | |
| roomPostUpdateUseCase.updateRecord( | |
| request.toCommand(userId, roomId, recordId) | |
| ) | |
| )); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java
around lines 69-85, update the @PathVariable annotations to explicitly name the
path variables (use @PathVariable("roomId") and @PathVariable("recordId")) to
avoid parameter-name binding issues when -parameters is not enabled; keep the
current route naming for now but add a TODO/comment to review and migrate
resource paths to plural form in the next major version for consistency; and
verify the data returned by roomPostUpdateUseCase.updateRecord() and
RecordUpdateResponse.of() — ensure the response includes the intended
identifiers (e.g., include the updated recordId as well as roomId or change
RecordUpdateResponse to reflect whatever the service actually returns) and
adjust the mapping/construction of RecordUpdateResponse accordingly.
| @Operation( | ||
| summary = "투표 수정", | ||
| description = "사용자가 방 투표를 수정합니다. (투표 내용만 수정 가능)" | ||
| ) | ||
| @PatchMapping("/rooms/{roomId}/votes/{voteId}") | ||
| @ExceptionDescription(VOTE_UPDATE) | ||
| public BaseResponse<VoteUpdateResponse> updateVote( | ||
| @Parameter(hidden = true) @UserId Long userId, | ||
| @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId, | ||
| @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable Long voteId, | ||
| @RequestBody @Valid final VoteUpdateRequest request | ||
| ) { | ||
| return BaseResponse.ok(VoteUpdateResponse.of( | ||
| roomPostUpdateUseCase.updateVote(request.toCommand(userId, roomId, voteId)) | ||
| )); | ||
| } | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
@PathVariable 이름 명시 + 응답 값 일치 여부 확인
vote 수정도 동일하게 @PathVariable 이름을 명시해 주세요. 또한 RoomPostUpdateService.updateVote의 반환값과 VoteUpdateResponse.of의 기대값 일치 여부를 점검해 주세요.
적용 diff:
- public BaseResponse<VoteUpdateResponse> updateVote(
- @Parameter(hidden = true) @UserId Long userId,
- @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId,
- @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable Long voteId,
+ public BaseResponse<VoteUpdateResponse> updateVote(
+ @Parameter(hidden = true) @UserId Long userId,
+ @Parameter(description = "수정할 방 ID", example = "1") @PathVariable("roomId") Long roomId,
+ @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable("voteId") Long voteId,
@RequestBody @Valid final VoteUpdateRequest request일관성을 확인하기 위해 @PathVariable 사용 방식과 반환 값 매핑을 점검하겠습니다. 다음 스크립트를 실행해 주세요.
🏁 Script executed:
#!/bin/bash
set -e
# 1. 컨트롤러 파일 경로 및 상단 메서드 확인
CONTROLLER=$(fd RoomPostCommandController.java)
echo "Controller file: $CONTROLLER"
sed -n '1,200p' "$CONTROLLER"
# 2. UseCase의 updateVote 메서드 위치 및 시그니처 확인
USECASE_FILE=$(rg -l "updateVote" -tjava src/main/java/konkuk/thip/roompost/application)
echo "UseCase file: $USECASE_FILE"
sed -n '1,200p' "$USECASE_FILE"
# 3. VoteUpdateResponse.of 메서드 시그니처 확인
RESPONSE_FILE=$(fd VoteUpdateResponse.java)
echo "VoteUpdateResponse class file: $RESPONSE_FILE"
sed -n '1,200p' "$RESPONSE_FILE"Length of output: 10287
@PathVariable 이름 명시와 반환값 매핑을 정확히 검증하기 위해, UseCase 인터페이스·서비스의 updateVote 시그니처와 VoteUpdateResponse.of(...) 메서드를 확인해 주세요. 아래 스크립트를 실행해 주세요.
🏁 Script executed:
#!/bin/bash
set -e
# 1. RoomPostUpdateUseCase에서 updateVote 시그니처 확인
USECASE_FILE=$(rg -l --glob="*RoomPostUpdateUseCase.java" -tjava src/main/java)
echo ">> UseCase 파일: $USECASE_FILE"
rg -nP "updateVote" -C3 "$USECASE_FILE"
# 2. RoomPostUpdateService에서 updateVote 구현부 확인
SERVICE_FILE=$(rg -l --glob="*RoomPostUpdateService.java" -tjava src/main/java)
echo ">> Service 파일: $SERVICE_FILE"
rg -nP "updateVote" -C5 "$SERVICE_FILE"
# 3. VoteUpdateResponse.of 메서드 시그니처 확인
RESPONSE_FILE=$(fd VoteUpdateResponse.java)
echo ">> Response 파일: $RESPONSE_FILE"
rg -nP "static.*of" -C3 "$RESPONSE_FILE"Length of output: 91
일관성 있는 @PathVariable 이름 명시와 서비스 반환값 매핑 검증을 위해 아래 스크립트를 실행해 주세요.
🏁 Script executed:
#!/bin/bash
set -e
# 1. UseCase 인터페이스: updateVote 시그니처 확인
USECASE_FILE=$(fd RoomPostUpdateUseCase.java)
echo "🔍 UseCase 파일: $USECASE_FILE"
sed -n '1,200p' "$USECASE_FILE"
rg -nP 'updateVote' -C3 "$USECASE_FILE"
# 2. Service 구현체: updateVote 구현부 확인
SERVICE_FILE=$(fd RoomPostUpdateService.java)
echo "🔍 Service 파일: $SERVICE_FILE"
sed -n '1,200p' "$SERVICE_FILE"
rg -nP 'updateVote\(' -C5 "$SERVICE_FILE"
# 3. 응답 DTO: VoteUpdateResponse.of 메서드 시그니처 확인
RESPONSE_FILE=$(fd VoteUpdateResponse.java)
echo "🔍 Response DTO 파일: $RESPONSE_FILE"
sed -n '1,200p' "$RESPONSE_FILE"
rg -nP 'static\s+.*of' -C3 "$RESPONSE_FILE"Length of output: 5236
@PathVariable 이름 명시 및 반환값 매핑 검증
– 아래와 같이 @PathVariable에 경로 변수 이름을 명시해주세요.
– RoomPostUpdateService.updateVote가 반환하는 Long roomId와 VoteUpdateResponse.of(Long roomId)의 파라미터가 일치함을 확인했습니다.
수정 제안 diff:
public BaseResponse<VoteUpdateResponse> updateVote(
@Parameter(hidden = true) @UserId Long userId,
- @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId,
- @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable Long voteId,
+ @Parameter(description = "수정할 방 ID", example = "1") @PathVariable("roomId") Long roomId,
+ @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable("voteId") Long voteId,
@RequestBody @Valid final VoteUpdateRequest request
) {
return BaseResponse.ok(VoteUpdateResponse.of(
roomPostUpdateUseCase.updateVote(request.toCommand(userId, roomId, voteId))
));
}📝 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.
| @Operation( | |
| summary = "투표 수정", | |
| description = "사용자가 방 투표를 수정합니다. (투표 내용만 수정 가능)" | |
| ) | |
| @PatchMapping("/rooms/{roomId}/votes/{voteId}") | |
| @ExceptionDescription(VOTE_UPDATE) | |
| public BaseResponse<VoteUpdateResponse> updateVote( | |
| @Parameter(hidden = true) @UserId Long userId, | |
| @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId, | |
| @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable Long voteId, | |
| @RequestBody @Valid final VoteUpdateRequest request | |
| ) { | |
| return BaseResponse.ok(VoteUpdateResponse.of( | |
| roomPostUpdateUseCase.updateVote(request.toCommand(userId, roomId, voteId)) | |
| )); | |
| } | |
| public BaseResponse<VoteUpdateResponse> updateVote( | |
| @Parameter(hidden = true) @UserId Long userId, | |
| @Parameter(description = "수정할 방 ID", example = "1") @PathVariable("roomId") Long roomId, | |
| @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable("voteId") Long voteId, | |
| @RequestBody @Valid final VoteUpdateRequest request | |
| ) { | |
| return BaseResponse.ok(VoteUpdateResponse.of( | |
| roomPostUpdateUseCase.updateVote(request.toCommand(userId, roomId, voteId)) | |
| )); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java
around lines 133 to 149, the @PathVariable annotations are missing explicit path
variable names; update the method signature to use @PathVariable("roomId") Long
roomId and @PathVariable("voteId") Long voteId so Spring binds variables by
name, and ensure the value returned from roomPostUpdateUseCase.updateVote (Long
roomId) is passed directly to VoteUpdateResponse.of(...) as already intended.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java (2)
69-85: @PathVariable 이름 명시 누락 — 바인딩 실패 위험 및 경로 일관성 TODO
- -parameters 미설정 시 이름 소실로 바인딩 실패 소지가 있습니다. 기존 deleteRecord와 동일하게 명시해 주세요.
- 생성(POST /rooms/{roomId}/record) vs 수정(PATCH /rooms/{roomId}/records/{recordId})의 단수/복수 불일치에 대한 vNext TODO 코멘트 권장합니다.
적용 diff:
- @PatchMapping("/rooms/{roomId}/records/{recordId}") + // TODO(vNext): /rooms/{roomId}/record/{recordId} 등으로 리소스 경로 일관성(단/복수) 정리 + @PatchMapping("/rooms/{roomId}/records/{recordId}") @ExceptionDescription(RECORD_UPDATE) public BaseResponse<RecordUpdateResponse> updateRecord( - @Parameter(hidden = true) @UserId Long userId, - @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId, - @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable Long recordId, + @Parameter(hidden = true) @UserId final Long userId, + @Parameter(description = "수정할 방 ID", example = "1") @PathVariable("roomId") final Long roomId, + @Parameter(description = "수정할 기록 ID", example = "1") @PathVariable("recordId") final Long recordId, @RequestBody @Valid final RecordUpdateRequest request ) { return BaseResponse.ok(RecordUpdateResponse.of( roomPostUpdateUseCase.updateRecord(request.toCommand(userId, roomId, recordId)) )); }검증 스크립트:
#!/bin/bash # -parameters 사용 여부 및 해당 컨트롤러의 PathVariable 명시 상태 점검 rg -n "<parameters>|compilerArgs" pom.xml build.gradle* || true rg -nP '@PathVariable\((?!")' src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java
133-149: updateVote도 @PathVariable 이름 명시 필요updateRecord와 동일 리스크입니다. 일관성 있게 명시해 주세요.
적용 diff:
@ExceptionDescription(VOTE_UPDATE) public BaseResponse<VoteUpdateResponse> updateVote( - @Parameter(hidden = true) @UserId Long userId, - @Parameter(description = "수정할 방 ID", example = "1") @PathVariable Long roomId, - @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable Long voteId, + @Parameter(hidden = true) @UserId final Long userId, + @Parameter(description = "수정할 방 ID", example = "1") @PathVariable("roomId") final Long roomId, + @Parameter(description = "수정할 투표 ID", example = "1") @PathVariable("voteId") final Long voteId, @RequestBody @Valid final VoteUpdateRequest request ) { return BaseResponse.ok(VoteUpdateResponse.of( roomPostUpdateUseCase.updateVote(request.toCommand(userId, roomId, voteId)) )); }
🧹 Nitpick comments (1)
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java (1)
169-181: deleteAttendanceCheck: 멱등성(idempotency)·에러 매핑 정책 확인 필요소프트 삭제(@SQLDelete) 환경에서 재요청 시 동작 정의가 중요합니다.
- 이미 INACTIVE인 경우: no-op 성공(200/204)로 처리할지, 404로 숨길지 정책 합의 필요.
- 본인 아님/비참여자는 403/401로 매핑되는지(ErrorCode 일관성 포함) 확인 바랍니다.
권장:
- 서비스 레벨에서 “이미 삭제됨”을 no-op으로 처리하여 DELETE의 멱등성 보장.
- 스웨거 문서에 상태별 응답 예시 추가(200/204/403/404).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java(5 hunks)src/test/java/konkuk/thip/roompost/adapter/in/web/AttendanceCheckDeleteApiTest.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/test/java/konkuk/thip/roompost/adapter/in/web/AttendanceCheckDeleteApiTest.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#195
File: src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java:0-0
Timestamp: 2025-08-13T05:22:32.287Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#278
File: src/main/java/konkuk/thip/common/exception/code/ErrorCode.java:210-211
Timestamp: 2025-08-24T09:40:48.595Z
Learning: seongjunnoh는 HTTP 상태 코드 선택에 대해 기술적 근거와 코드베이스 내 일관성을 중요하게 생각하며, 구체적인 사례 분석을 통한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/service/AttendanceCheckCreateService.java:22-40
Timestamp: 2025-08-14T09:15:31.371Z
Learning: seongjunnoh는 기술적 완벽성보다 실제 비즈니스 시나리오와 사용자 행동 패턴을 우선 고려하며, 발생 가능성이 낮은 엣지 케이스보다는 실용적인 구현을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#278
File: src/main/java/konkuk/thip/TestTokenController.java:0-0
Timestamp: 2025-08-24T09:33:52.982Z
Learning: seongjunnoh는 Spring의 ConditionalOnProperty 동작 원리를 정확히 이해하고 있으며, 보안 이슈에 대해서도 실질적인 위험성을 기준으로 판단하는 실용적 접근을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (4)
src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java (4)
34-34: AttendanceCheckDeleteUseCase DI 추가 적절삭제 유스케이스 주입 위치/가시성 모두 적절합니다. 추가 조치 없습니다.
124-124: VOTE_DELETE 예외 설명으로 정정, 적합스웨거 예외 설명이 도메인 동작과 일치합니다.
151-151: 섹션 주석 갱신 적합도메인 구분이 명확해졌습니다.
159-166: createAttendanceCheck 엔드포인트 설계 적절경로, 바인딩(@PathVariable 이름 명시), 검증(@Valid) 모두 적절합니다.
#️⃣ 연관된 이슈
📝 작업 내용
오늘의 한마디를 삭제하는 api를 개발하였습니다
주요 코드 설명
추가로 오늘의 한마디 entity 의 조회 관련 영속성 코드에서 soft delete 임에도 불구하고 status 조건 없이 entity를 find 하고 있어서, findByIdAndStatus 메서드를 통해 entity를 find 하도록 수정하였습니다
위처럼 soft delete 대상인 entity들의 조회 관련 코드에 status 조건이 빠진 부분이 아직 남아있는것 같아, api feature 개발 이후 전체 코드에 대해서 점검을 해보겠습니다
또한 개발 편의를 위해 access token 을 발급하는 테스트 용 api 를 뚫어놨습니다
📸 스크린샷
💬 리뷰 요구사항
개발 편의를 위해 access token 을 발급하는 테스트 용 api 를 추가했는데, 배포환경에서 해당 api 가 악용되는 것을 막기 위해 access token 발급 controller 에 property 조건을 추가하였습니다
또한 dev yml, prod yml 에 해당 property 의 값을 false로 정의하여 github secret 값을 update 해두었습니다!
local yml 에서는 true 로 설정해서 사용하시면 됩니다! (노션 yml 정리 파일 참고해주시면 됩니다)
사전에 의논이 된 부분은 아니지만, 로컬 서버에서의 테스트 용이성을 위해 access token 을 발급하는 개발 편의용 api 가 있으면 좋겠다 싶어서 추가해봤습니다!
추후에 개발 편의용 api 가 추가될 경우 동일한 방식으로 관리하면 좋을 것 같습니다!
관련해서 의견 자유롭게 주시면 감사하겠습니다!!
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit