Skip to content

Latest commit

ย 

History

History
920 lines (712 loc) ยท 18.3 KB

File metadata and controls

920 lines (712 loc) ยท 18.3 KB

๐Ÿ†˜ ON-AIR-mate ๋ฐฑ์—”๋“œ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ฐ€์ด๋“œ

๊ฐœ๋ฐœ ๋ฐ ์šด์˜ ์ค‘ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•œ ์™„์ „ ๊ฐ€์ด๋“œ

๐Ÿ“‹ ๋ชฉ์ฐจ

  1. ๐Ÿš€ ๋ฐฐํฌ ๊ด€๋ จ ๋ฌธ์ œ
  2. ๐Ÿ—„๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ œ
  3. ๐Ÿ”ง ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๋ฌธ์ œ
  4. โš™๏ธ PM2 ๊ด€๋ จ ๋ฌธ์ œ
  5. ๐ŸŒ ๋„คํŠธ์›Œํฌ ๋ฐ ์ ‘์† ๋ฌธ์ œ
  6. ๐Ÿค– GitHub Actions ๋ฌธ์ œ
  7. ๐Ÿ’พ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ ์„ฑ๋Šฅ ๋ฌธ์ œ
  8. ๐Ÿ”‘ ์ธ์ฆ ๋ฐ ๋ณด์•ˆ ๋ฌธ์ œ
  9. ๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊ทธ
  10. ๐Ÿ†˜ ์‘๊ธ‰ ๋ณต๊ตฌ ์ ˆ์ฐจ

๐Ÿš€ ๋ฐฐํฌ ๊ด€๋ จ ๋ฌธ์ œ

๋ฌธ์ œ 1: GitHub Actions ๋ฐฐํฌ ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • GitHub Actions ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์‹คํŒจํ•จ
  • "SSH connection failed" ์—๋Ÿฌ
  • "Permission denied" ์—๋Ÿฌ

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. GitHub Secrets ํ™•์ธ

# GitHub ๋ ˆํฌ์ง€ํ† ๋ฆฌ์—์„œ ํ™•์ธ
Settings โ†’ Secrets and variables โ†’ Actions

# ํ•„์š”ํ•œ Secrets:
EC2_KEY      # SSH ํ‚ค ํŒŒ์ผ ์ „์ฒด ๋‚ด์šฉ  
EC2_HOST     # 15.164.176.168
EC2_USER     # ec2-user

2. SSH ํ‚ค ํ˜•์‹ ํ™•์ธ

# ์˜ฌ๋ฐ”๋ฅธ ํ˜•์‹:
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
(ํ‚ค ๋‚ด์šฉ)
...
-----END RSA PRIVATE KEY-----

# ์ž˜๋ชป๋œ ํ˜•์‹:
- ๊ณต๋ฐฑ์ด๋‚˜ ์ค„๋ฐ”๊ฟˆ ๋ˆ„๋ฝ
- ํ—ค๋”/ํ‘ธํ„ฐ ๋ˆ„๋ฝ  
- ๋‹ค๋ฅธ ํ‚ค ํ˜•์‹ (OpenSSH ๋“ฑ)

3. EC2 ๋ณด์•ˆ๊ทธ๋ฃน ํ™•์ธ

# SSH ํฌํŠธ 22๋ฒˆ ์—ด๋ ค์žˆ๋Š”์ง€ ํ™•์ธ
Source: 0.0.0.0/0 (๋ชจ๋“  IP ํ—ˆ์šฉ)
๋˜๋Š” GitHub Actions IP ๋Œ€์—ญ

๐ŸŽฏ ์˜ˆ๋ฐฉ๋ฒ•

  • SSH ํ‚ค ์ •๊ธฐ์  ๊ฐฑ์‹ 
  • GitHub Secrets ๋ฐฑ์—…
  • ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ ํ…Œ์ŠคํŠธ

