Skip to content

Conversation

@jhan0121
Copy link
Collaborator

🚀 작업 내용

  • GET /api/v1/members 조회 기준을 email에서 X-Device-Id로 전환
  • DELETE /api/v1/device에 요청자-디바이스 소유권 검증 추가
  • 본인 소유가 아닌 디바이스 삭제 요청 시 404 반환 정책 적용
  • MemberFindInput, DeviceDeleteInput 정리 (email 의존 제거)
  • 멤버/디바이스 관련 테스트 및 문서 업데이트

📸 이슈 번호

✍ 궁금한 점

  • X

- 멤버 디바이스 조회 기능 수정
- RequestParam email 제거
- 인증된 디바이스 식별자로 멤버 조회하도록 변경

- 디바이스 삭제 기능 수정
- 인증된 디바이스 식별자로 요청자 식별
- 삭제 대상 디바이스 소유권 검증 로직 추가

- Service Input DTO 이메일 필드 제거
- MemberFindInput, DeviceDeleteInput
- MemberServiceTest 예외 메시지 검증 구체화
- MemberControllerTest 불필요한 테스트 및 파라미터 제거
@jhan0121 jhan0121 added this to the 1.1.* milestone Feb 13, 2026
@jhan0121 jhan0121 requested a review from Copilot February 13, 2026 06:53
@jhan0121 jhan0121 self-assigned this Feb 13, 2026
@jhan0121 jhan0121 added the 🛠 fix 버그 수정 label Feb 13, 2026
@github-actions
Copy link

github-actions bot commented Feb 13, 2026

🧪 테스트 커버리지 리포트

Overall Project 96.48% -1% 🍏
Files changed 83.04% 🍏

File Coverage
MemberController.java 100% 🍏
DeviceController.java 100% 🍏
MemberService.java 88.85% -6.62% 🍏

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

GET /api/v1/membersDELETE /api/v1/device에서 email 기반 권한 판단을 제거하고, X-Device-Id 기반으로 본인 조회/본인 소유 디바이스만 삭제되도록 동작을 수정하는 PR입니다. Issue #67에서 지적된 “타 계정 조회/타인 디바이스 삭제 가능” 문제를 해소하는 방향의 변경입니다.

Changes:

  • GET /api/v1/members 조회 기준을 emailX-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("인증되지 않은 디바이스입니다"));
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memberRepository.findByIdentifier() 실패 시 findAllMemberDevices()만 "인증되지 않은 디바이스입니다"를 사용하고, 같은 패턴을 쓰는 다른 메서드들(findNotificationTime/updateNotificationTime/deleteDevice)은 "유효하지 않은 디바이스입니다"를 사용하고 있습니다. @AuthDevice 리졸버의 메시지/다른 서비스들과도 달라서 클라이언트 입장에서 에러 메시지가 일관되지 않습니다. 이 메서드도 동일한 메시지로 통일하거나(권장), 여기서 구분하려면 실제로 활성 여부를 판단하는 로직을 함께 두는 쪽이 맞습니다.

Suggested change
.orElseThrow(() -> new UnauthorizedException("인증되지 않은 디바이스입니다"));
.orElseThrow(() -> new UnauthorizedException("유효하지 않은 디바이스입니다"));

Copilot uses AI. Check for mistakes.
Comment on lines 88 to +100
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);
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleteDevice()에서 deviceRepository.findByIdentifier(...)는 member를 fetch join하지 않기 때문에, 이후 targetDevice.getMember().hasEmail(...) 호출 시 추가 쿼리가 발생합니다(지금은 1건이라도 항상 추가 round-trip). 소유권 검증/삭제를 한 번의 쿼리로 처리하도록 (1) findByIdentifier에 JOIN FETCH를 추가한 메서드를 만들거나, (2) identifier+member 조건으로 조회/삭제하는 전용 repository 메서드(예: findByIdentifierAndMemberEmail / deleteByIdentifierAndMemberEmail)를 고려해 주세요.

Copilot uses AI. Check for mistakes.
- 이메일 파라미터 누락 시 200 응답 반환에 맞춰 테스트 설명 수정
@jhan0121 jhan0121 merged commit ec401c5 into be/dev Feb 13, 2026
1 check passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in recycle-study Feb 13, 2026
@jhan0121 jhan0121 deleted the refactor/device-auth branch February 13, 2026 07:07
jhan0121 added a commit that referenced this pull request Feb 13, 2026
* refactor: 디바이스 인증 기능 마이그레이션

- 멤버 디바이스 조회 기능 수정
- RequestParam email 제거
- 인증된 디바이스 식별자로 멤버 조회하도록 변경

- 디바이스 삭제 기능 수정
- 인증된 디바이스 식별자로 요청자 식별
- 삭제 대상 디바이스 소유권 검증 로직 추가

- Service Input DTO 이메일 필드 제거
- MemberFindInput, DeviceDeleteInput

* refactor: 리뷰 저장 시 커스텀 주기 소유권 검증 로직 수정

- MemberServiceTest 예외 메시지 검증 구체화
- MemberControllerTest 불필요한 테스트 및 파라미터 제거

* test: 멤버 디바이스 조회 테스트 설명 수정

- 이메일 파라미터 누락 시 200 응답 반환에 맞춰 테스트 설명 수정
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🛠 fix 버그 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant