처음엔 AWS 배포와 리눅스 배포의 차이점을 잘 몰랐는데, 이번 기회에 공부할 수 있었다.
AWS 배포는 리눅스 배포를 클라우드에서 하는 것이다.
로컬 리눅스 서버로 하면 Ubuntu를 설치하고 직접 서버를 관리하며, 보통 IP가 고정이 아니고 외부에서 접근하기 어렵다는 특징이 있다.
그래서 AWS(Amazon Web Service)를 이용하여 배포하면 실제 서비스처럼 다른 사람도 쉽게 이용할 수 있다.
서버를 인터넷에 공개하므로 IP가 제공되며, 대신 보안 설정이 필요하다. (포트 등)
AWS EC2 (리눅스 서버)
→ Java 설치
→ DB 설치 or RDS
→ jar 실행
→ 다른 사람도 접속 가능
또한 AWS는 비용 때문에 걱정했는데, 다행히 6개월 동안 프리티어로 이용할 수 있는 것 같아 바로 실행하기로 하였다.
이때 많이 들어본 3종류가 등장한다.
EC2: 서버 배포(Spring Boot)
RDS: DB 배포 (MariaDB)
S3: 정적 파일(선택)
이번에는 학습용 프로젝트이기도 하고 둘 다 하면 월 무료 범위가 초과될 수도 있기 때문에
EC2 하나에 MariaDB를 직접 설치하는 방식으로 결정했다.
전체 배포 구조는 다음과 같다.
사용자 브라우저
↓ http://EC2 퍼블릭 IP:8080
AWS EC2 (t2.micro, Ubuntu)
├── Spring Boot 앱 (jar 실행)
└── MariaDB (EC2에 직접 설치)
1. AWS EC2 생성
AWS 회원 가입 후에 EC2 생성하는 건 아래 글을 그대로 따라했다. (진짜 성공함!!)
https://olrlobt.tistory.com/83
[INFRA] AWS EC2 프리티어 인스턴스 생성하기
AWS (Amazon Web Services) AWS는 Amazon이 제공하는 클라우드 컴퓨팅 플랫폼 및 인프라 서비스 모음이다. 2006년에 시작된 AWS는 가상 컴퓨터, 스토리지, 데이터베이스, 네트워킹, 분석, 머신 러닝, 모바일,
olrlobt.tistory.com
EC2 인스턴스 생성할 때 우분투 22.04로 했는데 (프리티어)
실제 내 노트북에는 우분투 24.04가 설치되어 있어서 그러면 오류나는 거 아닌가 걱정했다가...
내 PC(WSL)는 SSH 접속 도구 터미널일 뿐이고, EC2 실제 서버는 따로 있으니까 서로 우분투 버전 달라도 괜찮은 거였다. (생각해 보니 맞는 말)

그리고 이제는 t2.micro가 프리 티어가 아니라서 기본으로 선택되어 있는 t3.micro로 선택했다. (여기서도 버전 다르면 오류 날까봐 조마조마했는데 다행히 괜찮았음)

키 페어는 diary-key로 이름 짓고 (이미 만들어 둔 상태에서 다시 캡처하느라...)
RSA, .pem으로 설정(기본)한 다음에 다운로드 한다. 이건 절대 분실하면 안 되고, 파일은 프로젝트 파일 쪽으로 옮겼다. (추후 우분투 터미널에서 찾아야 하니까 파일 위치도 알아야 함)


스토리지 설정은 아래와 같이 했는데, 어느 설정을 해도 아래 오류 문구 같은 글이 계속 떠서 식겁했었다...
그런데 찾아보니까 오류는 아니었다.

