해커톤 경험을 통해 엿본 AI시대에 개발자가 가져야 할 자세

해커톤 경험을 통해 엿본 AI시대에 개발자가 가져야 할 자세

요약: 카카오페이 해커톤에서 AI 채용 도우미 ‘AIVA’를 만들며, 채용 과정에서 생산성을 올리는 방법을 고민했습니다. 이력서와 과제를 LLM으로 분석하고, 실시간 코드 실행 환경까지 구현하며 다양한 실험을 시도해 보았습니다. 개발 과정에서 프롬프트 설계나 캐싱, 출력 제어 등 실제 활용 시 고려해야 할 점들도 많이 배울 수 있었습니다. 결국 중요한 판단은 여전히 사람이 해야 한다는 점을 다시 한번 느낄 수 있었던 경험이었습니다.

💡 리뷰어 한줄평

wade.hong AI 시대, ‘밥그릇’ 걱정 대신 직접 뛰어들어 채용 AI도구를 만든것이 인상 깊었습니다. 또한 아키텍처 설계와 어려웠던점도 같이 공유되어서 좋은 가이드가 되었습니다.

river.pool 채용이라는 익숙한 주제를 AI 도구로 풀어가는 여정이 인상 깊고 재미있게 다가왔습니다. 막연하게 느껴졌던 AI 기술의 실제 활용 방식을 엿볼 수 있어 많은 참고가 되었습니다.

시작하며

안녕하세요, 저희는 인프라플랫폼실의 잔, SE팀의 이곤, 빌리, 퐁, API플랫폼팀의 이안입니다. 하얗게 불태운 2025년 카카오페이 X AWS 해커톤(이하 카페톤) 참여 경험을 소개하고자 합니다.

지난 카페톤은 평소에 했던 채용 관련 고민을 AI로 풀어낼 수 있는 좋은 기회였습니다. AI 완전 초보자들이 모여 채용 AI 도우미 AIVA를 개발하며 무려 4위라는 유의미한 결과를 낳기까지 정말 쉽지 않은 과정이었는데요. 수상까지 해서 감사하지만, 무엇보다 해커톤을 진행하면서 AI 시대에 개발자는 어떤 자세로 접근해야 할지 고민해 볼 수 있어 더욱 뜻깊었습니다.

그럼 지금부터 해커톤에 참가한 계기, 왜 하필 채용을 주제로 했는지, 아키텍처까지 차근차근 말씀드리겠습니다.

해커톤 참가 계기

요즘의 AI는 빠르게 우리에게 다가오고 있습니다. 과거 특정 시기에 기술적으로 광풍은 불었지만 유의미한 형태로 현실 세계에 나오지 못했던 기술들과는 사뭇 다릅니다. 개발자로서 하루가 다르게 새로운 뉴스가 나오는 AI기술을 바라볼 때마다 한편으로는 신기하고 편리하면서도 이 기술이 내 밥그릇을 뺏는 것은 아닐까라는 걱정을 가지고 바라보고 있었습니다.

카페톤 공지사항
카페톤 공지사항

그러던 중 카페톤 공지가 발표되자마자 “그래 이 기술이 내 밥그릇을 뺏어갈지, 아니면 이번 기회에 나도 올라탈 수 있는지 확인하자”라는 생각으로 참가하게 되었습니다.(물론 우승상품이 마음을 먹는 데에 큰 도움을 주었습니다)

왜 채용을 주제로 했는가?

여느 기업들이 다 그렇겠지만, 카카오페이에서는 채용을 정말 중요하게 여겨 아주 많은 인원들이 긴 시간을 들여 채용 프로세스를 진행합니다.
하지만 매일 본래 자신의 업무를 하면서 지원자분들의 수많은 이력서를 하나하나 자세히 보는 것은 물리적으로 쉽지는 않습니다. 또한 객관성을 위해 타 팀의 채용 프로세스에 참여하게 되기도 하는데요, 이때에는 해당 팀의 업무도 파악하고 기술에 대해서 조사가 필요하기도 합니다.

심지어 상대적으로 간단한 이력서 평가와는 다르게 개발자 채용 시 기술과제 평가의 경우, 지원자의 역량과 채용을 진행하는 팀에서의 역량 요구 수준을 고려하여 다각도에서 분석이 이루어지고, 한 지원자의 과제를 평가하는 데만도 1시간 이상 걸리는 경우도 많습니다. 이 또한 여러 명이 개별적으로 평가해야 하므로, 많은 자원이 투입됩니다.

이 과정에서 저희는 한 가지 고민을 떠올리게 되었습니다.
‘지원자들이 들인 노력에 비해, 과연 그 정성이 충분히 전달될까?’

많은 지원자분들 역시 본업 혹은 준비 중인 상황 속에서 시간과 에너지를 들여 기술과제를 작성하셨을 텐데요, 평가자의 입장에서 체력적·시간적 한계 때문에 정성껏 작성된 과제를 그 가치만큼 주목받지 못하는 상황은 분명 아쉬운 일입니다.

저희는 이번 해커톤에서 개발한 AI 도우미 AIVA가 단순히 평가자의 부담을 줄이는 도구를 넘어, 지원자의 강점과 노력을 평가자에게 더 잘 전달하도록 돕는 역할을 할 수 있기를 기대했습니다.

실제로 직접 채용 프로세스에 몇 개월간 참여하면서, 이러한 문제들을 조금이나마 더 효율적으로 개선해보고자 하는 바람으로 채용을 주제로 AI 해커톤에 참여하게 되었습니다.

이력서 분석

이력서 요약
이력서 요약

이력서 분석의 경우 채용공고와 이력서의 내용을 콘텍스트로 제공하였습니다. 가장 기본적으로는 이력서의 요약과 결국 채용의 기준이 되는 채용공고의 각 항목별 일치도를 구하게 하였는데요, 여기에서 더 나아가 LLM의 진가인 ‘사고’에 따른 결과를 확인하기 위해서 채용공고를 고려하였을 때 강점과 적합성 등 인간이 할법한 사고 과정을 프롬프트로 입력하였더니, 정말로 사람’처럼’ 생각한 결과를 출력하게 되었습니다. 다음은 이 이력서 분석을 할 때의 시스템 프롬프트의 일부입니다.

당신은 전문 이력서 평가자입니다. 주어진 이력서 텍스트에 대한 피드백을 제공하는 것이 당신의 임무입니다.
다음 사항에 중점을 두고 평가해 주세요.
- 명확성과 간결성
- 직무 기술서와의 관련성
- 서식 및 구성
- 전반적인 인상
- 자세한 평가와 개선을 위한 제안을 제공해 주십시오. 이력서에서 눈에 띄는 특정 사항도 포함할 수 있습니다. 이력서가 직무 기술서에 적합한지 확인하십시오.
응답 형식은 markdown형식으로 작성하세요.
마크다운의 H1 헤더는 너무 강한 강조로 사용되므로 사용하지 마세요. 단, H2 헤더부터는 사용해도 괜찮습니다.
생성된 보고서의 각 섹션 제목은 H2 헤더로 작성하고, 각 섹션 제목에 적절한 아이콘을 포함하여 작성하세요.

단순히 ‘분석해 줘’라고 하는 경우에는 분석의 기준 및 출력 형태가 일정하지 않습니다. 그래서 직접 이력서 분석을 진행할 때 어떤 관점에서 분석하는지에 대해 고민을 해보며 1차적으로 그 내용들을 정리한 뒤 다시 LLM을 통해 이 내용들을 정리하여 프롬프트를 작성하였습니다.

채팅을 통한 이력서 분석

여기에 기본적으로 분석을 하도록 심어놓은 내용 외에도 시스템을 이용하는 사람이 직접 ‘이 사람의 부족한 점은?’, ‘이력서상 의심할만한 것은?‘과 같은 질문을 하거나 평가자마다 가지고 있는 추가적인 기준에 대해서 분석을 할 수 있는 채팅도 구현하였습니다.

비 인사팀의 많은 사람들이 채용과정을 처음 참여하게 되는 경우 ‘어떠한 것을 알아봐야 하는지 모르는 상황’에 부딪히게 되는데요, 이런 채용 관련 경험이 부족할 때에도 평가를 잘할 수 있도록 추천 질문도 받을 수 있도록 하였습니다.

외부 툴을 통한 이력서 분석

또한 이력서 평가 시 과거 경력 회사를 일일이 조사하는 수고스러움을 덜어내고자 했습니다. Bedrock agent에 lambda를 통하여 외부에서 회사 정보를 스크래핑하여 이를 요약하고 출력하였습니다.

