손기철 장로님(손기철 박사)의 헤븐리터치 미니스트리가 Kingdom Pathfinder 라는 AI 검색 서비스를 열었다. kingdompathfinder.com 에 가면 검색창 하나가 있고, 거기에 신앙의 질문을 그냥 평소 말투로 적으면 된다.

이름이 어디서 왔는지부터 짚어두는 게 좋겠다. HTM 공식 대표자 소개 페이지에 이런 문장이 있다.

손기철 박사는 하나님의 말씀과 성령의 기름부으심으로 사역하는 이 시대의 ‘킹덤 패스파인더’(kingdom pathfinder)입니다.1

서비스 이름은 거기서 그대로 가져온 것이다. 그리고 이건 팬이 만든 외부 서비스가 아니다. HTM 공식 홈페이지의 ‘인사이트’ 메뉴에 ‘킹덤빌더 용어사전’과 나란히 ‘킹덤 패스파인더’ 가 걸려 있고, 설명은 “HTM 유튜브 채널의 말씀 가운데 지금 필요한 메시지를 쉽고 빠르게 찾아보세요” 다.2

이 글은 신학 리뷰가 아니라 엔지니어링 분석이다. 그리고 분석 범위를 먼저 정직하게 적어둔다.

  • 근거로 쓴 것: 공개된 페이지(/, /about, /guide)와 브라우저로 내려받는 공개 클라이언트 JavaScript.
  • 하지 않은 것: 이 서비스의 검색 API 는 X-API-Key 를 요구한다. 나는 그 키를 추출하지도, 쓰지도 않았다. 인증 없이 보낸 한 번의 요청은 403 을 받았고, 거기서 멈췄다. 서버 내부 구현은 관측하지 않았다. 아래에서 “서버가 ~한다” 고 쓰는 대목은 전부 클라이언트가 그렇게 기대하고 있다 는 뜻이다.

1. 이 서비스가 푸는 문제

HTM 은 2008년 손기철 박사가 세운 초교파 선교단체다.3 매주 화요일 저녁 7시 30분에 헤븐리터치센터(서울 동작구 보라매로5길 35 지하1층)에서 화요말씀치유집회를 열고, 같은 시각 유튜브로 생중계한다.4 그게 수백 시간 분량으로 쌓였다.

서비스 소개 페이지는 문제를 이렇게 적는다.

수백 시간 분량의 설교 안에는 이미 좋은 답이 있습니다. 문제는 그걸 몇 시간씩 뒤져야 찾을 수 있었다는 것입니다.5

이건 검색 문제다. 그것도 키워드 검색이 구조적으로 잘 못하는 종류의 검색 문제다.

설교는 용어를 정의하고 넘어가는 논문이 아니다. 한 주제를 30분 동안 다루면서 정작 그 주제를 가리키는 단어는 한 번도 안 나올 수 있다. 반대로 ‘은혜’나 ‘믿음’ 같은 말은 거의 모든 설교에 나오니 검색어로서 변별력이 없다. 즉 찾고 싶은 것과 적혀 있는 것이 어긋난다. 자막 전문검색으로 이 컬렉션이 안 열리는 이유다. 소개 페이지가 “키워드 매칭이 아니라 의미 기반 검색” 이라고 못 박은 이유다.5

2. 뼈대: 임베딩 + 벡터 검색 + 근거 기반 답변

이건 교과서적인 RAG(Retrieval-Augmented Generation) 구조다. 질문을 벡터로 바꿔 유사한 대목을 찾아오고, 찾아온 원문을 근거로만 답을 쓰게 하는 방식이다. 2020년 Lewis 등이 정식화한 패턴이고, 파라미터 안에 지식을 욱여넣는 대신 외부 코퍼스를 붙여서 “출처를 댈 수 있는” 생성을 만든다는 게 원래 동기였다.6

클라이언트가 실제로 하는 일은 단순하다. 질문 문자열 하나를 POST /api/api/ask 로 던진다. (const API_BASE = '/api' 아래에서 다시 `${API_BASE}/api/ask` 를 만들어서 경로에 api 가 두 번 들어간다. 기능엔 문제없지만 눈에 띈다.)

응답으로 돌아오는 필드가 이 서비스의 설계를 거의 다 말해준다.

