단일 EC2에서 Express + Spring + MySQL 빅뱅 배포

한유진·2025년 11월 5일

배포

목록 보기
1/3
post-thumbnail

KTB 3기 클라우드 과정에서 진행한 개인 커뮤니티 프로젝트의 배포 단계로,
하나의 EC2 서버에 백엔드(Spring Boot), 프론트엔드(Node.js Express), 그리고 MySQL을 함께 올려 ‘단일 인스턴스 빅뱅 배포’를 진행한 과정을 기록한 글입니다.

개인적으로 처음부터 끝까지 직접 배포 과정을 경험해 본 것은 이번이 처음이라,
앞으로 비슷한 환경을 구성할 분들께 참고용으로 도움이 되었으면 합니다.

0️⃣ 기본 개념 및 작업 환경

  • 빅뱅 배포?
    모든 변경사항을 한번에 배포하는 방식입니다.
    준비와 배포가 빠르지만, 문제 발생시 전체 시스템에 영향을 줄 수 있습니다.

  • AWS-EC2?

    • EC2 : Elastic Compute Cloud의 줄임말로, AWS에서 제공해주는 가상 컴퓨터 본체
    • EC2 구성
      • 인스턴스 : 클라우드상의 가상 서버(가상 컴퓨터 본체)
      • EBS : Elastic Block Storage. 클라우드에서 사용하는 가상 하드디스크를 지칭(EC2와 별개로 동작가능함)
      • AMI : Amazon Machine Image. EC2 인스턴스를 실행하기 위한 정보를 담고 있는 이미지(템플릿)
      • EIP : Elastic IP Address. 고정적인 IP주소를 할당하기위해 인스턴스에 연결하는 서비스
  • EC2 접속 방법 비교

    • EC2 Instance Connect 접속 : AWS가 별도의 임시 공개키를 인스턴스에 업로드해서 접속하는 방법, 내 인스턴스가 그런 접속을 허용하도록 설정되어 있어야 합니다.
    • 터미널 SSH : .pem은 AWS가 인스턴스 생성할 때 등록한 공개키와 짝을 이루는 개인키, SSH가 그걸 풀어서 정상 인증된 것 입니다.
  • 작업 환경

    구분내용
    로컬 개발 환경macOS
    AWS 인스턴스EC2 프리티어 (t3.micro)
    사양CPU 2vCore / RAM 1GB / EBS 30GB
    운영체제(OS)Ubuntu 24.04 LTS
    빌드 도구Gradle
    JDK 버전OpenJDK 21
    Node.js / npmNode.js v24.10.0 / npm
    데이터베이스(DB)MySQL 9.4.0

1️⃣ AWS EC2 인스턴스 생성

  • SSH접속과 각 서버에 접속하기 위한 포트를 적어 인바운드 규칙을 설정합니다.
  • IP고정을 위해 EIP도 설정하여 생성한 EC2와 연결해주었습니다.

2️⃣ SSH 접속하여 환경세팅

  • terminal에서 Ubuntu 접속하여 필요한 프로그램들을 설치해줍니다.
# Ubuntu 접속
ssh -i ~/.ssh/key.pem ubuntu@<EC2_IP>

# Ubuntu가 “현재 등록된 리포지토리”에서 최신 패키지 목록을 받아옴
sudo apt-get update	

# 이미 설치된 패키지들을 최신 버전으로 업그레이드함
sudo apt-get upgrade
 
# jdk 21버전 설치
sudo apt install openjdk-21-jdk

# node 24버전 설치 - apt로 설치시에 낮은버전이 깔려서 24버전 설치를 위해 해당 코드 실행
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs

# 프론트엔드 서버 실행을 위해 Express 등 의존성을 설치
npm install

# mysql 설치
sudo apt install -y mysql-server

# git 설치
sudo apt install -y git

# 설치 후 버전 확인
java -version
node -v
mysql --version

3️⃣ git clone

  • 백엔드, 프론트엔드 레포지토리 클론을 받으면 현재 서버 내의 디렉토리 구조는 아래와 같습니다.
/home/ubuntu/be
/home/ubuntu/fe

4️⃣ MySQL 초기 보안 설정

  • MySQL 보안 설정을 마친 후, root 계정 대신 사용할 별도의 사용자 계정을 생성하고 데이터베이스를 등록합니다.
# 보안설정
sudo mysql_secure_installation

# DB 계정 생성 및 권한 부여
CREATE DATABASE community;
CREATE USER 'community_user'@'localhost' IDENTIFIED BY 'your_pw';
GRANT ALL PRIVILEGES ON community.* TO 'community_user'@'localhost';
FLUSH PRIVILEGES;

5️⃣ 로컬 DB 덤프 → EC2로 전송 → import

  • 로컬에서 DB 덤프파일을 서버로 가지고와서 설치하는 과정입니다.
# 덤프 업로드
scp -i ~/.ssh/key.pem ~/Desktop/community_dump.sql ubuntu@<EC2_IP>:/home/ubuntu/

# 덤프 설치
mysql -u root -p community < /home/ubuntu/community_dump.sql

