Skip to content

feat(oauth-google): 구글 oAuth 클라이언트 추가#172

Merged
huhdy32 merged 7 commits intodevelopfrom
feat/oauth-google
Sep 15, 2025
Merged

feat(oauth-google): 구글 oAuth 클라이언트 추가#172
huhdy32 merged 7 commits intodevelopfrom
feat/oauth-google

Conversation

@huhdy32
Copy link
Collaborator

@huhdy32 huhdy32 commented Sep 15, 2025

Summary by CodeRabbit

  • New Features
    • Google OAuth login support: sign in with Google, automatic token exchange and basic profile retrieval; registration enabled when Google OAuth settings are provided.
  • Compatibility
    • OAuth user IDs switched from numeric to string representation — may affect integrations and repository lookups.
  • Tests
    • Updated tests to reflect string-based OAuth IDs.
  • Security
    • Sensitive credentials omitted from logged/printed representations.

@huhdy32 huhdy32 self-assigned this Sep 15, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 15, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds Google OAuth integration and migrates OAuth user IDs from Long to String across domain: new GoogleConfiguration, GoogleInfoResponse, GoogleOAuthClient and bean registration; enum entry for GOOGLE; and corresponding model, entity, repository, and test updates to use String IDs.

Changes

Cohort / File(s) Summary of Changes
Google configuration
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleConfiguration.java
New Spring @Configuration bound to oauth.google with fields clientId, clientSecret, redirectUri; conditional on oauth.google.clientId; Lombok accessors and toString() (excludes secret).
Google user-info model
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleInfoResponse.java
New public record GoogleInfoResponse(String id, String given_name) implementing MemberInfoResponse and mapping to domain MemberInfo via toInfo().
Google OAuth client
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java
New GoogleOAuthClient implementing OAuthClientHandler; exchanges code for token at https://oauth2.googleapis.com/token, fetches userinfo from https://www.googleapis.com/oauth2/v2/userinfo, maps responses to domain MemberInfoResponse; supports(...) returns true for GOOGLE.
Bean wiring
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/OAuthConfiguration.java
Added googleOAuthClient(GoogleConfiguration) bean factory method guarded by @ConditionalOnBean(GoogleConfiguration.class) to register GoogleOAuthClient.
OAuth provider enum
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/OAuthProvider.java
Added enum constant GOOGLE (now: KAKAO, GOOGLE, NAVER).
Member identity type change
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/MemberInfo.java
MemberInfo record component memberId changed from LongString; constructor/signature updated accordingly.
Client response updates (Kakao/Naver)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/KakaoMemberInfoResponse.java, .../NaverMemberInfoResponse.java
Adapted to new MemberInfo(String, String) by converting/typing provider IDs as String (e.g., String.valueOf(id) or id now String). NaverResponse.id changed from LongString.
Domain entity changes
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/OAuthInfo.java, .../Member.java
OAuthInfo.oAuthUserId type changed from LongString; Member.fromOAuth(...) updated to accept String oAuthId.
Repository API updates
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/repository/MemberRepository.java
Repository methods findByOAuthIdAndProvider(...) and findAllByOAuthIdAndProviderNoLock(...) changed parameter type @Param("oAuthId") from LongString.
Tests updated
domain/mathrank-auth-domain/src/test/java/kr/co/mathrank/domain/auth/repository/MemberRepositoryTest.java, .../service/OAuthLoginServiceTest.java
Test fixtures updated to use String OAuth IDs (e.g., "12", "12345") instead of Long literals.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant Client as GoogleOAuthClient
  participant Token as oauth2.googleapis.com/token
  participant UserInfo as www.googleapis.com/oauth2/v2/userinfo

  Caller->>Client: getMemberInfo(OAuthLoginCommand)
  activate Client
  Note right of Client: POST form: client_id, client_secret,\ncode, grant_type, redirect_uri
  Client->>Token: POST /token (application/x-www-form-urlencoded)
  Token-->>Client: AccessTokenResponse { access_token }
  Note right of Client: Authorization: Bearer <token>
  Client->>UserInfo: POST /userinfo (Bearer auth)
  UserInfo-->>Client: GoogleInfoResponse { id, given_name }
  Client-->>Caller: MemberInfoResponse (mapped with String id)
  deactivate Client
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