필드 역할
references[] 찾아온 설교 대목들 (각각 theme 라벨을 가질 수 있음)
answer / answer_generated 근거 기반 요약 답변
answer_sources 그 답변이 어느 설교에 근거했는지
answer_status generated / insufficient / no_results / disabled / skipped
confidence 검색 자체의 확신도 (아래 4단계)
suggestions 다시 물어볼 검색어 제안
_debug.timings 단계별 소요 시간

그리고 서버가 돌리는 단계는 _debug.timings 의 키 이름에 그대로 노출돼 있다 — embedding_generation, keyword_extraction, vector_search, keyword_search, complete_query, total_query_time. 벡터 검색과 키워드 검색이 둘 다 있다. 의미 기반만으로 가지 않고 하이브리드로 간 것이다.

3. 진짜 볼 만한 부분: 4단계 기권(abstention) 사다리

여기가 이 서비스에서 가장 잘 만든 부분이다.

대부분의 “우리 문서로 답하는 AI” 는 답을 항상 내놓는다. 근거가 시원찮아도 그럴듯한 문장을 만들어 낸다. 성경이나 설교를 다루는 서비스에서 이건 단순한 품질 문제가 아니다. 하지 않은 말을 했다고 만들어내는 것이기 때문이다.

Kingdom Pathfinder 는 이걸 두 축으로 나눠서 막는다.

축 ①: confidence — 검색이 잘 됐는가. 클라이언트는 네 갈래로 다른 배너를 그린다.

  • (확신) — 배너 없이 담백하게: ※ 질문과 관련된 설교 대목을 찾아드렸어요. AI가 만든 답변이 아니라 실제 설교 영상입니다.
  • weak — “딱 맞는 설교는 못 찾았어요. 아래는 관련 있을 수 있는 영상이에요.”
  • rescued — 주석에 설계 의도가 그대로 적혀 있다: “floor 아래라 0개였지만 LLM이 관련 영상을 골라줌 + 더 정확한 재검색어 제안”. 유사도 하한선(floor)에 걸려 결과가 0개가 된 상황을, LLM이 후보를 골라 구제하되 구제했다는 사실을 사용자에게 알리는 상태다.
  • low — 아무것도 못 건짐. 이때는 suggestions 를 칩으로 뿌리면서 “질문에 여러 주제가 섞여 있거나, 설교에서 잘 쓰지 않는 표현일 수 있어요. 아래처럼 나눠서 찾아보세요.” 라고 안내한다. 실패를 사용자 탓으로 돌리지 않고 다음 행동을 준다.

축 ②: answer_status — 찾긴 찾았는데 답을 쓸 만한가. 이게 insufficient 면 클라이언트는 답변 영역에 딱 이 한 줄만 찍는다.

※ 관련 있어 보이는 설교 영상은 찾았지만, 설교 내용만으로는 정확한 답변을 드리기 어려워 임의로 답을 만들지 않았습니다. 아래 추천 설교 영상을 직접 확인해 주세요.

두 축을 나눈 게 핵심이다. “관련 영상을 못 찾았다”“영상은 찾았는데 그걸로 답을 만들 수는 없다” 는 완전히 다른 실패다. 하나로 뭉뚱그리면 둘 중 하나는 반드시 거짓말이 된다.

이건 지금 LLM 연구가 가리키는 방향과 정확히 맞는다. OpenAI 연구진이 2025년에 낸 「Why Language Models Hallucinate」 는 환각이 신비한 결함이 아니라 평가 방식이 만든 인센티브라고 주장한다. 정확도만 세는 이분법 채점에서 “모르겠다”는 0점이고 찍는 건 기댓값이 양수라, 모델은 늘 시험 보는 자세로 찍게 된다는 것이다. 논문의 처방은 “기권에 부분점수를 주고 확신에 찬 오답에 더 큰 벌점을 주라” 다.78

제품 층위로 번역하면 이렇게 된다. 기권을 UI에서 1등 시민으로 만들어라. 답이 없을 때 빈 화면을 주면 사용자는 그걸 고장으로 읽고, 개발자는 다음 스프린트에 “아무거나라도 보여주자” 는 패치를 넣게 된다. Kingdom Pathfinder 는 기권마다 전용 문안과 전용 다음 행동을 붙여서 그 압력을 미리 제거해 뒀다. 소개 페이지의 표현으로는 이렇다 — “근거가 부족하면 ‘근거 부족’이라고 솔직히 말합니다.”5

덧붙여, 확신이 있을 때조차 배너는 “AI가 만든 답변이 아니라 실제 설교 영상입니다” 를 매번 반복한다. 출처의 성격을 매 화면 다시 선언하는 건 설교 코퍼스를 다루는 서비스로선 옳은 보수성이다.

4. 흔적으로 남은 설계 후퇴: 2단계 재순위 → 단일 호출

코드에 이런 함수가 있다.

// --- 비동기 2단계 AI 검색 ---
// 1단계: 빠른 벡터 결과를 즉시 표시 → 2단계: 백그라운드 Gemini 재순위로 갱신
// (재순위 실패/지연 시 1단계 결과를 그대로 유지 = 안전)
async function fetchAskTwoStage(requestBody, headers, searchType, searchTerm) {
    // 정렬이 서버에서 즉시(키워드 부스팅, LLM 없음) 처리되므로 단일 호출.
    const resp = await fetch(ASK_API_URL, { ... });

함수 이름은 fetchAskTwoStage 인데, 안에서는 한 번만 부른다. 바로 아랫줄 주석이 이유를 말한다 — 정렬을 서버의 키워드 부스팅으로 옮겼고, LLM 은 정렬에서 빠졌다.

재순위(rerank)를 LLM 으로 돌리는 건 품질이 오르는 대신 초 단위 지연과 비용이 붙는다. 이 팀은 그걸 넣었다가 뺐다. 그리고 뺀 자리에 함수 이름과 “재순위 진행 표시기” (showRerankingIndicator, “관련 설교를 찾아 정리하는 중…”) 가 화석처럼 남아 있다.

여기서 나는 이 팀을 좋게 본다. 중요한 건 1단계 주석의 괄호다 — “(재순위 실패/지연 시 1단계 결과를 그대로 유지 = 안전)”. 고급 단계를 얹을 때 그 단계가 실패하면 기본 결과로 조용히 내려앉게 설계했다는 뜻이다. 이건 그냥 좋은 습관이 아니라, 나중에 그 단계를 통째로 들어낼 수 있게 만든 설계다. 실제로 들어냈다.

5. 느린 일은 뒤로 미루고, 클라이언트가 주워 담는다

theme — 설교 대목마다 붙는 주제 라벨 — 은 서버가 백그라운드로 만든다. 첫 응답에는 없을 수 있다. 클라이언트는 이렇게 처리한다.

const missing = (data.references || []).filter(r => !r.theme).length;
if (missing > 0) pollThemeLabels(requestBody, headers, searchType, searchTerm, 3);

5초 간격으로 최대 3회 다시 물어 라벨만 채운다. 폴링에서 눈여겨볼 두 줄이 있다.

if (currentSearchTerm !== searchTerm) return; // 사용자가 다른 검색을 했으면 중단

이 가드가 두 번 나온다 — setTimeout 진입 시 한 번, 응답이 돌아온 뒤 한 번. 사용자가 그새 다른 검색을 했으면 이전 검색의 늦은 응답이 현재 화면을 덮어쓰지 못한다. 비동기 UI에서 가장 흔한 버그(stale response 가 최신 결과를 밀어냄)를 정확히 막아둔 것이다.

그리고 주석 한 줄이 이 설계가 성립하는 조건을 말한다 — “키워드 부스팅은 순서가 고정 → 순서는 그대로, 라벨만 슬쩍 나타남”. 정렬에서 LLM 을 뺐기 때문에 순서가 결정적(deterministic) 이고, 그래서 같은 질문을 다시 물어도 같은 순서가 온다. 순서가 흔들렸다면 5초 뒤 목록이 통째로 재배열되는 꼴이 났을 것이다. 4장의 결정과 이 장의 폴링은 같은 결정의 앞뒤다.

6. 성능 패널을 사용자에게 열어뒀다

검색 결과 화면에 “성능 디버깅 정보 / 보기” 토글이 있고, 열면 _debug.timings 가 그대로 보인다.

서버 처리 시간: N.NNNN초
- 임베딩 생성: ...
- 키워드 추출: ...
- 벡터 검색: ...
- 키워드 검색: ...

보통은 내부용으로 숨기는 값이다. 열어둔 쪽을 나는 지지한다. 느리다는 인상은 남는데 어디서 느린지가 없으면 그건 영원히 “그냥 느린 서비스” 로 남는다. 다만 이건 개발 편의 기능이 프로덕션에 남은 것에 가까워 보이므로, 계속 둘 거라면 “이게 왜 보이는지” 한 줄 설명을 붙이는 편이 낫겠다.

7. 아쉬운 지점 하나

인증 부분을 보면 브라우저에 정적인 API 키가 실려 나간다 (X-API-Key). 로그인 토큰(Authorization: Bearer)은 따로 있고, 401 이면 갱신 후 재시도하는 흐름도 제대로 들어가 있다. 문제는 그 정적 키 쪽이다.

일반론으로 말하면 이렇다. 브라우저로 내려보낸 값은 비밀이 아니다. 누구나 개발자도구로 볼 수 있다. 그러니 그런 키는 “인증” 이 아니라 레이트 리밋의 손잡이 정도로만 취급해야 한다. 실제 방어는 서버 쪽 — 출처(Origin) 검사, IP·세션 단위 쿼터, 비정상 호출량 차단 — 에 있어야 한다. (이 서비스가 그런 방어를 갖췄는지는 내가 확인하지 않았다. 확인하려면 남의 서비스를 두드려봐야 하고, 그건 하지 않는다.)

서버 응답 헤더는 nginx/1.27.5X-Content-Type-Options: nosniff, X-Frame-Options: DENY 가 붙어 있다. 기본적인 것들은 챙겨져 있다는 뜻이다.

8. 정리 — 이 서비스가 잘한 것

기술 스택 자체는 특별하지 않다. 임베딩 + 벡터 검색 + 하이브리드 키워드 + 근거 기반 요약. 2026년에 이건 표준 부품이다.

잘한 건 부품이 아니라 실패를 다루는 방식이다.

  1. 검색 실패(confidence)와 답변 실패(answer_status)를 분리했다.
  2. 각 실패마다 전용 문안과 다음 행동을 줬다 — 기권이 빈 화면이 아니다.
  3. 고급 단계(LLM 재순위)를 실패 시 안전하게 내려앉게 설계했고, 실제로 그 단계를 들어낼 때 그 설계가 값을 했다.
  4. 느린 작업(주제 라벨)을 비동기로 빼면서 stale response 가드를 정확히 걸었다.
  5. “AI가 만든 답변이 아니라 실제 설교 영상” 을 매 화면 반복한다.

설교를 다루는 서비스가 “모르면 모른다고 한다” 를 UI의 1등 시민으로 올려놓은 것 — 그게 이 서비스에서 가장 배울 만한 결정이다. 하나님나라 복음을 검색하는 도구가 없는 말을 만들어내지 않겠다는 원칙을 코드 층위까지 내려서 구현했다는 뜻이니까.


References

본문의 코드·필드명·문안은 2026-09-23 기준 kingdompathfinder.com 이 공개적으로 배포하는 클라이언트 JavaScript 에서 직접 인용했다. 서버 구현은 관측 대상이 아니었으며, 인증이 필요한 API 는 호출하지 않았다.

  1. Heavenly Touch Ministry, 「대표자 소개 — 손기철 박사」. https://heavenlytouch.kr/founder (2026-09-23 열람) 

  2. Heavenly Touch Ministry 공식 홈페이지 ‘인사이트’ 섹션. https://heavenlytouch.kr/ (2026-09-23 열람) 

  3. Heavenly Touch Ministry, 「HTM 소개 — 설립목적」(2008년 손기철 박사 설립, 초교파적 선교단체). https://heavenlytouch.kr/intro (2026-09-23 열람) 

  4. Heavenly Touch Ministry, 「화요말씀치유집회」 안내(매주 화요일 저녁 7시 30분, 헤븐리터치센터 / 온라인 생중계 유튜브 @HTM0691). https://heavenlytouch.kr/service (2026-09-23 열람) 

  5. Kingdom Pathfinder, 「서비스 소개」. https://kingdompathfinder.com/about (2026-09-23 열람)  2 3

  6. Patrick Lewis et al., “Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks”, NeurIPS 2020. arXiv:2005.11401. https://arxiv.org/abs/2005.11401 

  7. Adam Tauman Kalai, Ofir Nachum, Santosh S. Vempala, Edwin Zhang, “Why Language Models Hallucinate”, arXiv:2509.04664 (2025-09-04). https://arxiv.org/abs/2509.04664 

  8. OpenAI, “Why language models hallucinate” (2025-09-05). https://openai.com/index/why-language-models-hallucinate/