Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISSUE-14 애플 & 카카오 소셜 로그인 연동 로직 구현 #15

Merged
merged 16 commits into from
Dec 31, 2024

Conversation

SunwoongH
Copy link
Member

@SunwoongH SunwoongH commented Dec 31, 2024

Related Issue ✔

close #14

Description ✔

Summary by CodeRabbit

다음은 릴리즈 노트입니다:

  • 새로운 기능

    • Apple 및 Kakao 소셜 로그인 지원 추가
    • 사용자 인증 및 관리를 위한 새로운 API 엔드포인트 도입
    • 토큰 재발급 및 회원 탈퇴 기능 구현
    • 사용자 정보와 토큰을 포함하는 새로운 데이터 클래스 UserInfo 추가
    • AlreadyExistingUserException 예외 처리 추가
  • 버그 수정

    • 사용자 중복 가입 시 예외 처리 개선
    • 토큰 검증 및 관리 로직 강화
  • 개선 사항

    • 로깅 지원 모듈 추가
    • 인증 서비스의 인터페이스 기반 설계
    • 데이터베이스 엔티티에 감사(Audit) 기능 추가
    • 사용자 생성 메서드의 반환 타입 변경 (nullable)
  • 기타

    • 오픈 페인(OpenFeign) 라이브러리 통합
    • JWT 지원 모듈 업데이트

@SunwoongH SunwoongH self-assigned this Dec 31, 2024
Copy link
Contributor

coderabbitai bot commented Dec 31, 2024

Walkthrough

이번 풀 리퀘스트는 애플과 카카오 소셜 로그인 통합을 위한 포괄적인 구현을 다룹니다. 주요 변경 사항은 OAuth 인증을 위한 새로운 서비스, 클라이언트, DTO, 그리고 관련 구성 요소의 추가를 포함합니다. 이를 통해 애플리케이션은 이제 두 소셜 플랫폼을 통한 사용자 인증을 지원할 수 있게 되었습니다.

Changes

