Java 프로젝트를 수행하며 항상 고민하는 라이브러리로 Guava와 Apache Commons가 있습니다. 세분화 된 라이브러리와 다양한 기능은 Apache Commons이 좋지만 결국 한 개, 한 개 추가하다보면 apache-xxx 라이브러리에 대한 많은 의존성이 생깁니다. 굳이 필요없는 기능까지 많이 의존하게 되어 나의 경우는 대부분 Guava를 기본으로 시작합니다. 그리고 Java 프로젝트를 하며 항상 사용하는 라이브러리를 정리하려고 기록합니다.

Guava

Guava는 Google의 코어 라이브러리입니다. Collection(JDK 버전이 올라가며 많이는 필요 없는), caching, string processing 등 프로젝트 구현 시 공통적으로 필요한 요소들이 구현되어 있습니다. 반복적으로 사용되는 코드가 줄어들고 간결하게 구현이 가능해져서 가독성이 좋아집니다. 그 중에서도 주로 사용하는 기능들을 [참고1 Guava Wiki의 예제]를 기반으로 정리하였습니다. 자세한 내용은 [참고2 소스코드]를 참고하시면 좋습니다.

Preconditions

전달된 Aurgments에 대한 검증을 수행하는데 매번 if-else를 사용하기에는 코드가 너무 길어집니다. 이 때 Preconditions를 사용하면 다음과 같이 한 줄로 구현 가능합니다.

checkArgument(i >= 0, "Argument was %s but expected nonnegative", i);
checkArgument(i < j, "Expected i < j, but %s > %s", i, j);

특히 checkNotNull, checkState 등 자주 사용하는 패턴들이 정의되어 있어 더 짧고 명시적으로 표현 가능합니다.

Common object methods

equals, hashCode, toString은 자바에서 클래스 구현 시 매번 구현하는 반복 작업입니다. 요즘에는 IntelliJ IDEA를 이용하여 샤~샥 한 방에 만들어내는데 이 때 Guava의 MoreObjects와 Objects를 이용하여 생성 가능합니다.

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;

public class Person {
    private final String id;
    private final int age;

    public Person(String id, int age) {
        this.id = id;
        this.age = age;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("id", id)
                .add("age", age)
                .toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equal(id, person.id);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(id, age);
    }
}

위 코드의 toString() 결과는 Person{id=dgb, age=84}입니다.

Caches

Cache 기능은 항상 최적화를 위해서 구현합니다. Guava를 이용하면 Cache 크기, expire 시간을 정의하여 쉽게 Cache를 만듭니다.

(특히 이 기능을 매우 좋아합니다)

LoadingCache<String, Person> peopleCache = CacheBuilder.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(
            new CacheLoader<String, Person>() {
                @Override
                public Person load(String id) throws Exception {
                    return personRepository.findById(id);
                }
            });
            
final Person person = peopleCache.get("P10000");

Person 객체에 대한 Cache를 만든 예제입니다. ID가 P10000인 Person 객체를 가져오는데 Cache에 존재하면 해당 인스턴스를 사용하고 없는 경우 personRepository.findById를 이용하여 가져옵니다. CacheLoader 구현체 정의 시에 load 함수 외에 loadAll 함수를 이용해서 모든 목록을 가져오게 적용 가능합니다.

Ranges

Range도 숫자 값에 대한 검증, 혹은 조건에 따른 처리 시에 많이 활용하는 기능입니다. 사실 더 많은 기능이 있지만 A < x < B 와 같은 조건 처리 용도로 많이 사용합니다.

Range.closed(1, 3).contains(2); // true
Range.closed(1, 3).contains(4); // false
Range.lessThan(5).contains(5); // returns false
Range.closed(1, 4).containsAll(Ints.asList(1, 2, 3)); // returns true

Guava Guide Wiki에 있는 예제(참고1)으로 Range를 이해하기에 가장 좋은 예제입니다. 이 외에도 greaterThan, atLeast, intersection 등이 유용합니다.

Guava 좋아요!

Guava는 참 좋습니다. 코드도 내가 선호하는 스타일입니다. 사실 위에서 언급한 내용보다 Guava는 훨씬 많은 기능을 가지고 있습니다.

그 외에도 자주 사용하는 기능

하지만 Guava도 JDK7, 8, 이제 9로 버전이 올라가면서 stream, collections 등 좋은 기능들이 많이 생겨서 Guava에 있는 일부 기능들은 Deprecated 되었습니다. 그리고 Apache commons에는 모듈화되어 훨씬 많은 기능(모듈도 더 크지만)을 제공하고 있습니다.

그래도 아직까지 Guava에서도 필요한 많은 기능들과 간결한 코드를 제공합니다.

(코멘트) 사실 Guava와 Apache commons는 그때 그때 기분에 따라 사용합니다. 둘 다 워낙 좋아서 해당 프로젝트에서 정의하는데로 사용하면 될 것 같습니다. 하지만 개인적으로는 GUAVA!