diff --git a/src/main/kotlin/roomescape/common/config/JacksonConfig.kt b/src/main/kotlin/roomescape/common/config/JacksonConfig.kt index 40232da4..c53a98d8 100644 --- a/src/main/kotlin/roomescape/common/config/JacksonConfig.kt +++ b/src/main/kotlin/roomescape/common/config/JacksonConfig.kt @@ -1,6 +1,9 @@ package roomescape.common.config -import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.* +import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer @@ -9,6 +12,8 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer import com.fasterxml.jackson.module.kotlin.kotlinModule 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.format.DateTimeFormatter @@ -20,6 +25,7 @@ class JacksonConfig { fun objectMapper(): ObjectMapper = ObjectMapper() .registerModule(javaTimeModule()) .registerModule(kotlinModule()) + .registerModule(longIdModule()) private fun javaTimeModule(): JavaTimeModule = JavaTimeModule() .addSerializer( @@ -38,4 +44,35 @@ class JacksonConfig { LocalTime::class.java, LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm")) ) as JavaTimeModule + + private fun longIdModule(): SimpleModule { + val simpleModule = SimpleModule() + simpleModule.addSerializer(Long::class.java, LongToStringSerializer()) + simpleModule.addDeserializer(Long::class.java, StringToLongDeserializer()) + return simpleModule + } +} + +class LongToStringSerializer : JsonSerializer() { + override fun serialize(value: Long?, gen: JsonGenerator, serializers: SerializerProvider) { + if (value == null) { + gen.writeNull() + } else { + gen.writeString(value.toString()) + } + } +} + +class StringToLongDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Long? { + val text = p.text + if (text.isNullOrBlank()) { + return null + } + return try { + text.toLong() + } catch (_: NumberFormatException) { + throw RoomescapeException(CommonErrorCode.INVALID_INPUT_VALUE) + } + } } diff --git a/src/test/kotlin/roomescape/common/config/JacksonConfigTest.kt b/src/test/kotlin/roomescape/common/config/JacksonConfigTest.kt index c290d1d9..98ca4e91 100644 --- a/src/test/kotlin/roomescape/common/config/JacksonConfigTest.kt +++ b/src/test/kotlin/roomescape/common/config/JacksonConfigTest.kt @@ -52,4 +52,18 @@ class JacksonConfigTest( }.message shouldContain "Text '$hour:$minute:$sec' could not be parsed" } } + + context("Long 타입은 문자열로 (역)직렬화된다.") { + val number = 1234567890L + val serialized: String = objectMapper.writeValueAsString(number) + val deserialized: Long = objectMapper.readValue(serialized, Long::class.java) + + test("Long 직렬화") { + serialized shouldBe "$number" + } + + test("Long 역직렬화") { + deserialized shouldBe number + } + } })