JK
프로젝트
JK
>로딩 중 // 로딩 중
뷰 초기화 중
제작 및 디자인:JK
JK
>로딩 중 // PROJECT DETAILS
PARSING ARCHITECTURE, DIAGRAMS & API ENDPOINTS
프로젝트 목록으로 돌아가기
GitHub에서 보기
AI 기반 학생 학습 진단 시스템
In Progressreacttypescriptvitetailwindgoginpostgres

AI 기반 학생 학습 진단 시스템

Go, PostgreSQL, Redis, Docker로 개발되어 고유 SQI 엔진을 통해 학습 진단을 제공하는 멀티테넌트 SaaS 솔루션.

문제 및 해결책

교육 기관에서 학생의 취약점을 조기에 자동 진단하고, 감독 기능과 구독 결제, 대규모 비동기 평가를 통합 처리할 솔루션이 부재했습니다.

기능

  • SQI(학습 품질 지수) 진단 분석 엔진
  • 학원 맞춤형 멀티테넌트 관리 시스템
  • 자동 동영상 감독 및 이상 징후 감지
  • Redis 대기열을 활용한 비동기 진단서 생성
  • Stripe 연동 정기 결제 및 구독 관리
  • 역할 기반 접근 제어(관리자, 강사, 학생)

기술 스택

기술버전목적선택 이유
Go1.25.1Backend languageStrong concurrency, fast compilation
Gin1.12.0HTTP frameworkHigh performance, mature middleware ecosystem
PostgreSQL12+Primary databaseJSONB for flexible analysis storage, pg_trgm for fuzzy search
React19.2.4Frontend SPAModern hooks-based architecture
TypeScript~6.0.2Type-safe JSCompile-time safety
Vite8.0.4Build tool/dev serverFast HMR, ESM-native
Tailwind CSS4.2.2StylingUtility-first, no CSS-in-JS runtime
Redis9.7.0 (go-redis)Cache, queues, rate limitingOptional; degrades gracefully without it
JWT (golang-jwt)5.3.1Auth tokensStateless auth
lib/pq1.12.3PostgreSQL driverMature, standard library

아키텍처

Student Exam UI

Admin/Coach Dashboard

Super Admin Portal

Gin HTTP Router

Auth Middleware

JWT

Rate Limiter

Quota Middleware

Handler Layer

Service Layer

Repository Layer

19 Repos

SQI v2 Engine

Sweeper

30s

Autosave Buffer

Job Queue

PostgreSQL

Redis

Optional

Cloudinary

Optional
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

Frontend (React SPA)

  • React 19 + Vite + TypeScript
  • TailwindCSS + Radix UI + shadcn components
  • Student exam UI with answer tracker and timer
  • Admin/coach dashboards with SQI analytics
  • Super admin portal for tenant management

Backend (Go Monolith)

  • Go 1.25 + Gin HTTP framework
  • Layered architecture: Handler → Service → Repository
  • 19 repositories, 10 services, multiple handlers
  • SQI v2 engine as pure functions (no DB dependency)
  • JWT authentication with role-based middleware

Data & Infrastructure

  • PostgreSQL 12+ with 22+ tables, JSONB for analysis storage
  • Redis (optional): job queue, rate limiting, autosave buffer, live view pub/sub
  • Cloudinary or local filesystem for video chunk storage
  • golang-migrate for auto-applied database migrations

Background Workers

  • Sweeper: 30s ticker auto-submits expired exam attempts
  • Autosave buffer: Redis-backed batched writes (1s interval, max 200)
  • Job queue: in-process or Redis Streams with XAUTOCLAIM recovery

프로젝트 구조

backend
cmd
api
main.goEntry point: config → migrations → DB → router → graceful shutdown
internal
auth
handler
helper
liveview
middleware
queue
repository
services
frontend
src

API 엔드포인트

Go API Server (포트 8080)

