개발자 취업 지원 서비스 만들기

 

LLM, RAG 핸즈온을 위한 취업도우미 서비스 제작기

재취업을 위해 내게 필요한 정보를 수집하고 분석해주는 웹을 만들어보자

  구직 계획을 세우다 보면 반복적으로 하게 되는 일들이 있다. 소위 네카라쿠…로 묶이는 기업들의 채용 공고를 훑으며, 어떤 직무가 활발히 채용 중인지 파악하고, 자격 요건과 우대 사항을 살펴보며 시장 분위기를 가늠한다. 그러면서 현재 내 이력서와 어떤 부분이 부족한지 비교해보고, 관심 있는 공고가 사라지진 않았는지 주기적으로 확인한다. 원티드 같은 플랫폼이 있긴 하지만, 내게 필요 없는 정보가 지나치게 많이 노출되기도 하고, 경험상 외부 플랫폼에 공고를 올리지 않는 기업도 있어서 결국 각 회사의 공식 홈페이지를 일일이 들어가보게 된다. 면접 준비에 비해 스트레스는 덜하지만, 손이 많이 가고 피로도가 쌓인다. 마침 이전 글에서 LLM을 활용한 버티컬 서비스 사례들을 공부해두었던 터라, 채용 공고라는 한정된 데이터의 스크래핑과 구조화, 이력서 기반 추천까지 LLM을 활용하여 웹 서비스 형태로 만들어보았다. 이름은 찹찹. 취업을 빨리 발음하면 , 그래서 찹찹이다. 이 글에서는 프로젝트의 핵심 기능과 설계 과정을 정리해보려 한다.

찹찹의 기능

https://chapchap.im

찹찹의 주요 기능은 아래 GIF에 모두 담겨 있다. 구직자가 반복해서 마주치는 귀찮은 작업을 몇 가지 간단한 흐름으로 대신해주어, 본질적인 판단과 준비에 집중할 수 있도록 돕는 것이 찹찹의 핵심 목표다.

전체

주요 채용공고 수집과 일관된 톤의 데이터 제공

찹찹은 미리 지정한 기업들의 채용 페이지에서 주요 공고를 수집한다. 공고의 포맷과 용어는 기업마다 제각각이기 때문에, 수집된 데이터는 통일된 형태로 가공된다. 담당 업무, 자격 요건, 우대 사항 등은 일관된 문체와 구성으로 변환되어 제공된다. 이렇게 정보를 정리하여 서로 다른 기업의 공고라도 비교와 판단이 쉬운 형태로 보여주도록 한다.

쿠팡공고

이력서 기반 채용공고 추천

구직자의 이력서 혹은 자기소개 텍스트를 제공하면, 찹찹은 이를 요약해 구직자의 커리어 흐름과 강점을 화면에 보여준다. 그다음, 수집된 채용공고들 중에서 내용이 구직자에게 적합한 공고들을 추천해준다. 추천된 공고마다, 왜 이 공고가 나에게 맞는지에 대한 간단한 설명도 함께 제공된다. 구직자가 이 설명을 통해 바탕으로 추천 결과를 빠르게 이해하고, 지원 여부를 판단할 수 있도록 돕는다.

쿠팡공고

AI 커버레터 생성

추천된 공고 중 하나를 선택하면, 찹찹은 그에 맞는 커버레터 초안을 생성한다. 이전 단계에서 분석된 이력서 요약과 선택된 공고 정보가 함께 활용되며, 지원자의 강점을 중심으로 구성된 지원 문장을 만들어낸다. 결과는 실시간으로 출력되며, 사용자는 이를 복사하거나 필요에 따라 직접 수정해 활용할 수 있다. 앞서 수집된 정보를 기반으로 작성하여, 구직자가 빈 화면에서 시작할 필요가 없도록 돕니다.

쿠팡공고

UX 고려사항

찹찹을 개발할 때 가장 중요하게 생각한 건, 사용자가 정보를 탐색하면서도 방해받지 않도록 하는 것이었다.

입력을 최소화하면서 맥락은 유지하기. 찹찹은 단순한 이력서 업로드만으로 전체 흐름이 시작된다. 별도의 회원가입이나 추가 질문 없이, 한 번의 입력으로 요약 → 추천 → 커버레터 생성까지 이어지도록 구성했다. 구직자의 부담을 줄이되, 필요한 정보는 충분히 전달되게 설계하는 것이 핵심이었다.

왜 이걸 추천했는지 설명해주기. 추천 시스템은 결과만 보여줄 때보다, 추천 이유를 함께 제공할 때 사용자 신뢰도가 높아진다. 찹찹은 추천된 공고마다 간단한 설명을 붙여, 사용자가 결과를 빠르게 이해하고 판단할 수 있도록 구성했다. AI가 구직자를 이해하고 있다는 느낌을 주기 위한 장치였다.

