generated from pricelees/issue-pr-template
[#22] 프론트엔드 React 전환 및 인증 API 수정 #23
@ -7,16 +7,16 @@ import roomescape.member.exception.MemberErrorCode
|
||||
import roomescape.member.exception.MemberException
|
||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository
|
||||
import roomescape.member.web.MemberRetrieveListResponse
|
||||
import roomescape.member.web.toRetrieveResponse
|
||||
import roomescape.member.infrastructure.persistence.Role
|
||||
import roomescape.member.web.*
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
class MemberService(
|
||||
private val memberRepository: MemberRepository
|
||||
private val memberRepository: MemberRepository
|
||||
) {
|
||||
fun findMembers(): MemberRetrieveListResponse = MemberRetrieveListResponse(
|
||||
members = memberRepository.findAll().map { it.toRetrieveResponse() }
|
||||
members = memberRepository.findAll().map { it.toRetrieveResponse() }
|
||||
)
|
||||
|
||||
fun findById(memberId: Long): MemberEntity = fetchOrThrow {
|
||||
@ -27,6 +27,21 @@ class MemberService(
|
||||
memberRepository.findByEmailAndPassword(email, password)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun create(request: SignupRequest): SignupResponse {
|
||||
memberRepository.findByEmail(request.email)?.let {
|
||||
throw MemberException(MemberErrorCode.DUPLICATE_EMAIL)
|
||||
}
|
||||
|
||||
val member = MemberEntity(
|
||||
name = request.name,
|
||||
email = request.email,
|
||||
password = request.password,
|
||||
role = Role.MEMBER
|
||||
)
|
||||
return memberRepository.save(member).toSignupResponse()
|
||||
}
|
||||
|
||||
private fun fetchOrThrow(block: () -> MemberEntity?): MemberEntity {
|
||||
return block() ?: throw MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
||||
}
|
||||
|
||||
@ -5,20 +5,33 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import roomescape.auth.web.support.Admin
|
||||
import roomescape.common.dto.response.CommonApiResponse
|
||||
import roomescape.member.web.MemberRetrieveListResponse
|
||||
import roomescape.member.web.SignupRequest
|
||||
import roomescape.member.web.SignupResponse
|
||||
|
||||
@Tag(name = "2. 회원 API", description = "회원 정보를 관리할 때 사용합니다.")
|
||||
interface MemberAPI {
|
||||
@Admin
|
||||
@Operation(summary = "모든 회원 조회", tags = ["관리자 로그인이 필요한 API"])
|
||||
@ApiResponses(
|
||||
ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "성공",
|
||||
useReturnTypeSchema = true
|
||||
)
|
||||
ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "성공",
|
||||
useReturnTypeSchema = true
|
||||
)
|
||||
)
|
||||
fun findMembers(): ResponseEntity<CommonApiResponse<MemberRetrieveListResponse>>
|
||||
|
||||
@Operation(summary = "회원 가입")
|
||||
@ApiResponses(
|
||||
ApiResponse(
|
||||
responseCode = "201",
|
||||
description = "성공",
|
||||
useReturnTypeSchema = true
|
||||
)
|
||||
)
|
||||
fun signup(@RequestBody request: SignupRequest): ResponseEntity<CommonApiResponse<SignupResponse>>
|
||||
}
|
||||
|
||||
@ -8,5 +8,6 @@ enum class MemberErrorCode(
|
||||
override val errorCode: String,
|
||||
override val message: String
|
||||
) : ErrorCode {
|
||||
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "M001", "회원을 찾을 수 없어요.")
|
||||
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "M001", "회원을 찾을 수 없어요."),
|
||||
DUPLICATE_EMAIL(HttpStatus.CONFLICT, "M002", "이미 가입된 이메일이에요.")
|
||||
}
|
||||
|
||||
@ -4,4 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository
|
||||
|
||||
interface MemberRepository : JpaRepository<MemberEntity, Long> {
|
||||
fun findByEmailAndPassword(email: String, password: String): MemberEntity?
|
||||
|
||||
fun findByEmail(email: String): MemberEntity?
|
||||
}
|
||||
|
||||
@ -2,16 +2,26 @@ package roomescape.member.web
|
||||
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import roomescape.common.dto.response.CommonApiResponse
|
||||
import roomescape.member.business.MemberService
|
||||
import roomescape.member.docs.MemberAPI
|
||||
import java.net.URI
|
||||
|
||||
@RestController
|
||||
class MemberController(
|
||||
private val memberService: MemberService
|
||||
private val memberService: MemberService
|
||||
) : MemberAPI {
|
||||
|
||||
@PostMapping("/members")
|
||||
override fun signup(@RequestBody request: SignupRequest): ResponseEntity<CommonApiResponse<SignupResponse>> {
|
||||
val response: SignupResponse = memberService.create(request)
|
||||
return ResponseEntity.created(URI.create("/members/${response.id}"))
|
||||
.body(CommonApiResponse(response))
|
||||
}
|
||||
|
||||
@GetMapping("/members")
|
||||
override fun findMembers(): ResponseEntity<CommonApiResponse<MemberRetrieveListResponse>> {
|
||||
val response: MemberRetrieveListResponse = memberService.findMembers()
|
||||
|
||||
@ -4,18 +4,34 @@ import io.swagger.v3.oas.annotations.media.Schema
|
||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||
|
||||
fun MemberEntity.toRetrieveResponse(): MemberRetrieveResponse = MemberRetrieveResponse(
|
||||
id = id!!,
|
||||
name = name
|
||||
id = id!!,
|
||||
name = name
|
||||
)
|
||||
|
||||
data class MemberRetrieveResponse(
|
||||
@Schema(description = "회원 식별자")
|
||||
val id: Long,
|
||||
@Schema(description = "회원 식별자")
|
||||
val id: Long,
|
||||
|
||||
@Schema(description = "회원 이름")
|
||||
val name: String
|
||||
@Schema(description = "회원 이름")
|
||||
val name: String
|
||||
)
|
||||
|
||||
data class MemberRetrieveListResponse(
|
||||
val members: List<MemberRetrieveResponse>
|
||||
val members: List<MemberRetrieveResponse>
|
||||
)
|
||||
|
||||
data class SignupRequest(
|
||||
val email: String,
|
||||
val password: String,
|
||||
val name: String
|
||||
)
|
||||
|
||||
data class SignupResponse(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
)
|
||||
|
||||
fun MemberEntity.toSignupResponse(): SignupResponse = SignupResponse(
|
||||
id = this.id!!,
|
||||
name = this.name
|
||||
)
|
||||
|
||||
@ -4,12 +4,16 @@ import io.kotest.assertions.assertSoftly
|
||||
import io.kotest.matchers.collections.shouldContainAll
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import roomescape.auth.exception.AuthErrorCode
|
||||
import roomescape.member.exception.MemberErrorCode
|
||||
import roomescape.member.infrastructure.persistence.Role
|
||||
import roomescape.member.web.MemberController
|
||||
import roomescape.member.web.MemberRetrieveListResponse
|
||||
import roomescape.member.web.SignupRequest
|
||||
import roomescape.util.MemberFixture
|
||||
import roomescape.util.RoomescapeApiTest
|
||||
import kotlin.random.Random
|
||||
@ -82,5 +86,61 @@ class MemberControllerTest(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
given("POST /members") {
|
||||
val endpoint = "/members"
|
||||
val request = SignupRequest(
|
||||
name = "name",
|
||||
email = "email@email.com",
|
||||
password = "password"
|
||||
)
|
||||
`when`("같은 이메일이 없으면") {
|
||||
every {
|
||||
memberRepository.findByEmail(request.email)
|
||||
} returns null
|
||||
|
||||
every {
|
||||
memberRepository.save(any())
|
||||
} returns MemberFixture.create(
|
||||
id = 1,
|
||||
name = request.name,
|
||||
account = request.email,
|
||||
password = request.password,
|
||||
role = Role.MEMBER
|
||||
)
|
||||
|
||||
then("id과 이름을 담아 성공 응답") {
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request
|
||||
) {
|
||||
status { isCreated() }
|
||||
jsonPath("$.data.name") { value(request.name) }
|
||||
jsonPath("$.data.id") { value(1) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`when`("같은 이메일이 있으면") {
|
||||
every {
|
||||
memberRepository.findByEmail(request.email)
|
||||
} returns mockk()
|
||||
|
||||
then("에러 응답") {
|
||||
val expectedError = MemberErrorCode.DUPLICATE_EMAIL
|
||||
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request
|
||||
) {
|
||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user