I nibble config under moonlit sky,
Hop to token, fetch a name with a sigh.
Bearer carrot snug and sweet,
IDs now strings beneath my feet.
A happy rabbit logs the feat. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat(oauth-google): 구글 oAuth 클라이언트 추가" concisely and accurately describes the primary change in the changeset (adding a Google OAuth client and related configuration), follows conventional commit style, and is specific enough for a reviewer scanning history to understand the PR's main intent.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/oauth-google

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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.

Summary of Changes

Hello @huhdy32, 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은 애플리케이션에 구글 OAuth 클라이언트 기능을 통합하여, 사용자가 구글 계정을 통해 인증하고 로그인할 수 있도록 합니다. 이는 외부 인증 서비스와의 연동을 확장하고 사용자 로그인 경험을 개선하는 데 기여합니다.

Highlights

  • 구글 OAuth 클라이언트 추가: 구글 OAuth를 통한 사용자 인증을 처리하는 새로운 클라이언트 로직이 추가되었습니다.
  • 구글 OAuth 설정: 구글 OAuth 관련 설정(클라이언트 ID, 시크릿, 리다이렉트 URI)을 관리하는 GoogleConfiguration 클래스가 도입되었습니다.
  • 구글 사용자 정보 응답 처리: 구글로부터 받은 사용자 정보를 파싱하기 위한 GoogleInfoResponse 레코드가 추가되었습니다.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

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 issue 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
Contributor

@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

안녕하세요. Google OAuth 클라이언트 추가에 대한 코드 변경사항을 검토했습니다. 전반적으로 새로운 기능 추가의 방향은 좋지만, 몇 가지 중요한 수정이 필요해 보입니다.

주요 검토 의견은 다음과 같습니다:

  • GoogleOAuthClientsupports 메서드가 항상 false를 반환하여 기능이 동작하지 않는 심각한 버그가 있습니다.
  • Google 사용자 ID의 데이터 타입이 Long으로 잘못 지정되어 있습니다. Google API 명세에 따라 String으로 변경해야 합니다.
  • 일부 설정(URL)이 하드코딩되어 있고, HTTP 메서드 사용이 부적절한 부분이 있습니다.
  • Bean 등록 방식이 기존 코드와 일관되지 않은 점도 발견되었습니다.

자세한 내용은 각 파일에 남긴 개별 코멘트를 참고해 주세요. 특히 critical로 표시된 부분은 반드시 수정이 필요합니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (4)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleConfiguration.java (1)

11-21: Properties naming nit: prefer “...Properties” for clarity

Consider renaming to GoogleOAuthProperties to avoid confusion with @configuration classes defining beans.

domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (3)

18-25: Optional: configure timeouts on RestClient

Set sane connect/read timeouts to avoid hanging threads on upstream slowness.

If you want, I can provide a small factory using JDK HttpClient (Spring 6) with configurable timeouts.


31-51: Optional: handle non-2xx with domain exceptions

Map 4xx/5xx to meaningful auth errors to improve UX and observability.

@@
-			.retrieve()
+			.retrieve()
+			.onStatus(s -> s.is4xxClientError() || s.is5xxServerError(),
+				(req, res) -> {
+					// TODO: replace with your domain exception type
+					return java.util.concurrent.CompletableFuture.failedFuture(
+						new IllegalStateException("Google token exchange failed: " + res.getStatusCode()));
+				})
 			.body(AccessTokenResponse.class);

39-51: Optional: PKCE support

If the auth code was obtained with PKCE, include code_verifier.

Confirm whether your frontend uses PKCE. If yes, I’ll add an optional codeVerifier field to OAuthLoginCommand and conditionally add it to the form.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7708b4d and da39def.

📒 Files selected for processing (3)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleConfiguration.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleInfoResponse.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (1 hunks)
⏰ 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: test

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (1)

42-53: Security/compliance: don’t send client_secret in URL; use x-www-form-urlencoded body

