Java 직렬화와 JSON 처리 - Jackson 활용



Jackson은 Java 객체와 JSON 간의 변환을 처리하는 가장 널리 사용되는 라이브러리입니다.



언제 사용하나요?



  • REST API 요청/응답 JSON 처리

  • 설정 파일 읽기/쓰기

  • 객체를 JSON 문자열로 로깅

  • 외부 API 응답 파싱



의존성 추가


<!-- Maven -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>

// Gradle
implementation \047com.fasterxml.jackson.core:jackson-databind:2.15.2\047


기본 사용법


import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();

// 객체 → JSON 문자열 (직렬화)
User user = new User("홍길동", 30);
String json = mapper.writeValueAsString(user);
// {"name":"홍길동","age":30}

// JSON 문자열 → 객체 (역직렬화)
String json = "{"name":"홍길동","age":30}";
User user = mapper.readValue(json, User.class);

// JSON 배열 → List
String jsonArray = "[{"name":"Kim"},{"name":"Lee"}]";
List<User> users = mapper.readValue(jsonArray,
new TypeReference<List<User>>() {});


ObjectMapper 설정


ObjectMapper mapper = new ObjectMapper();

// 알 수 없는 필드 무시 (중요!)
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

// null 값 무시
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

// 들여쓰기 (보기 좋게)
mapper.enable(SerializationFeature.INDENT_OUTPUT);

// 날짜 포맷
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

// Java 8 날짜/시간 지원
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);


주요 어노테이션


public class User {

// JSON 필드명 변경
@JsonProperty("user_name")
private String name;

// 직렬화에서 제외
@JsonIgnore
private String password;

// null이면 제외
@JsonInclude(JsonInclude.Include.NON_NULL)
private String nickname;

// 날짜 포맷
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthDate;

// 역직렬화 시 별칭
@JsonAlias({"userName", "user_name", "username"})
private String name;
}


클래스 레벨 어노테이션


// 알 수 없는 필드 무시
@JsonIgnoreProperties(ignoreUnknown = true)
public class User { }

// 특정 필드만 무시
@JsonIgnoreProperties({"password", "createdAt"})
public class User { }

// 필드 순서 지정
@JsonPropertyOrder({"id", "name", "age"})
public class User { }


파일 읽기/쓰기


// JSON 파일 쓰기
mapper.writeValue(new File("user.json"), user);

// JSON 파일 읽기
User user = mapper.readValue(new File("user.json"), User.class);

// InputStream에서 읽기
User user = mapper.readValue(inputStream, User.class);


복잡한 구조 처리


// 중첩 객체
public class Order {
private Long id;
private User user; // 중첩 객체
private List<Item> items; // 리스트
private Map<String, Object> metadata; // Map
}

// Generic 타입
List<User> users = mapper.readValue(json,
new TypeReference<List<User>>() {});

Map<String, Object> map = mapper.readValue(json,
new TypeReference<Map<String, Object>>() {});


JsonNode로 동적 파싱


// 구조를 모르는 JSON 파싱
JsonNode root = mapper.readTree(jsonString);

// 값 읽기
String name = root.get("name").asText();
int age = root.get("age").asInt();
boolean active = root.path("active").asBoolean(false); // 기본값

// 중첩 접근
String city = root.path("address").path("city").asText();

// 배열 순회
JsonNode items = root.get("items");
for (JsonNode item : items) {
System.out.println(item.get("name").asText());
}

// null 체크
if (root.has("email") && !root.get("email").isNull()) {
String email = root.get("email").asText();
}


커스텀 직렬화/역직렬화


// 커스텀 직렬화
public class MoneySerializer extends JsonSerializer<Money> {
@Override
public void serialize(Money value, JsonGenerator gen,
SerializerProvider provider) throws IOException {
gen.writeString(value.getAmount() + " " + value.getCurrency());
}
}

// 적용
@JsonSerialize(using = MoneySerializer.class)
private Money price;


Spring Boot에서 사용


// application.yml 설정
spring:
jackson:
serialization:
INDENT_OUTPUT: true
WRITE_DATES_AS_TIMESTAMPS: false
deserialization:
FAIL_ON_UNKNOWN_PROPERTIES: false
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Seoul

// 자동으로 ObjectMapper 빈 생성됨
@Autowired
private ObjectMapper objectMapper;


주의사항



  • ObjectMapper는 스레드 세이프 → 싱글톤으로 재사용

  • 기본 생성자 필요 (역직렬화 시)

  • getter/setter 또는 public 필드 필요

  • 순환 참조 주의 (@JsonIgnore 또는 @JsonManagedReference)