화상 강의실, 서버 동기화 화이트보드, AI 질문, 채팅, 오답노트와 결제를 하나의 사용자 경험으로 제공합니다.
StudyFlow 프론트엔드는 학생과 선생님이 수업을 찾고, 실시간 강의실에서 함께 학습하며, 질문과 복습까지 이어갈 수 있도록 만든 React SPA입니다.
프론트엔드는 다음 역할을 담당합니다.
- 사용자 역할과 인증 상태에 따른 화면 및 라우팅
- 수업·선생님 검색, 수강 신청과 승인 UI
- LiveKit 화상·음성·화면공유
- WebSocket/STOMP 기반 채팅과 강의실 도구 동기화
- 화이트보드, PDF, 이미지와 오디오 학습 도구
- AI 답변 SSE 스트리밍과 Markdown·수학 수식 렌더링
- 질문게시판, 오답노트와 복습 추천
- 마일리지 충전, Toss Payments 결제와 구독권
- 관리자 대시보드와 선생님 인증 처리
프로젝트 기간: 2026년 5월 19일 ~ 2026년 6월 26일
| 영역 | 설계 |
|---|---|
| 정적 배포 | HashRouter와 Vite base: './'을 사용해 별도 서버 라우팅 없이 Caddy에서 SPA 제공 |
| API 주소 | 환경별 VITE_API_BASE를 사용하고 LAN 개발 환경에서는 현재 호스트의 :8080으로 자동 해석 |
| 인증 | Access Token은 메모리에만 저장하고 Refresh Token은 백엔드 HttpOnly 쿠키로 관리 |
| 토큰 재발급 | authFetch가 401 응답을 받으면 single-flight 방식으로 한 번만 재발급하고 요청 재시도 |
| AI 스트리밍 | POST와 Authorization이 필요해 EventSource 대신 Fetch ReadableStream으로 SSE 직접 파싱 |
| 강의실 미디어 | LiveKit이 카메라·마이크·FHD 화면공유를 담당하고 실제 송출 권한은 백엔드 토큰으로 결정 |
| 강의실 도구 | STOMP/SockJS가 화이트보드·PDF 페이지·오디오·채팅·퀴즈 이벤트를 동기화 |
| 보이스톡 | WebRTC RTCPeerConnection과 STOMP 시그널링을 사용하며 STUN/TURN은 환경변수로 구성 |
| 문서와 수식 | PDF.js, React Markdown, KaTeX를 사용해 PDF와 AI 수학 답변 표시 |
| 구분 | 기술 |
|---|---|
| Core | React 18.3.1, React DOM |
| Build | Vite 5.4, @vitejs/plugin-react |
| Routing | React Router DOM 6.27, HashRouter |
| Styling | CSS, CSS Custom Properties, CSS Animation |
| API | Fetch API, ReadableStream, AbortController |
| Realtime | STOMP.js 7.3, SockJS 1.6 |
| Media | LiveKit Client 2.19, WebRTC |
| AI Rendering | React Markdown, remark-gfm, remark-math, rehype-katex, KaTeX |
| Documents | PDF.js 6.0 |
| Image | heic2any |
| Map | Kakao Maps JavaScript SDK |
| Payment | Toss Payments SDK 2.7 |
| Deployment | GitHub Actions, AWS EC2, Caddy, DuckDNS |
- 이메일 회원가입·로그인과 이메일 인증
- Kakao, Google, Naver OAuth2 로그인
- 새로고침 시 Refresh Token 쿠키를 이용한 인증 복구
- 401 응답 시 Access Token 재발급과 원래 요청 재시도
- 비밀번호 재설정, 로그인 기록과 회원 탈퇴
- 과목·수업 방식·지역·가격·인원 필터
- 최신순·가격순·거리순 정렬
- 선생님 검색과 학생·선생님 상세 페이지
- 수업 등록, 수정, 상세와 수업별 대시보드
- 수강 신청, 선생님 승인, 수강 포기와 마일리지 부족 처리
- Kakao Maps 기반 대면 수업 위치 지정
- LiveKit 카메라·마이크·화면공유
- 참가자 영상 타일, 활성 발화자와 전체화면
- 서버 권위 화이트보드와
seq/snapshot/resync - 펜·형광펜·지우개·도형·텍스트·레이어·확대·축소·손 도구
- 이미지와 PDF 업로드, PDF·화이트보드 페이지 분리 이동
- 오디오 재생·정지·탐색·배속·AB 반복 동기화
- 강의실 채팅, 반응, 참가자 판서 권한과 제한시간 퀴즈
- 비수강생 강의실 미리보기
- 모든 일반 페이지에서 열 수 있는 전역 채팅 위젯
- 학생·선생님 1:1 채팅과 읽음 처리
- 수업별 단체 채팅방 생성과 학생 초대·내보내기
- 여러 이미지 첨부와 드래그 앤 드롭
- WebRTC 음성 통화와 STOMP 기반 SDP/ICE 시그널링
- 과목별 AI 질문과 대화 목록
- 답변 토큰 SSE 스트리밍과 생성 취소
- 이미지 첨부 및 AI 생성 이미지 표시
- Markdown, GFM, LaTeX 수식 렌더링
- 질문게시판 작성·수정·검색·답변·채택
- 질문게시판과 AI 답변을 오답노트로 이동
- 오답노트 작성·수정·삭제·과목별 탐색
- 복습 추천, 답안 보기와 복습 결과 기록
- Toss Payments 결제창을 이용한 마일리지 충전
- AI 질문권과 Live 강의 구독권 구매
- 학생 마일리지와 선생님 수익 내역
- 관리자 회원 통계, 선생님 인증 승인·거절과 거래 내역
아래 역할은 프론트엔드와 백엔드를 합친 프로젝트 전체 기준입니다.
| 팀원 | 담당 기능 |
|---|---|
| 이재섭 (congsoony) | 팀장·발표, 실시간 강의실, 서버 권위 화이트보드, PDF·오디오 동기화, Redis 멀티 인스턴스 대응, 채팅·보이스톡, 질문게시판, AI 질문/SSE, 오답노트·복습 추천, 마일리지 충전·결제·수익 내역, 부하테스트와 AWS 운영 검증 |
| 김현우 (gusdnzla26-art) | 수업 상세 페이지, 학생 수강 포기, 수업 수정 제한, 화상수업 진행 시간 기반 내공 지급, 마이페이지 탭과 라우팅 개선 |
| 이준영 (leejy1019) | 수업·선생님 찾기, 찾기 페이지 노출 토글, 학생·선생님 상세, 수업 등록·수업별 페이지, 알림, 강의실 미리보기, 선생님 마이페이지 프로필·인증 |
| 전우현 (jwh039) | Spring Security, JWT 파싱과 역할별 인증·인가, 이메일 인증, 회원가입·로그인·소셜 로그인·로그아웃·회원 탈퇴, Access Token 재발급과 RTR, 비밀번호 재설정, 로그인 기록, 선생님 인증 승인·거절과 회원 통계 |
src/
├── main.jsx
├── App.jsx # HashRouter와 전체 페이지 라우팅
├── api/
│ ├── config.js # API 주소와 LAN 환경 해석
│ ├── authFetch.js # Access Token 첨부, 401 재발급·재시도
│ ├── aiApi.js # POST SSE 파싱
│ ├── chatSocket.js # STOMP/SockJS 연결과 구독
│ ├── voiceConfig.js # STUN/TURN 구성
│ └── *Api.js # 도메인별 REST API
├── auth/
│ ├── tokenStore.js # 메모리 Access Token 저장소
│ ├── authApi.js # Refresh Token 재발급
│ └── AuthBootstrap.jsx # 앱 시작 시 인증 복구
├── components/
│ ├── layout/ # Navbar와 공통 배경
│ ├── chat/ # 전역 채팅, 수업톡, 보이스톡
│ ├── notifications/ # 알림과 브라우저 알림
│ └── ui/ # 공통 UI
├── pages/
│ ├── home/ # 홈과 강의실 미리보기
│ ├── auth/ # 로그인·OAuth2·비밀번호 재설정
│ ├── search/ # 수업 검색과 필터
│ ├── teachers/ # 선생님 검색과 상세
│ ├── courses/ # 수업 등록·수정·상세·대시보드
│ ├── classroom/ # LiveKit 강의실과 화이트보드
│ ├── ai/ # AI 질문과 Markdown/KaTeX
│ ├── qna/ # 질문게시판
│ ├── mypage/ # 학생·선생님·오답노트
│ ├── payment/ # 마일리지와 구독권
│ └── admin/ # 관리자 대시보드
├── payment/ # Toss Payments SDK 연동
├── styles/ # 페이지별 CSS
└── utils/ # 공통 유틸
- Node.js 20 LTS 권장
- npm
- 실행 중인 StudyFlow 백엔드: 기본
http://localhost:8080
git clone <FRONTEND_REPOSITORY_URL>
cd <FRONTEND_DIRECTORY>
npm cipackage-lock.json을 기준으로 동일한 버전을 설치하기 위해 npm install보다 npm ci를 권장합니다.
PowerShell:
Copy-Item .env.local.example .env.localmacOS/Linux:
cp .env.local.example .env.local.env.local 예시:
VITE_API_BASE=http://localhost:8080
VITE_KAKAO_MAP_KEY=your-kakao-javascript-key
# 선택: 보이스톡 STUN/TURN
# JSON 배열 또는 콤마 구분 URL을 지원합니다.
VITE_VOICE_ICE_SERVERS=[{"urls":"stun:stun.l.google.com:19302"}]VITE_KAKAO_MAP_KEY가 없어도 지도를 제외한 기능은 실행할 수 있습니다.
npm run dev- 로컬 주소:
http://localhost:5173 - Vite가 브라우저를 자동으로 엽니다.
host: true설정으로 같은 LAN의 다른 기기에서도 접근할 수 있습니다.
http://<개발 PC의 사설 IP>:5173
프론트를 LAN IP로 열면 API 주소도 기본적으로 같은 호스트의 :8080으로 맞춰집니다. 백엔드 CORS에는 해당 프론트 Origin을 추가해야 합니다.
카메라와 마이크의 getUserMedia()는 보안 컨텍스트가 필요합니다. localhost는 HTTP에서도 허용되지만, 다른 기기의 http://192.168.x.x:5173에서는 브라우저가 차단할 수 있으므로 실제 기기 테스트는 HTTPS 환경을 권장합니다.
PowerShell:
$env:VITE_API_BASE="https://your-api-domain.example.com"
$env:VITE_KAKAO_MAP_KEY="your-kakao-javascript-key"
npm run build
npm run previewmacOS/Linux:
VITE_API_BASE=https://your-api-domain.example.com \
VITE_KAKAO_MAP_KEY=your-kakao-javascript-key \
npm run build
npm run preview빌드 결과는 dist/에 생성됩니다.
| 명령 | 설명 |
|---|---|
npm run dev |
Vite 개발 서버 실행 |
npm run build |
프로덕션 정적 파일 생성 |
npm run preview |
dist/ 로컬 미리보기 |
현재 별도의 test와 lint 스크립트는 등록되어 있지 않으므로 PR 전 최소 검증은 npm run build입니다.
Vite는 VITE_ 접두사가 붙은 값을 빌드 시점에 JavaScript 번들에 삽입합니다.
| 변수 | 필수 여부 | 설명 |
|---|---|---|
VITE_API_BASE |
운영 필수 | 백엔드 API Origin. 예: https://studyflow-mytutor-api.duckdns.org |
VITE_KAKAO_MAP_KEY |
지도 사용 시 | Kakao Maps JavaScript Key |
VITE_VOICE_ICE_SERVERS |
선택 | WebRTC 보이스톡용 STUN/TURN 목록 |
VITE_VOICE_ICE_SERVERS 지원 형식:
# JSON
VITE_VOICE_ICE_SERVERS=[{"urls":"turn:turn.example.com:3478","username":"user","credential":"password"}]
# 콤마 구분
VITE_VOICE_ICE_SERVERS=stun:stun1.example.com:3478,stun:stun2.example.com:3478설정하지 않으면 기본값 stun:stun.l.google.com:19302를 사용합니다. 실제 운영에서는 대칭 NAT와 방화벽 환경을 위해 TURN 서버를 함께 사용하는 것이 안전합니다.
VITE_* 값은 최종 dist/ JavaScript에 포함되므로 브라우저 개발자 도구에서 확인할 수 있습니다. GitHub Actions Secrets에 저장하더라도 빌드 로그에서만 가려질 뿐 최종 번들에서는 공개됩니다.
프론트에 넣어도 되는 값:
- 백엔드 공개 URL
- 도메인 제한을 설정한 Kakao JavaScript Key
- 공개 가능한 STUN URL
- Toss Payments Client Key와 같은 공개용 키
프론트에 절대 넣으면 안 되는 값:
- JWT Secret
- OpenAI API Key
- LiveKit API Secret
- Toss Payments Secret Key
- OAuth Client Secret
- AWS Access Key와 Secret Key
- DB·Redis 비밀번호
| 항목 | 처리 방식 |
|---|---|
| JWT | Access Token만 메모리에 저장, JWT 서명 Secret은 백엔드에만 존재 |
| Refresh Token | 백엔드가 HttpOnly 쿠키로 관리 |
| LiveKit | 프론트가 백엔드에서 단기 입장 토큰을 발급받아 연결 |
| Toss Payments | Client Key는 백엔드 API에서 받고 Secret Key와 결제 승인은 백엔드에서 처리 |
| OpenAI | 프론트는 StudyFlow API만 호출하며 OpenAI Key는 백엔드에만 존재 |
| Kakao Maps | JavaScript Key는 공개 키이므로 Kakao 콘솔에서 허용 도메인 제한 |
| TURN Credential | 정적 VITE_* 값은 노출되므로 운영에서는 단기 TURN Credential 발급 구조 권장 |
로컬 전용 파일은 Git에 커밋하지 않습니다.
.env.local
.env.production
.env.*.local
확인:
git check-ignore .env.local
git check-ignore .env.productionmain 브랜치 push
→ GitHub Actions
→ Node.js 20 + npm ci
→ VITE_API_BASE와 VITE_KAKAO_MAP_KEY 주입
→ npm run build
→ dist/* 생성
→ SCP로 EC2-A /home/ec2-user/studyflow/www 업로드
→ Caddy가 HTTPS 정적 파일 제공
develop은 개발 통합 브랜치이며 자동 배포하지 않습니다.
| 이름 | 용도 |
|---|---|
EC2_HOST |
EC2-A의 배포용 주소 |
EC2_USER |
SSH 사용자, 현재 ec2-user |
EC2_SSH_KEY |
EC2 접속용 PEM Private Key |
VITE_API_BASE |
운영 백엔드 API Origin |
VITE_KAKAO_MAP_KEY |
Kakao Maps JavaScript Key |
VITE_VOICE_ICE_SERVERS를 운영 빌드에 사용하려면 GitHub Actions Secret 등록뿐 아니라 deploy-fe.yml의 Build 단계 env에도 명시적으로 추가해야 합니다.
| 기능 | 연결 경로 |
|---|---|
| React 정적 파일 | Browser → DuckDNS → EIP/Caddy → www |
| REST·SSE·SockJS | Browser → API 도메인/Caddy → ALB → Spring Boot EC2-A/B |
| 화상·음성·화면공유 | Browser → LiveKit Cloud |
| 이미지·PDF·오디오 | Browser → S3 공개 URL |
프론트 운영 서버에는 Node.js 애플리케이션이 상시 실행되지 않습니다. Vite가 만든 정적 파일을 Caddy가 제공하는 구조입니다.
HashRouter를 사용하므로 실제 브라우저 주소는 /#/courses 형태입니다.
| 경로 | 화면 |
|---|---|
/ |
홈 |
/courses |
수업 찾기 |
/courses/new |
수업 등록 |
/courses/:id |
수업 상세 |
/courses/:id/edit |
수업 수정 |
/courses/:id/dashboard |
수업 대시보드 |
/teachers |
선생님 찾기 |
/teachers/:id |
선생님 상세 |
/classroom/:courseId |
실시간 강의실 |
/ai |
AI 질문 |
/qna |
질문게시판 |
/qna/write |
질문 작성 |
/qna/:questionId |
질문 상세 |
/mypage |
학생·선생님 마이페이지 |
/payment/charge |
마일리지 충전 |
/payment/subscriptions |
구독권 구매 |
/payment/success |
결제 성공 처리 |
/payment/fail |
결제 실패 처리 |
/admin |
관리자 |
/login |
로그인·회원가입 |
/password-reset |
비밀번호 재설정 요청 |
/reset-password |
비밀번호 변경 |
- 프론트 배포 워크플로우:
.github/workflows/deploy-fe.yml - 로컬 환경변수 예시:
.env.local.example - 운영 환경변수 예시:
.env.production.example
본 프로젝트는 AIBE5 데브코스 최종 프로젝트로 제작되었습니다.