Query params leak via logs/proxies; OAuth token requests must use form-encoded body.

 private AccessTokenResponse getAccessToken(final OAuthLoginCommand command) {
-    return tokenClient.post()
-        .uri(uriBuilder -> uriBuilder
-            .queryParam("client_id", googleConfiguration.getClientId())
-            .queryParam("client_secret", googleConfiguration.getClientSecret())
-            .queryParam("code", command.code())
-            .queryParam("grant_type", "authorization_code")
-            .queryParam("redirect_uri", googleConfiguration.getRedirectUri())
-            .build())
-        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
-        .retrieve()
-        .body(AccessTokenResponse.class);
+    var form = new org.springframework.util.LinkedMultiValueMap<String, String>();
+    form.add("client_id", googleConfiguration.getClientId());
+    form.add("client_secret", googleConfiguration.getClientSecret());
+    form.add("code", command.code());
+    form.add("grant_type", "authorization_code");
+    form.add("redirect_uri", googleConfiguration.getRedirectUri());
+
+    return tokenClient.post()
+        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+        .accept(MediaType.APPLICATION_JSON)
+        .body(form)
+        .retrieve()
+        .body(AccessTokenResponse.class);
 }
🧹 Nitpick comments (3)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/OAuthConfiguration.java (1)

23-23: Avoid leaking secrets in logs

Confirm GoogleConfiguration.toString() redacts clientSecret; otherwise, switch to structured logging with explicit safe fields.

domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (2)

16-27: Optional: externalize endpoints to configuration

Move TOKEN_URL/INFO_URL to GoogleConfiguration to avoid hardcoding and ease future changes.


21-27: Optional: timeouts/retries

Consider configuring RestClient with connect/read timeouts and small retry policy for transient 5xx.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da39def and f785b2c.

📒 Files selected for processing (2)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/OAuthConfiguration.java (1 hunks)
⏰ 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: test
🔇 Additional comments (1)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/OAuthConfiguration.java (1)

20-25: Good: bean is conditionally created alongside Kakao’s, keeping startup safe

Factory-based registration with @ConditionalOnBean(GoogleConfiguration.class) looks correct and consistent.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (3)

16-27: Avoid hardcoding OAuth endpoints; prefer configuration with sane defaults.

Expose tokenUri/userInfoUri in GoogleConfiguration (with defaults to Google endpoints) to ease future changes and testing.


42-53: Critical: client_secret is sent in URL query — move to x-www-form-urlencoded body.

Secrets in URLs leak via logs/metrics/proxies and may be rejected by Google. Send a form body.

Apply this diff:

-    return tokenClient.post()
-        .uri(uriBuilder -> uriBuilder
-            .queryParam("client_id", googleConfiguration.getClientId())
-            .queryParam("client_secret", googleConfiguration.getClientSecret())
-            .queryParam("code", command.code())
-            .queryParam("grant_type", "authorization_code")
-            .queryParam("redirect_uri", googleConfiguration.getRedirectUri())
-            .build())
-        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
-        .retrieve()
-        .body(AccessTokenResponse.class);
+    var form = new org.springframework.util.LinkedMultiValueMap<String, String>();
+    form.add("client_id", googleConfiguration.getClientId());
+    form.add("client_secret", googleConfiguration.getClientSecret());
+    form.add("code", command.code());
+    form.add("grant_type", "authorization_code");
+    form.add("redirect_uri", googleConfiguration.getRedirectUri());
+
+    return tokenClient.post()
+        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+        .accept(MediaType.APPLICATION_JSON)
+        .body(form)
+        .retrieve()
+        .body(AccessTokenResponse.class);

Add missing import if needed:

import org.springframework.util.LinkedMultiValueMap;

19-27: Use OIDC userinfo endpoint and GET (not POST form).

Switch to https://openidconnect.googleapis.com/v1/userinfo and call with GET + Accept: application/json.

Apply this diff:

