API를 불러올때 API키가 필요하다.
그러면 코드에 API키를 적어줘야하는데
그냥 class파일등에 적을경우 apk를 뜯어보는 사람이 있다면 키를 볼 수 있고
그렇게되면 데이터를 삭제하거나, 서비스를 마비시키거나, 아니면 그걸 사용해서 aws등을 사용할때 막대한 비용청구를 해야할 수도 있다.
그렇기 때문에 이걸 숨겨야하는데 어떻게 숨길까?
그래들에 보면 로컬 프로퍼티를 찾을 수 있는데
보통 키값을 로컬 프로퍼티에 많이 숨긴다고 한다.
로컬 프로퍼티는 접근하기 힘든데, 권한이 있는 사용자만 접근 할 수 있기 때문에 보안에 좋고
유지 관리나, 버전 관리등에도 유리 하기 때문에 로컬프로퍼티를 사용한다
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=C\:\\Users\\zaq86\\AppData\\Local\\Android\\Sdk
REST_API_KEY="키값"
로컬 프로퍼티에 키값을 적어주고
build.gradle.kts (module)로 이동해준다
val properties = Properties()
properties.load(project.rootProject.file("local.properties").inputStream())
일단 키값을 저장해줄 프로퍼디를 만들어준다
프로퍼티를 load해 로컬 프로퍼티의 내용을 읽어주고, inputStream 으로 파일의 바이트 스트림을 반환한다.
defaultConfig {
applicationId = "com.example.raffit"
minSdk = 26
targetSdk = 33
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField ("String", "REST_API_KEY", properties.getProperty("REST_API_KEY"))
}
기본 빌드를 설정해주는 defaultConfig {} 안에
buildConfigField ("String", "REST_API_KEY", properties.getProperty("REST_API_KEY")) : buildConfigField를 사용해 빌드 설정 필드를 추가한다
이 필드는 빌드 과정에서 생성되는 BuildConfig 클래스에 추가되는데
BuildConfig 클래스에 들어가보면 추가된걸 확인할수 있다
이제 이 키값을 받아줘야하는데
클래스가 있고 그 안에 REST_API_KEY가 선언 되었으니
BuildConfig.REST_API_KEY를 사용하면 된다
이걸 어디에 사용할까?
OkHTTP에선 Interceptor를 사용할 수 있는데
인터셉터는 HTTP 요청과 응답을 가로채어 전/후 처리를 진행하는 역할을 한다.
이를 이용하면, HTTP 요청과 응답에 대한 로깅, 헤더 추가, 인증 토큰 추가, 요청 재시도 등의 과정을 구현할 수 있다.
매 요청마다 헤더를 추가하는 코드를 만들 필요 없이 REST API 키를 KakaoAK라는 접두사와 함께 문자열로 만들고
헤더에 추가되며, 이 헤더를 통해 서버는 클라이언트가 유효한 API 키를 가지고 있는지 판단할 수 있게 만들 수 있기 때문에 인터셉터를 이용한다.
class KakaoInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
val auth = "KakaoAK ${BuildConfig.REST_API_KEY}"
builder.addHeader("Authorization", auth)
return chain.proceed(builder.build())
}
}
이런식으로 인터셉터에 헤더를 추가하게 만들어 준뒤
object KakaoApi {
private const val KAKAO_BASE_URL = "https://dapi.kakao.com/v2/search/"
private fun createOkHttpClient(): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
if (BuildConfig.DEBUG) {
interceptor.level = HttpLoggingInterceptor.Level.BODY
}
else {
interceptor.level = HttpLoggingInterceptor.Level.NONE
}
return OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
.addInterceptor(KakaoInterceptor())
.build()
}
private val gson = GsonBuilder()
.setDateFormat("yy-MM-dd HH-mm-ss")
.create()
private val kakaoRetrofit =
Retrofit.Builder().baseUrl(KAKAO_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(
createOkHttpClient() // Gson팩토리에서 데이트 컨퍼팅 가능 addConverterFactory 최소 논란의 법칙
).build()
val kakaoNetwork: KakaoSearchService = kakaoRetrofit.create(KakaoSearchService::class.java)
}
OkHttpClient.Builder()에 추가해주고 빌드 해주면 된다
그러면 자연스럽게 이전에 선언했던 서비스 인터페이스에서
헤더 부분을 빼면 된다
interface KakaoSearchService {
@GET("image") //카카오 이미지
suspend fun searchImage(//인터셉터 okhttp로 키 넣어줌
@Query("query") query: String, //검색을 원하는 질의어
@Query("sort") sort: String? = "recency", // 정렬 방식 accuracy(정확도순) 또는 recency(최신순), 기본 값 accuracy
@Query("page") page: Int, // 결과 페이지 번호, 1~50 사이의 값, 기본 값 1
@Query("size") size: Int? = 50 // 한 페이지에 보여질 문서 수, 1~80 사이의 값, 기본 값 80
) : ApiResponse<ImageDocument>
@GET("vclip")
suspend fun searchVclip(
@Query("query") query: String,
@Query("sort") sort: String? = "recency",
@Query("page") page: Int,
@Query("size") size: Int? = 30
) : ApiResponse<VclipDocument>
}
https://github.com/google-gemini/generative-ai-android/tree/main/generativeai-android-sample
깃을 보는대, 로컬프로퍼티에 apiKey만 추가해줘도 BuildConfig에 작성이 된다.
플러그인에 있는 secrets-gradle-plugin 때문이다.
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") version "2.0.1" apply false
프로젝트 레벨의 플러그인에 추가해주고
모듈 레벨에도 추가 해주기만 하면 자동으로 BuildConfig에 key가 추가가 된다.
프로퍼티 만들고 추가해주고 그럴 필요가 없어졌다.
좋은 글 감사합니다! 덕분에 도움 됐어요