
최근 사이드 프로젝트로 Spring Boot 기반의 RESTful API 서버를 개발하고 있었습니다. 로컬 환경에서는 Docker Compose를 활용해 Redis, MongoDB, MySQL을 함께 운영하며 개발을 진행했는데, 배포 단계에서 고민이 많았습니다.
AWS EC2나 GCP Compute Engine을 사용하자니 설정할 것이 너무 많고, Heroku는 2022년 11월부터 무료 플랜이 사라져서 대안을 찾고 있었죠. 그러던 중 개발자 커뮤니티에서 Railway라는 서비스를 알게 되었고, 직접 마이그레이션해본 경험을 상세히 공유하고자 합니다.
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- MYSQL_HOST=mysql
- MYSQL_PORT=3306
- MYSQL_DATABASE=myapp
- MYSQL_USERNAME=app_user
- MYSQL_PASSWORD=secure_password
- REDIS_HOST=redis
- REDIS_PORT=6379
- MONGO_HOST=mongo
- MONGO_PORT=27017
- MONGO_DATABASE=myapp_logs
depends_on:
- mysql
- redis
- mongo
networks:
- app-network
mysql:
image: mysql:8.0
environment:
- MYSQL_DATABASE=myapp
- MYSQL_USER=app_user
- MYSQL_PASSWORD=secure_password
- MYSQL_ROOT_PASSWORD=root_password
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- app-network
redis:
image: redis:7-alpine
ports:
- "6379:6379"
command: redis-server --requirepass redis_password
volumes:
- redis_data:/data
networks:
- app-network
mongo:
image: mongo:6
environment:
- MONGO_INITDB_ROOT_USERNAME=mongo_user
- MONGO_INITDB_ROOT_PASSWORD=mongo_password
- MONGO_INITDB_DATABASE=myapp_logs
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
networks:
- app-network
volumes:
mysql_data:
redis_data:
mongo_data:
networks:
app-network:
driver: bridge
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Railway 대시보드에서 새 프로젝트를 생성하는 과정은 놀라울 정도로 간단했습니다.
# Railway CLI 설치 (선택사항)
npm install -g @railway/cli
# 로컬에서 Railway 프로젝트 연결
railway login
railway link [프로젝트-ID]
Railway의 가장 큰 장점 중 하나는 데이터베이스를 별도로 관리할 필요가 없다는 것입니다.
Railway Dashboard → Add Service → Database → MySQL
Railway에서 제공하는 MySQL 인스턴스 정보:
containers-us-west-xxx.railway.app6543railwayroot Railway Dashboard → Add Service → Database → Redis
Redis 연결 정보:
redis-xxx.railway.app6379Railway Dashboard → Add Service → Database → MongoDB
MongoDB 연결 정보:
mongodb://mongo:xxx@containers-us-west-xxx.railway.app:6034/railwayRailway는 각 데이터베이스 서비스마다 연결 정보를 환경변수로 자동 제공합니다. 이를 Spring Boot의 application.yml에 맞게 매핑해야 합니다.
# MySQL
MYSQL_URL=mysql://root:xxx@containers-us-west-xxx.railway.app:6543/railway
MYSQLHOST=containers-us-west-xxx.railway.app
MYSQLPORT=6543
MYSQLDATABASE=railway
MYSQLUSER=root
MYSQLPASSWORD=xxx
# Redis
REDIS_URL=redis://:xxx@redis-xxx.railway.app:6379
REDISHOST=redis-xxx.railway.app
REDISPORT=6379
REDISPASSWORD=xxx
# MongoDB
MONGO_URL=mongodb://mongo:xxx@containers-us-west-xxx.railway.app:6034/railway
MONGOHOST=containers-us-west-xxx.railway.app
MONGOPORT=6034
MONGOUSER=mongo
MONGOPASSWORD=xxx
MONGODATABASE=railway
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:prod}
# MySQL 설정
datasource:
url: jdbc:mysql://${MYSQLHOST:localhost}:${MYSQLPORT:3306}/${MYSQLDATABASE:myapp}?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: ${MYSQLUSER:root}
password: ${MYSQLPASSWORD:password}
driver-class-name: com.mysql.cj.jdbc.Driver
# JPA 설정
jpa:
hibernate:
ddl-auto: update
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
format_sql: true
# MongoDB 설정
data:
mongodb:
host: ${MONGOHOST:localhost}
port: ${MONGOPORT:27017}
database: ${MONGODATABASE:myapp_logs}
username: ${MONGOUSER:}
password: ${MONGOPASSWORD:}
authentication-database: admin
# Redis 설정
data:
redis:
host: ${REDISHOST:localhost}
port: ${REDISPORT:6379}
password: ${REDISPASSWORD:}
timeout: 60000
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
# 서버 설정
server:
port: ${PORT:8080}
# JWT 설정
jwt:
secret: ${JWT_SECRET:your-secret-key}
expiration: ${JWT_EXPIRATION:3600000}
# 파일 업로드 설정
file:
upload:
path: ${FILE_UPLOAD_PATH:/tmp/uploads}
max-size: ${FILE_MAX_SIZE:10485760}
Railway 대시보드에서 Variables 탭으로 이동해 다음 환경변수들을 추가했습니다.
# Spring 프로파일
SPRING_PROFILES_ACTIVE=prod
# JWT 설정
JWT_SECRET=your-super-secure-jwt-secret-key-here
JWT_EXPIRATION=3600000
# 파일 업로드 설정
FILE_UPLOAD_PATH=/app/uploads
FILE_MAX_SIZE=10485760
# 로깅 레벨
LOGGING_LEVEL_ROOT=INFO
LOGGING_LEVEL_COM_MYAPP=DEBUG
# Railway 포트 (자동 설정됨)
PORT=${{PORT}}
코드를 GitHub에 push하면 Railway가 자동으로 배포를 시작합니다.
git add .
git commit -m "Configure Railway deployment"
git push origin main
배포 과정은 Railway 대시보드의 Deployments 탭에서 실시간으로 확인할 수 있습니다.
배포가 완료되면 Railway에서 제공하는 도메인 (예: https://myapp-production-xxxx.up.railway.app)으로 접근할 수 있습니다.
Railway 대시보드에서 다음과 같은 메트릭을 실시간으로 확인할 수 있습니다.
Railway는 실시간 로그 스트리밍을 제공합니다.
# Railway CLI로 로그 확인
railway logs --follow
# 특정 서비스 로그만 확인
railway logs --service mysql --follow
Spring Boot Actuator를 활용해 health check 엔드포인트를 구성했습니다.
@RestController
@RequestMapping("/actuator")
public class HealthController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private DataSource dataSource;
@GetMapping("/health")
public ResponseEntity<Map<String, String>> health() {
Map<String, String> status = new HashMap<>();
try {
// MySQL 연결 확인
try (Connection connection = dataSource.getConnection()) {
status.put("mysql", "UP");
}
// Redis 연결 확인
redisTemplate.opsForValue().set("health-check", "OK");
status.put("redis", "UP");
status.put("status", "UP");
return ResponseEntity.ok(status);
} catch (Exception e) {
status.put("status", "DOWN");
status.put("error", e.getMessage());
return ResponseEntity.status(503).body(status);
}
}
}
Railway는 사용량 기반 과금 모델을 사용합니다.
$20 무료 크레딧으로 약 1개월간 무료 사용이 가능합니다.
# Multi-stage build로 빌드 시간 단축
FROM gradle:7.6-jdk17 AS build
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle build --no-daemon
FROM openjdk:17-jdk-slim
COPY --from=build /home/gradle/src/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
Railway에서는 브랜치별로 다른 환경을 구성할 수 있습니다.
Railway는 자동으로 HTTPS를 적용하고, 커스텀 도메인도 쉽게 연결할 수 있습니다.
Settings → Environment → Custom Domain → Add Domain
각 데이터베이스는 자동 백업이 활성화되어 있지만, 중요한 데이터는 별도 백업 전략을 수립하는 것이 좋습니다.
GitHub Actions와 Railway를 연동한 자동 배포 파이프라인을 구축할 예정입니다.
# .github/workflows/deploy.yml
name: Deploy to Railway
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Railway CLI
run: npm install -g @railway/cli
- name: Deploy to Railway
run: railway deploy
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
Prometheus + Grafana를 통한 커스텀 메트릭 수집과 New Relic 연동을 검토하고 있습니다.
Apache JMeter를 활용해 Railway 환경에서의 성능 한계를 측정하고, 최적화 포인트를 찾을 계획입니다.
Docker Compose에서 Railway로의 마이그레이션은 예상보다 훨씬 수월했습니다. 특히 데이터베이스 관리의 복잡성이 크게 줄어들었고, 배포 과정도 자동화되어 개발에만 집중할 수 있게 되었습니다.
Railway는 다음과 같은 경우에 특히 추천합니다.
물론 대규모 트래픽을 처리해야 하거나, 특수한 인프라 요구사항이 있다면 AWS나 GCP를 고려하는 것이 좋습니다. 하지만 개인 프로젝트나 스타트업 초기 단계라면 Railway가 매우 실용적인 선택지라고 생각합니다.
💡 Railway $20 크레딧 받기: 여기서 가입
주워갑니다