-    // 사용자 정보 조회 URL
-    private static final String INFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
+    // 사용자 정보 조회 URL (OIDC)
+    private static final String INFO_URL = "https://openidconnect.googleapis.com/v1/userinfo";
@@
-    private GoogleInfoResponse getInfo(final String accessToken) {
-        return infoClient.post()
-            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
-            .header(HttpHeaders.AUTHORIZATION, TOKEN_FORMAT.formatted(accessToken))
-            .retrieve()
-            .body(GoogleInfoResponse.class);
-    }
+    private GoogleInfoResponse getInfo(final String accessToken) {
+        return infoClient.get()
+            .header(HttpHeaders.AUTHORIZATION, TOKEN_FORMAT.formatted(accessToken))
+            .accept(MediaType.APPLICATION_JSON)
+            .retrieve()
+            .body(GoogleInfoResponse.class);
+    }

Also applies to: 34-39

🧹 Nitpick comments (1)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (1)

21-27: Consider timeouts and observability on RestClient.

Set connect/read timeouts and add minimal structured logging/metrics to avoid request-thread stalls and ease ops.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f785b2c and dc231b9.

📒 Files selected for processing (2)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/OAuthProvider.java (1 hunks)
⏰ 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: test
🔇 Additional comments (4)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/OAuthProvider.java (2)

4-5: Enum extension looks good.

Adding GOOGLE alongside KAKAO is correct and unblocks provider routing.


3-6: No switches found — provider→client routing already handles GOOGLE.

Searched the codebase: no switch(OAuthProvider) usages. OAuthClientManager delegates to registered OAuthClientHandler beans and GoogleOAuthClient.supports(...) returns OAuthProvider.GOOGLE — so GOOGLE is handled by the existing handler.

Files to check:

  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/OAuthClientManager.java
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank.domain/auth/client/GoogleOAuthClient.java
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank.domain/auth/client/KakaoOAuthClient.java
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleOAuthClient.java (2)

56-59: LGTM on provider gating.

Null-safe check with equals is fine and correctly advertises GOOGLE support.


11-12: Bean registration/conditions confirmed

OAuthConfiguration declares a @bean guarded by @ConditionalOnBean(GoogleConfiguration.class) (domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/OAuthConfiguration.java); GoogleConfiguration is annotated with @ConditionalOnProperty(prefix = "oauth.google", name = "clientId") (domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleConfiguration.java).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (8)
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/MemberInfo.java (1)

4-6: LGTM: moving memberId to String aligns with provider “sub/ID” semantics

No functional issues spotted in this file. Consider documenting that memberId is the provider-scoped identifier, not our PK, to avoid confusion at call sites.

domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/NaverMemberInfoResponse.java (1)

7-9: Guard against null IDs from provider

If response.id() is null, we’ll persist "null"/fail later. Add a null check and raise the same domain exception used in Kakao flow.

Apply inside toInfo():

  @Override
  public MemberInfo toInfo() {
-    return new MemberInfo(response.id(), response.nickname());
+    if (response == null || response.id() == null) {
+      throw new InvalidOAuthLoginException("네이버 서버로부터 사용할 수 없는 메시지를 받았습니다.");
+    }
+    return new MemberInfo(response.id(), response.nickname());
  }

And add import (outside range):

import kr.co.mathrank.domain.auth.exception.InvalidOAuthLoginException;

Also applies to: 13-13

domain/mathrank-auth-domain/src/test/java/kr/co/mathrank/domain/auth/service/OAuthLoginServiceTest.java (1)

93-95: LGTM: same String oAuthId update in second test

Consistent with the new API.

If you add shutdown above, mirror it here for symmetry.

domain/mathrank-auth-domain/src/test/java/kr/co/mathrank/domain/auth/repository/MemberRepositoryTest.java (1)

25-33: Strengthen assertion and cover the no-lock finder.

Assert on the actual entity and also exercise the no-lock path.

-        Assertions.assertTrue(memberRepository.findByOAuthIdAndProvider(oAuthId, OAuthProvider.KAKAO).isPresent());
+        final Member found = memberRepository.findByOAuthIdAndProvider(oAuthId, OAuthProvider.KAKAO).orElseThrow();
+        Assertions.assertEquals(member.getId(), found.getId());
+        Assertions.assertEquals(1,
+            memberRepository.findAllByOAuthIdAndProviderNoLock(oAuthId, OAuthProvider.KAKAO).size());
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/Member.java (2)

