코딩하는 개굴이

[안드로이드] 저작권 걱정? No! 구글 폰트 적용하기 본문

안드로이드

[안드로이드] 저작권 걱정? No! 구글 폰트 적용하기

개굴이모자 2024. 4. 28. 21:09
반응형

사이드 프로젝트를 할 때, 가장 신경쓰이는 점이 무엇이냐 하면 개인적으로는 저작권이다.

회사에서는 별도의 법무팀의 검토 후 진행되기에 신경쓰지 않았던 것들을
리소스 하나하나 신경써야하기 때문인데 그 중 가장 골치 아픈 것이 폰트이다.

예쁜 것은 저작권에 걸리고 그렇다고 신경을 쓰지 않기에는 전체적으로 완성도에 영향을 끼친다.

 

따라서 결국 돌고 돌아 몇개의 안전한 폰트들만 받아 ttf 로 넣어 사용하게되곤한다.
언제까지 배달의 민족 주아체만 쓸 것인가?!

 

이게 아냐! 내가 원했던 폰트는..!!!

 

 

이걸 webview 를 통한 파일 다운로드 없이, 어떻게 만들어야하는가...

 

 

특히나 본인의 경우 사이드 프로젝트(사진을 읽다)를 하며 여러 폰트들을 예시로 보여주고 사용자가 고를 수 있게끔 하는 기능 구현을 위해 webview 로 font 사이트를 띄우고 download 를 감지해 그 파일을 찾아 폰트를 적용할 수 있게하는 방식을 사용했지만, 이제 더 이상 그것을 사용할 수 없게되면서 위기에 봉착했다.

 

 

해당 기능 구현 시 신경써야할 점은 아래와 같다.

 

첫째, 저 모든 ttf 파일들을 앱에서 가지고 있기에는 용량이 커지게 되므로 동적으로 파일을 가져올 수 있어야한다.

둘째, 동적으로 가져올 때 리스트에 효율적으로 뿌리기 위해 typeface 를 불러올 수 있어야한다.

셋째, 사용자에게 노출하기 위해 필요한 폰트들을 순차적으로 별도 호출 할 수 있어야한다.

 

 

 

그 과정에서 찾은 것이 바로 감사한 갓 구글에서 무려 무료로 서비스해주고 있는DownloadableFont 를 통해 Google Font 를 사용하는 것이다.

 

 

 

 

 

 


 

DownloadableFont란?

우선 DownloadableFont 란, 폰트 파일을 앱에 번들로 묶거나, 앱 내에서 글꼴을 다운로드 하도록 허용하는 방식 (마치 본인이 위에서 웹뷰를 통해 제공했던 기능과 같다) 대신, API 를 이용해 애플리케이션에서 글꼴을 요청할 수 있다.

 

DownloadableFont 를 사용할 경우, 앱 크기를 줄여 앱의 설치 성공률을 높이고, 사용자의 모바일 데이터, 기기의 메모리, 디스크 공간을 절약할 수 있다는 장점을 확보할 수 있다.

 

 

아니 어떻게 동작하기에 그게 되는가?

우선, 글꼴 제공 업체 (이번 포스팅에서는 Google Font 이다) 는 글꼴을 검색하고 로컬에서 캐시하여 다른 앱이 글꼴을 요청하고 공유할 수 있도록 한다. 따라서, 즉 다른 앱이 쓰고 있다면 공유가 가능하다는 것이다. 이 얼마나 효율적인가?

 

 

 

어떻게 적용하는가?

DownloadableFont 는 적용할 수 있는 방법이 3가지가 있다.

 

다운로드 가능한 글꼴 사용  |  Views  |  Android Developers

Android 8.0(API 수준 26)을 사용하면 APK에 번들로 묶지 않고 글꼴을 다운로드할 수 있습니다.

developer.android.com

 

그 중, 이번 포스팅에서는 2번, 3번을 짬뽕하여 동적으로 폰트 리스트를 가져오고,
사용자에게 노출되는 화면에 대해서 폰트를 불러와 적용되는 방식을 보여주도록 하여 기존의 구현 방식을 조금 더 업그레이드 해볼까한다.

 

 

 


 

 

Dependency 추가

