1. 들어가며
Java 개발을 하다 보면 직렬화(Serialization)는 네트워크 통신, 파일 저장, 캐시 관리와 같은 작업에서 중요한 역할을 합니다. 그러나 직렬화 과정에서 **NotSerializableException**과 같은 예외를 만날 수 있습니다. 특히, HttpSession 또는 세션에서 가져온 데이터를 사용하는 객체를 직렬화할 때 문제가 자주 발생합니다.
이 글에서는 세션에서 데이터를 가져와 사용하는 객체에서 발생한 직렬화 이슈를 중심으로 원인과 해결 방법을 소개합니다.
2. 문제 상황
아래는 세션에서 데이터를 가져와 사용하는 객체를 직렬화하려다 발생한 예외 상황입니다.
Object obj = this.request.getSession().getAttribute("vis");
위 코드에서 vis는 HashMap 객체로 저장되어 있습니다. 이를 포함한 객체를 직렬화하려 할 때 문제가 발생했습니다.
java.io.NotSerializableException: com.example.NonSerializableClass
3. 문제 원인 분석
Java에서 직렬화 가능한 객체가 되려면 해당 클래스와 포함된 모든 필드가 Serializable 인터페이스를 구현해야 합니다.
하지만 다음과 같은 상황이 문제를 일으켰습니다:
- HttpSession 자체는 직렬화 불가능
- HttpSession 객체를 직접 참조하면 직렬화가 불가능합니다.
- 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. 마무리하며
이 사례에서 직렬화 문제를 해결하기 위해 다음을 배웠습니다:
- 세션 객체 자체는 직렬화할 수 없으므로 transient로 선언합니다.
- 세션에서 필요한 데이터만 추출해 직렬화 가능한 형태로 저장합니다.
- 직렬화 불가능한 데이터를 필터링하거나, 데이터 구조를 변경합니다.
Tip: Java의 직렬화는 강력한 기능이지만, 데이터 구조와 직렬화 가능 여부를 미리 설계하면 문제를 사전에 방지할 수 있습니다.
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
Java로 TCP 프록시 서버 구현하기 - 라운드 로빈 로드밸런싱을 활용한 백엔드 서버 연결 (1) | 2025.01.02 |
---|