6️⃣ spring application.yml파일 수정

  • 빌드 전, application.yml 파일의 DB 관련 설정을 4번에서 생성한 community_user 계정 기준으로 수정합니다.

  • 현재는 로컬과 서버에서 같은 application.yml을 사용했지만,
    추후에는 환경별로 설정 파일(application-prod.yml 등)을 분리하여 보안 관리를 강화할 예정입니다.


spring:
  datasource:
    url: jdbc:mysql://localhost:3306/community?serverTimezone=Asia/Seoul&useSSL=false
    username: **community_user**
    password: **your_pw**
    driver-class-name: com.mysql.cj.jdbc.Driver

7️⃣ [트러블 슈팅] Swap 메모리 설정

문제

  • EC2 무료티어(t3.micro, RAM 1GB) 환경에서 Gradle 빌드 실행 시
    메모리 부족으로 인해 JVM이 비정상 종료(OOM, Out Of Memory)되는 현상이 발생했습니다.

원인

  • Gradle 빌드 과정은 의존성 로딩과 컴파일 시 많은 메모리를 사용합니다.
  • EC2 무료티어의 1GB 메모리만으로는 빌드 중 Java 프로세스를 유지하기 어려운 것 같습니다.

해결

  • 빌드 안정화를 위해 2GB Swap 메모리(가상 메모리) 를 추가 설정했습니다.
  • Swap은 RAM이 부족할 때 디스크 일부를 임시 메모리처럼 사용하는 보조 공간으로, 속도는 느리지만 빌드 시 한시적으로 사용하는 용도로 적합한 것 같습니다.
# 2GB 크기 스왑 파일 생성(2배 크기의 파일이 안전)
sudo fallocate -l 2G /swapfile

#권한 설정 및 활성화
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

8️⃣ [트러블 슈팅] Spring Boot 빌드

문제

  • Gradle 빌드 실행(./gradlew build) 중 테스트 코드(JUnit)에서 오류가 발생하여 전체 빌드가 실패했습니다.
  • EC2 메모리 부족 및 테스트 환경 미설정으로 인해 테스트 단계에서 예외 발생했습니다.

원인

  • Gradle은 기본적으로 ./gradlew build 명령 실행 시 테스트까지 자동 실행합니다.
  • 테스트 코드 실패 시 전체 빌드 프로세스가 “FAILED” 처리되어 jar 파일이 생성되지 않았습니다.

해결

  • 테스트 단계를 제외(-x test)하고 빌드를 재실행하여 jar 파일 생성했습니다.
  • EC2 메모리 부족으로 인한 빌드 지연 방지를 위해 --no-daemon 옵션 추가(Gradle 데몬 비활성화) 했습니다.
./gradlew clean build -x test --no-daemon

# build/libs/~~jar 있으면 빌드 성공

9️⃣ 백엔드 서버 실행

  • Spring Boot 서버를 백그라운드에서 실행하고,
    모든 로그(정상 출력 + 에러)를 /home/ubuntu/app/backend.log 파일에 저장하고, SSH 세션이 종료되어도 서버가 계속 동작하도록 nohup 명령어를 사용합니다.
nohup java -jar build/libs/community-0.0.1-SNAPSHOT.jar > /home/ubuntu/app/backend.log 2>&1 &

🔟 서버 로그 확인

  • 9번에서 찍힌 로그로 백엔드 서버가 잘 실행되고 있는지 확인해줍니다.
# 톰캣·DB 연결 확인
tail -f /home/ubuntu/app/backend.log

1️⃣1️⃣ 프론트엔드 서버 실행

  • 프론트엔드 폴더로 이동하여 프론트엔드 서버를 실행시켜줍니다.
npm start

1️⃣2️⃣ 접속테스트

  • http://<EC2_IP>:3000로 들어가서 잘 동작하면 배포가 완료된 것 입니다.
  • 커뮤니티 프로젝트가 아직 미완성이지만, 현재까지 완료된 기능이 잘돌아가는것을 확인했습니다.

1️⃣3️⃣ 접속 종료

  • 마지막으로 접속 종료를 원할때는 아래 명령어로 종료시키면 됩니다.
# Spring Boot 종료
ps -ef | grep java
kill -9 <PID>

# Node.js 종료
ps -ef | grep node
kill -9 <PID>

# MySQL 종료 (선택)
sudo systemctl stop mysql

📝 회고

처음으로 직접 배포를 진행하다 보니 어려움이 많았습니다.
하지만 여러 자료를 찾아보고 하나씩 시도해보면서,
눈에 보이는 UI뿐 아니라, 실제 서비스가 동작하기 위한 “보이지 않는 인프라의 작동 원리”를 경험할 수 있었던 의미 있는 시간이었습니다.

아직 보안적인 부분이나 운영 환경에서 고려해야 할 세부 요소들은 미숙하지만,
앞으로 공부를 이어가며 이를 보완하고,
더 안정적이고 확장성 있는 배포 방식(Docker, Nginx, CI/CD 등) 도 시도해볼 예정입니다.

[참고자료]

0개의 댓글