메서드경로목적
POST/auth/loginUser login (admin/coach)
POST/student/loginStudent login (student_code)
POST/student/assignments/:id/startStart exam attempt
POST/student/assignments/:id/submitSubmit exam answers
POST/student/assignments/:id/autosaveAutosave answers
POST/student/assignments/:id/video-chunkUpload video chunk
GET/admin/testsList tests
POST/admin/testsCreate test
POST/admin/assignmentsCreate assignment
POST/admin/sqi/computeCompute SQI for attempt
POST/admin/sqi/compute-batchBatch SQI computation
GET/admin/students/:id/sqiGet student SQI metrics
GET/super-admin/tenantsList tenants
POST/super-admin/plansCreate subscription plan
GET/healthHealth check (PostgreSQL ping)

데이터베이스 설계

tenants

4 cols
idPK
SERIAL
name
VARCHAR(100)
created_at
TIMESTAMP
suspended_at
TIMESTAMP

users

4 cols
idPK
SERIAL
tenant_idFK
INT FK
email
VARCHAR(100)
role
VARCHAR(20)

coaches

4 cols
idPK
SERIAL
tenant_idFK
INT FK
user_idFK
INT FK
name
VARCHAR(100)

students

5 cols
idPK
SERIAL
tenant_idFK
INT FK
coach_idFK
INT FK
student_code
VARCHAR(50)
name
VARCHAR(100)

tests

5 cols
idPK
SERIAL
tenant_idFK
INT FK
title
TEXT
coach_idFK
INT FK
duration
INT

questions

6 cols
idPK
SERIAL
test_idFK
INT FK
question_text
TEXT
correct_answer
CHAR(1)
difficulty
CHAR(1)
concept_tag
VARCHAR(100)

assignments

5 cols
idPK
SERIAL
student_idFK
INT FK
test_idFK
INT FK
coach_idFK
INT FK
status
VARCHAR(20)

attempts

5 cols
idPK
SERIAL
assignment_idFK
INT FK
started_at
TIMESTAMP
submitted_at
TIMESTAMP
status
VARCHAR(20)

answer_logs

6 cols
idPK
SERIAL
attempt_idFK
INT FK
question_idFK
INT FK
selected_answer
TEXT
is_correct
BOOLEAN
time_spent
FLOAT

attempt_results

5 cols
idPK
SERIAL
attempt_idFK
INT FK
sqi_score
FLOAT
analysis_json
JSONB
version
VARCHAR(10)
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

tenants

컬럼유형비고
idPKSERIALAuto-generated primary key
nameVARCHAR(100)Organization/institute name
created_atTIMESTAMPDefault NOW()
suspended_atTIMESTAMPNull if active
plan_idINTFK to subscription_plans (reference only)

Multi-tenant root entity. Every other table has tenant_id FK with CASCADE delete.

users

컬럼유형비고
idPKSERIALAuto-generated primary key
tenant_idINTFK → tenants CASCADE
emailVARCHAR(100)UNIQUE NOT NULL
passwordTEXTbcrypt hashed
roleVARCHAR(20)CHECK super_admin/admin/coach

Students do NOT have user accounts; they authenticate via student_code.

students

컬럼유형비고
idPKSERIALAuto-generated primary key
tenant_idINTFK → tenants CASCADE
coach_idINTFK → coaches CASCADE
student_codeVARCHAR(50)Auth credential (no password)
nameVARCHAR(100)Student display name
batch_idINTFK → batches (optional)

Partial unique index on (tenant_id, student_code) WHERE deleted_at IS NULL.

tests

컬럼유형비고
idPKSERIALAuto-generated primary key
tenant_idINTFK → tenants CASCADE
titleTEXTTest title
subject_idINTFK → subjects
coach_idINTFK → coaches CASCADE
durationINTDuration in seconds
subject_nameVARCHAR(100)Denormalized for SQI display

subject_name denormalized in migration 6 to avoid JOIN for SQI.

questions

컬럼유형비고
idPKSERIALAuto-generated primary key
test_idINTFK → tests CASCADE
question_textTEXTNOT NULL
correct_answerCHAR(1)CHECK A/B/C/D
marksFLOATPositive marks
neg_marksFLOATNegative marks
importanceVARCHAR(10)high/medium/low
difficultyCHAR(1)E(asy)/M(edium)/H(ard)
expected_timeFLOATExpected time in seconds
concept_tagVARCHAR(100)Concept grouping

MCQ questions with metadata for SQI weight computation.

attempt_results

컬럼유형비고
idPKSERIALAuto-generated primary key
attempt_idINTUNIQUE FK → attempts CASCADE
sqi_scoreFLOATOverall SQI score
raw_scoreFLOATRaw marks scored
analysis_jsonJSONBFull DiagnosticPayloadV2
versionVARCHAR(10)Default 'v2'

SQI analysis stored as JSONB — schema evolves without migrations.

tenants → users/coaches/students/subjects/tests/batches (1:N, tenant_id FK CASCADE). coaches → students (1:N). tests → questions (1:N CASCADE). students → assignments → attempts → answer_logs → attempt_results. 22+ tables total across 18 migrations.

주요 설계 결정

Multi-tenant with tenant_id FK Pattern

결정: Every table has tenant_id FK with CASCADE delete instead of schema-per-tenant

이유: Simpler than schema-per-tenant; adequate for expected scale

이점: Single database, simple queries, easy cross-tenant analytics

트레이드오프: Must include tenant_id in every query; no physical data isolation

Students Authenticate via student_code (No User Account)

결정: Students login with student_code only, no password or user account

이유: Students are numerous, short-lived, and don't need full user accounts

이점: Simplified onboarding, no password management

트레이드오프: Weaker auth (code-only), no email/password recovery

SQI Engine as Pure Functions

결정: SQI v2 engine has no DB dependencies; takes inputs, returns payload

이유: Separation of analytics from data access; testable in isolation

이점: Unit testable, deterministic, reusable

트레이드오프: Requires data to be fetched and assembled before calling

JSONB for Analysis Storage

결정: Store full DiagnosticPayloadV2 as JSONB in attempt_results

이유: Schema evolves faster than migrations; analysis payload is read-heavy, write-once

이점: No migration needed for analysis schema changes; flexible querying

트레이드오프: No referential integrity on nested data; harder to query specific fields

Optional Redis with Graceful Degradation

결정: Redis is optional — in-process queue and in-memory rate limiter as fallbacks

이유: Development simplicity; single-binary deployment possible

이점: Works without Redis in development; scales with Redis in production

트레이드오프: In-process queue loses jobs on restart; in-memory rate limiter isn't shared

Video Proctoring with Lazy Merge

결정: ffmpeg concat on first stream request, not on upload

이유: Most videos are never watched; avoid wasted compute

이점: Saves storage and compute for unwatched recordings

트레이드오프: First viewer experiences delay; requires ffmpeg on server

테스트

Go standard testing + testify (backend), None (frontend)•단위 테스트 8개

테스트 항목

  • Queue: in-process dispatches compute and finalize correctly
  • Quota: free tier blocks SQI/proctoring (402), within limits allows (200)
  • Plan repository: full CRUD cycle, nil-features regression
  • Subscription repository: subscription operations
  • Notification repository: unread count, mark read, delete, preferences, cross-tenant isolation
  • Notification service: fan-out respects disabled preferences
  • Assignment service: storage guard on free plan
  • Billing handler: list plans, get subscription, assign plan, cancel-reverts-to-free

부족한 커버리지

  • SQI engine unit tests (pure functions, easy to test)
  • Auth flow tests
  • Exam submission tests
  • Video proctoring tests
  • Frontend tests (zero test files)
  • E2E tests

주요 과제

  • 불규칙한 평가 데이터셋에서 결정론적 채점 알고리즘 설계
  • PostgreSQL 환경에서 테넌트 간 완벽한 데이터 격리 보장

주요 학습

  • Go 기반 엔터프라이즈 클린 아키텍처 구현
  • Redis 기반 분산 작업 큐 및 워커 풀 최적화