Skip to content

Commit

Permalink
fix: get kakao user info
Browse files Browse the repository at this point in the history
  • Loading branch information
DongGeon0908 committed Jul 30, 2024
1 parent feb4b82 commit 8f8520c
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 25 deletions.
22 changes: 22 additions & 0 deletions src/main/kotlin/com/hero/alignlab/client/kakao/KakaoInfoService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.hero.alignlab.client.kakao

import com.hero.alignlab.client.kakao.client.KaKaoInfoClient
import com.hero.alignlab.client.kakao.model.response.KakaoOAuthUserInfoResponse
import com.hero.alignlab.common.extension.resolveCancellation
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Service

@Service
class KakaoInfoService(
private val kaKaoInfoClient: KaKaoInfoClient,
) {
private val logger = KotlinLogging.logger { }

suspend fun getUserInfo(accessToken: String): KakaoOAuthUserInfoResponse {
return runCatching {
kaKaoInfoClient.getUserInfo(accessToken)
}.onFailure { e ->
logger.resolveCancellation("getUserInfo", e)
}.getOrThrow()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.hero.alignlab.client.kakao.client

import com.hero.alignlab.client.kakao.model.response.KakaoOAuthUserInfoResponse

interface KaKaoInfoClient {
suspend fun getUserInfo(accessToken: String): KakaoOAuthUserInfoResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.hero.alignlab.client.kakao.client

import com.hero.alignlab.client.kakao.SuspendableClient
import com.hero.alignlab.client.kakao.model.response.KakaoOAuthUserInfoResponse
import org.springframework.web.reactive.function.client.WebClient

class SuspendableKakaoInfoClient(client: WebClient) : KaKaoInfoClient, SuspendableClient(client) {
override suspend fun getUserInfo(accessToken: String): KakaoOAuthUserInfoResponse {
return client.get()
.uri("/v2/user/me")
.header("Authorization", "Bearer $accessToken")
.request()
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package com.hero.alignlab.client.kakao.client

import com.hero.alignlab.client.kakao.SuspendableClient
import com.hero.alignlab.client.kakao.config.KakaoOAuthClientConfig
import com.hero.alignlab.client.kakao.model.request.GenerateKakaoOAuthTokenRequest
import com.hero.alignlab.client.kakao.model.response.GenerateKakaoOAuthTokenResponse
import org.springframework.web.reactive.function.client.WebClient

class SuspendableKakaoOAuthClient(
client: WebClient,
private val config: KakaoOAuthClientConfig.Config,
) : KaKaoOAuthClient, SuspendableClient(client) {
class SuspendableKakaoOAuthClient(client: WebClient) : KaKaoOAuthClient, SuspendableClient(client) {
override suspend fun getOAuthAuthorizeCode(clientId: String, redirectUri: String) {
client
.get()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.hero.alignlab.client.kakao.config

import com.hero.alignlab.client.WebClientFactory
import com.hero.alignlab.client.kakao.client.KaKaoInfoClient
import com.hero.alignlab.client.kakao.client.KaKaoOAuthClient
import com.hero.alignlab.client.kakao.client.SuspendableKakaoInfoClient
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.validation.Valid
import jakarta.validation.constraints.NotBlank
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.validation.annotation.Validated

@Validated
@Configuration
class KakaoInfoClientConfig {
private val logger = KotlinLogging.logger { }

@Bean
@ConditionalOnProperty(prefix = "client.kakao-info", name = ["url"])
@ConfigurationProperties(prefix = "client.kakao-info")
fun kakaoInfoConfig() = Config()

@Bean
@ConditionalOnBean(name = ["kakaoInfoConfig"])
@ConditionalOnMissingBean(KaKaoOAuthClient::class)
fun kakaoInfoClient(
@Valid kakaoInfoConfig: Config
): KaKaoInfoClient {
logger.info { "initialized KaKaoInfoClient. $kakaoInfoConfig" }

val webclient = WebClientFactory.generate(kakaoInfoConfig.url)

return SuspendableKakaoInfoClient(webclient)
}

data class Config(
@field:NotBlank
var url: String = "",
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class KakaoOAuthClientConfig {

val webclient = WebClientFactory.generate(kakaoOAuthConfig.url)

return SuspendableKakaoOAuthClient(webclient, kakaoOAuthConfig)
return SuspendableKakaoOAuthClient(webclient)
}

data class Config(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.hero.alignlab.client.kakao.model.response

import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.annotation.JsonNaming
import java.time.LocalDateTime

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class KakaoOAuthUserInfoResponse(
val id: String,
val hasSignedUp: Boolean?,
val connectedAt: LocalDateTime?,
val synchedAt: LocalDateTime?,
val kakaoAccount: KakaoAccount?,
) {
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class KakaoAccount(
val profileNeedsAgreement: Boolean?,
val profile: Profile?,
val nameNeedsAgreement: Boolean?,
val name: String?,
val emailNeedsAgreement: Boolean?,
val isEmailValid: Boolean?,
val isEmailVerified: Boolean?,
val email: String?,
val ageRangeNeedsAgreement: Boolean?,
val ageRange: String?,
val birthyearNeedsAgreement: Boolean?,
val birthyear: String?,
val birthdayNeedsAgreement: Boolean?,
val birthday: String?,
val birthdayType: String?,
val genderNeedsAgreement: Boolean?,
val gender: String?,
val phoneNumberNeedsAgreement: Boolean?,
val phoneNumber: String?,
val ciNeedsAgreement: Boolean?,
val ci: String?,
val ciAuthenticatedAt: LocalDateTime?,
)

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class Profile(
val nickname: String?,
val thumbnailImageUrl: String?,
val profileImageUrl: String?,
val isDefaultImage: Boolean?,
val isDefaultNickname: Boolean?,
)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package com.hero.alignlab.domain.auth.resource

import com.hero.alignlab.common.extension.wrapOk
import com.hero.alignlab.domain.auth.application.OAuthFacade
import com.hero.alignlab.domain.auth.model.OAuthProvider
import com.hero.alignlab.domain.auth.model.request.OAuthAuthorizedRequest
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.*
Expand All @@ -18,20 +14,5 @@ import org.springframework.web.bind.annotation.*
class OAuthResource(
private val oAuthFacade: OAuthFacade,
) {
/**
* OAuth로 인가코드를 부여받고, 이를 Redirect Url로 반환.
* - redirectUrl은 Client의 주소값, 만약 변경시 yml 및 각 클라이언트 구조 변경 필요.
*/
@Operation(summary = "인가 코드 받기")
@PostMapping("/api/v1/oauth/{provider}/authorize")
suspend fun getOAuthAuthorizeCode(
@PathVariable provider: OAuthProvider,
) = oAuthFacade.getOAuthAuthorizeCode(provider).wrapOk()

@Operation(summary = "회원가입 또는 로그인 진행")
@PostMapping("/api/v1/oauth/{provider}/authorized")
suspend fun resolveOAuth(
@PathVariable provider: OAuthProvider,
@RequestBody request: OAuthAuthorizedRequest,
) = oAuthFacade.resolveOAuth(provider, request).wrapOk()
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.hero.alignlab.domain.dev.application

import com.hero.alignlab.client.kakao.KakaoInfoService
import com.hero.alignlab.client.kakao.KakaoOAuthService
import com.hero.alignlab.client.kakao.model.response.GenerateKakaoOAuthTokenResponse
import com.hero.alignlab.client.kakao.model.response.KakaoOAuthUserInfoResponse
import com.hero.alignlab.domain.auth.model.OAuthProvider
import com.hero.alignlab.domain.dev.model.response.DevOAuthCodeResponse
import io.github.oshai.kotlinlogging.KotlinLogging
Expand All @@ -10,6 +12,7 @@ import org.springframework.stereotype.Service
@Service
class DevOAuthService(
private val kakaoOAuthService: KakaoOAuthService,
private val kakaoInfoService: KakaoInfoService
) {
private val logger = KotlinLogging.logger { }

Expand All @@ -26,4 +29,8 @@ class DevOAuthService(
OAuthProvider.kakao -> kakaoOAuthService.generateOAuthToken(code)
}
}

suspend fun getUserInfo(accessToken: String): KakaoOAuthUserInfoResponse {
return kakaoInfoService.getUserInfo(accessToken)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,10 @@ class DevOAuthResource(
@PathVariable provider: OAuthProvider,
@RequestParam code: String,
) = devOAuthService.resolveOAuth(provider, code).wrapOk()

@Operation(summary = "사용자 정보 조회")
@GetMapping("/api/dev/v1/oauth/user")
suspend fun getOAuthUserInfos(
@RequestParam accessToken: String
) = devOAuthService.getUserInfo(accessToken).wrapOk()
}
4 changes: 4 additions & 0 deletions src/main/resources/config/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ oauth:
authorized-url: /authorize?client_id=%s&redirect_uri=%s&response_type=code
redirect-url: https://api.alignlab.site/api/dev/v1/oauth/kakao/token

client:
kakao-info:
url: https://kapi.kakao.com

encrypt:
key:
algorithm:
Expand Down

0 comments on commit 8f8520c

Please sign in to comment.