결과는 빠르게 보여주기. 로딩은 가능한 짧게, 선택지는 최소한으로 정리했다. 찹찹은 가능한 한 한두 번의 클릭만으로 다음 단계로 넘어가도록 설계되었고, 텍스트 결과는 스트리밍 방식으로 출력되도록 구현했다. 이런 설계를 통해, 사용 흐름이 끊기지 않는 것을 의도했다.

찹찹 차근차근 만들기

찹찹구조

위 그림은 diagramgpt를 통해 만든 찹찹의 서버 구조이다.

찹찹의 기획은 활용 가능한 데이터가 무엇인지 파악하는 것에서부터 시작했다. 먼저, 채용공고 데이터를 정당하게 수집하고 사용할 수 있다는 점을 확인했고, 이를 바탕으로 개인에게 적합한 채용공고와 구직 가이드를 제공하는 것을 핵심 기능으로 삼았다. 찹찹은 채용공고와 이력서, 두 종류의 비정형 데이터를 기반으로 동작한다. 이 두 데이터를 어떻게 수집하고, 어떻게 정리하고, 어떻게 연결해서 사용자에게 보여줄지를 중심으로 전체 흐름을 설계했다. 아래는 그 과정에서 거친 단계별 개발 내용이다.

채용공고 수집과 구조화

가장 먼저 한 일은 채용공고 데이터를 확보하는 작업이었다. 처음에는 링크드인을 활용하는 것이 가장 넓은 범위를 커버할 수 있을 것 같아 관련 라이브러리들을 살펴봤다. 하지만 링크드인은 크롤링을 공식적으로 금지하고 있었고, 관련한 차단 공지가 올해 게시된 상태였다. 그래서 방향을 바꿔, 일정한 기준을 가진 일부 기업들의 공식 채용 페이지를 스크래핑하는 방식으로 접근했다. 그리고 LLM을 활용하면 기존보다 훨씬 단순한 방식으로 구조화가 가능할 것이라는 아이디어를 실험했다. 기존의 방식처럼 HTML 구조를 세세히 분석해 각 구간의 텍스트를 발췌하지 않아도 됐다. 채용공고 페이지에서 전체 내용을 담고 있는 바깥쪽 HTML 섹션 하나만 추출해 Gemini에 전달하면, 내부적으로 팀 소개, 담당 업무, 자격 요건, 채용 절차 등을 파악해 구조화된 결과를 만들어냈다. 또한 공고가 영어로 작성된 경우에는 내용을 자연스러운 한국어로 번역하고, 문장 표현도 “회사에서 말하는 방식”이 아니라 찹찹이 정리해주는 정보 전달 형태로 바꿨다. 이 작업에는 아래와 같은 프롬프트를 활용했다:

당신은 친절한 헤드헌터입니다.
다음은 '{company_name}' 회사의 개발자 채용 공고 상세 내용 원본 텍스트입니다:

===
{job_content_text}
===

작업 목표:
주어진 원본 텍스트에서 구조화된 정보를 추출하고,
아래 설명된 '토스(Toss) 글쓰기 5가지 원칙' 에 따라 각 항목의 내용을 재작성하여 지정된 JSON 형식으로 반환합니다.
최종 결과물은 지원자가 쉽고 명확하게 이해하며, 친근하고 존중받는 느낌을 받도록 작성되어야 합니다.

...

작업 단계:

1. 원본 텍스트에서 다음 항목들에 해당하는 핵심 정보를 정확하게 추출합니다:

"팀 소개" (key: team_info)
"담당업무" (key: responsibilities)
"지원자격" (key: qualifications)
"우대사항" (key: preferred_qualifications)
"채용프로세스" (key: hiring_process)
"추가사항" (key: additional_info)

2. 추출된 각 항목의 내용을 위에 요약된 '토스 글쓰기 5가지 원칙'을 종합적으로 고려하여 새롭게 재작성합니다.

- 원본 텍스트의 문장을 그대로 가져오지 않고, 원칙에 맞춰 의미는 유지하되 표현 방식을 완전히 바꾼다는 생각으로 다시 작성해주세요.
- 당신은 회사가 아닙니다. '저희', '우리 회사' 같은 회사를 자칭하는 말은 사용하지 않고, 회사의 이름을 명시해주세요.
- 채용공고가 사용하는 화살표, >, 괄호 등의 표시는 사용하지 않습니다.
- 공고 전체가 영어로 쓰여졌을 경우, 한글로 재작성합니다.

...

채용공고와 이력서의 비교