BE개발자의 이력서를 API플랫폼팀 채용공고와 프론트엔드 채용공고에 각각 분석한 경우
BE개발자의 이력서를 API플랫폼팀 채용공고와 프론트엔드 채용공고에 각각 분석한 경우

저희 API플랫폼팀도 해커톤 진행당시 마침 채용을 진행하고 있어서, 해당 채용공고에 저의 이력서를 넣어봤더니 높은 채용 추천도가 나왔고, FE팀의 채용공고에 넣어보면 적합하지 않다고 출력되었습니다. 이러한 추천도/적합도를 평가에서 직접적인 근거자료로 사용하는 건 (스크린샷에 다 담기지 못한 내용을 포함하여) 여러 이유로 적합하지 않을 수 있습니다. 하지만 채용담당자가 빠르게 분석하는 도구로는 유용합니다.

기술과제 분석

코드 분석

과제내용과 문서화, 가독성 등에 대한 구현 분석
과제내용과 문서화, 가독성 등에 대한 구현 분석

기타 요소 및 강점과 약점 분석
기타 요소 및 강점과 약점 분석

기술과제 분석의 경우에는 후술하겠습니다만 전체 코드와 과제 명세를 콘텍스트로 LLM에 제공하였습니다. 여기에서 잠깐 생각해 봅시다. 잘 만들어진 기술 과제란 무엇일까요? 사실 이에 대해서는 모든 개발자가 저마다의 잣대를 가지고 있을 것입니다. 문제는 그 잣대라는 것이 그 사람의 경험과 능력에 따라 다르거나 아예 없을 수도 있습니다. 하지만 일반적으로 많은 개발자들이 가지고 있는 ‘일반적인 잣대’는 조금 생각을 해보면 어느 정도는 정해져 있다고 생각합니다. 예를 들어 기술과제이니 과제의 요구사항을 구현하는 것은 기본이겠지요? 이것 외에 문서화, 주석, 테스트코드의 커버리지 등도 있고 더 나아가 엣지케이스에 대한 처리, 효율성, 사용한 기술에 대한 이해도 등이 있을 것입니다. 이러한 기준들을 가지고 LLM에 질의하는 식으로 코드 분석 기능을 구현하였으며, 이는 해당 기술 또는 개발 그 자체의 숙련도가 부족한 평가자의 역량을 보완해 줄 수 있습니다.

이를 바탕으로 다음과 같이 프롬프트를 작성하였고, {criteria}에는 두 번째 줄의 각 기준을 하나씩 루프를 넣어가며 반복하여 요청하였습니다. 이는 출력 토큰의 한계 때문에 한번에 많은 기준에 대해 출력을 요구할 경우 상세한 내용을 생성할 수 없기 때문입니다.

먼저 과제내용을 분석해. 이후 과제에 대해 분석된 내용과 다음 항목들에 대해서 제출된 코드를 항목에 따라 하나하나 상세하게 최대한 많이 분석해. 분석 결과에는 그것이 왜 그러한지 상세한 설명을 달아줘.
과제내용에 대한 구현, 엣지 케이스, 에러처리, 보안, 테스트, 동시성, 효율성, 설계, 기술 스택의 이해도, 코드의 가독성 및 스타일, 문서, 주석, 강점 및 약점, 종합적인 평가

주의
- 제출된 코드를 분석할 때 과제내용을 기준으로 하는 것이 가장 중요해. 운영환경에서 사용할 코드가 아니라는 것을 고려해 줘. 따라서 운영환경에서 필요할만한 부분이 없다고 해서 그것은 부족한 것은 아니야. 물론 운영환경에서도 유효한 좋은 부분이라면 가산점을 부여해.
- 부족한 부분에 대해서는 설명할 때에는 관련 코드를 짧게 보여줘. 만약 관련 코드가 길다면 파일 이름으로 대체해 줘.
- 네가 대답할 수 있는 양에 한계가 있기 때문에 이번 대답에서는 다음에 대해 대답해 : {criteria}
- 대답할 때 무엇에 대해 대답하는지 처음에 다음과 같은 형식으로 먼저 명시하고 대답해.
# 🔍 {criteria}
- 가능하면 표 형태로 대답해.
- 점수는 표시하지 마.
- 종합의견을 마지막에 답할 때에는 최대한 상세하게 대답해.
- 출력포맷에서 마크다운을 사용해.
- 분석 시 긍정적인 것에는 👍🏻를, 부정적인 것에는 👎를 붙여서 한눈에 보이게 해 줘. 단 이것은 표 안에서의 평가에만 사용해.

