배포 가이드

배포 가이드 문서

CheonYakPlanet 백엔드 관련 문서입니다.

Deployment Guide

🚀 Environment Setup & Deployment

이 가이드는 CheonYakPlanet의 개발, 테스트, 프로덕션 환경 설정 및 배포 절차를 제공합니다.

📋 Prerequisites

System Requirements

  • Java: OpenJDK 17 이상
  • Database: MySQL 8.0 이상
  • Build Tool: Gradle 8.0 이상
  • Memory: 최소 4GB RAM (권장 8GB)
  • Storage: 10GB 이상 여유 공간

Development Tools

  • IDE: IntelliJ IDEA 또는 VS Code
  • Git: 버전 2.20 이상
  • Docker: 컨테이너 배포 (선택 사항)
  • Postman: API 테스트

🔧 Environment Configuration

1. Local Development Environment

Java Installation
# OpenJDK 17 설치
sudo apt update
sudo apt install openjdk-17-jdk

# 설치 확인
java -version
javac -version
MySQL Database Setup
# MySQL 8.0 설치
sudo apt install mysql-server-8.0

# 보안 설정
sudo mysql_secure_installation

# 데이터베이스 및 사용자 생성
mysql -u root -p
-- 데이터베이스 생성
CREATE DATABASE planet CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 사용자 생성 및 권한 부여
CREATE USER 'planet_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON planet.* TO 'planet_user'@'localhost';
FLUSH PRIVILEGES;
Project Setup
# 리포지토리 클론
git clone https://github.com/your-org/cheonyakplanet-be.git
cd cheonyakplanet-be

# gradlew 실행 권한 설정
chmod +x gradlew

# 빌드
./gradlew build

2. Environment Variables

.env 파일을 프로젝트 루트에 생성:

DB_HOST=localhost
DB_PORT=3306
DB_NAME=planet
DB_USERNAME=planet_user
DB_PASSWORD=secure_password

JWT_SECRET=your_super_secure_jwt_secret_key_min_256_bits
JWT_ACCESS_TOKEN_EXPIRY=3600000
JWT_REFRESH_TOKEN_EXPIRY=86400000

NAVER_CLIENT_ID=your_naver_client_id
NAVER_CLIENT_SECRET=your_naver_client_secret
KAKAO_API_KEY=your_kakao_api_key
LH_API_KEY=your_lh_api_key
REALESTATE_API_KEY=your_realestate_api_key
GEMINI_API_KEY=your_gemini_api_key

MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your_email@gmail.com
MAIL_PASSWORD=your_app_password

CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080
NEWS_CRAWL_ENABLED=true
CHAT_DAILY_LIMIT=15
SCHEDULER_ENABLED=true

LOG_LEVEL=DEBUG
LOG_FILE_PATH=./logs/application.log
Environment-Specific Configuration

application-dev.yml

spring:
  datasource:
    url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?useSSL=false&allowPublicKeyRetrieval=true
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
logging:
  level:
    org.cheonyakplanet.be: DEBUG
    org.springframework.web: DEBUG

application-prod.yml

spring:
  datasource:
    url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?useSSL=true&requireSSL=true
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
logging:
  level:
    org.cheonyakplanet.be: INFO
    org.springframework.web: WARN

3. External API Setup

Naver News API
  1. Visit Naver Developers
  2. Create application and obtain Client ID/Secret
  3. Add to environment variables
Kakao Developers
  1. Visit Kakao Developers
  2. Create application for Maps API
  3. Get REST API key
LH Corporation API
  1. Visit Korea Data Portal
  2. Register for LH APIs
  3. Obtain service key
Google Gemini API
  1. Visit Google AI Studio
  2. Create project and get API key
  3. Enable Gemini Pro API

🏃‍♂️ Running the Application

Development Mode

./gradlew bootRun --args='--spring.profiles.active=dev'
# 또는
export SPRING_PROFILES_ACTIVE=dev
./gradlew bootRun

Production Mode

./gradlew clean build -Pprod
java -jar -Dspring.profiles.active=prod build/libs/be-1.0.0.jar

Testing

./gradlew test
./gradlew test --tests UserServiceTest
./gradlew jacocoTestReport
./gradlew jacocoTestCoverageVerification

🐳 Docker Deployment

Dockerfile

FROM openjdk:17-jdk-slim
WORKDIR /app
COPY build/libs/be-1.0.0.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]

Docker Compose

version: '3.8'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      DB_HOST: mysql
      DB_USERNAME: planet_user
      DB_PASSWORD: secure_password
    depends_on:
      - mysql
    restart: unless-stopped
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: planet
      MYSQL_USER: planet_user
      MYSQL_PASSWORD: secure_password
      MYSQL_ROOT_PASSWORD: root_password
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    restart: unless-stopped
volumes:
  mysql_data:

Docker Commands

docker-compose up --build
docker-compose up -d
docker-compose logs -f app
docker-compose down
docker-compose down -v

☁️ Cloud Deployment

AWS Deployment

EC2 Setup
sudo yum update -y
sudo yum install java-17-amazon-corretto-devel -y
sudo yum install mysql -y
sudo useradd -m -s /bin/bash cheonyak
sudo mkdir -p /opt/cheonyakplanet\sudo chown cheonyak:cheonyak /opt/cheonyakplanet
RDS Database Setup
  1. Create RDS MySQL 8.0 instance
  2. Configure security groups for application access
  3. Create database and user
CREATE DATABASE planet CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'planet_user'@'%' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON planet.* TO 'planet_user'@'%';
FLUSH PRIVILEGES;
Application Deployment
scp build/libs/be-1.0.0.jar ec2-user@your-server:/opt/cheonyakplanet/
sudo vim /etc/systemd/system/cheonyakplanet.service
[Unit]
Description=CheonYakPlanet Application
After=network.target

[Service]
Type=simple
User=cheonyak
WorkingDirectory=/opt/cheonyakplanet
ExecStart=/usr/bin/java -jar -Dspring.profiles.active=prod be-1.0.0.jar
Restart=always
RestartSec=10
Environment=DB_HOST=your-rds-endpoint
Environment=DB_USERNAME=planet_user
Environment=DB_PASSWORD=secure_password

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable cheonyakplanet
sudo systemctl start cheonyakplanet
sudo systemctl status cheonyakplanet
Load Balancer Setup
sudo yum install nginx -y
sudo vim /etc/nginx/nginx.conf
upstream cheonyakplanet {
  server localhost:8080;
}
server {
  listen 80;
  server_name your-domain.com;
  location / {
    proxy_pass http://cheonyakplanet;
    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;
  }
  location /ws/ {
    proxy_pass http://cheonyakplanet;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }
}

🔒 Security Configuration

SSL/TLS Setup

sudo yum install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your-domain.com
sudo crontab -e
# Add renewal job: 0 12 * * * /usr/bin/certbot renew --quiet

Firewall Configuration

sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 8080
sudo ufw enable

Database Security

DELETE FROM mysql.user WHERE User='';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost','127.0.0.1','::1');
ALTER USER 'root'@'localhost' IDENTIFIED BY 'strong_password';
CREATE USER 'planet_app'@'%' IDENTIFIED BY 'app_password';
GRANT SELECT,INSERT,UPDATE,DELETE ON planet.* TO 'planet_app'@'%';

📊 Monitoring & Observability

Application Monitoring

management:
  endpoints:
    web:
      exposure:
        include: health, info, metrics, prometheus
  endpoint:
    health:
      show-details: always
  metrics:
    export:
      prometheus:
        enabled: true

Log Configuration

<!-- logback-spring.xml -->
<configuration>
  <springProfile name="prod">
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>/opt/cheonyakplanet/logs/application.log</file>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>/opt/cheonyakplanet/logs/application.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
      </rollingPolicy>
      <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
      </encoder>
    </appender>
    <root level="INFO">
      <appender-ref ref="FILE"/>
    </root>
  </springProfile>
</configuration>

Health Checks

curl http://localhost:8080/actuator/health
curl http://localhost:8080/actuator/health/db
curl http://localhost:8080/actuator/health/external-apis

🔄 CI/CD Pipeline

GitHub Actions

.github/workflows/deploy.yml

name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Run tests
        run: ./gradlew test
      - name: Generate coverage report
        run: ./gradlew jacocoTestReport
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Build application
        run: ./gradlew build -Pprod
      - name: Build Docker image
        run: docker build -t cheonyakplanet:${{ github.sha }} .
      - name: Deploy to server
        run: |
          # Deploy script here
          ssh user@server "docker pull cheonyakplanet:${{ github.sha }} && docker-compose up -d --remove-orphans"

🛠️ Troubleshooting

Common Issues

Database Connection Issues
sudo systemctl status mysql
mysql -h $DB_HOST -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD
sudo ufw status
Application Startup Issues
sudo journalctl -u cheonyakplanet -f
sudo netstat -tlnp | grep 8080
java -version
Memory Issues
java -Xmx2048m -Xms1024m -jar app.jar
top -p $(pgrep java)

Performance Tuning

JVM Options
java -jar \
  -Xmx4g \
  -Xms2g \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -XX:+UseStringDeduplication \
  app.jar
Database Optimization
EXPLAIN SELECT * FROM subscription_info WHERE region = 'Seoul';
CREATE INDEX idx_subscription_region_city ON subscription_info(region, city);
ANALYZE TABLE subscription_info;

📋 Deployment Checklist

Pre-deployment
  • [ ] Environment variables configured
  • [ ] Database setup and migrated
  • [ ] External API keys obtained and tested
  • [ ] SSL certificates configured
  • [ ] Firewall rules configured
  • [ ] Monitoring setup
  • [ ] Backup strategy implemented
Post-deployment
  • [ ] Application health check passed
  • [ ] All endpoints responding correctly
  • [ ] WebSocket connections working
  • [ ] Scheduled tasks running
  • [ ] Logs generating properly
  • [ ] Monitoring alerts configured
  • [ ] Performance baseline established
Rollback Plan
  • [ ] Previous version JAR available
  • [ ] Database backup created
  • [ ] Rollback script tested
  • [ ] Team notification process
  • [ ] Health check validation

Deployment Guide Version: 1.0

Last Updated: 2025-06-26

Environment: Production-ready