Skip to content

feat: 멤버 관련 기능 추가#8

Merged
Dockerel merged 7 commits intodevfrom
feat/member
Nov 3, 2025
Merged

feat: 멤버 관련 기능 추가#8
Dockerel merged 7 commits intodevfrom
feat/member

Conversation

@Dockerel
Copy link
Owner

@Dockerel Dockerel commented Nov 3, 2025

#️⃣ 연관된 이슈

x

📚 배경

x

📝 작업 내용

  • 멤버 관련 기능 추가
    • 마이페이지
    • 로그아웃
    • 회원탈퇴
    • 비밀번호 수정

💬 리뷰 요구사항

x

✏ Git Close

x

@Dockerel Dockerel self-assigned this Nov 3, 2025
@Dockerel Dockerel added the enhancement New feature or request label Nov 3, 2025
@gemini-code-assist
Copy link

Summary of Changes

Hello @Dockerel, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 사용자 계정 관리를 위한 중요한 기능들을 도입합니다. 사용자는 이제 마이페이지를 통해 자신의 정보를 확인하고, 안전하게 로그아웃하며, 필요에 따라 계정을 탈퇴하거나 비밀번호를 변경할 수 있습니다. 이는 전반적인 사용자 경험을 향상시키고 계정 보안을 강화하는 데 기여합니다.

Highlights

  • 회원 기능 추가: 마이페이지 조회, 로그아웃, 회원 탈퇴, 비밀번호 변경 등 회원 관련 핵심 기능이 추가되었습니다.
  • 새로운 DTO 및 요청/응답 객체: 비밀번호 변경, 로그아웃, 회원 탈퇴를 위한 ChangePasswordResponse, ChangePasswordServiceRequest, LogoutResponse, WithdrawResponse, ChangePasswordRequest 등의 DTO가 새로 추가되었습니다.
  • 회원 리포지토리 및 서비스 로직 확장: MemberRepositorydeleteMemberByEmailupdatePasswordByEmail 메서드가 추가되었고, MemberService에 해당 기능들의 비즈니스 로직이 구현되었습니다. 특히 비밀번호 변경 시 기존 비밀번호 검증 로직이 포함되었습니다.
  • 회원 엔티티 및 JPA 리포지토리 업데이트: Member 엔티티에 비밀번호 변경을 위한 changePassword 메서드가 추가되었고, MemberJpaRepository에 이메일을 통한 회원 삭제를 위한 deleteByEmail 메서드가 추가되었습니다.
  • 컨트롤러 엔드포인트 추가 및 유효성 검사: MemberController에 로그아웃(POST /logout), 회원 탈퇴(DELETE /), 비밀번호 변경(PATCH /)을 위한 새로운 API 엔드포인트가 추가되었으며, 비밀번호 변경 요청에 대한 유효성 검사 로직이 적용되었습니다.
  • 테스트 코드 강화: 새로 추가된 모든 기능에 대한 단위 및 통합 테스트 코드가 작성 및 업데이트되어 코드의 안정성과 신뢰성을 높였습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

이번 PR은 마이페이지, 로그아웃, 회원 탈퇴, 비밀번호 변경 등 멤버 관련 주요 기능들을 추가하는 변경 사항을 담고 있습니다. 전반적으로 기능 구현이 잘 이루어졌고, 특히 다양한 시나리오에 대한 테스트 코드를 충실하게 작성해주신 점이 인상적입니다. 몇 가지 개선점을 제안드립니다. 로그아웃 시 응답 메시지 오류, 회원 탈퇴 시 연관 데이터 처리 문제, 테스트 코드의 검증 로직 강화 등에 대한 피드백을 포함했습니다. 이를 통해 코드의 안정성과 유지보수성을 더욱 높일 수 있을 것입니다.