늘 그랬듯 의존성을 추가해준다. androidx core 는 Android API 버전 14 이상 기기에 대해 DownloadableFont 를 지원한다.

dependencies {
    ...
    implementation("androidx.core:core-ktx:2.2.0")
}

 

 

우리가 사용할 클래스 중 중요한 것은 아래 두개인데, 각각 어떤 역할을 하는지 코드를 짜기 전에 Developer Guide 의 내용을 먼저 빠르게 살펴보자.

 

음, 당연한 얘기다. 설명을 덧붙이자면, 우리는 아까 위의 다이어그램에서 보았듯이 FontContract API 를 사용해 필요한 폰트를 검색할 것이다. 그 과정에서 FontContract API 에게 FontRequest 를 넘기고 결과를 받기 위해 Callback 또한 override 할 것이다. 그 과정에서 쓰이는 클래스들이라고 생각하면 간단할 것 같다. 이상은 코드를 확인하면서 알아보자.

 

 

FontRequest 생성하기

FontRequest 클래스를 생성하는 것이 제일 첫번째 단계이다. 인스턴스를 만들어 글꼴 제공 업체가 어디인지, 권한은 어떤지, 어떤 쿼리인지, 인증서는 어떤지 확인하는 것이다. 대부분의 내용은  Google Fonts 혹은 해당하는 글꼴 제공 업체의 문서들에서 확인 할 수 있다.

그 결과 아래와 같이 인스턴스를 생성할 수 있었다.

 

val request = FontRequest(
                "com.google.android.gms.fonts",
                "com.google.android.gms",
                font,
                R.array.com_google_android_gms_fonts_certs
            )

 

인증서는 Developer Guide 에 링크되어있는 Sample App 의 certs 파일을 참고하였다.

 

 

FontRequestCallback 생성하기

val googleFontDownloadCallback = @RequiresApi(Build.VERSION_CODES.O)
            object : FontsContractCompat.FontRequestCallback() {

                @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
                override fun onTypefaceRetrieved(typeface: Typeface) {
                    logd("typeface retrieved $typeface")
                }

                override fun onTypefaceRequestFailed(reason: Int) {
                    // do nothing
                    logd("typeface request failed with $reason")
                }
            }

 

위를 보면 onTypefaceRetrieved() 와 onTypefaceRequestFailed() 메서드가 재정의 된 것을 볼 수 있다.

각각을 살펴보자면 우선, onTypefaceRetrieved 는 요청이 완료되었을 때 콜되며, typeface 가 반환된다. 따라서, 이 시점에 TextView 의 글꼴 설정을 할 수 있다. onTypefaceRequestFailed 의 경우 글꼴 요청 프로세스에 오류가 발생할 경우 수신된다. 관련 reason 코드에 해당하는 내용은 별도 문서를 참고할 수 있다.

 

 

이 모든 것은 requestFont 를 하기 위한 빌드업이었다

준비가 완료되었다면 이제 FontsContractCompat.requestFont 메서드를 호출하는 일만 남았다. 글꼴 제공 업체에서 글꼴을 검색하는데 사용되는 이 메서드는 글꼴이 캐시에 있는지 먼저 확인하고 로컬에서 사용할 수 없다면 비동기적으로 글꼴을 검색해 결과를 콜백에 전달한다.

 

파라미터의 경우 아래와 같다.

  • Context 클래스의 인스턴스
  • FontRequest
  • FontRequestCallback
  • 스레드에서 글꼴을 가져오는 핸들러

 

