dev.to

I Built EchoCadence: A Vocabulary App Built to Make Words Stick 🔁

EchoCadence를 만들었습니다 — 단어를 오래 기억하게 하는 어휘 앱

EchoCadence는 사용자가 직접 어휘를 추가하고, 복습 난이도에 따라 다음 복습 시점을 정하는 spaced repetition 기반 앱입니다. 개발자는 복습 이력을 TimescaleDB hypertable로 저장하고 continuous aggregate로 일별 통계를 만들어, PostgreSQL 모델 안에서 학습 데이터를 시계열로 다루는 방법을 구현했습니다.

AI 요약

EchoCadence는 단어의 뜻을 한 번 찾아보는 것과 실제로 나중에 그 단어를 사용하는 것은 다르다는 문제에서 출발한 어휘 학습 앱입니다. 개발자는 영어를 유창하게 구사하면서도 같은 단어를 반복해서 찾아보는 경험을 했고, 종이 플래시카드를 만들어도 카드가 계속 쌓여 실제 복습으로 이어지지 않는다는 한계를 겪었습니다. 그래서 MLH Global Hack Week: Data에 맞춰, 직접 단어를 추가하고 복습하며 알고리즘이 다음 복습 시점을 정하는 spaced repetition 애플리케이션 EchoCadence를 혼자 구축했습니다.

■ 학습 흐름과 기능 구성

사용자는 Google 또는 이메일로 가입한 뒤 지원되는 언어를 선택합니다. 최대 7개 언어를 동시에 다룰 수 있으며, 기본 제공 카테고리를 사용하거나 직접 카테고리와 필드를 만들 수 있습니다. 새 단어를 학습할 때는 카드를 뒤집어 답을 확인하고, 얼마나 어려웠는지 평가합니다. 이후 이전 복습 이력에 따라 복습 기한이 된 카드를 다시 확인합니다. 주간 복습 통계, 카드 난이도, 다음 복습 날짜, 첫 시도 성과도 확인할 수 있습니다.

앱은 새 어휘가 시스템에 들어오는 Learn과 반복 학습이 일어나는 Revise를 의도적으로 분리합니다. 카드를 복습하면 해당 카드의 interval과 ease 같은 scheduling information이 갱신되고, 이 상태가 다음 카드 노출 시점을 결정합니다. 목표는 모든 단어를 매일 복습하게 만드는 것이 아니라, 계속 늘어나는 어휘 목록을 사용자가 실제로 따라갈 수 있는 복습 일정으로 바꾸는 것입니다.

■ AI 대신 SM-2 스타일 알고리즘 사용

EchoCadence는 학습자가 다음에 무엇을 공부할지 AI API나 recommendation model로 결정하지 않습니다. 핵심 scheduling logic은 고전적인 spaced-repetition 접근법을 따르는 SM-2-style algorithm입니다. 각 카드는 ease factor와 review interval 같은 scheduling state를 유지하며, 사용자가 제출한 난이도 평가가 이 상태를 바꿉니다. 어려웠던 카드는 더 빠르게 다시 나타나고, 꾸준히 쉬웠던 카드는 더 긴 간격을 갖게 됩니다.

이 구조에서 중요한 부분은 단어 자체보다 각 카드에서 어떤 일이 일어났는지를 저장하는 데이터 모델입니다. 시스템은 카드가 언제 복습됐는지, 어떤 사용자가 복습했는지, 정답이었는지, 사용자가 난이도를 어떻게 평가했는지, response-related metrics가 무엇이었는지, 다음 복습을 위해 scheduling state가 어떻게 바뀌었는지를 기억해야 합니다. 개발자는 이 복습 기록이 시간이 흐르면서 계속 쌓이는 chronological record라는 점에 주목했습니다.

■ PostgreSQL 테이블을 TimescaleDB hypertable로 전환

Tiger Cloud는 TimescaleDB 기능이 포함된 managed PostgreSQL 환경을 제공했습니다. EchoCadence는 애플리케이션 전체를 별도의 time-series database에 맞춰 다시 설계하지 않고, 기존 PostgreSQL 연결 방식과 SQL을 그대로 유지하면서 시간이 중요한 부분에만 TimescaleDB 기능을 적용했습니다.

복습 이벤트는 card_reviews 테이블에 저장합니다. 이 테이블을 먼저 일반 PostgreSQL 테이블로 만든 다음 다음 명령으로 hypertable로 변환했습니다.

