Conversation
- 멤버 디바이스 조회 기능 수정 - RequestParam email 제거 - 인증된 디바이스 식별자로 멤버 조회하도록 변경 - 디바이스 삭제 기능 수정 - 인증된 디바이스 식별자로 요청자 식별 - 삭제 대상 디바이스 소유권 검증 로직 추가 - Service Input DTO 이메일 필드 제거 - MemberFindInput, DeviceDeleteInput
- MemberServiceTest 예외 메시지 검증 구체화 - MemberControllerTest 불필요한 테스트 및 파라미터 제거
🧪 테스트 커버리지 리포트
|
There was a problem hiding this comment.
Pull request overview
GET /api/v1/members 및 DELETE /api/v1/device에서 email 기반 권한 판단을 제거하고, X-Device-Id 기반으로 본인 조회/본인 소유 디바이스만 삭제되도록 동작을 수정하는 PR입니다. Issue #67에서 지적된 “타 계정 조회/타인 디바이스 삭제 가능” 문제를 해소하는 방향의 변경입니다.
Changes:
GET /api/v1/members조회 기준을email→X-Device-Id(@AuthDevice)로 전환하고 입력 모델을 정리DELETE /api/v1/device에 요청자 디바이스 기반 소유권 검증 추가 및 비소유 시404정책 적용- 관련 테스트/REST Docs 및 에러 메시지 일부 수정
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/com/recyclestudy/member/controller/MemberController.java | 멤버 조회에서 email 쿼리 파라미터 제거, 헤더 기반 조회로 전환 |
| src/main/java/com/recyclestudy/member/controller/DeviceController.java | 디바이스 삭제 입력 생성 시 email 제거(legacy body 값 무시) |
| src/main/java/com/recyclestudy/member/service/MemberService.java | 멤버 조회를 device identifier 기반으로 변경, 디바이스 삭제 시 소유권 검증 추가 |
| src/main/java/com/recyclestudy/member/service/input/MemberFindInput.java | email 의존 제거(식별자만 받도록 변경) |
| src/main/java/com/recyclestudy/member/service/input/DeviceDeleteInput.java | email 의존 제거(요청 디바이스/대상 디바이스 식별자만 사용) |
| src/test/java/com/recyclestudy/member/service/MemberServiceTest.java | 서비스 로직 변경에 맞춘 테스트 수정 및 소유권/Unauthorized 케이스 추가 |
| src/test/java/com/recyclestudy/member/controller/MemberControllerTest.java | 멤버 조회 API 문서/테스트를 헤더 기반으로 일부 전환(일부 레거시 문서 잔존) |
| src/test/java/com/recyclestudy/member/controller/DeviceControllerTest.java | 디바이스 인증/삭제 문서 및 legacy email optional 처리, 에러 메시지 갱신 |
Comments suppressed due to low confidence (2)
src/test/java/com/recyclestudy/member/controller/MemberControllerTest.java:197
- GET /api/v1/members는 이제 @AuthDevice(X-Device-Id) 기반으로만 조회하도록 변경되어 email 쿼리 파라미터로 멤버를 찾지 않습니다(MemberController에서도 @RequestParam 제거). 그런데 이 테스트는 email 파라미터를 문서화/요청에 포함하고, 404(존재하지 않는 멤버) 시나리오를 유지하고 있어 변경된 API 동작과 문서가 어긋납니다. 404 시나리오를 실제 발생 가능한 케이스(예: 401/Unauthorized - 유효하지 않거나 비활성 디바이스)로 바꾸거나, email 파라미터 문서화를 일관되게 optional로 정리해 주세요.
@DisplayName("존재하지 않는 멤버 조회 시 404 응답을 반환한다")
void findAllMemberDevices_NotFoundMember() {
// given
final String email = "notfound@test.com";
final String headerIdentifier = "device-identifier";
given(memberService.findAllMemberDevices(any()))
.willThrow(new NotFoundException("존재하지 않는 멤버입니다"));
// when
// then
given(this.spec)
.filter(document(DEFAULT_REST_DOC_PATH,
builder()
.tag("Member")
.summary("멤버 디바이스 조회")
.description("존재하지 않는 멤버 조회 시 404 응답을 반환한다")
.requestHeaders(
headerWithName("X-Device-Id").description("디바이스 식별자")
)
.queryParameters(
parameterWithName("email").description("이메일 (다음 버전에서 제거 예정)")
)
.responseFields(
fieldWithPath("message").type(JsonFieldType.STRING).description("에러 메시지")
),
queryParameters(
parameterWithName("email").description("이메일 (다음 버전에서 제거 예정)")
)
))
.header("X-Device-Id", headerIdentifier)
.param("email", email)
.when()
.get("/api/v1/members")
.then()
.statusCode(HttpStatus.NOT_FOUND.value())
.body("message", equalTo("존재하지 않는 멤버입니다"));
}
src/test/java/com/recyclestudy/member/controller/MemberControllerTest.java:329
- "존재하지 않는 디바이스 식별자"로 조회 시나리오가 404로 문서화/검증되어 있는데, 실제로는 @AuthDevice 리졸버에서 디바이스 미존재/비활성은 401(UnauthorizedException)로 처리됩니다. 또한 queryParameters 블록 내 email 설명이 한 곳은 "이메일"로, 다른 곳은 "다음 버전에서 제거 예정"으로 섞여 있어 문서가 일관되지 않습니다. 이 케이스를 401로 정리하고, legacy email 파라미터를 유지한다면 optional로 동일하게 표기해 주세요.
@Test
@DisplayName("존재하지 않는 디바이스 식별자로 조회 시 404 응답을 반환한다")
void findAllMemberDevices_NotFoundDevice() {
// given
final String email = "test@test.com";
final String headerIdentifier = "not-found-id";
given(memberService.findAllMemberDevices(any()))
.willThrow(new NotFoundException("존재하지 않는 디바이스 아이디입니다"));
// when
// then
given(this.spec)
.filter(document(DEFAULT_REST_DOC_PATH,
builder()
.tag("Member")
.summary("멤버 디바이스 조회")
.description("존재하지 않는 디바이스 식별자로 조회 시 404 응답을 반환한다")
.requestHeaders(
headerWithName("X-Device-Id").description("디바이스 식별자")
)
.queryParameters(
parameterWithName("email").description("이메일")
)
.responseFields(
fieldWithPath("message").type(JsonFieldType.STRING).description("에러 메시지")
),
queryParameters(
parameterWithName("email").description("이메일 (다음 버전에서 제거 예정)")
)
))
.header("X-Device-Id", headerIdentifier)
.param("email", email)
.when()
.get("/api/v1/members")
.then()
.statusCode(HttpStatus.NOT_FOUND.value())
.body("message", equalTo("존재하지 않는 디바이스 아이디입니다"));
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| final List<Device> devices = deviceRepository.findAllByMemberEmail(input.email()); | ||
| return MemberFindOutput.of(input.email(), devices); | ||
| final Member member = memberRepository.findByIdentifier(input.deviceIdentifier()) | ||
| .orElseThrow(() -> new UnauthorizedException("인증되지 않은 디바이스입니다")); |
There was a problem hiding this comment.
memberRepository.findByIdentifier() 실패 시 findAllMemberDevices()만 "인증되지 않은 디바이스입니다"를 사용하고, 같은 패턴을 쓰는 다른 메서드들(findNotificationTime/updateNotificationTime/deleteDevice)은 "유효하지 않은 디바이스입니다"를 사용하고 있습니다. @AuthDevice 리졸버의 메시지/다른 서비스들과도 달라서 클라이언트 입장에서 에러 메시지가 일관되지 않습니다. 이 메서드도 동일한 메시지로 통일하거나(권장), 여기서 구분하려면 실제로 활성 여부를 판단하는 로직을 함께 두는 쪽이 맞습니다.
| .orElseThrow(() -> new UnauthorizedException("인증되지 않은 디바이스입니다")); | |
| .orElseThrow(() -> new UnauthorizedException("유효하지 않은 디바이스입니다")); |
| public void deleteDevice(final DeviceDeleteInput input) { | ||
| deviceRepository.deleteByIdentifier(input.targetDeviceIdentifier()); | ||
| final Member requestMember = memberRepository.findByIdentifier(input.deviceIdentifier()) | ||
| .orElseThrow(() -> new UnauthorizedException("유효하지 않은 디바이스입니다")); | ||
|
|
||
| final Device targetDevice = deviceRepository.findByIdentifier(input.targetDeviceIdentifier()) | ||
| .orElseThrow(() -> new NotFoundException("존재하지 않는 디바이스입니다: %s" | ||
| .formatted(input.targetDeviceIdentifier().getValue()))); | ||
|
|
||
| if (!targetDevice.getMember().hasEmail(requestMember.getEmail())) { | ||
| throw new NotFoundException("존재하지 않는 디바이스입니다: %s".formatted(input.targetDeviceIdentifier().getValue())); | ||
| } | ||
|
|
||
| deviceRepository.delete(targetDevice); |
There was a problem hiding this comment.
deleteDevice()에서 deviceRepository.findByIdentifier(...)는 member를 fetch join하지 않기 때문에, 이후 targetDevice.getMember().hasEmail(...) 호출 시 추가 쿼리가 발생합니다(지금은 1건이라도 항상 추가 round-trip). 소유권 검증/삭제를 한 번의 쿼리로 처리하도록 (1) findByIdentifier에 JOIN FETCH를 추가한 메서드를 만들거나, (2) identifier+member 조건으로 조회/삭제하는 전용 repository 메서드(예: findByIdentifierAndMemberEmail / deleteByIdentifierAndMemberEmail)를 고려해 주세요.
src/test/java/com/recyclestudy/member/controller/MemberControllerTest.java
Show resolved
Hide resolved
- 이메일 파라미터 누락 시 200 응답 반환에 맞춰 테스트 설명 수정
* refactor: 디바이스 인증 기능 마이그레이션 - 멤버 디바이스 조회 기능 수정 - RequestParam email 제거 - 인증된 디바이스 식별자로 멤버 조회하도록 변경 - 디바이스 삭제 기능 수정 - 인증된 디바이스 식별자로 요청자 식별 - 삭제 대상 디바이스 소유권 검증 로직 추가 - Service Input DTO 이메일 필드 제거 - MemberFindInput, DeviceDeleteInput * refactor: 리뷰 저장 시 커스텀 주기 소유권 검증 로직 수정 - MemberServiceTest 예외 메시지 검증 구체화 - MemberControllerTest 불필요한 테스트 및 파라미터 제거 * test: 멤버 디바이스 조회 테스트 설명 수정 - 이메일 파라미터 누락 시 200 응답 반환에 맞춰 테스트 설명 수정
🚀 작업 내용
GET /api/v1/members조회 기준을email에서X-Device-Id로 전환DELETE /api/v1/device에 요청자-디바이스 소유권 검증 추가404반환 정책 적용MemberFindInput,DeviceDeleteInput정리 (email의존 제거)📸 이슈 번호
✍ 궁금한 점