(삽질 기념) 자바 직렬화에 대해서 알아봅시다.

chisae·2024년 4월 21일

끄적이기

목록 보기
11/12
post-thumbnail

안녕하세요, 오늘은 자바 직렬화에 대해서 알아보고자 합니다, 최근 ObejctMapper 사용 시 주의점을 잘 파악하지 않고 사용하다가 시간을 많이 쓰기도 했고, 재작년(2022년) POCU 객체지향프로그래밍 실습 강의 이후에, 백엔드 공부와 인프라 공부 등 다른 공부를 하느라 자바에 많이 소홀해진 감이 있어서, 오늘 다시 한번 공부해보고자 합니다 ~ !




자바 직렬화란 무엇인가?

개념

자바 직렬화를 요약하자면 아래와 같습니다.

자바 시스템 내에서 사용되는 객체를 바이트 형태로 변환하는 과정

즉, 자바 직렬화란 자비 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터를 변환하는 기술을 의미합니다, 이때 변환된 바이트 스트림은 파일, 데이터베이스, 메모리 등 다양한 저장 매체에 저장되거나 네트워크를 통해 다른 JVM(Java Virtual Machine)으로 전송될 수 있습니다.


바이트 스트림이란?
  : 데이터를 바이트 단위로 주고 받는 것
  이를 통해 파일, 네트워크, 메모리 등 다양한 입력 또는 출력 소스와의 데이터 교환이 가능
  - 종류
  	- InputStream
     : 이 클래스는 데이터를 읽을 때 사용
    - OutputStream
     : 이 클래스는 데이터를 쓸 때 사용



자바 직렬화는 왜 필요할까?

그럼 자바 직렬화 과정은 왜? 필요한 걸까요 사실 이 글을 쓰게된 이유라고 볼 수도 있기 때문에 유의깊게 볼 필요가 있는 거 같습니다, 그렇기에 데이터 직렬화 종류의 대해서 먼저 살펴보고 왜 직렬화가 필요한지 알아봅시다 ~ !

문자열 기반 직렬화

문자열 형태로 확인이 가능한 직렬화 방법으로, 범용적으로 API나 데이터를 변환하여 추출할 수 있습니다, CSV의 경우 다랑의 데이터를 표형태로 직렬화 시, XML은 구조적인 데이터를, JSON 형태는 최근에 많이 사용되고 있습니다

  • CSV
    • 자바에서는 간단한 문자열 포맷팅을 통해 CSV를 생성할 수 있으며, 데이터를 표현하는 가장 많이 사용되는 방법 중 하나입니다.
      치새황,chisaehwang@naver.com
    • 자바에서 사용방법
      Member member = new Member("치새황", "chisaehwang@naver.com", 25);
      // member객체를 csv로 변환 
      String csv = String.format("%s,%s,%d",member.getName(), member.getEmail(), member.getAge()); 
      System.out.println(csv);



  • JSON
    • 최근에 가장 많이 사용되는 포맷으로 JS에서 쉽게 사용 가능하고, 다른 데이터 포맷 방식에 비해서 오버헤드가 적어 인기가 많습니다.
      { 
      name: "치새황",
      email: "chisaehwang@naver.com",
      age: 22
      }
    • 자바에서 사용방법
      Member member = new Member("치새황", "chisaehwang@naver.com", 22);
      // member객체를 json으로 변환 
      String json = String.format(
            "",
            member.getName(), member.getEmail(), member.getAge());
      System.out.println(json);


이진 직렬화

