1.0x
> cd ..

이 글은 AI로 프로젝트를 분석해서 쓴 글입니다.

NiVoca

NiVoca

2026-06-24 ~ 진행 중

Intro

NiVoca는 일본어 단어를 모으고 복습하는 앱입니다. 이전에 소개했던 NiVoca (Legacy)를 Flutter와 Kotlin/Spring Boot로 다시 만들었고, 지금은 이 버전을 App Store에서 운영하고 있습니다.

처음 만들 때의 목적은 일본어 단어를 조금 더 편하게 입력하는 것이었습니다. 한글·영문 키보드를 쓰다가 일본어 단어 하나를 넣으려고 키보드를 바꾸는 게 번거로워, 로마자로 입력하면 가나로 바뀌는 단어장에서 시작했습니다. 여기에 책이나 화면 속 단어를 사진으로 가져오고, 저장한 단어를 다시 꺼내 공부하는 기능이 하나씩 붙었습니다.

기존 버전을 만들면서 온디바이스 AI처럼 기기 기능을 직접 다루는 작업도 시도했습니다. 웹과 WebView 중심 구조에서 이런 기능을 이어 붙이는 데 한계를 느껴, 앱은 Flutter로 옮기고 백엔드도 별도로 구성했습니다. 단어 입력부터 학습까지 모바일 앱 안에서 자연스럽게 이어지는 방향으로 다시 만든 프로젝트입니다.

프로젝트 화면

NiVoca 사전 검색 결과로 읽는 법과 뜻을 채운 단어 추가 화면
사전에서 찾은 단어를 뜻·읽는 법과 함께 추가합니다.
NiVoca 사진 인식으로 찾은 일본어 단어 후보를 선택하는 화면
사진에서 읽은 단어 중 필요한 것만 골라 저장합니다.
NiVoca의 주제별 내 단어장 목록
공부할 주제에 따라 단어장을 나눕니다.
NiVoca 단어 카드에서 예문을 보고 복습 난이도를 평가하는 화면
단어와 예문을 확인하고 복습 난이도를 평가합니다.
NiVoca 한자 획순 가이드를 따라 직접 쓰는 연습 화면
획순 가이드를 보며 직접 써 봅니다.

Flutter 앱의 사전 검색 후 단어 추가, 사진 인식 결과, 단어장, 퀴즈, 쓰기 연습 화면입니다. 2026년 8월경 직접 촬영한 실기기 자료로, 현재 배포 버전과 일부 UI가 다를 수 있습니다.

링크

주요 기능

단어를 모으는 여러 방법

사전에서 직접 검색하거나 사진과 복사한 일본어 글에서 단어를 찾아 단어장에 추가할 수 있습니다. 로마자 입력뿐 아니라 음성·손글씨 입력도 지원합니다. 저장할 때 읽는 법과 뜻을 확인하고, 단어장과 태그로 공부할 내용을 나눌 수 있습니다.

다른 앱에서 보던 일본어를 공유 시트로 보내거나 클립보드에서 가져오는 경로도 마련했습니다. 사진 OCR은 iOS의 Apple Vision과 Android의 ML Kit를 사용하고, 읽어 낸 글에서 단어 후보를 고르는 단계와 구분했습니다.

직접 단어장을 채우는 것 외에 JLPT 레벨별 단어장과 공유 단어장을 이용하는 방법도 있습니다. 단어 상세에서는 예문과 한자 정보를 함께 확인할 수 있어, 뜻만 외우고 끝나지 않도록 구성했습니다.

단어장을 공유하고, 구독하거나 복사하기

제가 정리한 단어장을 공개하고 다른 사람의 단어장을 구독하거나 복사해서 사용할 수 있습니다. JLPT 레벨별 기본 단어장 외에도 여행·음식·읽고 있는 글처럼 같은 주제로 공부할 단어를 묶어 전달하는 용도입니다. 공유 단어장을 확인한 뒤 필요한 것을 선택할 수 있어, 처음부터 모든 단어를 직접 입력하지 않아도 학습을 시작할 수 있습니다.

