Next.js 15 기반의 AI 기반 한국 주식 투자 플랫폼 프론트엔드입니다.
Stockelper는 투자자에게 AI 기반 포트폴리오 추천, 백테스팅, 실시간 채팅 상담을 제공하는 종합 투자 플랫폼입니다. Next.js 15의 최신 기능을 활용하여 빠르고 반응적인 사용자 경험을 제공합니다.
- AI 채팅 어시스턴트: LLM 기반 실시간 투자 상담 및 분석
- 포트폴리오 추천: 투자 성향 기반 맞춤형 포트폴리오 제안
- 백테스팅: 투자 전략 시뮬레이션 및 성과 분석
- 대시보드: 투자 현황 및 주요 지표 시각화
- KIS 증권 연동: 한국투자증권 API 통합
- 투자 성향 분석: 8가지 질문 기반 투자자 유형 판별
- Next.js 15.3: React 기반 풀스택 프레임워크
- React 19.1: 최신 React 기능 활용
- TypeScript 5.8: 타입 안전성 보장
- Tailwind CSS 4.1: 유틸리티 우선 CSS 프레임워크
- Radix UI: 접근성 우선 컴포넌트 라이브러리
- Dialog, Label, Radio Group, Separator, Slot, Tooltip
- Framer Motion 12.7: 애니메이션 및 전환 효과
- Lucide React 0.488: 아이콘 라이브러리
- Sonner 2.0: 토스트 알림
- Prisma ORM 6.6: 타입 안전 데이터베이스 ORM
- PostgreSQL: 관계형 데이터베이스
- TanStack React Query 5.90: 서버 상태 관리 및 캐싱
- React Hook Form 7.55: 폼 상태 관리
- Zod 3.24: 스키마 유효성 검증
- @hookform/resolvers 5.0: Zod와 React Hook Form 통합
- jsonwebtoken 9.0: JWT 토큰 생성 및 검증
- bcryptjs 3.0: 비밀번호 해싱
- cookies-next 5.1: 쿠키 관리
- @xyflow/react 12.6: 플로우 차트 및 그래프
- ReactFlow 11.11: 노드 기반 다이어그램
- react-force-graph-2d 1.29: 포스 다이렉티드 그래프
- react-markdown 10.1: 마크다운 렌더링
- remark-gfm 4.0: GitHub Flavored Markdown
- remark-breaks 4.0: 줄바꿈 지원
- rehype-raw 7.0: HTML 태그 지원
- rehype-sanitize 6.0: XSS 보안
- axios 1.8: HTTP 클라이언트
- date-fns 4.1: 날짜 조작
- uuid 11.1: 고유 ID 생성
- clsx 2.1: 조건부 클래스 이름
- tailwind-merge 3.2: Tailwind 클래스 병합
- class-variance-authority 0.7: 컴포넌트 변형 관리
stockelper-fe/
├── src/
│ ├── app/ # Next.js 앱 라우터
│ │ ├── (no-layout)/ # 레이아웃 없는 페이지
│ │ │ ├── sign-in/ # 로그인
│ │ │ └── sign-up/ # 회원가입 (2단계)
│ │ ├── (has-layout)/ # 레이아웃 포함 페이지
│ │ │ ├── dashboard/ # 대시보드
│ │ │ ├── chat/ # AI 채팅
│ │ │ ├── analysis/ # 분석
│ │ │ ├── backtesting/ # 백테스팅
│ │ │ ├── portfolio/ # 포트폴리오
│ │ │ ├── settings/ # 설정
│ │ │ └── components/ # 공유 컴포넌트
│ │ └── api/ # API 라우트
│ │ ├── auth/ # 인증 API
│ │ ├── chat/ # 채팅 API
│ │ ├── conversations/ # 대화 API
│ │ ├── backtesting/ # 백테스팅 API
│ │ ├── portfolio/ # 포트폴리오 API
│ │ ├── settings/ # 설정 API
│ │ └── survey/ # 설문조사 API
│ │
│ ├── components/ # React 컴포넌트
│ │ ├── ui/ # Radix UI 기반 컴포넌트
│ │ ├── chat/ # 채팅 관련 컴포넌트
│ │ ├── common/ # 공통 컴포넌트
│ │ └── survey/ # 설문조사 컴포넌트
│ │
│ ├── hooks/ # 커스텀 React 훅
│ │
│ ├── lib/ # 유틸리티 라이브러리
│ │ ├── api/ # API 클라이언트
│ │ └── types/ # TypeScript 타입 정의
│ │
│ ├── generated/ # 자동 생성 파일
│ │ └── prisma/ # Prisma 클라이언트
│ │
│ └── middleware.ts # Next.js 미들웨어 (인증)
│
├── prisma/ # Prisma 스키마
│ ├── schema.prisma # 데이터베이스 스키마
│ └── migrations/ # 마이그레이션 파일
│
├── public/ # 정적 파일
│
├── .github/ # GitHub 설정
│ └── workflows/ # GitHub Actions 워크플로우
│
├── package.json # 프로젝트 메타데이터
├── tsconfig.json # TypeScript 설정
├── next.config.ts # Next.js 설정
├── tailwind.config.js # Tailwind CSS 설정
├── postcss.config.mjs # PostCSS 설정
├── components.json # shadcn/ui 설정
├── deploy.sh # 배포 스크립트
├── DEPLOY.md # 배포 가이드
└── README.md # 이 파일
- 이메일/비밀번호 인증
- JWT 토큰 기반 세션 관리
- 자동 리다이렉트 (/dashboard)
1단계: 계정 정보
- 이름, 이메일, 닉네임, 비밀번호
- 중복 검사 및 유효성 검증
2단계: 투자 성향 설문조사
- 8가지 질문 (투자 경험, 목적, 위험 선호도)
- 투자자 유형 판별 (안정형, 안정추구형, 위험중립형, 적극투자형, 공격투자형)
- 투자 포트폴리오 요약
- 주요 지표 시각화
- 최근 활동 내역
- LLM 기반 실시간 대화
- 스트리밍 응답
- 대화 이력 관리
- 서브그래프 시각화 (지식 그래프)
- 거래 액션 제안
- 특정 대화 세션
- 메시지 페이지네이션
- 피드백 제출 (긍정/부정)
- 시장 분석 도구
- 종목 상세 정보
- 전략 시뮬레이션
- 성과 분석 및 리포트
- 결과 시각화
- AI 추천 포트폴리오
- 투자자 유형별 맞춤 제안
- 리밸런싱 제안
- 계정 설정 (
/settings/account)- 닉네임 변경
- 비밀번호 변경
- KIS 증권 API (
/settings/kis)- App Key, App Secret 설정
- 계좌 번호 등록
- 투자 성향 재평가 (
/settings/survey)- 설문조사 다시 하기
- 투자자 유형 업데이트
회원가입 (투자 성향 평가 포함)
요청:
{
"email": "user@example.com",
"password": "SecurePass123!",
"name": "홍길동",
"nickname": "투자왕",
"surveyAnswers": {
"q1": "1",
"q2": "2",
// ... q8까지
}
}응답:
{
"success": true,
"message": "회원가입이 완료되었습니다",
"user": {
"id": 1,
"email": "user@example.com",
"nickname": "투자왕",
"investor_type": "위험중립형"
}
}로그인 (JWT 쿠키 반환)
요청:
{
"email": "user@example.com",
"password": "SecurePass123!"
}응답:
{
"success": true,
"user": {
"id": 1,
"email": "user@example.com",
"nickname": "투자왕",
"investor_type": "위험중립형"
},
"token": "jwt-token-here"
}현재 사용자 정보 조회
응답:
{
"id": 1,
"email": "user@example.com",
"nickname": "투자왕",
"investor_type": "위험중립형"
}로그아웃 (쿠키 삭제)
대화 목록 조회
응답:
[
{
"id": "uuid-1",
"title": "삼성전자 투자 상담",
"createdAt": "2024-01-01T00:00:00Z",
"lastActive": "2024-01-01T12:00:00Z"
}
]새 대화 생성
요청:
{
"title": "새로운 투자 상담"
}대화 상세 조회
대화 제목 수정
대화 삭제
메시지 목록 조회 (페이지네이션)
쿼리 파라미터:
page: 페이지 번호 (기본값: 1)limit: 페이지 크기 (기본값: 50)
메시지 저장
피드백 제출
요청:
{
"messageId": "uuid",
"feedback": true // true: 긍정, false: 부정
}LLM 스트리밍 응답
요청:
{
"conversationId": "uuid",
"message": "삼성전자 주식 추천해줘",
"userId": 1
}응답: Server-Sent Events (SSE) 스트리밍
백테스트 실행 요청
요청:
{
"strategy": "momentum",
"ticker": "005930",
"startDate": "2023-01-01",
"endDate": "2023-12-31",
"initialCapital": 10000000
}백테스트 결과 조회
포트폴리오 추천 조회
새 포트폴리오 추천 요청
닉네임/비밀번호 변경
요청:
{
"nickname": "새닉네임",
"currentPassword": "old-password",
"newPassword": "new-password"
}KIS API 정보 조회
KIS API 정보 업데이트
요청:
{
"kis_app_key": "PSxxxx",
"kis_app_secret": "xxxxx",
"account_no": "12345678-01"
}투자 성향 설문 제출/수정
요청:
{
"answers": {
"q1": "1",
"q2": "2",
// ... q8까지
}
}사용자 계정 정보
model users {
id Int @id @default(autoincrement())
email String @unique
name String
nickname String @unique
password String
investor_type String @default("안정형")
kis_app_key String
kis_app_secret String
kis_access_token String?
account_no String
created_at DateTime @default(now())
updated_at DateTime @default(now())
conversations Conversation[]
portfolioRecommendations PortfolioRecommendation[]
}투자자 유형:
- 안정형
- 안정추구형
- 위험중립형
- 적극투자형
- 공격투자형
투자 성향 설문 답변
model survey {
id Int @id @default(autoincrement())
user_id Int @unique
answer Json // { q1: "1", q2: "2", ... q8: "5" }
created_at DateTime @default(now())
updated_at DateTime @default(now())
}채팅 대화방
model Conversation {
id String @id @default(uuid())
title String?
userId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lastActive DateTime @default(now())
messages Chat[]
user users @relation(fields: [userId], references: [id])
}채팅 메시지
model Chat {
id String @id @default(uuid())
messageId String
role String // "user" | "assistant" | "question"
content String
timestamp DateTime
conversationId String
jobId String?
subgraphData Json? // 서브그래프 시각화 데이터
tradingActionData Json? // 거래 액션 데이터
errorMessage String?
humanFeedbackResponse Boolean? // 피드백 (true: 긍정, false: 부정)
createdAt DateTime @default(now())
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
}포트폴리오 추천
model PortfolioRecommendation {
id String @id @default(uuid())
userId Int
investorType String
result String // JSON 문자열
jobId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user users @relation(fields: [userId], references: [id], onDelete: Cascade)
}백테스팅 작업
model backtesting {
id String @id
job_id String @unique
user_id Int
request_source String @default("llm")
status String // pending|in_progress|completed|failed
input_json Json
output_json Json
result_file_path String?
report_file_path String?
error_message String?
// ... 타임스탬프 필드
}# 데이터베이스
DATABASE_URL=postgresql://user:password@host:5432/database
# JWT 인증
JWT_SECRET=your_super_secret_jwt_key_min_32_characters
JWT_EXPIRES_IN=7d
COOKIE_NAME=auth-token
# LLM 서비스 엔드포인트 (서버 사이드 전용)
LLM_ENDPOINT=https://your-llm-service-endpoint
# 환경
NODE_ENV=development# LLM 엔드포인트 (하위 호환성)
NEXT_PUBLIC_LLM_ENDPOINT=https://your-llm-service-endpoint
# 포트 (프로덕션)
PORT=80- Node.js 18 이상
- pnpm 8 이상
- PostgreSQL 12 이상
-
저장소 클론
git clone <repository-url> stockelper-fe cd stockelper-fe
-
의존성 설치
pnpm install
-
환경 변수 설정
cp .env.example .env # .env 파일을 편집하여 설정 값 입력 -
데이터베이스 마이그레이션
pnpm prisma:generate pnpm prisma:migrate
-
개발 서버 실행
pnpm dev
-
브라우저에서 접속
# 프로덕션 빌드
pnpm build
# 빌드된 앱 실행 (포트 80)
pnpm start# 개발
pnpm dev # 개발 서버 실행 (포트 3000)
pnpm dev -- -p 3001 # 다른 포트로 실행
# 빌드
pnpm build # 프로덕션 빌드
# 실행
pnpm start # 빌드된 앱 실행 (포트 80)
# 린팅
pnpm lint # ESLint 검사
pnpm typecheck # TypeScript 타입 검사
# Prisma
pnpm prisma:generate # Prisma 클라이언트 생성
pnpm prisma:migrate # 개발 마이그레이션 실행
pnpm prisma:migrate-deploy # 프로덕션 마이그레이션 적용
pnpm prisma:migrate-reset # 마이그레이션 초기화
pnpm prisma:pull # DB 스키마를 Prisma 스키마로 가져오기
pnpm prisma:studio # Prisma Studio (DB GUI)npm install -g pm2-
빌드
pnpm build
-
PM2로 실행
pm2 start "pnpm start" --name stockelper-fe -
자동 시작 설정
pm2 startup pm2 save
# 프로세스 목록
pm2 list
# 로그 확인
pm2 logs stockelper-fe
pm2 logs stockelper-fe --lines 100
# 프로세스 재시작
pm2 restart stockelper-fe
# 프로세스 중지
pm2 stop stockelper-fe
# 프로세스 삭제
pm2 delete stockelper-fe
# 모니터링
pm2 monit
# 상세 정보
pm2 show stockelper-femain 브랜치에 push 시 AWS EC2에 자동으로 배포됩니다.
자세한 배포 가이드는 DEPLOY.md를 참조하세요.
필수 GitHub Secrets:
EC2_SSH_PRIVATE_KEY: SSH 개인 키EC2_HOST: EC2 인스턴스 IPEC2_USER: EC2 사용자명 (기본: ubuntu)EC2_DEPLOY_PATH: 배포 경로
- GitHub 저장소의 Actions 탭으로 이동
- "Deploy to AWS EC2" 워크플로우 선택
- "Run workflow" 버튼 클릭
shadcn/ui 컴포넌트 추가:
npx shadcn@latest add [component-name]src/app/api/아래에 디렉토리 생성route.ts파일 생성- GET, POST 등 HTTP 메서드 export
예시:
// src/app/api/example/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
return NextResponse.json({ message: 'Hello' });
}src/app/(has-layout)/아래에 디렉토리 생성page.tsx파일 생성
src/lib/types/ 에 타입 정의 파일 추가
prisma/schema.prisma수정- 마이그레이션 생성 및 적용:
pnpm prisma:migrate
- 클라이언트 재생성:
pnpm prisma:generate
-
환경 변수 관리
.env파일을 절대 커밋하지 마세요.gitignore에.env포함 확인
-
JWT 보안
- 프로덕션에서 강력한
JWT_SECRET사용 (32자 이상) - 적절한 만료 시간 설정 (
JWT_EXPIRES_IN)
- 프로덕션에서 강력한
-
비밀번호 보안
- bcrypt를 사용한 해싱
- 최소 8자 이상, 특수문자 포함 권장
-
API 보안
- 미들웨어를 통한 인증 확인
- CORS 설정
- Rate limiting (필요 시)
-
XSS 방지
rehype-sanitize를 통한 마크다운 sanitization- 사용자 입력 이스케이핑
-
데이터베이스
- Prisma의 파라미터화된 쿼리 사용
- SQL 인젝션 자동 방지
// ❌ 나쁜 예
const password = req.body.password;
// 평문 저장
// ✅ 좋은 예
import bcrypt from 'bcryptjs';
const hashedPassword = await bcrypt.hash(password, 10);
// 해시 저장# 다른 포트로 개발 서버 실행
pnpm dev -- -p 3001# Prisma 클라이언트 재생성
pnpm prisma:generate
# 마이그레이션 재적용
pnpm prisma:migrate# 캐시 삭제
rm -rf .next
# 의존성 재설치
rm -rf node_modules
pnpm install
# 다시 빌드
pnpm build# DATABASE_URL 확인
echo $DATABASE_URL
# PostgreSQL 서버 실행 여부 확인
psql $DATABASE_URL -c "SELECT 1"
# 방화벽 설정 확인# PM2 상태 확인
pm2 status
# 로그 확인
pm2 logs stockelper-fe --lines 100
# 프로세스 재시작
pm2 restart stockelper-fe
# PM2 초기화
pm2 kill
pm2 start "pnpm start" --name stockelper-fe# 빌드 시 메모리 증가
NODE_OPTIONS='--max-old-space-size=4096' pnpm build-
이미지 최적화
next/image사용- WebP 형식 자동 변환
-
코드 스플리팅
- 동적 import 사용
const Component = dynamic(() => import('./Component'));
-
캐싱
- React Query 캐싱 전략
- API 응답 캐싱
-- 인덱스 추가
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_conversations_user_id ON conversations(user_id);
-- VACUUM 실행
VACUUM ANALYZE;# 번들 분석기 설치
pnpm add -D @next/bundle-analyzer
# 분석 실행
ANALYZE=true pnpm build# 테스트 실행 (구성 필요)
pnpm test# Playwright 또는 Cypress 설정 후
pnpm test:e2e- 저장소 포크
- 기능 브랜치 생성:
git checkout -b feature/my-feature - 변경사항 커밋:
git commit -am 'Add new feature' - 브랜치에 푸시:
git push origin feature/my-feature - Pull Request 생성
- ESLint 규칙 준수
- TypeScript strict 모드 사용
- 컴포넌트는 함수형으로 작성
- 커밋 메시지는 명확하게
MIT License
Copyright (c) 2025 Stockelper-Lab
- 이슈 리포트: GitHub Issues
- 기능 요청: Pull Request
- 문서: 이 README 및 DEPLOY.md
- Next.js 팀
- Vercel
- Prisma 팀
- shadcn/ui
- 모든 오픈소스 기여자
- 포괄적인 README 작성
- 프로젝트 구조 문서화
- API 엔드포인트 상세 설명 추가
- GitHub Actions 자동 배포 설정
- PM2 배포 가이드 추가
- 백테스팅 기능 추가
- 포트폴리오 추천 기능 구현
- 초기 프로젝트 설정
- 인증 시스템 구현
- AI 채팅 기능 추가