🚀 Environment Setup & Deployment
이 가이드는 CheonYakPlanet의 개발, 테스트, 프로덕션 환경 설정 및 배포 절차를 제공합니다.
배포 가이드 문서
CheonYakPlanet 백엔드 관련 문서입니다.
이 가이드는 CheonYakPlanet의 개발, 테스트, 프로덕션 환경 설정 및 배포 절차를 제공합니다.
# OpenJDK 17 설치
sudo apt update
sudo apt install openjdk-17-jdk
# 설치 확인
java -version
javac -version
# 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;
# 리포지토리 클론
git clone https://github.com/your-org/cheonyakplanet-be.git
cd cheonyakplanet-be
# gradlew 실행 권한 설정
chmod +x gradlew
# 빌드
./gradlew build
.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
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
./gradlew bootRun --args='--spring.profiles.active=dev'
# 또는
export SPRING_PROFILES_ACTIVE=dev
./gradlew bootRun
./gradlew clean build -Pprod
java -jar -Dspring.profiles.active=prod build/libs/be-1.0.0.jar
./gradlew test
./gradlew test --tests UserServiceTest
./gradlew jacocoTestReport
./gradlew jacocoTestCoverageVerification
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"]
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-compose up --build
docker-compose up -d
docker-compose logs -f app
docker-compose down
docker-compose down -v
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
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;
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
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;
}
}
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
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 8080
sudo ufw enable
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'@'%';
management:
endpoints:
web:
exposure:
include: health, info, metrics, prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
<!-- 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>
curl http://localhost:8080/actuator/health
curl http://localhost:8080/actuator/health/db
curl http://localhost:8080/actuator/health/external-apis
.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"
sudo systemctl status mysql
mysql -h $DB_HOST -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD
sudo ufw status
sudo journalctl -u cheonyakplanet -f
sudo netstat -tlnp | grep 8080
java -version
java -Xmx2048m -Xms1024m -jar app.jar
top -p $(pgrep java)
java -jar \
-Xmx4g \
-Xms2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+UseStringDeduplication \
app.jar
EXPLAIN SELECT * FROM subscription_info WHERE region = 'Seoul';
CREATE INDEX idx_subscription_region_city ON subscription_info(region, city);
ANALYZE TABLE subscription_info;
Deployment Guide Version: 1.0
Last Updated: 2025-06-26
Environment: Production-ready