본문 바로가기
Programing/Java

Jackson @JsonView 사용기

by Tomining 2017. 9. 27.
Jackson 을 이용하여 Json 문자열을 쓸 때, 모든 속성을 다 노출하고 싶지 않은 경우가 있다.
이럴 때 @JsonView 를 사용할 수 있다.

예제 코드를 살펴보자.

@Data
@Builder
public class Album {
   private String albumId;
   private String albumName;
   private Singer representSinger;
   private List<Track> tracks;
}
@Data
@Builder
public class Singer {
   private String name;
   private String nickname;
   private String managementCompany;
}
@Data
@Builder
public class Track {
   private String trackId;
   private String trackTitle;
   private String trackNo;
   private String genre;
   private Singer singer;
}
위 처럼 Album, Singer, Track 3가지 모델이 있다고 가정하자.
Album 클래스를 Json 으로 변환하면 모든 정보를 포함하게 된다. 하지만 ID 정보는 외부에 노출하고 싶지 않을 수 있다.
이 경우 어떻게 설정할 수 있을까?

public class JsonViewTest {
   private static JacksonJsonParser parser;

   @BeforeClass
   public static void init() {
      parser = new JacksonJsonParser();
   }

   @Test
   public void nonJsonViewTest() throws Exception {
      Album album = Album.builder()
         .albumId("albumId")
         .albumName("albumName")
         .representSinger(
            Singer.builder()
               .name("singerName")
               .nickname("singerNickname")
               .managementCompany("managementCompany")
            .build()
         )
         .tracks(
            Arrays.asList(
               Track.builder()
                  .trackId("trackId1")
                  .trackNo("1")
                  .trackTitle("No.1 Track")
                  .genre("genre")
                  .singer(
                     Singer.builder()
                        .name("singerName")
                        .nickname("singerNickname")
                        .managementCompany("managementCompany")
                     .build()
                  )
                  .build()
            )
         )
         .build();

      parser.printPrettyJson(album);
   }
}
{
  "albumId" : "albumId",
  "albumName" : "albumName",
  "representSinger" : {
    "name" : "singerName",
    "nickname" : "singerNickname",
    "managementCompany" : "managementCompany"
  },
  "tracks" : [ {
    "trackId" : "trackId1",
    "trackTitle" : "No.1 Track",
    "trackNo" : "1",
    "genre" : "genre",
    "singer" : {
      "name" : "singerName",
      "nickname" : "singerNickname",
      "managementCompany" : "managementCompany"
    }
  } ]
}
Json 은 위 결과와 같이 모든 정보를 포함하게 된다.
ViewModel 을 선언한 다음 아래와 같이 @JsonView 설정을 해 보자.
public abstract class ViewModel implements Serializable {
   public interface DefaultView {}
   public interface SummaryView extends DefaultView {}
   public interface DetailView extends SummaryView {}
}
@Data
@Builder
public class Album {
   private String albumId;
   @JsonView(ViewModel.DefaultView.class)
   private String albumName;
   @JsonView(ViewModel.SummaryView.class)
   private Singer representSinger;
   @JsonView(ViewModel.SummaryView.class)
   private List<Track> tracks;
}
@Data
@Builder
public class Singer {
   @JsonView(ViewModel.DetailView.class)
   private String name;
   @JsonView(ViewModel.SummaryView.class)
   private String nickname;
   @JsonView(ViewModel.DetailView.class)
   private String managementCompany;
}

@Data
@Builder
public class Track {
   private String trackId;
   @JsonView(ViewModel.DefaultView.class)
   private String trackTitle;
   @JsonView(ViewModel.DefaultView.class)
   private String trackNo;
   @JsonView(ViewModel.SummaryView.class)
   private String genre;
   @JsonView(ViewModel.DetailView.class)
   private Singer singer;
}
TC에서 아래와 같이 변경 후 수행해 보자.

parser.printPrettyJson(album, ViewModel.SummaryView.class);
{
  "albumId" : "albumId",
  "albumName" : "albumName",
  "representSinger" : {
    "nickname" : "singerNickname"
  },
  "tracks" : [ {
    "trackId" : "trackId1",
    "trackTitle" : "No.1 Track",
    "trackNo" : "1",
    "genre" : "genre"
  } ]
}

SummaryView 를 사용했기 때문에 DetailView 로 선언된 속성은 제외되었음을 확인할 수 있다.
그래도 여전히 albumId 와 trackId 는 노출되고 있는데, 별도의 @JsonView 옵션을 주지 않은 경우 기본적으로 Json 에 포함되는 것을 확인할 수 있다. 그 이유는 ObjectMapper 설정에 아래 옵션 때문이다.

MapperFeature.DEFAULT_VIEW_INCLUSION
위 옵션은 기본값이 True 로 @JsonView 설정이 안 된 속성도 Json 변환 시 기본적으로 포함한다는 의미이다.
이 값을 False로 설정한 뒤 다시 수행해 보자.

MAPPER.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
{
  "albumName" : "albumName",
  "representSinger" : {
    "nickname" : "singerNickname"
  },
  "tracks" : [ {
    "trackTitle" : "No.1 Track",
    "trackNo" : "1",
    "genre" : "genre"
  } ]
}
이제 처음 의도했던 대로 ID 정보가 제외된 것을 확인할 수 있다.
Jackson 의 이러한 기능을 Spring 에서 MessageConverter 설정 시 적용할 수 있다.

@Bean
public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setPrettyPrint(true);
    converter.setObjectMapper(new ObjectMapper());
    return converter;
}
* 기본값으로 ObjectMapper 를 생성하고 있으나 MapperFeature 옵션을 추가해 주면된다.