본문 바로가기
내 것 만들기

보잘 것 없는 주니어의 Google Gemini API Competition 출전 썰 | 그런데 Android Jetpack Compose를 곁들인..

by nongsusanmul 2024. 8. 13.
반응형

마감 한 달 전..

알고 지내던 백엔드 친구에게 연락이 왔다. 너 이런 건 안나가냐? 하며 던져 준 링크. 와우, 들은 적도 없는 초-대규모의 구글 대회라니? 심지어 총 상금이 100만 달러라니?? 근데 이걸 마감 한 달 전에 알았다니????

 

Gemini API 개발자 대회 참가하기  |  Gemini API Developer Competition  |  Google AI for Developers

Gemini API를 통합하고 프롬프트를 빠르게 개발하며 아이디어를 코드로 변환하여 AI 앱을 빌드할 수 있습니다.

ai.google.dev

 

시작은 세 달 전인 5월 14일 이었지만 내가 전달 받은 날은 2개월 가량 지난 7월 6일 이었기에, 조금만 더 일찍 알았더라면 상금을 가져갔을 거라는 굉장히 거만한 후회를 하고 있던 참이었다. 혹시나 주변의 안드로이드 노예분들 중에 몰래 고려하고 있는 사람이 있다면 슬쩍 껴서 자그마한 팀 프로젝트라도 해보면 어떨까 하며 이래저래 수소문을 해보았지만, 다들 처음 들었다면서 지금 시작하면 뭐 하나 제대로 만들 수는 있겠냐는 반응이 대부분이었다.

 

 

하지만 나, 패기있는 주니어

한창 내 피땀 흘려 번, 내 피부와도 같은 돈이 소모되는 게 너무나 아까워 돈욕심에 물이 올랐을 시기였다. 지금 생각해보면 전문가들이 판치는 대회에 만 1년차 개발자 따위가 노려볼 산이 아니긴 했으나, 아무도 못한다고 혀를 내두를 때 아낌없이 시간을 투자해 본 경험은 자랑스럽기도 하다(?).

그 소식을 들은 순간 부터 밥을 먹으면서도, 화장실에서 사색의 시간을 가질 때도 초간단 앱을 만들어 상금을 타 먹는 멋찐 나를 상상했다. (진짜 최고의 동기는 Money가 맞습니다) 그러다 순간 떠오른 것이, 평소에 내가 Gemini를 찾던 순간은 문제 해결과 조언이 필요할 때임이었다는 것을 깨달았다.

그 생각이 든 후 1분 만에 구상해 낸 서비스가 타로 점술 앱이었다. 과학과 미신의 대 통합! Gemini가 말아주는 카드 타로 점!! 이거다, 바로 출전!!!

 

 

3주만에 완성하기

회사는 개인 기술력을 확장하고 스킬업을 하는 데에 있어서 자유롭게 열려있었기 때문에, 이런 대회 참여는 경험의 목적으로도 좋았고, 기존 업무도 마무리가 끝난 idle 상태라 타이밍도 적절했다. 👍 다른 프로젝트에 이름이 올라가 있었다면 절대 상상도 못했을 것..

컨셉을 가장 먼저 정해보았다. 머리 속에 떠오르는 결과물은 거의 친구와도 같은 보모의 모습을 한 점술가 제미니였지만, 그렇게 까지 LLM 프롬프트를 잘 다룰 자신은 없었으므로 간단한 기능만 만들되, 카드 애니메이션을 멋들어지게 넣어 사람들의 눈을 사로잡아 보기로 했다. 그리고 Gemini가 나와 같은 공간에서 카드를 주고, 카드를 "직접 터치"하여 진짜 타로 점을 보러 온 것 같은 느낌을 주는 것에 집중했다.

기본 기능은 Gemini가 고민을 듣고 타로 스프레드를 직접 결정한 다음, 카드를 뽑게 하여 나온 결과를 읽어 준다. 그것만 있어서는 재미없을 것 같아 자그맣게 다른 기능도 추가했는데, 바로 "좋은 생각 카드"다. 예전에 네이버 웹툰 비질란테1을 매주 재밌게 챙겨보던 애독자로서, 조강옥이라는 캐릭터의 좋은 생각 카드는 잊을 수 없는 굉장히 매력적인 디자인이었다. 어떤 앱을 만들 구상을 해도 항상 그 기능을 보조 기능으로 넣고 싶었고, 위로와 조언을 위한 점술 앱에 정말 딱 맞는 기능이라는 생각이 들었다.

