Skip to content

Conversation

@Be-HinD
Copy link
Collaborator

@Be-HinD Be-HinD commented Dec 31, 2025

📝 Pull Request

📌 PR 종류

해당하는 항목에 체크해주세요.

  • 기능 추가 (Feature)
  • 버그 수정 (Fix)
  • 문서 수정 (Docs)
  • 코드 리팩터링 (Refactor)
  • 테스트 추가 (Test)
  • 기타 변경 (Chore)

✨ 변경 내용

구글 소셜 로그인을 위한 로직이 추가되었습니다.
OAuth 인증 흐름은 아래와 같습니다.

  • 프론트에서 Authorization Code 발급까지 진행
  • 백에서는 Authorization Code를 통한 사용자 정보 조회 및 로그인/회원가입 처리 진행

추가로 닉네임의 경우 unique 특성을 가지며, Stateless 특성 상 추가 정보를 받는것이 어려워 닉네임 자동 생성 로직도 적용하였습니다.
닉네임 생성 기준

  • prefix : wego_

  • counter : 1부터 순차적 증가
    카운터를 위한 엔티티 추가되었으며, 비관적 락을 통한 동시성 제어도 추가하였습니다.
    주의할 점으로 초기 데이터(id = 1)가 없을 경우 예외가 발생함으로 DB 초기 데이터 삽입이 필요합니다.


🔍 관련 이슈

해당 PR이 해결하는 이슈가 있다면 연결해주세요.
Closes #168


🧪 테스트

변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.

  • 유닛 테스트 추가 / 수정
  • 통합 테스트 검증
  • 수동 테스트 완료
_.mp4

🚨 확인해야 할 사항 (Checklist)

PR을 제출하기 전에 아래 항목들을 확인해주세요.

  • 코드 포매팅 완료
  • 불필요한 파일/코드 제거
  • 로직 검증 완료
  • 프로젝트 빌드 성공
  • 린트/정적 분석 통과 (해당 시)

🙋 기타 참고 사항

구글 소셜 로그인에 필요한 client_id 및 secret 값의 경우 프론트와 sync 맞추어두었습니다.

Summary by CodeRabbit

  • 새로운 기능
    • Google OAuth 로그인이 추가되어 Google 계정으로 가입·로그인할 수 있습니다.
    • Google 가입 시 자동으로 고유 닉네임이 생성됩니다.
    • 로그인 시 리프레시 토큰을 쿠키로 발급합니다.
    • 클라이언트 측 요청 검증이 강화되어 필수 필드 검증 메시지가 추가되었습니다.
    • Google OAuth 설정(클라이언트 ID/시크릿)을 환경변수로 구성할 수 있습니다.

✏️ Tip: You can customize this high-level summary in your review settings.

- HTTP 요청을 위한 RestClient Timeout 설정
- 구글 로그인 엔드포인트 개발
- 닉네임 자동 생성 로직 개발
@Be-HinD Be-HinD self-assigned this Dec 31, 2025
@Be-HinD Be-HinD added the ✨enhancement New feature or request label Dec 31, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 31, 2025

Walkthrough

Google OAuth 소셜 로그인을 추가했습니다: AuthService에 googleLogin 메서드, 관련 DTO/엔티티/레포지토리(RestClient, UserCounter, UserRepository 변경 포함), 컨트롤러 엔드포인트 및 설정(application.yml, SecurityEndpoints)을 도입해 Google 토큰 교환과 사용자 생성 흐름을 구현했습니다.

Changes