๋ฌธ์ œ 2: EC2 ์ˆ˜๋™ ๋ฐฐํฌ ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • git pull ์‹คํŒจ
  • npm install ์˜ค๋ฅ˜
  • pm2 restart ์‹คํŒจ

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. Git ๊ด€๋ จ ์˜ค๋ฅ˜

# ๋กœ์ปฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ ์ถฉ๋Œ
git stash
git pull origin main
git stash pop

# ๊ฐ•์ œ ์—…๋ฐ์ดํŠธ (์ฃผ์˜!)
git fetch origin
git reset --hard origin/main

2. ๊ถŒํ•œ ๋ฌธ์ œ

# ํŒŒ์ผ ๊ถŒํ•œ ํ™•์ธ
ls -la /home/ec2-user/on-air-mate

# ๊ถŒํ•œ ์ˆ˜์ •
sudo chown -R ec2-user:ec2-user /home/ec2-user/on-air-mate
chmod -R 755 /home/ec2-user/on-air-mate

3. ๋””์Šคํฌ ๊ณต๊ฐ„ ๋ถ€์กฑ

# ๋””์Šคํฌ ์‚ฌ์šฉ๋Ÿ‰ ํ™•์ธ
df -h

# ๋ถˆํ•„์š”ํ•œ ํŒŒ์ผ ์ •๋ฆฌ
sudo yum autoremove      # Amazon Linux

# ๋กœ๊ทธ ํŒŒ์ผ ์ •๋ฆฌ
pm2 flush
sudo journalctl --vacuum-time=7d

๐Ÿ—„๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ œ

๋ฌธ์ œ 1: RDS ์—ฐ๊ฒฐ ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • Error: connect ETIMEDOUT
  • Error: Access denied for user
  • Error: Unknown database 'onairmate'

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. ์—ฐ๊ฒฐ ์ •๋ณด ํ™•์ธ

# ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ™•์ธ
grep -E "(DATABASE_URL|DB_)" .env

# ์˜ฌ๋ฐ”๋ฅธ ํ˜•์‹:
DATABASE_URL="mysql://admin:on-air-mate@onairmate-db-seoul.cviw844m2ex4.ap-northeast-2.rds.amazonaws.com:3306/onairmate"

2. ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ

# RDS ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ
telnet onairmate-db-seoul.cviw844m2ex4.ap-northeast-2.rds.amazonaws.com 3306

# DNS ํ™•์ธ
nslookup onairmate-db-seoul.cviw844m2ex4.ap-northeast-2.rds.amazonaws.com

3. RDS ๋ณด์•ˆ๊ทธ๋ฃน ํ™•์ธ

# AWS Console์—์„œ ํ™•์ธ:
# RDS โ†’ onairmate-db-seoul โ†’ Connectivity & security โ†’ Security groups
# Inbound rules: MySQL/Aurora (3306) 
# Source: EC2 ๋ณด์•ˆ๊ทธ๋ฃน ๋˜๋Š” EC2 private IP

4. Prisma ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ

# Prisma ์—ฐ๊ฒฐ ํ™•์ธ
npx prisma db pull

# ์Šคํ‚ค๋งˆ ๋™๊ธฐํ™”
npx prisma generate

๋ฌธ์ œ 2: ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • Migration failed
  • Schema drift detected
  • Connection pool timeout

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒํƒœ ํ™•์ธ

# ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํžˆ์Šคํ† ๋ฆฌ ํ™•์ธ
npx prisma migrate status

# ์‹คํŒจํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ™•์ธ
npx prisma migrate resolve --rolled-back migration_name

2. ์Šคํ‚ค๋งˆ ๋ฆฌ์…‹ (๊ฐœ๋ฐœํ™˜๊ฒฝ๋งŒ)

# โš ๏ธ ์ฃผ์˜: ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์‚ญ์ œ๋จ
npx prisma migrate reset

# ์ƒˆ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
npx prisma migrate dev --name init

3. ์ˆ˜๋™ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

# SQL ์ง์ ‘ ์‹คํ–‰
npx prisma db execute --file ./migration.sql

# ๋˜๋Š” MySQL ํด๋ผ์ด์–ธํŠธ๋กœ ์ ‘์†
mysql -h onairmate-db-seoul.cviw844m2ex4.ap-northeast-2.rds.amazonaws.com -u admin -p onairmate

๐Ÿ”ง ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๋ฌธ์ œ

๋ฌธ์ œ 1: ์˜์กด์„ฑ ์„ค์น˜ ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • npm ERR! peer dep missing
  • npm ERR! code EACCES
  • npm ERR! network timeout

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. npm ์บ์‹œ ์ •๋ฆฌ

# npm ์บ์‹œ ํ™•์ธ
npm cache verify

# ์บ์‹œ ์ •๋ฆฌ
npm cache clean --force

# node_modules ์™„์ „ ์žฌ์„ค์น˜
rm -rf node_modules package-lock.json
npm install

2. ๊ถŒํ•œ ๋ฌธ์ œ

# npm ๊ถŒํ•œ ์„ค์ • (๊ธ€๋กœ๋ฒŒ)
sudo chown -R $(whoami) ~/.npm

# ๋˜๋Š” nvm ์‚ฌ์šฉ ๊ถŒ์žฅ
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install 20
nvm use 20

3. ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ

# npm ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ํ™•์ธ
npm config get registry

# ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋ณ€๊ฒฝ (๊ตญ๋‚ด)
npm config set registry https://registry.npmjs.org/

# ํƒ€์ž„์•„์›ƒ ์ฆ๊ฐ€
npm config set timeout 60000

๋ฌธ์ œ 2: TypeScript ์ปดํŒŒ์ผ ์˜ค๋ฅ˜

๐Ÿ” ์ฆ์ƒ

  • error TS2307: Cannot find module
  • error TS2345: Argument of type is not assignable
  • error TS2532: Object is possibly 'undefined'

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. ํƒ€์ž… ์ •์˜ ํ™•์ธ

# ํƒ€์ž… ์ •์˜ ์„ค์น˜
npm install --save-dev @types/node @types/express

# tsconfig.json ํ™•์ธ
cat tsconfig.json

2. ๋ชจ๋“ˆ ํ•ด๊ฒฐ ๋ฌธ์ œ

// tsconfig.json ์ˆ˜์ •
{
  "compilerOptions": {
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true
  }
}

3. ํƒ€์ž… ์ฒดํฌ ๋ฌด์‹œ (์ž„์‹œ)

// ์ž„์‹œ ํ•ด๊ฒฐ์ฑ… (๊ถŒ์žฅํ•˜์ง€ ์•Š์Œ)
// @ts-ignore
const result = problematicCode();

// ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•
const result = problematicCode() as any;

โš™๏ธ PM2 ๊ด€๋ จ ๋ฌธ์ œ

๋ฌธ์ œ 1: PM2 ํ”„๋กœ์„ธ์Šค ์‹œ์ž‘ ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • PM2: Process failed to start
  • PM2: Application has thrown an uncaught exception
  • PM2: EADDRINUSE: address already in use :::3000

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. ํฌํŠธ ์ถฉ๋Œ ํ™•์ธ

# ํฌํŠธ ์‚ฌ์šฉ ํ™•์ธ
sudo ss -tulpn | grep :3000
sudo lsof -i :3000

# ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
sudo kill -9 PID_NUMBER

# ๋˜๋Š” ํฌํŠธ ๋ณ€๊ฒฝ
export PORT=3001

2. PM2 ์™„์ „ ์ดˆ๊ธฐํ™”

# ๋ชจ๋“  PM2 ํ”„๋กœ์„ธ์Šค ์ •์ง€
pm2 stop all
pm2 delete all
pm2 kill

# PM2 ์žฌ์‹œ์ž‘
pm2 start npm --name "onairmate-api" -- run start
pm2 save

3. PM2 ์„ค์ • ํŒŒ์ผ ์‚ฌ์šฉ

// ecosystem.config.js ์ƒ์„ฑ
module.exports = {
  apps: [{
    name: 'onairmate-api',
    script: 'npm',
    args: 'start',
    instances: 1,
    autorestart: true,
    watch: false,
    max_memory_restart: '1G',
    env: {
      NODE_ENV: 'production',
      PORT: 3000
    }
  }]
};
# ์„ค์ • ํŒŒ์ผ๋กœ ์‹œ์ž‘
pm2 start ecosystem.config.js

๋ฌธ์ œ 2: PM2 ์ž๋™ ์‹œ์ž‘ ์„ค์ • ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • ์„œ๋ฒ„ ์žฌ๋ถ€ํŒ… ํ›„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋˜์ง€ ์•Š์Œ
  • pm2 startup ์‹คํŒจ

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. Startup ์Šคํฌ๋ฆฝํŠธ ์žฌ์„ค์ •

# ๊ธฐ์กด startup ์ œ๊ฑฐ
pm2 unstartup

# ์ƒˆ๋กœ ์„ค์ •
pm2 startup
# ์ถœ๋ ฅ๋œ ๋ช…๋ น์–ด ์‹คํ–‰ (sudo ๊ถŒํ•œ ํ•„์š”)

# ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค ์ €์žฅ
pm2 save

2. ์‹œ์Šคํ…œ ์„œ๋น„์Šค ํ™•์ธ

# ์„œ๋น„์Šค ์ƒํƒœ ํ™•์ธ
sudo systemctl status pm2-ec2-user

# ์„œ๋น„์Šค ์žฌ์‹œ์ž‘
sudo systemctl restart pm2-ec2-user

# ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹œ์ž‘ ํ™œ์„ฑํ™”
sudo systemctl enable pm2-ec2-user

3. ์ˆ˜๋™ ํ…Œ์ŠคํŠธ

# ์„œ๋ฒ„ ์žฌ๋ถ€ํŒ…
sudo reboot

# ์žฌ์ ‘์† ํ›„ ํ™•์ธ
ssh -i your-key.pem ec2-user@15.164.176.168
pm2 status

๐ŸŒ ๋„คํŠธ์›Œํฌ ๋ฐ ์ ‘์† ๋ฌธ์ œ

๋ฌธ์ œ 1: ์™ธ๋ถ€์—์„œ ์„œ๋ฒ„ ์ ‘์† ๋ถˆ๊ฐ€

๐Ÿ” ์ฆ์ƒ

  • curl: (7) Failed to connect to 15.164.176.168 port 3000: Connection refused
  • This site can't be reached

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. ์„œ๋ฒ„ ์ƒํƒœ ํ™•์ธ

# PM2 ์ƒํƒœ ํ™•์ธ
pm2 status

# ํฌํŠธ ๋ฐ”์ธ๋”ฉ ํ™•์ธ
sudo ss -tulpn | grep :3000

# ๋กœ์ปฌ ์ ‘์† ํ…Œ์ŠคํŠธ
curl http://localhost:3000/health

2. ๋ณด์•ˆ๊ทธ๋ฃน ์„ค์ • ํ™•์ธ

# AWS Console โ†’ EC2 โ†’ Security Groups
# onairmate-sg ๊ทœ์น™ ํ™•์ธ:

Inbound Rules:
Type: Custom TCP
Port: 3000
Source: 0.0.0.0/0

Type: SSH
Port: 22
Source: 0.0.0.0/0 (๋˜๋Š” ํŠน์ • IP)

3. ๋ฐฉํ™”๋ฒฝ ํ™•์ธ (Amazon Linux)

# iptables ํ™•์ธ
sudo iptables -L

# ๋ฐฉํ™”๋ฒฝ ๋น„ํ™œ์„ฑํ™” (์ž„์‹œ)
sudo systemctl stop iptables

# ์˜๊ตฌ ๋น„ํ™œ์„ฑํ™” (๊ฐœ๋ฐœํ™˜๊ฒฝ๋งŒ)
sudo systemctl disable iptables

๋ฌธ์ œ 2: CORS ์—๋Ÿฌ

๐Ÿ” ์ฆ์ƒ

Access to fetch at 'http://15.164.176.168:3000/api/users' 
from origin 'http://localhost:3000' has been blocked by CORS policy

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. CORS ์„ค์ • ํ™•์ธ

// src/app.ts์—์„œ CORS ์„ค์ • ํ™•์ธ
const corsOptions = {
  origin: [
    'http://localhost:3000',
    'http://localhost:3001',
    'https://your-frontend-domain.com',
    'https://onairmate.vercel.app'
  ],
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization']
};

app.use(cors(corsOptions));

2. ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ ์ž„์‹œ ํ•ด๊ฒฐ

// ๋ชจ๋“  origin ํ—ˆ์šฉ (๊ฐœ๋ฐœ์šฉ๋งŒ)
app.use(cors({
  origin: true,
  credentials: true
}));

๐Ÿค– GitHub Actions ๋ฌธ์ œ

๋ฌธ์ œ 1: ์›Œํฌํ”Œ๋กœ์šฐ ๊ถŒํ•œ ์—๋Ÿฌ

๐Ÿ” ์ฆ์ƒ

  • Error: Permission denied (publickey)
  • Host key verification failed

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. SSH ํ‚ค ์žฌํ™•์ธ

# ๋กœ์ปฌ์—์„œ SSH ํ‚ค ํ™•์ธ
cat ~/.ssh/your-key.pem

# GitHub Secrets์˜ EC2_KEY์™€ ์™„์ „ํžˆ ๋™์ผํ•œ์ง€ ํ™•์ธ
# ๊ณต๋ฐฑ, ์ค„๋ฐ”๊ฟˆ, ํ—ค๋”/ํ‘ธํ„ฐ ๋ชจ๋‘ ํฌํ•จ

2. SSH ํ˜ธ์ŠคํŠธ ํ‚ค ๋ฌธ์ œ

# .github/workflows/deploy.yaml ์ˆ˜์ •
- name: Deploy to EC2
  run: |
    echo "${{ secrets.EC2_KEY }}" > private_key
    chmod 600 private_key
    ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
    ssh -o StrictHostKeyChecking=no -i private_key ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} '
      cd /home/ec2-user/on-air-mate &&
      git pull origin main &&
      npm ci &&
      npm run build &&
      pm2 restart onairmate-api
    '

๋ฌธ์ œ 2: ๋นŒ๋“œ ๋‹จ๊ณ„ ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • npm ERR! code ELIFECYCLE
  • npm ERR! errno 1
  • ESLint errors found

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. ๋กœ์ปฌ์—์„œ ๋ฏธ๋ฆฌ ํ…Œ์ŠคํŠธ

# ๋กœ์ปฌ์—์„œ ๋นŒ๋“œ ํ™•์ธ
npm run format
npm run build

# ์—๋Ÿฌ ์ˆ˜์ • ํ›„ ์ปค๋ฐ‹
git add .
git commit -m "[fix] ESLint ์—๋Ÿฌ ์ˆ˜์ •"
git push origin main

2. ๋นŒ๋“œ ์—๋Ÿฌ ๋ฌด์‹œ (์ž„์‹œ)

# .github/workflows/deploy.yaml
- name: Run ESLint
  run: npm run lint
  continue-on-error: true  # ์ž„์‹œ๋กœ ์—๋Ÿฌ ๋ฌด์‹œ

๐Ÿ’พ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ ์„ฑ๋Šฅ ๋ฌธ์ œ

๋ฌธ์ œ 1: ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ

๐Ÿ” ์ฆ์ƒ

  • PM2: Process failed due to memory limit
  • JavaScript heap out of memory

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ํ™•์ธ

# ์‹œ์Šคํ…œ ๋ฉ”๋ชจ๋ฆฌ ํ™•์ธ
free -h

# PM2 ํ”„๋กœ์„ธ์Šค๋ณ„ ๋ฉ”๋ชจ๋ฆฌ
pm2 monit

# ํ”„๋กœ์„ธ์Šค ๋ฉ”๋ชจ๋ฆฌ ์ƒ์„ธ
ps aux --sort=-%mem | head

2. ์Šค์™‘ ํŒŒ์ผ ์„ค์ •

# 1GB ์Šค์™‘ ์ƒ์„ฑ
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# ์˜๊ตฌ ์ ์šฉ
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

3. PM2 ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ ์„ค์ •

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'onairmate-api',
    script: 'npm',
    args: 'start',
    max_memory_restart: '500M',  // 500MB ์ดˆ๊ณผ์‹œ ์žฌ์‹œ์ž‘
    node_args: '--max-old-space-size=512'  // Node.js ํž™ ํฌ๊ธฐ ์ œํ•œ
  }]
};

๋ฌธ์ œ 2: ์‘๋‹ต ์†๋„ ์ €ํ•˜

๐Ÿ” ์ฆ์ƒ

  • API ์‘๋‹ต์ด 5์ดˆ ์ด์ƒ ๊ฑธ๋ฆผ
  • ํƒ€์ž„์•„์›ƒ ์—๋Ÿฌ ๋ฐœ์ƒ

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง

# CPU ์‚ฌ์šฉ๋ฅ  ํ™•์ธ
htop

# ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ƒํƒœ
ss -s

# ๋””์Šคํฌ I/O ํ™•์ธ
iostat -x 1

2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ํ’€ ์ตœ์ ํ™”

// src/db.config.ts
export const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  connectionLimit: 10,        // ์—ฐ๊ฒฐ ์ˆ˜ ์ œํ•œ
  acquireTimeout: 60000,      // ์—ฐ๊ฒฐ ๋Œ€๊ธฐ ์‹œ๊ฐ„
  timeout: 60000,             // ์ฟผ๋ฆฌ ํƒ€์ž„์•„์›ƒ
  reconnect: true,            // ์ž๋™ ์žฌ์—ฐ๊ฒฐ
  queueLimit: 0
});

๐Ÿ”‘ ์ธ์ฆ ๋ฐ ๋ณด์•ˆ ๋ฌธ์ œ

๋ฌธ์ œ 1: JWT ํ† ํฐ ๊ฒ€์ฆ ์‹คํŒจ

๐Ÿ” ์ฆ์ƒ

  • JsonWebTokenError: invalid token
  • TokenExpiredError: jwt expired

๐Ÿ› ๏ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

1. JWT Secret ํ™•์ธ

# ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ™•์ธ
grep JWT_SECRET .env

# ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๋™์ผํ•œ secret ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ™•์ธ

2. ํ† ํฐ ํ˜•์‹ ํ™•์ธ

// ์˜ฌ๋ฐ”๋ฅธ ํ† ํฐ ํ˜•์‹
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

// ์ž˜๋ชป๋œ ํ˜•์‹
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...  // Bearer ๋ˆ„๋ฝ

3. ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ์กฐ์ •

// src/auth/jwt.ts
export const generateToken = (user: { id: string; nickname: string }) => {
  return jwt.sign(user, JWT_SECRET, {
    expiresIn: '7d',  // 7์ผ๋กœ ์—ฐ์žฅ
  });
};

๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊ทธ

๋กœ๊ทธ ํ™•์ธ ๋ฐฉ๋ฒ•

1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ

# PM2 ๋กœ๊ทธ ์‹ค์‹œ๊ฐ„ ํ™•์ธ
pm2 logs onairmate-api

# ์—๋Ÿฌ ๋กœ๊ทธ๋งŒ
pm2 logs onairmate-api --err

# ํŠน์ • ์ค„ ์ˆ˜๋งŒ
pm2 logs onairmate-api --lines 100

# ๋กœ๊ทธ ํŒŒ์ผ ์ง์ ‘ ํ™•์ธ
tail -f ~/.pm2/logs/onairmate-api-out.log
tail -f ~/.pm2/logs/onairmate-api-error.log

2. ์‹œ์Šคํ…œ ๋กœ๊ทธ

# ์‹œ์Šคํ…œ ๋กœ๊ทธ
sudo journalctl -u pm2-ec2-user -f

# ๋ถ€ํŒ… ๋กœ๊ทธ
sudo journalctl -b

# ์‹œ๊ฐ„ ๋ฒ”์œ„๋ณ„ ๋กœ๊ทธ
sudo journalctl --since "1 hour ago"

3. ์›น์„œ๋ฒ„ ์ ‘๊ทผ ๋กœ๊ทธ

# Nginx ๋กœ๊ทธ (์‚ฌ์šฉ์‹œ)
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

๐Ÿ†˜ ์‘๊ธ‰ ๋ณต๊ตฌ ์ ˆ์ฐจ

์„œ๋น„์Šค ์™„์ „ ์ค‘๋‹จ ์‹œ

1๋‹จ๊ณ„: ๋น ๋ฅธ ์ง„๋‹จ

# EC2 ์ ‘์† ํ™•์ธ
ssh -i your-key.pem ec2-user@15.164.176.168

# ๊ธฐ๋ณธ ์ƒํƒœ ํ™•์ธ
pm2 status
curl http://localhost:3000/health
free -h
df -h

2๋‹จ๊ณ„: ์„œ๋น„์Šค ์žฌ์‹œ์ž‘

# PM2 ๊ฐ•์ œ ์žฌ์‹œ์ž‘
pm2 restart onairmate-api --force

# ์‹คํŒจ ์‹œ ์™„์ „ ์žฌ์‹œ์ž‘
pm2 delete onairmate-api
pm2 start npm --name "onairmate-api" -- run start
pm2 save

3๋‹จ๊ณ„: ๋ฐฑ์—…์—์„œ ๋ณต๊ตฌ

# ์ฝ”๋“œ ๋ฐฑ์—…์—์„œ ๋ณต๊ตฌ
cd /home/ec2-user
git clone https://github.com/ON-AIR-mate/Node.js.git on-air-mate-backup
cd on-air-mate-backup

# ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋ณต์‚ฌ
cp ../on-air-mate/.env .

# ์˜์กด์„ฑ ์„ค์น˜ ๋ฐ ์‹œ์ž‘
npm install
npm run build
pm2 start npm --name "onairmate-api-backup" -- run start

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ถˆ๊ฐ€ ์‹œ

1. ์ž„์‹œ ๋Œ€์•ˆ ์„œ๋น„์Šค

// ์ž„์‹œ ์‘๋‹ต์„ ์œ„ํ•œ Mock API
app.get('/api/*', (req, res) => {
  res.status(503).json({
    success: false,
    error: {
      code: 'DATABASE_MAINTENANCE',
      message: '๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ๊ฒ€ ์ค‘์ž…๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.'
    }
  });
});

2. RDS ์žฌ์‹œ์ž‘

# AWS CLI๋กœ RDS ์žฌ์‹œ์ž‘ (๊ถŒํ•œ ํ•„์š”)
aws rds reboot-db-instance --db-instance-identifier onairmate-db-seoul

# ๋˜๋Š” AWS Console์—์„œ ์ˆ˜๋™ ์žฌ์‹œ์ž‘

GitHub Actions ๋ฐฐํฌ ๋ถˆ๊ฐ€ ์‹œ

1. ์ˆ˜๋™ ๋ฐฐํฌ๋กœ ์ „ํ™˜

# EC2์—์„œ ์ง์ ‘ ๋ฐฐํฌ
cd /home/ec2-user/on-air-mate
git pull origin main
npm ci
npm run build
pm2 restart onairmate-api

2. ๋กค๋ฐฑ

# ์ด์ „ ์ปค๋ฐ‹์œผ๋กœ ๋กค๋ฐฑ
git log --oneline -10
git reset --hard COMMIT_HASH
npm run build
pm2 restart onairmate-api

๐Ÿ“ž ๊ธด๊ธ‰ ์—ฐ๋ฝ์ฒ˜ ๋ฐ ๋ฆฌ์†Œ์Šค