이진 직렬화는 뜻 그대로 데이터 구조나 객체 상태를 이진 형식의 연속된 바이트로 변환하는 과정입니다, 때문에 데이터 변환 및 전송 속도에 최적화하여 직렬화 할 수 있는 방법이며, 전송 방법에 대한 추가적인 공부가 필요한 부분이지만, 오늘은 프로토컬 버퍼만 살펴보도록 하겠습니다.

  • Protocol Buffer (프로토콜 버퍼)

    • 프로토콜 버퍼는 구글에서 개발한 데이터 직렬화 프로토콜입니다.

    • 자바에서 사용법

      • 프로토콜 버퍼를 사용하기 위해서는 먼저 .proto 파일에 데이터 구조를 정의해야 합니다.
      message Member {
        optional string name = 1;
        optional int32 id = 2;
        optional string email = 3;
      }
      • 이렇게 기쉴된 Member.proto 문서는 프로토콜 버퍼 컴파일러를 이용해서 개발하기 원하는 언어로 변환할 수 있습니다, 자바로 변환할 경우 프로토콜 버퍼 형태의 Member 클래스를 생성합니다.

        Member member = Member.newBuilder()
            .setAge(22)
            .setName("치새황")
            .setEmail("chisaehwang@naver.com")
            .build();
        ByteArrayOutputStream baos = new ByteArrayOutputStream()
        member.writeTo(baos);
        // 프로토콜 버퍼 직렬화된 데이터
        byte[] serializedMember = baos.toByteArray();   

이렇듯 직렬화에는 여러가지 방법이 있습니다,


그래서 왜 필요할까

앞서, CSV, JSON, 프로토콜 버퍼 등 직렬화에는 여러가지 방법이 있고, 시스템 고유 특성과 상관없는 대부분의 시스템에서의 데이터 교환 시 사용될 수 있습니다, 하지만 자바 직렬화의 경우 Java 시스템 간의 데이터 교환을 용이하게 하기 위해 존재합니다, 아래 그림을 참고하시면 도움이 되실겁니다.


https://techblog.woowahan.com/2550/
출처

굳이 자바 직렬화를 쓰는 이유

그러면 CSV, JSON과 같은 방법을 사용하면 되는거지, 왜? 자바 직렬화를 써야 할까요? 장점과 단점을 비교하며 알아보도록 합시다.

  • 장점
    • 모든 객체 상태를 정확히 저장 및 복원하는 등 자바 개발에 최적화되어 있음,
    • 별도의 라이브러리 필요없음
    • 네트워크를 통해서 쉽게 객체 바이트 스트림 전송 가능
  • 단점
    • 큰 객체 처리시 CPU 과소비로 성능 이슈가 있음
    • 역직렬화 시 악의적 데이터에 취약해서 보안이 위험할 수 있음
    • 자바 특화로 되어있기에 다른 플랫폼과의 호환이 어려움

어? 이렇게 보면 장점과 단점이 둘다 비슷비슷한 거 같습니다.. 즉, 결론은 정답은 없습니다, 기존에 개발이 다 그렇듯 상황에 따라서 적절하게 처리하는게 가장 중요합니다.



자바 직렬화 사용법