Cohort / File(s) 변경 요약
OAuth 핵심 로직
src/main/java/team/wego/wegobackend/auth/application/AuthService.java
Google 인증 코드 교환, 사용자 정보 조회, 기존 사용자 조회 또는 신규 Google 사용자 생성, JWT 발급 및 LoginResponse 반환하는 googleLogin(...) 추가. 자동 닉네임 생성(카운터 락 이용) 보조 메서드 포함
요청/응답 DTO
src/main/java/team/wego/wegobackend/auth/application/dto/request/GoogleLoginRequest.java, .../response/GoogleTokenResponse.java, .../response/GoogleUserInfoResponse.java
Google OAuth 요청/응답 매핑용 DTO 추가/검증 어노테이션(@NotBlank) 적용, JSON 필드 매핑(@JsonProperty) 포함
User 카운터 엔티티 및 레포지토리
src/main/java/team/wego/wegobackend/auth/entity/UserCounter.java, src/main/java/team/wego/wegobackend/auth/repository/UserCounterRepository.java
자동 닉네임용 카운터 엔티티 추가(@Version 포함), PESSIMISTIC_WRITE로 잠그는 findWithLock() 메서드 추가
예외 및 에러코드
src/main/java/team/wego/wegobackend/auth/exception/NotInitializedUserCounterException.java, src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java
UserCounter 미초기화 전용 예외 추가 및 NOT_INIT_COUNTER 에러 코드 등록
컨트롤러 및 문서화
src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java, .../AuthControllerDocs.java
POST /api/v1/auth/google 엔드포인트 추가(요청 바디: GoogleLoginRequest), 스웨거 문서용 오버로드 추가
인프라/HTTP 클라이언트 설정
src/main/java/team/wego/wegobackend/infrastructure/config/RestClientConfig.java
Google API 호출용 RestClient 빈 추가(커넥트/리드 타임아웃 설정)
User 도메인 변경 및 저장소 확장
src/main/java/team/wego/wegobackend/user/domain/ProviderType.java, .../User.java, .../UserRepository.java
ProviderType 열거형(GOOGLE, LOCAL) 추가, User에 providerId/provider 필드 및 createLocalUser/createGoogleUser 팩토리 추가, findByProviderAndProviderId(...) 조회 메서드 추가
설정 및 보안 엔드포인트
src/main/resources/application.yml, src/main/java/team/wego/wegobackend/common/security/SecurityEndpoints.java
google.oauth.client-id/client-secret 설정 추가 및 /api/v*/auth/google 공개 엔드포인트로 등록

Sequence Diagram

sequenceDiagram
    participant Client
    participant AuthController
    participant AuthService
    participant GoogleAPI as Google OAuth API
    participant UserRepo as UserRepository
    participant CounterRepo as UserCounterRepository
    participant DB as Database

    Client->>AuthController: POST /api/v1/auth/google\n(authorization code, redirect URI)
    AuthController->>AuthService: googleLogin(GoogleLoginRequest)

    rect rgba(200,220,255,0.6)
    Note over AuthService,GoogleAPI: 토큰 교환
    AuthService->>GoogleAPI: Exchange code -> tokens\n(client_id, client_secret, code, redirect_uri)
    GoogleAPI-->>AuthService: GoogleTokenResponse (access_token,...)
    end

    rect rgba(200,220,255,0.6)
    Note over AuthService,GoogleAPI: 사용자 정보 조회
    AuthService->>GoogleAPI: Get user info (access_token)
    GoogleAPI-->>AuthService: GoogleUserInfoResponse (id, email, name, picture)
    end

    AuthService->>UserRepo: findByProviderAndProviderId(GOOGLE, google_id)
    UserRepo->>DB: SELECT user...
    DB-->>UserRepo: user or empty
    UserRepo-->>AuthService: Optional<User>

    alt 기존 사용자 존재
        AuthService->>AuthService: 기존 사용자 사용
    else 신규 사용자
        rect rgba(220,255,220,0.6)
        Note over AuthService,CounterRepo: 자동 닉네임 생성 (동시성 제어)
        AuthService->>CounterRepo: findWithLock()  -- PESSIMISTIC_WRITE
        CounterRepo->>DB: SELECT ... FOR UPDATE
        DB-->>CounterRepo: UserCounter (locked)
        CounterRepo-->>AuthService: UserCounter
        AuthService->>CounterRepo: plusCounter() -> UPDATE
        CounterRepo->>DB: UPDATE counter
        DB-->>CounterRepo: OK
        end
        AuthService->>UserRepo: save(new Google user)
        UserRepo->>DB: INSERT user
        DB-->>UserRepo: OK
    end

    AuthService->>AuthService: JWT 생성 (access + refresh)
    AuthService-->>AuthController: LoginResponse
    AuthController->>Client: Set refresh-cookie + return LoginResponse
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰✨ 달빛 아래 코드로 다리 놓고,
구글 토큰 훔쳐와 사용자 만나고,
카운터 꽉 잠그고 닉네임 지어,
새 친구 DB에 안착시키네,
토큰 들고 집으로 깡총깡총! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main change: implementing Google social login functionality, which is the primary objective of this changeset.
Linked Issues check ✅ Passed The PR successfully implements Google social login with frontend-responsible OAuth flow. Code changes include GoogleLogin service, DTOs, OAuth integration, nickname generation with counter, pessimistic locking for concurrency, and security endpoint configuration.
Out of Scope Changes check ✅ Passed All changes are directly related to Google OAuth implementation. Modifications to User entity, AuthService, repositories, and configuration files all support the core feature. No unrelated changes detected.
✨ Finishing touches
  • 📝 Generate docstrings

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

