์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ObjectMapper๋ฅผ ์œ„ํ•ด์„œ Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ง€์›ํ•˜๊ณ  ์žˆ๋Š” JavaTimeModule์„ ๋“ฑ๋กํ•˜์—ฌ OffsetDateTime์— ๋Œ€ํ•œ Serializer๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต๋˜๋Š” OffsetDateTimeSerializer๋Š”2025-01-24T00:00:00+09:00 ์™€ ๊ฐ™์€ ํŒจํ„ด์— ๋Œ€ํ•œ ๋ณ€ํ™˜์„ ์ง€์›ํ•˜๊ธฐ๋„ ํ•˜๊ณ  ์ง€๋‚œ ์ž๋ฐ” ๋‚ ์งœ ๋ฐ ์‹œ๊ฐ„ ํฌ๋งท์—์„œ ์—ฌ๋Ÿฌ ํ˜•ํƒœ์˜ ํŒจํ„ด์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด DateTimeFormatter๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

KDB+์˜ Timestamp ๋ฌธ์ž์—ด

KDB+
`timestamp$.z.d 2025.01.24D00:00:00.000000000 `datetime$`timestamp$.z.d 2025.01.24T00:00:00.000
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.OffsetDateTime` from String "2025-01-24T00:00:00.000000000": Failed to deserialize java.time.OffsetDateTime: (java.time.format.DateTimeParseException) Text '2025-01-24T00:00:00.000000000' could not be parsed at index 29

KDB+๋ผ๊ณ  ํ•˜๋Š” ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ Timestamp๋Š” JSON์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ2025-01-24T00:00:00.000000000 ํ˜•ํƒœ์˜ ๋‚˜๋…ธ์ดˆ ์ •๋ฐ€๋„๋ฅผ ๊ฐ€์ง€๋Š” UTC ๊ธฐ์ค€์˜ ๋ฌธ์ž์—ด์ด ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๊ธฐ์กด์— ๋งŒ๋“ค์—ˆ๋˜ DateTimeFormatter๋กœ๋Š” ์ปค๋ฒ„ํ•  ์ˆ˜ ์—†๋Š” ํŒจํ„ด์ด๋ฏ€๋กœ OffsetDateTime์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†์Œ์„ ์œ„ ์˜ค๋ฅ˜ ๋กœ๊ทธ๋ฅผ ํ†ตํ•ด ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ์ผ๋ฐ˜์ ์ธISO_OFFSET_DATE_PATTERN์ด ์•„๋‹ˆ๋ฏ€๋กœ ObjectMapper์— OffsetDateTime์„ ์œ„ํ•œ Deserializer ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ์˜๋„ํ•˜๋Š” DateTimeFormatter๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผํ•  ์ˆ˜ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค.

๋‚˜๋…ธ์ดˆ ์ •๋ฐ€๋„๋ฅผ ์ง€์›ํ•˜๋Š” OffsetDateTimeDeserializer

JsonConfig
@Bean public Jackson2ObjectMapperBuilder objectMapperBuilder() { return new Jackson2ObjectMapperBuilder() .modules(new JavaTimeModule()) .deserializerByType(OffsetDateTime.class, new OffsetDateTimeDeserializer()); } private static class OffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> { @Override public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { String str = jsonParser.getValueAsString(); if (str == null || str.isEmpty()) { return null; } // NOTE: ์ˆซ์ž๋กœ๋งŒ ๊ตฌ์„ฑ๋˜๋Š” ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„์˜ ํƒ€์ž„์Šคํƒฌํ”„ if (str.matches("^\\d+$")) { long value = jsonParser.getNumberValue().longValue(); Instant instant = Instant.ofEpochMilli(value); return OffsetDateTime.ofInstant(instant, ZoneOffset.UTC); } try { return OffsetDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME); } catch (DateTimeException e) { // NOTE: KDB+ Timestamp return OffsetDateTime.parse(str, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS").withZone(ZoneOffset.UTC)); } } }

์ƒ์„ฑ๋œ ObjectMapper์— Deserializer๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ Jackson2ObjectMapperBuilder ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์œ„๊ณผ ๊ฐ™์ด OffsetDateTime ํด๋ž˜์Šค์— ๋Œ€ํ•œ Deserializer๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ๋ณ„๋„๋กœ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„ ์˜ˆ์‹œ ์ฝ”๋“œ๋Š” ์ˆซ์ž๋กœ ๊ตฌ์„ฑ๋œ ๊ฒฝ์šฐ์™€ ์ผ๋ฐ˜์ ์ธ ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜์„ ํ•  ์ˆ˜ ์—†์œผ๋ฉด๋‚˜๋…ธ์ดˆ ์ •๋ฐ€๋„๋ฅผ ๊ฐ€์ง€๋Š” KDB+ ํƒ€์ž„์Šคํƒฌํ”„ ๋ฌธ์ž์—ด์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์ž‘์„ฑํ•œ ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜์˜ ์ฝ”๋“œ์—์„œ๋Š” ObjectMapper ๋ฟ๋งŒ ์•„๋‹ˆ๋ผGson์— ๋Œ€ํ•œ Deserializer๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.