이 정도 볼륨의 두 가지 기능이라면 3주로 충분하겠다는 판단이 섰고, GitHub 레포지토리를 바로 만들어 빌드업을 시작했다. 테스트 코드나 CI/CD 설계는 짧은 리소스에 고려할 수 있는 대상이 아니었기 때문에 적용하지 않았다. 하지만 Issue와 PR은 계속 발전하는 앱을 만들고자 한다면 사용자와의 소통에서 필수적이라고 생각하기 때문에 배제하지 않았다!

물론 한동안은 나밖에 없을 예정이다.

 

 

기능 1: 좋은 생각 카드

"좋은 생각 카드" 라는 말은 영어로 직역하기 쉽지 않았다. "Good Idea Card~" 처럼 들리기 쉽기 때문이다. 물론 그렇게 담백하게도 해석할 수 있지만, "좋은 생각"이라는 건 현재의 문제에 대한 시각을 바꿔 바라보라는, 더 포괄적인 "조언자"의 느낌이 강했다. 어떻게 번역하면 좋을까 싶었다. 이럴 때, 또 Gemini가 일상에 활용된다!

역시 Gemini는 최고야!

한국적 감성이 들어간 선택일지도 모르지만, 어감 같은 것도 고려하면서 선택한 것은 "Wisdom Card" 였다. 간단하면서 컨셉에 부합하는 게 아주 마음에 든다.

이제 Gemini를 이용해 기능을 구현해보자. 조강옥의 좋은 생각 카드를 잘 살펴보면 여러 나라의 "명언"으로만 구성되어 있다는 것을 알 수 있다. 항상 고정된 명언 목록만 가져오면 별로니까, 랜덤한 10개의 짧은 명언을 뽑아달라고 작성했다.
(Gemini API 적용은 Docs를 참고하여 쉽게 적용할 수 있다)

val quoteList = mutableStateListOf<String>()

// suspend function
try {
    val prompt = """
        Please select 10 short quotes of less than 60 characters
        and write 10 lines in the format of [number]:[content] without any additional words.
    """.trimIndent()
    val response = model.generateContent(prompt)

    isDoneRequest = true
    if (response.text.isNullOrBlank()) { // fail
        speech("Something is wrong with Gemini.\nPlease wait a moment and try again.")
        onError()
    } else { // success
        quoteList.addAll(
            response.text?.split("\n")?.map {
                it.split(":").last().trim()
            } ?: emptyList()
        )
        speech("Alright! Now, take your pick.") // TODO : temp line
    }
} catch (e: GoogleGenerativeAIException) { // fail
    isDoneRequest = true
    speech("Something is wrong with Gemini.\nPlease wait a moment and try again.")
    onError()
}

그리고 quoteList로 push된 데이터를 이용해 카드를 촤라락 펼쳐주는 애니메이션을 보여주도록 구현하며, 도중에 터치할 수 없도록 enable State를 잘 설정했다. 최근 프로젝트는 전부 AndroidView를 이용했었는데, 확실히 Jetpack Compose가 흐름 설계나 애니메이션 구현에 대해서는 엄청 빠르고 쉽다고 느꼈다. 왜 안씀?

 

 

기능 2: 타로 카드

타로에 대한 지식이 거의 전무했던 나에게 스프레드와 결과 해석의 정보를 준 건 역시 나무위키였다. 그리고 카드의 이미지는 열심히 구글링을 하다 찾은 AI factory의 글에서 위키피디아 이미지 링크라는 좋은 정보를 찾을 수 있었다. 그 중 "The Lovers"의 카드 이미지 링크는 잘못 되어 있어서, 직접 위키피디아를 뒤져 정확한 링크로 고쳐주었다.

타로 카드는 과정이 복잡하지만 Gemini에게 자율성을 최대한 주고 싶었다. 타로의 꽃은 상대의 "고민"이 아닌가? 고민을 듣고 제미니가 스스로 판단하여 고민으로써 적절한 문장인지, 또 그 고민에 가장 걸맞는 스프레드는 무엇인지 고르도록 프롬프트를 짰다. 결과 해석도 "고민"에 초점 맞추어 해석하도록 지시했다. 여기에서는 앞서 했던 대화들이 중요했기 때문에 생성형 model의 startChat 메소드를 이용했다.

// send question and request to select spread
suspend fun sendSpreadPrompt(question: String): String {
    val prompt = """
        You are a Tarot expert.
        Understand the [Questions] below and select one of the “One Card”, “Three Card” or “Celtic Cross” spreads and mark them after [Spread].
        And after [Reason], briefly state the reason for your choice in relation to the [Question].
        Also if the question is not a complete sentence, write “None” after [Spread].
        [Question]: $question
        [Spread]:
        [reason]:
    """.trimIndent()
    val response = model.generateContent(prompt)
    
    // ...
    addPromptHistory(prompt)
    addResponseHistory(response.text!!)
}

