240207 TIL | ObjectMapper

2024. 2. 7. 20:56· 왕초보일지
목차
  1. test 코드 작성 중 LocalDateTime 관련 에러
  2. 문제
  3. 해결

test 코드 작성 중 LocalDateTime 관련 에러

 

PostControllerTest

더보기
@SpringBootTest
@AutoConfigureMockMvc // mock 빈 등록
@ExtendWith(MockKExtension::class)
class PostControllerTest @Autowired constructor(
    private val mockMvc: MockMvc, private val jwtPlugin: JwtPlugin
): DescribeSpec({
    extension(SpringExtension) //Junit5 확장

    afterContainer { clearAllMocks() }

    val postService = mockk<PostService>()

    describe("GET/posts/{postId}") {
        context("존재하는 ID를 요청할 때") {
            it("http 상태 코드 200을 반환한다.") {
                val postId = 10L

                every { postService.getPost(any()) } returns PostResponse(
                    id = postId,
                    title = "postTitle",
                    content = "postContest",
                    status = PostStatus.FALSE,
                    author = "postAuthor",
                    createdAt = LocalDateTime.now(),
                    comments = mutableListOf()
                )

                val jwtToken = jwtPlugin.generateAccessToken("test", "test", "USER")
                val result = mockMvc.perform( // mockMvc 로 특정 요청 실행
                    get("/posts/$postId")
                        .header("Authorization", "Bearer $jwtToken")
                ).andReturn()

                result.response.status shouldBe 200

                val responseDto = jacksonObjectMapper().readValue(
                    result.response.contentAsString,
                    PostResponse::class.java
                )
                responseDto.id shouldBe postId
            }
            }
        }
})

 

 

문제

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
 at [Source: (String)"{"id":10,"title":"2","content":"2","status":"FALSE","author":"1","createdAt":"2024-02-06T21:01:06.38197","comments":[]}"; line: 1, column: 78] (through reference chain: org.example.todolist.domain.post.dto.PostResponse["createdAt"])

반환값의 내용을 가져오기 위해 jacksonObjectMapper() 을 사용한 부분에서

Java8 Jackson 라이브러리가 LocalDateTime 과 같은 날짜/시간 유형을 처리하지 못함

날짜/시간 유형을 직렬화/역직렬화하는 기능이 없기 때문

 

 

찾은 방법

참조 : https://woo-chang.tistory.com/75 / https://tlatmsrud.tistory.com/114 

jackson-datatype-jsr310 모듈 추가