AMI는 서버 이미지로, 운영체제 + 기본 설정이다.
인스턴스 저장소는 EC2에 붙는 임시 디스크로, 속도가 빠르고 인스턴스 종료하면 데이터가 날아가는 형태이다.
즉, 현재 AMI(우분투 22.04)는 임시 디스크도 쓸 수 있는 이미지이지만 선택한 EC2타입(t3.micro)는 임시 디스크 지원을 안 해서 그 기능만 못 쓴다는 뜻이었다.
내 프로젝트는 MariaDB를 사용하고 파일은 없거나 조금밖에 없을 예정이므로 인스턴스 저장소가 필요 없다.
인스턴스 저장소는 초고속 데이터 처리, 캐시 서버, 로그 임시 저장에 주로 쓰이므로 웹 서비스에서는 거의 안 쓰는 것 같다.
-> 지금은 무시해도 되는 경고이다.
여기까지 설정하고 인스턴스 생성 누르기!
그리고 이제 보안그룹 설정을 해야 한다. 어떤 포트에서 들어오는 걸 허용할지, 어떤 트래픽을 내보낼 수 있는지 설정할 수 있다.
사용자 지정 TCP는 8080: 0.0.0.0/0 -> spring Boot 앱
HTTP는 80: 0.0.0.0/0
SSH는 22: (내 IP) -> 서버 접속용
0.0.0.0/0은 Anyware로, 인터넷 어디서든 누구나 접속 가능하다는 뜻이다.
8080을 열지 않으면 사이트가 안 열리므로 꼭 열어주자. (HTTP는 선택, 나중에 Nginx붙이거나 도메인 연결할 때 필요한 거)
이때 SSH는 본인만 서버 접속 가능하게 해야 하므로 반드시 자신의 IP로 소스를 설정한다.
만일 SSH를 0.0.0.0/0으로 열면 보안상 치명적인 문제가 벌어질 수 있으므로 자신의 IP로 제안해야 한다.
=> 결론: 22번은 자신만, 8080은 모두에게 열기

이제 연결 버튼 누르고 본격적으로 터미널 창에서 실행 시작!!
2. EC2 서버 접속
나는 윈도우를 사용하므로 리눅스 명령어를 사용하려면 WSL을 이용해야 한다.
WSL은 윈도우 안에서 리눅스(우분투)를 실행하는 기능이다.
설치한 다음에 wsl 명령어를 치면 우분투 환경으로 넘어간다.

초록색 표시처럼 환경이 바뀌면 cd 명령어를 이용해서 키를 저장해 둔 위치로 이동한다.
아까 프로젝트 파일에 넣어놓았으므로 그쪽으로 이동한 다음에, 다음 명령어를 이용해서 키의 설정 권한을 바꾼다.
#키 파일 권한 설정 (Mac/Linux)
chmod 400 diary-key.pem
400: 소유자에게 읽기 권한 부여, 그 외 나머지는 전부 권한 없음 (파일 수정 및 삭제, 다른 사람이 읽기 방지)
그리고 나서 EC2에 접속하려면
#EC2 접속 (퍼블릭 IP는 EC2 콘솔에서 확인)
ssh -i diary-key.pem ubuntu@{EC2_퍼블릭_IP}
이 명령어를 작성하면 된다. 이때 퍼블릭 IP는 아까 AWS의 EC2 설정 창에서 왼쪽 메뉴바에서 인스턴스를 클릭하면 하단에 퍼블릭 IP 주소가 나온다. 그거 그대로 쓰면 된다.

처음 연결하면 아래와 같은 메시지가 나오는데, 이 서버가 진짜 맞는지, 처음 보는 서버인데 믿고 연결할 건지 묻는 절차이므로 그냥 yes 입력하면 된다.
This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])?
하지만 여기까지 하면......
오류가 발생한다.