채팅을 통한 기술과제 분석

이력서 분석과 마찬가지로 자신만의 기준으로 코드를 추가적으로 분석하거나, 추천 질문을 보여주고 그에 대한 답변을 확인할 수 있도록 하였습니다.

다음은 위 추천 질문을 생성할 때의 프롬프트입니다.

채용담당자가 제출된 코드에 대해서 LLM에게 다시 질문할만한 질문들을 추천해 줘.
다음에 형식에 어울리는 질문을 추천해 줘. 이 외에는 그 어떠한 설명도 포함하지 마.
"- ~에 대해서 알려줘"
5개의 질문만을 추천하고 질문 5개 중 1개는 반드시 다음과 같은 범주의 질문을 포함해.
- 이 코드에서 가장 부족한 부분
- 이 코드에서 가장 잘 작성된 부분
- 이 코드에서 가장 개선할 수 있는 부분
과거 이력 중 비슷한 질문이 있다면 그것은 제외해 줘.
주의:
- 채용담당자가 코드 작성자에게 묻는 게 아니라 이것을 더 자세히 분석해서 평가하기 위해 LLM에게 질문하는 거야.
- 다른 설명은 절대 하지 마. 오직 질문만 출력해.
- 개선 방안에 대한 것은 제안하지 마.
- 평가하는 듯한 내용은 제안하지 마.
- 과제를 분석하는 것에 집중된 것은 제안하지 마.
- 각 질문은 라인을 다르게 하고 각 질문에 대한 넘버링도 하지 마.

추가 질문의 경우에는 단순히 ‘질문을 추천해 줘’와 같이 LLM에 요청을 하게 되면 LLM에게 묻는 게 아니라 코드 작성자에게 직접 할만한 질문을 추천이 생성되었습니다. 그렇기 때문에 질문의 대상과 질문의 카테고리를 제한하였고, 분석 코드에 프롬프트 캐싱이 적용되어 있기 때문에 ‘xxx에 대해서 알려줘’, ‘xxx는 무엇일까?’, ‘xxx는 어떻게 구현되어 있어?‘와 같이 사실상 동일한 의미를 갖는 질문들이 같은 형태의 문장 형태를 갖도록 하였습니다. 그 결과 동일한 질문에 대해 즉각적인 응답이 캐시를 통해 제공되어 비용절감 효과와 더욱 빠른 분석이 가능하도록 하였습니다.

코드 실행

카페톤이 그래도 대회이기 때문에 LLM의 극한을 맛볼 수 있는 기능을 만들고 싶었습니다. 바로 그 결과가 이 자체 코드 실행인데요, 이것은 Manus라는 서비스로부터 영감을 받았습니다. 서버에 코드를 실행할 수 있는 최소한의 프로그램만 설치해 놓은 뒤 LLM에게 사용할 수 있는 리눅스 커맨드들, 과제 명세와 코드를 주고 해당 과제를 테스트하기 위한 시나리오를 작성하고 작성된 시나리오를 테스트하기 위한 명령어들을 생성하도록 하였습니다.

[
  {
    "explanation": "모든 컨테이너를 실행",
    "command": "docker-compose up -d",
    "should_evaluate": false,
    "expected_result": ""
  },
  {
    "explanation": "앱 컨테이너가 실행될 때까지 대기하면서 로그 확인",
    "command": "timeout 300 bash -c 'until curl -s http://localhost:8081/ > /dev/null; do echo \"Waiting for app to start... Last 10 lines of log:\"; docker-compose logs --tail=10 api; sleep 15; done; sleep 20'",
    "should_evaluate": false,
    "expected_result": ""
  },
  {
    "explanation": "메뉴 목록 조회 - 초기 데이터 확인",
    "command": "curl -s http://localhost:8081/api/v1/menu/items",
    "should_evaluate": true,
    "expected_result": "응답은 JSON 형식이어야 하며, content 배열에 11개의 메뉴 아이템이 있어야 함. 각 메뉴는 menuItemId, name, price를 포함해야 함. 아메리카노(2000원), 에스프레소(2000원), 카푸치노(2500원) 등이 포함되어야 함"
  },
  ...
  {
    "explanation": "Mock API 서버 상태를 down으로 변경",
    "command": "curl -s 'http://localhost:8091?mode=down'",
    "should_evaluate": true,
    "expected_result": "응답은 503 상태코드여야 함"
  },
  {
    "explanation": "Mock API 서버가 down 상태일 때 주문 생성",
    "command": "curl -s -X POST http://localhost:8081/api/v1/orders -H 'Content-Type: application/json' -d '{\"userId\":1,\"menuItems\":[{\"menuItemId\":1,\"quantity\":1}]}'",
    "should_evaluate": true,
    "expected_result": "응답은 JSON 형식이어야 하며, 주문은 정상적으로 생성되어야 함. content.totalAmountOfOrder가 2000이어야 함"
  },
  {
    "explanation": "모든 컨테이너 정리",
    "command": "docker-compose down -v",
    "should_evaluate": false,
    "expected_result": ""
  }
]

