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

[YS-31] feat: 구글 OAuth 로그인 구현 #13

Merged
merged 21 commits into from
Jan 3, 2025
Merged

[YS-31] feat: 구글 OAuth 로그인 구현 #13

merged 21 commits into from
Jan 3, 2025

Conversation

chock-cho
Copy link
Contributor

@chock-cho chock-cho commented Dec 30, 2024

💡 작업 내용

  • 구글 OAuth 로그인 구현

✅ 셀프 체크리스트

  • PR 제목을 형식에 맞게 작성했나요?
  • 브랜치 전략에 맞는 브랜치에 PR을 올리고 있나요?
  • 테스트는 잘 통과했나요?
  • 빌드에 성공했나요?
  • 본인을 assign 해주세요.
  • 해당 PR에 맞는 label을 붙여주세요.

🙋🏻‍ 확인해주세요

localtest - Swagger UI에서 테스트한 모습입니다.

1. 자물쇠 아이콘의 Authorization 버튼에서 'OAuth' 인증을 누릅니다.

  • 알맞은 client_id, client_secret 키를 입력하고, Scopes에서 'Select All' 을 눌러주고, Authorize 버튼을 누릅니다.
    image

2. 리다이렉팅된 Google 계정으로 로그인을 해줍니다. 로그인 성공 시에는 다음과 같은 화면이 뜹니다.
image

3. 이때의 Flow에서 authorizationCode를 발급받습니다.

4. POST /v1/auth/oauth/login 로 요청을 보내면 response로 accessToken과 refreshToken, 로그인 성공한 유저 정보를 포함한 인증 정보가 반환됩니다.
image

image

🔗 Jira 티켓


https://yappsocks.atlassian.net/browse/YS-31

- 프로젝트의 핵심 데이터를 표현하는 엔티티 클래스 정의
- 표준화를 위한 enum 클래스 구현
- 프로젝트 확장성과 명확성을 위한 기본 도메인 설계 완료
- `AuditingEntityListener` 추가
- 기존: 리스트 순차 탐색 ➡️  Map의 key값으로 탐색 개선
- 패키지명 `enums` ➡️  `enum` 으로 변경
- `AuditingEntity` 변경 내용 추가
- `Member` 의존성 알맞게 주입
- Google OAuth 로그인 구현
- 추후 회원가입 로직의 OAuth 정보 받아오는 과정 고려하여, 도메인 클래스
  일부 수정
- `.env` 파일 로드를 위한 의존성 파일 추가
@chock-cho chock-cho self-assigned this Dec 30, 2024
@chock-cho chock-cho added ✨ FEATURE 기능 추가 🔒 HOLD 홀드 labels Dec 30, 2024
@github-actions github-actions bot changed the title [Feat/YS-31] 구글 OAuth 로그인 구현 [YS-31] [Feat/YS-31] 구글 OAuth 로그인 구현 Dec 30, 2024
@chock-cho chock-cho changed the title [YS-31] [Feat/YS-31] 구글 OAuth 로그인 구현 [YS-31] 구글 OAuth 로그인 구현 Dec 30, 2024
@Ji-soo708 Ji-soo708 changed the title [YS-31] 구글 OAuth 로그인 구현 [YS-31] feat: 구글 OAuth 로그인 구현 Dec 31, 2024
- 에러 수정을 위해 빌드 파일에 `dotenv` 라이브러리 추가
- 테스트 중 적절한 빈 구성 보장을 위해 `@ActiveProfiles("test")` 추가
- `SecurityFilterChain` 관련 Application Context 로딩 오류 해결
- 구글 oauth 로그인 구현
- 구글 oauth token, email, name 정보 가져오도록 설정
principal

- Google API 호출 로직을 `FetchGoogleUserInfoUseCase`로 이동하여 책임 분리
  개선
- `OauthService` 를 수정하여 사용자 정보 조회를 새로운 UseCase로 위임
- 구글 OAuth 로직에 대한 test code 작성: `OauthServiceTest`
  `FetchGoogleUserInfoTest`