SELECT create_hypertable( 'card_reviews', 'time', if_not_exists => TRUE );

여기서 핵심은 time 컬럼입니다. 애플리케이션 관점에서 card_reviews는 여전히 PostgreSQL 테이블처럼 동작하므로 백엔드는 기존처럼 SQL을 사용해 복습 기록을 삽입하고 조회할 수 있습니다. TimescaleDB는 내부적으로 hypertable을 시간 기반 chunk로 구성해 관리합니다. 따라서 개발자는 애플리케이션을 완전히 다른 데이터베이스 프로그래밍 모델에 맞추지 않고도, 시간이 지나며 자연스럽게 증가하는 학습 활동 로그를 시계열 데이터처럼 다룰 수 있었습니다.

개발자가 설명한 설계의 핵심은 “애플리케이션을 time-series database에 억지로 맞추는 것”이 아니라, 애플리케이션 안에서 이미 시간순 데이터인 부분을 찾아내는 것입니다. 학습자가 보는 것은 vocabulary이지만, 애플리케이션이 기억해야 하는 것은 review history입니다. EchoCadence에서는 이 복습 이력이 hypertable을 적용할 자연스러운 대상이 됩니다.

■ Continuous aggregate로 일별 복습 통계 생성

두 번째 MLH Global Hack Week: Data 과제는 continuous aggregate를 사용해 대시보드를 가속하는 것이었습니다. 개발자는 card_reviews hypertable을 기반으로 daily_review_stats라는 continuous aggregate를 만들었습니다.

CREATE MATERIALIZED VIEW daily_review_stats WITH (timescaledb.continuous) AS SELECT user_id, time_bucket('1 day', time) AS day, COUNT(*) AS total_reviews, SUM(CASE WHEN correct THEN 1 ELSE 0 END) AS correct_count, SUM(CASE WHEN correct THEN 0 ELSE 1 END) AS incorrect_count, AVG(response_time) AS avg_response_time, AVG(rating) AS avg_rating FROM card_reviews GROUP BY user_id, day WITH NO DATA;

이 aggregate는 개별 복습 이벤트를 매번 다시 훑는 대신, user_id와 하루 단위 bucket을 기준으로 데이터를 묶습니다. 그 결과 사용자별 총 복습 수, 정답 수, 오답 수, 평균 응답 시간, 평균 난이도 평가를 계산할 수 있습니다. 여기서 WITH (timescaledb.continuous)가 continuous aggregate를 지정하고, time_bucket('1 day', time)이 복습 이벤트를 일별 구간으로 나눕니다.

개발자는 두 과제가 EchoCadence의 실제 요구와 자연스럽게 연결됐다고 설명합니다. hypertable은 계속 증가하는 복습 이벤트 스트림을 저장하고, continuous aggregate는 그 이벤트를 시간 기반 분석 요약으로 바꿉니다. 따라서 시간에 따라 쌓이는 원본 이벤트와 화면에 표시할 일별 통계를 서로 다른 역할로 나누면서도 같은 PostgreSQL·TimescaleDB 기반 안에서 처리합니다. 다만 글에서는 이 구조에 대한 성능 수치나 기존 방식과의 벤치마크 결과를 제시하지는 않습니다.

■ 인증과 애플리케이션 구성

인증에는 Auth0를 사용했습니다. Auth0는 authentication, session persistence, 첫 로그인 시 username 설정을 담당합니다. 프런트엔드는 Auth0Provider로 감싸고, 인증된 API 요청에는 사용자의 token을 Authorization header에 담아 보냅니다. 여기에 15분 동안 활동이 없으면 세션을 종료하는 client-side inactivity timeout도 추가했으며, 사용자 활동이 발생하면 타이머를 초기화합니다.

개발자는 Global Hack Week: Data의 두 과제를 수행하며 Tiger Cloud의 managed database 환경과 연결 정보를 활용했습니다. 일주일짜리 해커톤 동안 데이터베이스 인프라를 직접 구성하고 유지하는 대신 애플리케이션과 데이터 모델에 집중할 수 있었고, SQL을 계속 사용하면서 필요한 곳에만 create_hypertable, time_bucket, continuous aggregate를 적용했습니다. EchoCadence는 아이디어 구상부터 구현, 데이터베이스 설계, 배포까지 혼자 진행한 프로젝트이며, 개발자는 학습 중이던 SQL과 data modeling을 실제 애플리케이션에 적용한 점을 주요 결과로 설명합니다.

원문: dev.to / 번역·요약: Trawling