Skip to content

Commit

Permalink
[feat #32] 닉네임 수정 API 구현 (#34)
Browse files Browse the repository at this point in the history
* feat : 닉네임 수정 유스케이스

* feat : 닉네임 수정 API

* feat : 닉네임 수정 DTO

* feat : 기존 코드 형식 수정
  • Loading branch information
dlswns2480 authored Jul 25, 2024
1 parent db85cd0 commit b39cc9d
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 37 deletions.
34 changes: 26 additions & 8 deletions adapters/in-web/src/main/kotlin/com/pokit/user/UserController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package com.pokit.user
import com.pokit.auth.config.ErrorOperation
import com.pokit.auth.model.PrincipalUser
import com.pokit.auth.model.toDomain
import com.pokit.user.dto.ApiSignUpRequest
import com.pokit.common.wrapper.ResponseWrapper.wrapOk
import com.pokit.user.dto.request.ApiSignUpRequest
import com.pokit.user.dto.request.ApiUpdateNicknameRequest
import com.pokit.user.dto.request.toDto
import com.pokit.user.dto.response.CheckDuplicateNicknameResponse
import com.pokit.user.dto.response.SignUpResponse
import com.pokit.user.dto.response.UserResponse
import com.pokit.user.dto.response.toResponse
import com.pokit.user.exception.UserErrorCode
import com.pokit.user.port.`in`.UserUseCase
import io.swagger.v3.oas.annotations.Operation
Expand All @@ -25,20 +29,34 @@ class UserController(
fun signUp(
@AuthenticationPrincipal principalUser: PrincipalUser,
@Valid @RequestBody request: ApiSignUpRequest
): ResponseEntity<SignUpResponse> {
): ResponseEntity<UserResponse> {
val user = principalUser.toDomain()
val signUpRequest = request.toSignUpRequest()
val response = userUseCase.signUp(user, signUpRequest)
return ResponseEntity.ok(response)
return userUseCase.signUp(user, request.toDto())
.toResponse()
.wrapOk()
}

@GetMapping("/duplicate/{nickname}")
@Operation(summary = "닉네임 중복 체크 API")
@ErrorOperation(UserErrorCode::class)
fun checkNickname(
@AuthenticationPrincipal principalUser: PrincipalUser,
@PathVariable("nickname") nickname: String
): ResponseEntity<CheckDuplicateNicknameResponse> {
return ResponseEntity.ok(userUseCase.checkDuplicateNickname(nickname))
return userUseCase.checkDuplicateNickname(nickname)
.toResponse()
.wrapOk()
}

@PutMapping("/nickname")
@Operation(summary = "닉네임 수정 API")
@ErrorOperation(UserErrorCode::class)
fun updateNickname(
@AuthenticationPrincipal principalUser: PrincipalUser,
@Valid @RequestBody request: ApiUpdateNicknameRequest
): ResponseEntity<UserResponse> {
val user = principalUser.toDomain()
return userUseCase.updateNickname(user, request.toDto())
.toResponse()
.wrapOk()
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.pokit.user.dto
package com.pokit.user.dto.request

import com.pokit.user.dto.request.SignUpRequest
import com.pokit.user.model.InterestType
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size
Expand All @@ -11,15 +10,12 @@ data class ApiSignUpRequest(
val nickName: String,
@Size(min = 1, max = 3, message = "최소 하나 이상, 세개 이하만 가능합니다.")
val interests: List<String>
) {
fun toSignUpRequest(): SignUpRequest {
val interestTypes = interests.map {
InterestType.of(it)
}
)

return SignUpRequest(
nickName = nickName,
interests = interestTypes
)
}
internal fun ApiSignUpRequest.toDto(): SignUpRequest {
val interestTypes = interests.map { InterestType.of(it) }
return SignUpRequest(
nickName = this.nickName,
interests = interestTypes
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.pokit.user.dto.request

import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size

data class ApiUpdateNicknameRequest(
@field:NotBlank(message = "닉네임은 필수값입니다.")
@field:Size(max = 10, message = "닉네임은 10자 이하만 가능합니다.")
val nickname: String
)

internal fun ApiUpdateNicknameRequest.toDto() = UpdateNicknameRequest(
nickname = this.nickname
)
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.pokit.user.port.`in`

import com.pokit.user.dto.request.SignUpRequest
import com.pokit.user.dto.response.CheckDuplicateNicknameResponse
import com.pokit.user.dto.response.SignUpResponse
import com.pokit.user.dto.request.UpdateNicknameRequest
import com.pokit.user.model.User

interface UserUseCase {
fun signUp(user: User, request: SignUpRequest): SignUpResponse
fun signUp(user: User, request: SignUpRequest): User

fun checkDuplicateNickname(nickname: String): CheckDuplicateNicknameResponse
fun checkDuplicateNickname(nickname: String): Boolean

fun updateNickname(user: User, request: UpdateNicknameRequest): User
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.pokit.user.port.service

import com.pokit.common.exception.ClientValidationException
import com.pokit.common.exception.NotFoundCustomException
import com.pokit.user.dto.request.SignUpRequest
import com.pokit.user.dto.response.CheckDuplicateNicknameResponse
import com.pokit.user.dto.response.SignUpResponse
import com.pokit.user.dto.request.UpdateNicknameRequest
import com.pokit.user.exception.UserErrorCode
import com.pokit.user.model.User
import com.pokit.user.port.`in`.UserUseCase
Expand All @@ -17,20 +17,32 @@ class UserService(
private val userPort: UserPort
) : UserUseCase {
@Transactional
override fun signUp(user: User, request: SignUpRequest): SignUpResponse {
override fun signUp(user: User, request: SignUpRequest): User {
user.modifyUser(
request.nickName,
)

val savedUser = userPort.register(user)
?: throw NotFoundCustomException(UserErrorCode.NOT_FOUND_USER)

return SignUpResponse(savedUser.id)
return savedUser
}

override fun checkDuplicateNickname(nickname: String)
: CheckDuplicateNicknameResponse {
val isDuplicate = userPort.checkByNickname(nickname)
return CheckDuplicateNicknameResponse(isDuplicate)
override fun checkDuplicateNickname(nickname: String): Boolean {
return userPort.checkByNickname(nickname)
}

@Transactional
override fun updateNickname(user: User, request: UpdateNicknameRequest): User {
val findUser = (userPort.loadById(user.id)
?: throw NotFoundCustomException(UserErrorCode.NOT_FOUND_USER))

val isDuplicate = userPort.checkByNickname(request.nickname)
if (isDuplicate) {
throw ClientValidationException(UserErrorCode.ALREADY_EXISTS_NICKNAME)
}

findUser.modifyUser(request.nickname)
return userPort.persist(findUser)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.pokit.user.port.service

import com.pokit.common.exception.ClientValidationException
import com.pokit.common.exception.NotFoundCustomException
import com.pokit.user.UserFixture
import com.pokit.user.dto.request.UpdateNicknameRequest
import com.pokit.user.model.User
import com.pokit.user.port.out.UserPort
import io.kotest.assertions.throwables.shouldThrow
Expand All @@ -25,8 +27,8 @@ class UserServiceTest : BehaviorSpec({
When("수정하려는 정보를 받으면") {
val response = userService.signUp(user, request)
Then("회원 정보가 수정되고 수정된 회원의 id가 반환된다.") {
response.userId shouldBe modifieUser.id
user.nickName shouldBe modifieUser.nickName
response.id shouldBe modifieUser.id
response.nickName shouldBe modifieUser.nickName
}
}

Expand All @@ -49,14 +51,43 @@ class UserServiceTest : BehaviorSpec({
When("해당 닉네임의 유저가 존재하지 않으면") {
val response = userService.checkDuplicateNickname(notDuplicateName)
Then("false가 반환된다") {
response.isDuplicate shouldBe false
response shouldBe false
}
}

When("해당 닉네임의 유저가 존재하면 ") {
val response = userService.checkDuplicateNickname(duplicateName)
Then("true가 반환된다.") {
response.isDuplicate shouldBe true
response shouldBe true
}
}
}

Given("닉네임을 수정할 때") {
val user = UserFixture.getUser()
val request = UpdateNicknameRequest(
nickname = "수정하려는 닉네임"
)
val invalidRequest = UpdateNicknameRequest(
nickname = "이미 사용중인 닉네임"
)

every { userPort.loadById(user.id) } returns user
every { userPort.checkByNickname(request.nickname) } returns false
every { userPort.checkByNickname(invalidRequest.nickname) } returns true
every { userPort.persist(any(User::class)) } returns user

When("사용 가능한 닉네임으로 수정하려 하면 ") {
val userResponse = userService.updateNickname(user, request)
Then("수정된 유저 도메인을 반환한다.") {
userResponse.nickName shouldBe request.nickname
}
}
When("이미 사용중인 닉네임으로 수정하려 하면") {
Then("예외가 발생한다.") {
shouldThrow<ClientValidationException> {
userService.updateNickname(user, invalidRequest)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.pokit.user.dto.request

data class UpdateNicknameRequest(
val nickname: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ package com.pokit.user.dto.response
data class CheckDuplicateNicknameResponse(
val isDuplicate: Boolean
)

fun Boolean.toResponse() = CheckDuplicateNicknameResponse(
isDuplicate = this
)
15 changes: 15 additions & 0 deletions domain/src/main/kotlin/com/pokit/user/dto/response/UserResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.pokit.user.dto.response

import com.pokit.user.model.User

data class UserResponse (
val id: Long,
val email: String,
val nickname: String
)

fun User.toResponse() = UserResponse(
id = this.id,
email = this.email,
nickname = this.nickName
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ enum class UserErrorCode(
) : ErrorCode {
INVALID_EMAIL("올바르지 않은 이메일 형식의 유저입니다.", "U_001"),
INVALID_INTEREST_TYPE("관심사가 잘못되었습니다.", "U_002"),
NOT_FOUND_USER("존재하지 않는 회원입니다.", "U_003")
NOT_FOUND_USER("존재하지 않는 회원입니다.", "U_003"),
ALREADY_EXISTS_NICKNAME("이미 존재하는 닉네임입니다.", "U_004")
}

0 comments on commit b39cc9d

Please sign in to comment.