Skip to content

juneyr.dev

타입 속 타입 : Parameterized Type

Java2 min read

서비스를 만들 때 DTO 를 List로 내려주는 것이 아니라, 항상 특정한 형태로 내려준다. 바로 DataResponse 형태! 이 DataResponse class는, paging을 위해서 만들어진 커스텀 클래스인데 🙂 항상 DataResponse<T> 의 형식으로 사용한다. 이와 같이, T (파라미터가 되는 클래스) 를 품고 있는 클래스(여기서는 DataResponse)의 타입을 일반적으로 Parameterized Type이라고 한다. 비단 커스텀 클래스가 아니더라도, 우리가 자주 사용하는 List<T> 역시 Parameterized Type 이라고 할 수 있겠다. 찬찬히 생각해볼 수록 꽤 논리적인 네이밍이다.

아래 코드를 보자.

문제상황

문제는, 이 DataResponse를 가지고 테스트를 할 때 Type을 유추할 수 가 없다는 것이다. 일반적으로 controller 테스트에서 다음과 같이 mvc request를 하고, 그 결과를 다시 매핑해서 유효한 값인지 확인한다.

단일 결과를 받을 때는 결과 string 을 UserDto 로 쉽게 매핑할 수 있지만, 아래 DataResponse<UserDto> 의 경우 에러가 발생한다. 이는 DataResponse<UserDto>.class 혹은 DataResponse<UserDto.class>.class 등으로 parameterizedType을 유추할 수가 없기 때문이다. 파라미터를 품었기때문에 일반적인 방식으로는 유추가 불가능한 것!

잠깐.. 근데 나는 UserDto.class도 익숙하지 않은데?

더 진행하기 전에 UserDto.class 라는 형식에 대해서도 짚고 넘어가자.

위에서 objectMapper 의 readValue 클래스가 그렇듯이, 클래스나 JavaType을 메소드의 파라미터로 받는 경우가 있다. 파라미터의 종류 를 이야기할 때 이를 타입토큰 이라고 하고, 그 값으로는 클래스 리터럴 을 넘긴다. 클래스 리털은 User.class, Product.class와 같이 <클래스명>.class 의 형식을 띄고 있다.

클래스리터럴과 타입토큰

일반적이면 그렇게 하지만.. 이렇게 이중인 parameterized type 은 어떻게 하는데?

위에서 지적했듯이, 파라미터를 품은 형태는 <클래스명<T>.class 등의 방식으로는 유추가 불가능하다.

그러면 어떻게 objectMapper 가 원하는 JavaType으로 넘겨줄 수 있을까?

첫번째 방법 (jackson : typereference 사용)

첫번째는 jackson 의 typereference를 사용하는 방법이다.

TypeReference<T> 의 T 자리에 우리가 원하던 DataResponse<UserDto> 를 넘겨주면, 자동으로 타입을 유추하여 진행해준다.

  • 편리하고 깔끔하다.
  • 하지만 { } 로 클래스를 정의하는 것이라서 보기에 따라 깔끔하지 않을 수 있다.

두번째 방법

jackson의 Typefactory로 공통 type referencing 로직을 넣는 방법이다. 따로 유틸 클래스에 다음과 같은 메소드를 정의한다.

objectMapper는 JavaType을 받으므로, 해당하는 값을 맞춰주기 위해서 TypeFactory를 사용했다. 이 constructParametricType이라는 메소드는 parameterized Type을 의미하는 JavaType을 만들어주는 팩토리 메소드이다. 우리의 예제에서는 parametrized 에 DataResponse.class를 , 그리고 parameterclasses에는 UserDto.class 를 넘겨줄 수 있다.

  • 유틸 클래스를 따로 만들어야한다.
  • 좀더 깔끔한 파라미터로 처리가 가능하다.

결론 & TO-DO

이렇게 간단하게 일단 parameterized type과 그 타입 유추를 알아보았다.

사실 이 글을 작성하면서, 이 parameterized type 의 타입 유추를 하는 과정에서 super type token의 개념을 알게되었다.

  • jackson에서 제공하는 슈퍼타입토큰이 TypeReference 이고
  • spring에서는 ParametizedType을 사용한다는 것도 !

제네릭 클래스를 매핑할 때 생기는 타입 erasure을 막고,타입 안정성을 보장하는 과정이 super type token 이라고 하는데 요 개념을 추가해서 차후에 이 글을 더 개선하려고한다.

참고

https://homoefficio.github.io/2016/11/30/%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A6%AC%ED%84%B0%EB%9F%B4-%ED%83%80%EC%9E%85-%ED%86%A0%ED%81%B0-%EC%88%98%ED%8D%BC-%ED%83%80%EC%9E%85-%ED%86%A0%ED%81%B0/

https://umbum.dev/925 https://multifrontgarden.tistory.com/135