[#35] 결제 스키마 재정의 & 예약 조회 페이지 개선 #36

Merged
pricelees merged 37 commits from refactor/#35 into main 2025-08-22 06:43:16 +00:00
2 changed files with 82 additions and 23 deletions
Showing only changes of commit 477415b3ba - Show all commits

View File

@ -14,16 +14,21 @@ import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import roomescape.common.exception.CommonErrorCode
import roomescape.common.exception.RoomescapeException
import java.time.LocalDate
import java.time.LocalTime
import java.time.*
import java.time.format.DateTimeFormatter
@Configuration
class JacksonConfig {
companion object {
private val ISO_OFFSET_DATE_TIME_FORMATTER: DateTimeFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX")
}
@Bean
fun objectMapper(): ObjectMapper = ObjectMapper()
.registerModule(javaTimeModule())
.registerModule(dateTimeModule())
.registerModule(kotlinModule())
.registerModule(longIdModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
@ -52,9 +57,15 @@ class JacksonConfig {
simpleModule.addDeserializer(Long::class.java, StringToLongDeserializer())
return simpleModule
}
}
class LongToStringSerializer : JsonSerializer<Long>() {
private fun dateTimeModule(): SimpleModule {
val simpleModule = SimpleModule()
simpleModule.addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer())
simpleModule.addSerializer(OffsetDateTime::class.java, OffsetDateTimeSerializer())
return simpleModule
}
class LongToStringSerializer : JsonSerializer<Long>() {
override fun serialize(value: Long?, gen: JsonGenerator, serializers: SerializerProvider) {
if (value == null) {
gen.writeNull()
@ -62,9 +73,9 @@ class LongToStringSerializer : JsonSerializer<Long>() {
gen.writeString(value.toString())
}
}
}
}
class StringToLongDeserializer : JsonDeserializer<Long>() {
class StringToLongDeserializer : JsonDeserializer<Long>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Long? {
val text = p.text
if (text.isNullOrBlank()) {
@ -76,4 +87,29 @@ class StringToLongDeserializer : JsonDeserializer<Long>() {
throw RoomescapeException(CommonErrorCode.INVALID_INPUT_VALUE)
}
}
}
class LocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
override fun serialize(
value: LocalDateTime,
gen: JsonGenerator,
serializers: SerializerProvider
) {
value.atZone(ZoneId.systemDefault())
.toOffsetDateTime()
.also {
gen.writeString(it.format(ISO_OFFSET_DATE_TIME_FORMATTER))
}
}
}
class OffsetDateTimeSerializer : JsonSerializer<OffsetDateTime>() {
override fun serialize(
value: OffsetDateTime,
gen: JsonGenerator,
serializers: SerializerProvider
) {
gen.writeString(value.format(ISO_OFFSET_DATE_TIME_FORMATTER))
}
}
}

View File

@ -7,7 +7,10 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
class JacksonConfigTest(
private val objectMapper: ObjectMapper = JacksonConfig().objectMapper()
@ -66,4 +69,24 @@ class JacksonConfigTest(
deserialized shouldBe number
}
}
context("OffsetDateTime은 ISO 8601 형식으로 직렬화된다.") {
val date = LocalDate.of(2025, 7, 14)
val time = LocalTime.of(12, 30, 0)
val dateTime = OffsetDateTime.of(date, time, ZoneOffset.ofHours(9))
val serialized: String = objectMapper.writeValueAsString(dateTime)
test("OffsetDateTime 직렬화") {
serialized shouldBe "\"2025-07-14T12:30:00+09:00\""
}
}
context("LocalDateTime은 ISO 8601 형식으로 직렬화된다.") {
val dateTime = LocalDateTime.of(2025, 7, 14, 12, 30, 0)
val serialized: String = objectMapper.writeValueAsString(dateTime)
test("LocalDateTime 직렬화") {
serialized shouldBe "\"2025-07-14T12:30:00+09:00\""
}
}
})