티스토리 뷰

반응형

Author: 니용

 

학창시절 배운 직렬연결, 출처:ZUM 학습백과


안녕하세요! 이번 글에서는 Java에서 종종 보게 되는 직렬화에 대해 알아보고자 합니다. 직렬이라고 하면 학창시절 과학시간에 전지가 일렬로 놓여져 있는 것을 종종 보셨을텐데, 그 개념을 그대로 따온 것이라고 이해하시면 빠르실 겁니다.

 

먼저 직렬화의 정의에 대해서 알아보겠습니다.

직렬화: 객체들을 일렬로 정렬해놓아 서로 정보 전달이 가능한 상태를 의미하고, 연속적으로 데이터를 변형하기 때문에 Stream을 사용하기 유리합니다.

역직렬화: 직렬화된 객체들을 다시 풀어놓아 원래의 상태로 복원하는 것을 의미합니다.

 

정의

직렬화 인터페이스는 기본적으로 serialVersionUID를 가지고 있습니다.

public interface Serializable {
    private static final long serialVersionUID = 1L;
}

여기서 정해진 serialVersionUID를 가지고 객체들을 일렬로 정렬시키는 메모리 주소의 기준이 됩니다.

 

 

사용법

@Data
@AllArgsConstructor
@NoArgsConstructor
public class SerialClass implements Serializable {
    private Integer id;
    private String name;
    private String password;
    private String address;
}

Java에서는 기본적으로 제공해주는 Serializable이라는 인터페이스가 존재합니다.

인터페이스를 상속받으면 직렬화가 가능한 클래스가 됩니다.

여기서 transient 키워드를 붙이게 되면 해당 property는 직렬화에서 제외대상이 됩니다.

@Data
@AllArgsConstructor
@NoArgsConstructor
public class SerialClass implements Serializable {
    private Integer id;
    private String name;
    private transient String password; // 비밀번호는 이 객체 직렬화에서 제외됨
    private String address;
}

이를 서비스에서 사용하기 위해서는 ObjectInputStream / ObjectOutputStream이라는 클래스를 사용합니다.

 

@Slf4j
public class UsingSerializable {
    public static void doSerialization() {
        try {
            ObjectOutputStream out = new ObjectOutputStream(new ByteArrayOutputStream());
            SerialClass sc1 = new SerialClass(1, "Niyo", "", "Seoul");
            SerialClass sc2 = new SerialClass(2, "Abbo", "", "Gyounggi");
            List list = new ArrayList<>();
            list.add(sc1);
            list.add(sc2);
            
            out.writeObject(sc1);
            out.writeObject(sc2);
            out.writeObject(list);
            out.close();
        } catch(Exception ignored) {}
    }
    
    public static void doDeserialization() {
        try {
            ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream());
            SerialClass sc1 = (SerialClass) in.readObject();
            SerialClass sc2 = (SerialClass) in.readObject();
            List list = (List) in.readObject();
            log.info("1 >> " + sc1); // SerialClass(1, Niyo, , Seoul)
            log.info("2 >> " + sc2); // SerialClass(2, Abbo, , Gyounggi)
            log.info("3 >> " + list); // [SerialClass(1, Niyo, , Seoul), SerialClass(2, Abbo, , Gyounggi)]
        } catch(Exception ignored) {}
    }
    
    public static void main(String[] args) {
        doSerialization();
        doDeserialization();
    }
}

 

근데... List도 있자나? 거기다가 넣어서 반환하면 안되나?

네 그렇습니다. List와의 차이가 있다면 메모리 주소를 기존에 사용하고 싶었던 객체를 그대로 사용하기에 차이가 있죠.

특이한 점은, Java 직렬화 형태의 데이터 교환은 Java System 간 데이터 교환을 위해 직렬화를 사용합니다. 

다시 말하면 직렬화는 Java 언어에서 시스템 개발 시 최적화되어 있고 기본적으로 제공해주는 API같은 개념이어서 쉽게 사용할 수 있을 뿐더러 JVM위에서 호환성이 높습니다. 

 

그럼 Java의 어디서 사용할까요?

1. Cache

자바 시스템에서 성능적인 부분을 향상시키기 위해 캐시 라이브러리 시스템을 사용합니다. 대표적으로 Redis가 있죠.

많은 수의 클래스를 다루는 자바에서는 MyBatis 등으로 조회된 데이터를 특정 주소에 담아두고, 똑같은 SQL이 호출되었을 때 다시 조회할 불필요함을 줄이기 위해 기존에 담아두었던 데이터를 재사용합니다. 이럴 때 캐시를 많이 사용하게 되는데 캐시의 구조가 직렬화구조입니다.

 

2. Servlet Session

서블릿 기반의 WAS(Web Application Server)는 세션의 자바 직렬화를 지원합니다. 그래서 세션에 담아두기 위한 객체는 Serializable 인터페이스를 선언해두어 사용하는 것을 추천합니다.

 

3. RMI(Remote Method Invocation)

다소 생소한 개념일 수도 있지만, RMI는 원격 시스템 간 메시지 교환을 위해 사용하는 기술입니다. 대개 원격 시스템을 통신하려면 IP와 Port를 사용하여 Socket 통신을 진행합니다. RMI는 이 부분이 추상화되어 원격 시스템의 메소드를 로컬화시켜 사용하도록 지원해줍니다. 

 

사용 시 주의해야할 점

 

1. 특이점이 없다면 serialVersionUID 값은 개발하며 직접 관리해주어야 합니다.

-> 직접 설정하지 않으면 내부적으로 자동으로 잡아주고 해쉬 값이 생성됩니다. 

-> 자동으로 설정해주고 클래스에 프로퍼티가 추가되거나 타입이 변경되면 역직렬화시 에러(InvalidClassException)가 발생합니다.

 

2. 외부 서버에 저장하지만 장기적으로 보관할 때는 가급적이면 사용하면 안됩니다.

-> 외부 서버에 저장하는 경우는 위의 3개(Cache, Session, RMI)이 해당됩니다.

-> 역직렬화시 장기간 지속된 정보는 오염된 데이터일 가능성이 높습니다.

-> 오염된 데이터는 사용할 수 없는 Garbage가 되어 GC시 삭제됩니다.

 

3. 개발자가 직접 설정한 클래스의 객체가 아닌 클래스는 직렬화를 사용하지 않는 것이 좋습니다.

-> @Component 또는 @Service 등의 클래스를 의미합니다. 

-> 스프링 프레임워크에서는 SecurityContextImpl 클래스를 지원해주는데, 버전에 따라 serialVersionUID 값이 변경됩니다.

 

마치며, 직렬화는 장점이 많지만 개념이 다소 생소한 개발자이거나 긴 시간 데이터를 유지하여야 하는 부분에서는 직렬화 사용을 안하는 것이 좋습니다. 역직렬화시 예외처리도 진행해주어야 하는 것도 번거로운 일 중에 하나여서 퍼포먼스를 올리기 위해 생산성이 떨어지는 경우도 생깁니다. 이 글이 직렬화를 기본적으로 이해하시는데 도움이 되셨으면 좋겠습니다!!

 

참고: https://woowabros.github.io/experience/2017/10/17/java-serialize.html

 

자바 직렬화, 그것이 알고싶다. 훑어보기편 - 우아한형제들 기술 블로그

자바의 직렬화 기술에 대한 대한 이야기입니다. 간단한 질문과 답변 형태로 자바 직렬화에 대한 간단한 설명과 직접 프로젝트를 진행하면서 겪은 경험에 대해 이야기해보려 합니다.

woowabros.github.io

 

반응형
댓글
공지사항