WARNING: UNPROTECTED PRIVATE KEY FILE!
Permissions 0555 for 'diary-key.pem' are too open. It is required that your private key files are NOT accessible by others.
그렇다.
키 설정 명령이 안 들어먹힌 것이다......
여기서 한참 해맸다. 분명 chmod 400 했는데 왜 안 되는 건지...
그런데 알고 보니 윈도우에서 파일을 가져오는 경우에는 다르게 접근해야 한다는 것을 깨달았다!!
WSL에서 mnt/c/... 경로면 권한이 이상할 수 있다고 한다.
그래서 다음과 같이 해결한다.
cp /mnt/c/프로젝트 위치/diary-key.pem ~/
cd ~
chmod 400 diary-key.pem
위치를 리눅스 홈으로 해서, key 파일을 그쪽으로 복사한 다음에 거기로 이동해서 chmod를 실행하면...
정상적으로 권한 설정이 된다!
WSL은 리눅스지만, C드라이브는 진짜 리눅스 파일 시스템이 아니기 때문에 보안 권한이 깨지는 것이었다.
그러고 나서 다시 실행하면
ssh -i diary-key.pem ubuntu@내 퍼블릭 IP
그러면 기존에는 키 파일 권한 때문에 SSH가 막았으나, 이제는 chmod로 정상적으로 권한 설정이 됐으니 다음 단계로 넘어가진다.
이렇게 하면 터미널에서 초록 글씨가 ubuntu@ip-...:~$으로 바뀐다. 제대로 우분투를 실행한 것이다.
또한 이 과정에서 rebooting하라고 뜨는데, 커널 업데이트 됐는데 아직 재부팅 안 해서 적용 안 됐다는 뜻이긴 하지만 지금 당장 안 해도 돼서 일단 무시해도 된다.
이건 창으로 떠서 마우스로 조작이 안 되고 키보드로만 조작되는데, Tab 키로 버튼 간 이동할 수 있으니까 이거 여러번 눌러서 cancel 선택되면 엔터 치거나, 그냥 간단하게 Esc 누르거나 Ctrl+C 눌러서 빠르게 창을 빠져나가면 된다.
3. EC2에 Java & MariaDB 설치
아래 명령어를 순서대로 입력한다.
#패키지 업데이트
sudo apt update && sudo apt upgrade -y
#Java 17 설치
sudo apt install -y openjdk-17-jdk
#Java 버전 확인
java -version
#MariaDB 설치
sudo apt install -y mariadb-server
#MariaDB 시작 & 부팅 시 자동 시작 등록
sudo systemctl start mariadb
sudo systemctl enable mariadb
#MariaDB 보안 설정 (root 비밀번호 설정)
sudo mysql_secure_installation
이때 마지막 명령어에서 여러가지 질문이 나오면서 계속 y/n 선택하라고 그러는데, 그건 아래처럼 하면 된다.
Switch to unix_socket authentication [Y/n]
(MariaDB에서 비밀번호 대신 OS 계정으로 로그인 할래?)
=> Y (앞으로 sudo mysql 로 바로 접속 가능. 그냥 Y 선택하는 게 나음)
-> N (이러면 mysql -u root -p 로 매번 비밀번호 입력해야 함)
Remove anonymous users? (익명 사용자 제거)
-> Y (보안 좋음)
Disallow root login remotely? (root 외부 접속 차단)
-> Y (보안 좋음)
Remove test database? (테스트 DB 삭제)
-> Y (깔끔)
Reload privilege tables? (변경사항 적용)
-> Y
Change the root password?
-> N (sudo mysql로 접속하게 했는데 여기서 Y 누르면 다시 비밀번호 방식으로 바뀌어서 불편해짐)
그리고 이제 mariaDB에 접속해서 DB를 생성하고 앱 전용 계정을 생성한다.
db이름에 맞춰 사용자를 설정하는 게 좋다.
(지금 설치하는 MariaDB와 원래 노트북에 설치된 MariaDB는 완전히 별개이므로 서로 절대 영향이 없다.)
-- MariaDB 접속
sudo mysql
-- DB 생성
CREATE DATABASE diary_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 앱 전용 계정 생성 (root 직접 사용 지양)
CREATE USER 'diary_user'@'localhost' IDENTIFIED BY '비밀번호설정';
GRANT ALL PRIVILEGES ON diary_db.* TO 'diary_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
4. Spring Boot 빌드 & 업로드
이제 잠깐 인텔리제이로 돌아와서, (wsl 터미널 창 끄지 말고)
인텔리제이 터미널에 아래 명령어를 입력한다. 실행 파일인 jar 파일을 생성하는 것이다.
#인텔리제이 터미널 또는 로컬 터미널에서
./gradlew clean build -x test
#빌드 완료 후 jar 파일 위치
#build/libs/myDiary-0.0.1-SNAPSHOT.jar

그 다음에 다시 WSL 창에서 scp 명령어로 jar 파일을 EC2에 업로드 한다.
//엔터 쳤는데 원래는 한 줄에 공백으로만 구분해서 작성함
scp -i diary-key.pem
build/libs/myDiary-0.0.1-SNAPSHOT.jar
ubuntu@{EC2_퍼블릭_IP}:/home/ubuntu/
가뜩이나 WSL에 복붙하기도 잘 안 돼서 하나하나 다 쳐야 하는데 또 여기서 계속 오류가 발생한다.....
(이전 명령어 다시 하고 싶으면 방향키 ↑ 누르면 됨)
Warning: Identity file diary.key.pem not accessible: No such file or directory.
이 에러 발생하는 이유는 키 파일 경로가 틀렸기 때문이다.
그러니까 맞게 입력했는데 왜 틀렸냐면
터미널 창의 초록 글씨를 보자... 아직 별다른 명령을 안 했다면 아직 계속 ubuntu@ip-... 일 것이다.
이미 EC2 서버 안에 있기 때문에, 이 서버 안에는 key 파일이 없으니 당연히 에러가 나는 것이었다. 내 컴퓨터에서 EC2로 파일을 보내야 하는데 EC2에서 EC2로 보내는 꼴이었다.
그래서 우선 exit로 빠져나와서 다시 초록 글씨가 되돌아오게 만든 다음에 cd로 내 프로젝트 경로로 이동한다.
exit
cd /mnt/c/myStudy/myDiary/myDiary
그런 다음에 방향키 ↑ 이걸로 이전 명령어 다시 선택하면 (이거 까먹어서 하나하나 계속 쳤던 사람)
scp -i ~/diary-key.pem build/libs/myDiary-0.0.1-SNAPSHOT.jar ubuntu@퍼블릭IP:/home/ubuntu
WSL -> EC2로 무사히 파일이 전송된다.
5. application.yml 운영 설정
EC2에서 운영용 설정 파일을 별도로 만든다. DB 비밀번호와 같은 민감한 정보를 jar 파일 안에 넣지 않기 위해서이다.
#EC2에서 설정 파일 생성
vi /home/ubuntu/application-prod.yml
vi에서 i 눌러서 입력 모드로 들어간 다음에 Ctrl + Shift + V 하거나 마우스 우클릭으로 복붙할 수 있다.
#application-prod.yml (EC2에만 존재, Git에 올리면 안 됨)
spring:
datasource:
url: jdbc:mariadb://localhost:3306/diary_db
username: diary_user
password: 아까 설정한 비밀번호
driver-class-name: org.mariadb.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: false
jackson:
serialization:
write-dates-as-timestamps: false
time-zone: Asia/Seoul
jwt:
secret: "원래 설정한 키"
expiration: 86400000
server:
port: 8080
그리고 Esc 누른 다음에
:wq
입력하면 저장후 나갈 수 있다.
6. 앱 실행
#운영 프로파일로 실행
java -jar
-Dspring.config.location=/home/ubuntu/application-prod.yml
/home/ubuntu/diary-0.0.1-SNAPSHOT.jar
이렇게 하면...!
또 오류난다.........
ERROR 17105 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
*** APPLICATION FAILED TO START ***
Description: Failed to bind properties under 'spring.jackson.serialization' to java.util.Map<tools.jackson.databind.SerializationFeature, java.lang.Boolean>:
Reason: failed to convert java.lang.String to tools.jackson.databind.SerializationFeature (caused by java.lang.IllegalArgumentException: No enum constant tools.jackson.databind.SerializationFeature.write-dates-as-timestamps)
그 이유는 설정 파일에서 쓴 json 직렬화 때문이다. 대문자 enum 이름만 허용하니 아래처럼 바꿔야 한다.
jackson:
serialization:
WRITE_DATES_AS_TIMESTAMPS: false
vi application-prod.yml 열어서 다시 수정하고 :wq 저장한 다음에 다시 java - jar myDiary-0.0.1-SNAPSHOT.jar로 실행한다.
그래서 이렇게 하면.........
또 오류난다.(제발)
Reason: failed to convert java.lang.String to tools.jackson.databind.SerializationFeature (caused by java.lang.IllegalArgumentException: No enum constant tools.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
대문자 바꿔도 이것대로 또 오류나는 걸 보니 그게 문제가 아니었나 보다.
그래서 찾아보니 Jackson 패키지가 잘못되었다고 한다.
가장 빠른 해결 방법은... 그냥 yml에서 직렬화 관련 코드를 다 삭제하는 것이다.
이 설정 없어도 Spring Boot 기본 설정으로 정상 동작하기 때문에 괜찮다고 한다.
또한 그 전에 백엔드에서 Map으로서 json으로 가게 하고, 프론트엔드에서 json 관련 try 예외 처리 문을 만들었으니 어느 정도는 괜찮을 것 같아 이 설정을 지워 버렸다. 로컬에선 이게 있어도 정상 작동돼서 남겨 두었고, EC2 yml에서만 지웠다.
근본적인 문제 원인은 스프링 부트 4.0.3을 썼는데 4.x 버전은 아직 안정적이 아니라 Jackson 패키지가 꼬일 수 있다고 한다. 그래서 3.x로 낮추면 해결될 수도 있다고 하는데...
로컬에서는 이미 정상 Jackson 라이브러리가 캐싱돼서 문제 없이 돌아가는 것이고, EC2에서는 새로 build해서 dependency가 새로 다운로드 되므로 잘못된 조합이 설치(tools.jackson같은 거)돼서 영향을 미칠 수도 있다고 한다...
하여튼 추후에는 버전 점검도 같이 해보기로 하고, 이번에는 일단 직렬화 설정을 빼도 실제 테스트 시 프론트에서 오류가 나지 않아서 그냥 빼기로 했다!
7. 백그라운드 실행
오류를 해결했으면 일단 이 설정을 해 주자. 서버가 꺼져도 유지되도록 하는 것이다. (노트북 껐다고 서버도 같이 꺼지면 로컬과 다를 바가 없으니...)
우선 서비스 파일을 생성해 준다.
sudo vi /etc/systemd/system/diary.service
vi로 열어서 아래 코드 복붙한다.
[Unit]
Description=Diary Spring Boot App
After=network.target mariadb.service
[Service]
User=ubuntu
ExecStart=java -jar \
-Dspring.config.location=/home/ubuntu/application-prod.yml \
/home/ubuntu/diary-0.0.1-SNAPSHOT.jar
SuccessExitStatus=143
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
저장 후 나간 다음 아래 명령어를 입력한다.
#서비스 등록 & 시작
sudo systemctl daemon-reload
sudo systemctl start diary
sudo systemctl enable diary #부팅 시 자동 시작
#실행 상태 확인
sudo systemctl status diary
#로그 확인
sudo journalctl -u diary -f
그래서 이제 오류까지 무사히 해결한다면.......
인터넷 주소에 퍼블릭 IP 치면 접속 완료~!!

그런데......
회원가입하려니까 <또다시 오류 발생>
failed to fetch가 떴다.
이건 프론트와 백엔드의 연결 문제이다.
CORS 문제 또는 API 주소 문제인데, 개발자도구 F12로 확인할 수 있다.
//CORS 에러인 경우
Access to fetch at 'http://...' from origin 'http://...' has been blocked by CORS
//요청 자체 실패인 경우
Failed to fetch
난 아래였다. 프론트가 서버에 못 붙는 상태라는 뜻이다.
하여튼 코드를 수정하고 다시 jar파일을 업로드해서 실행해야 하므로 현재 EC2에서 실행중인 스프링부트는 Ctrl + C를 통해 종료한다.
오류 원인: 아직 코드에서 로컬 주소로 되어 있어서
그래서 그 전에 작성했던 api.js를 수정해야 한다.
프론트와 백엔드가 같은 EC2 서버(같은 포트 8080)에서 돌아가고 있으니까, 빈 문자열로 바꾸면 현재 접속한 서버로 자동으로 요청을 보낼 수 있다. 이렇게 하면 어느 환경에서 열든(로컬이든 EC2든) 자기 자신한테 요청을 보내서 따로 수정할 필요가 없다.
//기존 - 로컬 주소 하드코딩
const BASE_URL = 'http://localhost:8080';
//빈 문자열로 변경
const BASE_URL = '';
재배포 순서는 다음과 같다.
#1. 인텔리제이 터미널에서 jar 다시 빌드
./gradlew clean build -x test
#2. WSL에서 EC2로 업로드 (이때 또 우분투로 되어있으니 exit로 나가고 실행)
scp -i diary-key.pem
build/libs/myDiary-0.0.1-SNAPSHOT.jar
ubuntu@{EC2_퍼블릭_IP}:/home/ubuntu/
#3. EC2에서 서비스 재시작
ssh -i diary-key.pem ubuntu@{EC2_퍼블릭_IP}
sudo systemctl restart diary
#전체 순서 요약
1. AWS 계정 생성
2. EC2 t3.micro 생성 (Ubuntu, 보안그룹 22/8080 오픈)
3. EC2 접속 후 Java 17 + MariaDB 설치
4. MariaDB에 db 생성
5. 로컬에서 jar 빌드 후 scp로 업로드
6. application-prod.yml 작성
7. java -jar 로 실행 확인
8. systemd 서비스 등록으로 상시 가동
8. 결과
-시작 화면 (로그인 화면)

-회원가입 화면

-메인 화면 (캘린더)

-날짜별 일기 목록 화면

-상세 조회 화면

-글 작성 화면 (수정 시엔 기존의 제목, 내용 불러와서 여기에 표시됨)

-삭제는 알림으로

-사실 모바일로 주로 이용할 거라 그에 더 잘 맞는 것 같아서 목업해 봄 ㅎㅎ
Free templates | Canva
Browse through our professionally designed selection of free templates and customize a design for any occasion.
www.canva.com



모바일 & 웹 무사히 연동된다!!
이제 기본적인 구현과 배포까지 모두 완료~!~!
학습 목표 완전 달성했습니다... 뿌듯하네요
여기에서 추가로 CI/CD 자동화 구축하거나 환경 변수 등록하는 것도 있는데 그건 추후에 더 공부해 봐야겠다!
아무튼 만드는데 성공했으니까 메모장으로 잘 써야겠다 ㅎㅎ
이번 프로젝트는 여기서 끝~~~

'🌟Project > myDiary - 일기 기록 웹' 카테고리의 다른 글
| [토이프로젝트 개발일지 05] 일기 기록 웹 (js, localhost test) (0) | 2026.04.01 |
|---|---|
| [토이프로젝트 개발일지 04] 일기 기록 웹 (Test code / 예외처리통합) (0) | 2026.04.01 |
| [토이프로젝트 개발일지 03] 일기 기록 웹 (Spring Security, JWT) (0) | 2026.04.01 |
| [토이프로젝트 개발일지 02] 일기 기록 웹 (JPA, DTO) (0) | 2026.03.19 |
| [토이프로젝트 개발일지 01] 일기 기록 웹 설계 (0) | 2026.03.19 |