- `AuditingEntityTest` 에 대한 test code 작성

- JaCoco 커버리지 테스트에서 AuditingEntityTest 커버리지 0.5로 목표치
  미달 → 추후 테스트 커버리지 개선 필요
- `OauthMapperTest` 테스트 코드 추가
- `MemberServiceTest` 테스트 코드 추가

- `AuditingEntityTest` → 테스트 커버리지 50%로 목표치 70% 달성 실패,
  추후 개선 필요
@chock-cho chock-cho removed the 🔒 HOLD 홀드 label Jan 2, 2025
@chock-cho chock-cho requested a review from Ji-soo708 January 2, 2025 08:45
@chock-cho chock-cho marked this pull request as ready for review January 2, 2025 08:46
@chock-cho chock-cho added the ⚙️ CHORE config, workflow.yaml label Jan 2, 2025
Copy link
Member

@Ji-soo708 Ji-soo708 left a comment

Choose a reason for hiding this comment

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

전체적으로 잘 구현하셨고 테스트도 정말 꼼꼼하게 해주셨습니다! 👏 다만 몇 가지 개선할 부분이나 논의하고 싶은 부분을 제안드리고 싶어 Request Changes합니다.

1. 서비스 계층 추상화에 대해
클린 아키텍처의 핵심 원칙 중 하나인 DIP을 지키기 위해서는 Service/Repository 계층을 추상화하는 것이 더 바람직하다고 생각합니다. Service 인터페이스를 정의하고, 실제 구현체는 infrastructure 계층에서 구현해 application 계층이 infrastructure 계층에 의존하지 않도록 하는 것이 더 적합하다고 생각하는데 어떻게 생각하시나요?
물론, 이 방식은 코드의 유연성과 유지보수성을 향상시킬 수 있지만, 파일이 많아지고 구조가 복잡해지는 단점이 있고 저도 공감합니다. 그래서 이 부분은 지금 수정님과 협의하여 균형을 찾아가면 좋겠습니다. ☺️

2. OAuth 로그인 후 토큰 발급 방식
코드를 봤을 때 제가 이해하기로는 사용자가 OAuth 로그인을 하면, OAuth 토큰을 발급받는 것으로 이해했습니다. 혹시 맞을까요?
맞다면 제 생각에는 OAuth 로그인 시, OAuth 토큰을 그대로 주는 것보다는 정상적인 사용자가 로그인하면 바로 accessToken과 refreshToken을 발급하는 방식이 더 직관적이고 효율적일 것 같습니다.
이렇게 하면 사용자는 한 번의 요청으로 필요한 모든 정보를 받을 수 있고, 프론트엔드에서도 보다 간편하게 처리할 수 있습니다. 별도의 추가 API 호출 없이 로그인 후 즉시 토큰을 발급해주는 것이 흐름을 간소화할 수 있다고 생각하는데 수정님은 어떻게 생각하는지 궁금합니다!

3. 클린 아키텍처에서의 Service 역할
개인적으로 UseCase와 Service의 역할을 명확히 구분하기가 어렵더라고요. 수정님은 이번 PR에서 Service를 구현하셨는데, 어떻게 UseCase와 Service의 목적과 역할을 구분하셨는지 의견을 듣고 싶습니다!

전체적으로 잘 구현하셔서 작은 조정들만 고려하면 좋을 거 같습니다! 고생하셨습니다. 👏

Comment on lines 30 to 42
@Column(name = "role", nullable = true)
@Enumerated(EnumType.STRING)
val role: RoleType,
val role: RoleType?,

@Column(name = "contact_email", length = 100, nullable = false)
val contactEmail : String,
@Column(name = "contact_email", length = 100, nullable = true)
val contactEmail : String?,

@Column(name = "name", length = 10, nullable = false)
val name : String,
@Column(name = "name", length = 10, nullable = true)
val name : String?,

