학생들의 수학 취약점을 진단하고, 맞춤 학습을 제공하여 수학 실력 향상을 돕는 서비스
수학 개념의 선후 관계 그래프 탐색을 Neo4j → MySQL 재귀 CTE 로 옮긴 마이그레이션. v1 시기 도입했던 그래프 DB 가 1,631 노드 / 3,446 엣지 규모에서는 오히려 오버 엔지니어링이라 판단해 단일 RDB 로 통합.
- 응답 시간 p95 14.034 ms → 0.556 ms (약 25× 개선) — 깊이 3 그래프 탐색 기준
- 결과 정확성·시각화 안전성·알고리즘 의미 보존을 자동화 테스트로 검증 (회귀 15 케이스 + 성능 6 케이스 + DTO 안정성 4 케이스 + 거리 맵 의미 4 케이스)
- 데이터 중복 · 리액티브 안티패턴(
.block()) · 인프라 복잡도를 동시 해소 - 피처 플래그 + 결과 스냅샷 비교 기반 롤백 가능 마이그레이션
- 작업 중 알고리즘 함정(옛 BFS 유틸의 path-order 가정 → CTE 결과와 비호환) 을 사전 발견해 silent regression 방지
자세한 회고: docs/reports/m2-cte-migration.md (읽는 데 5~7 분)
핵심 의사결정 기록 (ADR):
- 0001 — 마이그레이션 전 테스트 커버리지 선행 구축 (정답지·롤백·피처 플래그 우선)
- 0003 — 캐싱 패턴: Spring Cache 추상화 미도입 (사용처 5개 → 직접 호출이 더 명확)
- 0005 — CTE 객체 매핑 정책 (사용처 기반 정합화)
단일 EC2(t3.micro · 1 GiB) 위에서 Spring Boot 백엔드를 재배포할 때 발생하던 다운타임(502 · 요청 유실)을 blue-green 으로 제거. 쿠버네티스 · ECS · ALB 같은 큰 전환 없이 기존 프론트 nginx 를 전환 지점으로 재사용했다.
- 재배포 중 요청 유실 60.3% → 0% — 부하(k6 constant-arrival-rate)를 계속 쏘며 배포. 측정 대상은
permitAll+ DB 왕복이 있는 대표 GET(sub-ms/health단독 측정은 드레인 경계의 502 를 못 드러내는 거짓 확신이라 배제). in-place stop→run 대조군 60.3% vs blue-green 502·transport_err 0. - 원자적 전환: 신버전 health 통과 →
nginx -s reload로 upstream 을 한 번에 넘김 → 구버전 graceful drain(server.shutdown=graceful, 30s). "어느 순간에도 트래픽 받는 정상 백엔드 최소 1개"를 보장. - 핵심 병목은 메모리가 아니라 CPU 였다 — 전환 구간 JVM 2개 공존 시 신버전 부팅이 1 vCPU 를 148%까지 점유해 구버전 응답이 밀림(blue-green 만으로도 컷오버에서 잔여 11.3% 유실).
docker run --cpus=0.5로 부팅 CPU 를 55%로 억제해 잔여 유실까지 제거(11.3% → 0%). - 배포 채널을 SSH-from-runner → AWS SSM Run Command 로 전환 — 러너에 SSH 인바운드를 열지 않고 GitHub OIDC 단기 자격으로 배포(장기 AWS 키 폐기).
- 프로비저닝을 Terraform IaC 로 — EC2/RDS/EIP/SG 18 리소스를
apply → 측정 → destroy사이클로 짧게 열고 닫음(크레딧 방어). 비가역 지점(계정·MFA·apply)만 사람 게이트, flip-back 판정도 사람 직감이 아닌 유실 grader(계측 게이트).
핵심 의사결정 기록 (ADR):
- 0007 — blue-green 무중단 배포 (프론트 nginx 재사용 · 원자적 reload · graceful drain)
- 0008 — 배포 채널 SSH → SSM Run Command (러너 SSH 제거 · GitHub OIDC 단기 자격)
기간 : (v1) 2023.12 ~ 2024.07 (8개월), (v2) 2025.02 ~ (진행 중)
개발 인원 : 1인 개발
서비스 링크 : https://www.my-math-teacher.com
MMT는 수학 지식 간 선/후 관계를 그래프로 확인하고 수학 취약점을 진단하고 맞춤학습을 제공하여 학생들의 수학 실력 향상을 돕는 서비스입니다.
수학은 위계가 강한 학문이라 이전 지식의 이해도가 다음 지식의 학습에 영향을 미칩니다. 즉, 수학 실력 향상을 위해 선수지식을 잘 알고 있는지 파악하는 것도 중요합니다. 따라서 지식 간 선후 관계를 파악하고 취약점을 찾아내는 것은 수학 공부의 효과적인 방법입니다.
- 고등학교 수학을 공부하는데, 중학교 때 배운 개념들이 필요해서 힘들어!
- 내가 뭘 알고 있고, 무엇을 모르는지 명확히 알고 싶어!
- 나에게 딱 맞는 문제들을 풀어서 수학 실력을 키우고 싶어!
(맨 위로)
| 분류 | 기술 |
|---|---|
| Frontend | |
| Backend | |
| Database | |
| AI | |
| Infrastructure/DevOps |
(맨 위로)
docker-compose로 인프라(MySQL / Neo4j / Redis)만 띄우고, 백엔드와 프론트는 호스트에서 직접 실행하는 구성입니다.
- Docker & Docker Compose
- Java 17
- Node.js 20 이상
아래 두 파일은 자격증명을 포함해 .gitignore로 제외되어 있습니다. 팀 내부에서 공유받거나 기존 환경을 참고해 루트에 작성합니다.
docker-compose.yml— 인프라 컨테이너 설정 (MySQL/Neo4j/Redis 비밀번호 등)api/src/main/resources/application-securelocal.yml— Spring DataSource·Neo4j·Redis 접속 정보
두 파일의 자격증명은 서로 일치해야 합니다.
docker compose up -d mmt-mysql mmt-neo4j mmt-redis호스트에서 각각 3306 / 7474·7687 / 6379 포트로 접근할 수 있습니다.
MySQL — FK 제약 때문에 아래 순서대로 import해야 합니다.
cd api/sql
for f in create.sql insert_chapters.sql insert_concepts_latex.sql \
insert_concepts_sections.sql insert_knowledge_space.sql \
insert_diag_tests.sql insert_diag_items.sql insert_diag_testsitems.sql \
update_diag_answers.sql insert_users.sql; do
docker compose exec -T -e MYSQL_PWD=<mmt2024_비번> mmt-mysql \
mysql -u mmt2024 --default-character-set=utf8mb4 mmt < "$f"
done
--default-character-set=utf8mb4가 없으면 한글 컬럼에서Data too long에러가 납니다.- 개념(concepts) 데이터는 반드시
insert_concepts_latex.sql을 사용합니다 (작은따옴표 escape 처리된 버전).
Neo4j — CSV를 로드합니다.
docker compose exec -T mmt-neo4j \
cypher-shell -u neo4j -p <neo4j_비번> < neo4j-deprecated/init/init.cypher적재 후 MySQL concepts / knowledge_space row 수와 Neo4j 노드 / 관계 수가 일치하면 정상입니다.
# 백엔드 (securelocal 프로파일이 자동 활성화)
cd api && ./gradlew bootRun
# 프론트
cd web && npm install && npm run dev- 백엔드: http://localhost:8080
- 프론트: http://localhost:5173
docker compose down볼륨(mysql-vol, neo4j-vol)은 유지되므로 다음 실행 시 초기 데이터 적재를 반복할 필요가 없습니다.
(맨 위로)
(맨 위로)