이렇게 생성된 계획에 따라 API 서버는 직접 해당 커맨드를 사용하여 응답 메시지를 받고 실행 결과를 평가합니다. 만약 이 명령이 실행 결과를 평가해야 하는 경우라면, 명령의 결과와 예상 기댓값을 다시 LLM에게 전달합니다. LLM은 이 정보를 바탕으로 명령이 정상적으로 실행되었는지 평가합니다.

계획에 따른 코드 실행 및 실행 결과 분석
계획에 따른 코드 실행 및 실행 결과 분석

물론, LLM을 제대로 활용하려면 더 고립된 환경 구축이나 필요한 프로그램을 임시 서버에 프로비저닝하는 등 더 많은 준비가 필요합니다. 하지만 이번 실험만으로도 LLM의 능력을 확인하기에 충분했습니다.

브라우저상에서의 VSCode

code-server를 이용한 웹브라우저에서의 VSCode
code-server를 이용한 웹브라우저에서의 VSCode

vscode를 브라우저에서 사용할 수 있다는 것을 혹시 알고 계셨나요? code-server 프로젝트는 이를 가능하게 하는데요, 사실 카페톤을 위한 AWS 교육 시 vscode를 웹사이트에서 직접 임베딩하여 사용할 수 있는 것을 보고 찾아서 이것 또한 추가해 보았습니다. 이렇게 하면 LLM에 의한 코드 분석과 동시에 직접 코드를 확인하는 것도 가능하답니다. 사실 LLM에 의해 만들어진 코드 분석 결과에 해당 코드에 대한 링크를 제공하는 것도 고려하여 이 기능을 구현하였지만 결국 한정된 시간 때문에 진행하지는 못했습니다. 🥲

어떻게 만들었을까?

아키텍처

아키텍처
아키텍처

AWS의 클래식한 구성인 ALB + EC2 + S3에 AWS Bedrock agent와 lambda를 더한 구조입니다. 작업이 가능한 기간이 굉장히 짧은 해커톤 특성상 빠른 프로토타이핑과 조금이라도 디버깅에 편한 이점을 취하기 위하여 API Gateway + lambda 혹은 ECS 등이 아닌 EC2에 쉘스크립트를 통해 배포하는 전략을 선택했습니다.

Streamlit을 통해 만들어진 FE
Streamlit을 통해 만들어진 FE

같은 이유로 FE는 별도의 복잡한 FE코드 없이도 LLM의 streaming 형식의 데이터를 빠르게 프로토타이핑할 수 있는 streamlit을 선택했습니다. 한참 만들고 나서야 깨달았지만, streamlit은 선형적인 UX를 구현하는 데에는 괜찮은 툴이지만 조금이라도 비선형적인 흐름, 예를 들어 페이지 구성상 아래 혹은 우측의 결과를 위쪽에 이미 출력된 컴포넌트에 반영을 해야 한다던지 하는 상황이나, 비동기 통신을 동시에 진행하는 등의 상황에서는 구현이 쉽지 않았습니다.

BE는 LLM을 사용하여 개발하기 쉬운 여러 프레임워크가 준비되어 있는 python을 주언어로 하고 fastapi로 API 서버를 구성하였습니다.

