Skip to content

Return if the user logged in is a new member#148

Merged
choridev merged 1 commit intostgfrom
feat/#147-check-new-member
Jan 10, 2026
Merged

Return if the user logged in is a new member#148
choridev merged 1 commit intostgfrom
feat/#147-check-new-member

Conversation

@choridev
Copy link
Contributor

✅ PR 타입

  • feat: 새로운 기능 추가
  • fix: 버그 수정
  • docs: 문서 작업
  • style: 코드 포맷팅, 세미콜론 누락, 코드 변경이 없는 경우
  • refactor: 코드 리팩토링
  • cicd: 지속적 통합 및 배포 작업
  • sre: 시스템 관리 및 IT 인프라 자동화 작업

🪾 반영 브랜치

feat/#147-check-new-member -> stg

✨ 변경 사항

로그인 시 기존 회원인지를 나타내는 정보를 프론트엔드로 반환합니다.

💯 테스트 결과

$ ./gradlew clean check test
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2026-01-10T23:12:59.832+09:00  INFO 61998 --- [server] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
Hibernate: drop table if exists location_notification cascade 
Hibernate: drop table if exists member cascade 
Hibernate: drop table if exists mission cascade 
Hibernate: drop table if exists notification cascade 
Hibernate: drop table if exists scenario cascade 
Hibernate: drop table if exists terms cascade 
Hibernate: drop table if exists time_notification cascade 

[Incubating] Problems report is available at: file:///workspace/beforegoing-server/build/reports/problems/problems-report.html

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.13/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 20s
10 actionable tasks: 10 executed

📂 관련 이슈

#147

👀 리뷰어에게

@choridev choridev requested a review from sotogito January 10, 2026 14:21
@choridev choridev self-assigned this Jan 10, 2026
@choridev choridev added documentation Improvements or additions to documentation enhancement New feature or request labels Jan 10, 2026
@gemini-code-assist
Copy link

Summary of Changes

Hello @choridev, 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 확장: 로그인 응답 DTO인 AuthResponse에 사용자가 신규 회원인지 여부를 나타내는 isNewMember 필드를 추가했습니다.
  • 회원 생성 결과 DTO 도입: 회원 객체와 신규 회원 여부 플래그를 함께 캡슐화하는 새로운 레코드 MemberCreationResult를 도입하여 회원 생성 또는 조회 결과를 명확하게 전달합니다.
  • 인증 서비스 로직 업데이트: AuthService의 로그인 및 토큰 발급 관련 메서드들이 MemberCreationResult를 활용하도록 수정되었으며, isNewMember 정보를 최종 AuthResponse에 포함시킵니다.
  • 회원 서비스 로직 변경: MemberServicefindOrCreateMember 메서드가 이제 MemberCreationResult를 반환하여, 기존 회원을 찾았는지 또는 새로운 회원을 생성했는지에 대한 정보를 제공합니다.
  • 테스트 코드 업데이트: 새로운 isNewMember 필드와 MemberCreationResult DTO의 변경 사항을 반영하기 위해 관련 컨트롤러 및 서비스 테스트 코드를 수정했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

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을 잘 보았습니다. 전반적으로 변경 사항은 명확하며, MemberCreationResult 레코드를 사용하여 findOrCreateMember 메서드가 멤버 객체와 신규 회원 여부를 함께 반환하도록 수정한 점이 좋습니다. 이를 통해 코드의 명확성이 향상되었습니다. 관련 서비스 및 DTO, 테스트 코드까지 모두 일관성 있게 수정된 점도 훌륭합니다. 한 가지 제안 사항으로, AuthServiceTest에 신규 사용자가 로그인하는 경우에 대한 테스트 케이스를 추가하면 login 메서드의 동작을 더 완벽하게 검증할 수 있을 것 같습니다.

Comment on lines +117 to 145
@DisplayName("Login successfully")
void Given_ValidLoginRequest_When_Login_Then_ReturnsAuthResponse() {
// given
final AuthRequest authRequest = new AuthRequest("facebook", idToken);
AuthRequest request = new AuthRequest("kakao", "id-token");
Member member = Member.builder().id(1L).build();
MemberCreationResult creationResult = new MemberCreationResult(member, false);
OidcClient oidcClient = mock(OidcClient.class);
OidcPublicKeys publicKeys = mock(OidcPublicKeys.class);

// when & then
final ServerException exception = assertThrows(ServerException.class,
() -> authService.login(authRequest));

assertThat(exception.getErrorResult()).isEqualTo(AuthErrorResult.INVALID_PROVIDER);
}

@Test
@DisplayName("Issues tokens successfully when a registered Kakao member logs in")
void Given_RegisteredKakaoMember_When_Login_Then_IssuesTokensSuccessfully() {
// given
final AuthRequest authRequest = new AuthRequest("kakao", idToken);
final String providerId = "kakao-id-123";
final OidcClient oidcClient = mock(OidcClient.class);
final OidcPublicKeys keys = mock(OidcPublicKeys.class);
final Member member = Member.builder().id(memberId).kakaoId(providerId).build();

doReturn("nonce").when(jwtProvider).extractNonce(idToken);
doNothing().when(nonceService).verifyNonce("nonce", Provider.KAKAO);
doReturn("nonce").when(jwtProvider).extractNonce("id-token");
doReturn(oidcClient).when(oidcClientFactory).getOidcClient(Provider.KAKAO);
doReturn(keys).when(oidcClient).getOidcPublicKeys();
doReturn(providerId).when(oidcProviderFactory).getProviderId(Provider.KAKAO, idToken, keys);
doReturn(member).when(memberService).findOrCreateMember(Provider.KAKAO, providerId);
setupTokenIssuance(accessToken, refreshToken);

// when
final AuthResponse response = authService.login(authRequest);

// then
verify(nonceService).verifyNonce("nonce", Provider.KAKAO);
verify(refreshTokenService).saveRefreshToken(memberId, refreshToken);
assertThat(response.tokenType()).isEqualTo("Bearer");
assertThat(response.accessToken()).isEqualTo(accessToken);
assertThat(response.refreshToken()).isEqualTo(refreshToken);
assertThat(response.accessTokenExpiresIn()).isEqualTo(accessTokenExpireTime);
assertThat(response.refreshTokenExpiresIn()).isEqualTo(refreshTokenExpireTime);
}

