FastJSON과 Jackson JSON 처리 라이브러리 비교 분석

Java 애플리케이션에서 JSON 데이터를 다룰 때, FastJSON과 Jackson은 널리 사용되는 라이브러리입니다. 이 두 라이브러리의 사용 편의성, 성능, 그리고 동작 방식의 미묘한 차이를 비교 분석하여 어떤 상황에 어떤 라이브러리가 더 적합한지 살펴보겠습니다.

1. 사용 편의성 및 성능

1.1. 호출 편의성

  • FastJSON: 다수의 정적 메소드를 제공하여 별도의 객체 인스턴스화 없이 메소드를 직접 호출할 수 있어 간결하고 편리합니다.
  • Jackson: ObjectMapper와 같은 클래스의 인스턴스를 생성해야 하므로 상대적으로 호출 과정이 번거로울 수 있습니다. 하지만 JSON 유틸리티 클래스를 별도로 만들어 사용하면 이러한 불편함을 해소할 수 있습니다.

1.2. 성능

256KB 크기의 JSON 문자열을 기준으로 성능을 비교했을 때:

  • 역직렬화 (Deserialization):
    • FastJSON: 평균 약 700ms
    • Jackson (Data Binding): 평균 약 600ms
    Jackson이 역직렬화 성능에서 약간 더 우수합니다.
  • 직렬화 (Serialization):
    • 두 라이브러리 모두 유사한 성능을 보이며, 평균 약 140ms
  • Jackson의 스트림 방식: ObjectMapper.writeValueAsString() 메소드를 사용하는 Data Binding 방식보다 JsonGenerator.writeObject()를 사용하는 스트림 출력 방식이 평균 약 130ms로 성능이 소폭 더 좋습니다.

2. 구현 세부 사항 및 동작 차이

2.1. 직렬화 및 역직렬화 구현 원리

객체를 JSON으로 직렬화할 때, 라이브러리는 주로 리플렉션(Reflection)을 사용하여 객체의 Getter 메소드(getXXX(), isXXX())를 탐색합니다. 메소드 이름에서 속성 이름을 추출하고, 해당 Getter를 호출하여 속성 값을 가져와 JSON 문자열을 생성합니다. 역직렬화 시에는 JSON 문자열의 속성 이름을 기반으로 Setter 메소드(setXXX())를 생성하여 객체를 구성합니다.

이 과정은 특정 필드와 직접적인 연관 없이 메소드 명명 규칙에 따라 동작합니다.


public class Product {
    private String name;
    private Double price;
    // private boolean sold; // 필드가 주석 처리된 경우

    // Getter 메소드: 'sold' 필드가 없어도 isSold() 메소드가 있으면 직렬화에 포함됨
    public boolean isSold() {
        return true;
    }

    // name, price 필드에 대한 Getter 및 Setter 메소드 (생략)
    // ...
}
    

// FastJSON 예시
@Test
public void testFastJsonBoolField() {
    Product product = new Product();
    product.setName("Hello");
    product.setPrice(12.36);

    String json = JSON.toJSONString(product);
    System.out.println(json); // 출력: {"name":"Hello","price":12.36,"sold":true}

    // 'sold' 필드가 Product 클래스에 없더라도, isSold() 메소드가 있으면 역직렬화는 가능
    // 단, 'sold' 필드에 대한 Setter가 없으므로, JSON에 'sold' 키가 있어도 값은 할당되지 않음
    Product product2 = JSON.parseObject(json, Product.class);
    System.out.println(product2.getName() + " " + product2.getPrice()); // 출력: Hello 12.36
}
    

위 예시처럼, `Product` 클래스에 `sold` 필드가 실제로 존재하지 않더라도, `isSold()` Getter 메소드가 존재하면 직렬화 결과에 `sold` 필드가 포함될 수 있습니다. 반대로, `sold` 필드가 클래스에 존재하더라도 해당 필드의 Setter 메소드가 없다면, JSON 문자열에 `sold` 키가 포함되어 있더라도 역직렬화 시 값을 가져올 수 없습니다.

주의: Java 프로그래밍 규약에 따라 boolean 타입 변수 앞에 `is` 접두사를 붙이는 것을 지양해야 합니다. 예를 들어 `Boolean isDeleted` 속성의 Getter/Setter 메소드는 `isDeleted()`/`setIsDeleted()`가 됩니다. 이 경우, 일부 역직렬화 도구(예: Jackson)는 해당 속성 이름을 `deleted`로 잘못 인식하여 `setDeleted()` 메소드를 찾으려 할 수 있으며, 해당 메소드가 없으면 예외가 발생할 수 있습니다.

