본문 바로가기
프로그램 개발(분석, 설계, 코딩, 배포)/2.2.1 java

ObjectMapper의 스레드 안정성(스레드-세이프, Thread Safety)

by 3604 2025. 6. 19.
728x90

 

편집

ObjectMapper의 **스레드 안전성(Thread Safety)**에 대해 설명드리겠습니다.

ObjectMapper의 스레드 안전성

기본적으로 스레드-세이프함

  • ObjectMapper는 한 번 설정된 후에는 스레드-세이프합니다
  • 여러 스레드가 동시에 readValue(), writeValue() 등의 메서드를 호출해도 안전합니다

단, 설정 변경 시에는 주의 필요

 
java
ObjectMapper mapper = new ObjectMapper();

// ❌ 위험: 설정 변경은 스레드-세이프하지 않음
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);

// ✅ 안전: 읽기 작업은 스레드-세이프
String json = mapper.writeValueAsString(object);
MyObject obj = mapper.readValue(json, MyObject.class);

실무에서의 올바른 사용법

1. 싱글톤 패턴으로 사용 (권장)

 
java
public class JsonUtil {
    private static final ObjectMapper MAPPER = new ObjectMapper()
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
    
    public static <T> T readValue(String json, Class<T> valueType) throws Exception {
        return MAPPER.readValue(json, valueType);
    }
    
    public static String writeValueAsString(Object value) throws Exception {
        return MAPPER.writeValueAsString(value);
    }
}

2. Spring에서 Bean으로 등록

 
java
@Configuration
public class JacksonConfig {
    
    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .registerModule(new JavaTimeModule())
            .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
    }
}

@Service
public class MyService {
    private final ObjectMapper objectMapper;
    
    public MyService(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
    
    public void processData(String json) throws Exception {
        // 스레드-세이프하게 사용 가능
        MyData data = objectMapper.readValue(json, MyData.class);
    }
}

잘못된 사용 예시

❌ 매번 새로운 ObjectMapper 생성 (비효율적)

 
java
public String convertToJson(Object obj) throws Exception {
    ObjectMapper mapper = new ObjectMapper(); // 매번 생성 - 비효율적
    return mapper.writeValueAsString(obj);
}

❌ 멀티스레드 환경에서 설정 변경

 
java
public class UnsafeService {
    private ObjectMapper mapper = new ObjectMapper();
    
    public void method1() {
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 다른 스레드에서 동시에 설정을 변경하면 문제 발생 가능
    }
    
    public void method2() {
        mapper.setPropertyNamingStrategy(PropertyNamingStrategies.CAMEL_CASE);
        // 스레드-세이프하지 않음
    }
}

성능 최적화 관점

ObjectMapper 생성 비용

  • ObjectMapper 생성은 상당히 무거운 작업입니다
  • 내부적으로 리플렉션 정보, 직렬화 규칙 등을 초기화하기 때문

권장 사용 패턴

 
java
public class OptimizedJsonProcessor {
    // 애플리케이션 전체에서 하나의 인스턴스 공유
    private static final ObjectMapper MAPPER = createObjectMapper();
    
    private static ObjectMapper createObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.registerModule(new JavaTimeModule());
        return mapper;
    }
    
    public static <T> T fromJson(String json, Class<T> clazz) throws Exception {
        return MAPPER.readValue(json, clazz);
    }
    
    public static <T> T fromJson(String json, TypeReference<T> typeRef) throws Exception {
        return MAPPER.readValue(json, typeRef);
    }
    
    public static String toJson(Object obj) throws Exception {
        return MAPPER.writeValueAsString(obj);
    }
}

요약

  • ✅ 스레드-세이프: 설정 완료 후 읽기/쓰기 작업
  • ❌ 스레드-세이프 아님: 설정 변경 작업
  • 권장: 애플리케이션 시작 시 한 번 설정하고 싱글톤으로 사용
  • 성능: 매번 새로 생성하지 말고 재사용

따라서 앞서 제공한 코드에서 ObjectMapper를 매번 새로 생성하는 것보다는, 클래스 레벨의 상수로 선언하거나 스프링 빈으로 주입받아 사용하는 것이 더 효율적입니다.

728x90