-
Notifications
You must be signed in to change notification settings - Fork 0
3week_AOS_tech_post_4
์ด๋ป๊ฒ ๋์ํ๋ ๊ฑธ๊น? interface ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๊ฑฐ ๊ฐ์๋ฐ ์ด๊ฒ ์๋ฐ์์ ๊ฐ๋ฅํ๊ฐ?
- REST API ํต์ ์ ์ํด ๊ตฌํ๋ ํต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
- AsyncTask ์์ด Background Thread์์ ์คํ๋๋ฉด callback์ ํตํด Main Thread์์ UI ์ ๋ฐ์ดํธ๋ฅผ ๊ฐ๋จํ๊ฒ ํ ์ ์๋๋ก ์ ๊ณต.
- ๋ค๋ฅธ ํต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์กด์ฌํ์ง๋ง Retrofit์ ์ฑ๋ฅ๊ณผ ๊ตฌํ ๋ฐฉ๋ฒ์ด ์ฝ๋ค.
AsyncTask๋?
AsyncTask
๋ Android์์ ๋น๋๊ธฐ ์์
์ ์ํํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ์ธ ์ค๋ ๋์ ๊ฒ์ํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ์ ํธ๋ฆฌํฐ ํด๋์ค์
๋๋ค.
๊ณผ๊ฑฐ์๋ Android ๊ฐ๋ฐ์์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์
์ ์ฝ๊ฒ ์ํํ ์ ์๋๋ก ์ค๊ณ๋ ๊ฒ์ด์ง๋ง,
์ค๋๋ ์๋ Android์ WorkManager
, Coroutines
, RxJava
์ ๊ฐ์ ๋ค๋ฅธ ๋ฐฉ๋ฒ๋ค์ด ๋ ๋ง์ด ์ฌ์ฉ๋๋ฉฐ, AsyncTask
์์ฒด๋ deprecated(์ฌ์ฉ์ด ๊ถ์ฅ๋์ง ์๋) ์ํ์
๋๋ค.
โข @GET, @POST, @DELETE, @UPDATE ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์ธํฐํ์ด์ค๋ฅผ ์์ฑ
interface TrimApiService {
@GET("/trims")
suspend fun getTrimList(): CaArtResponse<List<Trim>>
}
Retrofit.Builder ๋ง๋ค๊ธฐ
fun getRetrofit(): Retrofit = retrofit ?: Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
DTO ํด๋์ค ๋ง๋ค๊ธฐ
data class Trim(
val description: String,
val exteriorColors: List<TrimItemColor>,
val interiorColors: List<TrimItemColor>,
val mainOptions: List<TrimMainItem>,
val trimImage: String,
val trimName: String,
val trimPrice: Long,
var isChecked: Boolean = false
)
์์ ๊ณผ์ ์ ํตํด API Interface์ ๋ฉ์๋ ๊ฐ์ฒด๋ฅผ ์ ์ธํ๊ณ ๋๊ธฐ/๋น๋๊ธฐ๋ก ์คํํ์ฌ ์๋ฒ์์ Response๋ฅผ ๋ฐ์์จ ๋ค ์์ ์ ์ํ
var service: TrimApiService = retrofit.create(TrimApiService::class.java)
service.getTrimList()
// ์๋ฐ๋ ์ธํฐํ์ด์ด์ค๋ฅผ ๊ฐ์ฒดํํ๋ ๊ฒ์ ํ์ฉํ์ง ์๋๋ฐ ์ฌ๊ธฐ์๋ ์ด๋ป๊ฒ ๋ง๋๋๊ฑธ๊น?
๊ทธ๋ ๋ค๋ฉด ์ด๋ป๊ฒ API Interface๋ง ๊ตฌํํด์ request๋ฅผ ์ํํด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ ๊ฑธ๊น ?
์ฐ์ retrofit.create()
๋ด๋ถ ์ฝ๋ ๊ตฌํ๋ถ๋ฅผ ํ์ธํด๋ณด์
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public <T> T create(final Class<T> service) {
// ์ฌ๋ฐ๋ฅธ ์ธํฐํ์ด์ค ํ์
์ธ์ง ๊ฒ์ฌ
// Class ํด๋์ค์ ๋ด๋ถ ํจ์์ isInterface()๋ฅผ ํ์ฉํ์ฌ ๊ฒ์ฌ
// Retrofit์ ๋งค๊ฐ๋ณ์๋ฅผ ๊ฐ์ง ์ธํฐํ์ด์ค๋ฅผ ์ง์ํ์ง ์์ (๋ค์ ๊ธ ์ฐธ๊ณ )
validateServiceInterface(service);
return (T)
//Proxy ํด๋์ค์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์๋ก์ด ํ๋ก์ ์ธ์คํด์ค๋ฅผ ์์ฑ
Proxy.newProxyInstance(
service.getClassLoader(),
// ํ๋ก์๊ฐ ๊ตฌํํด์ผ ํ ์ธํฐํ์ด์ค ๋ชฉ๋ก์ ๋ฐฐ์ด๋ก ์ ๊ณต, ์ฌ๊ธฐ์๋ service๋ง์ ๊ตฌํํ๋๋ก ์ค์ ๋์ด ์์
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
์ฐ์ ์ฝ๋ ๋ถ๋ฅผ ์ดํดํ๊ธฐ ์ํด์๋ ํ๋ก์ ํจํด, ๋ค์ด๋๋ฏน ํ๋ก์์ ๋ํ ์ดํด๊ฐ ํ์ํ๋ค.
์ฝ๋ ๊ตฌํ๋ถ๋ฅผ ๋ณด๋ฉด ๋ค์ด๋๋ฏน ํ๋ก์์ฌ์ฉํ๊ณ ์๋ค.
ํ๋ก์ ํจํด์ ๋ํด ๊ฐ๋จํ ์ค๋ช ํ์๋ฉด
- ํ๋ก์๋ "๋๋ฆฌ์ธ"์ ์๋ฏธํฉ๋๋ค.
- ์๋ฐ์์ ํ๋ก์๋ ํ๊ฒ์ ๊ธฐ๋ฅ์ ํ์ฅํ๊ฑฐ๋ ํ๊น์ ๋ํ ์ ๊ทผ์ ์ ์ดํ๊ธฐ ์ํ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉํ๋ ํด๋์ค
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉ๋ ์ธํฐํ์ด์ค์ ๊ทธ ๊ตฌํ์ฒด์ ๋๋ค.
interface OnlineStore {
fun purchaseProduct(productName: String): String
fun refundProduct(productName: String): String
fun checkInventory(productName: String): Int
}
class ActualStore : OnlineStore {
override fun purchaseProduct(productName: String) = "$productName purchased!"
override fun refundProduct(productName: String) = "$productName refunded!"
override fun checkInventory(productName: String) = 100 // ์ฌ๊ณ ๋ 100๊ฐ๋ก ๊ฐ์
}
- ํ๋ก์ ํด๋์ค์ ๋๋ค. ๊ธฐ๋ณธ ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํ๋, purchaseProduct() ํจ์์ ๋ก๊ทธ ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ด ์๋ค
class LoggedStore(private val store: OnlineStore) : OnlineStore {
override fun purchaseProduct(productName: String): String {
println("Attempting to purchase $productName")
val result = store.purchaseProduct(productName)
println("$productName purchase complete.")
return result
}
override fun refundProduct(productName: String)
= store.refundProduct(productName)
override fun checkInventory(productName: String)
= store.checkInventory(productName)
}
- ์ค์ ๊ฐ์ฒด์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ , ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ฑฐ๋ ์์ ํ ์ ์์ต๋๋ค.
ํ๋ก์ ํจํด์ ํ๊ฒ์ ๊ธฐ๋ฅ์ ํ์ฅํ๊ฑฐ๋ ํ๊ฒ์ ๋ํ ์ ๊ทผ์ ์ ์ดํ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
- ์ธํฐํ์ด์ค๋ฅผ ์ง์ ๊ตฌํํด์ผํ๋ค.
interface OnlineStore {
fun purchaseProduct(productName: String): String
fun refundProduct(productName: String): String
fun checkInventory(productName: String): Int
}
OnlineStore์ ๊ตฌํ์ฒด์ ๋ํ ํ๋ก์๋ฅผ ๋ง๋ค๋ ค๋ฉด OnlineStore๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
์ฆ, OnlineStore์ ๋ชจ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
๋ง์ฝ purchaseProduct() ๋ฉ์๋๋ง ๊ธฐ๋ฅ์ ํ์ฅํ๊ณ ์ถ๋ค๊ณ ํ๋๋ผ๋ refundProduct()์ checkInventory()๋ฅผ ๋ชจ๋ overrideํด์ฃผ์ด์ผ ํ๋ค
override ํจ์๊ฐ ํ๋ ์ผ์ด๋ผ๊ณค ํ๊ฒ์ ๋ฉ์๋์ ์์ํ๋ ๊ฒ ๋ฟ์ธ๋ฐ๋ ๋ง์ด๋ค.
- ํด๋์ค์ ์ค๋ณต์ด ๋ฐ์ํ๋ค.
Java์ Reflection API๋ฅผ ํ์ฉํ์ฌ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋์ ์ผ๋ก ์์ฑํ ์ ์์ต๋๋ค.
์ด๋ฅผ ํตํด ์์ ๋ฌธ์ ์ ๋ค์ ํด๊ฒฐํ ์ ์์ต๋๋ค
์๋ฐ ๊ณต์ ๋ฌธ์๋ฅผ ๋ณด๋ฉด ์๋์ ๊ฐ๋ค
์ฝํ๋ฆฐ ํ์ฅ ํจ์ ๊ธฐ๋ฅ์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋ฉด
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
interface OnlineStore {
fun purchaseProduct(productName: String): String
fun refundProduct(productName: String): String
fun checkInventory(productName: String): Int
}
class ActualStore : OnlineStore {
override fun purchaseProduct(productName: String) = "$productName purchased!"
override fun refundProduct(productName: String) = "$productName refunded!"
override fun checkInventory(productName: String) = 100
}
class StoreInvocationHandler(private val store: OnlineStore) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array<Any?>): Any? {
println("Method ${method.name} is being invoked with arguments: ${args.joinToString()}")
val result = method.invoke(store, *args)
println("Method ${method.name} invocation completed. Result: $result")
return result
}
}
inline fun <reified T> createProxy(store: OnlineStore): T {
return Proxy.newProxyInstance(
T::class.java.classLoader,
arrayOf(T::class.java),
StoreInvocationHandler(store)
) as T
}
fun main() {
val storeProxy: OnlineStore = createProxy(ActualStore())
println(storeProxy.purchaseProduct("Shoes"))
println(storeProxy.checkInventory("Shoes"))
}
- ํน์ ์ธํฐํ์ด์ค์ ๋ฉ์๋๋ฅผ ๋ชจ๋ ๊ตฌํํ ํ์๊ฐ ์์ต๋๋ค.
- ์ค๋ณต ์ฝ๋๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
- ํน์ ๊ธฐ๋ฅ์ ํ์ฅํ๊ณ ์ถ์ ๊ฒฝ์ฐ์๋ง ๊ทธ ๊ธฐ๋ฅ์ ๋ํ ๋ก์ง์
InvocationHandler
๋ด๋ถ์ ๊ตฌํํ๋ฉด ๋ฉ๋๋ค.
โ Retrofit์ Java์ Proxy
ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ธํฐํ์ด์ค์ ๋ค์ด๋๋ฏน ํ๋ก์๋ฅผ ์์ฑํฉ๋๋ค.
โ ํ๋ก์๋ ๊ฐ๋ฐ์๊ฐ ํธ์ถํ๋ ๋ฉ์๋์ ๋ํ ์ ๋ณด (๋ฉ์๋ ์ด๋ฆ, ๋งค๊ฐ๋ณ์, ์ด๋
ธํ
์ด์
๋ฑ)๋ฅผ ์ ์ ์์ต๋๋ค.
โ ์ด๋ฅผ ํตํด Retrofit์ ์ด๋ค HTTP ์์ฒญ์ ์์ฑํด์ผ ํ๋์ง ์์๋
๋๋ค.
์ฝ๊ฒ ๋น์ ๋ฅผ ํ์๋ฉด ๋ ์คํ ๋์์ ๋ฉ๋ด๋ฅผ ์ฃผ๋ฌธํ๋ ค๊ณ ํฉ๋๋ค.
์ฌ๊ธฐ์ ๋ ์คํ ๋์ Retrofit, ๋์์ ๊ฐ๋ฐ์, ๋ฉ๋ด๋ ์ธํฐํ์ด์ค(API), ๊ฐ ๋ฉ๋ด ํญ๋ชฉ์ API์ ๋ฉ์๋์ ๋๋ค.
- ๋ฉ๋ด ์ ํ (API ์ ์): ๋น์ ์ ์์์ ์ฃผ๋ฌธํ๊ธฐ ์ ์ ๋ฉ๋ด๋ฅผ ํ์ธํฉ๋๋ค. ๊ฐ ๋ฉ๋ด ํญ๋ชฉ์ ํน์ ์์์ ์๋ฏธํ๋ฉฐ, ์ด๋ป๊ฒ ๋ง๋ค์ด์ผ ํ๋์ง์ ์ง์์ฌํญ(์ด๋ ธํ ์ด์ )์ด ํฌํจ๋์ด ์์ต๋๋ค.
- ์ฃผ๋ฌธ ์ ๋ฌ (๋ฉ์๋ ํธ์ถ): ๋น์ ์ ์จ์ดํฐ์๊ฒ ๋ฉ๋ด ํญ๋ชฉ์ ์ง๋ชฉํ๋ฉฐ ์ฃผ๋ฌธํฉ๋๋ค. ์ฌ๊ธฐ์ ์จ์ดํฐ๋ ๋ค์ด๋๋ฏน ํ๋ก์์ ์ญํ ์ ํฉ๋๋ค.
- ์จ์ดํฐ์ ์ญํ (๋ค์ด๋๋ฏน ํ๋ก์): ์จ์ดํฐ๋ ๋น์ ์ด ์ง์ ํ ๋ฉ๋ด ํญ๋ชฉ(๋ฉ์๋)๊ณผ ๊ทธ์ ๋ํ ์ง์์ฌํญ(์ด๋ ธํ ์ด์ )์ ํ์ธํ๊ณ ์ด๋ฅผ ์ฃผ๋ฐฉ์ ์ ๋ฌํฉ๋๋ค. ์ฃผ๋ฐฉ์์๋ ์ด ์ง์์ฌํญ์ ๋ฐ๋ผ ์์์ ์ค๋นํ๊ฒ ๋ฉ๋๋ค.
์ด ๋น์ ์์ ์จ์ดํฐ์ ์ฃผ๋ฌธ ์ฒ๋ฆฌ ๋ฐฉ์์ ๋ฐํ์์ ์ด๋ฃจ์ด์ง๋๋ค. ์ฆ, ์จ์ดํฐ(๋ค์ด๋๋ฏน ํ๋ก์)๋ ์ฌ์ ์ ์ด๋ค ์ฃผ๋ฌธ์ด ์ฌ์ง ์ ์ ์์ผ๋ฉฐ, ์ค์ ์ฃผ๋ฌธ์ด ๋ค์ด์์ ๋ ํด๋น ์ฃผ๋ฌธ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋์ ์ผ๋ก ๊ฒฐ์ ํฉ๋๋ค.
๊ฐ๋ฐ์๊ฐ Retrofit ์ธํฐํ์ด์ค์์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด, ์ค์ ๋ก ํด๋น ๋ฉ์๋์ ๊ตฌํ์ฒด๋ ์์ต๋๋ค
๋์ , Java์ Proxy
ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐํ์์ ๋์ ์ผ๋ก ์์ฑ๋ ํ๋ก์๊ฐ ํธ์ถ๋ฉ๋๋ค.
์ด ํ๋ก์๋ ๋ฉ์๋์ ์ด๋ฆ, ๋งค๊ฐ๋ณ์, ๊ทธ๋ฆฌ๊ณ ์ด๋ ธํ ์ด์ ๋ฑ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํ์ธํ๊ณ ์ด๋ฅผ ๋ฐํ์ผ๋ก ์ค์ HTTP ์์ฒญ์ ๊ตฌ์ฑํฉ๋๋ค.
์๋ฅผ ๋ค์ด, @GET("users/{user}/repos")
์ด๋
ธํ
์ด์
์ด ์๋ ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด, ํ๋ก์๋ GET
์์ฒญ์ /users/{user}/repos
๊ฒฝ๋ก๋ก ์์ฑํ๊ฒ ๋ฉ๋๋ค.
์ด์ ๊ฐ์ ๋ฐฉ์์ผ๋ก, Retrofit์ ๊ฐ๋ฐ์๊ฐ ์ ๊ณตํ ์ธํฐํ์ด์ค์ ์ด๋ ธํ ์ด์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋คํธ์ํน ํ๊ธฐ ์ํ ํ๋ก๊ทธ๋จ ์ฝ๋๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด์ค๋๋ค.
ํ๋ก์ ๊ฐ์ฒด๊ฐ Service ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ฃผ๊ณ Service์ ํจ์๋ฅผ ํธ์ถํ์ฌ Call ๊ฐ์ฒด๋ฅผ ๋ฐ๊ณ Call ๊ฐ์ฒด์๊ฒ ์ผ์ ์ํค๋ฉด ๋๋ค.
validateServiceInterface
๋ฉ์๋์ ๊ตฌํ ๋ถ์ธ๋ฐ
์ ๊ณต๋ ์ธํฐํ์ด์ค์ ๊ทธ ์์ ์ธํฐํ์ด์ค๋ค์ด ํ์ ๋งค๊ฐ๋ณ์(์ฆ, ์ ๋ค๋ฆญ)๋ฅผ ๊ฐ์ง๊ณ ์์ง ์์์ง ํ์ธํฉ๋๋ค. ์ค์ ๋ก Retrofit์ ํ์ ๋งค๊ฐ๋ณ์๊ฐ ์๋ ์ธํฐํ์ด์ค๋ฅผ ์ง์ํ์ง ์์ต๋๋ค.
private void validateServiceInterface(Class<?> service) {
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
Deque<Class<?>> check = new ArrayDeque<>(1);
check.add(service);
while (!check.isEmpty()) {
Class<?> candidate = check.removeFirst();
if (candidate.getTypeParameters().length != 0) {
StringBuilder message =
new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
if (candidate != service) {
message.append(" which is an interface of ").append(service.getName());
}
throw new IllegalArgumentException(message.toString());
}
Collections.addAll(check, candidate.getInterfaces());
}
// ์กฐ๊ฑด๋ฌธ์ ์๋น์ค ์ธํฐํ์ด์ค์ ์ ์ธ๋ ๋ชจ๋ ๋ฉ์๋๋ฅผ ๊ฒ์ฌํ์ฌ ๊ทธ๊ฒ๋ค์ด ์ ํจํ์ง ๋ฏธ๋ฆฌ ํ์ธํ๋ ๋ก์ง
// ์ด๊ฒ์ ๊ฐ๋ฐ ์ค์ ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ ์ ์๊ฒ ๋์์ค
// ์ฌ๊ธฐ์๋ Java 8์ default ๋ฉ์๋์ ์ ์ ๋ฉ์๋๋ฅผ ์ ์ธํ ๋๋จธ์ง ๋ฉ์๋๋ค์ ๋ํ ์ ํจ์ฑ์ ๊ฒ์ฌํฉ๋๋ค.
if (validateEagerly) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
loadServiceMethod(method);
}
}
}
}
์ ์ฝ๋๋ฅผ ๋ณด๋ Deque๋ฅผ ์ฌ์ฉํ๋ค? ์ด ๋ค์ ๋ณด๋ ์ฝ๋ ๋ก์ง์ด BFS๋ค?? ์ ์ธํฐํ์ด์ค ํ๋๋ง ๋๊ฒจ์ฃผ๋๋ฐ Deque ์ฌ์ด์ฆ๋ฅผ ๊ตณ์ด 1๋ก ์ด๊ธฐํํด์ ์ธํฐํ์ด์ค๋ฅผ ๋ฃ์ด์ฃผ์ง?
๋ฌด์จ ์ด์ ์ผ๊น?
BFS (Breadth-First Search, ๋๋น ์ฐ์ ํ์) ์ฌ์ฉ ์ด์
-
์ธํฐํ์ด์ค๋ ์ฌ๋ฌ ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ ์ ์์
-
์ฃผ์ด์ง ์ธํฐํ์ด์ค์ ๊ทธ ์ธํฐํ์ด์ค๊ฐ ์์๋ฐ์ ๋ชจ๋ ์ธํฐํ์ด์ค๋ฅผ ์ฒดํฌํ๊ธฐ ์ํด BFS ๋ฐฉ์์ด ํจ์จ์
์์: ์ธํฐํ์ด์ค A๊ฐ B์ C๋ฅผ ์์๋ฐ๊ณ , B๊ฐ D์ E๋ฅผ ์์๋ฐ๋ ๊ฒฝ์ฐ, A -> B, C ๋ฐ B -> D, E ์์ผ๋ก ํ์
-
Deque
๋ BFS ๋ฐฉ์์ ํ์์ ํจ๊ณผ์ ์ผ๋ก ์ํํ๊ธฐ ์ํ ๋๊ตฌ๋ก ์ฌ์ฉ๋ฉ๋๋ค. -
์ฝ๋:
Deque<Class<?>> check = new ArrayDeque<>(1);
์ด ์ฝ๋์์ **
ArrayDeque
**์ ์ด๊ธฐ ํฌ๊ธฐ๋ฅผ 1๋ก ์ค์ ํ๋ ๊ฒ์ ์ต์ ํ์ ์ผ๋ถ์ผ ์ ์์ต๋๋ค. ์ฒ์์๋ ํ๋์ ์ธํฐํ์ด์ค๋ง ์์ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ํ์ ํฌ๊ธฐ๋ฅผ 1๋ก ์์ํ์ฌ ํ์์ ๋ฐ๋ผ ๋์ ์ผ๋ก ํ์ฅํฉ๋๋ค.
- ์ฃผ์ด์ง ์๋น์ค ์ธํฐํ์ด์ค๋ฅผ ํ์ ์ถ๊ฐ:
check.add(service);
- ํ์ ์์ดํ
์ด ์์ผ๋ฉด, ํด๋น ์ธํฐํ์ด์ค๋ฅผ ๊บผ๋ด์ด ๊ฒ์ฌํ๊ณ , ๊ทธ ์ธํฐํ์ด์ค๊ฐ ์์๋ฐ์ ๋ค๋ฅธ ์ธํฐํ์ด์ค๋ค์ ๋ค์ ํ์ ์ถ๊ฐ:
Collections.addAll(check, candidate.getInterfaces());
Retrofit์ API ์ ์ธ์ ๊ฐ๋จํ๊ณ ๋ช ํํ๊ฒ ์ ์งํ๋ ค๊ณ ํฉ๋๋ค.
์ ๋ค๋ฆญ์ ๊ฐ์ง ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํ๋ฉด ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ๋ฉฐ,
๋ด๋ถ์ ์ผ๋ก ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ์ฌ ๋ฉ์๋์ ์ด๋ ธํ ์ด์ ์ ๋ณด๋ฅผ ๋ถ์ํ๋ ๊ณผ์ ์์ ์์์น ๋ชปํ ๋ฌธ์ ๋ ๋ณต์ก์ฑ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
์ ๋ค๋ฆญ์ ์ปดํ์ผ ์์ ์ ํ์ ์ ์์ ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํ ๊ฒ์ด๋ฏ๋ก, ๋ฐํ์์๋ ์ ๋ค๋ฆญ ์ ๋ณด๋ ๋๋ถ๋ถ ์ง์์ง๋๋ค. ์ด๊ฒ์ ํ์ ์๊ฑฐ(Type Erasure)๋ผ๊ณ ํฉ๋๋ค.
๋ฐ๋ผ์ ๋ฐํ์์ ์ ๋ค๋ฆญ ์ ๋ณด๋ฅผ ์ ํํ๊ฒ ๋ถ์ํ๋ ๊ฒ์ ์ด๋ ค์ธ ์ ์์ต๋๋ค.
Retrofit์ ๋ฐํ์์ ์ธํฐํ์ด์ค์ ๊ทธ ๋ฉ์๋๋ค์ ์ ๋ณด๋ฅผ ๋ถ์ํ๋ฏ๋ก,
์ ๋ค๋ฆญ์ ๊ฐ์ง ์ธํฐํ์ด์ค๋ฅผ ์ง์ํ์ง ์๋ ์ด์ ๊ฐ ์ด๋ฌํ ๋ณต์ก์ฑ์ ํผํ๋ ํ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ์ถ์ธก ํด ๋ณผ ์ ์๋ค ๐ค
๋ฆฌํ๋ ์ ์ฌ์ฉํ๋ ์
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
method,
"Method return type must not include a type variable or wildcard: %s",
returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
getGenericReturnType์ ์ฌ์ฉํ์ฌ ๋ฉ์๋์ ๋ฐํ ํ์ ์ ๊ฐ์ ธ์ด
์ด๋ ธํ ์ด์ ์ ํ์ฑ ๊ตฌํ๋ถ
RequestFactory.java
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
RequestFactory ํด๋์ค ๋ด๋ถ๋ฅผ ๋ค์ด๊ฐ๋ฉด Path, Headers ๋ฑ ํ์ฑํ๋ ๊ตฌํ๋ถ๋ฅผ ํ์ธํ ์ ์๋ค.
์กฐ๊ธ ๋ ๊น๊ฒ ํ์ธํด๋ณด๊ณ ์ถ์ง๋ง ์์ง ํ ๊ฒ ๋๋ฌด๋๋ ๋ง๊ธฐ ๋๋ฌธ์.. ์ฌ๊ธฐ๊น์ง ์์๋ณด์
๐ป [H6-CaArt] โDone is better than perfectโ ๐ป
- [์ต๊ทํ] Sentry ๋์ ๊ธฐ (feat. ๋ก๊ทธ ๊ด๋ฆฌ)
- [์ต๊ทํ] GPT3.5 ๋ชจ๋ธ์ ํ์ฉํด์ ์ถ์ฒ ๋ฌธ๊ตฌ ์์ฝํ๊ธฐ
- [์ต๊ทํ] ์คํ๋ง์์ OpenAI API ์ฑ๋ฅ ๊ฐ์ ๊ธฐ
- [๊ถ๋ฏผ์] CaArt CI/CD ๊ตฌ์ถ ๊ณผ์
- [๊ถ๋ฏผ์] ์ฑํ๋ฅ ๊ณ์ฐ ๋ก์ง ๊ฐ์ - Index tuning๊ณผ Batch processing
- [์ด์นํ] useReducer, useContext๋ฅผ ์ฌ์ฉํ ์ ์ญ ์ํ ๊ด๋ฆฌ with React, TS
- [์ด์นํ] ํ๋กํ ํ์ ํจํด์ด๋?
- [์ด์นํ] UX ๊ฐ์ ์ ์ํ ์ด๋ฏธ์ง ํ๋ฆฌ๋ก๋ฉ With Promise
- [์์ํ] ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ์ด๋?
- [๋ฐ์ธ์] Android์ Data Binding: DataBindingUtil vs Binding.inflate
- [๋ฐ์ธ์] DI, Hilt ๋ฝ๊ฐ๊ธฐ
- [๋ฐ์ธ์] Retrofit ๋์ ๊ธฐ
- [๋ฐ์ธ์] 360๋ Spin Image Touble Shooting
- [๊น์ ๋น] ์ปค์คํ ๋ค์ด์ผ๋ก๊ทธ ๊ตฌํ