찹찹의 핵심 기능은 이력서를 기반으로 채용공고를 추천하는 것이었다. 그 중심에서 가장 고민했던 질문은 채용공고와 이력서를 어떻게 비교 가능한 형태로 만들 것인가였다. 조사해보니 Resume–JD 매칭 문제는 이미 기업에서 활발히 연구되고 있었고, 대기업 채용 시스템에서는 이를 자동화해 인력 선별 비용을 줄이려는 방향으로 진행되고 있었다. 관련 논문과 공개된 모델도 다수 있었지만, 직접 LLM을 파인튜닝하거나 모델을 호스팅할 수 있는 리소스가 없었기 때문에, 퍼블릭 LLM API나 임베딩 API만으로 문제를 해결할 수 있을지를 중심으로 접근했다. 처음엔 ConFit v2에서 제안한 방식처럼 채용공고를 기반으로 더미 이력서를 생성한 뒤, 이력서 간의 유사도를 비교하는 방식을 고려했다. 형식이 같아지면(BM25나 퍼블릭 임베딩 API 기반으로) 비교가 쉬워질 것이라고 판단했다. 하지만 곧, 이력서 데이터의 크기 때문에 퍼블릭 임베딩 API로는 직무별 세부 역할이나 도메인 차이 같은 문맥적인 차이를 충분히 포착하기 어렵다는 점이 우려되었다. 그래서 방향을 바꿔, 이력서에서 핵심 역량만을 요약한 간결한 문장 리스트를 만드는 방식으로 접근했다. 이 역량문장 추출에는 다음과 같은 프롬프트를 이용했다.

아래 이력서를 읽고 지원자의 경력의 순서를 명시하고, 지원자가 가진 능력을 한 줄 씩 적어주세요.
경력 기준 가장 강한 능력으로 보이는 것을 먼저 적어주세요.

출력 형식:
- 구분된 학력/경력
- 능력 1
- 능력 2
- 능력 3
...

출력 예시:
- 카이스트 학부 -> 크래프톤 (풀스택 엔지니어) -> 토스 (데이터 엔지니어)
- Kotlin 및 Spring Framework 활용 개발 경험 및 AWS 환경 경험
- React, Vue 등 최신 프론트엔드 프레임워크 및 유형 컴포넌트 UI 설계 경험
...

이렇게 추출된 역량문장은, 형태와 내용 면에서 채용공고의 지원자격우대사항과 동일한 형식을 가지고 있다는 것을 파악했다. 이에 따라 공고별 지원 요건 문장을 임베딩하여, 이력서 요약과 동일한 포맷으로 비교하는 방식으로 구조를 변경했다. 이렇게 되면 공고를 기반으로 더미 이력서를 만드는 중간 단계는 필요하지 않았다. 실제 이력서의 핵심 내용을 추출해서 비교 가능한 상태로 만든다는 점에서 더 직관적인 구조가 되었다. 이 기능은 “이력서 요약”이라는 이름으로 사용자에게도 그대로 제공되었다. 처음 상정한 회사들에서 수집하는 공고가 수백 개 단위로 존재할 것을 고려해, 추천은 2단계로 나누어 구성했다.

추천 흐름

1차 필터링

  • 이력서에서 추출된 역량문장과
  • 각 공고의 지원자격/우대사항 문장을 임베딩한 후
  • 유사도 기반으로 소수의 공고를 선별

2차 랭킹

  • 선별된 공고들을 프롬프트에 포함시켜
  • 가장 적합한 10개를 선택하고,
  • 각 공고에 대해 적합한 이유를 요약

이렇게 구성한 흐름은 정제된 데이터 -> 임베딩 기반 필터링 -> LLM 기반 해석으로 이어지며, 복잡한 모델 없이 설명과 납득이 가능한 추천 구조가 되었다. 다음은 랭킹 단계에서 사용된 프롬프트이다.

다음은 당신이 가진 채용공고의 리스트입니다.
당신은 이 채용공고 중 지원자가 적합한 {settings.RERANK_COUNT}개의 공고를
선별하고 적합한 순으로 정렬하고 설명을 첨부해야 합니다.
적합한 이유는 100자 이내로 명시해야 합니다.

===이력서 (정제되지 않은 텍스트)
{resume_text}


===채용공고 리스트
{job_list}


===출력 형식
"공고번호" (key: job_idx)
"공고이름" (key: job_title)
"적합한 이유" (key: reason)

UI 설계