@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: 10

🧹 Nitpick comments (4)
src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java (1)

36-36: HTTP 상태 코드 검토 권장

카운터 미초기화는 클라이언트가 해결할 수 없는 서버 내부 상태 문제입니다. HttpStatus.NOT_FOUND보다는 HttpStatus.INTERNAL_SERVER_ERROR 또는 HttpStatus.SERVICE_UNAVAILABLE이 더 적절할 수 있습니다. NOT_FOUND는 일반적으로 리소스(사용자, 게시물 등)가 없을 때 사용됩니다.

🔎 제안하는 수정
-    NOT_INIT_COUNTER(HttpStatus.NOT_FOUND, "회원 : 유저 카운터가 초기화 되어있지 않습니다."),
+    NOT_INIT_COUNTER(HttpStatus.INTERNAL_SERVER_ERROR, "회원 : 유저 카운터가 초기화 되어있지 않습니다."),
src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java (1)

77-93: 메서드 명명 개선 권장

기존 로그인 메서드와 동일한 이름 login을 사용하여 오버로딩하고 있습니다. 파라미터 타입으로 구분되지만, 가독성을 위해 googleLogin으로 명명하는 것을 권장합니다.

🔎 제안하는 수정
     /**
      * OAuth-Google 로그인
      */
     @PostMapping("/google")