public LogoutResponse logout(String refreshToken) {
memberRepository.deleteRefreshToken(refreshToken);
return LogoutResponse.of("회원가입이 완료되었습니다.");

Choose a reason for hiding this comment

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

high

로그아웃 성공 메시지가 "회원가입이 완료되었습니다."로 잘못 설정되어 있습니다. "로그아웃이 완료되었습니다."와 같이 올바른 메시지로 수정해야 합니다.

Suggested change
return LogoutResponse.of("회원가입이 완료되었습니다.");
return LogoutResponse.of("로그아웃이 완료되었습니다.");

Comment on lines +103 to +109
public WithdrawResponse withdraw(String email) {
memberRepository.deleteMemberByEmail(email);

// TODO : 히스토리 및 채팅 삭제

return WithdrawResponse.of("회원탈퇴가 완료되었습니다.");
}

Choose a reason for hiding this comment

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

high

회원 탈퇴 시 member만 삭제하고 관련 데이터(히스토리, 채팅)는 삭제하지 않고 있습니다. TODO로 명시되어 있지만, 이로 인해 고아 데이터(orphaned data)가 발생하여 데이터 무결성을 해칠 수 있습니다. 회원과 연관된 모든 데이터를 함께 삭제하거나, soft delete를 적용하는 것을 고려해야 합니다. 예를 들어, Member 엔티티에 deleted 플래그를 추가하고 회원 탈퇴 시 이 플래그를 업데이트하는 방식이 있습니다.

Comment on lines +120 to +141
@DisplayName("비밀번호를 변경한다.")
@Test
void updatePasswordByEmail() {
// given
String email = "email";
String oldPassword = "password";
String newPassword = "newPassword";

Member member = Member.builder()
.email(email)
.password(oldPassword)
.build();

memberJpaRepository.save(member);

// when
memberRepositoryImpl.updatePasswordByEmail(email, newPassword);

// then
MemberDto memberDto = memberRepositoryImpl.findByEmail(email);
assertThat(memberDto.getPassword()).isEqualTo(newPassword);
}

Choose a reason for hiding this comment

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

high

updatePasswordByEmail 테스트가 잘못 작성되었습니다.

  1. updatePasswordByEmail 메서드는 파라미터 이름(newEncryptedPassword)에서 알 수 있듯이 암호화된 비밀번호를 받도록 의도되었습니다. 하지만 테스트에서는 암호화되지 않은 newPassword를 전달하고 있습니다.
  2. then 절에서 memberDto.getPassword()가 암호화되지 않은 newPassword와 같다고 단언하고 있습니다. 이는 암호화되지 않은 비밀번호가 DB에 저장되는 것을 테스트하는 셈이며, 이는 보안상 매우 위험합니다.

테스트를 수정하여 PasswordEncryptor를 사용해 비밀번호를 암호화하여 전달하고, verifyPassword를 통해 올바르게 변경되었는지 검증해야 합니다.

// 예시
@Autowired
private PasswordEncryptor passwordEncryptor;

// ...

// given
// ...
memberJpaRepository.save(member);
String encryptedNewPassword = passwordEncryptor.encryptPassword(newPassword);

// when
memberRepositoryImpl.updatePasswordByEmail(email, encryptedNewPassword);

// then
MemberDto memberDto = memberRepositoryImpl.findByEmail(email);
assertThat(passwordEncryptor.verifyPassword(newPassword, memberDto.getPassword())).isTrue();

@@ -0,0 +1,18 @@
package knu_chatbot.application.dto.response;

Choose a reason for hiding this comment

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

medium

이 클래스는 서비스 계층에 대한 요청 데이터를 담는 DTO이므로, knu_chatbot.application.dto.request 패키지로 이동하는 것이 패키지 구조의 일관성 측면에서 더 적절해 보입니다. 현재는 response 패키지에 위치해 있어 혼란을 줄 수 있습니다.


// 비밀번호 검증
if (!passwordEncryptor.verifyPassword(request.getOldPassword(), memberDto.getPassword())) {
throw new KnuChatbotException(ErrorType.USER_LOGIN_ERROR);

Choose a reason for hiding this comment

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

medium

비밀번호 변경 시 현재 비밀번호가 일치하지 않을 때 USER_LOGIN_ERROR를 사용하고 있습니다. 이 오류 유형은 로그인 실패를 위해 만들어진 것으로, "아이디 또는 비밀번호가 맞지 않습니다."라는 메시지를 반환하여 사용자에게 혼란을 줄 수 있습니다. "현재 비밀번호가 일치하지 않습니다."와 같은 명확한 메시지를 제공하는 새로운 오류 유형(예: CURRENT_PASSWORD_MISMATCH_ERROR)을 정의하여 사용하는 것이 좋습니다.


member.changePassword(newEncryptedPassword);

memberJpaRepository.save(member);

Choose a reason for hiding this comment

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

medium

updatePasswordByEmail 메서드는 서비스 계층의 @Transactional 컨텍스트 내에서 호출됩니다. 따라서 getOrThrowMemberByEmail을 통해 조회된 Member 엔티티는 영속성 컨텍스트에 의해 관리되는 상태(managed state)입니다. member.changePassword()를 통해 엔티티의 상태를 변경하면, 트랜잭션이 커밋될 때 변경 감지(dirty checking)에 의해 자동으로 UPDATE 쿼리가 실행됩니다. 따라서 이 save 호출은 불필요하며, 제거하여 코드를 더 간결하게 만들 수 있습니다.

ChangePasswordResponse response = memberService.changePassword(authUser, request);

// then
assertThat(response).isNotNull();

Choose a reason for hiding this comment

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

medium

테스트의 then 절에서 responsenull이 아닌지만 검증하고 있습니다. 이는 비밀번호가 실제로 변경되었는지 보장하지 못합니다. 테스트의 신뢰도를 높이기 위해, 변경된 비밀번호로 로그인이 성공하는지, 그리고 이전 비밀번호로는 로그인이 실패하는지 등을 검증하는 로직을 추가하는 것이 좋습니다.

// then
assertThat(response).isNotNull();

// 새 비밀번호로 로그인 시도
LoginServiceRequest loginRequest = LoginServiceRequest.builder()
        .email(email)
        .password(newPassword)
        .build();
// 예외가 발생하지 않아야 함
assertThatCode(() -> memberService.login(loginRequest)).doesNotThrowAnyException();

// 이전 비밀번호로 로그인 시도
LoginServiceRequest oldLoginRequest = LoginServiceRequest.builder()
        .email(email)
        .password(oldPassword)
        .build();
// 예외가 발생해야 함
assertThatThrownBy(() -> memberService.login(oldLoginRequest))
        .isInstanceOf(KnuChatbotException.class);

@Dockerel Dockerel merged commit 6051b71 into dev Nov 3, 2025
1 check passed
@Dockerel Dockerel deleted the feat/member branch November 3, 2025 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant