아직은 NULL NULL 합니다

SerialVersionUID는 무엇이고 이를 선언해야 하는 이유가 무엇일까요? 본문

Computer Science/Java

SerialVersionUID는 무엇이고 이를 선언해야 하는 이유가 무엇일까요?

is낫널 2023. 9. 22. 16:18
728x90
서론

안녕하세요. is낫널입니다.

나중에 이름을 바꾸게 될 수도 있지만 지금은 마음 가는 대로 짓게 된 이름인데 제 가치관을 표현해 줄 이름으로 

생각해 내서 바꿔보려구요. 그게 이 블로그를 들어오는 사람들에게 제 이미지가 됐으면 좋겠네요! 

오늘은 날이 되게 따뜻합니다. 이런 날도 이제 얼마 남지 않았어요. 곧 겨울이 올 것 같습니다. 

12월이 되기 전에 취직을 해서 삿포로 가고 싶네요.

아 좀 주저리주저리 하는 타입이라, 의식의 흐름대로 말이 나오는 것 같습니다 ㅋㅋㅋ 

이제 시작할게요. 

 

SerialVersionUID를 선언해야 하는 이유

먼저 이 글을 들어온 이유로 이게 가장 궁금한 부분일 거 같습니다. 

그래서 두괄식으로 결론부터 딱 내놓자면

직렬화 할 때와 역직렬화할 때 SerialVersionUID 라는 클래스의 버전이 일치해야 하기 때문입니다. 

 

자세히 설명하자면, SerialVersionUID 라는 클래스의 버전은 객체가 직렬화될 때

클래스에 정의된 멤버변수의 정보를 이용해서 자동으로 생성되어 직렬화 내용에 포함됩니다. 

그리고 역직렬화 할 때 클래스의 버전을 비교함으로써, 직렬화할 때의 클래스 버전과 일치하는지 확인할 수 있는 것입니다. 

 

SerialVersionUID 의 값이 달라지는 이유

그렇다면, 왜 SerialVersionUID의 값이 달라지는 일이 발생하게 되는 걸까요? 

보통 SerialVersionUID의 값이 자동으로 생성되는 데 있어, 역직렬화할 때 클래스의 버전이 갑자기 달라지는 경우가 크게 없지만 
직렬화했던 클래스의 내용이 변경되었을 경우 새로운 SerialVersionUID 를 자동 생성하기 때문에 기존 역직렬화가 구성된 클래스에서 

클래스 버전이 일치하지 않다는 에러문구가 뜨게 됩니다. 

 

이유는 SerialVersionUID 의 값을 생성해 내는 계산은 클래스의 세부사항에 매우 민감하게 반응하기 때문에

컴파일러 구현체에 따라서 달라질 수 있어서 역직렬화 과정에서 예상하지 못한 InvalidClassException 에러를 유발할 수 있습니다.

이것은 클래스의 이름이 똑같다고 하더라도 클래스 버전이 달라지는 경우가 발생합니다. 

 

하지만 만약 여기서, static 변수나 transient 키워드가 붙은 인스턴스변수는 직렬화에 영향을 끼치지 않기 때문에

클래스의 버전을 다르게 인식하도록 할 필요가 없습니다. 

static 에 대해서는 보통 아실 테지만, transient 키워드는 직렬화 대상을 제외시키도록 하는 제어자입니다. 

이 키워드가 어떨 때 쓰이는지는 밑에서 조금 더 자세히 설명하도록 하겠습니다. 

 

 

SerialVersionUID 를 수동으로 설정하는 방법

자동으로 생성되는 경우가 아닌, 직접 사용자가 수동으로 설정하는 방법은 어떻게 해야 하는지 간단하게 설명해 보겠습니다.
SerialVersionUID 의 값은 정수값이면 어떠한 값으로도 지정할 수 있지만, 서로 다른 클래스 간에 같은 값을 갖지 않도록

serialver.exe 를 사용해서 생성된 값을 사용하는 것이 보통이라고 합니다.

그래서 serialver.exe 뒤에 SerialVersionUID 를 얻고자 하는 클래스 이름만 적어주면

클래스의 SerialVersionUID 를 얻어낼 수 있게 됩니다. 

그러나, 이클립스 3.3 이후부터는 serialver.exe 로 생성되는 값을 자동으로 생성해 주는 방식이 생겼는데

사진으로 설명드리겠습니다. 

 

먼저 직렬화가 구현되어있는 클래스에서 노란색 경고표시가 뜨고 있는 것을 확인할 수 있습니다.

이때 빨간색이 아닌 노란색인 이유는 SerialVersionUID의 값이 자동으로 생성되기는 하나, 수동으로 선언하는 것을 권장하기 때문에 

이클립스 쪽에서는 노란색으로 경고표시를 띄우는 것입니다. 

그러면 저 노란색 밑줄이 그어진 Person 위에 마우스를 올리면 SerialVersionUID 값을 생성할 수 있는데

default 는 기본값이므로 1L 를 생성해 주기 때문에, 그 아래의 generated SerialVersionUID 값을 눌러줘야 합니다

출처 : 스크린샷도 아니고, 제가 직접 찍은 사진인데 그러게요 .. 왜 스크린샷이 안될까요 하하

 

누르게 되면 이와 같이 SerialVersionUID 값이 생성되는 것을 확인할 수 있습니다. 

출처 : 마우스 올리면 뜨는 부분만 스크린샷을 못해서 이렇게 그냥 스크린샷 찍는건 가능하더라구요. 어쨌든 출처는 접니다!

 

transient 란?

아까 설명드리기로 한 transient 제어자에 대해서 짚고 넘어가보겠습니다. 

간단하게 위에서도 언급했었지만, 제어자 transient 를 인스턴스 변수 앞에 붙이게 되면, 직렬화 대상에서 제외할 수 있게 됩니다. 

 

즉, 클래스 내용안에 직렬화가 안 되는 객체에 대한 참조를 포함하고 있다면 transient 키워드를 붙여서 제외할 수 있습니다. 

보통 Object obj = new Object(); 의 경우 직렬화를 할 때 에러가 발생하면서 실패하는데

그 이유는 모든 객체의 최고 조상인 Object 클래스에는 Serializable 인터페이스가 구현되어있지 않기 때문입니다.

이럴 때 직렬화 하려는 클래스 내부에 해당 인스턴스 변수가 존재할 경우 아래 코드와 같이 앞에 붙여주면 직렬화 대상에서 제외됩니다.

코드로 살펴보자면, 

class Person implements java.io.Serializable {
		String name;
		int age;

		transient Object obj = new Object();
}

이렇게 간단하게 붙여주면 끝납니다! 

추가적으로 만약 Object obj = new String(”abc”); 로 하게 될 경우에는 직렬화가 가능해집니다. 

저장된 객체가 Object 가 아닌 String 인스턴스이기 때문이에요. 

인스턴스변수의 타입이 아닌 실제로 연결된 객체의 종류에 의해서 결정된다는 것을 기억해 두면 좋을 것 같습니다. 

 

마무리 

 

오늘도 끝까지 봐주신 분들께 감사드립니다. 

이번 이 주제는 남궁 성 선생님의  Java 의 정석 책으로 공부하고 참고하였습니다. 

 

 

728x90