각 채용 공고의 '진짜' 여부를 점수화한 구인구직 사이트를 만들다 (A–F)
요약
유령 채용 공고를 식별하기 위해 공고의 실효성을 A-F 등급으로 점수화하는 서비스 Remoty.work의 개발 과정을 다룹니다. 스크래핑, 데이터 중복 제거, DeepSeek을 활용한 텍스트 분석 및 인프라 구축 경험을 공유합니다.
핵심 포인트
- 채용 공고의 재게시 횟수와 게시 기간을 통한 유령 공고 탐지
- DeepSeek을 활용하여 비용 효율적으로 직무 설명 및 대화 내용 분석
- Cloudflare Edge와 VPS 간의 데이터베이스 연결 문제 해결 사례
- 커뮤니티의 부정적 피드백을 점수화 엔진의 주요 신호로 활용
대부분의 원격 근무 채용 공고는 유령과 같습니다. 이미 마감되었거나, 한 번도 열리지 않았거나, 혹은 단순히 이력서를 수집하기 위해 게시된 것들입니다. 개발자로서 이것이 저를 충분히 짜증 나게 했고, X에서 불평하는 대신 코드로 해결하기로 했습니다.
그래서 저는 **Remoty.work**을 만들었는데, 이 사이트는 각 공고가 실제로 '진짜'일 가능성을 A부터 F까지 등급 매깁니다. 실제 탐지 과정이 내부적으로 어떻게 작동하는지 알려드리겠습니다.
엔지니어링 관점에서의 문제점
구인구직 사이트는 끝없는 스크래핑 루프입니다. 보드 A가 보드 B를 스크랩하고, 그 보드 B는 보드 C를 스크랩했습니다. 똑같은 유효하지 않은 공고가 수십 개의 사이트에 퍼져나가지만, 어느 곳도 아무것도 검증하지 않습니다. '이것이 진짜인가'에 대한 신호가 없기 때문에 노이즈가 쌓여 모든 보드가 동일해 보입니다.
저는 그 루프에서 열세 번째 보드를 만들고 싶지 않았습니다. 저는 모든 공고가 가져야 할 하나의 질문, 즉 **'누군가 실제로 내 지원서를 읽어줄 것인가?'**에 답하는 상위 계층을 원했습니다.
아키텍처
모든 것이 **단일 VPS(Virtual Private Server)**에서 실행됩니다. Postgres 데이터베이스, 스크래퍼, 그리고 일정에 따라 감독되는 점수화 작업들입니다. 의도적으로 지루합니다. 높은 수준으로 설명하자면:
- 데이터 수집 (Ingestion): 예약된 스크래퍼들이 소스 보드와 회사 ATS(Applicant Tracking System) 피드에서 데이터를 가져와 Postgres에 저장합니다. 각 원본 공고는 지문(title + company + normalized URL)을 통해 중복 제거되며, 따라서 다섯 개의 보드에 걸쳐 다시 게시된 동일한 채용 건은
repost_count가 포함된 하나의 행으로 통합됩니다. - 점수화 엔진 (Scoring engine): 모든 공고는 몇 가지 신호(signal)를 기반으로 **유령 위험 점수(ghost-risk score)**를 받으며, 이를 사람이 읽기 쉬운 A–F 등급으로 매핑하여 블랙박스 숫자가 아닌 형태로 만듭니다.
- '불만 토로' 신호 (The "rant" signal): 저는 r/recruitinghell이나 채용 스레드 같은 곳에서 사람들이 회사에 대해 말하는 내용을 교차 참조합니다. 바로 그곳에서 회사의 채용에 대한 진실이 새어 나오며, 이는 강력한 예측 변수임이 밝혀졌습니다.
- 에이전트 (Agents): 저는 지저분한 텍스트의 직무 설명과 회사 대화 내용을 분류하고 요약하기 위해 DeepSeek을 사용합니다. GPT-4급 모델로 매일 수천 개의 공고를 처리하는 것은 제가 단 한 명의 사용자도 확보하기 전에 서비스 경제성(unit economics)을 망가뜨렸을 것입니다.
제가 주말 동안 예상치 못하게 시간을 쓴 인프라 세부 사항이 있습니다: 프론트엔드는 Cloudflare의 엣지(edge)에 있지만, 엣지는 Postgres로 원본 TCP 연결을 열 수 없습니다 (VPS에서 localhost로 방화벽 처리되어 있기 때문입니다). 따라서 동적 페이지는 데이터베이스에 직접 연결하는 대신 박스 위의 작고 인증된 읽기 전용 HTTP 브릿지와 통신합니다. 사후적으로 보면 당연하지만, 새벽 1시에는 그렇지 않았습니다.
점수화의 형태는 다음과 같습니다 (실제 것에서 단순화됨):
type Signals = {
repostCount: number; // N개의 보드에 걸쳐 같은 공고가 보이는 경우
ageDays: number; // '열려' 있는 기간
...
예상치 못했던 신호
가장 좋은 예측 변수는 나이나 재게시 횟수가 아니었습니다. 일부 공고는 채용 건 자체가 아니라 광고라는 것을 깨달은 것이었습니다.
몇몇 잘 알려진 '검증된 인재 네트워크(vetted talent network)' 플랫폼들은 실제 채용 공고처럼 보이는 수백 개의 역할을 게시합니다. 제가 테스트하기 위해 지원했을 때, 받은 거절 이메일에는 속사정을 말하고 있었습니다. 즉, 각 공고마다 지원자가 과잉이라는 것입니다. 그 목록은 직무가 아닙니다. 그것은 직무의 옷을 입고 자신들의 인재 풀(bench)을 늘리기 위한 리드 생성 광고입니다.
따라서 그런 것들은 F를 받지 않습니다. 대신 '광고 — 채용 아님(Ad — Not Hiring)'이라는 자체 라벨을 받습니다. 설명 어디에도 그것을 알려주는 것이 없습니다. 오직 지원해 봐야만 알 수 있는데, 이것이 바로 이 제품이 사용자에게서 구해주려고 하는 정확한 부분입니다.
인간의 개입 (The human in the loop)
AI가 최종 심사관은 아닙니다. 시스템이 비정상적인 것을 감지하면, 저는 실제로 그 직무에 지원하여 무슨 일이 일어나는지 지켜봅니다. 맞았나요? 좋습니다. 틀렸나요? 제가 라벨을 수정하고 이유를 제공하여 다음 검토가 더 날카로워지게 합니다.
완전히 자동화된 보드보다는 느립니다. 그것이 요점입니다. 이 제품 전체의 목적은 '우리가 확인했으니, 당신은 시간을 낭비할 필요가 없다'는 것입니다.
제가 잘못했던 점들
- 뒤에 슬래시(/) 하나가 조용히 제 트래픽의 절반을 앗아갔습니다. 제 정적 페이지(static pages)는 누락된 뒤쪽 슬래시를 자동으로 리디렉션하지만, 서버 렌더링된 직무 페이지(server-rendered job pages)는 그렇지 않습니다. 그래서 직무에 대한 모든 클릭이 홈페이지로 흘러들어 갔습니다. 분석을 통해 특정 /jobs/12345 URL이 #2 페이지로 표시되는 것을 보고서야 알게 되었습니다. 이 URL은 홈페이지의 제목을 보여주고 있었습니다. 사용자가 직무를 클릭했는데 메인 페이지가 나온 것입니다. 며칠 동안이나요.
- 더블 인코딩 지옥(Double-encoding hell). 스크래퍼들이 원시 HTML 엔티티(&)를 저장했고, 그 후 제 템플릿 엔진이 그것을 다시 이스케이프 처리하여 제목이 Q&A로 표시되었습니다. 전형적이고 창피했으며, 발견하는 데 너무 오래 걸렸습니다.
- 콘텐츠가 많아질수록 SEO는 나빠졌습니다. 저는 수천 개의 프로그램 키워드 페이지를 생성했습니다. Google은 그중 약 9천 개를 '발견'했고 거의 아무것도 색인하지 않았는데, 한계는 콘텐츠 양이 아니라 크롤링 예산(crawl budget)이었습니다. 더 많이 게시하는 것이 오히려 해로웠습니다. 저는 페이지 생성의 쳇바퀴를 멈췄습니다. <약 9k라는 숫자를 확인하세요>
- 저 자신의 자동화가 환각을 일으켰습니다. 아웃리치 에이전트(outreach agent)는 스폰서 이메일 6개를 보냈다고 의기양양하게 보고했지만, 실제로 전송된 것은 단 하나도 없었습니다.
제품이 가짜 신호를 감지하도록 만들어졌는데, 내부적으로는 가짜 신호를 전송했다는 아이러니를 저는 놓치지 않았습니다. 이제 모든 에이전트의 행동은 신뢰받기 전에 실제 부작용(side effect)과 교차 검증됩니다. 이는 구인 공고 점수화가 작동하는 것과 동일한 '실제로 일어났는지?'라는 규칙입니다.
스택 (The stack)
- Cloudflare Pages에서 Astro 5 (정적 + 온디맨드 하이브리드).
- 단일 VPS에 설치된 Postgres.
- LLM 통과 검사를 위한 DeepSeek.
- 검색을 위한 Pagefind.
- 자체 호스팅 GitHub Actions 러너(박스가 스스로 구축됨)
- 결제 처리를 위한 Whop. ‹결제 확인›
어떤 부분에 대해서든 댓글로 더 깊이 이야기 나누는 것을 환영합니다.
사용해 보기 / 제가 틀렸다고 말해주세요
지금 라이브입니다: Remoty.work. 만약 '유령 채용 공고(ghost jobs)' 때문에 피해를 입은 적이 있거나, 제 점수화가 순진하다고 생각하신다면, 저는 진심으로 듣고 싶습니다. 그 피드백이 다음 통과 검사를 훈련시킵니다.
(그리고 이것이 유용하다고 느끼신다면 — 오늘 Product Hunt에 막 출시했습니다. 한 표의 지지가 큰 의미가 있을 것입니다: 여기)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기