@Column(name = "birth_date", nullable = false)
val birthDate : LocalDate,
@Column(name = "birth_date", nullable = true)
val birthDate : LocalDate?,
) : AuditingEntity()
Copy link
Member

Choose a reason for hiding this comment

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

기존 컬럼 규칙이 바뀌면 dev DB의 테이블도 이에 맞게 업데이트되어야 할 것 같습니다. 현재 DB에 데이터가 쌓이지 않은 상태라, 머지 전에 기존 테이블(Member, Participant, Researcher을 drop한 후 새로 업데이트된 테이블 정보로 create하면 문제없이 진행될 것 같아요! 😊

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
Copy link
Member

Choose a reason for hiding this comment

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

사소한 부분이지만, JpaRepository를 상속받으면 자동으로 빈으로 등록되기 때문에 @Repository를 생략해도 괜찮습니다.

개인적으로는 @Repository를 추가하는 것이 중복된 선언이라 생각해 생략하는 편인데, 명시적으로 표현하려는 의도라면 추가하는 것도 좋다고 생각해요. 서로 컨벤션을 맞춰야 할 거 같은데 혹시 수정님은 @Repository를 붙이시는 이유가 명시성을 위해서일까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

명시성을 위해서였는데, @Repository 를 추가하는 것이 중복된 선언이라는 지수님 의견이 일부 동의해서 반영하겠습니다☺️

Comment on lines 32 to 58
Components()
.addSecuritySchemes(
"JWT 토큰",
SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("Bearer")
.bearerFormat("JWT")
)
.addSecuritySchemes(
"Google OAuth2 토큰",
SecurityScheme()
.type(SecurityScheme.Type.OAUTH2)
.flows(
OAuthFlows()
.authorizationCode(
OAuthFlow()
.authorizationUrl("https://accounts.google.com/o/oauth2/auth")
.tokenUrl("https://oauth2.googleapis.com/token")
.scopes(
Scopes()
.addString("email", "Access your email address")
.addString("profile", "Access your profile information")
)
)
)
)
)
Copy link
Member

Choose a reason for hiding this comment

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

val googleAuthUrl = "https://accounts.google.com/o/oauth2/auth"
val googleTokenUrl = "https://oauth2.googleapis.com/token"

val jwtScheme = SecurityScheme()
    .type(SecurityScheme.Type.HTTP)
    .scheme("Bearer")
    .bearerFormat("JWT")

val googleOAuthScheme = SecurityScheme()
    .type(SecurityScheme.Type.OAUTH2)
    .flows(
        OAuthFlows().authorizationCode(
            OAuthFlow()
                .authorizationUrl(googleAuthUrl)
                .tokenUrl(googleTokenUrl)
                .scopes(
                    Scopes()
                        .addString("email", "Access your email address")
                        .addString("profile", "Access your profile information")
                )
        )
    )

@Bean
fun openAPI(): OpenAPI = OpenAPI()
    .addSecurityItem(SecurityRequirement().addList("JWT 토큰").addList("Google OAuth2 토큰"))
    .components(
        Components()
            .addSecuritySchemes("JWT 토큰", jwtScheme)
            .addSecuritySchemes("Google OAuth2 토큰", googleOAuthScheme)
    )

중첩된 부분을 위와 같이 별도 변수로 나누면 가독성이 좋아질 거 같습니다! 혹시 어떠신가요??

Comment on lines 38 to 42
// val oauthUserDto = OauthUserDto(email = email, name = name, provider = ProviderType.GOOGLE)
// val member = memberService.login(oauthUserDto)
//
// val newAuthentication = AuthenticationUtils.createAuthentication(member)
// SecurityContextHolder.getContext().authentication = newAuthentication
Copy link
Member

Choose a reason for hiding this comment

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

이 주석 부분은 나중에 사용하기 위한 부분일까요??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

넵 맞습니다 !

