JAVA

Java의 직렬화(Serialize)

wellbell 2021. 6. 26. 23:56

직렬화(Serialize)

  • 자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 byte 형태로 데이터를 변환하는 기술.
  • JVM(Java Virtual Machine 이하 JVM)의 메모리에 상주(힙 또는 스택)되어 있는 객체 데이터를 바이트 형태로 변환하는 기술

역직렬화(Deserialize)

  • byte로 변환된 Data를 원래대로 Object나 Data로 변환하는 기술을 역직렬화(Deserialize)라고 부릅니다.
  • 직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태.

 

직렬화방법에는 여러 Format이 존재합니다.

  • 표형태의 다량의 데이터를 직렬화할때는 CSV형태
Member member = new Member("김배민", "deliverykim@baemin.com", 25);
  // member객체를 csv로 변환 
  String csv = String.format("%s,%s,%d",member.getName(), member.getEmail(), member.getAge()); 
  System.out.println(csv);
  • 구조적인 데이터는 XML, JSON형태.

-  no 라이브러리

Member member = new Member("김배민", "deliverykim@baemin.com", 25);
  // member객체를 json으로 변환 
  String json = String.format(
          "{\"name\":\"%s\",\"email\":\"%s\",\"age\":%d}",
          member.getName(), member.getEmail(), member.getAge());
  System.out.println(json);        

- Jackson

// 직렬화
ObjectMapper mapper = new ObjectMapper(); 
String jsonResult = mapper.writeValueAsString(json으로 바꾸고싶은 java객체);

// 역직렬화
String jsonInput = "json 데이터";
ObjectMapper mapper = new ObjectMapper();
Example exam = mapper.readValue(jsonInput, Example.class);

- Gson

// 직렬화
String jsonResult = new Gson().toJson(json으로 바꾸고싶은 java객체);

// 역직렬화
String jsonInput = "json 데이터";
Example exam = new Gson().fromJson(jsonInput, Example.class);

 

어디에 사용되는가?

서블릿 세션 (Servlet Session)

  • 세션을 서블릿 메모리 위에서 운용한다면 직렬화를 필요로 하지 않지만, 파일로 저장하거나 세션 클러스터링, DB를 저장하는 옵션 등을 선택하게 되면 세션 자체가 직렬화가 되어 저장되어 전달됩니다.

캐시 (Cache)

  • Ehcache, Redis, Memcached 라이브러리 시스템을 많이 사용됩니다.

자바 RMI(Remote Method Invocation)

  • 원격 시스템 간의 메시지 교환을 위해서 사용하는 자바에서 지원하는 기술.

자바의 직렬화 단점?

1. 역직렬화시 클래스 구조 변경 문제

특정 클래스를 직렬화한 뒤

해당 클래스의 변경이 발생하였을 때

역직렬화를 진행하면 java.io.InvalidClassException이 발생

이를 막기 위해서 모델의 버젼간의 호환성을 유지하기 위해서는 SUID(serialVersionUID)를 정의해야합니다.

 

ex)

import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = -5129628467395047900L;
    
    String name;
    int age;
    double height;
    String hobby;
}

 

2. 직렬화 Data Size 문제

간단한 객체의 내용도 직렬화시 2배이상의 차이

- 메모리기반의 Cache에서는 Data를 저장할 수 있는 용량의 한계가 있기 때문에 Json 형태와 같은 경량화된 형태로 직렬화하는 것도 좋은 방법

 

정리

  • 외부 저장소로 저장되는 데이터는 짧은 만료시간의 데이터를 제외하고 자바 직렬화를 사용을 지양합니다.
  • 역직렬화시 반드시 예외가 생긴다는 것을 생각하고 개발합니다.
  • 자주 변경되는 비즈니스적인 데이터를 자바 직렬화을 사용하지 않습니다.
  • 긴 만료 시간을 가지는 데이터는 JSON 등 다른 포맷을 사용하여 저장합니다.

사용 경험

Spring Framework(boot, webflux)과 Redis 연동

- String, String

@Configuration
public class BasicRedisConfig {

    ...

    @Primary
    @Bean
    ReactiveRedisOperations<String, String> redisOperations(ReactiveRedisConnectionFactory factory) {
        RedisSerializer<String> serializer = new StringRedisSerializer();
        RedisSerializationContext<String, String> serializationContext = RedisSerializationContext
                .<String, String>newSerializationContext()
                .key(serializer)
                .value(serializer)
                .hashKey(serializer)
                .hashValue(serializer)
                .build();

        return new ReactiveRedisTemplate<>(factory, serializationContext);
    }
}

- String, Oject

@Configuration
public class RedisConfig {

    @Value("${spring.redis.port}")
    public int port;

    @Value("${spring.redis.host}")
    public String host;

    @Autowired
    public ObjectMapper objectMapper;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }
}

Reference

https://woowabros.github.io/experience/2017/10/17/java-serialize2.html