일반적으로 앱 개발시 서버와의 데이터 연동에서 Json 포맷을 이용하기 때문에 Gson을 이용한 AutoValue 작성법을 알아보자.
우선 gradle 세팅부터 해야겠지?
AutoValue를 사용하기 위한 세팅은 끝났다. AutoValue를 이용한 코드를 보면 약간 생소할 수가 있다. 아래는 AutoValue 어노테이션을 이용한 기본적인 방법이다.
@AutoValue
public abstract class Product {
@SerializedName("id")
public abstract long getId();
@SerializedName("name")
public abstract String getName();
@SerializedName("price")
public abstract float getPrice();
public static TypeAdapter<Product> typeAdapter(Gson gson) {
return new AutoValue_Product.GsonTypeAdapter(gson);
}
}
코드를 보면 알겠지만 변수가 없다. id, name, price의 변수가 있어야 할 것 같은데 해당 변수는 자동 생성 되는 AutoValue_Product 클래스에 생성된다. 마찬가지로 abstract 키워드가 의문일 텐데 자동 생성되는 클래스는 위의 Product 클래스를 상속받고 abstract 메서드를 구현하게 된다.
예제 코드에서는 @SerializedName 어노테이션을 사용하였고 get을 prefix로 메서드를 정의하였지만 어노테이션을 제거하고 id(), name(), price() 이런 식으로 메서드 이름을 데이터 이름과 맞춰주면 @SerializedName 어노테이션이 필요 없지만 간혹 서버에서 내려주는 이름이 카멜 표기법을 준수하지 않는 경우도 있고 네이밍을 변경 시 크게 의미가 바뀌지 않는 이상 @SerializedName 어노테이션만 변경하면 되기 때문에 필자는 위와 같이 주로 쓰는 편이다.
이제 typeAdapter라는 static 메서드의 용도가 무엇일지 궁금할 것이다. @GsonTypeAdapterFactory와 관련이 있는데 위에 작성된 메서드 시그너처를 반드시 지켜서 선언해야 한다.
Class 타입에 따라 Gson 매핑을 하는 AutoValueGson_AutoValueGsonFactory라는 클래스가 자동 생성되는데 Class 타입에 따라 분기되고 해당 클래스의 typeAdapter 메서드를 호출하게 된다.
@GsonTypeAdapterFactory
public abstract class AutoValueGsonFactory implements TypeAdapterFactory {
@Override
public static TypeAdapterFactory create() {
return new AutoValueGson_AutoValueGsonFactory();
}
}
@GsonTypeAdapterFactory 어노테이션을 이용하여 TypeAdapterFactory 인터페이스를 구현한다. auto-value-gson 0.3.0 버전까지는 저렇게 AutoValueGson_AutoValueGsonFactory 클래스가 자동 생성되지 않고 개발자가 직접 해당 로직을 구현해야 한다. 0.3.0 이상 버전을 사용하면 위와 같이 작성하기만 해도 된다.
지금까지 AutoValue에 대해서 간단히 살펴보았다. 그런데 의문이 생길 것이다. get메서드는 제공하는데 set은 없네?
그렇다. AutoValue는 기본적으로 Immutable을 기본 전제로 한다. 그렇다면 해당 클래스의 속성 등을 변경하고자 한다면 어떻게 해야 할까? Immutable을 기본 전제로 하기 때문에 새로운 객체로 생성해야 된다. 아래처럼 static factory 메서드를 제공할 수도 있다.
@AutoValue
public abstract class Product {
@SerializedName("id")
public abstract long getId();
@SerializedName("name")
public abstract String getName();
@SerializedName("price")
public abstract float getPrice();
public static TypeAdapter<Product> typeAdapter(Gson gson) {
return new AutoValue_Product.GsonTypeAdapter(gson);
}
public static Product create(long id, String name, float price) {
return new AutoValue_Product(id, name, price);
}
}
하지만 Effective Java에서도 설명하고 있지만 위와 같은 create메서드는 텔레스코핑패턴이라는 안티 패턴이다. 위의 Product 클래스는 3개의 속성뿐이 없지만 만약 속성이 많은 클래스거나 속성이 추가된다면 메서드 호출자는 파라미터의 순서 및 데이터 타입에 혼돈이 생길 것이다.
텔레스코핑 패턴을 보완하기 위해 빌더 패턴이 존재한다. 아마 AutoValue Gson 개발자도 그러한 요구 사항을 받았을 것이고 Builder 패턴을 적용하기 위한 어노테이션을 제공한다. @AutoValue.Builder 어노테이션이다.
@AutoValue
abstract class ProductBuilder {
@SerializedName("id")
abstract long getId();
return new AutoValue_ProductBuilder.GsonTypeAdapter(gson);
}
static Builder builder() {
return new AutoValue_ProductBuilder.Builder();
}
@AutoValue.Builder
static abstract class Builder {
abstract Builder setId(long id);
abstract Builder setName(String name);
abstract Builder setPrice(float price);
abstract ProductBuilder build();
}
}
주의해야 될 점이 있는데 Builder 클래스에 모든 속성에 대한 메서드가 정의되지 않으면 AutoValue_ProductBuilder.Builder 클래스가 생성되지 않는다.
지금까지 AutoValue와 Gson을 이용한 개발 방법을 소개했다. 가장 중요한 게 남아 있는데 간단하고 가장 중요한 사실이다.
AutoValue_ 로 시작하는 자동 생성 클래스를 생성하기 위해선 코드를 다시 컴파일 해야 한다. Android Studio의 rebuild project를 통해 AutoValue 코드가 생성되도록 하자. 그렇지 않으면 AutoValue_ 로 시작하는 클래스는 생성되지 않을 것이다.