@Test
@DisplayName("Creates a new Kakao member and issues tokens on the first login")
void Given_NewKakaoMember_When_Login_Then_CreatesMemberAndIssuesTokens() {
// given
final AuthRequest authRequest = new AuthRequest("kakao", idToken);
final String providerId = "kakao-id-123";
final OidcClient oidcClient = mock(OidcClient.class);
final OidcPublicKeys keys = mock(OidcPublicKeys.class);
final Member newMember = Member.builder().id(memberId).kakaoId(providerId).build();
doReturn(publicKeys).when(oidcClient).getOidcPublicKeys();
doReturn("provider-id").when(oidcProviderFactory).getProviderId(Provider.KAKAO, "id-token", publicKeys);
doReturn(creationResult).when(memberService).findOrCreateMember(Provider.KAKAO, "provider-id");

doReturn("nonce").when(jwtProvider).extractNonce(idToken);
doReturn(oidcClient).when(oidcClientFactory).getOidcClient(Provider.KAKAO);
doReturn(keys).when(oidcClient).getOidcPublicKeys();
doReturn(providerId).when(oidcProviderFactory).getProviderId(Provider.KAKAO, idToken, keys);
doReturn(newMember).when(memberService).findOrCreateMember(Provider.KAKAO, providerId);
setupTokenIssuance(accessToken, refreshToken);
doReturn("access-token").when(jwtProvider).generateAccessToken(1L);
doReturn("refresh-token").when(refreshTokenService).generateRefreshToken();
doReturn("Bearer").when(jwtProperties).type();
doReturn(3600).when(jwtProperties).accessTokenExpireTime();
doReturn(1209600).when(jwtProperties).refreshTokenExpireTime();

// when
final AuthResponse response = authService.login(authRequest);
AuthResponse response = authService.login(request);

// then
assertThat(response.accessToken()).isEqualTo("access-token");
assertThat(response.isNewMember()).isFalse();
verify(nonceService).verifyNonce("nonce", Provider.KAKAO);
verify(refreshTokenService).saveRefreshToken(memberId, refreshToken);
assertThat(response.accessToken()).isEqualTo(accessToken);
assertThat(response.refreshToken()).isEqualTo(refreshToken);
}

Choose a reason for hiding this comment

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

medium

좋은 테스트 리팩토링입니다! login 메서드에 대한 테스트가 현재 기존 회원(isNewMemberfalse인 경우)에 대해서만 검증하고 있습니다. 신규 회원이 로그인하는 경우(isNewMembertrue인 경우)에 대한 테스트 케이스를 추가하면 login 메서드의 모든 경로를 커버하여 안정성을 더욱 높일 수 있을 것 같습니다.

아래와 같은 테스트 케이스를 추가하는 것을 고려해 보세요:

@Test
@DisplayName("신규 유저 로그인 성공")
void Given_ValidLoginRequestForNewUser_When_Login_Then_ReturnsAuthResponseWithIsNewMemberTrue() {
    // given
    AuthRequest request = new AuthRequest("kakao", "id-token");
    Member member = Member.builder().id(1L).build();
    MemberCreationResult creationResult = new MemberCreationResult(member, true); // 신규 회원
    OidcClient oidcClient = mock(OidcClient.class);
    OidcPublicKeys publicKeys = mock(OidcPublicKeys.class);

    doReturn("nonce").when(jwtProvider).extractNonce("id-token");
    doReturn(oidcClient).when(oidcClientFactory).getOidcClient(Provider.KAKAO);
    doReturn(publicKeys).when(oidcClient).getOidcPublicKeys();
    doReturn("provider-id").when(oidcProviderFactory).getProviderId(Provider.KAKAO, "id-token", publicKeys);
    doReturn(creationResult).when(memberService).findOrCreateMember(Provider.KAKAO, "provider-id");

    doReturn("access-token").when(jwtProvider).generateAccessToken(1L);
    doReturn("refresh-token").when(refreshTokenService).generateRefreshToken();
    doReturn("Bearer").when(jwtProperties).type();
    doReturn(3600).when(jwtProperties).accessTokenExpireTime();
    doReturn(1209600).when(jwtProperties).refreshTokenExpireTime();

    // when
    AuthResponse response = authService.login(request);

    // then
    assertThat(response.accessToken()).isEqualTo("access-token");
    assertThat(response.isNewMember()).isTrue(); // 신규 회원인지 검증
    verify(nonceService).verifyNonce("nonce", Provider.KAKAO);
}

@sonarqubecloud
Copy link

@choridev choridev removed the request for review from sotogito January 10, 2026 14:26
@choridev choridev merged commit 24dd39a into stg Jan 10, 2026
2 checks passed
@choridev choridev deleted the feat/#147-check-new-member branch January 10, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments