개발 기록

아이가 쓸 앱에서 LLM을 실시간 호출하지 않기로 한 이유

자녀용 독서 퀴즈 앱을 만들며 내린 결정. 문장 해설을 실시간 생성 대신 미리 만들어둔 이유와 그때 얻은 것들을 정리했다.

초등학생 자녀가 집에서 책을 읽고 챕터마다 퀴즈를 푸는 웹 앱을 만들었다. 왼쪽에 삽화, 오른쪽에 본문을 두고 태블릿에서 편하게 읽도록 했다.

기능 중에 문장 분석이 있다. 아이가 어려운 문장을 누르면 의역, 요약, 단어 뜻, 숙어, 문법 설명이 뜬다.

이걸 처음엔 당연히 실시간 LLM 호출로 만들려고 했다. 문장을 누르면 API를 부르고 결과를 보여주는 구조다. 결국 그렇게 하지 않았다.

실시간 호출을 접은 세 가지 이유

1. 아이는 2초를 기다리지 않는다

어른은 로딩 스피너를 보면 기다린다. 뭔가 처리 중이라는 걸 알고, 그럴 만한 가치가 있다고 판단하면 참는다.

아이는 다르다. 눌렀는데 바로 안 나오면 다른 걸 누른다. 그리고 또 누른다. 결과가 뜰 때쯤엔 이미 관심이 다른 데 가 있다.

읽기를 돕는 기능인데 읽는 흐름을 끊으면 목적이 무너진다.

2. 매번 다른 답이 나온다

같은 문장을 두 번 눌렀는데 설명이 다르면 아이가 혼란스러워한다. “아까는 이렇게 말했는데?“가 된다.

학습용 콘텐츠에서 이건 생각보다 큰 문제다. 어른은 “AI라서 그래”로 넘기지만, 아이에게는 그냥 앱이 말을 바꾸는 것이다.

3. 비용과 장애가 아이 경험에 직결된다

집에서 아이가 쓰는 앱이 외부 API 상태에 좌우된다. 서버가 느리면 앱이 느리고, 장애가 나면 기능이 죽는다. 부모 입장에서 관리하고 싶지 않은 변수다.

사전 생성으로 바꿨다

그래서 문장 해설을 미리 만들어 DB에 저장하는 방식으로 바꿨다. 책을 앱에 넣는 시점에 문장별 해설을 한 번 생성해두고, 아이가 누르면 저장된 걸 읽어온다.

바뀐 점은 이렇다.

실시간 호출 사전 생성
응답 속도 1~3초 즉시
같은 문장의 답 매번 다름 항상 같음
외부 장애 영향 기능 정지 없음
비용 발생 시점 누를 때마다 책 등록 시 한 번
품질 확인 불가능 등록 시 미리 검토 가능

마지막 항목이 예상 밖의 이득이었다. 미리 만들어두면 내가 먼저 읽어볼 수 있다. 이상한 설명이 있으면 고치거나 지울 수 있다. 실시간 생성에서는 아이가 먼저 본다.

퀴즈 해설에 근거 페이지를 붙인 이유

퀴즈를 틀리면 해설이 나오는데, 여기에 근거(evidence) 페이지를 함께 표시한다. “이 답이 맞는 이유는 3페이지에 나온다”는 식이다.

이걸 넣은 이유는 두 가지다.

첫째, 아이가 확인할 수 있어야 한다. 해설만 있으면 “그렇구나” 하고 넘어간다. 근거 페이지가 있으면 돌아가서 다시 읽는다. 그게 원래 목적이다.

둘째, 내가 문제의 품질을 검증할 수 있다. 근거 페이지를 함께 생성하게 하면, 본문에 없는 내용으로 만든 문제가 걸러진다. 근거를 못 대는 문제는 대체로 잘못된 문제였다.

AI로 문제를 만들 때 “답과 함께 근거 위치를 내놓게 하는 것”은 검증 장치로 꽤 쓸모가 있었다.

틀린 문제를 다시 만나게 하기

단어장에는 SM-2 기반 간격 반복(SRS)을 넣었다. 모르는 단어를 저장하면 일정 간격으로 다시 나온다. 잘 맞히면 간격이 늘고, 틀리면 짧아진다.

퀴즈 점수와 틀린 문제도 프로필별로 저장한다. 자녀가 여러 명이면 각자 읽던 위치와 성적이 따로 관리된다.

여기서 중요했던 건 점수를 강조하지 않는 것이었다. 점수를 크게 띄우면 아이가 점수를 위해 찍는다. 틀린 문제를 다시 만나게 하는 쪽이 목적에 맞다.

같은 이유로 챕터 퀴즈 결과에서 이어지는 분기 모험도 경쟁 요소 없이 만들었다. 잘 맞혀야 좋은 결말이 나오는 구조가 아니라, 선택에 따라 이야기가 갈리는 구조다.

기술 스택 이야기

  • Next.js 15 (App Router) + React 18
  • better-sqlite3 — 동기 SQLite, WAL 모드
  • Tailwind CSS
  • TypeScript를 빌드 없이 tsx로 실행

SQLite를 고른 건 집에서 도는 앱이기 때문이다. 사용자가 가족 몇 명이고, 서버는 하나고, 백업은 파일 하나 복사하면 된다. 여기에 별도 DB 서버를 띄울 이유가 없다.

동기 API(better-sqlite3)를 쓴 것도 같은 이유다. 동시 접속이 적은 환경에서는 async 오버헤드 없이 그냥 읽는 게 빠르고 코드도 단순하다.

정리

  • 아이용 앱에서 로딩 시간은 어른용보다 훨씬 치명적이다. 기다리지 않고 다른 걸 누른다
  • 학습 콘텐츠는 같은 질문에 같은 답이 나와야 한다. 실시간 생성은 이걸 보장하지 못한다
  • 사전 생성의 진짜 이득은 속도가 아니라 내가 먼저 검수할 수 있다는 것이다
  • AI에게 답과 함께 근거 위치를 내놓게 하면 잘못된 문제가 걸러진다
  • 규모에 맞는 도구를 쓴다. 가족용 앱에 DB 서버는 필요 없다