[JAVA] 직렬화(Serializable)와 역직렬화
직렬화란?
자바 직렬화는 객체를 바이트 스트림으로 변환하는 과정이다. 역직렬화는 그 반대로, 바이트 스트림을 객체로 복원하는 과정이다.
주로 다음과 같은 상황에서 사용한다:
- 객체를 파일로 저장할 때
- 네트워크로 객체를 전송할 때
- 캐시할 때
- 클립보드에 복사할 때
기본 사용법
직렬화 가능한 클래스 만들기
public class User implements Serializable {
private String name;
private int age;
private String email;
// 생성자, getter, setter 생략
}
객체 직렬화하기
User user = new User("John", 30, "john@example.com");
// 파일로 저장
try (ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("user.ser"))) {
out.writeObject(user);
}
역직렬화하기
try (ObjectInputStream in = new ObjectInputStream(
new FileInputStream("user.ser"))) {
User user = (User) in.readObject();
}
serialVersionUID 역할과 중요성
serialVersionUID는 직렬화된 객체의 버전 관리에 사용되는 식별자다. 클래스 구조가 변경될 때 버전 불일치를 감지할 수 있다.
명시적 정의
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String email;
}
IDE 자동 생성
IntelliJ IDEA에서는 Alt + Insert
(맥에서는 ⌘ + N
)를 눌러 자동 생성할 수 있다.
직렬화 제어
transient 키워드
직렬화에서 제외할 필드를 지정한다.
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
transient private String password; // 직렬화에서 제외
}
커스텀 직렬화
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// 추가적인 데이터 직렬화
out.writeObject(name.toUpperCase());
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
// 추가적인 데이터 역직렬화
String upperName = (String) in.readObject();
}
}
보안 고려사항
잠재적 위험
- 역직렬화 공격
- 민감한 데이터 노출
- 버전 관리 문제
보안 강화 예제
public class SecureUser implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private transient String password; // 민감한 정보는 제외
// 역직렬화 시 유효성 검사
private Object readResolve() {
// 기본적인 유효성 검사
if (name == null || name.isEmpty()) {
throw new IllegalStateException("Invalid user data");
}
return this;
}
}
대안과 권장사항
JSON 직렬화
// Jackson 라이브러리 사용
ObjectMapper mapper = new ObjectMapper();
// 직렬화
String json = mapper.writeValueAsString(user);
// 역직렬화
User user = mapper.readValue(json, User.class);
Protocol Buffers 사용
message User {
string name = 1;
int32 age = 2;
string email = 3;
}
직렬화 대신 권장되는 방법들
- REST API 사용
- JSON/XML 기반 통신
- 더 나은 버전 관리
- 플랫폼 독립적
- 데이터베이스 저장
- JPA/Hibernate 사용
- 구조화된 데이터 관리
- 더 나은 보안
적용 사례
캐시 시스템 구현
public class CacheEntry implements Serializable {
private static final long serialVersionUID = 1L;
private final Object data;
private final long timestamp;
private final long expiryTime;
public boolean isExpired() {
return System.currentTimeMillis() > timestamp + expiryTime;
}
}
분산 시스템에서의 사용
public class DistributedMessage implements Serializable {
private static final long serialVersionUID = 1L;
private final String messageId;
private final String content;
private final long timestamp;
// 메시지 무결성 검사
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
validateMessage();
}
private void validateMessage() {
if (messageId == null || content == null) {
throw new IllegalStateException("Invalid message");
}
}
}
자바 직렬화는 강력한 기능이지만, 신중하게 사용해야 한다. 다음 사항들을 기억하자:
- serialVersionUID는 항상 명시적으로 선언
- 민감한 데이터는 transient로 선언
- 가능하면 JSON 같은 표준 포맷 사용
- 커스텀 직렬화 메서드로 추가 보안 구현
- 역직렬화 시 항상 유효성 검사 수행
'개발&프로그래밍' 카테고리의 다른 글
[IntelliJ IDEA] 코드 리팩토링을 도와주는 플러그인 TOP 5 (0) | 2024.11.05 |
---|---|
[JAVA] try-with-resources와 AutoCloseable 인터페이스 (0) | 2024.11.04 |
[JAVA] equals()와 hashCode() 메서드 (0) | 2024.11.02 |
[JAVA] Stream의 map과 flatMap 차이 (0) | 2024.11.01 |
[JAVA] Java 컬렉션 프레임워크 성능 비교 (ArrayList vs LinkedList vs HashSet) (1) | 2024.10.24 |
댓글