24.01.27 Rest_API키를 숨기기

KSang·2024년 1월 29일
1

TIL

목록 보기
42/101

Rest_API키를 숨기기

API를 불러올때 API키가 필요하다.

그러면 코드에 API키를 적어줘야하는데

그냥 class파일등에 적을경우 apk를 뜯어보는 사람이 있다면 키를 볼 수 있고

그렇게되면 데이터를 삭제하거나, 서비스를 마비시키거나, 아니면 그걸 사용해서 aws등을 사용할때 막대한 비용청구를 해야할 수도 있다.

그렇기 때문에 이걸 숨겨야하는데 어떻게 숨길까?

Gradle

local.properties

그래들에 보면 로컬 프로퍼티를 찾을 수 있는데

보통 키값을 로컬 프로퍼티에 많이 숨긴다고 한다.

로컬 프로퍼티는 접근하기 힘든데, 권한이 있는 사용자만 접근 할 수 있기 때문에 보안에 좋고

유지 관리나, 버전 관리등에도 유리 하기 때문에 로컬프로퍼티를 사용한다

# 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)로 이동해준다

build.gradle

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 클래스에 추가되는데

image

BuildConfig 클래스에 들어가보면 추가된걸 확인할수 있다

Retrofit

이제 이 키값을 받아줘야하는데

클래스가 있고 그 안에 REST_API_KEY가 선언 되었으니

BuildConfig.REST_API_KEY를 사용하면 된다

이걸 어디에 사용할까?

OkHTTP에선 Interceptor를 사용할 수 있는데

인터셉터는 HTTP 요청과 응답을 가로채어 전/후 처리를 진행하는 역할을 한다.

이를 이용하면, HTTP 요청과 응답에 대한 로깅, 헤더 추가, 인증 토큰 추가, 요청 재시도 등의 과정을 구현할 수 있다.

매 요청마다 헤더를 추가하는 코드를 만들 필요 없이 REST API 키를 KakaoAK라는 접두사와 함께 문자열로 만들고

헤더에 추가되며, 이 헤더를 통해 서버는 클라이언트가 유효한 API 키를 가지고 있는지 판단할 수 있게 만들 수 있기 때문에 인터셉터를 이용한다.

Interceptor

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가 추가가 된다.

프로퍼티 만들고 추가해주고 그럴 필요가 없어졌다.

2개의 댓글

comment-user-thumbnail
2024년 4월 27일

좋은 글 감사합니다! 덕분에 도움 됐어요

1개의 답글