84-94: Guard against null/blank OAuth IDs (and null provider/role) at creation.

Prevents bad rows after migrating IDs to String.

   public static Member fromOAuth(final Long id, final String oAuthId, final OAuthProvider provider,
     final String nickName, final Role role) {
+    if (oAuthId == null || oAuthId.isBlank()) {
+      throw new IllegalArgumentException("oAuthId must not be blank");
+    }
+    java.util.Objects.requireNonNull(provider, "provider must not be null");
+    java.util.Objects.requireNonNull(role, "role must not be null");
     final Member member = new Member();
     member.id = id;
     member.oAuthInfo.setOAuthUserId(oAuthId);
     member.oAuthInfo.setOAuthProvider(provider);
     member.name = nickName;
     member.role = role;

29-31: Consider enforcing a DB-level uniqueness on (oauth_user_id, oauth_provider).

You already query as if the pair is unique; a unique constraint will prevent duplicates at the source.

-@Table(name = "member", indexes = {
-  @Index(name = "idx_oAuthUserId_oAuthProvider", columnList = "oauth_user_id, oauth_provider")
-})
+@Table(
+  name = "member",
+  indexes = {
+    @Index(name = "idx_oAuthUserId_oAuthProvider", columnList = "oauth_user_id, oauth_provider")
+  },
+  uniqueConstraints = {
+    @jakarta.persistence.UniqueConstraint(
+      name = "uk_oauth_user_provider",
+      columnNames = {"oauth_user_id", "oauth_provider"}
+    )
+  }
+)

Before merging, confirm your migration adds this unique constraint (and that index length limits are respected on your DB).

domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/repository/MemberRepository.java (2)

21-23: Nit: rename params to oAuthId and make the no-lock intent explicit.

Improves readability and avoids future ambiguity.

   @Lock(LockModeType.PESSIMISTIC_WRITE)
   Optional<Member> findByOAuthIdAndProvider(
-    @Param("oAuthId") final String oAuthInfoOAuthUserId,
+    @Param("oAuthId") final String oAuthId,
     @Param("provider") final OAuthProvider provider
   );
 
   @Query("SELECT m FROM Member m WHERE m.oAuthInfo.oAuthUserId = :oAuthId AND m.oAuthInfo.oAuthProvider = :provider")
+  @Lock(LockModeType.NONE)
   List<Member> findAllByOAuthIdAndProviderNoLock(
-    @Param("oAuthId") final String oAuthInfoOAuthUserId,
+    @Param("oAuthId") final String oAuthId,
     @Param("provider") final OAuthProvider provider
   );

Also applies to: 27-29


18-23: Optional: add an existence check method for hot paths.

When you only need presence, existsBy... avoids loading the entity.

Example:

boolean existsByOAuthInfo_OAuthUserIdAndOAuthInfo_OAuthProvider(String oAuthId, OAuthProvider provider);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc231b9 and 457909d.

📒 Files selected for processing (11)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleInfoResponse.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/KakaoMemberInfoResponse.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/MemberInfo.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/NaverMemberInfoResponse.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/OAuthConfiguration.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/Member.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/OAuthInfo.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/OAuthProvider.java (1 hunks)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/repository/MemberRepository.java (1 hunks)
  • domain/mathrank-auth-domain/src/test/java/kr/co/mathrank/domain/auth/repository/MemberRepositoryTest.java (1 hunks)
  • domain/mathrank-auth-domain/src/test/java/kr/co/mathrank/domain/auth/service/OAuthLoginServiceTest.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/GoogleInfoResponse.java
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/entity/OAuthProvider.java
  • domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/client/OAuthConfiguration.java
⏰ 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: test
🔇 Additional comments (2)
domain/mathrank-auth-domain/src/test/java/kr/co/mathrank/domain/auth/service/OAuthLoginServiceTest.java (1)

59-61: LGTM: switching test oAuthId to String matches the domain change

Change is consistent with repository and DTO updates.

Optionally, shut down the ExecutorService to avoid thread leakage in CI:

-    try {
+    try {
       countDownLatch.await();
     } catch (InterruptedException e) {
       throw new RuntimeException(e);
     }
+    executorService.shutdown();
domain/mathrank-auth-domain/src/main/java/kr/co/mathrank/domain/auth/repository/MemberRepository.java (1)

18-29: Verified — no Long-typed OAuth ID call sites remain.
MemberRepository methods use String for oAuthId; usages in OAuthLoginService and tests pass String. Member.fromOAuth still takes a Long for the uniqueId (expected).

@huhdy32 huhdy32 merged commit 768141d into develop Sep 15, 2025
2 checks passed
@huhdy32 huhdy32 deleted the feat/oauth-google branch September 15, 2025 13:34
huhdy32 added a commit that referenced this pull request Sep 15, 2025
* feat(oauth-naver): 네이버 oAuth 클라이언트 추가 (#171)

* feat(oauth-naver): 네이버 oAuth 클라이언트 추가

* fix(oauth-naver): 설정값 있을때만 빈으로 등록되도록 수정

* docs(oauth-naver): url 주석 추가

* refactor(oauth-naver): 상수로 변경

* feat(oauth-google): 구글 oAuth 클라이언트 추가 (#172)

* feat(oauth-google): 구글 oAuth 클라이언트 추가

* fix(oauth-google): 프로퍼티 구성에 따라 빈으로 등록되도록 수정

* refactor(oauth-google): 상수로 변경

* docs(oauth-google): 주석 추가

* fix(oauth-google): oauthProvider 확인 기능 수정

* fix(oauth): string 처리 필요함에 따른 도메인 타입 수정 ㅠ
huhdy32 added a commit that referenced this pull request Sep 16, 2025
* docs(README): 아키텍쳐 그림 추가 (#161)

* feat(ssm): 외부 설정 ssm으로 옮김 (#167)

* test(chore): cicd 프로퍼티 parameter store로 빼기

* fix(ssm): 로그 설정으로 인한 어플리케이션 실행 실채 해결

로그 설정 파일 위치 변경

* fix(id): 프론트에서 Number 타입으로 받음에 따라 유실 발생(부동소수점), 따라서 String 으로 전환해서 보내기~ (#169)

* feat(oauth-naver): 네이버 oAuth 클라이언트 추가 (#171)

* feat(oauth-naver): 네이버 oAuth 클라이언트 추가

* fix(oauth-naver): 설정값 있을때만 빈으로 등록되도록 수정

* docs(oauth-naver): url 주석 추가

* refactor(oauth-naver): 상수로 변경

* feat(oauth-google): 구글 oAuth 클라이언트 추가 (#172)

* feat(oauth-google): 구글 oAuth 클라이언트 추가

* fix(oauth-google): 프로퍼티 구성에 따라 빈으로 등록되도록 수정

* refactor(oauth-google): 상수로 변경

* docs(oauth-google): 주석 추가

* fix(oauth-google): oauthProvider 확인 기능 수정

* fix(oauth): string 처리 필요함에 따른 도메인 타입 수정 ㅠ

* feat(rank): 개별 사용자 랭크 조회 레디스로 변경 (#174)

* feat(rank-redis): 사용자 랭크 조회 기능 레디스로 변경

* feat(rank-redis): 풀이기록 추가 성공 시, 레디스 내 점수 업데이트하도록 수정

* feat(rank-redis): 개별랭크 조회 시, 레디스 사용하도록 변경

* feat(rank-redis): 레디스와 RDB 동기화 로직 구성

* feat(rank-redis): 레디스와 RDB 동기화 배치 어플리케이션 구성

* fix(rank): 의존성 오타 수정

* feat(rank): 점수가 없으면 null로 응답한다

* fix(rank): 배치 처리에서 offset 오류 수정

* chore: log 삭제

* feat(rank): 컨텍스트 준비되면 실행되도록 수정

* test(rank): 다른 클래스에 검증 중인 기능임으로 제거
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant