Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ PORT=3000
SOCKET_PORT=8080
JWT_SECRET="t7HVUdpWIjfHXhZGGIkZHxuFyBPXixJr"

#image Server
CLOUDINARY_CLOUD_NAME="your-cloud-name"
CLOUDINARY_API_KEY="1234567890"
CLOUDINARY_API_SECRET="your-api-secret"
NODE_ENV=production # production, development, test

# CORS & Cookie
CORS_ORIGIN=''
COOKIE_DOMAIN=''

# AWS
AWS_S3_REGION='ap-northeast-2'
AWS_S3_BUCKET=''
AWS_S3_ACCESS_KEY_ID=''
AWS_S3_SECRET_KEY_ID=''
43 changes: 0 additions & 43 deletions .github/workflows/coverage-badge.yml

This file was deleted.

125 changes: 80 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,98 @@
![Coverage](./coverage/badges.svg)
# 📋 요구사항

# 요구사항
## 🎯 기본 요구사항

## 기본 요구사항
- [x] 프로젝트에 프로덕션 배포를 위한 환경 변수 설정을 해 주세요.
- `.env` 파일에 NODE_ENV, AWS_S3, AWS_RDS, JWT 등 프로덕션 환경 변수 설정
- `lib/constants.ts`를 통한 환경 변수 중앙 관리

- [x] Jest의 테스트 커버리지 도구를 사용하도록 설정해 주세요.
- `jest.config.js`에서 `collectCoverage: true` 설정
- `coverageReporters`로 다양한 리포트 형식 지원 (text, lcov, json-summary)
- `coveragePathIgnorePatterns`로 불필요한 파일 제외 설정
### 🎨 AWS S3 적용

- [x] 인증이 필요하지 않은 상품 API에 대한 통합 테스트를 작성해 주세요.
- `test/integration/noAuth/product.test.ts`
- 상품 목록 조회, 상품 상세 조회 등 공개 API 테스트
- [x] AWS S3 버킷을 생성하고, 퍼블릭 액세스를 허용해 주세요.
- S3 버킷 생성 시 퍼블릭 액세스 차단 설정 해제
- CORS 설정으로 웹 애플리케이션에서 S3 접근 허용

- [x] 인증이 필요하지 않은 게시글 API에 대한 통합 테스트를 작성해 주세요.
- `test/integration/noAuth/article.test.ts`
- 게시글 목록 조회, 게시글 상세 조회 등 공개 API 테스트
- [x] 일반 사용자가 S3 업로드된 파일에 접근할 수 있도록 S3 버킷 정책을 설정해 주세요.
- 버킷 정책에서 `s3:GetObject` 액션을 퍼블릭으로 허용

- [x] 로그인, 회원가입 API에 대한 통합 테스트를 작성해 주세요.
- `test/integration/auth.test.ts`
- 회원가입, 로그인, 토큰 검증 등 인증 관련 API 테스트
- [x] AWS EC2에서 AWS S3를 사용하기 위한 액세스 키를 AWS IAM에서 발급하세요.
- S3 전용 IAM 사용자 생성 및 액세스 키 발급
- 최소 권한 원칙에 따라 필요한 S3 권한인 `s3:GetObject`, `s3:PutObject`만 부여
- 액세스 키를 EC2 환경 변수로 안전하게 설정

- [x] 인증이 필요한 상품 API에 대해 통합 테스트를 작성해 주세요.
- `test/integration/withAuth/product.test.ts`
- 상품 생성, 수정, 삭제, 좋아요, 댓글 등 인증 필요 API 테스트
- [x] 프로덕션 환경에서는 파일 업로드에 AWS S3를 사용하도록 구현을 수정해 주세요.
- 기존 Cloudinary 업로드 방식을 S3 업로드로 변경
- multer 메모리 스토리지와 AWS SDK를 활용한 S3 업로드 구현
- 업로드된 파일의 퍼블릭 URL 반환

- [x] 인증이 필요한 게시글 API에 대해 통합 테스트를 작성해 주세요.
- `test/integration/withAuth/article.test.ts`
- 게시글 생성, 수정, 삭제, 좋아요, 댓글 등 인증 필요 API 테스트
### 🗄️ AWS RDS 적용

## 심화 요구사항
- [x] AWS RDS 프리티어에 해당하는 인스턴스를 생성합니다.
- PostgreSQL 데이터베이스 인스턴스 생성

- [x] 상품 API의 비즈니스 로직에 대해 Mock, Spy를 활용해 유닛 테스트를 작성해 주세요.
- `test/unit/services/productService.test.ts`
- Repository와 Notification Service를 Mock으로 격리
- Service Layer 함수들에 Spy 적용하여 호출 추적
- 모든 비즈니스 로직 함수에 대한 성공/실패 케이스 테스트
- [x] RDS 인스턴스에 대한 보안 그룹을 설정합니다.
- EC2 인스턴스에서 RDS 접근을 위한 보안 그룹 규칙 설정
- 포트 5432(PostgreSQL) 열기

# 주요 변경사항
- [x] 프로덕션 환경에서는 Prisma에 프로젝트 데이터베이스와 연결하도록 합니다.
- RDS 연결을 위한 DATABASE_URL 포트 재설정
- Prisma 마이그레이션으로 데이터베이스 스키마 동기화
- SSH를 통한 데이터베이스 연결

## 테스트 스크립트 분리
### 🖥️ AWS EC2에 Express 서버 배포하기

- `test:integration`: 통합 테스트 실행 (데이터베이스 마이그레이션 포함)
- `test:unit`: 유닛 테스트만 실행
- `test:all`: 모든 테스트 실행
- `badge`: 커버리지 배지 생성
- [x] AWS EC2 프리티어에 해당하는 인스턴스를 생성합니다.
- t3.micro 인스턴스 생성 (프리티어 범위)
- 키 페어를 통한 SSH 접속 설정

## 통합 테스트 구현
- [x] SSH를 사용해 EC2 인스턴스에 접속해 Express 서버를 배포해 주세요.
- `start.sh` 파일을 통한 자동화된 서버 배포
- Git pull, 의존성 설치, Prisma 마이그레이션 자동화
- 환경 변수 설정 및 PM2를 통한 애플리케이션 실행

- `beforeAll`, `beforeEach`, `afterAll` 를 활용한 적절한 설정과 정리 작업
- 데이터베이스 시드 데이터 초기화로 일관된 테스트 환경 제공
- 공통 리소스 해제 (DB 연결, Socket 서버 등)로 메모리 누수 방지
## 🚀 심화 요구사항

## 유닛 테스트 구현
### ⚡ EC2 인스턴스에서 pm2 프로세스 매니저를 사용하여 애플리케이션을 실행해 주세요.

### Mock과 Spy 활용
- PM2 프로세스 관리 및 모니터링
- Node.js 애플리케이션 프로세스 관리
- 자동 재시작, 로그 관리 설정
- 시스템 재부팅 시 자동 시작 설정

- **Repository Mock**: `productRepository` 완전 격리로 데이터베이스 의존성 제거
- **Service Mock**: `notificationService` 격리로 외부 서비스 의존성 제거
- **Socket.IO Mock**: `test/mocks/socketMock.ts`로 포트 충돌 방지
- **Service Layer Spy**: 모든 비즈니스 로직 함수에 Spy 적용하여 호출 추적
- `test/mocks/productMocks.ts`에서 통일된 Mock 데이터 제공
- PM2 설정 파일 구성 (`ecosystem.config.js`)
- 환경 변수 중앙 관리 (AWS S3, RDS, JWT 등)
- 로그 파일 분리 (error.log, out.log, combined.log)
- 메모리 제한(100M) 및 자동 재시작 설정

### 🌐 EC2 인스턴스에서 nginx 리버스 프록시를 설정해 서버를 80번 포트로 서비스합니다.

- Nginx 리버스 프록시 설정
- Express 서버(포트 3000)를 80번 포트로 프록시
- 원본 요청 정보 전달을 위한 헤더 설정 (Host, X-Real-IP, X-Forwarded-For)
- HTTP/1.1 프로토콜 사용으로 성능 최적화

- Nginx 설정 파일 구성
- `/etc/nginx/default.d/` 경로에 프록시 설정 파일 생성
- `proxy_pass`를 통한 Express 애플리케이션 연결

# ✨ 주요 변경사항

## 📁 infra 폴더 생성

- LMS의 제출 안내대로 infra 폴더에 스프린트 미션 제출용 파일을 저장했습니다.
- `s3` 폴더에 버킷 정책 설정 스크린샷을 저장했습니다.
- `rds` 폴더에 RDS 인스턴스의 보안 그룹 설정 스크린샷들을 저장했습니다.
- `ec2` 폴더에 EC2 인스턴스의 보안 그룹 설정 스크린샷들을 저장했습니다.
- `ec2` 폴더에 `start.sh`, `ecosystem.config.js`, `nginx.conf`를 저장했습니다.

## 🚀 s3 이미지 업로드 구현

- **Cloudinary에서 AWS S3로 전환**
- 기존 Cloudinary 업로드 방식을 AWS S3 업로드로 완전 전환
- `src/utils/cloudinary.ts` → `src/utils/s3.ts`로 유틸리티 변경
- `src/controllers/imageController.ts`에서 S3 업로드 로직 구현

- **multer 및 aws sdk 활용**
- 디스크 저장 없이 메모리에서 직접 S3로 업로드 (`multer.memoryStorage()`)
- `@aws-sdk/client-s3` 패키지를 통한 S3 연동
- 타입 안전성을 위한 `src/types/s3Types.ts` 분리
42 changes: 42 additions & 0 deletions infra/ec2/ecosystem.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 환경 변수 추출
const {
NODE_ENV = "production",
PORT = 3000,
SOCKET_PORT = 8080,
DATABASE_URL,
JWT_SECRET,
AWS_S3_REGION,
AWS_S3_ACCESS_KEY_ID,
AWS_S3_SECRET_KEY_ID,
AWS_S3_BUCKET
} = process.env;

module.exports = {
apps: [
{
name: "sprint-mission", // pm2 list에 표시될 프로세스 이름
cwd: "/home/ec2-user/3-sprint-mission", // 프로젝트 루트 경로
script: "dist/main.js", // 컴파일된 JS 실행 파일
instances: 1, // 실행 인스턴스 수 (학습용은 1, 운영이면 'max'도 가능)
autorestart: true, // 크래시 시 자동 재시작
watch: false, // 코드 변경 감지 (운영 환경은 보통 false)
env: {
NODE_ENV,
PORT,
SOCKET_PORT,
DATABASE_URL,
JWT_SECRET,
AWS_S3_REGION,
AWS_S3_ACCESS_KEY_ID,
AWS_S3_SECRET_KEY_ID,
AWS_S3_BUCKET
},
error_file: "/home/ec2-user/logs/error.log",
out_file: "/home/ec2-user/logs/out.log",
log_file: "/home/ec2-user/logs/combined.log",
merge_logs: true,
max_memory_restart: "100M",
time: true
}
]
};
15 changes: 15 additions & 0 deletions infra/ec2/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# /etc/nginx/default.d/ 경로에 생성
location / {
proxy_pass http://127.0.0.1:3000; # Express 서버 주소/포트
proxy_http_version 1.1;

# 원본 요청 정보 전달(로그/쿠키/리다이렉션 등에 중요)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# [웹소켓 관련 설정] 업그레이드 헤더
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
}
Binary file added infra/ec2/secure-group-inbound.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added infra/ec2/secure-group-outbound.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions infra/ec2/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash

echo "🚀 배포 프로세스 시작..."

# --- 기본 설정 ---
PROJECT_DIR="/home/ec2-user/3-sprint-mission"

echo "📁 프로젝트 디렉토리: $PROJECT_DIR"

echo "📝 로그 디렉토리 생성..."
mkdir -p /home/ec2-user/logs

# --- 실행 단계 ---
echo "📂 프로젝트 디렉토리로 이동..."
cd $PROJECT_DIR

echo "🔄 최신 코드 가져오기..."
git pull

echo "📦 의존성 설치..."
npm install

echo "🔧 Prisma 클라이언트 재생성..."
npx prisma generate

echo "🗄️ 데이터베이스 마이그레이션..."
npx prisma migrate deploy

echo "🔨 TypeScript 빌드..."
npm run build

echo "🧹 devDependencies 정리..."
npm prune --production

echo "🚀 PM2 애플리케이션 시작..."
pm2 start infra/ec2/ecosystem.config.js

echo "💾 PM2 프로세스 목록 저장..."
pm2 save

echo "⚙️ PM2 자동 시작 설정..."
pm2 startup systemd -u ec2-user --hp /home/ec2-user

echo "✅ 배포 완료!"
Binary file added infra/rds/secure-group-inbound.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added infra/rds/secure-group-outbound.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added infra/s3/policy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading