일급 컬렉션이란? (08.28)

참고 링크

일급 컬렉션이란 단어는 소트웍스 앤솔로지의 객체지향 생활체조 파트에서 언급된 내용으로 요약된 내용은 다음과 같다.

일급 컬렉션(First-Class Collection) 은 하나의 컬렉션을 감싸는 클래스를 만들고, 해당 클래스에서 컬렉션과 관련된 비지니스 로직을 관리하는 패턴을 의미한다.

간단하게 설명한다면, 아래 코드를

Map<String, String> map = new HashMap<>(); 
map.put("1", "A");
map.put("2", "B");
map.put("3", "C");

아래와 같이 Wrapping 하는 것을 이야기한다.

public class GameRanking {
    
    private Map<String, String> ranks;
    
    public GameRanking(Map<String, String> ranks) {
        this.ranks = ranks;
    }
}

컬렉션을 래핑하면서, 그 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라고 한다.

래핑 함으로써 다음과 같은 이점을 가지게 된다.

  1. 비지니스에 종속적인 자료구조

  2. 컬렉션의 불변성을 보장

  3. 상태와 행위를 한 곳에서 분리

  4. 이름이 있는 컬렉션

하나씩 이야기 해보자.

1. 비지니스에 종속적인 자료구조

예를 들어, 다음과 같은 로또 복권 게임을 만든다고 가정하자.

로또 복권은 다음의 조건이 있다.

  • 6개의 번호가 존재

  • 6개의 번호는 서로 중복되지 않아야 한다.

일반적으로 이런 일은 서비스 메서드에서 진행한다.

그래서 구현을 해보면 아래처럼 된다.

위와 같이 서비스 메서드에서 비지니스 로직을 처리할 경우 큰 문제가 있다.

로또 번호가 필요한 모든 장소에선 검증로직이 들어가야 한다.

  • List<Long> 으로 된 데이터는 모두 검증 로직이 필요할까?

  • 신규 입사자들이 어떻게 이 검증로직이 필요한지 알 수 있을까?

등등 모든 코드와 도메인을 알고 있지 않다면 언제든 문제가 발생할 여지가 있다.

그렇다면 어떻게 이 문제를 깔끔하게 해결할 수 있을까?

  • 6개의 숫자로만 이루어져야 하고,

  • 6개의 숫자는 수로 중복되지 않아야 한다.

이런 자료구조가 없을까? 우리가 만들면 된다.

아래와 같이 해당 조건으로만 생성할 수 있는 자료구조를 만들면 위에서 언급한 문제들이 모두 해결된다.

그리고 이런 클래스를 일급 컬렉션이라 부른다.

그리고 로또 번호가 필요한 서비스 로직에서는 이 일급 컬렉션을 사용하면 된다.

비지니스에 종속적인 자료구조를 만들어내어, 이후 발생할 문제를 최소화 할 수 있다.

2. 불변

일급 컬렉션은 컬렉션의 불변을 보장한다.

여기서 final 을 사용하면 안되는가? 라고 생각할 수 있지만, Java 의 final 은 정확히는 불변이 아닌, 재할당만 금지한다.

때문에, 아래 테스트 코드에서 collection 변수의 값이 계속 할당되는 것을 확인할 수 있다.

요즘 같이 소프트웨어 규모가 커지고 있는 상황에서 불변 객체는 매우 중요하다. 각각의 객체들이 절대 값이 바뀔일이 없다는게 보장되면 그만큼 코드를 이해하고 수정하는데 드는 비용이 최소화되기 때문이다.

Java 에서는 final 로 그 문제를 해결할 수 없기 때문에, 일급 컬렉션과 래퍼 클래스 등의 방법으로 해결해야만 한다.

그래서 아래와 같이 컬렉션의 값을 변경할 수 있는 메서드가 없는 컬렉션을 만들면 불변 컬렉션이 된다.

이 클래스는 생성자와 getAmountSum() 외에 다른 메서드는 없다.

즉, 이 클래스의 사용법은 새로 만들거나 값을 가져오는 것 뿐이다. List 라는 컬렉션에 접근할 수 있는 방법이 없기 때문에, 값을 변경/추가가 안된다.

이렇게 일급 컬렉션을 만들면, 불변 컬렉션을 만들 수 있다.

3. 상태와 행위를 한 곳에서 관리

일급 컬렉션의 세번째 장점은 값과 로직이 함께 존재한다는 것이다.

예를 들어, 여러 Pay 들이 모여있고, 이 중 NaverPay 금액의 합이 필요하다고 가정해보자.

일반적으로는 아래와 같이 작성한다.

  • List 에 데이터를 담고,

  • Service 혹은 Util 클래스에서 필요한 로직 수행

이 상황에서 문제가 발생한다. 결국 pays 라는 컬렉션과 계산 로직은 서로 관계가 있는데, 이를 코드로 표현이 안된다.

Pay 타입의 상태에 따라 지정된 메서드에서만 계산되길 원하는데, 현재 상태로는 강제할 수 있는 수단이 없다.

지금의 Pay 타입의 List 라면 사용될 수 있기 때문에, 히스토리를 모르는 사람이라면 실수할 여지가 많다.

  • 똑같은 기능을 하는 메서드를 중복 생성할 수 있다.

    • 히스토리가 관리 안된 상태에서 신규화면이 추가되어야 할 경우 계산 메서드가 있다는 것을 몰라 다시 만드는 경우가 빈번하다.

    • 만약 기존 화면의 계산 로직이 변경 될 경우, 신규 인ㄱ력은 2개의 메서드의 로직을 다 변경해야 하는지, 해당 화면만 변경하면 되는 것인지 알 수 없다.

    • 관리 포인트가 증가할 확률이 매우 높다 .

  • 계산 메서드를 누락할 수 있다.

    • 리턴 받고자 하는 것이 Long 타입이기 때문에, 꼭 이 계산식을 사용하해야 한다고 강제할 수 없다 .

결국에 네이버페이 총 금액을 뽑으로면 이렇게 해야한다는 계산식을 컬렉션과 함께 두어야 한다. 만약 네이버페이 외에 카카오페이의 총금액도 필요하다면 더더욱 코드가 흩어질 확률이 높다.

그래서 이제 문제 역시 일급 컬렉션으로 해결한다.

만약 다른 결제 수단들의 합이 필요하다면 아래와 같이 람다식으로 리팩토링 가능하다.

이렇게 PayGroups 이라는 일급 컬렉션이 생김으로 상태와 로직이 한곳에서 관리 된다.

4. 이름이 있는 컬렉션

마지막 장점은 컬렉션에 이름을 붙일 수 있다는 것이다.

같은 Pay 들의 모임이지만, 네이버페이의 List 와 카카오페이의 List 는 다르다.

이 둘을 구분하기 위해서 가장 쉽게 사용하는 것이 바로 변수명을 다르게 하는 것이다.

위 코드의 단점은 다음과 같다.

  • 검색이 어렵다.

    • 네이버페이 그룹이 어떻게 사용되는지 검색 시 변수명으로만 검색할 수 있다.

    • 이 상황에서 검색은 거의 불가능하다.

    • 네이버페이의 그룹이라는 뜻의 변수명을 개발자마다 다르게 지을 수 있기 때문이다.

    • 네이버페이 그룹은 어떤 검색어로 검색이 가능할까?

  • 명확한 표현이 불가능

    • 변수명에 불과하기 때문에 의미를 부여하기가 어렵다.

    • 이는 개발팀/운영팀간에 의사소통시 보편적인 언어로 사용하기가 어려움을 이야기한다.

    • 중요한 값임에도 이를 표현할 명확한 단어가 없다는것이다.

위 문제 역시 일급 컬렉션으로 쉽게 해결이 가능하다.

네이버페이 그룹과 카카오페이 그웁 각각의 일급 컬렉션을 만들면 이 컬렉션 기반으로 용어사용과 검색을 하면 된다.

개발팀/운영팀 내에서 사용될 표현은 이제 이 컬렉션에 맞추면 된다.

검색 역시 이 컬렉션 클래스를 검색하면 모든 사용 코드를 찾아낼 수 있다.

Last updated