처음에는 langchain을 통해서 구현을 시도했지만 AWS Bedrock를 알게 될수록 생각이 달라졌습니다. 많은 부분에서 굳이 langchain을 사용할 것이 아니라, AWS SDK로 여러 파라미터를 직접 지정해 가며 Bedrock에 접근하는 것이 개발을 하기에 더 좋았습니다. (물론 이는 langchain의 기타 여러 기능을 이용하지 않아도 되는 케이스인 것도 한몫하였습니다.) 결과적으로 langchain은 문서 및 파일들을 읽는 loader관련만 사용하고, LLM은 Bedrock agent를 사용하여 호출하는 식으로 확정되었습니다.

처음에는 이 API 서버를 중심으로 RAG를 위한 벡터스토어와 데이터 적재용 lambda 등으로 구성한 더 복잡한 구조였습니다. LLM을 활용한 서비스를 직접 개발을 해본 적이 없어 AWS SA분들로부터 많은 도움을 받았고, 결국 RAG는 사용하지 않기로 하면서 구조가 단순해졌습니다.

RAG란?

RAG란 Retrieval-Augmented Generation으로 모델이 모델 내부적으로 가진 지식 혹은 자료만으로는 부족한 부분에 대해서 외부에서 자료를 참조하는 기술입니다. 또한 일반적으로 LLM이 가지고 있는 한계인 입력 토큰 수의 한계와 입력의 양이 많아질수록 비용도 비례해서 늘어나기 때문에 LLM에게 주입하는 입력값인 콘텍스트를 효율적으로 조절하기 위해 사용합니다.

AWS Bedrock + AWS Knowedgebase을 통한 RAG
AWS Bedrock + AWS Knowedgebase을 통한 RAG

매우 간단히 설명하면 다음과 같습니다.

  • LLM이 RAG를 통해 자료를 참조하기 위해서는 우리가 일반적으로 데이터를 데이터베이스에 저장하고 그것을 꺼내어 사용하듯이, 거의 동일한 플로우로 데이터를 미리 저장해야 합니다.
  • 이때의 데이터베이스가 바로 여러 자료를 수치화된 표현인 벡터로 저장하는 벡터스토어이며, 자료를 수치화하여 저장하는 것을 임베딩이라고 합니다.
  • 임베딩은 Amazon Titian Text Embedding 같은 임베딩 모델로 진행됩니다. 데이터를 저장할 때와 마찬가지로, 벡터스토어에서 유사한 데이터를 찾을 때에도 먼저 이 임베딩모델로 수치화합니다. 그리고, 이 수치화된 벡터 값과 차이가 작은 것을 벡터스토어에서 찾게 됩니다.

왜 RAG를 사용하지 않았을까?

앞서 설명드렸다시피 RAG의 가장 큰 목적은 LLM모델의 입력 토큰수의 한계 극복입니다. 반대로 말하면 RAG를 사용하게 되면 LLM에 의해 선택되지 못한 자료는 LLM응답 생성 시의 콘텍스트로 참조하지 못하는 상황이 발생하는, 즉 콘텍스트를 누락할 여지가 있습니다.

코드와 같이 디테일하게 분석 및 응답이 되어야 하는 경우에는 이러한 콘텍스트 누락은 응답의 신뢰도를 떨어트리게 됩니다. 또한 저희가 사용한 claude 3.5 sonnet 모델의 경우 최대 입력 토큰의 수는 20만 개인데요, 이는 기술과제와 같은 작은 사이즈의 프로젝트 코드 전체를 입력값으로 넣기에는 충분한 양입니다.

<SystemPrompt>
  ...
</SystemPrompt>
<Context>
  <Code path="/src/main/kotlin/com/kakaopay/PayApplication.kt">
    ...
  </Code>
  <Code path="/src/main/kotlin/com/kakaopay/controllers/OrderController.kt">
    ...
  </Code>
  ...
</Context>

그 결과 RAG를 사용하지 않고, 코드들을 전부 마크업 형태로 구조화한 텍스트 콘텍스트로 제공하였습니다. 물론 시간이 조금 더 있었다면 프롬프트의 내용에 해당하는 파일들만을 선택하게 하는 전처리 과정을 넣을 수도 있었습니다. 하지만 시간 관계상 퀄리티에 포커스를 조금 더 두고 개발을 진행하였습니다.

무엇을 배웠나?

출력 토큰 수 제한의 한계

