프로그래밍 언어/JAVA

Java 직렬화와 NotSerializableException 문제

코딩금융치료 2025. 1. 2. 12:11

1. 들어가며

Java 개발을 하다 보면 직렬화(Serialization)는 네트워크 통신, 파일 저장, 캐시 관리와 같은 작업에서 중요한 역할을 합니다. 그러나 직렬화 과정에서 **NotSerializableException**과 같은 예외를 만날 수 있습니다. 특히, HttpSession 또는 세션에서 가져온 데이터를 사용하는 객체를 직렬화할 때 문제가 자주 발생합니다.

이 글에서는 세션에서 데이터를 가져와 사용하는 객체에서 발생한 직렬화 이슈를 중심으로 원인과 해결 방법을 소개합니다.

 

2. 문제 상황

아래는 세션에서 데이터를 가져와 사용하는 객체를 직렬화하려다 발생한 예외 상황입니다.

Object obj = this.request.getSession().getAttribute("vis");

 

위 코드에서 vis는 HashMap 객체로 저장되어 있습니다. 이를 포함한 객체를 직렬화하려 할 때 문제가 발생했습니다.

java.io.NotSerializableException: com.example.NonSerializableClass

 

3. 문제 원인 분석

Java에서 직렬화 가능한 객체가 되려면 해당 클래스와 포함된 모든 필드가 Serializable 인터페이스를 구현해야 합니다.
하지만 다음과 같은 상황이 문제를 일으켰습니다:

  1. HttpSession 자체는 직렬화 불가능
    • HttpSession 객체를 직접 참조하면 직렬화가 불가능합니다.
  2. HashMap 내부의 직렬화 불가능한 값
    • vis가 HashMap 타입이지만, 그 내부에 저장된 값 중 일부가 직렬화 불가능한 객체였습니다.

4. 해결 방법

4.1. 세션에서 필요한 데이터만 추출

직렬화 가능한 데이터만 필드로 저장하고, 세션 객체는 직렬화에서 제외합니다.

import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class SerializableExample implements Serializable {
    private static final long serialVersionUID = 1L;

    private HashMap<String, String> visData; // 직렬화 가능한 데이터만 저장
    private transient HttpSession session; // 직렬화 제외

    public SerializableExample(HttpServletRequest request) {
        this.session = request.getSession();
        this.visData = (HashMap<String, String>) session.getAttribute("vis");
    }

    public HashMap<String, String> getVisData() {
        return visData;
    }
}

 

설명:

  • transient 키워드를 사용해 HttpSession을 직렬화 대상에서 제외합니다.
  • 세션에서 vis 데이터를 가져와 직렬화 가능한 HashMap<String, String> 형태로 저장합니다.

4.2. HashMap 내부 값 확인 및 필터링

HashMap 내부의 값 중 직렬화 불가능한 객체가 있으면 직렬화 가능한 값만 필터링합니다.

HashMap<String, Object> vis = (HashMap<String, Object>) session.getAttribute("vis");
HashMap<String, Serializable> serializableVis = new HashMap<>();

for (Map.Entry<String, Object> entry : vis.entrySet()) {
    if (entry.getValue() instanceof Serializable) {
        serializableVis.put(entry.getKey(), (Serializable) entry.getValue());
    }
}

 

4.3. 직렬화 불가능한 필드 제외

직렬화가 필요하지 않은 필드는 아예 직렬화 대상에서 제외합니다.

private transient HashMap<String, Object> vis;

5. 직렬화 문제를 해결한 코드

최종적으로 아래와 같은 구조를 사용할 수 있습니다:

 

import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

public class UserData implements Serializable {
    private static final long serialVersionUID = 1L;

    private HashMap<String, String> visData; // 직렬화 가능 데이터만 저장
    private transient HttpSession session; // 직렬화 제외

    public UserData(HttpServletRequest request) {
        this.session = request.getSession();
        this.visData = new HashMap<>();

        // 세션 데이터 필터링
        HashMap<String, Object> vis = (HashMap<String, Object>) session.getAttribute("vis");
        if (vis != null) {
            for (Map.Entry<String, Object> entry : vis.entrySet()) {
                if (entry.getValue() instanceof String) {
                    this.visData.put(entry.getKey(), (String) entry.getValue());
                }
            }
        }
    }

    public HashMap<String, String> getVisData() {
        return visData;
    }
}

6. 마무리하며

이 사례에서 직렬화 문제를 해결하기 위해 다음을 배웠습니다:

  1. 세션 객체 자체는 직렬화할 수 없으므로 transient로 선언합니다.
  2. 세션에서 필요한 데이터만 추출해 직렬화 가능한 형태로 저장합니다.
  3. 직렬화 불가능한 데이터를 필터링하거나, 데이터 구조를 변경합니다.

Tip: Java의 직렬화는 강력한 기능이지만, 데이터 구조와 직렬화 가능 여부를 미리 설계하면 문제를 사전에 방지할 수 있습니다.