Retrofit의 Query, Path 등에 Enum클래스 사용하기

Ted Park
박상권의 삽질블로그
6 min readOct 22, 2019

Retrofit에서 API콜을 할때 파라미터로 Enum을 넘겨 사용하는 방법에 대해 공유합니다.
Retrofit의 Converter를 활용해서 Enum을 String으로 쉽게 변환합니다.

Retrofit을 사용하면서 API를 호출할때 Enum을 그대로 넣고 싶은 경우가 있습니다.

하지만 Retrofit에서는 파라미터로 String을 넣어주어야 하기 때문에
Enum에 항상 value같은 개념의 필드를 만들어서 이를 넘겨주도록 해야 합니다.😥

enum class User(val value: String) {
TED("ParkSangGwon"),
DINO("sjjeong")
}
interface ApiService {
@GET("users")
fun getUser(@Query("name") userName: String): Single<MyUser>
}
apiService.getUser(User.TED.value)

value를 만들고 이를 API에 사용할때마다 넘겨주는건 너무 귀찮습니다.
(저는 귀찮은걸 정말 싫어합니다😡)

이때 Retrofit의 Converter를 활용해서 Enum을 String으로 쉽게 변환하도록 할 수 있습니다.
(Retrofit의 Converter에 대해서 잘 모르신다면 공식홈페이지의 내용을 참고해주세요)

Retrofit을 사용한다면 retrofit뿐만 아니라 아래와 같은 라이브러리들도 추가하셨을겁니다.

implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
or
implementation 'com.squareup.moshi:moshi:1.8.0'

이런 라이브러리들도 모두 이 Convert를 활용해서 만들어져 있습니다.

🔑 알고리즘

Converter를 사용해서 Enum으로 넘어온 데이터를 중간에 캐치해서 String형태로 바꿔주는 작업을 하도록 넣어둘 것입니다.

  1. 해당 type이 Class인지 확인
  2. 해당 Class가 enum인지 확인
  3. enum으로 부터 SerializeName 으로 정의되어 있는 값을 가져와서 이를 리턴

🏭 EnumConverterFactory

먼저 위의 알고리즘으로 만들어진 EnumConverterFactory 입니다.

class EnumConverterFactory : Converter.Factory() {

override fun stringConverter(
type: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<Enum<*>, String>? =
if (type is Class<*> && type.isEnum) {
Converter { enum ->
try {
enum.javaClass.getField(enum.name)
.getAnnotation(SerializedName::class.java)?.value
} catch (exception: Exception) {
null
} ?: enum.toString()
}
} else {
null
}
}

이를 Retrofit을 build할때 함께 넣어주면 됩니다.

Retrofit.Builder()
.baseUrl(...)
.addConverterFactory(...)
.addConverterFactory(EnumConverterFactory())
.build()
.create(ApiService::class.java)

🔍 비교

이렇게 코드를 변경했을때 어떻게 사용하게 되는지 비교해보면 아래와 같습니다.

🚫 변경전

enum class User(val value: String) {
TED("ParkSangGwon"),
DINO("sjjeong")
}
interface ApiService {
@GET("users")
fun getUser(@Query("name") userName: String): Single<MyUser>
}
apiService.getUser(User.TED.value)

변경후

enum class User {
@SerializedName("ParkSangGwon")
TED,
@SerializedName("sjjeong")
DINO
}
interface ApiService {
@GET("users")
fun getUser(@Query("name") user: User): Single<MyUser>
}
apiService.getUser(User.TED)

아래의 방법이 더 직관적이고 사용하기 편합니다.🎉🎉

서버에서 내려오는 데이터를 Enum으로 매핑하기 위해서 SerializedName를 사용하는경우가 많은데
이 개념까지 더해지면서 편하고 효율적인 enum관리를 해줄 수 있습니다.

💻 데모

실제 구현되어 있는 코드를 확인하고 싶으신 분들은 아래 GitHub Repository에서 확인해보시면 됩니다.

단, 위의 코드는 GitHub API를 활용하고 있기 때문에 실제 앱을 실행하시려면 GitHub에서 Token을 발급받으셔서 ApiService.TOKEN에 넣어주셔야 합니다.

저는 반복되는 작업이 있을때 귀찮은걸 너무 싫어해서 항상 어떻게 하면 개선할 수 있을지를 고민합니다.

여러분도 Retrofit을 사용하면서 유용했던 본인만의 꿀팁이 있다면 댓글로 공유해주세요.

감사합니다.

안드로이드 개발자들이 모여있는 오픈채팅방에 참여해보세요.
Q&A 및 팁을 공유하는 방입니다.

--

--