Comment on lines 17 to 19
class OauthController(
private val jwtTokenProvider: JwtTokenProvider,
private val oauthService: OauthService
Copy link
Member

Choose a reason for hiding this comment

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

/v1/auth에서는 OAuth 로그인뿐만 아니라 리프레시 토큰을 이용한 액세스 토큰 갱신 API도 추가되어야 해서, AuthController가 더 적합할 것 같습니다!

Comment on lines 6 to 19
@Schema(description = "OAuth 로그인 결과 사용자 정보 DTO")
data class OauthTokenResponse(
@Schema(description = "OAuth 토큰")
val jwtToken: String,

@Schema(description = "OAuth 이메일")
val email: String,

@Schema(description = "OAuth 이름")
val name: String,

@Schema(description = "OAuth 제공자 (GOOGLE, NAVER)")
val provider: ProviderType
)
Copy link
Member

Choose a reason for hiding this comment

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

혹시 OAuth 로그인한 사용자에게 응답으로 바로 TokenProvider를 이용해 accessToken과 refreshToken을 발급하는 방식은 어떨까요?

사용자가 로그인을 한 후, 즉시 accessToken과 refreshToken을 발급받으면 별도로 추가 API를 호출할 필요가 없어서 흐름이 간단하고 직관적일 것 같아요. 한 번의 요청으로 필요한 정보가 모두 제공되므로, 프론트엔드에서도 쉽게 처리할 수 있을 거라 생각합니다. #16의 액세스 토큰 재발급 API처럼요! 😊

Copy link
Member

Choose a reason for hiding this comment

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

public class OAuthTokenResponse {

    private String accessToken; // jwt access-token
    private String refreshToken; // jwt refresh-token
    private String email;
    private Boolean isRegistered; // 회원가입 여부
}

이전 프로젝트에서는 OAuth 로그인을 호출하면 이렇게 응답을 제공했습니다!

Comment on lines 22 to 41
security:
oauth2:
client:
registration:
google:
redirect-uri: "http://localhost:8080/login/oauth2/code/google"
authorization-grant-type: authorization_code
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
scope:
- email
- profile
provider:
google:
authorization-uri: https://accounts.google.com/o/oauth2/auth
token-uri: https://oauth2.googleapis.com/token
user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
springdoc:
swagger-ui:
oauth2-redirect-url: http://localhost:8080/swagger-ui/oauth2-redirect.html
Copy link
Member

Choose a reason for hiding this comment

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

해당 PR이 머지되기 전에 꼭 DEV_YML 내용을 갱신해주세요~

Comment on lines 12 to 23
@Service
class MemberService(
private val memberRepository: MemberRepository,
private val jwtTokenProvider: JwtTokenProvider
) {
@Transactional
fun login(oauthUserDto: OauthUserDto): Member{
val member= memberRepository.findByOauthEmailAndStatus(oauthUserDto.email, MemberStatus.ACTIVE)
?: throw MemberNotFoundException()
return member
}
}
Copy link
Member

Choose a reason for hiding this comment

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

제가 이해한 바로는, 클린 아키텍처에서 중요한 DIP을 지키기 위해서는 서비스 계층을 추상화하는 것이 올바른 접근이라고 생각합니다.
Service 인터페이스를 정의하고, 실제 구현체는 infrastructure 계층에서 구현하도록 하여 application 계층이 infrastructure에 의존하지 않도록 해야 합니다. 다만, 이렇게 구현하면 파일이 많아지는 단점이 있습니다.

따라서, 다음 두 가지 방식 중에서 어떤 것이 더 적합할지 고민해볼 필요가 있을 거 같습니다.

  1. 클린 아키텍처 원칙에 따라 추상화를 철저히 한다.
  2. 융통성 있게 상황에 맞게 조정한다.

수정님은 어떤 방향이 더 낫다고 생각하나요?

Copy link
Member

Choose a reason for hiding this comment

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

1/2 서버 회의에서는 추상화를 철저히 하지만, 이번 주 주말 해커톤까지는 MVP를 빠르게 개발하는 것이 우선이므로, 추후 리팩터링을 통해 개선하기로 결정했습니다.

- Google OAuth 연동을 위한 `GoogleAuthFeignClient` 추가
- 사용자 정보를 가져오는 `GoogleUserInfoFeignClient` 구현
- GoogleTokenRequest 및 GoogleTokenResponse DTO 정의
- OauthLoginResponse에 사용자 정보 및 accessToken, refreshToken 포함
- 기존 WebClientConfig 삭제 → FeignClient로 대체
- Google OAuth 로그인 시도 시 발생할 수 있는 예외처리
- Application에 FeignClient 설정
- 중복된 선언: `@Repository` 어노테이션 제거
- SwaggerConfig 가독성 개선
@chock-cho chock-cho requested a review from Ji-soo708 January 2, 2025 22:13
Copy link
Member

@Ji-soo708 Ji-soo708 left a comment

Choose a reason for hiding this comment

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

제안해드린 로직대로 수정하면서 많은 변경사항이 있었을 텐데, 정말 잘 구현하신 것 같아요! 👏 개인적으로 FeignClient가 HTTP 클라이언트 호출을 추상화해줘서 DIP 원칙을 따르기 좋다고 생각해요. 저희 프로젝트가 클린 아키텍처라면 FeignClient와 잘 어울릴 거라고 봅니다!

추가로 코멘트를 남길 부분이 있어서 확인 부탁드려요~

build.gradle.kts Outdated
Comment on lines 41 to 44
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("com.github.f4b6a3:ulid-creator:5.2.3")
implementation("org.mariadb.jdbc:mariadb-java-client:2.7.3")
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
Copy link
Member

Choose a reason for hiding this comment

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

같은 dependency가 두 번 추가된 것 같습니다! 그리고 이제 FeignClient 라이브러리를 사용해서 구현할 예정인데, 이 라이브러리도 필요할지 궁금합니다.

return fetchGoogleUserInfoUseCase.execute(oauthLoginRequest)
}

}
Copy link
Member

