Spring에서 Jackson 사용 시 snake_case 매핑 문제 해결하기
API를 구현하고 테스트하는 중, 분명히 요청 JSON에 값이 들어있는데도 DTO의 필드 값이 null로 들어오는 이슈가 있었다.
코드도 문제 없고, 필드명도 잘 맞췄다고 생각했는데, 알고 보니 클라이언트는 snake_case로 JSON을 보내고 있었고, 서버는 camelCase로 작성된 DTO로 값을 받도록 설계되어 있었던 것이 원인이었다.
이에 따라 이번 포스팅에서는 Jackson 라이브러리에서의 snake_case JSON 매핑 이슈와 그 해결 방법에 대해 정리해보려 한다.
💡 문제 상황
다음과 같은 요청 JSON이 클라이언트로부터 들어올 때,
{
"user_id": "kim123",
"user_name": "kim"
}
DTO는 일반적으로 자바 컨벤션을 따라 camelCase로 정의하고 있다.
public class UserDto {
private String userId;
private String userName;
// getters, setters
}
이때, 별도 설정 없이 Jackson 라이브러리를 사용할 경우 위 JSON은 제대로 매핑되지 않고, userId와 userName 필드는 null로 남게 된다.
🔎 원인 분석
Jackson은 기본적으로 camelCase 규칙에 따라 JSON과 Java 객체를 매핑한다.
따라서 JSON 필드가 snake_case로 들어오면 이를 Java 필드와 일치시키지 못하고 매핑 실패가 발생한다.
✅ 해결 방법
Jackson은 다양한 속성명 변환 전략(Naming Strategy) 을 제공한다.
snake_case ↔ camelCase 자동 변환을 위해서는 아래 설정이 필요하다.
1. ObjectMapper에 NamingStrategy 설정
Spring에서는 Jackson의 ObjectMapper를 직접 설정하여 사용할 수 있다.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
이 설정을 통해 user_id와 같은 필드가 DTO의 userId로 자동 변환되어 매핑된다.
2. 전역 설정
Jackson 설정을 전역 HTTP 요청/응답에 적용하려면, MappingJackson2HttpMessageConverter에 ObjectMapper를 주입해야 한다.
@Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
return new MappingJackson2HttpMessageConverter(objectMapper);
}
XML 설정 방식
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="propertyNamingStrategy">
<util:constant static-field="com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE"/>
</property>
</bean>
</property>
</bean>
📌 주의할 점
1. DTO 필드는 반드시 camelCase로 유지
Jackson이 변환을 자동으로 해주기 때문에, DTO 클래스는 자바 컨벤션을 따라 camelCase로 작성해야 한다.
public class UserDto {
private String userId; // OK
private String user_name; // ❌ 비추천
}
user_name처럼 필드를 snake_case로 작성하면 오히려 매핑이 중복되거나 문제가 생길 수 있다.
2. 일부 필드만 매핑하려면 @JsonProperty 사용
전역 설정이 불가능하거나, 특정 필드만 다르게 매핑하고 싶을 경우 아래처럼 @JsonProperty 어노테이션을 사용할 수 있다.
public class UserDto {
@JsonProperty("user_id")
private String userId;
@JsonProperty("user_name")
private String userName;
}
단, 필드마다 반복적으로 선언해야 하므로 유지보수성이 떨어지고, 전체적으로 snake_case를 사용하는 구조라면 전역 설정이 훨씬 효율적이다.
✅ 정리
설정 | 방법 | 설명 |
전역 설정 | ObjectMapper + NamingStrategy | 전체 DTO에 자동 매핑 적용 |
개별 설정 | @JsonProperty("user_id") | 필드 단위 수동 매핑 |
매핑 전략 | camelCase ↔ snake_case | 자동 변환 가능 (설정 필요) |
마무리
이번 경험을 통해 클라이언트와 서버 간 네이밍 컨벤션의 차이가 단순한 스타일 문제가 아니라, 실제 동작 오류로 이어질 수 있다는 점을 알게 되었다.
특히 Jackson을 사용할 때, 클라이언트가 snake_case, 서버가 camelCase를 사용하는 구조라면, PropertyNamingStrategy 설정은 필수적이라는 것도 알게 되었다.
앞으로는 API 설계 시 초기 단계에서 네이밍 컨벤션을 명확히 정하고, 전역 설정을 통해 매핑 이슈를 미리 방지하려 한다.