그러면 자바 직렬화는 어떻게 사용할 수 있을까요?

  • Serializable

    • 가장 기본적인 방법으로 Serializable 인터페이스를 구현함으로써, 객체를 직렬화하고 역직렬화할 수 있습니다.

          import java.io.*;
      
      public class SerializationExample {
          public static void main(String[] args) {
              // 직렬화할 객체 생성
              User user = new User("치새황", 22);
      
              // 객체를 파일에 직렬화하기
              try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
                  oos.writeObject(user);
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              // 파일에서 객체 역직렬화하기
              try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
                  User deserializedUser = (User) ois.readObject();
                  System.out.println("Deserialized User: " + deserializedUser.getName());
              } catch (IOException | ClassNotFoundException e) {
                  e.printStackTrace();
              }
          }
      
          static class User implements Serializable {
              private String name;
              private int age;
      
              public User(String name, int age) {
                  this.name = name;
                  this.age = age;
              }
      
              public String getName() {
                  return name;
              }
          }
      }
      
  • Google Gson

    • Google Gson 라이브러리를 사용하면 JSON 형태로 객체를 직렬화 할 수 있습니다, 이 방법은 네트워크를 통한 데이터 교환에 특히 유용합니다.

       import com.google.gson.Gson;
      
       public class GsonSerialization {
           public static void main(String[] args) {
               User user = new User("치새황", 22);
      
               // Gson 객체 생성
               Gson gson = new Gson();
      
               // 객체를 JSON으로 직렬화
               String json = gson.toJson(user);
               System.out.println("JSON Representation: " + json);
      
               // JSON을 객체로 역직렬화
               User deserializedUser = gson.fromJson(json, User.class);
               System.out.println("Deserialized User: " + deserializedUser.getName());
           }
      
           static class User {
               private String name;
               private int age;
      
               public User(String name, int age) {
                   this.name = name;
                   this.age = age;
               }
      
               public String getName() {
                   return name;
               }
           }
       }
      
  • ObjectMapper

    • Gson과 유사하게 동작하지만 성능과 기능면에서 더 많은 옵션을 제공하고 있습니다.

      import com.fasterxml.jackson.databind.ObjectMapper;
      
      public class JacksonSerialization {
          public static void main(String[] args) throws IOException {
              User user = new User("John Doe", 30);
      
              // ObjectMapper 인스턴스 생성
              ObjectMapper mapper = new ObjectMapper();
      
              // 객체를 JSON으로 직렬화
              String json = mapper.writeValueAsString(user);
              System.out.println("JSON Representation: " + json);
      
              // JSON을 객체로 역직렬화
              User deserializedUser = mapper.readValue(json, User.class);
              System.out.println("Deserialized User: " + deserializedUser.getName());
          }
      
          static class User {
              private String name;
              private int age;
      
              public User(String name, int age) {
                  this.name = name;
                  this.age = age;
              }
      
              public String getName() {
                  return name;
              }
          }
      }
      



자바 직렬화는 언제 어디서 사용될까?

자바 직렬화의 경우 다양한 상황에서 유용하게 사용할 수 있습니다,

  • 서블릿 세션 관리
    : 서블릿 기반의 서버에서는 사용자의 세선 정보를 직렬화 하여 파일, DB 또는 세션 클러스터에 저장합니다, 때문에 서버가 재시작 되어도 세션 정보를 유지할 수 있게 해주며 세션에 필요한 객체들은 Serializable 인터페이스를 구현해야 합니다.
  • 캐싱
    : 자바 시스템에서는 데이터 처리 성능을 향상시키기 위해선 캐시 시스템을 사용해야 합니다, Redis, Memcached 같은 캐시 라이브러리를 통해 자주 사용되는 데이터 객체를 직렬화 하여 저장하고, 동일한 요청이 오면 DB에 다시 접근하는 대신 저장도니 객체를 빠르게 응답하여 자원 사용을 줄이고 응답 시간을 단축할 수 있습니다.
  • 자바 RMI (Remote Method Invocation)
    : 원격 시스템 칸 메소드 호출 시 자바 RMI를 사용하여 메소드 호출을 쉽게할 수 있습니다, 호출 객체는 직렬화 를 통해 네트워크로 전송되며, 원격지에서는 이를 역직렬화하여 사용합니다, 즉 복잡한 네트워크 통신을 추상화하여 개발자가 원격 메소드를 로컬 메소드처럼 사용할 수 있도록 해줍니다.



오늘은 이렇게 자바 직렬화에 대해서 알아봤습니다, 이번에 서비스를 개발하면서 ObjectMapper를 잘못 사용해서 30분 정도 삽질을 하게 됐는데요.. 때문에 다시는 이런일이 발생하지 않길 바라며 자바 직렬화에 대해서 알아봤는데, 조금은 더 잘 모르는 기술을 사용할 때 경각심을 깨워준 거 같습니다.. 솔직히 ObjectMapper가 뭔지만 알면 알 수 있었던 문제라 무지성으로 GPT가 알려준 정보만으로 만족하지 말고 기술 블로그나 Tech 유튜브, Document를 많이 읽어봐야할 거 같습니다.. 굿 !

감사합니다.



참고
https://techblog.woowahan.com/2550/

profile
초보 개발자

0개의 댓글