Career Radar를 만들면서 최근 두 글은 모델 호출 전에 갖춘 측정 장치와 근거를 확인할 수 없는 설명을 ‘모름’으로 남기는 처리에 관한 것이었다. 필요한 작업이었지만, 두 글을 나란히 보고 조금 걱정됐다.
지원할 공고를 고르는 앱을 만들고 있었는데, 어느새 검증 도구만 계속 만들고 있는 건 아닐까?
처음의 질문은 단순했다. 내 경력을 놓고 봤을 때 이 공고는 현실적인 선택인지, 조금 무리해서 도전할 만한지, 아니면 넘기는 편이 나은지 알고 싶었다. 테스트가 늘었다는 사실만으로 그 질문에 답했다고 할 수는 없다.
이번에는 OpenRouter를 모델 제공자로 추가했다. OpenAI API 크레딧이 없는 상태에서 다른 모델로도 흐름을 확인할 수 있도록 연결 경로를 하나 더 만든 것이다. 무료 모델을 쓸 수 있을지 살펴보려는 의도는 있지만, 이 PR에서 특정 무료 모델의 동작을 확인한 것은 아니다.
이 글은 PR #17의 0104271 기준 병합 전 기록이다. 구현은 Claude Code가, 리뷰와 수정 후 재검증은 Codex가 맡았다.
제공자는 바꾸고, 묻는 내용은 유지했다
새 제공자를 붙이면서 앱이 하는 질문까지 바꾸고 싶지는 않았다. 이력서와 공고에서 정보를 추출하고, 적합도를 판정하고, 서류 검토에서 확인할 맥락을 만드는 규칙은 그대로 두었다.
그래서 프롬프트, 응답 초안의 스키마, 앱 내부 데이터로 바꾸는 코드를 공통으로 분리했다. 기존 OpenAI 어댑터는 Responses API를 쓰고, 새 OpenRouter 어댑터는 Chat Completions 형식으로 요청한다. 앱이 받는 결과의 형태는 같게 맞췄다.
기본 제공자는 여전히 OpenAI다. OpenRouter를 쓰려면 제공자와 모델을 명시해야 하고, 설정이 잘못됐다고 앱이 조용히 다른 제공자로 넘어가지는 않는다.
물론 같은 프롬프트를 보낸다고 같은 답이 나오는 것은 아니다. 구조화 출력의 지원도 모델과 제공 엔드포인트에 따라 다르다. 요청에는 엄격한 JSON 스키마와 필요한 파라미터를 지원하는 경로를 요구하고, 돌아온 응답은 앱에서도 다시 파싱하고 검증하도록 했다. 형식에 맞지 않는 답을 판정 결과로 억지로 받아들이지는 않는다. OpenRouter 구조화 출력 문서
연결을 바꾸니 기록도 바꿔야 했다
리뷰에서 나온 수정은 세 가지였다. 새 모델을 잘 호출하는지만 볼 일이 아니었다.
첫째는 오류였다. 정상 응답의 본문을 조심해서 다뤄도, SDK가 만든 오류 메시지에는 제공자의 응답 본문이 들어갈 수 있었다. 합성 HTTP 오류로 확인하니 테스트용 표식이 MCP 응답까지 전달됐다. 실제 개인정보가 유출된 사건은 아니지만, 입력 내용이 오류에 섞여 돌아오는 경로를 그대로 둘 수는 없었다.
두 어댑터가 공통으로 오류를 처리하도록 바꿨다. SDK 요청 오류는 고정 문구로 바꾸고 상태 코드와 요청 ID 같은 진단 정보만 남긴다. 취소는 취소로 구분할 수 있게 유지했다.
둘째는 보관 설정의 기록이었다. 기존 측정 보고서는 OpenAI 요청에 맞춰 store: false라고 적었다. 그런데 새 OpenRouter 어댑터는 그 파라미터를 보내지 않았다. 요청하지 않은 설정을 보고서에 남기면, 읽는 사람은 적용된 보호 장치로 오해할 수 있다.
OpenRouter 보고서는 store: "n/a"로 바꿨다. 이 값은 보관하지 않는다는 보장이 아니다. 실제 데이터 취급은 OpenRouter와 상위 제공자의 정책, 선택한 라우팅·데이터 설정을 확인해야 한다. 무료 모델을 선택하더라도 데이터가 로컬 안에만 머무는 것은 아니다. OpenRouter 제공자 라우팅 문서
셋째는 누가 응답했는지였다. OpenRouter를 거치면 모델 이름뿐 아니라 실제 요청을 처리한 상위 제공자도 기록할 필요가 있다. 판정 결과에는 그 정보가 있었지만 측정 보고서로 전달되는 과정에서 빠졌다. 호출별 기록과 요약에 함께 남기고, 응답에 정보가 없으면 unknown으로 표시하도록 고쳤다.
세 수정 모두 결과를 해석할 때 필요한 것이었다. 어떤 오류가 밖으로 나갔는지, 실제로 어떤 설정을 보냈는지, 누구의 응답을 측정했는지. 제공자가 바뀌었는데 기록은 예전 그대로라면, 나중에 숫자를 얻어도 무엇을 확인한 것인지 헷갈릴 수 있다.
다음은 기능보다 사용 장면
수정 후 테스트는 302개, 정책 평가는 28개가 통과했다. 타입 검사·린트·빌드도 통과했다. 실제 SDK에 합성 응답을 연결해 요청과 오류 처리, 보고서에 정보가 남는 경로를 확인한 결과다. OpenAI와 OpenRouter 어느 쪽에도 실제 모델 요청은 보내지 않았다.
여기까지가 이번 PR의 범위다. 제공자를 하나 더 연결할 수 있게 됐지만, 그 모델이 좋은 판정을 내리는지나 ChatGPT 안에서 매끄럽게 동작하는지는 아직 별개다.
앞으로 OpenRouter로 시간과 사용량을 측정하더라도 그 결과는 해당 엔드포인트의 값이다. 기존에 측정하려던 OpenAI 경로의 90초 배치 예산을 검증한 것으로 볼 수는 없다.
이제 다음 목표는 작은 사용 장면으로 잡으려고 한다. 먼저 개인정보가 없는 합성 프로필 하나와 공고 세 개를 준비한다. 사용할 모델과 데이터 전달 조건을 확인하고 호출을 승인한 뒤, 결과를 놓고 이런 질문을 해보는 것이다.
- 왜 지원할 만한지, 또는 넘길 만한지 근거를 따라갈 수 있는가?
- 모르는 부분을 억지로 채우지 않고, 내가 확인할 질문으로 남기는가?
- 읽고 나서 어느 공고부터 살펴볼지 정하는 데 도움이 되는가?
합성 프로필과 세 공고로는 주어진 경력을 기준으로 근거를 따라가며 우선순위를 정할 수 있는지 살펴보려 한다. 이것만으로 추천 정확도나 내 구직에 실제로 도움이 되는지를 확인했다고 할 수는 없다. 같은 자료를 일반 채팅에 넣었을 때와 비교해, 이 도구로 묶어 둔 이유가 있는지도 확인하고 싶다.
본인 이력서를 넣는 검증은 외부 전송 범위와 보관 정책을 확인한 뒤 별도로 결정하려 한다. 로컬에서 앱을 실행해도 모델 호출 시 입력은 외부로 전달되며, 로컬 데이터를 삭제한다고 제공자 측 보관까지 지워지는 것은 아니다.
다음 글에는 무엇을 더 붙였는지보다, 써보니 무엇이 도움이 됐고 무엇이 필요 없었는지를 적고 싶다.