suspend fun sendSpreadChat() {
    val chat = model.startChat(history = listOf(
        content(role = "user") { text(prompt) },
        content(role = "model") { text(response) },
    ))
    val response = chat.sendMessage("""
        I drew the cards $cardList in order.
        After briefly interpreting each card in relation to [question],
        kindly write a comprehensive evaluation.
        Add a line break at the end of each sentence.
    """.trimIndent())
    // ...
}

 

 

[좌] 3-Card Spread (적당한 볼륨의 질문) [우] Celtic-Cross Spread (복잡한 질문)

 

 

플레이 스토어 등록 실?패

거의 다 만들고 나니 플레이 스토어에도 올리고 싶었다. 예전 플레이 스토어에 쉽게 올리던 과거를 떠올리며 제출 직전에 심사를 받아보려고 했다. 그러나 너무 많은 것이 바뀌어 있었다.

주소인증 문서 업로드, 안드로이드 기기 보유 인증, 비공개 테스터 20명 모집 등 개발자 계정을 등록하려면 3만원 가량의 돈을 지불해야함에도 불구하고 플레이스토어에 앱을 올릴 권한은 또 인증 받아야만 했다. 애초에 이럴거면 첫 앱 출시할때 돈을 받던지요... 주소인증만 5일 걸려서 성공했는데 비공개 테스터 20명 구하기는 내 수준에서 정말 쉽지 않은 일이라 지금으로써는 포기를 마음먹어야만 했다. 뭐 품앗이 오픈톡방 같은 것도 있다곤 하는데, 20명이 바로 모일지도 모르거니와 모르는 사람들과 메일 왔니 안왔니 연락 주고 받으며 출시 전의 앱을 공개하기도 꺼려졌다.

20개 자기 구글 계정 만들어서 하신 분도 있다곤 하던데 나중에 인싸가 되었을 때 다시 해보는 걸로 하고, 그 동안은 간간히 부족한 점을 발전시켜 나가야겠다.

 

 

아무튼 제출 완료

다 쓰고 보니, 스프레드 종류가 몇 개 있고 이런 거 저런 거 테스트 해봐 달라 얘기했어야 했는데 그걸 너무 두루뭉술하게 적었다. 아쉽지만 그래도 수고했으니 스스로 칭찬이나 해줘야겠다.

자신을 칭찬해 주세요...

아래는 제출한 유튜브 데모 영상이다. 편집은 Vrew 라는 국내 무료 편집 툴을 이용했다. 설명에는 AI 보이스 무료/유료가 있댔는데 처음 편집한 영상은 무료 체험을 시켜주는 건지 돈이 나가지 않았다. 공식 사이트에 사용법에 대한 가이드가 잘 나와있고, 질의 응답도 접근성이 좋았다. 실제로 사용하기도 어렵지 않아서 금방 익숙해졌고 덕분에 빠르게 제출하고 쉴 수 있게 되었다. 😩 내 주말을 지켜 준 Vrew 팀에게 감사의 인사를... (광고 아님)

 

도입부도 많이 신경써서 디자인 했다. 앱 내에서 화면 줌-인/아웃을 넣고 싶었지만 시간이나 실력같은 리소스가 너무 부족하여 구현하지는 못했다. 주인공인 GArcani는 다른 사람에게도 수정구슬처럼 보일까 궁금하긴 하다. 애니메이션과 Canvas 조작으로 최대한 신비로운 AI처럼 보이도록 디자인했긴 하다만..

https://youtu.be/Yidab-97g1g?si=saQBA3RTeF2OYTBo

주말이나 자투리시간 이용해서 개발하다보니 실제 개발 기간은 더 짧았지만, 4주에 가까운 기간 동안 배운 것도 많고 자신감도 조금 올랐다. 유튜브에 검색해 출품작들 하나씩 둘러보는데 역시 전문가들은 다르긴 다르다. 보상같은 건 주니어 개발자는 기대도 못할 수준이긴 했다. ㅎㅎㅎ. 참여에 의의를 둔다. 어찌됐든 좋아하는 일로 참여하는 외부 활동은 나에게 긍정적인 영향을 잔뜩 안겨주는 것 같다.

다음에도 비슷한 기회가 있으면 팀으로 참여해보고 싶다. 사람들과 열정적으로 고민하고 토론하며 완성품을 만드는 게 혼자할 때보다 더 재밌지 않을까? 내 글을 읽게 된 누군가도 기회 비스무리한 게 눈 앞에 보인다면 그것을 잡고 진짜 기회로 바꾸는 것은 나 자신이라는 것을 잊지 않았으면 좋겠다. 상황은 핑계일 뿐!

반응형