안녕하세요.

오늘은 Spring 프레임워크에 빼놓을 수 없는 라이브러리중 하나인 Jackson에 대해 간단하게 포스팅 합니다.

주의!! 해당 포스팅은 Jackson의 라이브러리 2.9.7 버전을 다룹니다.

또한, 프로젝트에 Jackson 라이브러리의 설치 방법에 대해서는설명하지 않습니다.

패키지 매니저를 쓰면 너무 간단하며, 구글에 검색해보면 엄청 많이 나와요우우

Jackson 라이브러리?

Spring 개발을 하다 보면, 컨트롤러 text/html 형식이 아닌 데이터 전달 목적으로 사용하고 싶을 때가 있습니다. 물론, 쌩 문자열인 plain/text 형식으로 보내도 상관은 없습니다만, 보통은 데이터 구조를 표현하는 방식인 XML 또는 JSON 형태로 많이 보냅니다.

데이터의 구조를 표현하는 이유는 데이터 표현도 있지만, 사실상 데이터를 사용하는 대상이 편하게 사용하기 위해서 입니다.

이 두개 중 JacksonJSON 데이터 구조를 처리해주는 라이브러리 입니다.

만약 JSON으로 데이터 구조를 표현 한다면, 아래와 같습니다.

{
   "name": "Mommoo",
   "age": 28,
   "isDeveloper": true,
   "equipment": ["NoteBook", "Mouse", "SmartPhone"],
   "etc": {
       "favoriteFood": 'Coffee'
  }
}

Jackson 라이브러리의 없이, JSON 데이터를 작성해보자.

Java에서 데이터를 저장할 때는, 아래와 같이 인스턴스를 사용합니다.

public class Person {
   private String name;
   private String job;
   
   public Person(String name, String job) {
       this.name = name;
       this.job = job;
  }
   
   public String getName(){
       return name;
  }
   
   public String getJob() {
       return job;
  }
}

public static void main(String[] args) {
   Person person = new Person("Mommoo", "Developer");
}

JSON 데이터를 어떻게 작성할까요? 그냥 작성해야 합니다... 아래와 같이요

미친짓이니 따라하지 마세요

String JSON = "\"{"+
   "\"name\": \"" + person.getName() + "\","+
   "\"job\": \"" + person.getJob() + "\""+
"}\"";

JavaSingle Quotes즉, 쌍따옴표("")를 정말 문자열로 사용하고 싶을때는 위와 같이 \문자를 붙여줘야 합니다. 끔찍한 코딩은 덤

항상 저렇게 코딩 할 순 없으니, JSON 변환용 클래스를 따로 만들고 그 클래스안에 저장된 멤버변수를 이용하여 JSON 데이터를 출력하는 클래스를 생각하게 됩니다.

대표적인 클래스가 Google 이 만든 GSON 또는 SimpleJSON 등이 있습니다.

SimpleJSON을 예로 든다면, 윗 코딩이 아래와 같이 바뀝니다.

JSONObject jsonObject = new JSONObject();
jsonObject.put("name", person.getName());
jsonObject.put("job", person.getJob());
String JSON = jsonObject.toString();

해당 방식은 지금도 많이 쓰입니다.

물론 Spring 컨트롤러에 위와 같이 코딩하여 리턴하더라도, 충분합니다.

그렇다면, Jackson은 무엇을 더 제공하길래 SpringJackson을 더 선호하는 것일까요?

Jackson과 기존 GSON or SimpeJSON과의 차이?

사실 차이는 없습니다.

JacksonObjectMapper API를 사용하여, 여타 GSON or SimpeJSON과 같이 객체에 데이터를 셋팅해줘야 하는건 마찬가지 입니다.

특별한 점은 Spring 프레임워크와 Jackson의 관계로부터 장점이 있습니다.

Spring 3.0 이후로부터, Jacskon과 관련된 API를 제공함으로써, Jackson라이브러리를 사용할때, 자동화 처리가 가능하게 되었습니다.

덕분에, JSON데이터를 직접 만들던가, GSON or SimpleJSON방식과 같이 직접 키와 벨류를 셋팅하는 방식에서 한단계 더 발전한 방식이 가능해졌습니다.

어떤 방식이길래 발전했을까요? 아래의 Jackson의 동작 원리와 함께 설명할까 합니다.

Jackson 은 어떻게 동작하는가?

Spring3.0 이후로 컨트롤러의 리턴 방식이 @RequestBody 형식이라면, SpringMessageConverter API 를 통해, 컨트롤러가 리턴하는 객체를 후킹 할 수 있습니다.

JacksonJSON데이터를 출력하기 위한 MappingJacksonHttpMessageConverter를 제공합니다. 만약 우리가 스프링 MessageConverter를 위의 MappingJacksonHttpMessageConverter으로 등록한다면, 컨트롤러가 리턴하는 객체를 다시 뜯어(자바 리플렉션 사용), JacksonObjectMapper APIJSON 객체를 만들고 난 후, 출력하여 JSON데이터를 완성합니다.

더욱 편리해진 점은, Spring 3.1 이후로 만약 클래스패스에 Jackson 라이브러리가 존재한다면, ( 쉽게 말해Jackson을 설치했느냐 안했느냐 ) 자동적으로 MessageConverter가 등록된다는 점입니다.

덕분에 우리는 아래와 같이 매우 편리하게 사용할 수 있습니다.

@RequestMapping("/json")
@ResponseBody()
public Object printJSON() {
   Person person = new Person("Mommoo", "Developer");
   return person;
}

이제는 그냥 데이터 인스턴스만 리턴 하더라도 JSON 데이터가 출력됩니다. 엄청나죠ㄷㄷ

위에서 설명한 방식보다 매우 진보한 방식인걸 알 수 있습니다.

다만, Jackson을 더 잘쓰기 위해서는 알아야 하는 기본 지식이 몇가지 존재합니다.

Jackson을 사용하기 위해 알아야 하는 기본지식

소개하는 기본 지식은 순전히 제 생각입니다... 의견이 다를 수 있습니다.

Jackson은 기본적으로 프로퍼티로 동작합니다.

Java프로퍼티를 제공하는 문법이 없습니다. ( 멤버변수랑은 다릅니다. )

Java프로퍼티는 보통 GetterSetter의 이름 명명 규칙으로 정해집니다.

문법적으로 정해지는것이 아니다 보니, 개발자가 일일이 신경써줘야 하는게 자바의 단점 중 하나입니다.

Person 같은 경우는 Getter만 존재 하므로, Getter를 기준으로 프로퍼티를 도출 할 수 있습니다. 즉 NameJobPerson 프로퍼티입니다.

Person의 멤버변수 이름도 똑같이 name, job이지만,

앞서 설명했드시 프로퍼티Getter, Setter기준이므로 멤버변수 이름을 변경하더라도 상관 없습니다.

갑자기 프로퍼티를 설명한 이유는 많은 라이브러리가 해당 프로퍼티 개념으로 작동하기 때문입니다.

Jackson라이브러리도 마찬가지 입니다. JSON데이터로 출력되기 위해서는 멤버변수의 유무가 아닌 프로퍼티 즉, Getter, Setter를 기준으로 작동합니다.

예로 아래와 같이 코딩하더라도 전혀 문제가 없습니다.

public class Person {
   public String getName() {
       return "Mommoo";
  }
   
   public String getJob() {
       return "Developer";
  }
}

@RequestMapping("/json")
@ResponseBody()
public Object printJSON() {
   return new Person();
}

결론적으로 Jackson을 사용한다면, Getter에 신경쓰셔야 합니다!!.

Jackson의 데이터 매핑을 Getter가 아닌 멤버변수로 하고 싶다면?

그렇다면, 이번에는 Jackson의 매핑을 프로퍼티가 아닌 멤버변수로 할 수 있는 방법은 무엇일까요? Jackson은 이와 관련하여 @JsonProperty 어노테이션 API를 제공합니다. 아래와 같이 멤버변수 위에 프로퍼티 이름과 함께 선언해준다면, JSON데이터로 출력 됩니다.

public class Person {
@JsonProperty("name")
   private String myName = "Mommoo";
}

위의 예시는 {"name": "Mommoo"}로 출력 됩니다.

그렇다면 JSON 매핑을 멤버변수로 하고 싶다면, 매번 @JsonProperty를 선언 해야 할까요? 귀찮습니다. 애초에 Jackson 매핑 구조를 바꾸면 어떨까 하는 생각이 듭니다.

Jackson의 데이터 매핑 법칙 변경하기

Jackson은 매핑 법칙을 바꿀 수 있는 @JsonAutoDetect API를 제공합니다.

위 예시와 같이 멤버변수로만 Jackson을 구성하고 싶은 경우 @JsonProperty를 일일이 붙이는 것보다 아래와 같이 설정하는 것이 더 편리합니다.

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Person {
   private String myName = "Mommoo";
}

@JsonAutoDetect멤버변수 뿐만 아니라, Getter, Setter의 데이터 매핑 정책도 정할 수 있습니다. 만약 아래의 경우는 멤버변수 뿐만 아니라, 기본정책인 Getter역시 데이터 매핑이 진행됩니다.

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Person {
   private String myName = "Mommoo";
   
   public String getJob() {
       return "Developer";
  }
}

Getter를 제외하고 싶다면, @JsonIgnore API를 쓰셔도 됩니다.

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Person {
   private String myName = "Mommoo";
   
   @JsonIgnore
   public String getJob() {
       return "Developer";
  }
}

하지만, 역시 일일이 붙여야 하는 상황이 온다면 매핑 정책을 바꾸시는게 좋습니다.

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NON_PRIVATE)
public class Person {
   private String myName = "Mommoo";
   
   public String getJob() {
       return "Developer";
  }
}

Getter정책으로 private 만 데이터 바인딩에 제외 하였습니다.

이렇듯, 제외 범위를 설정할 수 있습니다. 자세한건 아래를 참고해 주세요.

https://fasterxml.github.io/jackson-annotations/javadoc/2.9/com/fasterxml/jackson/annotation/JsonAutoDetect.Visibility.html

Jackson의 데이터 상태에 따른 포함 관계 설정

만약 Jackson데이터 매핑시 NULL 값 과 같은 특정 데이터 상태인 경우 제외하고 싶다면 어떻게 해야 할까요?

Jackson은 이와 관련하여 @JsonIncludeAPI를 제공합니다.

NULL을 클래스 전반적으로 제외하고 싶다면, 클래스 위에 선언하면 됩니다.

또한 특정 프로퍼티NULL일때 해당 프로퍼티만을 제외하고 싶다면 역시 해당 프로퍼티위에 선언하면 됩니다.

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
   private String myName = "Mommoo";
   
   public String getJob() {
       return "Developer";
  }
}

public class Person {
   private String myName = "Mommoo";
   
   @JsonInclude(JsonInclude.Include.NON_NULL)
   public String getJob() {
       return "Developer";
  }
}

JsonInclude.Include 속성은 NON_NULL뿐만 아니라 몇몇 개가 더 존재합니다.

자세한건 아래를 참고 해주세요.

https://fasterxml.github.io/jackson-annotations/javadoc/2.9/com/fasterxml/jackson/annotation/JsonInclude.Include.html

제가 준비한 Jackson 포스팅은 여기까지 입니다.

간단하게 하려 했는데 쓸때 없이 글이 길어진건 아닌가 싶네요.

긴 글 읽어주셔서 감사합니다.

포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee
  1. 모두의개발 2020.07.16 09:11

    잘읽었습니다~

  2. 감사합니다 2020.12.08 12:16

    감사히 잘봤습니다

  3. Kyu Kim 2021.03.25 08:07 신고

    잘 읽었습니다 JsonProperty, JsonIgnore가 뭔지 궁금해서 찾아왔는데 그전에 Json이 뭔지 Jackson이 뭔지 잘 알게 된거 같아요. 오늘 TIL에 읽은글을 정리해보려했는데 작성하다보니까 그냥 거의 복붙이 되버렸네요.출처는 잘 표기했고 덕분에 잘 이해했습니다. 공부만하고있어서 커피는 못사드리지만 감사인사드리고 갑니다.

  4. 신입 2022.04.13 10:57

    잘 읽었습니다~

  5. MyeongDev 2022.06.16 21:35 신고

    글 너무 잘 읽었습니다.
    혼자 개발 공부중인 대학생입니다.
    @RestController 혹은 @ResponseBody 로 인해 Json 결과로 파싱되서 출력되는 것은 직접 실행시켜보고 본 글을 읽어 이해가 되었습니다.
    그런데 혹시 개발 할 때 데이터를 json 객체로 가공해서 넘겨주는게 맞는 걸까요?
    아니면 해당 라이브러리가 자동으로 변경 시켜주니 json 데이터로 가공하지 않고, 그대로 넘겨주는 것이 맞는 건가요?
    궁금증이 생겨 이렇게 댓글 남겨봅니다...

+ Recent posts