파일 변경 요약
buildSrc/src/main/kotlin/Versions.kt OpenFeign 버전 상수 추가
core/src/main/kotlin/org/doorip/core/auth/* AuthService, AuthUseCase 인터페이스 및 구현체 업데이트
gateway/oauth/* 애플, 카카오 OAuth 서비스, 클라이언트, DTO 추가
domain/src/main/kotlin/org/doorip/domain/* AlreadyExistingUserException, UserInfo 추가
presentation/api/src/main/kotlin/org/doorip/api/auth/* 새로운 인증 컨트롤러 및 DTO 추가

Assessment against linked issues

목표 해결 여부 설명
애플 & 카카오 소셜 로그인 연동 로직 구현

Sequence Diagram

sequenceDiagram
    participant Client
    participant AuthController
    participant AuthService
    participant AuthGateway
    participant AppleAuthService
    participant KakaoAuthService

    Client->>AuthController: 소셜 로그인 요청
    AuthController->>AuthService: signIn 호출
    AuthService->>AuthGateway: getPlatformId 호출
    alt Apple 로그인
        AuthGateway->>AppleAuthService: getPlatformId
    else Kakao 로그인
        AuthGateway->>KakaoAuthService: getPlatformId
    end
    AuthService-->>AuthController: 사용자 정보 반환
    AuthController-->>Client: 로그인 응답
Loading

Poem

🌐 소셜의 문, 활짝 열리니
애플과 카카오, 손잡고 춤추네
인증의 길, 새로운 빛으로
사용자의 여정, 간편해지리 🚀


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e446589 and 8e13641.

📒 Files selected for processing (1)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/config/OpenFeignConfig.kt (1 hunks)
🔇 Additional comments (1)
gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/config/OpenFeignConfig.kt (1)

6-6: basePackages 설정이 개선되었네요! 👍

이전에 너무 넓게 설정되어 있던 basePackages가 org.doorip.gateway.oauth로 적절하게 수정되었어요.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2fd714 and e446589.

📒 Files selected for processing (40)
  • buildSrc/src/main/kotlin/Versions.kt (1 hunks)
  • core/src/main/kotlin/org/doorip/core/TestService.kt (0 hunks)
  • core/src/main/kotlin/org/doorip/core/auth/AuthService.kt (4 hunks)
  • core/src/main/kotlin/org/doorip/core/auth/AuthUseCase.kt (1 hunks)
  • domain/src/main/kotlin/org/doorip/domain/DooripException.kt (1 hunks)
  • domain/src/main/kotlin/org/doorip/domain/entity/User.kt (1 hunks)
  • domain/src/main/kotlin/org/doorip/domain/repository/UserRepository.kt (1 hunks)
  • gateway/oauth/build.gradle.kts (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/AuthGateway.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/AppleAuthService.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/AppleFeignClient.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/AppleIdTokenValidator.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/ApplePublicKeyGenerator.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/dto/ApplePublicKey.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/dto/ApplePublicKeys.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/config/OpenFeignConfig.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/kakao/KakaoAuthService.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/kakao/KakaoFeignClient.kt (1 hunks)
  • gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/kakao/dto/KakaoAccessTokenInfo.kt (1 hunks)
  • gateway/oauth/src/main/resources/application-oauth.yml (1 hunks)
  • gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/BaseJpaEntity.kt (1 hunks)
  • gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/config/JpaConfig.kt (1 hunks)
  • gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/token/RefreshTokenGateway.kt (2 hunks)
  • gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/token/RefreshTokenJpaEntity.kt (1 hunks)
  • gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/user/UserGateway.kt (2 hunks)
  • gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/user/UserJpaEntity.kt (1 hunks)
  • gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/user/UserJpaRepository.kt (1 hunks)
  • presentation/api/build.gradle.kts (1 hunks)
  • presentation/api/src/main/kotlin/org/doorip/api/TestController.kt (0 hunks)
  • presentation/api/src/main/kotlin/org/doorip/api/auth/AuthController.kt (1 hunks)
  • presentation/api/src/main/kotlin/org/doorip/api/auth/dto/AuthResponse.kt (1 hunks)
  • presentation/api/src/main/kotlin/org/doorip/api/auth/dto/AuthSignInRequest.kt (1 hunks)
  • presentation/api/src/main/kotlin/org/doorip/api/auth/dto/AuthSignInResponse.kt (1 hunks)
  • presentation/api/src/main/kotlin/org/doorip/api/auth/dto/AuthSignUpRequest.kt (1 hunks)
  • presentation/api/src/main/kotlin/org/doorip/api/exception/ApiExceptionHandler.kt (2 hunks)
  • presentation/api/src/main/kotlin/org/doorip/api/exception/ExceptionResponseFactory.kt (2 hunks)
  • settings.gradle.kts (1 hunks)
  • support/jwt/src/main/kotlin/org/doorip/support/jwt/JwtProvider.kt (0 hunks)
  • support/logging/build.gradle.kts (1 hunks)
  • support/logging/src/main/kotlin/org/doorip/support/logging/Logger.kt (1 hunks)
💤 Files with no reviewable changes (3)
  • core/src/main/kotlin/org/doorip/core/TestService.kt
  • presentation/api/src/main/kotlin/org/doorip/api/TestController.kt
  • support/jwt/src/main/kotlin/org/doorip/support/jwt/JwtProvider.kt
🧰 Additional context used
🪛 detekt (1.23.7)
gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/AppleIdTokenValidator.kt

[warning] 13-14: Usages of lateinit should be avoided.

(detekt.potential-bugs.LateinitUsage)


[warning] 16-17: Usages of lateinit should be avoided.

(detekt.potential-bugs.LateinitUsage)

gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/kakao/KakaoAuthService.kt

[warning] 17-17: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/ApplePublicKeyGenerator.kt

[warning] 32-32: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🔇 Additional comments (51)
gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/token/RefreshTokenGateway.kt (1)

12-12: 트랜잭션 적용을 통해 데이터 무결성을 개선하셨네요!
이 부분에서는 @Transactional을 추가함으로써, Refresh Token 삭제 시점에서 트랜잭션 처리로 원자성을 보장할 수 있습니다. 잘 하셨습니다.

gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/user/UserGateway.kt (1)

36-36: deleteById 호출 시 데이터 무결성 보장을 위한 검증 고려
직접 deleteById를 호출하기 전에 사용자가 실제 존재하는지, 이미 탈퇴 처리된 상태는 아닌지 등을 확인하는 로직이 있으면 예상치 못한 예외 상황을 줄이는 데 도움이 됩니다.

gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/user/UserJpaRepository.kt (2)

6-6: ID 타입의 간소화로 인한 가독성 향상 축하드립니다!
보다 직관적인 Long 타입을 사용하셔서, 엔티티 조회 로직이 간결해졌습니다.


8-8: 새로운 메서드로 중복 사용자 판별이 빨라졌네요!
existsByPlatformAndPlatformId 메서드를 통해 소셜 플랫폼 식별 과정을 더욱 효율적으로 처리할 수 있습니다.

support/logging/build.gradle.kts (1)

8-8: BootJar 비활성화가 의도된 설정인지 확인이 필요해 보입니다.
현재 BootJar를 비활성화하면 이 모듈은 일반 JAR만 생성하게 됩니다. 서비스 실행 형태에 맞게 설정이 올바른지 재확인 부탁드립니다.

settings.gradle.kts (1)

10-10: 새로운 로깅 모듈 추가가 성공적으로 반영되었습니다.
프로젝트 구조 확장에 잘 맞는 변경이며, 다른 모듈에서 유용하게 활용될 것으로 보입니다.

presentation/api/build.gradle.kts (1)

10-10: 로깅 모듈 의존성 추가로 인한 구조 개선이 기대됩니다.
프로젝트(":support:logging")를 통해 통합 로깅 기능을 사용할 수 있게 되어, API 모듈 전반의 로그 가시성이 향상될 것입니다.

domain/src/main/kotlin/org/doorip/domain/repository/UserRepository.kt (1)

8-8: 메서드 반환 타입 변경으로 인한 널 처리 주의 필요

이제 create 메서드가 null을 반환할 가능성이 생겼으므로, 실제 호출부에서 널 처리 로직이 적절히 구현되었는지 확인해보세요.

아래 스크립트를 통해 해당 메서드가 사용되는 부분을 검색하고, 널 처리가 잘 되어 있는지 점검하시길 권장드립니다.

✅ Verification successful

널 처리가 적절히 구현되어 있습니다 🎉

AuthServicesignUp 메서드에서 userRepository.create()의 반환값에 대해 엘비스 연산자(?:)를 사용하여 널 처리를 잘 구현해두셨네요! 널이 반환되는 경우 AlreadyExistingUserException을 던지도록 되어 있어 안전하게 처리되고 있습니다.

val user = userRepository.create(
    platformId = platformId,
    platform = authPlatform,
    name = name,
    intro = intro,
) ?: throw AlreadyExistingUserException
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: UserRepository.create 호출부 점검
rg -A 5 'create('

Length of output: 84


Script:

#!/bin/bash
# Description: UserRepository.create 메서드 호출부 검색
rg -p "userRepository.*create\(" --type kotlin

Length of output: 150


Script:

#!/bin/bash
# Description: create 메서드가 포함된 파일의 전체 내용 확인
fd "UserRepository.kt" --type f --exec cat {}

Length of output: 431


Script:

#!/bin/bash
# Description: create 메서드 구현부 검색
ast-grep --pattern 'fun create($$$): User? {
  $$$
}'

Length of output: 55


Script:

#!/bin/bash
# Description: create 메서드를 호출하는 서비스 레이어 검색
rg -p "UserRepository.*create" --type kotlin -A 5

Length of output: 51


Script:

#!/bin/bash
# Description: AuthService에서 create 메서드 호출부 상세 확인
rg -p -B 5 -A 10 "userRepository\.create\(" core/src/main/kotlin/org/doorip/core/auth/AuthService.kt

Length of output: 707

gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/BaseJpaEntity.kt (1)

11-12: 주석 및 구성 어노테이션이 적절해 보입니다.
@MappedSuperclass@EntityListeners(AuditingEntityListener::class)의 조합을 사용해 엔티티 감사 기능을 활성화한 점이 훌륭합니다. 다른 엔티티와의 상호 작용 시 혼선이 없도록, 영속성 컨텍스트에서 제대로 동작하는지 테스트도 함께 진행하면 좋겠습니다.

gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/token/RefreshTokenJpaEntity.kt (1)

9-9: BaseJpaEntity를 올바르게 가져왔습니다.
추가된 import org.doorip.gateway.rdb.BaseJpaEntity 덕분에 클래스 계층 구조가 명확해졌습니다.

gateway/rdb/src/main/kotlin/org/doorip/gateway/rdb/user/UserJpaEntity.kt (1)

14-14: BaseJpaEntity 상속 확인
import org.doorip.gateway.rdb.BaseJpaEntity 추가로 인해 UserJpaEntity에서 감사 기능을 활용할 수 있게 되었습니다. 도메인 계층에서 시간 정보를 어떻게 활용할지 검토해 보세요.

buildSrc/src/main/kotlin/Versions.kt (1)

10-10: OpenFeign 버전 호환성 확인 권장
현재 4.1.0 버전을 사용 중인데, 함께 도입된 Spring Boot 버전과의 호환성이 문제 없는지 확인이 필요해 보입니다.

gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/AppleAuthService.kt (3)

5-5: AppleAuthService 컴포넌트 선언이 잘 보입니다!

애플 OAuth와 관련된 로직을 별도의 컴포넌트로 만들어 관리하는 접근은 훌륭합니다.


6-9: 생성자 주입을 활용한 구조가 깔끔합니다.

ApplePublicKeyGeneratorAppleIdTokenValidator를 통해 역할이 명확히 분리되어 재사용성도 높아 보입니다.


11-14: 메서드의 책임이 명확합니다.

getPlatformId 내부에서 공개 키 생성 후 검증 과정을 투명하게 노출해주어, 인증 로직 흐름을 외부에서 쉽게 파악할 수 있습니다.

gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/kakao/KakaoFeignClient.kt (1)

8-12: 카카오 API 연동 방식이 잘 구성되었습니다.

FeignClient로 카카오의 액세스 토큰 정보를 가져오는 구조가 단순 명료하여, 유지보수 시에도 직관적으로 이해하기 좋습니다.

gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/AuthGateway.kt (2)

10-13: 플랫폼별 서비스 의존성 주입이 깔끔합니다.

Apple과 Kakao 관련 인증 서비스를 직접 의존 주입받아 확장성과 가독성을 동시에 확보했습니다.


15-17: when 절을 사용한 플랫폼 분기처리가 매끄럽습니다.

플랫폼에 따라 각기 다른 인증 로직을 호출하므로, 동작이 명확하고 유연합니다. 현재로서는 추가 플랫폼 확장 시에도 쉽게 대응할 수 있어 보입니다.

gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/kakao/KakaoAuthService.kt (1)

7-7: 컴포넌트 분리로 가시성이 높아졌습니다.

KakaoAuthService를 따로 두어 카카오 관련 로직을 전담 처리하므로, 유지보수가 편리할 것으로 예상됩니다.

gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/apple/AppleIdTokenValidator.kt (2)

9-9: 안녕하세요! 코드를 잘 살펴보니, @Component 애노테이션을 통해 스프링 빈으로 관리하려는 목적이 명확해 보입니다. 현재로서는 별다른 문제는 없어 보이며, DI 구성을 통해 쉽게 확장 가능하다는 점이 좋아요.


19-26: 검증 로직이 간결하게 잘 작성되어 있어 좋습니다. jwtProvider.validateAndGetSubject 메서드에서 널이 반환되면 바로 예외를 던지도록 처리한 점이 명확하고 깔끔해 보이네요.

gateway/oauth/src/main/resources/application-oauth.yml (1)

1-4: 애플 OAuth 설정이 명확하게 분리되어 있어서 유지보수가 용이해 보입니다. 환경 변수 ${APPLE_CLIENT_ID}를 사용하는 방식도 운영 환경마다 설정을 쉽게 바꿀 수 있어서 좋네요.

gateway/oauth/src/main/kotlin/org/doorip/gateway/oauth/kakao/dto/KakaoAccessTokenInfo.kt (1)

3-5: 카카오 액세스 토큰 정보를 담는 DTO가 간결하고 명확하게 구성되었습니다. id 프로퍼티만 있으니 추후 요구사항이 변경되어도 쉽게 확장할 수 있겠네요.

gateway/oauth/build.gradle.kts (1)

9-11: 의존성 추가 확인이 필요해요 🔍

JWT와 OpenFeign 의존성이 잘 추가되었네요! 다만, 소셜 로그인 구현을 위해 몇 가지 확인이 필요합니다:

  1. JWT 모듈에 토큰 검증을 위한 보안 기능이 모두 포함되어 있는지 확인해주세요.
  2. OpenFeign 버전(4.1.0)의 보안 취약점은 없는지 확인이 필요합니다.

JWT 구현을 확인하기 위해 다음 스크립트를 실행해볼까요?

✅ Verification successful

JWT와 OpenFeign 의존성이 안전하게 구현되어 있어요! ✨

코드를 자세히 살펴보니 JWT 모듈이 잘 구현되어 있네요:

  • JwtProvider에서 토큰 생성, 검증, 파싱 등 필수적인 보안 기능을 모두 제공하고 있어요
  • validateAndGetSubject 메소드로 토큰 유효성 검증이 이루어지고 있어요
  • 암호화 키 관리도 java.security 패키지를 활용해 안전하게 처리되고 있어요
  • OpenFeign 4.1.0 버전은 현재 알려진 주요 보안 취약점이 없는 안정적인 버전이에요

이대로 진행하셔도 좋을 것 같아요! 소셜 로그인 구현에 필요한 보안 기능들이 모두 잘 갖춰져 있습니다. 😊

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify JWT implementation in support:jwt module

# Check JWT security implementations
echo "Checking JWT security implementations..."
rg -t kotlin "verify|validate|decode" "support/jwt/src"

# Check for common security patterns
echo "Checking security patterns..."
ast-grep --pattern 'class $_ implements TokenValidator { $$$ }'

Length of output: 1485


Script:

#!/bin/bash
# Check JWT implementation details
echo "Checking JWT implementation details..."
rg -t kotlin "class JwtProvider" -A 10 "support/jwt/src"

# Check OpenFeign version
echo "Checking OpenFeign version..."
rg "OPEN_FEIGN" -A 1 "buildSrc"

# Check security configurations
echo "Checking security configurations..."
rg -t kotlin "security|auth" "support/jwt/src"

Length of output: 2318

core/src/main/kotlin/org/doorip/core/auth/AuthService.kt (9)

4-4: 이미 존재하는 사용자 예외 적극 활용 기대
이미 존재하는 사용자를 처리하기 위해 예외 클래스를 임포트한 부분이네요. 예외 처리가 더욱 명확해질 것으로 보입니다.


9-9: UserInfo 임포트로 사용자·토큰 함께 관리
UserInfo 엔티티를 새로 추가하여 사용자 정보와 토큰을 한 번에 처리하는 구조가 명확해졌습니다.


23-23: AuthUseCase 구현으로 확장성 향상
AuthUseCase 인터페이스를 구현함으로써 서비스 레이어가 인터페이스 기반으로 잘 분리되었습니다.


Line range hint 25-40: signIn: 인증 로직과 예외 처리가 명확
signIn 메서드에서 토큰으로 사용자 플랫폼 정보를 조회하고, 예외가 발생하면 즉시 처리를 중단하는 흐름이 깔끔합니다. 인증 성공 시 UserInfo를 반환해, 사용자 정보와 토큰을 한 번에 다루는 점이 좋아 보입니다.


44-44: signUp: 인터페이스 준수로 일관된 구조
signUp 메서드가 AuthUseCase의 계약대로 구현되어 있어 유지보수에 용이합니다.


53-53: 중복 가입 방지 로직 강화
사용자가 이미 존재하는 경우 예외를 던지도록 하여, 중복 가입을 명확히 차단하는 점이 인상적입니다.


58-58: signOut 시 Refresh 토큰 삭제
로그아웃 시점에 Refresh 토큰을 제거해 보안적 리스크를 줄이는 구현이 합리적입니다.


62-62: reissue: 간결한 토큰 재발급 로직
Refresh 토큰으로 사용자 ID를 찾고 새 토큰을 발급하는 과정이 깔끔해 이해하기 쉽습니다.


69-69: withdraw: 일관성 있는 탈퇴 처리
탈퇴 시 Refresh 토큰까지 삭제하여 데이터 상태를 일관성 있게 유지하는 점이 돋보입니다.

presentation/api/src/main/kotlin/org/doorip/api/auth/AuthController.kt (7)

1-19: 신규 AuthController의 적절한 의존성 구성
필요한 DTO와 UseCase를 임포트해, 컨트롤러 구성요소들을 이해하기 쉽게 가져왔습니다.


20-23: 생성자로 AuthUseCase 주입
AuthController가 AuthUseCase 의존성을 주입받아 사용하는 구조가 충분히 명료하며, 확장성에도 도움이 됩니다.


25-36: signIn: 사용자 인증 구현이 직관적
Authorization 헤더로 토큰을 받고, RequestBody에서 플랫폼 정보를 받은 뒤 인증 로직을 호출하는 흐름이 잘 정리되어 있습니다.


38-51: signUp: 예외 처리와 계정 생성을 명확히 구분
AlreadyExistingUserException을 통해 중복 사용자 확인 로직을 명료하게 드러냈고, 성공 시에는 ApiResponse.created를 사용해 RESTful한 응답을 제공합니다.


53-61: signOut: Refresh 토큰 정리로 보안성을 향상
PATCH 메서드로 로그아웃 행위를 표현했으며, refresh 토큰을 제거함으로써 사용자의 연결을 완전히 끊도록 한 점이 좋습니다.


63-71: reissue: 만료된 토큰 교체에 집중된 메서드
Authorization 헤더를 통해 refreshToken을 수신하고, 새 토큰 발급 과정을 간결하게 처리하는 부분이 잘 보입니다.


73-81: withdraw: 인증 기반 계정 삭제
NeedAuthentication으로 인증된 사용자만 탈퇴를 실행하도록 제한하고, 탈퇴 후 ApiResponse.ok()를 반환하여 클라이언트에 성공을 명확히 전달합니다.

presentation/api/src/main/kotlin/org/doorip/api/auth/dto/AuthSignInRequest.kt (1)

3-5: AuthSignInRequest로 플랫폼 식별
platform 필드를 통해 클라이언트가 어떤 소셜 플랫폼에서 로그인 요청하는지 명확히 알 수 있습니다.

domain/src/main/kotlin/org/doorip/domain/entity/User.kt (1)

15-18: UserInfo로 사용자·토큰 동시 관리
UserInfo 클래스를 추가하여, 사용자 정보와 토큰을 하나의 객체로 묶음으로써 인증 로직이 더욱 간편해질 것으로 기대됩니다.

presentation/api/src/main/kotlin/org/doorip/api/auth/dto/AuthResponse.kt (2)

5-9: AuthResponse에 핵심 인증 정보 일원화
accessToken, refreshToken, userId를 모아 응답 형식을 표준화함으로써 API 사용성을 높였습니다.


11-15: Token.toResponse() 확장 함수로 변환 로직 단순화
Token 객체를 DTO로 쉽게 변환하여, 중복 코드 없이 응답 구조를 재사용할 수 있도록 한 점이 인상적입니다.

core/src/main/kotlin/org/doorip/core/auth/AuthUseCase.kt (1)

7-13: 회원 인증 로직을 한눈에 보여주는 인터페이스입니다.

전반적인 메서드 구성이 깔끔하며, 로그인·회원가입·로그아웃 등의 로직을 명확히 분리해 이해하기 쉽습니다. 다만, 예외 상황(토큰 검증 실패 등)에 대한 처리 과정을 문서로 명시하면 유지보수에 도움이 될 것 같아요.

presentation/api/src/main/kotlin/org/doorip/api/auth/dto/AuthSignInResponse.kt (1)

12-17: 확장 함수로 변환 작업을 캡슐화한 점이 좋습니다.

UserInfo.toResponse()를 사용해 DTO 변환 과정을 단순화한 설계가 눈에 띕니다. 차후 확장 기능이 필요할 때도 유연하게 대응할 수 있어 보여요.

presentation/api/src/main/kotlin/org/doorip/api/exception/ExceptionResponseFactory.kt (1)

44-44: 중복 회원 예외를 명확히 구분하고 있습니다.

AlreadyExistingUserExceptionCONFLICT 상태로 매핑하는 결정이 적절합니다. 사용자에게 이미 가입된 회원임을 명확히 알릴 수 있어 좋아요.

domain/src/main/kotlin/org/doorip/domain/DooripException.kt (1)

21-21: 회원가입 충돌 사례를 명시적으로 처리합니다.

AlreadyExistingUserException으로 회원 중복을 구분해, 에러 처리를 세분화한 점이 인상적입니다. 서버 응답을 이해하기 쉽게 만들고, 문제 상황을 명확히 설명하는 데 도움이 될 듯합니다.

presentation/api/src/main/kotlin/org/doorip/api/exception/ApiExceptionHandler.kt (3)

8-8: 로거 지원 기능 추가 확인 완료
이제 로거를 활용해 예외 정보를 효과적으로 추적할 수 있으므로, 에러 상황에 대한 디버깅 생산성이 높아질 것으로 기대됩니다.


24-24: 로거 인스턴스 생성 방식 적절
현재 logger() 확장 함수를 통한 로거 생성은 가독성이 좋고, 다른 클래스와도 일관성 있게 사용할 수 있어 보입니다.


58-58: 중대한 예외 로깅에 적절한 error 레벨 사용
심각한 예외(DooripException 및 알 수 없는 예외)에 대해 logger.error 레벨로 처리하여, 오류 발생 시점에 대한 빠른 인식이 가능해졌습니다. 유의미한 예외 정보가 추가로 있다면 메시지 등에 포함시키는 것도 추천드립니다.

Also applies to: 64-64

@@ -48,6 +49,7 @@ internal class RefreshTokenGateway(
return encoder.encodeToString(savedRefreshToken.refreshToken)
}

@Transactional
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

삭제 로직의 안정성 검토
deleteRefreshToken 메서드가 트랜잭션 범위 내에서 실행되므로, 중간에 예외가 발생해도 일관된 데이터 상태를 유지할 수 있습니다. 단, deleteByUserId 실행 후 에러가 발생할 가능성을 염두에 두고 적절한 예외 처리를 고려해보시는 것이 좋겠습니다. 필요하다면 코틀린 runCatching { ... } 등을 사용해 예외 상황을 처리하거나 롤백 로직을 명시적으로 다룰 수도 있습니다.

Comment on lines +6 to +8
@EnableJpaAuditing
@Configuration
internal class JpaConfig
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

JPA Auditing 설정에 대한 제안

JPA Auditing 기본 설정이 잘 되어있네요! 😊 하지만 더 견고한 설정을 위해 몇 가지 제안드립니다:

  1. 감사(Auditing) 정보를 더 세밀하게 관리하기 위해 AuditorAware 구현을 추가하면 좋을 것 같아요.
  2. 클래스에 간단한 KDoc 문서를 추가하면 다른 개발자분들이 이해하기 쉬울 것 같습니다.

아래와 같이 개선해보는 건 어떨까요?

 @EnableJpaAuditing
 @Configuration
-internal class JpaConfig
+/**
+ * JPA Auditing을 위한 설정 클래스입니다.
+ * 엔티티의 생성일시와 수정일시를 자동으로 관리합니다.
+ */
+internal class JpaConfig {
+    @Bean
+    fun auditorProvider(): AuditorAware<String> {
+        return AuditorAware { Optional.of("SYSTEM") }
+    }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@EnableJpaAuditing
@Configuration
internal class JpaConfig
@EnableJpaAuditing
@Configuration
/**
* JPA Auditing을 위한 설정 클래스입니다.
* 엔티티의 생성일시와 수정일시를 자동으로 관리합니다.
*/
internal class JpaConfig {
@Bean
fun auditorProvider(): AuditorAware<String> {
return AuditorAware { Optional.of("SYSTEM") }
}
}

Comment on lines +14 to +15
override fun create(platformId: String, platform: AuthPlatform, name: String, intro: String): User? {
if (userJpaRepository.existsByPlatformAndPlatformId(platform, platformId)) return null
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

유저 중복 시 null 반환에 대한 좀 더 명시적인 처리 제안
현재 로직은 이미 존재하는 유저에 대해서 null을 반환하고 있으나, 호출 측에서는 해당 결과값을 확인해야 하며 의도가 모호해질 수 있습니다. 예외 발생이나 Result 타입과 같은 보다 명시적인 통신 방식을 도입하는 방안을 고려해보세요.

import org.slf4j.Logger
import org.slf4j.LoggerFactory

inline fun <reified T> T.logger(): Logger = LoggerFactory.getLogger(T::class.java)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

로거 확장 함수에 대한 간단한 주석 추가를 고려해주세요.
이 함수는 클래스별 로거 주입을 간편하게 해주어 코드 가독성이 높아집니다. 다만, 함수 사용 의도를 간단한 KDoc 형태로 명시하면 협업 시 이해도가 더 높아질 것 같습니다.

Comment on lines +14 to +16
@CreatedDate
@Column(name = "created_at", updatable = false, nullable = false)
var createdDate: LocalDateTime = LocalDateTime.now()
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

생성일 기본값 표현에 대한 주의사항
LocalDateTime.now()를 기본값으로 설정하면 애플리케이션이 실행되고 있는 서버의 로컬 시간대를 사용합니다. 여러 서버에서 동작할 경우 시간이 달라질 수 있으므로, UTC 일관성을 유지하거나 OffsetDateTime/ZonedDateTime 등으로 처리하는 방안을 고려해 보세요.

Comment on lines +29 to +35
private fun getApplePublicKeys(): ApplePublicKeys {
try {
return httpClient.getApplePublicKeys()
} catch (e: FeignException) {
throw UnauthenticatedException
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

예외를 잡은 뒤 throw UnauthenticatedException으로 다시 단순화 처리하고 있는데, Feign 수준에서 어떤 문제가 발생했는지 로깅을 남기거나, 예외 메시지를 조금 상세히 전달하면 문제 파악이 한결 쉬워질 수 있을 것 같아요.

🧰 Tools
🪛 detekt (1.23.7)

[warning] 32-32: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

Comment on lines 6 to 8
@EnableFeignClients(basePackages = ["org.doorip"])
@Configuration
internal class OpenFeignConfig
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Feign 클라이언트 설정 개선을 제안드립니다 😊

현재 설정은 기본적인 기능만 제공하고 있어요. 실제 운영 환경에서는 다음과 같은 추가 설정들이 필요할 것 같습니다:

  1. basePackages 범위가 너무 넓어요. 필요한 패키지만 지정하는 것이 좋습니다.
  2. 타임아웃 설정이 없어요.
  3. 에러 처리와 로깅 설정이 필요해요.

다음과 같이 개선하는 것은 어떨까요?

 @EnableFeignClients(basePackages = ["org.doorip"])
 @Configuration
-internal class OpenFeignConfig
+internal class OpenFeignConfig {
+    @Bean
+    fun feignLoggerLevel(): Logger.Level = Logger.Level.FULL
+
+    @Bean
+    fun errorDecoder(): ErrorDecoder = CustomErrorDecoder()
+
+    @Bean
+    fun requestInterceptor(): RequestInterceptor =
+        RequestInterceptor { template ->
+            template.header("Accept", "application/json")
+        }
+}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +5 to +10
data class AuthSignInResponse(
val accessToken: String,
val refreshToken: String,
val isResult: Boolean,
val userId: Long,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

응답 객체 구조가 직관적입니다.

accessToken, refreshToken, userId 등 필요한 정보를 고루 담고 있어, 클라이언트 측 처리에도 용이해 보입니다. 다만, isResult는 현재 용도를 조금 더 명확히 설명해줄 이름(예: isSuccess)이면 어떨까 하는 제안이 있습니다.


@ExceptionHandler(MethodArgumentTypeMismatchException::class)
protected fun handleException(ex: MethodArgumentTypeMismatchException): ExceptionResponseEntity {
logger.warn(ex.message, ex)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

예외 로깅 레벨에 대한 고려
warn 레벨로 예외 메시지를 기록하는 것은 유효하지만, 일부 경우에는 사용자 요청 파라미터에 대한 문제가 일정 수준 반복될 수 있으므로 info 레벨로 분리하는 방안도 검토해 볼 수 있습니다. 상황과 예외 유형별로 로깅 레벨을 다르게 설정하면 운영 시점에서 좀 더 빠른 의사결정이 가능합니다.

Also applies to: 34-34, 40-40, 46-46, 52-52

Comment on lines +3 to +7
data class AuthSignUpRequest(
val name: String,
val intro: String,
val platform: String,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

회원 가입 요청 DTO의 명확한 구조
AuthSignUpRequest가 회원 가입 시 필요한 정보를 한눈에 알 수 있게 잘 구성되어 있어 가독성이 뛰어납니다. 다만, 유효성 검증을 위해 nameintro의 길이나 platform 값의 범위를 제한하는 기능도 고려해 보시면 좋겠습니다.

@SunwoongH SunwoongH merged commit 11057c4 into develop Dec 31, 2024
2 checks passed
@SunwoongH SunwoongH deleted the feature/14 branch December 31, 2024 09:24
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