-    public ResponseEntity<ApiResponse<LoginResponse>> login(
+    public ResponseEntity<ApiResponse<LoginResponse>> googleLogin(
         @Valid @RequestBody GoogleLoginRequest request,
         HttpServletResponse response
     ) {
src/main/java/team/wego/wegobackend/infrastructure/config/RestClientConfig.java (1)

17-28: LGTM! 타임아웃 설정이 적절합니다.

RestClient 구성이 Google OAuth API 호출에 적합합니다. 5초 연결 타임아웃과 10초 읽기 타임아웃은 외부 API 호출에 합리적인 값입니다.

향후 개선 사항으로, 기본 에러 핸들러 추가를 고려해 볼 수 있습니다:

🔎 Optional: 에러 핸들러 추가 예시
 return RestClient.builder()
     .requestFactory(factory)
+    .defaultStatusHandler(HttpStatusCode::isError, (request, response) -> {
+        throw new RestClientResponseException(
+            "External API error",
+            response.getStatusCode(),
+            response.getStatusText(),
+            response.getHeaders(),
+            response.getBody().readAllBytes(),
+            null
+        );
+    })
     .build();
src/main/java/team/wego/wegobackend/auth/entity/UserCounter.java (1)

31-36: 비관적 잠금과 @Version 동시 사용은 중복됨

UserCounterRepository.findWithLock()에서 PESSIMISTIC_WRITE 잠금을 사용하는데, @Version을 통한 낙관적 잠금까지 함께 사용하면 불필요한 오버헤드가 발생합니다.

🔎 수정 제안: @Version 제거
     @Column(nullable = false)
     private Long counter;

-    @Version
-    private Long version;
-
     public void plusCounter() {
         this.counter++;
     }

비관적 잠금만으로 동시성 제어가 충분히 보장됩니다. @Version을 유지하려면 비관적 잠금을 제거하고 낙관적 잠금 기반으로 재시도 로직을 구현하는 방안을 고려하세요.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50e5e76 and 8b71c2b.

📒 Files selected for processing (16)
  • src/main/java/team/wego/wegobackend/auth/application/AuthService.java
  • src/main/java/team/wego/wegobackend/auth/application/dto/request/GoogleLoginRequest.java
  • src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleTokenResponse.java
  • src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleUserInfoResponse.java
  • src/main/java/team/wego/wegobackend/auth/entity/UserCounter.java
  • src/main/java/team/wego/wegobackend/auth/exception/NotInitializedUserCounterException.java
  • src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java
  • src/main/java/team/wego/wegobackend/auth/presentation/AuthControllerDocs.java
  • src/main/java/team/wego/wegobackend/auth/repository/UserCounterRepository.java
  • src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java
  • src/main/java/team/wego/wegobackend/common/security/SecurityEndpoints.java
  • src/main/java/team/wego/wegobackend/infrastructure/config/RestClientConfig.java
  • src/main/java/team/wego/wegobackend/user/domain/ProviderType.java
  • src/main/java/team/wego/wegobackend/user/domain/User.java
  • src/main/java/team/wego/wegobackend/user/repository/UserRepository.java
  • src/main/resources/application.yml
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleTokenResponse.java (1)
src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleUserInfoResponse.java (1)
  • Getter (8-20)
src/main/java/team/wego/wegobackend/auth/application/AuthService.java (1)
src/main/java/team/wego/wegobackend/auth/exception/NotInitializedUserCounterException.java (1)
  • NotInitializedUserCounterException (6-11)
🪛 ast-grep (0.40.3)
src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java

[warning] 83-83: The application does not appear to verify inbound requests which can lead to a Cross-site request forgery (CSRF) vulnerability. If the application uses cookie-based authentication, an attacker can trick users into sending authenticated HTTP requests without their knowledge from any arbitrary domain they visit. To prevent this vulnerability start by identifying if the framework or library leveraged has built-in features or offers plugins for CSRF protection. CSRF tokens should be unique and securely random. The Synchronizer Token or Double Submit Cookie patterns with defense-in-depth mechanisms such as the sameSite cookie flag can help prevent CSRF. For more information, see: [Cross-site request forgery prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Req\ uest_Forgery_Prevention_Cheat_Sheet.html).
Context: response.addCookie(createRefreshTokenCookie(loginResponse.getRefreshToken()));
Note: [CWE-352] Cross-Site Request Forgery (CSRF). [REFERENCES]
- https://stackoverflow.com/questions/42717210/samesite-cookie-in-java-application

(cookie-missing-samesite-java)


[warning] 83-83: A cookie was detected without setting the 'HttpOnly' flag. The 'HttpOnly' flag for cookies instructs the browser to forbid client-side scripts from reading the cookie. Set the 'HttpOnly' flag by calling 'cookie.setHttpOnly(true);
Context: response.addCookie(createRefreshTokenCookie(loginResponse.getRefreshToken()));
Note: [CWE-1004] Sensitive Cookie Without 'HttpOnly' Flag. [REFERENCES]
- https://owasp.org/www-community/HttpOnly

(cookie-missing-httponly-java)

🔇 Additional comments (9)
src/main/java/team/wego/wegobackend/user/domain/ProviderType.java (1)

3-5: LGTM!

OAuth 제공자를 구분하기 위한 enum이 명확하게 정의되었습니다. GOOGLE과 LOCAL 값이 적절합니다.

src/main/java/team/wego/wegobackend/common/security/SecurityEndpoints.java (1)

8-8: LGTM!

Google OAuth 로그인 엔드포인트가 public 패턴에 올바르게 추가되었습니다.

src/main/java/team/wego/wegobackend/user/repository/UserRepository.java (1)

14-14: LGTM!

OAuth 제공자와 제공자 ID로 사용자를 조회하는 메서드가 적절하게 추가되었습니다. Spring Data JPA의 명명 규칙을 따르고 있으며 Optional 반환이 적절합니다.

src/main/java/team/wego/wegobackend/auth/exception/NotInitializedUserCounterException.java (1)

6-11: LGTM!

예외 클래스가 올바르게 정의되었으며 기존 패턴을 따르고 있습니다.

src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java (2)

82-84: 정적 분석 도구 경고는 오탐입니다

정적 분석 도구가 CSRF 및 HttpOnly 플래그 누락을 경고하고 있으나, 실제로는 createRefreshTokenCookie 헬퍼 메서드(lines 152-161)에서 다음과 같이 올바르게 설정되고 있습니다:

  • cookie.setHttpOnly(true) (line 154)
  • cookie.setAttribute("SameSite", "Strict") (line 159)
  • cookie.setSecure(true) (line 155)

이는 OWASP 권장사항을 충족합니다.


82-84: AuthService.googleLogin 메서드 구현이 모두 정상적으로 완료되었습니다

해당 메서드(@transactional 118줄)에서 다음 모든 항목이 올바르게 구현되어 있습니다:

  • Authorization code를 사용한 Google token 교환 (124-136줄)
  • Google 사용자 정보 조회 (140-144줄)
  • 기존 사용자 확인 및 신규 가입 처리 (148-165줄)
  • 닉네임 자동 생성 및 중복 처리 (152줄, 233줄의 while 루프)
  • UserCounter의 pessimistic lock 처리 (226줄의 findWithLock())
  • 트랜잭션 경계 설정 (@transactional 118줄)
src/main/java/team/wego/wegobackend/auth/presentation/AuthControllerDocs.java (1)

32-36: 메서드 오버로딩과 Swagger 문서화 확인 필요

인터페이스에서 메서드 오버로딩은 유효하지만, Swagger/OpenAPI에서 동일한 메서드명의 오버로드를 문서화할 때 문제가 발생할 수 있습니다. API 문서가 올바르게 생성되는지 확인해 주세요.

Swagger UI에서 두 login 엔드포인트가 모두 정상적으로 표시되는지 확인해 주세요. 필요시 @OperationoperationId를 명시적으로 지정하면 구분이 명확해집니다:

@Operation(summary = "구글 소셜 로그인", operationId = "googleLogin", ...)
src/main/java/team/wego/wegobackend/auth/repository/UserCounterRepository.java (1)

10-15: 비관적 잠금 방식은 적절하나, 하드코딩된 ID에 주의 필요

동시성 제어를 위한 PESSIMISTIC_WRITE 잠금은 적절합니다. 다만 id = 1이 하드코딩되어 있어 초기 데이터가 반드시 삽입되어야 합니다.

PR 설명에 언급된 대로 초기 데이터 삽입이 필요합니다. 데이터베이스 마이그레이션 스크립트나 data.sql에 초기 레코드가 포함되어 있는지 확인해 주세요:

INSERT INTO user_counter (id, counter, version) VALUES (1, 0, 0);

운영 환경 배포 시 이 초기 데이터가 누락되면 NotInitializedUserCounterException이 발생합니다.

src/main/java/team/wego/wegobackend/user/domain/User.java (1)

81-86: OAuth 사용자 필드 추가 - LGTM

providerIdprovider 필드 추가로 OAuth 공급자 정보를 적절히 저장할 수 있습니다. @Enumerated(EnumType.STRING) 사용도 적절합니다.

Comment on lines +223 to +236
private String generateAutoNickname() {

UserCounter counter = userCounterRepository
.findWithLock()
.orElseThrow(NotInitializedUserCounterException::new);

String nickname;
do {
counter.plusCounter();
nickname = "wego_" + counter.getCounter();
} while (userRepository.existsByNickName(nickname));

return nickname;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

무한 루프 가능성 및 성능 우려

generateAutoNickname()에서 모든 닉네임이 사용 중일 경우 무한 루프가 발생할 수 있습니다. 또한 while 루프 내에서 매번 DB 쿼리를 실행하여 성능 저하가 우려됩니다.

🔎 수정 제안: 최대 시도 횟수 제한 추가
     private String generateAutoNickname() {

         UserCounter counter = userCounterRepository
             .findWithLock()
             .orElseThrow(NotInitializedUserCounterException::new);

         String nickname;
+        int maxAttempts = 100;
+        int attempts = 0;
         do {
+            if (++attempts > maxAttempts) {
+                throw new RuntimeException("Failed to generate unique nickname after " + maxAttempts + " attempts");
+            }
             counter.plusCounter();
             nickname = "wego_" + counter.getCounter();
         } while (userRepository.existsByNickName(nickname));

         return nickname;
     }

현실적으로 닉네임 충돌은 드물겠지만, 방어적 코딩으로 최대 시도 횟수를 제한하는 것이 안전합니다.

@@ -0,0 +1,5 @@
package team.wego.wegobackend.auth.application.dto.request;

public record GoogleLoginRequest(String authorizationCode, String redirectUri) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

입력 검증 추가 필요

GoogleLoginRequest에 검증 어노테이션이 없어 null 또는 빈 값이 서비스 레이어로 전달될 수 있습니다. 특히 redirectUri는 보안상 중요하므로 형식 검증이 필요합니다.

🔎 제안하는 수정
 package team.wego.wegobackend.auth.application.dto.request;

-public record GoogleLoginRequest(String authorizationCode, String redirectUri) {
+import jakarta.validation.constraints.NotBlank;
+
+public record GoogleLoginRequest(
+    @NotBlank(message = "Authorization code는 필수입니다")
+    String authorizationCode,
+    
+    @NotBlank(message = "Redirect URI는 필수입니다")
+    String redirectUri
+) {

 }
🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/auth/application/dto/request/GoogleLoginRequest.java
around line 3, the record lacks validation so null/empty or malformed values
(especially redirectUri) can reach the service; add bean validation annotations
to the record components (e.g., @NotBlank on authorizationCode and redirectUri,
and a format constraint on redirectUri such as @Pattern(...) or a @URL/@ValidUri
custom validator) and import the corresponding javax/hibernate validation
annotations; ensure the controller accepting this request uses @Valid on the
parameter so validation is triggered.

Comment on lines +7 to +21
@Getter
@AllArgsConstructor
public class GoogleTokenResponse {

@JsonProperty("access_token")
private String accessToken;

@JsonProperty("expires_in")
private Long expiresIn;

@JsonProperty("token_type")
private String tokenType;

private String scope;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Jackson 역직렬화를 위해 @NoArgsConstructor 추가 필요

@AllArgsConstructor만으로는 Jackson의 기본 역직렬화가 실패할 수 있습니다. JSON 응답을 객체로 변환하려면 기본 생성자가 필요합니다.

🔎 수정 제안
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
+import lombok.NoArgsConstructor;

 @Getter
+@NoArgsConstructor
 @AllArgsConstructor
 public class GoogleTokenResponse {

참고: GoogleUserInfoResponse@NoArgsConstructor가 있어 정상 작동합니다.

🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleTokenResponse.java
around lines 7 to 21, Jackson deserialization fails because the class only has
@AllArgsConstructor; add a no-argument constructor for Jackson by annotating the
class with Lombok's @NoArgsConstructor (keep the existing @AllArgsConstructor
and @Getter), or add a public no-args constructor manually if Lombok is not
desired; ensure fields are non-final so the no-arg constructor can populate them
during deserialization.

Comment on lines +114 to +118
# OAuth-Google
google:
oauth:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for README files and look for Google OAuth environment variable documentation
fd -t f 'README' | head -20

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 79


🏁 Script executed:

#!/bin/bash
# Search for Google OAuth environment variables in README and deployment documentation
fd -t f -e md -e txt | xargs rg -i 'GOOGLE_CLIENT_ID|GOOGLE_CLIENT_SECRET' -l 2>/dev/null | head -20

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check for deployment-related documentation files
fd -t f -i 'deploy|doc|env' | grep -i -E '(deploy|env|doc)' | head -20

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 766


🏁 Script executed:

#!/bin/bash
# List all markdown and text files in repository root
git ls-files | grep -E '\.(md|txt|yml|yaml)$' | head -30

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 388


🏁 Script executed:

cat -n README.md

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 2060


🏁 Script executed:

# Also check docker-compose.yml for environment variable documentation
cat -n docker-compose.yml

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 546


🏁 Script executed:

wc -l README.md

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 82


🏁 Script executed:

# Check docs folder for environment variable documentation
git ls-files docs/ 2>/dev/null || fd -t f docs/

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 618


🏁 Script executed:

# Look for .env files or environment configuration examples
git ls-files | grep -E '\.env' || fd -t f -e 'env' -e 'env.example'

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 52


🏁 Script executed:

# Check if there's deployment documentation
git ls-files | grep -iE '(deploy|setup|install|guide)' | head -20

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 52


🏁 Script executed:

cat "docs/기술_스택_가이드.md" | head -100

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 2104


환경 변수 문서화 추가 필요

Google OAuth 설정이 올바르게 구성되었으나, GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET 환경 변수에 대한 문서화가 README.md 또는 배포 가이드에 없습니다. 개발 환경 설정 및 프로덕션 배포 시 필요한 환경 변수 목록과 설명을 추가해주세요.

🤖 Prompt for AI Agents
In src/main/resources/application.yml around lines 114 to 118, the Google OAuth
env vars GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are used but not documented;
update the project README.md (or deployment guide) to list these environment
variables with a short description of each (GOOGLE_CLIENT_ID: OAuth client ID
from Google Cloud Console; GOOGLE_CLIENT_SECRET: OAuth client secret), indicate
where to obtain them (Google Cloud Console > APIs & Services > Credentials),
provide example formats or placeholder values, specify which environments they
apply to (dev/staging/prod), and add a security note advising to keep the secret
out of source control and use secrets management (e.g., environment variables,
vault, or CI/CD secret store) along with any required redirect URI settings.

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 (1)
src/main/java/team/wego/wegobackend/auth/application/AuthService.java (1)

131-131: 민감 정보 로깅 제거 필요

사용자 정보 객체를 직접 로깅하면 이메일 등의 개인정보가 로그에 노출될 수 있습니다. 이전 리뷰에서도 지적된 보안 이슈입니다.

🔎 수정 제안
-        log.debug("사용자 정보 조회 성공 -> {}", userInfo);
+        log.debug("Google 사용자 정보 조회 성공");
🧹 Nitpick comments (2)
src/main/java/team/wego/wegobackend/auth/application/AuthService.java (2)

143-143: 선택 사항: 디버그 로그 제거 고려

자동 생성된 닉네임을 로깅하는 것이 보안상 큰 문제는 아니지만, 프로덕션 환경에서는 불필요한 로그일 수 있습니다.

🔎 수정 제안
-            log.debug("create Init nickname -> {}", nickname);

211-224: 방어적 코딩: 최대 시도 횟수 제한 추가 권장

이전 리뷰에서 지적된 무한 루프 가능성이 여전히 존재합니다. 실제로 카운터가 순차 증가하므로 충돌 가능성은 낮지만, 예외 상황(예: 데이터 정합성 문제)을 대비한 방어 로직 추가를 권장합니다.

🔎 수정 제안
     private String generateAutoNickname() {
 
         UserCounter counter = userCounterRepository
             .findWithLock()
             .orElseThrow(NotInitializedUserCounterException::new);
 
         String nickname;
+        int maxAttempts = 100;
+        int attempts = 0;
         do {
+            if (++attempts > maxAttempts) {
+                throw new RuntimeException("닉네임 생성 실패: 최대 시도 횟수 초과");
+            }
             counter.plusCounter();
             nickname = "wego_" + counter.getCounter();
         } while (userRepository.existsByNickName(nickname));
 
         return nickname;
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b71c2b and 3e881a2.

📒 Files selected for processing (4)
  • src/main/java/team/wego/wegobackend/auth/application/AuthService.java
  • src/main/java/team/wego/wegobackend/auth/application/dto/request/GoogleLoginRequest.java
  • src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleUserInfoResponse.java
  • src/main/java/team/wego/wegobackend/user/domain/User.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/team/wego/wegobackend/auth/application/dto/request/GoogleLoginRequest.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleUserInfoResponse.java (1)
src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleTokenResponse.java (1)
  • Getter (7-21)
src/main/java/team/wego/wegobackend/auth/application/AuthService.java (8)
src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java (1)
  • Slf4j (29-177)
src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java (1)
  • Slf4j (23-168)
src/main/java/team/wego/wegobackend/auth/exception/DeletedUserException.java (1)
  • DeletedUserException (6-11)
src/main/java/team/wego/wegobackend/auth/exception/NicknameAlreadyExistsException.java (1)
  • NicknameAlreadyExistsException (6-11)
src/main/java/team/wego/wegobackend/auth/exception/NotInitializedUserCounterException.java (1)
  • NotInitializedUserCounterException (6-11)
src/main/java/team/wego/wegobackend/user/exception/UserNotFoundException.java (1)
  • UserNotFoundException (6-11)
src/main/java/team/wego/wegobackend/auth/exception/UserNotFoundException.java (1)
  • UserNotFoundException (6-11)
src/main/java/team/wego/wegobackend/common/security/exception/ExpiredTokenException.java (1)
  • ExpiredTokenException (5-10)
🔇 Additional comments (10)
src/main/java/team/wego/wegobackend/auth/application/dto/response/GoogleUserInfoResponse.java (1)

7-18: LGTM! 이전 리뷰 피드백 반영 완료

@ToString 애노테이션이 제거되어 PII 로깅 위험이 해결되었습니다. DTO 구조가 적절하며, Jackson 매핑도 올바르게 설정되어 있습니다.

src/main/java/team/wego/wegobackend/auth/application/AuthService.java (3)

42-56: 의존성 주입 구조가 적절합니다.

Google OAuth 설정값과 새로운 의존성(RestClient, UserCounterRepository)이 올바르게 주입되었습니다.


61-79: LGTM! 이전 리뷰 피드백 반영 완료

불필요한 빌더 코드가 제거되어 사용자 생성 로직이 간결하고 명확해졌습니다.


136-138: LGTM! 삭제된 사용자 검증 로직 추가 완료

이전 리뷰에서 지적된 삭제된 사용자 확인 로직이 올바르게 추가되었습니다. 기존 login() 메서드와 동일한 방식으로 일관성 있게 처리하고 있습니다.

src/main/java/team/wego/wegobackend/user/domain/User.java (6)

25-27: 빌더 패턴 구현이 적절합니다.

@Builderprivate 전체 인자 생성자, protected 기본 생성자 조합이 올바르게 설정되었습니다. JPA와 빌더 패턴의 best practice를 잘 따르고 있습니다.


38-38: OAuth 지원을 위한 password nullable 처리 적절

OAuth 사용자의 경우 비밀번호가 없으므로 nullable = true 설정이 올바릅니다. 주석으로 의도를 명확히 표시한 점도 좋습니다.


57-79: 빌더 기본값 설정 완료

@Builder.Default 애노테이션으로 카운트 필드와 플래그 필드의 기본값이 올바르게 설정되었습니다. 빌더 사용 시 누락 방지에 효과적입니다.


81-86: OAuth 프로바이더 필드 추가 완료

providerIdprovider 필드가 적절하게 추가되었습니다. @Enumerated(EnumType.STRING) 사용으로 데이터베이스 호환성도 좋습니다.


94-102: LGTM! 이전 리뷰 피드백 반영 완료

createLocalUser() 팩토리 메서드에 provider(ProviderType.LOCAL) 설정이 추가되어 이전 리뷰에서 지적된 문제가 해결되었습니다. 일관성 있는 사용자 생성 로직입니다.


104-113: Google OAuth 사용자 생성 팩토리 메서드 구현 완료

createGoogleUser() 메서드가 OAuth 사용자 생성에 필요한 모든 필드를 적절하게 설정하고 있습니다. password 필드를 null로 유지하는 것이 OAuth 흐름에 적합합니다.

Comment on lines +113 to +129
String requestBody = String.format(
"code=%s&client_id=%s&client_secret=%s&redirect_uri=%s&grant_type=authorization_code",
request.authorizationCode(), clientId, clientSecret, request.redirectUri()
);

GoogleTokenResponse response = restClient.post()
.uri("https://oauth2.googleapis.com/token")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(requestBody)
.retrieve()
.body(GoogleTokenResponse.class);

GoogleUserInfoResponse userInfo = restClient.get()
.uri("https://www.googleapis.com/oauth2/v3/userinfo")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + response.getAccessToken())
.retrieve()
.body(GoogleUserInfoResponse.class);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for RestClientConfig class and examine its implementation
rg -A 15 "class RestClientConfig" --type=java

# Also check if there are any RestClient configuration beans
rg -B 2 -A 10 "RestClient" --type=java | head -50

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 7353


🏁 Script executed:

#!/bin/bash
# Check the full method context around lines 113-129 and error handling
rg -B 20 -A 5 "https://oauth2.googleapis.com/token" --type=java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 2812


🏁 Script executed:

#!/bin/bash
# Get the complete googleLogin method to check for error handling
rg -A 50 "public LoginResponse googleLogin" --type=java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 5572


Google API 호출에 대한 에러 처리 구현 필요

RestClientConfig에서 타임아웃 설정(연결 5초, 읽기 10초)은 적절하게 구성되어 있습니다. 그러나 googleLogin 메서드에서 Google 토큰 교환 및 사용자 정보 조회 시 HTTP 에러나 타임아웃에 대한 예외 처리가 없습니다. RestClient의 onStatus()를 통한 에러 처리 또는 try-catch 블록을 추가하여 API 호출 실패 시나리오를 처리해야 합니다.

🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/auth/application/AuthService.java around
lines 113 to 129, the Google token exchange and userinfo HTTP calls lack error
and timeout handling; update the WebClient calls to handle non-2xx responses
with onStatus(...) to parse and throw a meaningful custom exception (e.g.,
ExternalAuthException) including response body/details, and wrap the overall
interaction in a try-catch to catch WebClientRequestException/TimeoutException
and other runtime errors, log the failure with context, and rethrow or translate
to your domain exception so callers can handle API failure scenarios gracefully.

@Be-HinD Be-HinD merged commit 6e436ae into main Dec 31, 2025
1 check passed
@Be-HinD Be-HinD deleted the feat/oauth-google branch December 31, 2025 10:56
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.

[FEAT] Google 소셜 로그인 적용

2 participants