최근 출시되는 여러 LLM모델의 경우 입력 토큰의 한계는 빠르게 늘어나고 있으며, 이를 넘기는 상황에서도 RAG와 같은 기술을 활용하여 어느 정도는 극복이 되고 있지만, 출력 토큰의 수에 대해서는 아직은 개선이 필요한 부분이 많이 남아있다는 것을 확인하는 계기가 되었습니다. 물론 OpenAI사의 최신 모델의 경우 기존의 모델들이 제공하는 4K를 넘어 더욱 큰 출력 토큰의 크기를 제공하고 있습니다만, 특정 모델을 사용해야 하는 등 아직은 범용적으로 접근하기에는 제약이 있는 느낌입니다.

이러한 출력 토큰의 제한은 요약이 아닌 원본을 거의 그대로 보존하여 출력하는 전처리에는 매우 취약한 구조를 갖게 됩니다. 그래서 전체 내용을 재처리한 후 재출력하는 방법보다는 다음과 같은 방법들로 전처리를 진행하게 됩니다.

  • 콘텍스트를 분할하여 처리 한 뒤 병합
  • 출력을 부분적으로 한 뒤 병합
  • 수정이 필요한 부분만을 출력하게 한 뒤 병합 어느 방법을 선택하더라도 특정상황에서는 오동작할 수 있고, 합쳐진 결과가 매끄럽지 않다거나 결국은 다시 그 안에서도 토큰의 한계에 부딪히기도 합니다.

큰 출력이 필요한 경우, 아직은 각 모델의 성능과 이러한 한계들을 고려하여 서비스를 설계하여야 합니다.

프롬프트 캐싱의 중요성

입력 토큰이 많을수록, 복잡한 명령을 내릴수록 LLM의 연산생성 비용과 시간은 증가하게 됩니다. 물론 이러한 상황을 극복하기 위한 방법 중 하나가 RAG입니다. 하지만 LLM 특유의 창의성이 덜 필요하면서, RAG를 사용하지 않고 동일한 내용을 LLM에게 물었을 때 동일한 응답이 리턴되어도 괜찮은 상황에서는 프롬프트 캐싱을 고려할 수 있습니다.

코드 분석의 경우, 최초 분석이 굉장히 많은 기준들에 의해 응답이 생성되는데요, 사실 이는 여러 사람이 요청하더라도 같은 결과를 내보내도 상관이 없는 것들입니다. 그렇기 때문에 최초 생성 시에만 조금은 많은 시간이 소요되고, 이후는 프롬프트 내용을 해싱한 값을 키값으로 하는 프롬프트 캐시를 사용하여 즉각적인 응답을 출력하도록 API를 개발하였습니다.

이번 해커톤에서는 구현하지 못했지만, 채팅을 통한 질문의 경우에도 전처리를 통해서 DB화 혹은 LLM에 의한 정규화를 통해 질문을 최대한 비슷한 형태로 재구축하여 캐시의 히트율을 높이는 것도 가능할 것입니다.

출력 결과물에 대한 제어의 어려움

LLM의 출력은 크게 두 가지의 어려움이 있습니다.

  1. 출력형태를 제한하는 것
    예를 들어, “JSON으로만 출력해”라고 시스템 프롬프트를 넣었더라도, 이 어디로 튈지 알 수 없는 LLM은 다음과 같은 형태의 응답을 내보내고는 합니다.
```json
{"json결과물..." : [...]}
```

정상적으로 출력된 것 아닌가?라고 볼 수도 있겠지만 LLM모델들이 대체로 markdown형식의 출력을 하는 성향이 강합니다. 이에 대해서 제한을 걸어도 모델의 성능에 따라 위와 같이 엄격히 형식을 지키지 않거나 아예 “네 알겠습니다”와 같은 불필요한 응답이 포함되는 등 예측이 힘든 결과물을 얻을 때가 많았습니다. 물론 이것들을 처리하기 위한 툴들이 존재하지만, 어쨌거나 생각보다도 많은 프롬프트 엔지니어링과 후처리가 필요했습니다.

  1. 고퀄리티의 출력내용을 유도하는 것
    많은 사람들이 LLM을 마법과 같은 기술로 보는 경향이 강합니다만, 이를 이용해 직접 서비스를 개발을 해보면, “LLM을 가지고 업무를 하는 것은 일처리가 빠른 주니어와 일을 하는 것과 같다”라는 말을 뼈저리게 느끼게 됩니다. 예를 들어 “이력서를 분석해”라는 질문에는 유의미한 결과를 얻기에는 힘들 것입니다. 이러한 단순한 질문보다는 “이력서를 채용공고의 내용을 고려하여 각 항목별로 적합한지 확인해”라는 것이 채용도우미 서비스 관점에서는 더 나은 결과를 얻을 수 있게 됩니다.
    결국은 더 나은 응답을 생성하기 위해서는 서비스를 이용하는 사람/개발하는 사람이 양질의 질문을 프롬프트에 작성하여야 합니다. 이를 위해서는 사람의 지식을 직접 옮기는 방법과 LLM에게 상세한 계획생성을 지시하는 방법 등이 존재합니다. 물론 LLM에게 맡긴 경우에는 서비스 개발자가 의도한 것과는 다른 계획들이 튀어나올 수도 있기 때문에 또다시 이것을 제한하는 내용의 프롬프트가 필요할 수도 있습니다.

