Skip to content

와인 홍보 및 이벤트 안내를 위한 블로그 서비스

Notifications You must be signed in to change notification settings

jayychoi/biswine

Repository files navigation

Biswine

와인 홍보 및 이벤트 안내를 위한 블로그 서비스입니다.

https://www.biswine.co.kr

Tech Stack

Frontend

  • React 19
  • TypeScript
  • Vite
  • TanStack Query
  • Zustand
  • TailwindCSS v4
  • Shadcn/ui
  • Tiptap

Backend

  • NestJS
  • TypeScript
  • Prisma
  • MySQL 8.4
  • Passport.js

Infrastructure

  • Docker Compose
  • Nginx - 리버스 프록시 및 정적 파일 서빙
  • Let's Encrypt - SSL/TLS 인증서

AWS

  • EC2 (t3.micro) - 서울 리전
  • EBS (30GB) - 블록 스토리지
  • Elastic IP - 고정 IP 주소
  • Security Group - 방화벽 설정

AWS Architecture

                              ┌─────────────────┐
                              │    Internet     │
                              └────────┬────────┘
                                       │
                              ┌────────┴────────┐
                              │  Elastic IP     │
                              │  biswine.co.kr  │
                              └────────┬────────┘
                                       │
                                       ▼
┌───────────────────────────────────────────────────────────────────────────────┐
│  AWS Cloud (ap-northeast-2 / Seoul)                                           │
│                                                                               │
│  ┌─────────────────────────────────────────────────────────────────────────┐  │
│  │  VPC                                                                    │  │
│  │                                                                         │  │
│  │  ┌───────────────────────────────────────────────────────────────────┐  │  │
│  │  │  Public Subnet                                                    │  │  │
│  │  │                                                                   │  │  │
│  │  │  ┌────────────────┐    ┌───────────────────────────────────────┐  │  │  │
│  │  │  │ Security Group │    │  EC2 (t3.micro)                       │  │  │  │
│  │  │  │                │    │                                       │  │  │  │
│  │  │  │ :80  ← 0.0.0.0 │    │  ┌─────────────────────────────────┐  │  │  │  │
│  │  │  │ :443 ← 0.0.0.0 │───▶│  │  Docker Compose (3 containers)  │  │  │  │  │
│  │  │  │ :22  ← My IP   │    │  │  ┌───────────────────────────┐  │  │  │  │  │
│  │  │  │                │    │  │  │  Nginx :80/443            │  │  │  │  │  │
│  │  │  └────────────────┘    │  │  │  (React Static Files)     │  │  │  │  │  │
│  │  │                        │  │  └───────────────────────────┘  │  │  │  │  │
│  │  │                        │  │  ┌───────────┐  ┌───────────┐   │  │  │  │  │
│  │  │                        │  │  │  NestJS   │  │ MySQL 8.4 │   │  │  │  │  │
│  │  │                        │  │  │  :3000    │  │ :3306     │   │  │  │  │  │
│  │  │                        │  │  └───────────┘  └───────────┘   │  │  │  │  │
│  │  │                        │  └─────────────────────────────────┘  │  │  │  │
│  │  │                        │                                       │  │  │  │
│  │  │                        │  EBS Volume (30GB gp3)                │  │  │  │
│  │  │                        │  ┌─────────────────────────────────┐  │  │  │  │
│  │  │                        │  │  ~/mysql   (DB Data)            │  │  │  │  │
│  │  │                        │  │  ~/static  (Image Files)        │  │  │  │  │
│  │  │                        │  │  ~/logs    (Log Files)          │  │  │  │  │
│  │  │                        │  └─────────────────────────────────┘  │  │  │  │
│  │  │                        └───────────────────────────────────────┘  │  │  │
│  │  └───────────────────────────────────────────────────────────────────┘  │  │
│  └─────────────────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────────────────┘

Infrastructure Decisions

항목 선택 이유
EC2 vs ECS/EKS EC2 단일 인스턴스로 충분, 프리티어 활용
RDS vs EC2 내 DB EC2 내 Docker 비용 절감, 추후 서버 이전 시 데이터 마이그레이션 용이
S3 vs EBS EBS 이미지를 로컬 저장, 서버 이전 시 함께 이동 가능
ALB vs Nginx Nginx (컨테이너) 단일 인스턴스에서 ALB 불필요, 비용 절감

Application Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         Client (Browser)                        │
└─────────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────┐
│                         Nginx (HTTPS)                           │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐  │
│  │   Static Files  │  │   SPA Routing   │  │   API Proxy     │  │
│  │   (React App)   │  │   (index.html)  │  │   (/api/*)      │  │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
                    │                              │
                    │ Frontend Network             │
                    ▼                              ▼
           ┌──────────────┐              ┌──────────────────┐
           │  React SPA   │              │   NestJS API     │
           │  (Static)    │              │   (Port 3000)    │
           └──────────────┘              └──────────────────┘
                                                  │
                                                  │ Backend Network
                                                  ▼
                                         ┌──────────────────┐
                                         │   MySQL 8.4      │
                                         │   (Port 3306)    │
                                         └──────────────────┘

Key Features

Authentication

  • JWT 기반 인증: Access Token + Refresh Token 전략
  • 보안 강화:
    • Refresh Token은 bcrypt로 해시화하여 DB 저장
    • HttpOnly 쿠키를 통한 Refresh Token 전송
    • Access Token 만료 시 자동 갱신 (Axios Interceptor)
  • Rate Limiting: Brute-force 공격 방지를 위한 요청 제한

Rich Text Editor

  • Tiptap 에디터 기반의 WYSIWYG 편집기
  • 지원 기능:
    • 텍스트 스타일링 (Bold, Italic, Strikethrough)
    • 제목 레벨 (H1, H2, H3)
    • 목록 (순서 있음/없음)
    • 인용문
    • 이미지 삽입 (드래그 앤 드롭, 붙여넣기, 버튼 클릭)
image

Image Management

  • 이미지 최적화: Sharp를 사용한 WebP 변환 (최대 1920px, 품질 80%)
  • 자동 이미지 정리: 매일 새벽 미사용 이미지 자동 삭제
  • 이미지-게시글 연결: 게시글에 포함된 이미지 URL 파싱 후 자동 연결

Infinite Scroll

  • IntersectionObserver API 활용
  • TanStack Query의 useInfiniteQuery로 페이지네이션

Type Safety

  • Monorepo 구조에서 타입 정의 패키지 공유
  • 프론트엔드와 백엔드 간 API 계약 보장
  • 컴파일 타임 타입 검증

API Endpoints

Authentication

Method Endpoint Description
POST /api/auth/signup 회원가입
POST /api/auth/login 로그인
POST /api/auth/refresh 토큰 갱신
POST /api/auth/logout 로그아웃
GET /api/auth/me 현재 사용자 정보

Posts

Method Endpoint Description
GET /api/posts 게시글 목록 (페이지네이션)
GET /api/posts/:id 게시글 상세
POST /api/posts 게시글 작성 (인증 필요)
PATCH /api/posts/:id 게시글 수정 (작성자만)
DELETE /api/posts/:id 게시글 삭제 (작성자만)

Images

Method Endpoint Description
POST /api/images/upload 이미지 업로드 (인증 필요)

Security Features

  • Helmet.js: HTTP 보안 헤더 설정
  • Rate Limiting: API 요청 횟수 제한
  • Input Validation: class-validator를 통한 요청 데이터 검증
  • Prisma Exception Filter: DB 에러 안전하게 처리
  • Password Hashing: bcrypt를 사용한 비밀번호 해시화
  • Network Isolation: Docker 네트워크 분리 (프론트엔드/백엔드)
  • Health Check Protection: 내부 전용 헬스체크 엔드포인트

Project Structure

biswine/
├── apps/
│   ├── api/            # NestJS 백엔드
│   └── web/            # React 프론트엔드
├── packages/
│   └── shared-types/   # API 타입 정의 공유
├── compose.prod.yml    # 프로덕션 Docker Compose
├── compose.local.yml   # 로컬 개발 환경
└── biome.json          # 코드 포매터/린터 설정

Getting Started

Prerequisites

  • Node.js 22+
  • pnpm 10+
  • Docker & Docker Compose

Development

# 의존성 설치
pnpm install

# 로컬 MySQL 실행
docker compose -f compose.local.yml up -d

# API 개발 서버
cd apps/api
pnpm db:generate
pnpm db:migrate-dev
pnpm dev

# Web 개발 서버
cd apps/web
pnpm dev

Production Deployment

# 환경 변수 설정
cp .env.example .env.production

# 빌드 및 배포
pnpm build
pnpm dup

About

와인 홍보 및 이벤트 안내를 위한 블로그 서비스

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published