2.2. 역직렬화 시 차이점

a. 알 수 없는 속성 처리

  • FastJSON (기본): JSON 문자열에 포함된 속성이 대상 클래스에 존재하지 않거나 해당 Setter 메소드가 없을 경우, 해당 속성을 무시하고 처리를 계속합니다.
  • Jackson (기본): JSON 문자열에 포함된 속성이 대상 클래스에 존재하지 않거나 해당 Setter 메소드가 없을 경우, 기본적으로 오류(UnrecognizedPropertyException)를 발생시킵니다.

// Jackson 예시 (기본 설정 시 오류 발생)
@Test
public void testJacksonUnknownProperty() throws Exception {
    Product product = new Product();
    product.setName("Hello");
    product.setPrice(12.36);

    ObjectMapper objectMapper = new ObjectMapper();
    String json = objectMapper.writeValueAsString(product);
    System.out.println(json); // {"name":"Hello","price":12.36}

    // JSON에 존재하지 않는 속성(예: "newField")이 있다면 오류 발생
    // String invalidJson = "{\"name\":\"Hello\",\"price\":12.36,\"newField\":\"someValue\"}";
    // Product product2 = objectMapper.readValue(invalidJson, Product.class); // 여기서 오류 발생

    // 오류를 무시하려면 ObjectMapper 설정 변경
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    String jsonWithUnknown = "{\"name\":\"Hello\",\"price\":12.36,\"newField\":\"someValue\"}";
    Product product3 = objectMapper.readValue(jsonWithUnknown, Product.class); // 오류 없이 처리됨
    System.out.println(product3.getName() + " " + product3.getPrice()); // Hello 12.36
}
    

Spring MVC의 MappingJackson2HttpMessageConverter는 ObjectMapper의 FAIL_ON_UNKNOWN_PROPERTIES 설정을 관리합니다. Spring 3에서는 이 설정이 기본적으로 true였으나, Spring 4 이상 버전부터는 false로 변경되었습니다. 따라서 최신 Spring 프레임워크 환경에서는 요청 JSON에 추가 필드가 있어도 별도 설정 없이 오류 없이 처리될 가능성이 높습니다.

b. Setter 메소드 미발견 시 필드 접근 시도

  • FastJSON: Setter 메소드를 찾지 못하면 해당 속성을 무시합니다.
  • Jackson: Setter 메소드를 찾지 못하면, 같은 이름의 필드(Field)에 직접 접근을 시도합니다.

public class Product {
    private String name;
    private Double price;
    private boolean sold; // 'sold' 필드가 실제로 존재

    // Getter 메소드: 'sold' 필드의 값을 반환
    public boolean isSold() {
        return this.sold;
    }

    // name, price 필드에 대한 Getter 및 Setter 메소드 (생략)
    // 'sold' 필드에 대한 Setter 메소드는 없음
}
    

@Test
public void testJacksonVsFastJsonFieldAccess() throws Exception {
    String json = "{\"name\":\"Hello\",\"price\":12.36,\"sold\":true}";

    // Jackson: 'sold' Setter가 없으므로, 'sold' 필드에 직접 접근하여 값을 설정
    ObjectMapper objectMapper = new ObjectMapper();
    Product jacksonProduct = objectMapper.readValue(json, Product.class);
    System.out.println("Jackson sold: " + jacksonProduct.isSold()); // 출력: Jackson sold: true

    // FastJSON: 'sold' Setter가 없으므로, 해당 속성을 무시
    Product fastjsonProduct = JSON.parseObject(json, Product.class);
    System.out.println("FastJSON sold: " + fastjsonProduct.isSold()); // 출력: FastJSON sold: false (기본값)
}
    

위 예시에서 `Product` 클래스에 `sold` 필드는 존재하지만 `setSold()` 메소드가 없습니다. Jackson은 `sold`라는 이름의 필드를 직접 찾아 값을 할당하지만, FastJSON은 Setter가 없으므로 `sold` 속성을 무시하고 기본값(`false`)으로 남겨둡니다.

태그: JSON Fastjson Jackson java 직렬화

11월 15일 21:49에 게시됨