개발자 취업을 준비하는 사용자를 떠올렸을 때, 이력서 기반 채용공고 추천은 가끔 활용하는 기능이지만, 수집된 채용공고를 확인하고 업데이트 여부를 파악하는 일은 더 자주 반복되는 작업이다. 이를 고려해 홈 화면에는 최신순으로 정렬된 채용공고 리스트를 배치하고, 각 공고에는 직무, 조직, 기술 키워드를 태그 형태로 표시하여 공고의 성격과 업데이트 상황을 한눈에 파악할 수 있도록 했다. 태그는 별도의 분류 모델 없이 스크래핑 시 키워드 매칭을 통해 자동으로 생성했다. 화면은 1차적으로는 투박하지만, 핵심 정보만 빠르게 보여주는 데 집중해 UI를 단순하게 유지했다. 또한 모든 기능은 회원가입 없이 바로 이용할 수 있도록 구성해, 접근 장벽을 낮추는 데 집중했다.

홈화면

이력서 기반 추천 흐름은 별도의 화면으로 구성했다. 사용자는 PDF 파일을 첨부하거나 자기소개 텍스트를 입력해 공고 추천을 받을 수 있다. 모바일 사용자나 파일 첨부를 꺼리는 사용자를 고려하여 이런 방식으로 구성했다. PDF 파일에서는 PyMuPDFLoader를 사용해 데이터를 추출했다. 더 정밀한 구조화가 필요한 경우 UnstructuredPDFLoader를 사용하는 선택지도 있었지만, OCR 모델이 자동 포함되는 등 처리 과정이 무거워 속도를 우선시해 사용하지 않았다.

결과 출력 방식에서는, 이력서 요약이나 커버레터 생성처럼 문장 단위의 결과물이 출력되는 과정에서 스트리밍 출력 여부가 체감되는 레이턴시에 큰 영향을 준다는 점을 확인했다. 그래서 텍스트 결과는 스트리밍 형태로 빠르게 전달되도록 구성했다. 또한 찹찹은 이력서 요약 -> 채용공고 추천 -> 커버레터 생성까지 모든 단계가 분리되어 있다. 맥락을 계속 유지하기 위해서는 세션 단위로 모델과 상호작용 중인 데이터를 계속 유지할 필요가 있었다. 이 구조는 LangGraph 라이브러리를 활용해 구현했다. 단계별 흐름을 그래프로 정의하고, 각 단계의 입력과 출력을 자연스럽게 이어갈 수 있어 흐름 유지를 쉽게 처리할 수 있었다.

추가: 커버레터 생성

추천된 채용공고 중 하나를 선택하면, 찹찹은 해당 공고와 사용자 이력서 요약을 바탕으로 커버레터 초안을 생성한다. 생성된 커버레터는 지원자의 강점이 공고의 요구사항과 어떻게 연결되는지를 설명하는 흐름으로 구성되어 있다. 다음은 그를 위해 이용한 프롬프트이다.

당신은 IT회사의 지원자입니다.
당신은 아래 주어진 이력서를 바탕으로 주어진 채용공고에 지원하려 합니다.
회사의 문화를 고려하여 알맞은 커버레터를 작성해 주세요.

당신의 이력서는 다음과 같습니다.
===이력서 (정제되지 않은 텍스트)
{resume_text}


===채용공고
- 공고 이름: {job_dict["job_title"]}
- 회사 이름: {job_dict["company_name"]}
- 팀 소개: {job_dict["team_info"]}
- 담당업무:
    {job_responsibilities}
- 지원자격:
    {job_qualifications}
- 우대사항:
    {job_preferred_qualifications}


===주의사항
- 문어체로 작성할 것
- 문단 1. 본인 소개, 지원동기, 지원직무 언급
- 문단 2. 업무경력, 성과, 본인의 핵심역량 어필
- 문단 3. 입사 포부
- 문단 4. 인터뷰 기회 요청으로 마무리

기술스택

찹찹은 다음 기술스택을 기반으로 만들어졌다.

  • 프론트엔드: React, vite, shadcn, zustand, zod
  • 백엔드: FastAPI, Supabase, pgvector, LangGraph
  • 스크래핑: BeautifulSoup, google.genai
  • 배포: Vercel, Render, Supabase

코드는 아래 링크에서 볼 수 있다.

https://github.com/rokrokss/chapchap

추후 계획

이외에도 여러 기능을 상상해보았지만, 프로젝트가 커지더라도 빠르게 필요한 정보를 전달하는 흐름은 계속 유지하고 싶다. 바라는 최종적인 모습은 연봉 정보나 면접 후기처럼 구직자가 궁금해할 수 있는 정보까지 함께 제공되는 서비스다. 하지만 이를 구현하기 위해 어떤 방식으로 데이터를 확보하는 단계까지 갈 수 있을지는 아직 고민 중이다. 현재 단계에서 할 수 있는 일은, 기존 데이터를 기반으로 어떤 기능이 실질적으로 유용할지를 더 살펴보고 차근차근 개선해볼 계획이다.