๋ฌธ์„œ ๋ฐ ์ฐธ๊ณ ์ž๋ฃŒ

์™ธ๋ถ€ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ

# ์„œ๋ฒ„ ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง (Uptime Robot ๋“ฑ)
curl -X POST "https://api.uptimerobot.com/v2/getMonitors" \
     -d "api_key=YOUR_API_KEY" \
     -d "format=json"

๋ฐฑ์—… ๋ฐ ๋ณต๊ตฌ

# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—…
mysqldump -h onairmate-db-seoul.cviw844m2ex4.ap-northeast-2.rds.amazonaws.com \
          -u admin -p onairmate > backup_$(date +%Y%m%d).sql

# ์ฝ”๋“œ ๋ฐฑ์—…
tar -czf backup_$(date +%Y%m%d).tar.gz /home/ec2-user/on-air-mate

โœ… ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ

๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ํ™•์ธ ์ˆœ์„œ

  • PM2 ํ”„๋กœ์„ธ์Šค ์ƒํƒœ ํ™•์ธ (pm2 status)
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ ํ™•์ธ (pm2 logs onairmate-api)
  • ์‹œ์Šคํ…œ ๋ฆฌ์†Œ์Šค ํ™•์ธ (free -h, df -h)
  • ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ํ™•์ธ (curl localhost:3000/health)
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ํ™•์ธ (npx prisma db pull)
  • ๋ณด์•ˆ๊ทธ๋ฃน ์„ค์ • ํ™•์ธ (AWS Console)
  • ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ™•์ธ (grep -E "(DATABASE_URL|JWT_SECRET)" .env)
  • GitHub Actions ์›Œํฌํ”Œ๋กœ์šฐ ํ™•์ธ (Actions ํƒญ)

๋ณต๊ตฌ ํ›„ ํ™•์ธ์‚ฌํ•ญ

  • ํ—ฌ์Šค์ฒดํฌ ์ •์ƒ ์‘๋‹ต (curl http://15.164.176.168:3000/health)
  • API ๋ฌธ์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅ (http://15.164.176.168:3000/api-docs)
  • PM2 ์ž๋™ ์‹œ์ž‘ ์„ค์ • (pm2 startup, pm2 save)
  • ๋กœ๊ทธ ๋กœํ…Œ์ด์…˜ ์ž‘๋™ ํ™•์ธ
  • ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ •์ƒ ๋ฒ”์œ„
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์•ˆ์ •์„ฑ

๐ŸŽฏ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

CPU ์ตœ์ ํ™”

# PM2 ํด๋Ÿฌ์Šคํ„ฐ ๋ชจ๋“œ (๋ฉ€ํ‹ฐ์ฝ”์–ด ํ™œ์šฉ)
pm2 start ecosystem.config.js
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'onairmate-api',
    script: 'npm',
    args: 'start',
    instances: 'max',  // CPU ์ฝ”์–ด ์ˆ˜๋งŒํผ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    exec_mode: 'cluster'
  }]
};

๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”

// ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€
process.on('exit', () => {
  console.log('Process exiting...');
});

process.on('SIGINT', () => {
  console.log('SIGINT received, shutting down gracefully');
  process.exit(0);
});

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ตœ์ ํ™”

// ์—ฐ๊ฒฐ ํ’€ ์ตœ์ ํ™”
export const pool = mysql.createPool({
  connectionLimit: 10,
  acquireTimeout: 60000,
  timeout: 60000,
  reconnect: true
});

๐Ÿšจ Remember: ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ๋‹นํ™ฉํ•˜์ง€ ๋ง๊ณ  ์ฒด๊ณ„์ ์œผ๋กœ ์ ‘๊ทผํ•˜์„ธ์š”!

  1. ๋กœ๊ทธ ํ™•์ธ โ†’ 2. ์ƒํƒœ ์ง„๋‹จ โ†’ 3. ๋‹จ๊ณ„๋ณ„ ํ•ด๊ฒฐ โ†’ 4. ์žฌ๋ฐœ ๋ฐฉ์ง€