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
21 changes: 21 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
node_modules/
.env*
!.env.example
dist

uploads

coverage
*.test.js

*.md

.git
.gitignore
.github

src/generated/

#dev test files
http
example
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
DATABASE_URL="postgresql://postgres:password@localhost:5432/sprint_mission?schema=public"
POSTGRES_USER="postgres"
POSTGRES_PASSWORD="password"
POSTGRES_DB="sprint_mission"


PORT=3000
SOCKET_PORT=8080
JWT_SECRET="t7HVUdpWIjfHXhZGGIkZHxuFyBPXixJr"
Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Deploy to EC2

on:
push:
branches: [main, 박재성, 박재성-sprint11]

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: 코드 체크아웃
uses: actions/checkout@v5

- name: Node.js 설치
uses: actions/setup-node@v5
with:
node-version: 'lts/*'

- name: EC2 배포
uses: appleboy/ssh-action@v1.2.2
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script: |
# 코드 최신화
cd ~/3-sprint-mission
git pull origin main

# 패키지 설치 및 DB 마이그레이션
npm install
npx prisma generate
npx prisma migrate deploy

# 빌드 및 재시작
npm run build

# PM2로 애플리케이션 관리
pm2 stop 3-sprint-mission || true
pm2 delete 3-sprint-mission || true
pm2 start dist/main.js --name "3-sprint-mission" --env production

# 배포 검증
pm2 status
# curl -f http://localhost:3000/health || exit 1

- name: 배포 성공 알림
if: success()
run: |
echo "✅ 배포가 성공적으로 완료되었습니다!"

- name: 배포 실패 알림
if: failure()
run: |
echo "❌ 배포가 실패했습니다!"
67 changes: 67 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Test

on:
push:
branches: [main, 박재성, 박재성-sprint11]
pull_request:
branches: [main, 박재성, 박재성-sprint11]

jobs:
test:
runs-on: ubuntu-latest

# PostgreSQL 서비스 컨테이너 (테스트용 DB 자동 생성)
services:
postgres:
image: postgres:17.5
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: test_password
POSTGRES_DB: test-sprint_mission
ports:
- 5432:5432
# DB 준비 완료까지 대기
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- name: 코드 체크아웃
uses: actions/checkout@v5

- name: Node.js 설치
uses: actions/setup-node@v5
with:
node-version: 'lts/*'

- name: 패키지 설치
run: npm install

- name: Prisma 클라이언트 생성
env:
DATABASE_URL: postgresql://postgres:test_password@localhost:5432/test-sprint_mission?schema=public
run: npx prisma generate

- name: TypeScript 타입 체크
run: npm run build

- name: DB 스키마 생성 (테이블 생성)
env:
DATABASE_URL: postgresql://postgres:test_password@localhost:5432/test-sprint_mission?schema=public
run: npx prisma migrate deploy

- name: 테스트 데이터 삽입
env:
DATABASE_URL: postgresql://postgres:test_password@localhost:5432/test-sprint_mission?schema=public
run: npx prisma db seed

- name: 테스트 실행
env:
DATABASE_URL: postgresql://postgres:test_password@localhost:5432/test-sprint_mission?schema=public
NODE_ENV: test
PORT: 3000
ACCESS_TOKEN_SECRET: test_access_token_secret_key_for_ci
REFRESH_TOKEN_SECRET: test_refresh_token_secret_key_for_ci
run: npm run test:all -- --coverage=false
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ coverage/*
/src/generated/prisma

# dev test files
http
http
example
55 changes: 55 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# ====================================
# 빌드 스테이지 (build stage)
# ====================================
ARG NODE_VERSION=22.16.0
FROM node:${NODE_VERSION} AS build

# 작업 디렉터리
WORKDIR /app

# 의존성 모듈 설치
COPY package*.json ./
RUN npm ci

# openssl 설치
RUN apt-get update -y && apt-get install -y openssl

# 소스 코드 복사
COPY . .

# Prisma 클라이언트 생성
RUN npx prisma generate

# TypeScript 빌드
RUN npm run build

# 개발 의존성 제거 (프로덕션 최적화)
RUN npm prune --omit=dev

# ====================================
# 런타임 스테이지 (runtime stage)
# ====================================
FROM node:${NODE_VERSION}-slim AS runtime

# openssl 설치
RUN apt-get update -y && apt-get install -y openssl
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 중복 될 필요 없을 것 같은데 혹시 다른 이유가 있었나요 ??


# 보안을 위해 node 사용자 사용
USER node
WORKDIR /app

# 필요한 파일만 복사
COPY --chown=node:node --from=build /app/package*.json ./
COPY --chown=node:node --from=build /app/node_modules ./node_modules
COPY --chown=node:node --from=build /app/dist ./dist
COPY --chown=node:node --from=build /app/prisma ./prisma

# 환경
ENV NODE_ENV=production
EXPOSE 3000

# Prisma 및 bcrypt 등 네이티브 모듈 호환성을 위한 보강
RUN apk add --no-cache openssl libc6-compat

# 앱 시작: dist/app.js 기준
ENTRYPOINT [ "sh", "-c", "npx prisma migrate deploy && npm run start" ]
110 changes: 21 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,30 @@
# 📋 요구사항
## ✅ 요구사항

## 🎯 기본 요구사항
### Github Actions 활용

- [x] 프로젝트에 프로덕션 배포를 위한 환경 변수 설정을 해 주세요.
- `.env` 파일에 NODE_ENV, AWS_S3, AWS_RDS, JWT 등 프로덕션 환경 변수 설정
- `lib/constants.ts`를 통한 환경 변수 중앙 관리
- [x] 브랜치에 pull request가 발생하면 테스트를 실행하는 액션을 구현해 주세요.
- `.github/workflows/test.yml`
- PostgreSQL 컨테이너를 사용한 테스트 실행
- Pull Request 시 자동으로 테스트 및 커버리지 수집

### 🎨 AWS S3 적용
- [x] main 브랜치에 push가 발생하면 AWS 배포를 진행하는 액션을 구현해 주세요.
- `.github/workflows/deploy.yml`
- EC2 SSH 접속을 통한 자동 배포
- PM2를 사용한 무중단 재시작

- [x] AWS S3 버킷을 생성하고, 퍼블릭 액세스를 허용해 주세요.
- S3 버킷 생성 시 퍼블릭 액세스 차단 설정 해제
- CORS 설정으로 웹 애플리케이션에서 S3 접근 허용
- [x] 개인 Github 리포지터리에서 Actions 동작을 확인해 보세요.
- GitHub Secrets 설정: EC2_HOST, EC2_USER, EC2_PRIVATE_KEY, TEST_DB_PASSWORD

- [x] 일반 사용자가 S3 업로드된 파일에 접근할 수 있도록 S3 버킷 정책을 설정해 주세요.
- 버킷 정책에서 `s3:GetObject` 액션을 퍼블릭으로 허용
### Docker 이미지 만들기

- [x] AWS EC2에서 AWS S3를 사용하기 위한 액세스 키를 AWS IAM에서 발급하세요.
- S3 전용 IAM 사용자 생성 및 액세스 키 발급
- 최소 권한 원칙에 따라 필요한 S3 권한인 `s3:GetObject`, `s3:PutObject`만 부여
- 액세스 키를 EC2 환경 변수로 안전하게 설정
- [x] Express 서버를 실행하는 Dockerfile을 작성해 주세요.
- `Dockerfile` - Node.js 22 빌드, PM2 프로세스 관리

- [x] 프로덕션 환경에서는 파일 업로드에 AWS S3를 사용하도록 구현을 수정해 주세요.
- 기존 Cloudinary 업로드 방식을 S3 업로드로 변경
- multer 메모리 스토리지와 AWS SDK를 활용한 S3 업로드 구현
- 업로드된 파일의 퍼블릭 URL 반환
- [x] Express 서버가 파일 업로드를 처리하는 폴더는 Docker의 Volume을 활용하도록 구현해 주세요.
- `docker-compose.yaml` - `uploads-volume:/app/uploads` 볼륨 사용

### 🗄️ AWS RDS 적용
- [x] 데이터베이스는 Postgres 이미지를 사용해 연결하도록 구현해 주세요.
- PostgreSQL 17.5 이미지 사용, 환경 변수를 통한 연결 설정

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

- [x] RDS 인스턴스에 대한 보안 그룹을 설정합니다.
- EC2 인스턴스에서 RDS 접근을 위한 보안 그룹 규칙 설정
- 포트 5432(PostgreSQL) 열기

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

### 🖥️ AWS EC2에 Express 서버 배포하기

- [x] AWS EC2 프리티어에 해당하는 인스턴스를 생성합니다.
- t3.micro 인스턴스 생성 (프리티어 범위)
- 키 페어를 통한 SSH 접속 설정

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

## 🚀 심화 요구사항

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

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

- 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` 분리
- [x] 실행된 Express 서버 컨테이너는 호스트 머신에서 3000번 포트로 접근 가능하도록 구현해 주세요.
- `docker-compose.yaml` - 포트 매핑 `3000:3000` 설정
50 changes: 50 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: '3.9'

services:
app:
build:
args:
NODE_VERSION: 22.16.0
dockerfile: ./Dockerfile
context: .
container_name: sprint-mission-app
restart: always
ports:
- '3000:3000'
env_file:
- .env
networks:
- sprint-mission-network
volumes:
- uploads-volume:/app/uploads
depends_on:
db:
condition: service_healthy

db:
image: postgres:17.5
container_name: sprint-mission-db
restart: always
ports:
- '5432:5432'
env_file:
- .env
networks:
- sprint-mission-network
volumes:
- db-volume:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres -d sprint_mission']
interval: 5s
timeout: 3s
retries: 10

networks:
sprint-mission-network:
name: sprint-mission-network

volumes:
uploads-volume:
name: uploads-volume
db-volume:
name: db-volume
Loading