- lib/api.ts: GoalType('math-suneung'|'ps'|...), MathUnit, MATH_UNIT_LABEL,
MeUser{focusUnits,bojHandle}, ps.* helper (search/bookmark/sync/getProblem)
- onboarding/page.tsx: 5개 GoalType 옵션, math-suneung 단원 멀티셀렉트,
ps 인풋 + 즉시 동기화 토글, focusUnits/bojHandle PATCH
- profile/page.tsx: 동일 컨트롤 노출 + 검증
- ps/{page,bookmarks/page,sync/page}.tsx: 검색·필터·북마크·동기화 신규 섹션
- components/ps/PsTabs.tsx, layout/{SideNav,BottomNav}.tsx, ui/Icon.tsx:
PS 진입점 + Phosphor code/bookmark-simple 아이콘
- exam/shared.ts: getExamDurationSeconds → 수학 100분 고정
- 비수학 하드코딩 전면 제거: dashboard/exams/study/subjects/school/assignment
- 카피 갱신: landing/pricing/layout metadata + README + docs(user/admin/pipeline)
- .eslintrc.json: Next core lint 결정성 확보
Phase 10C DoD 통과 — pnpm build + lint 0 에러, /ps /ps/bookmarks /ps/sync 라우트 생성,
grep -rE '국어|영어|한국사|생활과 윤리|사회탐구' frontend/src docs = 0
5.4 KiB
5.4 KiB
ReLoop 운영 및 배포 가이드
서버 환경
| 항목 | 값 |
|---|---|
| OS | Debian 12 (VM on Proxmox) |
| Node.js | 20+ |
| pnpm | 9.12+ |
| DB | MariaDB (Docker, Port 33006) |
| 프로세스 매니저 | PM2 |
| 리버스 프록시 | Nginx |
| 도메인 | reloop.nabomhalang.co.kr (프론트), reloop-api.nabomhalang.co.kr (API) |
배포 절차
1. 코드 동기화
# Gitea에서 pull
cd /home/dev/reloop-v2
git fetch origin && git reset --hard origin/master
2. 백엔드 배포
cd backend
pnpm install --no-frozen-lockfile
pnpm prisma:deploy # 마이그레이션 적용 (안전, 데이터 보존)
rm -f tsconfig.build.tsbuildinfo # 빌드 캐시 초기화
pnpm exec tsc -p tsconfig.build.json # 컴파일
pm2 restart reloop-api
pnpm build(nest build) 대신tsc -p tsconfig.build.json을 직접 사용합니다. nest CLI의 빌드 래퍼가 간헐적으로 dist/ 를 비우는 버그가 있어서 직접 tsc를 실행하는 것이 안정적입니다.
3. 프론트엔드 배포
cd frontend
pnpm install --no-frozen-lockfile
pnpm build # Next.js 프로덕션 빌드
cp -r .next/static .next/standalone/.next/
cp -r public .next/standalone/ 2>/dev/null || true
pm2 restart reloop-web
4. 상태 확인
pm2 list | grep reloop
# reloop-api online
# reloop-web online
# 헬스체크
curl -s -o /dev/null -w "%{http_code}\n" https://reloop-api.nabomhalang.co.kr/api/health
curl -s -o /dev/null -w "%{http_code}\n" https://reloop.nabomhalang.co.kr/
KICE 기출 데이터 관리
PDF 원본 배치
data/kice/
├── 2025/
│ └── 수학/
│ ├── (25)수학_공통_문제지.pdf
│ ├── (25)수학_확통_문제지.pdf
│ ├── (25)수학_미적분_문제지.pdf
│ ├── (25)수학_기하_문제지.pdf
│ └── (25)수학_정답표.pdf
└── 2026/
└── (같은 구조)
임포트 실행
cd backend
# 특정 단원 (subject=math)
pnpm cli:kice-import --year=2026 --subject=math
# 특정 연도 전체
pnpm cli:kice-import --year=2026
# 전체 (2025 + 2026)
pnpm cli:kice-import
# 드라이 런 (DB 변경 없이 파일 스캔만)
pnpm cli:kice-import --dry-run
임포트가 하는 일:
- PDF 페이지를 문제별로 PNG 크롭 →
uploads/problems/{year}/{subject}/{number}.png - 정답표 파싱 (텍스트 또는 OCR)
- ProblemSet / Problem / Passage DB upsert (멱등)
OCR 보정 (2025 정답표 등)
2025 정답표는 이미지 기반 PDF라 텍스트 추출이 안 됩니다. Codex Vision OCR로 보정:
pnpm cli:ocr-fix --year=2025 --answers-only # 정답만 채움
pnpm cli:ocr-fix --year=2025 # 문항 본문까지 보정
pnpm cli:ocr-fix --dry-run # 미리보기
codexCLI가 설치되어 있어야 하고 ChatGPT 로그인이 되어 있어야 합니다.
문제 이미지 서버 전송
임포트는 로컬에서 실행하고 이미지는 서버로 전송:
# 로컬에서
cd backend
tar -czf /tmp/problem-images.tar.gz -C uploads problems/
# 서버에서
cd /home/dev/reloop-v2/backend/uploads
tar -xzf /tmp/problem-images.tar.gz
PM2 ecosystem 설정
ecosystem.config.js 예시:
module.exports = {
apps: [
{
name: 'reloop-api',
script: 'dist/main.js',
cwd: '/home/dev/reloop-v2/backend',
env: { NODE_ENV: 'production', PORT: 3001 },
},
{
name: 'reloop-web',
script: '.next/standalone/server.js',
cwd: '/home/dev/reloop-v2/frontend',
env: { NODE_ENV: 'production', PORT: 3000, HOSTNAME: '0.0.0.0' },
},
],
};
모니터링
pm2 logs reloop-api --lines 50 # API 로그
pm2 logs reloop-web --lines 50 # 프론트 로그
pm2 monit # 실시간 CPU/메모리
DB 관리
# Prisma Studio (로컬에서)
cd backend && pnpm exec prisma studio
# 직접 SQL
mysql -h 10.10.10.146 -P 33006 -u reloop_v2 -p reloop_v2
# 마이그레이션 상태 확인
pnpm exec prisma migrate status
# 데이터 초기화 (주의!)
pnpm prisma:reset # 전체 드롭 + 재생성 + 시드
보안 체크리스트
JWT_SECRET이 32자 이상 랜덤 문자열인지 확인CORS_ORIGINS에 프론트엔드 도메인만 허용되어 있는지 확인.env파일이 git에 커밋되지 않았는지 확인- rate limit 동작 확인:
for i in {1..70}; do curl -s -o /dev/null -w "%{http_code} " https://reloop-api.nabomhalang.co.kr/api/auth/login; done - HTTPS가 nginx에서 강제되는지 확인
uploads/디렉토리 권한이 644 (파일) / 755 (디렉토리)인지 확인
트러블슈팅
| 증상 | 원인 | 해결 |
|---|---|---|
| reloop-api 즉시 크래시 | dist/main.js 없음 | tsc -p tsconfig.build.json 실행 |
| express 모듈 못 찾음 | pnpm strict 모드 | pnpm install 재실행 (express가 deps에 명시됨) |
| 프론트 빌드 실패 (@phosphor-icons) | 패키지 미설치 | pnpm install --no-frozen-lockfile |
| 문제 이미지 404 | uploads 디렉토리 없음 | mkdir -p uploads/problems + 이미지 전송 |
| 2025 정답 null | 이미지 PDF + OCR 미실행 | pnpm cli:ocr-fix --year=2025 --answers-only |
| 마이그레이션 실패 (shadow DB) | INDEX 권한 없음 | MariaDB root로 GRANT INDEX ON *.* TO 'reloop_v2'@'%' |