대략적인 request 코드는 아래와 같다.

 

    var fontDataForDisplay: ArrayList<FontData?> = arrayListOf()
    val fontNamesForRequest: ArrayList<String> = arrayListOf()

    private val handlerThread = HandlerThread("FontRequest").apply { start() }
    private val handler = Handler(handlerThread.looper)

    fun requestFont(
            fontList: List<String>,
            onRetrieved: ((ArrayList<FontData>) -> Unit)
        ) {
            var nameCount = 0 
            val retrievedFontData = arrayListOf<FontData>()
            fontList.map { font ->
                val request = FontRequest(
                    "com.google.android.gms.fonts",
                    "com.google.android.gms",
                    font,
                    R.array.com_google_android_gms_fonts_certs
                )

                val googleFontDownloadCallback = @RequiresApi(Build.VERSION_CODES.O)
                object : FontsContractCompat.FontRequestCallback() {

                    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
                    override fun onTypefaceRetrieved(typeface: Typeface) {
                        logd("typeface retrieved $typeface")
                        retrievedFontData.add(
                            FontData(
                                typeface,
                                fontList[nameCount],
                                FontType.DOWNLOAD,
                                fontList[nameCount]
                            )
                        )
                        nameCount++
                        if (nameCount == fontList.size) {
                            onRetrieved.invoke(retrievedFontData)
                        }
                    }

                    override fun onTypefaceRequestFailed(reason: Int) {
                        // do nothing
                        logd("${fontList[nameCount]} typeface request failed with $reason")
                        nameCount++
                        if (nameCount == fontList.size) {
                            onRetrieved.invoke(retrievedFontData)
                        }
                    }
                }

                FontsContractCompat.requestFont(
                    getApplication<Application>(),
                    request,
                    googleFontDownloadCallback,
                    handler
                )
            }
        }

 

 

아니 잠깐만, 그러면 저거 request 의 쿼리는 어찌알고 호출합니까

그렇다. 마치 마무리 같지만 우리는 한가지를 간과했다.

저 쿼리에는 Font Name 이 들어가야하는데, 이를 어떻게 가져올 것인지 모르지 않는가?

Developers Guide 에 속을 뻔했다.

 

그 답은 Google Font 가이드의 개발자 API 문서를 살펴보면 찾을 수 있다.

 

웹 및 앱 개발자를 위해 제공되는 해당 API 는 별도의 조건 없이 전체를 불러오고자 한다면 그냥 간략히 API KEY 를 발급받아 아래처럼 오출하기만 하면 된다.

 

https://www.googleapis.com/webfonts/v1/webfonts?key=YOUR-API-KEY

 

그러면 아래와 같이 response 를 받을 수 있는데, 이를 이용해 입맛대로 데이터를 생성하면 되겠다.

 

{
 "kind": "webfonts#webfontList",
 "items": [
  [...]
  {
    "family": "Anonymous Pro",
    "variants": [
      "regular",
      ...
      "700italic"
    ],
    "subsets": [
      "cyrillic",
      ...
      "latin-ext"
    ],
    "version": "v21",
    "lastModified": "2022-09-22",
    "files": {
      "regular": "http://fonts.gstatic.com/s/anonymouspro/v21/rP2Bp2a15UIB7Un-bOeISG3pLlw89CH98Ko.ttf",
	  ....
      "700italic": "http://fonts.gstatic.com/s/anonymouspro/v21/rP2ap2a15UIB7Un-bOeISG3pHl4OTCzc6IG30KqB9Q.ttf"
    },
    "category": "monospace",
    "kind": "webfonts#webfont",
    "menu": "http://fonts.gstatic.com/s/anonymouspro/v21/rP2Bp2a15UIB7Un-bOeISG3pHl028A.ttf"
  },
  {
    "family": "Antic",
    "variants": [
      "regular"
    ],
    "subsets": [
      "latin"
    ],
    ...
    "kind": "webfonts#webfont",
    "menu": "http://fonts.gstatic.com/s/antic/v19/TuGfUVB8XY5DRZZKq9w.ttf"
  },
  [...]
 ]
}

 

 

API KEY 를 받는 방법은 문서를 통해 확인할 수 있고, 결과적으로 아래와 같은 요청의 결과에서 family 에 해당하는 값들을 리스트로 뽑아내면 아까의 쿼리하는 대상이 되는 것이다.

 

interface GoogleWebFontApi {
    @GET("/webfonts/v1/webfonts")
    suspend fun getWebFont(
        @Query("sort") sort: String = "alpha",
        @Query("subset") subset: String? = null,
        @Query("family") family: String? = null,
        @Query("capability") capability: String = "WOFF2",
        @Query("key") key: String = "your key"
    ): Response<WebFontResponse>
}

 

 

그 결과가 이것이다.

 

Google Font 웹을 통해 사용자에게 직접 다운받으라고 했던 지난 날들은 굳바이 이다.

더 방대하게 보여줄 수 있고, 간편하게 받아올 수 있게 되었다.

 

쏟아지는 폰트 무한 스크롤 계획대로

 

반응형
Comments