LLM을 안 쓰는 콘텐츠를 40개 만들어 봤습니다 — 만들 때만 돈이 드는 구조
AI로 결과를 만드는 서비스는 사람이 늘수록 비용이 늘어납니다. 답을 URL에 담는 방식으로 40개를 만들어 보니 운영비가 0이 됐습니다. 그 구조와 실제로 걸린 함정을 정리했습니다.
LLM으로 뭔가를 만들면 대개 이런 구조가 됩니다. 사용자가 입력하고, 모델이 답을 만들고, 그걸 저장하고, 링크로 공유합니다. 잘 되면 좋은데 잘 될수록 비용이 늘어납니다. 방문자가 열 배가 되면 토큰 비용도 열 배가 됩니다.
그래서 반대 구조를 하나 만들어 봤습니다. 만들 때만 비용이 들고 이후에는 사실상 0인 콘텐츠입니다. 성격 유형 테스트 같은 것들인데, 40개를 만들고 나니 몇 가지가 정리돼서 적어둡니다.
핵심: 답을 URL에 담는다
12문항짜리 테스트가 있다고 합시다. 각 문항의 선택지가 3개면, 사용자의 답은 [0,2,1,1,0,...] 같은 숫자 배열입니다. 이걸 문자로 바꿉니다.
[0,2,1,1,0,2,0,1,2,1,0,1] → "acbbacabcbab"그리고 결과 페이지 주소를 이렇게 만듭니다.
/games/worktype/r/acbbacabcbab서버는 이 주소를 받으면 문자열을 다시 숫자로 풀고, 점수를 계산하고, 결과를 렌더합니다. 저장하는 게 하나도 없습니다. 데이터베이스에 쓰지 않고 읽지도 않습니다. 결과 자체가 주소 안에 들어 있으니까요.
효과는 세 가지입니다.
- 비용: LLM 호출 0, DB 쓰기 0, DB 읽기 0. 남는 건 페이지 렌더 비용뿐입니다
- 공유: 링크를 받은 사람이 바로 결과를 봅니다. 로그인도 조회도 필요 없습니다
- 개인정보: 저장할 게 없으니 유출될 것도 없습니다. "결과가 저장되지 않습니다"를 사실로 쓸 수 있습니다
여기에 궁합을 얹으면 공유가 강제된다
답이 주소에 들어 있으니 두 사람의 답을 한 주소에 넣을 수 있습니다.
/games/worktype/vs/acbbacabcbab/cbaacbbacbab
↑ 내 답 ↑ 상대 답이렇게 하면 초대 화면이 만들어집니다. 내 결과 페이지에서 "궁합 링크"를 복사해 친구에게 보내면, 친구는 자기 답만 채우고 두 사람의 궁합 결과로 넘어갑니다. 초대가 있어야 완성되는 화면이라 공유가 자연스럽게 일어납니다.
여기서 한 발 더 나가면 N명도 됩니다.
/games/worktype/team/답안1-답안2-답안3-답안4단톡방에 한 번 던지면 여러 명이 순서대로 들어오고, 참여할 때마다 주소 뒤에 답안이 붙습니다. 이 과정에서도 저장은 여전히 0입니다.
걸린 함정 세 가지
1. 점수가 아무것도 구분하지 못했다
궁합 점수를 축 거리로 계산했습니다. 성향을 3~4개 축으로 재고, 두 사람의 축 값 차이를 평균 내서 점수로 바꿨습니다.
만들고 나서 무작위 답안 쌍을 4,000개 돌려봤더니 이랬습니다.
중앙값 83점, p90 98점열에 아홉이 80점을 넘었습니다. 점수가 아무것도 구분하지 못하는 상태였습니다.
원인은 평균입니다. 축 서너 개를 평균 내면 값이 가운데로 몰립니다(중심극한정리). 그런데 제가 "적당히 다른 게 가장 잘 맞는다"는 설계로 가운데를 최고점으로 잡아 놨으니, 대부분이 최고점 근처에 떨어진 겁니다.
고친 방법은 거리를 백분위로 바꾸는 것이었습니다. 게임마다 무작위 짝 2,000쌍의 거리를 미리 뽑아 분포표를 만들어 두고, 어떤 짝의 거리가 그 분포에서 몇 번째인지로 환산합니다. 백분위는 정의상 고르게 퍼지므로 점수도 퍼집니다.
중앙값 73점, p10 45점, p90 93점한 가지 주의할 게 있습니다. 표본을 만들 때 난수 시드를 고정해야 합니다. 안 그러면 서버마다 다른 표가 만들어져서, 같은 링크가 서버에 따라 다른 점수를 냅니다. 공유되는 링크에서는 치명적입니다.
2. 한 유형에 86%가 몰렸다
가치관을 재는 테스트를 만들었는데, 8개 유형 중 하나에 86%가 몰리고 하나는 아예 나오지 않았습니다.
원인은 선택지 배점이었습니다. 문항이 "무엇을 중요하게 여기나요"였는데, 각 선택지가 한 축만 올리고 아무것도 깎지 않았습니다. 그러니 어떤 답을 고르든 세 축이 전부 양수가 되고, 결국 모두가 "다 중요하게 여기는 사람"으로 분류됩니다.
가치관은 서로 경쟁하는 것입니다. 하나를 고르면 나머지는 상대적으로 덜 중요해집니다. 배점을 트레이드오프로 다시 짰습니다.
전: 돈 → { 보상: +3 }
후: 돈 → { 보상: +3, 성장: -2, 안정: -2 }이것만으로 쏠림이 사라지고 8개 유형이 다 나왔습니다.
3. 두 축이 같은 선택지에 묶여 있었다
여러 게임에서 특정 조합이 0.4~0.8%로 사실상 안 나왔습니다.
예를 들어 갈등 대처 테스트에서 "즉시 반응하지만 양보하는" 유형이 안 나왔습니다. 문항을 보니 "즉시"를 고르는 선택지가 전부 "주장"과 같이 묶여 있었습니다. 즉시 반응하면서 양보하는 답을 고를 자리가 아예 없었던 겁니다.
이건 눈으로는 절대 못 잡습니다. 문항 12개에 선택지 3개면 조합이 53만 개인데, 그중 어떤 조합이 도달 불가능한지 사람이 알 수가 없습니다.
그래서 검증 스크립트를 만들었다
위 세 가지를 전부 스크립트가 잡았습니다. 게임을 추가할 때마다 이걸 돌립니다.
npm run check:games확인하는 항목은 이렇습니다.
| 검사 | 안 하면 생기는 일 |
|---|---|
| 모든 답안 조합이 정의된 결과를 내는가 | 결과 페이지가 500 |
| 한 유형에 45% 넘게 몰리는가 | 결과가 무의미해짐 |
| 1% 미만으로 안 나오는 유형이 있는가 | 죽은 콘텐츠 |
| 궁합 목록에 모순이 있는가 | 같은 유형이 "잘 맞음"과 "부딪힘"에 동시 등장 |
| 결과마다 필수 항목이 채워졌는가 | 어떤 결과만 빈약해짐 |
콘텐츠를 40개 만드는 동안 이 스크립트가 잡아준 실제 버그가 열 건 가까이 됩니다. 그중에는 자가 점검 테스트가 응답자의 70%를 "경고" 단계로 판정하던 것도 있었습니다. 건강을 다루는 문항에서 과잉 경고는 도움이 안 되고 결과를 못 믿게 만듭니다.
정리
- 답을 URL에 담으면 LLM도 DB도 없이 결과 페이지가 성립합니다. 사람이 늘어도 비용이 안 늘어납니다
- 같은 구조로 1:1 궁합과 N명 궁합을 만들 수 있고, 초대가 있어야 완성되는 화면이라 공유가 자연스럽습니다
- 점수를 축 평균으로 내면 값이 가운데로 몰립니다. 백분위로 바꾸고 시드를 고정하세요
- 선택지 배점이 한 축만 올리면 모든 사람이 같은 유형이 됩니다. 경쟁하는 축은 서로 깎아야 합니다
- 조합이 수십만 개라 눈으로는 못 잡습니다. 분포를 재는 스크립트를 먼저 만드세요
LLM으로 만드는 서비스와 이런 콘텐츠는 배타적이지 않습니다. 토큰을 쓰는 축과 안 쓰는 축을 같이 두면, 비용이 트래픽에 정비례하지 않는 구조가 됩니다.
관련해서 LLM으로 서비스 만들 때 토큰 비용이 실제로 새는 곳도 같이 보시면 도움이 됩니다.
이어서 읽을 글
- CDN 뒤에 사이트를 올리고 나서 실제로 당한 캐시 함정 네 가지배포는 성공했는데 의도한 대로 서빙되지 않는 일이 반복됐습니다. 검증 파일이 사라지고, 기능 플래그를 켜도 404가 남고, 지운 파일이 계속 응답했습니다. 각각의 원인과 확인 방법입니다.
- 면접 '마지막으로 하고 싶은 말' — 뭘 말해야 하고 뭘 말하면 안 되나면접이 끝나갈 때 나오는 이 질문은 인사치레가 아닙니다. 마지막 3분이 인상을 바꾸는 경우를 자주 봅니다.
- 1분 자기소개 만드는 법 — 구조 하나만 알면 됩니다면접 첫 질문이자 인상이 정해지는 1분. 외워서 읊는 대신 구조를 잡아 두면 어떤 회사에도 맞출 수 있습니다.