implementation ("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation ("com.fasterxml.jackson.core:jackson-databind")

해당 클래스에 어노테이션 추가

@JsonSerialize(using = LocalDateTimeSerializer::class)
@JsonDeserialize(using = LocalDateTimeDeserializer::class)

모듈 config

@Configuration
class ModuleConfig {
    @Bean
    fun objectMapper(): ObjectMapper {
        val objectMapper = ObjectMapper()
        objectMapper.registerModules(JavaTimeModule())
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        return objectMapper
    }
}

 

찾아본 방법 다 했는데 똑같은 에러가 나온다.

 

https://umanking.github.io/2021/07/24/jackson-localdatetime-serialization/

 

 

해결

ObejectMapper

: json 을 java 객체로 역직렬화하거나 java 객체를 json 으로 직렬화할 때 사용하는 jackson 라이브러리의 클래스이다.

컬렉션 타입, 날짜/시간 타입과 같이 복잡한 데이터 유형을 적절하게 처리하기 위해서는 사용자 정의 모듈을 추가해준다.

 

 

 ❗jsr310 은 스프링 부트에서 기본적으로 가져와준다. => gradle 에서 삭제

 

❗ 테스트코드 안에 objectmapper 를 직접 생성해주고  jacksonObjectMapper 가 아닌 objectMapper 을 사용

=> 실패, 다른 에러 ("PostResponse 인스턴스를 생성할 수 없다")

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.example.todolist.domain.post.dto.PostResponse` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

 

response 객체에 생성자를 만들어줬다.

data class PostResponse(
    val id: Long,
    val title: String,
    val content: String,
    val status: PostStatus,
    val author: String,
    val createdAt: LocalDateTime,
    val comments: List<CommentResponse>
) {
    constructor() : this(0, "", "", PostStatus.FALSE, "", LocalDateTime.now(), mutableListOf())
}

=> 통과

 

 

어느 부분이 만족한건지 모르겠어서 여러가지를 주석처리했다가 다시 실행해봤다.

 

❗ SerializationFeature 삭제

@Configuration
class ModuleConfig {
    @Bean
    fun objectMapper(): ObjectMapper {
        val objectMapper = ObjectMapper()
        objectMapper.registerModules(JavaTimeModule())
        //objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        return objectMapper
    }
}

=> 통과

직렬화 write 부분 disable 한 것은 상관없는 것 같다.

SerializationFeature.WRITE_DATES_AS_TIMESTAMPS

: Date 를 Timestamps 로 찍어내는 직렬화

 

❗objectMapper 가 아닌 jacksonObjectMapper 를 사용했을 때

=> 같은 오류로 실패

 

jacksonObjectMapper()

: kotlin 에서 jackson 라이브러리를 사용할 때 제공되는 확장함수, objectmapper 의 인스턴스를 생성하고 구성

 

 

❗ objectMapper 빈 등록만 했을 때

LocalDateTime 문제를 찾아봤을 때 obejctMapper 을 따로 설정없이 해주는게

문제라 javatimeModule 을 등록해주어야한다는 부분이 있었다.

   @Bean
    fun objectMapper(): ObjectMapper {
        val objectMapper = ObjectMapper()
//        objectMapper.registerModules(JavaTimeModule())
//        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        return objectMapper
    }

=> 실패

JavatimeModule 설정이 핵심인 것 같다.

 

JavatimeModule

: ObjectMapper 에서 제공하는 모듈 중 하나로 날짜/시간 타입의 데이터를 처리해준다.

 

 

❗ 테스트코드가 아닌 다른 파일에 config 클래스를 작성해서 생성한 뒤

테스트코드에서 objectMapper 을 주입받아서 실행

=> 실패

 

 

결론

처음 찾아본 해결방법에서 답이 적혀있었는데 빙빙 돌아왔다...

 

ObejectMapper  https://woo-chang.tistory.com/75

: json 을 java 객체로 역직렬화하거나 java 객체를 json 으로 직렬화할 때 사용하는 jackson 라이브러리의 클래스이다.

컬렉션 타입, 날짜/시간 타입과 같이 복잡한 데이터 유형을 적절하게 처리하기 위해서는 사용자 정의 모듈을 추가해준다.

JavatimeModule

: java8 에서 도입된 날짜 및 시간 API (LocalDate, LocalTime, LocalDateTiem) 을 jackson 라이브러리에서 인식하지 못하기 때문에 직렬화/역직렬화에서 문제가 발생한다. JavatimeModule 은 ObjectMapper 에서 제공하는 모듈 중 하나로 이러한 날짜 및 시간 API 를 jackson 라이브러리에서 날짜/시간 타입의 데이터를 처리해준다.

 

=> 스프링 부트 프로젝트에서 LocalDateTime 형의 데이터를 직렬화/역직렬화하기 위해 objectMapper() 를 사용할 때 날짜/시간 데이터를 처리해주기 위한 JavatimeModule 을 추가해준다.

  @Bean
    fun objectMapper(): ObjectMapper {
        val objectMapper = ObjectMapper()
        objectMapper.registerModules(JavaTimeModule()) // 날짜 및 시간 처리
        return objectMapper
    }

 

 

 

 

 

🚩 더 찾아볼 점

❓ 강의에서는 jacksonObjectMapper 를 사용해 별다른 에러 없이 잘 이용됐는데 어떻게 된걸까?

- 게시글 생성날짜 타입이 LocalDateTime 이 아닌 다른건지?

❓ 별도의 config 파일을 따로 빼서 만들었을 때 왜 사용이 안되지?

=> 코드 직접 작성한 분께 물어봐야할 것 같다.

 

 

'왕초보일지' 카테고리의 다른 글

240213 TIL | 테스트 코드  (0) 2024.02.13
240208 TIL |  (0) 2024.02.08
240206 TIL |  (1) 2024.02.06
240202 TIL | validation / spring aop / querydsl  (0) 2024.02.02
복습과제  (0) 2024.02.02
  1. test 코드 작성 중 LocalDateTime 관련 에러
  2. 문제
  3. 해결
'왕초보일지' 카테고리의 다른 글
  • 240213 TIL | 테스트 코드
  • 240208 TIL |
  • 240206 TIL |
  • 240202 TIL | validation / spring aop / querydsl
다시은
다시은
🔥
다시은
재은로그
다시은
전체
오늘
어제
  • 분류 전체보기 (127)
    • 코딩테스트 (40)
    • Language (2)
      • JAVA (2)
      • Kotlin (0)
      • TypeScript (0)
    • SQL (1)
    • 인프라 (1)
    • 왕초보일지 (77)
    • 회고 (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • SQL문법
  • mysql
  • 문자열변환
  • sql
  • googleapis
  • 스프레드시트
  • Kotlin

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
다시은
240207 TIL | ObjectMapper
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.