구독한 단어를 내 단어장으로 담는 기능도 있습니다. 공유를 끄거나 사용자를 차단했을 때 공유 단어가 계속 노출되지 않도록 접근 범위를 관리하고, 현재 퀴즈에 사용할 수 없는 단어장은 이유를 안내합니다. 앱 밖에서 단어를 가져오는 공유 메뉴와, 앱 안에서 단어장을 나누는 공유는 별개의 흐름으로 구성했습니다.

구독은 원본의 단어를 읽는 관계이고, 복사는 접근 가능한 단어를 내 소유로 가져와 새 단어장에 연결하는 동작입니다. 원본의 공개 상태를 따르는 구독과 독립된 내 단어를 구분해야 해서, 둘을 같은 「추가」 처리로 뭉치지 않았습니다. AI 답변을 복사하거나 공유하는 기능도 별도로 제공합니다.

목록으로 정리해 둔 단어는 AI 대화에 CSV·TSV 같은 텍스트 파일로 첨부해 활용할 수 있습니다. 파일을 읽는 단계와 실제 단어 저장은 분리하고, 제안 내용을 확인한 뒤 반영합니다. 오너용 MCP에서도 외부 AI가 CSV를 해석하고 가져오기 미리 보기와 적용 도구를 호출하는 경로를 마련했습니다.

모은 단어를 다시 공부하기

복습에는 FSRS 간격 반복 알고리즘을 사용합니다. 퀴즈를 푼 뒤 난이도를 평가하면 단어별 복습 간격이 조정됩니다. 단어 카드, 예문 빈칸 채우기, 뜻을 보고 쓰기 등 여러 방식으로 기억을 확인할 수 있습니다.

앱을 다시 열어도 오늘 받은 퀴즈와 퀴즈에서 나누던 AI 대화를 이어갈 수 있습니다. 히라가나·가타카나와 문법을 살펴보는 기초 학습, 획순을 따라 쓰는 연습, 음성으로 발음을 확인하는 기능도 있습니다.

최근에는 단어·뜻·예문을 차례로 읽어주는 듣기 모드도 추가했습니다. 다른 앱을 쓰거나 화면을 꺼도 재생되고, 반복 횟수와 속도·읽기 간격을 조절할 수 있습니다. 한국어 뜻을 읽는 음성 팩은 한 번 내려받으면 기기 안에서 동작합니다.

AI 어시스턴트

초기 Flutter 버전에서는 기기 안에서 실행하는 모델을 실험했지만, 현재 배포 버전의 AI 어시스턴트는 사용자 본인의 OpenAI 또는 Anthropic(Claude) API 키로 동작합니다. 설정에서 제공사를 고르고 전송에 동의한 뒤 사용하며, API 사용 요금은 해당 키의 계정에 청구됩니다.

일본어에 관해 질문하거나 단어 추가·수정, 단어장 정리를 요청할 수 있습니다. 데이터를 바꾸는 요청은 변경 내용을 먼저 보여주고 사용자가 확인해야 실행합니다. 대화는 기본적으로 기기에 저장하고, 직접 백업을 요청했을 때 서버에 최신 사본을 보관하는 방식입니다. 현재 제공 방식은 App Store 설명과 업데이트 내역에서도 확인할 수 있습니다.

이미지·PDF·텍스트 파일을 첨부해 질문하거나 여러 단어를 한 번에 찾고 정리하는 것도 가능합니다. 사진에서 문자를 읽는 OCR의 기기 처리와, 질문·첨부 자료를 선택한 AI 제공사에 전송하는 경로는 별개입니다.

NiVoca AI에 여행 중 찍은 안내문 사진을 첨부해 단어 추가를 요청한 실제 iPhone 화면
사진 속 단어를 골라 달라고 요청한 실제 앱 화면입니다. AI는 등록 대기 목록을 만들고 저장 전 승인이 필요하다고 안내합니다.
NiVoca 실제 iPhone 앱의 퀴즈 결과와 듣기·AI 질문 진입 화면
직접 촬영한 퀴즈 결과 화면입니다. 복습한 카드와 새 카드, 응답 점수를 확인하고 듣기와 AI 질문으로 이어집니다.

MCP로 외부 AI와 연결하기

앱 안의 AI 채팅 외에, 평소 사용하는 AI 도구에서 NiVoca의 단어와 학습 기록을 다루는 MCP 연결도 만들었습니다. 예를 들어 「최근 퀴즈에서 틀린 단어를 모아서 설명해 줘」라고 요청하거나, 정리한 단어 목록을 새 단어장에 넣는 흐름입니다. 현재는 개발자 본인 계정에 한정한 운영 기능이며, 모든 사용자에게 공개한 기능은 아닙니다.

서버의 MCP 주소는 https://n-api.jun-devlog.win/mcp입니다. 외부 클라이언트가 OAuth 승인을 요청하면 NiVoca 계정으로 로그인하고 접근을 허용합니다. 앱 로그인 토큰과 MCP 토큰은 분리했으며, 연결 목록에서 특정 연결을 끊으면 해당 연결의 접근도 막습니다. 갱신 토큰은 사용하면서 교체하고, 이미 교체한 토큰이 다시 쓰이면 연결을 철회하는 방식입니다.

현재 13개 도구를 제공하며, 크게 다음 작업으로 나뉩니다.

작업활용 예시
퀴즈 기록·오답 조회학습한 날짜와 문제 기록을 읽어 자주 틀리는 표현을 다시 설명하기
내 단어·단어장 조회이미 모아 둔 단어와 단어장을 검색해 학습 범위 정하기
사전 일괄 검색여러 단어의 읽는 법과 뜻을 함께 확인하기
단어 추가·단어장 만들기확인한 사전 항목을 단어장에 담기
가져오기 미리 보기·적용단어 목록의 확정·모호·미발견 항목을 먼저 나누고 확정된 항목만 저장하기
누락 단어 요청사전에 없는 단어를 콘텐츠 보강 경로로 넘기기

단어를 추가할 때는 AI가 보내 온 뜻을 그대로 저장하지 않고, 서버가 사전 항목 ID를 기준으로 읽는 법과 뜻을 채웁니다. 여러 후보가 있는 단어는 가져오기 단계에서 모호한 항목으로 남깁니다. 미리 보기와 실제 적용을 분리한 것도 AI가 추측한 값을 곧바로 내 단어장에 쓰지 않도록 하기 위해서입니다.

Claude Code에서 운영 서버로 실제 연결해 퀴즈 기록과 오답을 조회한 기록이 있습니다. 다른 클라이언트의 연결과 쓰기 작업은 확인 범위가 달라, 모두 검증된 것처럼 소개하지는 않습니다. 앱 안의 BYOK 어시스턴트와 이 MCP는 역할도 다릅니다. 전자는 앱에서 AI 제공사를 호출하고, 후자는 외부 AI가 NiVoca의 제한된 도구를 호출합니다.

오답 조회에서도 평점만 보고 추측하지 않고 당시 문제의 정답·선택한 보기·쓰기 인식 결과를 함께 다룹니다. 초기에는 쓰기 오답이 「어려움」으로 저장되어 일반 오답 집계에서 빠지는 문제가 있었는데, 저장한 정오답 결과를 기준으로 구분하도록 수정했습니다. 판정 정보가 없는 과거 기록은 판정 불명으로 남깁니다.

Safari에서 읽다가 바로 단어 모으기

웹페이지 안에서 검색하고 요미가나 붙이기

별도로 개발한 Safari Web Extension은 읽고 있는 페이지에서 일본어를 선택해 뜻과 요미가나를 확인하는 기능입니다. 앱의 Safari 확장 타깃과 공통 브라우저 확장 소스를 연결했고, 데스크톱 Chrome 계열에서도 공통 소스를 사용합니다. 기존 공유 메뉴의 확장과는 별개입니다.

한 페이지에서 수동으로 켜거나, 접근을 허용한 사이트에서 자동으로 사용하도록 설정할 수 있습니다. 본문에 요미가나를 붙일 때는 표시할 난도를 고르고 「현재 화면에 적용」을 누릅니다. 입력창이나 코드처럼 건드리면 안 되는 영역을 제외하고, 현재 보이는 본문 일부에만 적용하도록 범위를 제한했습니다.

NiVoca 앱 계정과 확장을 연결할 때는 앱에서 연결 코드와 계정을 확인하고 승인합니다. Google·Apple로 가입한 계정도 확장을 쓰려고 새 비밀번호를 만들 필요가 없도록 한 흐름입니다. 선택 검색으로 보낸 글과 본문 요미가나 적용으로 보낸 글의 범위도 구분합니다. 페이지 전체를 백그라운드에서 자동 수집하는 방식으로 설명하면 실제 동작과 다릅니다.

Safari에서는 Native Messaging으로 네이티브 브리지를 거치고, 연결 상태와 세션은 Keychain에서 관리합니다. 페이지의 JavaScript에 앱의 인증 쿠키를 넘기지 않으며, 브리지가 요청할 수 있는 서버와 경로도 제한합니다. Chrome 계열의 서비스 워커 전송과 UI 소스는 공유하되, 브라우저별 인증·네트워크 경계는 나눴습니다.

iPhone Safari에서 선택한 일본어 단어의 뜻과 읽기를 보여주는 NiVoca 확장 카드
읽던 웹페이지에서 「以下」를 선택해 뜻·읽기를 확인하고 단어장에 추가하는 실제 화면입니다.
NiVoca Safari 확장의 자동 사용과 요미가나 난도 설정
웹사이트 자동 사용과 요미가나 표시 범위를 설정합니다.

위 이미지는 개발 중인 확장을 기기에 설치해 확인한 디버그 캡처입니다. 공개 iOS 앱의 기능 목록과 구분해서 보아야 합니다. 설치·계정 연결·검색·전송 범위의 사용 흐름은 브라우저 확장 가이드에도 정리했습니다.

다른 앱의 공유 메뉴에서 가져오기

기존 iOS 공유·Action 확장은 선택한 텍스트나 사진을 NiVoca로 전달합니다. 단어 하나는 사전 검색으로, 긴 일본어 글은 단어 후보 추출로, 사진은 이미지 인식으로 이어지도록 진입 경로를 나눴습니다. Safari 안에서 읽기를 보조하는 Web Extension과 달리, 다른 앱에서 발견한 내용을 단어장 앱으로 가져오는 역할입니다.

학습의 성취감을 이어 주는 게임, Mate — 개발 중

단어를 외운 결과가 복습 통계에만 남지 않고 캐릭터의 성장으로 이어지도록 Mate라는 게임 기능도 개발하고 있습니다. 공부를 계속할 동기를 만들기 위한 시도이며, 현재 App Store에서 제공하는 완성된 게임은 아닙니다.

초기의 펫 구상에서 인간형 캐릭터를 성장시키고 꾸미는 방향으로 발전했습니다. 학습으로 경험치와 성장 자원을 얻고, 그 결과로 탐험하고 장비와 카드를 모으는 흐름을 만들고 있습니다. 공부를 대신하는 미니게임보다는, 학습한 결과를 가지고 다음 탐험을 준비하는 구조입니다.

탐험에서는 전투·상점·미지의 사건 같은 방을 선택하고, 전투에서는 손에 든 카드의 순서와 조합을 정해 행동합니다. 카드별 비용과 적의 다음 행동을 보며 콤보를 준비하는 방식이라, 캐릭터를 얻는 것에서 끝나지 않고 성장시킨 뒤 사용할 곳도 함께 만들고 있습니다.

전투는 카드와 주사위 눈금을 함께 사용하는 구조입니다. 카드를 놓는 칸의 눈금에 따라 효과가 달라지고, 방어·표식·주문·피니셔를 어떤 순서로 배치할지 결정합니다. 실행 전에 예상 피해와 적의 공격이 끼어드는 위치를 보고, 콤보를 확정하면 결과를 재생합니다. 장기 학습으로 얻는 성장과 한 번의 탐험에서 만드는 전투 조합은 나눠, 오래 공부한 수치만으로 모든 전투가 결정되지 않도록 조정하고 있습니다.

개발 중인 NiVoca Mate의 캐릭터와 학습 경험치·성장 스탯 화면
학습 경험치와 성장 스탯을 표시한 개발용 고정 데이터 렌더입니다.
직접 촬영한 개발 중 NiVoca Mate의 달의 유적 전투와 콤보 준비 화면
추가 디버그 자료에서 고른 실제 iPhone 전투 화면입니다.
직접 촬영한 개발 중 NiVoca Mate의 탐험 경로와 방 선택 지도
방문한 방과 다음 경로를 확인하는 개발 앱의 탐험 지도입니다.

첫 이미지는 고정 예시 데이터로 렌더했고, 나머지 두 이미지는 직접 촬영한 개발 앱 화면입니다. 정확한 촬영 빌드는 확인하지 못했으며, 화면의 수치와 밸런스도 출시 사양은 아닙니다. 실제 Flutter 화면과 픽셀 아트 에셋을 맞추며 캐릭터·전투·탐험 화면을 다듬는 단계입니다.

구현은 mate_feature라는 Flutter 기능 패키지로 분리했습니다. 캐릭터 장면에는 Flame을 쓰고 화면과 전투 UI에는 Flutter 위젯·CustomPainter를 함께 사용합니다. 서버가 탐험 진행과 보상을 판정하고, 학습 완료 요청에는 고정된 학습 목록과 요청 식별자를 사용해 재시도 때문에 보상을 두 번 주지 않도록 했습니다. 듣기를 중간에 멈췄거나 TTS 오류로 끝난 경우와 정상 완주도 구분합니다.

2026년 11월 4일 기준 stage 반영과 테스트 앱 설치·실행 기록이 있습니다. 다만 전체 탐험의 실기기 플레이와 재미·밸런스 확인, 최종 성장 정책은 계속 검토 중이며 App Store 출시는 별도입니다.

데이터셋은 어떻게 확보했는가

단어장 UI를 만드는 것과, 공부할 수 있는 사전·뜻·예문을 채우는 일은 별도의 작업이었습니다. NiVoca는 외부 공개 데이터로 만든 기초 사전, AI로 번역·보강한 내용, 직접 작성하거나 검수한 내용을 함께 사용합니다. 전부 처음부터 직접 작성한 사전이나, 전부 AI가 생성한 데이터로 소개하면 둘 다 실제와 다릅니다.

자료확보 경로와 앱에서 쓰는 내용
일본어 사전JMdict/EDRDG의 데이터를 jmdict-simplified JSON 형태로 가져와 표기·읽기·품사·뜻을 구성
검색 시 보강Jisho 검색 결과를 확인해 캐시하고, 같은 단어라도 표기와 읽기가 맞는 후보인지 검증
JLPT 기본 단어장Tanos에서 유래한 JLPT 목록을 가져와 레벨별 기초 단어와 단어장 구성
한자KANJIDIC2/EDRDG를 기반으로 음독·훈독·획수 등 한자 정보 구성
획순KanjiVG의 SVG와 획 순서를 가나·한자 안내와 쓰기 연습에 활용
문법Hanabira의 공개 일본어 콘텐츠를 바탕으로 문법 항목을 정리하고 한국어 설명 보강
일본사·문화 어휘Jisho/JMdict와 일본어 Wikipedia의 표기·읽기 근거를 대조한 뒤 주제별로 큐레이션
한국어 뜻·예문원천 데이터의 뜻을 번역·보강하고 학습용 예문을 AI로 생성한 뒤, 직접 작성한 자료와 검수·정정 작업을 함께 반영

가져온 뒤에도 정규화와 보강이 필요했다

사전에서 단어 하나를 가져왔다고 바로 학습 카드가 완성되지는 않습니다. 같은 표기의 다른 읽기를 구분하고, 품사와 JLPT 레벨을 맞추고, 한국어 뜻과 예문을 채워야 합니다. 읽기나 뜻이 여러 개인 항목을 하나로 뭉개면 검색은 편해도 학습 데이터가 부정확해질 수 있습니다.

이미 사전에 있는 항목은 기존 데이터를 재사용하고, 부족한 내용은 별도 콘텐츠 생성·보강 경로로 채웁니다. AI가 만든 내용도 원천 자료와 구분해 추적합니다. 특정 주제의 단어장을 만들 때도 단어 목록과 사전 항목을 먼저 대조하고, 새 항목이 필요한 경우에만 보강하는 방식입니다.

검색은 DB의 기존 항목을 우선 확인하고, 필요한 경우 Jisho에서 정확히 일치하는 표기·읽기를 찾은 뒤 AI 보강으로 이어집니다. 외부 응답을 기다리는 동안 DB 잠금이나 연결을 계속 잡지 않도록 네트워크 호출과 저장 트랜잭션도 분리했습니다.

예문을 채우는 경로도 하나가 아닙니다. 에이전트가 빈 항목을 작성하는 루틴, Claude API로 처리하는 대량 배치, 서버의 비동기 자동 보강을 따로 운영하며 출처를 남깁니다. 자동 보강은 이미 채워진 내용을 다시 덮어쓰지 않고, 작업 중복·예산·재시도 조건을 확인합니다. 신고를 받아 기존 내용을 고치는 정정 작업은 이런 추가용 경로와 구분합니다.

예문과 주제별 단어장은 꾸준히 보충하기

2026년 11월 1일 작업 기록에는 새 예문 11,630행과 기존 예문 수정 2,138행을 반영한 내역이 있습니다. 11월 2일에는 일본사 725개, 일본 문화 255개의 단어를 묶은 단어장을 추가했습니다. 이 수치는 해당 작업의 기록으로, 사전 전체의 현재 규모를 의미하지 않습니다.

AI로 대량 생성할 수 있다고 해서 곧바로 정확한 학습 자료가 되는 것은 아니었습니다. 예문 속 일본어와 번역이 맞는지, 단어의 읽기와 실제 쓰임이 자연스러운지 확인하고, 잘못된 내용은 신고와 별도 정정 작업으로 다시 손보고 있습니다.

예문 배치에는 단어의 뜻·품사·동형이의어 정보를 함께 주고, 확신이 없는 항목은 건너뛰게 했습니다. 생성 후에는 표제어 포함 여부, 읽기에 쓰인 문자, 번역과 중복·길이를 검사합니다. 기존 예문 정정은 오류 후보를 찾는 단계와 별도 재검토 단계를 나눴습니다. 「心中」처럼 표기는 같아도 읽기와 뜻이 다른 단어는 문장 자체가 그 의미에 맞는지도 살펴봐야 했습니다.

데이터 출처도 앱 안에서 확인하기

외부 데이터와 코드 라이브러리의 출처는 따로 관리합니다. 앱의 「설정 → 정보 → 데이터 출처」에 사전·한자·JLPT·문법의 원천과 이용 조건을 표시했습니다. 공개 저장소에 들어 있는 자료라고 해서 코드와 콘텐츠의 조건이 같다고 가정하지 않고, 데이터별 출처를 나누어 기록했습니다.

기술 스택

영역사용 기술
모바일 앱Flutter, Dart
앱 구조MVVM, Repository, Riverpod, go_router
UIThemeData·ThemeExtension 기반 자체 테마, Widgetbook
백엔드Kotlin, Spring Boot, PostgreSQL
복습FSRS 간격 반복
MCPMCP Java SDK, Streamable HTTP, OAuth·PKCE
브라우저 확장TypeScript 공통 소스, Safari Web Extension·Native Messaging, Chromium Manifest V3
개발 중인 게임Flutter 기능 패키지, Flame, CustomPainter, Kotlin 탐험·전투 엔진
OCR·음성Apple Vision·ML Kit, OS TTS·VOICEVOX·Supertonic, Dart FFI
소개 사이트Astro
서버 운영Docker, GitHub Actions, Cloudflare Tunnel

Flutter UI는 웹에서 쓰던 컴포넌트 라이브러리를 그대로 대체하는 대신, ThemeData와 ThemeExtension 위에 앱의 색상·타이포그래피·컴포넌트를 맞추는 방식으로 구성했습니다. 공통 UI는 Widgetbook에서 따로 확인합니다.

앱, 백엔드, 소개 사이트는 각각의 저장소로 관리하고 모노레포에서 서브모듈로 연결합니다. 앱 화면만 바꾸는 작업과 사전·학습 데이터를 다루는 서버 작업을 구분하면서 운영하고 있습니다.

운영하면서

단어장 앱은 기능을 추가하는 것만큼 사전 내용과 문제의 정확성을 유지하는 일이 중요했습니다. 뜻이나 예문이 어색하면 학습 자체에 영향을 주고, 빈칸 퀴즈의 오답 보기가 정답과 비슷하면 문제를 풀어도 찜찜해집니다.

정답이 둘처럼 보이는 문제 줄이기

빈칸 채우기에서는 정답과 같은 의미 분야의 단어가 오답 보기로 섞이지 않도록 조정했습니다. 적절한 보기를 충분히 만들지 못하면 억지로 객관식 문제를 내는 대신 단어 카드로 전환합니다. 단어를 학습 대상에서 빼지 않으면서, 출제하기 곤란한 형식만 바꾸는 선택입니다.

기기 기능과 서버 데이터를 나눠 다루기

OCR과 내려받은 음성 모델은 기기에서 실행하지만, 단어와 학습 기록은 서버에 저장하고 AI 질문은 선택한 제공사로 보냅니다. 기능마다 처리 위치와 저장 수명이 달라, 앱 전체를 완전한 오프라인 서비스로 취급하지 않았습니다. 대화의 로컬 보관과 수동 백업도 이런 구분 안에서 설계했습니다.

음성은 일본어의 OS TTS·VOICEVOX와 한국어의 OS 음성·Supertonic을 목적에 맞게 나눴습니다. 한국어 음성 팩을 붙일 때는 ONNX 런타임을 하나 더 싣는 대신 기존 VOICEVOX 쪽 런타임의 C API를 Dart FFI로 재사용해 중복을 줄였습니다.

저장 응답이 끊겨도 복습을 두 번 처리하지 않기

후속 개발 버전에서는 복습 요청에 안정된 식별자를 붙이고, 전송 결과를 확인하지 못한 요청을 계정별 대기 목록에 남깁니다. 재시도할 때 새 복습을 하나 더 만드는 대신 원래 요청을 확인해 FSRS 상태가 두 번 진전되지 않도록 하는 방식입니다. 잘못 누른 평가를 정정하는 경로도 분리했습니다. 이 변경은 공개 iOS 1.7.0과 구분되는 후속 구현입니다.

오류를 고칠 수 있는 경로 남기기

단어 상세뿐 아니라 사전 검색 결과, 퀴즈 정답 확인, 한자 정보에서도 잘못된 내용을 신고할 수 있습니다. 뜻·예문·훈음·요미가나처럼 오류가 난 부분을 구분해서 받고, 확인한 내용만 정정합니다. 생성 모델을 바꾸는 것만으로 기존 데이터까지 자동으로 정확해지는 것은 아니기 때문에, 이미 저장된 내용을 다시 검수하는 작업도 계속 필요했습니다.

처음에는 단어 입력을 편하게 하려고 시작했는데, 지금은 단어를 만나는 순간부터 모으고, 공유하고, 다시 공부하는 과정 전체를 직접 쓰면서 다듬는 프로젝트가 되었습니다.