Skip to content

Commit

Permalink
feat: 이름, 프로필 이미지 추가 (#25)
Browse files Browse the repository at this point in the history
* feat: member name profile image 추가

* test: 빌드 실패 해결
  • Loading branch information
junhaesung authored Dec 25, 2023
1 parent b6913cb commit 6239486
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@ class LoginApplicationService(
private val providerUserIdServices: List<ProviderUserIdService>,
private val memberService: MemberService,
private val tokenService: TokenService<Long>,
private val providerUserNameService: ProviderUserNameService,
private val providerUserProfileImageService: ProviderUserProfileImageService,
) {
fun login(
loginRequestVo: LoginRequestVo,
): LoginResponseVo {
val authenticatedProviderIdentifier = resolveLoginService(loginRequestVo).getProviderUserId(loginRequestVo)
val member = (memberService.findByProviderIdentifier(authenticatedProviderIdentifier)
?: memberService.create(authenticatedProviderIdentifier))
member.name?.run {
member.name = providerUserNameService.getProviderUserName(loginRequestVo)
}
member.profileImageUrl?.run {
member.profileImageUrl = providerUserProfileImageService.getProviderUserProfileImage(loginRequestVo)
}
return LoginResponseVo(
memberVo = MemberVo.from(member),
accessToken = tokenService.encode(member.memberId),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.poseplz.server.application.auth

interface ProviderUserNameService {
fun getProviderUserName(loginRequestVo: LoginRequestVo): String?
fun supports(loginRequestVo: LoginRequestVo): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.poseplz.server.application.auth

interface ProviderUserProfileImageService {
fun getProviderUserProfileImage(loginRequestVo: LoginRequestVo): String?
fun supports(loginRequestVo: LoginRequestVo): Boolean
}
2 changes: 2 additions & 0 deletions src/main/kotlin/com/poseplz/server/domain/member/Member.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class Member(
strategy = "com.poseplz.server.infrastructure.hibernate.SnowflakeIdentifierGenerator",
)
val memberId: Long = 0L,
var name: String? = null,
var profileImageUrl: String? = null,
@Enumerated(EnumType.STRING)
var status: MemberStatus,
@Embedded
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.poseplz.server.infrastructure.kakao

import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component
import org.springframework.web.client.RestClientException
import org.springframework.web.client.RestTemplate
import org.springframework.web.util.UriComponentsBuilder

@Component
class KakaoAdminApiClient(
@Qualifier("kakaoAdminRestTemplate")
private val kakaoAdminRestTemplate: RestTemplate,
) {
fun getUserInfo(
kakaoUserId: String,
): KakaoUserMeResponse {

return try {
val responseEntity = kakaoAdminRestTemplate.getForEntity(
UriComponentsBuilder.fromHttpUrl("https://kapi.kakao.com/v1/user/me")
.queryParam("target_id_type", "user_id")
.queryParam("target_id", kakaoUserId)
.build()
.toUri(),
KakaoUserMeResponse::class.java,
)
responseEntity.body!!
} catch (e: RestClientException) {
throw KakaoApiFailedException("카카오 사용자 정보 가져오기 API 호출에 실패했습니다.", e)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.springframework.web.client.RestClientException
import org.springframework.web.client.RestTemplate
import org.springframework.web.util.UriComponentsBuilder
import java.net.URI
import java.time.ZonedDateTime


@Component
Expand All @@ -21,9 +22,12 @@ class KakaoApiClient(
* 카카오 사용자 정보 가져오기
* @see "https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info"
*/
fun getKakaoUserId(kakaoAccessToken: String): String {
fun getKakaoUserInfo(kakaoAccessToken: String): KakaoUserMeResponse {
if (kakaoAccessToken == "kakaoAccessToken") {
return "kakaoUserId"
return KakaoUserMeResponse(
id = 0L,
connectedAt = ZonedDateTime.now(),
)
}
val url: URI = UriComponentsBuilder.fromHttpUrl("https://kapi.kakao.com/v2/user/me")
.build()
Expand All @@ -33,9 +37,9 @@ class KakaoApiClient(
headerMap["Authorization"] = listOf("Bearer $kakaoAccessToken")
val responseEntity = kakaoRestTemplate.exchange(
RequestEntity<Any?>(headerMap, HttpMethod.GET, url),
KakaoUserMeDto::class.java
KakaoUserMeResponse::class.java
)
responseEntity.body!!.id
responseEntity.body!!
} catch (e: RestClientException) {
throw KakaoApiFailedException("카카오 사용자 정보 가져오기 API 호출에 실패했습니다.", e)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
package com.poseplz.server.infrastructure.kakao

import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.client.ClientHttpRequestInterceptor
import org.springframework.web.client.RestTemplate
import java.time.Duration

@Configuration
class KakaoConfig {
@Value("\${poseplz.kakao.api.admin-key}")
lateinit var kakaoAdminSdkKey: String

@Bean("kakaoRestTemplate")
fun kakaoRestTemplate(): RestTemplate {
return RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(3))
.build()
}

@Bean("kakaoAdminRestTemplate")
fun kakaoAdminRestTemplate(): RestTemplate {
return RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(3))
.additionalInterceptors(kakaoAdminApiHeaderInterceptor())
.build()
}

@Bean
fun kakaoAdminApiHeaderInterceptor(): ClientHttpRequestInterceptor {
return ClientHttpRequestInterceptor { request, body, execution ->
request.headers.set("Authorization", "KakaoAK ${kakaoAdminSdkKey}")
request.headers.set("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
execution.execute(request, body)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ class KakaoUserIdService(
private val kakaoApiClient: KakaoApiClient,
) : ProviderUserIdService {
override fun getProviderUserId(loginRequestVo: LoginRequestVo): ProviderIdentifier {
val kakaoUserId = kakaoApiClient.getKakaoUserId(loginRequestVo.providerUserCredential!!)
val kakaoUserInfo = kakaoApiClient.getKakaoUserInfo(loginRequestVo.providerUserCredential!!)
return ProviderIdentifier(
providerType = ProviderType.KAKAO,
providerUserId = kakaoUserId,
providerUserId = kakaoUserInfo.id.toString(),
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.poseplz.server.infrastructure.kakao

import com.fasterxml.jackson.annotation.JsonProperty
import java.time.ZonedDateTime

data class KakaoUserMeResponse (
val id: Long,
@JsonProperty("connected_at")
val connectedAt: ZonedDateTime,
@JsonProperty("kakao_account")
val kakaoAccount: KakaoAccount? = null,
)

data class KakaoAccount (
val profile: KakaoProfile,
)

data class KakaoProfile (
@JsonProperty("nickname")
val nickName: String?,
@JsonProperty("thumbnail_image_url")
val thumbnailImageUrl: String?,
@JsonProperty("profile_image_url")
val profileImageUrl: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.poseplz.server.infrastructure.kakao

import com.poseplz.server.application.auth.LoginRequestVo
import com.poseplz.server.application.auth.ProviderUserNameService
import com.poseplz.server.domain.member.ProviderType
import org.springframework.stereotype.Service

@Service
class KakaoUserNameService(
private val kakaoApiClient: KakaoApiClient,
) : ProviderUserNameService {
override fun getProviderUserName(loginRequestVo: LoginRequestVo): String? {
return kakaoApiClient.getKakaoUserInfo(loginRequestVo.providerUserCredential!!)
.kakaoAccount
?.profile
?.nickName
}

override fun supports(loginRequestVo: LoginRequestVo) =
loginRequestVo.providerType == ProviderType.KAKAO
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.poseplz.server.infrastructure.kakao

import com.poseplz.server.application.auth.LoginRequestVo
import com.poseplz.server.application.auth.ProviderUserProfileImageService
import com.poseplz.server.domain.member.ProviderType
import org.springframework.stereotype.Service

@Service
class KakaoUserProfileImageService(
private val kakaoApiClient: KakaoApiClient,
) : ProviderUserProfileImageService {
override fun getProviderUserProfileImage(loginRequestVo: LoginRequestVo): String? {
return kakaoApiClient.getKakaoUserInfo(loginRequestVo.providerUserCredential!!)
.kakaoAccount
?.profile
?.profileImageUrl
}

override fun supports(loginRequestVo: LoginRequestVo) =
loginRequestVo.providerType == ProviderType.KAKAO
}
3 changes: 3 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ poseplz:
api:
client-id: ${NAVER_API_CLIENT_ID}
client-secret: ${NAVER_API_CLIENT_SECRET}
kakao:
api:
admin-key: ${KAKAO_API_ADMIN_KEY}
management:
endpoints:
enabled-by-default: false
Expand Down
3 changes: 3 additions & 0 deletions src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@ poseplz:
api:
client-id: clientId
client-secret: clientSecret
kakao:
api:
admin-key: adminKey
jwt:
secret-key: secretKey

0 comments on commit 6239486

Please sign in to comment.