마치며

이번 해커톤에서 다양한 기술을 구현해 보며 인사이트를 많이 얻을 수 있었습니다. 그러나 이 과정에서 기술 자체보다 더 깊이 고민하게 된 건 바로 AI 시대를 우리가 어떻게 살아가야 할까라는 질문이었습니다.

분명 LLM은 앞으로도 더 많은 일을 할 수 있는 잠재력을 가지고 있다고 생각합니다. 이번 해커톤에 참여하며 하나의 완전한 AI서비스를 만들며 때로는 감탄하고, 놀라움도 느꼈습니다. 하지만 동시에 그 결과를 해석하고 최종적으로 판단하는 주체는 결국 인간이어야 한다는 점을 다시 한번 깨닫게 되었습니다.

앞으로 시간이 흘러 AI와 같은 기계에게 사고의 결과에 대한 판단까지 맡기는 세상이 올 수도 있겠지만, 저는 그런 기술이 세상에 나오더라도 사람이 그 ‘권한’을 내려놓지는 않을 것이라고 생각됩니다. 물론 이 권한의 범위는 지금보다는 많이 줄어들 수 있고, 단순한 판단일수록 더욱 쉽게 사라질 수도 있습니다.

혹시 이 글을 읽으시면서 갑자기 왜 이렇게 거창한 이야기를 하나 생각하실 수도 있겠지만, 저는 결국 가장 중요한 부분에서의 판단은 앞으로도 사람이 맡게 될 것이라 의심치 않습니다. 이를 위해서는 “AI가 나를 대체할 거야”라는 근심을 안고 살기보다는, 생각을 조금 다르게 해 보는 건 어떨까요?

저는 더욱더 전문성을 갖추어 AI를 사용해 효율적으로 작업하면서, 그것에 대한 책임을 질 수 있는 개발자가 되어야겠다고 다짐했습니다. 이점이 이번 카페톤에 참가하면서 가장 크게 배운점입니다.

호기심과 상품에 대한 욕심으로 가벼운 마음에 시작했지만, 팀원 모두가 점점 더 열정을 쏟으며 참여했습니다. 마치 대학교 동아리 활동시절과 같이 거의 날을 세워가며 임했던 이번 해커톤은 비록 3위안에 들지는 못해 좋은 상품은 얻지 못했지만, AI시대를 맞이한 개발자로서의 방향이라는 훨씬 더 값진 배움과 깨달음을 얻게 된 정말 소중한 기회였습니다.

참조

ian.wizard
ian.wizard

단순히 결과를 내기보단, 문제의 본질을 이해하고 기술을 끊임없이 연구하여 최적의 방법을 찾는 진정한 프로그래머가 되기 위해 노력하고 있습니다.

jan.nabi
jan.nabi

새로운 것은 받아들이고 경험은 축적하며 발전하고자 하는 카카오페이의 인프라 기획자 jan입니다.

billy.j
billy.j

인프라플랫폼실 클라우드 & OSS 엔지니어 빌리입니다.

eygon.park
eygon.park

아는 게 힘이고 무엇이든 뛰어들어 경험해 보자! 새로움과 다채로움을 추구하며 무엇이든 경험하여 지식의 탑을 쌓고 싶은 시스템엔지니어입니다.

pong.du
pong.du

카카오페이에서 사내시스템 업무를 담당하고 있는 pong입니다.

태그