refactor: JacksonConfig 및 테스트 코드 코틀린 전환

This commit is contained in:
이상진 2025-07-14 13:53:00 +09:00
parent 99878df560
commit 2093221ff8
3 changed files with 90 additions and 114 deletions

View File

@ -1,40 +1,41 @@
package roomescape.common.config; package roomescape.common.config
import java.time.LocalDate; import com.fasterxml.jackson.databind.ObjectMapper
import java.time.LocalTime; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import java.time.format.DateTimeFormatter; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer
import org.springframework.context.annotation.Bean; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer
import org.springframework.context.annotation.Configuration; import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer
import com.fasterxml.jackson.module.kotlin.kotlinModule
import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Configuration
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import java.time.LocalDate
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; import java.time.LocalTime
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import java.time.format.DateTimeFormatter
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
@Configuration @Configuration
public class JacksonConfig { class JacksonConfig {
@Bean @Bean
public ObjectMapper objectMapper() { fun objectMapper(): ObjectMapper = ObjectMapper()
ObjectMapper objectMapper = new ObjectMapper(); .registerModule(javaTimeModule())
objectMapper.registerModule(javaTimeModule()); .registerModule(kotlinModule())
return objectMapper;
} private fun javaTimeModule(): JavaTimeModule = JavaTimeModule()
.addSerializer(
@Bean LocalDate::class.java,
public JavaTimeModule javaTimeModule() { LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE)
JavaTimeModule javaTimeModule = new JavaTimeModule(); )
.addDeserializer(
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE)); LocalDate::class.java,
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE)); LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE)
)
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm"))); .addSerializer(
javaTimeModule.addDeserializer(LocalTime.class, LocalTime::class.java,
new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm"))); LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm"))
)
return javaTimeModule; .addDeserializer(
} LocalTime::class.java,
LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm"))
) as JavaTimeModule
} }

View File

@ -1,80 +0,0 @@
package roomescape.common;
import static org.assertj.core.api.Assertions.*;
import java.time.LocalDate;
import java.time.LocalTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import roomescape.common.config.JacksonConfig;
class JacksonConfigTest {
private ObjectMapper objectMapper;
@BeforeEach
void setUp() {
JacksonConfig jacksonConfig = new JacksonConfig();
objectMapper = jacksonConfig.objectMapper();
}
@DisplayName("날짜는 yyyy-MM-dd 형식으로 직렬화 된다.")
@Test
void dateSerialize() throws JsonProcessingException {
// given
LocalDate date = LocalDate.parse("2021-07-01");
// when
String json = objectMapper.writeValueAsString(date);
LocalDate actual = objectMapper.readValue(json, LocalDate.class);
// then
assertThat(actual.toString()).isEqualTo("2021-07-01");
}
@DisplayName("시간은 HH:mm 형식으로 직렬화된다.")
@Test
void timeSerialize() throws JsonProcessingException {
// given
LocalTime time = LocalTime.parse("12:30:00");
// when
String json = objectMapper.writeValueAsString(time);
LocalTime actual = objectMapper.readValue(json, LocalTime.class);
// then
assertThat(actual.toString()).isEqualTo("12:30");
}
@DisplayName("yyyy-MM-dd 형식의 문자열은 LocalDate로 역직렬화된다.")
@Test
void dateDeserialize() throws JsonProcessingException {
// given
String json = "\"2021-07-01\"";
// when
LocalDate actual = objectMapper.readValue(json, LocalDate.class);
// then
assertThat(actual).isEqualTo(LocalDate.of(2021, 7, 1));
}
@DisplayName("HH:mm 형식의 문자열은 LocalTime으로 역직렬화된다.")
@Test
void timeDeserialize() throws JsonProcessingException {
// given
String json = "\"12:30\"";
// when
LocalTime actual = objectMapper.readValue(json, LocalTime.class);
// then
assertThat(actual).isEqualTo(LocalTime.of(12, 30));
}
}

View File

@ -0,0 +1,55 @@
package roomescape.common.config
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.exc.InvalidFormatException
import io.kotest.assertions.throwables.shouldThrow
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.LocalTime
class JacksonConfigTest(
private val objectMapper: ObjectMapper = JacksonConfig().objectMapper()
) : FunSpec({
context("날짜는 yyyy-mm-dd 형식이다.") {
val date = "2025-07-14"
val serialized: String = objectMapper.writeValueAsString(LocalDate.parse(date))
val deserialized: LocalDate = objectMapper.readValue(serialized, LocalDate::class.java)
test("LocalDate 직렬화") {
serialized shouldBe "\"$date\""
}
test("LocalDate 역직렬화") {
deserialized shouldBe LocalDate.parse(date)
}
test("형식이 잘못되면 InvalidFormatException을 던진다.") {
shouldThrow<InvalidFormatException> {
objectMapper.readValue("\"2025/07/14\"", LocalDate::class.java)
}.message shouldContain "Text '2025/07/14' could not be parsed"
}
}
context("시간은 HH:mm 형식이다.") {
val (hour, minute, sec) = Triple(12, 30, 45)
val serialized: String = objectMapper.writeValueAsString(LocalTime.of(hour, minute, sec))
val deserialized: LocalTime = objectMapper.readValue(serialized, LocalTime::class.java)
test("LocalTime 직렬화") {
serialized shouldBe "\"$hour:$minute\""
}
test("LocalTime 역직렬화") {
deserialized shouldBe LocalTime.of(hour, minute)
}
test("형식이 잘못되면 InvalidFormatException을 던진다.") {
shouldThrow<InvalidFormatException> {
objectMapper.readValue("\"$hour:$minute:$sec\"", LocalTime::class.java)
}.message shouldContain "Text '$hour:$minute:$sec' could not be parsed"
}
}
})