최근에 스스로 공부를 하면서 java14부터 도입된 record 기능을 사용해 보았는데
그냥 그럭저럭 사용을 해보았다.
문제는 그냥 사용을 하는데 그치는게 아니라 왜 불변 객체가 필요한지 이해를 못하고 있다는 점이다.
전에 들어본적 있는데 솔직히... 개발자로 계약해서 들어왔는데 코딩 업무 안주는거 뭐냐...?
ㅠㅠ...
하여간 이번 기회에 다시 무엇이 문제인지, 왜 필요한지 찾아보고 정리하려고 한다.
참고한 내용은 다음과 같다.
출처:
https://ko.wikipedia.org/wiki/%EB%B6%88%EB%B3%80%EA%B0%9D%EC%B2%B4
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Record.html
1. 불변 객체란 무엇인가?(What is Immutable Object?)
객체 지향 프로그래밍에 있어서 불변객체(immutable object)는 생성 후 그 상태를 바꿀 수 없는 객체를 말한다.
그 상태를 바꿀 수없는 객체를 불변 객체라고 한다.
그래... 사실 뭔지는 누구나 안다.
필자가 궁금한 점은 이것이 왜 중요하고, 후에 어떤 문제가 있기 때문에 이토록 많이 보이는가이다.
2. 불변 객체가 중요한 5가지 이유(The Five Reasons Why Immutable Object Is Important)
왜 불변 객체가 중요한지 인터넷을 찾아보았다.
구글에 찾아보니 "다음 프로젝트에 고려할 가치가 있는 불변 객체의 5가지 장점" 에 대해 소개하고 있다.
2-1. Thread Safety(쓰레드 안정성)
불변 객체는 생성된 후에 변하지 않기 때문에, 동기화 이슈를 신경쓰지 않아도 된다.
특정 쓰레드(thread)가 접근하는 객체의 버전에 상관없이 원래 객체의 상태를 보장한다.
만약 다른 버전의 객체가 필요하다면, 새로운 객체를 만들어야만 하기 때문이다.
이로 얻을 수 있는 이점은 더 간단하고, thread-safe 한 코드를 작성할 수 있다는 점이다.
2-2. No Invalid State(유효하지 않은 상태가 없음)
일단 불변 객체를 받아서 그것의 상태를 확인한다면, 우리는 그 객체가 항상 안전하게 남아있는걸 안다.
당신의 직접적인 지식 없이는 그 객체를 변경할 수 없다.
이에 더해, 그 객체를 완성하는데 필요한 모든 데이터들이 제공되어야만 한다.
이게 매우 유용한 상황은 높은 보안을 요구할 때다.
2-3. Better Encapsulation(더 나은 캡슐화)
불변 객체를 넘길때, 당신의 메소드를 더 좋게 캡슐화할 수 있다.
객체가 변하지 않는다는 것을 알면, 언제든지 원래 상태가 변하지 않기 때문에 어떤 일을하던 전달받은 객체를 다루는게 긍정적이다.
더욱이, 에러 발생시 디버깅이 쉬워진다.
객체의 상태가 변하지 않으니 에러 추적이 쉬워지기 떄문이다.
2-4. Simpler to Test(테스트하기 쉬움)
더 나은 캡슐화와 밀접하게 관련있는건 테스트하기 쉬운 코드다.
테스트 가능한 코드의 장점은 명백하고 거 견고하고 에러에 자유로운 코드 기반을 제공한다.
당신의 코드가 더 적은 부작용(Side Effect)를 제공하게끔 디자인 됬다면 그 코드들을 추적하기에 덜 복잡하다.
2-5. More readable and maintainable code(더 읽기 쉽고 유지하기 쉬운 코드)
불변 객체와 반대로 작동하는 모든 코드는 그 객체를 사용하는 위치에 어떻게 영향을 주는지 신경쓸 필요가 없다. 이것은 당신의 코드에서 덜 엮여있다는걸 의미한다.
그리므로, 당신의 코드는 이해하기 더 쉽고 깨끗해 보일 것이다.
정말 많은 장점이 존재하는구나!
그래서 그래... 사실 좋으니까 쓰겠지!
그런데 이러한 장점들을 어떻게 자바에서 구현해서 사용할 수 있는지도 필자는 더 궁금하다!
3. Java에서 불변객체 만들기(Make Immutable Object In Java)
이제 불변 객체를 생성해보도록 하겠다.
public final class TestClass {
private final String name;
private final int id;
public TestClass(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
}
위 코드를 보고 어떤 규칙을 따르면 되는지 살펴보자면...
- class를 final로 선언한다.
- 모든 field를 final과 private으로 만든다.
- setter는 만들지 않는다.
이정도 될 것이다.
해당 클래스를 인스턴스화해서 사용중이다가 정보를 변경하려면 이제 final 키워드를 선언했으니 그 어떤 성분도 수정이 불가능하다.
고로, 새로이 인스턴스를 선언해야한다.
4. Java에서 record 사용하기(use 'record' in java)
우리가 여태 이야기한 불변 객체를 java 의 record를 사용하면 쉽게 구현이 가능하다.
oracle에서 해당 record를 어떻게 설명하는지 보자.
A record class is a shallowly immutable, transparent carrier for a fixed set of values, called the record components.
A record declaration specifies in a header a description of its contents; the appropriate accessors, constructor,
equals, hashCode, and toString methods are created automatically. A record's fields are final because the class is intended to serve as a simple "data carrier".
여기서 주목해야 할 점은 불변 객체가 간단한 '데이터 캐리어'로서 제공되기 때문에 record가 final이라는 점이다.
우리는 record를 사용하기 전에 위에서 이미 final로 class를 선언한 적이 있다.
문서를 들여다보면 Canonical Constructor라는 표현이 나온다.
이 말은 아마 기본적으로 record가 생성자를 만들어 준다는 의미해서 canonical(표준적인)한 생성자를 만들어 준다고 표현한 것 같다.
또한, appropriate accessors, constructor, equals, hashCode, and toString methods are created automatically 라는 부분에서 알 수 있듯이 record로 선언시 많은 부분이 자동적으로 생성이 된다고 한다.
public record TestClass(String name, int id) {
}
해당 주소를 본 결과, java20에는 정말 많은 기능을 보완해주고 있다.(아니, 자바 20이 출시됐어? 무쳣다!)
너무 많아서 필자도 좀 오래 읽어봐야 할 것 같다.
여러분도 새로운 기능을 눈여겨보기 바란다.
이번 글의 목적은 왜 불변 객체가 필요한지 필자가 궁금해서 찾은 정보를 정리한 것이다.
record의 경우 다양하게 활용할 방안이 있는것 같은데
그것은 oracle에서 제공한 공식 tech문서에 자세히 명시되어 있으니 한 번 살펴보길 바란다.
'programming language > Java' 카테고리의 다른 글
[Java] 스레드 - Future, FutureTask, ComplatebleFuture? (2) | 2024.01.28 |
---|---|
[Java] @annotation을 통한 Response 데이터 변경하기 (feat : jackson) (0) | 2023.12.01 |
[Java] 배열 - Arrays 정리하기 : 2탄 - copyOfRange, equals, fill, deepEquals (0) | 2023.02.09 |
[Java] 배열 - Arrays 정리하기 : 1탄 - copyOf, asList, binarySearch (0) | 2023.02.09 |
Tree 구조(Structure)란? (0) | 2022.04.27 |