Choose a reason for hiding this comment

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

엔터 라인 한 번 쳐주세요~

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영하겠습니다!
앞으로 작업 전에 EOF 문제 있는지까지 꼼꼼히 처리해야겠네요!


val userInfo = googleUserInfoFeginClient.getUserInfo("Bearer $oauthToken")
val email = userInfo.email as? String?: throw OAuth2EmailNotFoundException()
val name = userInfo.name as? String?: throw OAuth2NameNotFoundException()
Copy link
Member

Choose a reason for hiding this comment

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

현재 로직에서 name이 필요하지 않으며, 미래에도 활용될 가능성이 낮다고 판단되면 해당 코드를 제거해도 좋을 거 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영하겠습니다~

import com.dobby.backend.infrastructure.database.entity.enum.RoleType
import org.springframework.http.HttpStatus

class LoginRoleMismatchException(
Copy link
Member

Choose a reason for hiding this comment

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

저는 #16 에서 Login 대신 SignIn을 사용했습니다! 이름 통일성을 위해 회원가입을 SignUp으로 지칭한다면, 로그인도 SignIn으로 네이밍하는 것이 더 좋다고 판단했습니다. 혹시 수정님도 회원가입을 SignUp으로 한다면, 로그인도 SignIn으로 통일하는 게 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

저는 항상 로그인 로직을 Login 으로 네이밍해왔었는데요, 회원가입이 SignUp 이라면 SignIn 으로 네이밍하는 편이 좋다는 지수님 의견에 저도 동의합니다!
반영하겠습니다.

it.anyRequest().permitAll()
println("[DEBUG] authSecurityFilterChain triggered")
it.requestMatchers("/v1/auth/oauth/**").permitAll()
it.anyRequest().authenticated()
Copy link
Member

Choose a reason for hiding this comment

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

혹시 이 부분이 /v1/auth/oauth/**에 해당하는 요청에 대해서만 인증 없이 접근을 허용한다는 코드일까요?

그렇다면 추후 추가될 기능 을 위해서 /v1/auth/** 로 하면 더 좋을 거 같습니다! 👍

Comment on lines +71 to 83
@Bean
@Order(2)
fun swaggerSecurityFilterChain(httpSecurity: HttpSecurity): SecurityFilterChain = httpSecurity
.securityMatcher("/swagger-ui/**", "/v3/api-docs/**")
.csrf { it.disable() }
.cors(Customizer.withDefaults())
.authorizeHttpRequests {
it.requestMatchers(
"/swagger-ui/**",
"/v3/api-docs/**"
).permitAll()
}
.build()
Copy link
Member

Choose a reason for hiding this comment

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

Swagger 추가하면서 이 부분 고려하는 걸 놓쳤는데 추가해주셔서 감사합니다 ☺️

Comment on lines 1 to 4
package com.dobby.backend.presentation.api.controller

class AuthController {
}
Copy link
Member

Choose a reason for hiding this comment

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

해당 클래스 내용이 비어있는 것으로 보입니다. 혹시 OAuthController의 이름을 AuthController로 변경하려다가 관련된 코드 업데이트를 놓치신 걸까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

네 정확합니다 😅 사실 이번 브랜치 이후에 작업들을 하면서 작업 내용이 일부 혼재가 되어있어 (이후에 참여자 회원가입 로직 등등...) 작업 내용을 stash 하고 커밋 내용이 반영되는 파일들을 걸러내는 과정 중에 놓친 것 같습니다!
좋은 지적 감사합니다~🖐️

Comment on lines 14 to 22
@Tag(name = "OAuth 로그인 API")
@RestController
@RequestMapping("/v1/auth")
class OauthController(
private val oauthService: OauthService
) {

@PostMapping("/oauth/login/google")
@Operation(summary = "Google OAuth 로그인 API", description = "Google OAuth 로그인 후 인증 정보를 반환합니다")
Copy link
Member

Choose a reason for hiding this comment

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

지금 URI가 세부적으로 나누어져 있어 좋은 점이 있지만, 제가 생각할 때 authoauth가 중복된다는 느낌이 있습니다.

저희 서비스에서는 OAuth 로그인만 제공하므로 URI에서 oauth를 제외하고 /v1/auth/google 또는 /v1/auth/google/login처럼 표현해도 충분히 API의 역할을 잘 전달할 수 있을 거라 생각하는데, 혹시 어떠신가요??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

해당 내용 반영했습니다!

- 빌드 파일에 중복된 의존성 제거
- `OAuthController` 제거 → `AuthController` 로 통일
- 파일 끝에 EOF 추가
@chock-cho chock-cho requested a review from Ji-soo708 January 3, 2025 03:17
Copy link
Member

@Ji-soo708 Ji-soo708 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다! 전반적으로 잘 구현되었다고 생각해 approve하고 merge합니다! 👏

@Ji-soo708 Ji-soo708 merged commit dc4d52e into dev Jan 3, 2025
3 checks passed
@Ji-soo708 Ji-soo708 deleted the feat/YS-31 branch January 3, 2025 04:55
Ji-soo708 pushed a commit that referenced this pull request Jan 3, 2025
* feat: define domain models and enums for the project

- 프로젝트의 핵심 데이터를 표현하는 엔티티 클래스 정의
- 표준화를 위한 enum 클래스 구현
- 프로젝트 확장성과 명확성을 위한 기본 도메인 설계 완료

* refact: update domain structures for refactoring

- `AuditingEntityListener` 추가
- 기존: 리스트 순차 탐색 ➡️  Map의 key값으로 탐색 개선
- 패키지명 `enums` ➡️  `enum` 으로 변경

* fix: fixing some errors(Member, AuditingEntity)

- `AuditingEntity` 변경 내용 추가
- `Member` 의존성 알맞게 주입

* feat: 구글 OAuth 로그인 구현

- Google OAuth 로그인 구현
- 추후 회원가입 로직의 OAuth 정보 받아오는 과정 고려하여, 도메인 클래스
  일부 수정
- `.env` 파일 로드를 위한 의존성 파일 추가

* fix: add dotenv libraries

- 에러 수정을 위해 빌드 파일에 `dotenv` 라이브러리 추가

* fix: delete import which occurs dependency error

* fix: resolve context loading issue with active test profile

- 테스트 중 적절한 빈 구성 보장을 위해 `@ActiveProfiles("test")` 추가
- `SecurityFilterChain` 관련 Application Context 로딩 오류 해결

* feat: implement google oauth login

- 구글 oauth 로그인 구현
- 구글 oauth token, email, name 정보 가져오도록 설정

* refact: seperate responsibilities according to Clean Architecture
principal

- Google API 호출 로직을 `FetchGoogleUserInfoUseCase`로 이동하여 책임 분리
  개선
- `OauthService` 를 수정하여 사용자 정보 조회를 새로운 UseCase로 위임

* test: test code for Google OAuth login logic, AuditingEntity

- 구글 OAuth 로직에 대한 test code 작성: `OauthServiceTest`
  `FetchGoogleUserInfoTest`
- `AuditingEntityTest` 에 대한 test code 작성

- JaCoco 커버리지 테스트에서 AuditingEntityTest 커버리지 0.5로 목표치
  미달 → 추후 테스트 커버리지 개선 필요

* test: add OauthMapperTest and MemberServiceTest for refact test coverage

- `OauthMapperTest` 테스트 코드 추가
- `MemberServiceTest` 테스트 코드 추가

- `AuditingEntityTest` → 테스트 커버리지 50%로 목표치 70% 달성 실패,
  추후 개선 필요

* feat : add Google OAuth integration

- Google OAuth 연동을 위한 `GoogleAuthFeignClient` 추가
- 사용자 정보를 가져오는 `GoogleUserInfoFeignClient` 구현
- GoogleTokenRequest 및 GoogleTokenResponse DTO 정의
- OauthLoginResponse에 사용자 정보 및 accessToken, refreshToken 포함
- 기존 WebClientConfig 삭제 → FeignClient로 대체

* fix: add dependency code and custom exception code

- Google OAuth 로그인 시도 시 발생할 수 있는 예외처리
- Application에 FeignClient 설정

* fix: adjust things to build safely

* refact: update test codes to satisfy the newly requirement

* refact: reflect pr reviews and update changes

- 중복된 선언: `@Repository` 어노테이션 제거
- SwaggerConfig 가독성 개선

* refact: reflect code from reviews

- 빌드 파일에 중복된 의존성 제거
- `OAuthController` 제거 → `AuthController` 로 통일
- 파일 끝에 EOF 추가
chock-cho added a commit that referenced this pull request Jan 3, 2025
* style: add line for EOF

* chore: update springdoc version

* feat: add generateTestTokenForTestMember method for test

* feat: add UseCase interface

* feat: add GenerateTestToken api for test member

* test: change memberId type from String to Long in test code

* chore: add mock dependency for test

* test: add GenerateTestToken test code

* test: refactor JwtTokenProviderTest to use Kotest style

* style: rename dto name

* feat: add @componentscan to include UseCase beans in application context

* refact: refactor TokenProvider code

* feat: add dto for SignIn logic

* feat: add GenerateTokenWithRefreshToken

* test: add GenerateTokenWithRefreshToken test code

* refact: rename entity

* refact: refactor member domain

* test: fix test due to changed domain

* feat: add MemberGateway

* feat: add GetMemberById usecase

* refact: update MemberRefreshToken response

* style: add line for eof

* style: update role example

* [YS-31] feat: 구글 OAuth 로그인 구현 (#13)

* feat: define domain models and enums for the project

- 프로젝트의 핵심 데이터를 표현하는 엔티티 클래스 정의
- 표준화를 위한 enum 클래스 구현
- 프로젝트 확장성과 명확성을 위한 기본 도메인 설계 완료

* refact: update domain structures for refactoring

- `AuditingEntityListener` 추가
- 기존: 리스트 순차 탐색 ➡️  Map의 key값으로 탐색 개선
- 패키지명 `enums` ➡️  `enum` 으로 변경

* fix: fixing some errors(Member, AuditingEntity)

- `AuditingEntity` 변경 내용 추가
- `Member` 의존성 알맞게 주입

* feat: 구글 OAuth 로그인 구현

- Google OAuth 로그인 구현
- 추후 회원가입 로직의 OAuth 정보 받아오는 과정 고려하여, 도메인 클래스
  일부 수정
- `.env` 파일 로드를 위한 의존성 파일 추가

* fix: add dotenv libraries

- 에러 수정을 위해 빌드 파일에 `dotenv` 라이브러리 추가

* fix: delete import which occurs dependency error

* fix: resolve context loading issue with active test profile

- 테스트 중 적절한 빈 구성 보장을 위해 `@ActiveProfiles("test")` 추가
- `SecurityFilterChain` 관련 Application Context 로딩 오류 해결

* feat: implement google oauth login

- 구글 oauth 로그인 구현
- 구글 oauth token, email, name 정보 가져오도록 설정

* refact: seperate responsibilities according to Clean Architecture
principal

- Google API 호출 로직을 `FetchGoogleUserInfoUseCase`로 이동하여 책임 분리
  개선
- `OauthService` 를 수정하여 사용자 정보 조회를 새로운 UseCase로 위임

* test: test code for Google OAuth login logic, AuditingEntity

- 구글 OAuth 로직에 대한 test code 작성: `OauthServiceTest`
  `FetchGoogleUserInfoTest`
- `AuditingEntityTest` 에 대한 test code 작성

- JaCoco 커버리지 테스트에서 AuditingEntityTest 커버리지 0.5로 목표치
  미달 → 추후 테스트 커버리지 개선 필요

* test: add OauthMapperTest and MemberServiceTest for refact test coverage

- `OauthMapperTest` 테스트 코드 추가
- `MemberServiceTest` 테스트 코드 추가

- `AuditingEntityTest` → 테스트 커버리지 50%로 목표치 70% 달성 실패,
  추후 개선 필요

* feat : add Google OAuth integration

- Google OAuth 연동을 위한 `GoogleAuthFeignClient` 추가
- 사용자 정보를 가져오는 `GoogleUserInfoFeignClient` 구현
- GoogleTokenRequest 및 GoogleTokenResponse DTO 정의
- OauthLoginResponse에 사용자 정보 및 accessToken, refreshToken 포함
- 기존 WebClientConfig 삭제 → FeignClient로 대체

* fix: add dependency code and custom exception code

- Google OAuth 로그인 시도 시 발생할 수 있는 예외처리
- Application에 FeignClient 설정

* fix: adjust things to build safely

* refact: update test codes to satisfy the newly requirement

* refact: reflect pr reviews and update changes

- 중복된 선언: `@Repository` 어노테이션 제거
- SwaggerConfig 가독성 개선

* refact: reflect code from reviews

- 빌드 파일에 중복된 의존성 제거
- `OAuthController` 제거 → `AuthController` 로 통일
- 파일 끝에 EOF 추가

* feat: add GenerateTestToken api for test member

* test: refactor JwtTokenProviderTest to use Kotest style

* feat: add @componentscan to include UseCase beans in application context

* feat: add GenerateTokenWithRefreshToken

* refact: rename entity

* test: fix test due to changed domain

* refact: update MemberRefreshToken response

* fix: fix conflicts for merge

* refact: move usecase file to application

* refact: refactor OAuthLoginResponse response structure

* fix: fix conflicts for merge

---------

Co-authored-by: Sujung Shin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⚙️ CHORE config, workflow.yaml ✨ FEATURE 기능 추가
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants