Chapter 03

RDS 데이터베이스

"SQLite를 진짜 데이터베이스로 교체하자"

학습 목표

  • RDS가 무엇이고 왜 필요한지 설명할 수 있다
  • RDS용 보안 그룹을 생성하고 EC2 보안 그룹을 소스로 참조할 수 있다
  • DB 서브넷 그룹을 생성하여 프라이빗 서브넷에 RDS를 배치할 수 있다
  • RDS MySQL 인스턴스를 생성하고 EC2에서 접속할 수 있다
  • ShopEasy API 서버의 데이터베이스를 SQLite에서 RDS MySQL로 전환할 수 있다

1. 왜 RDS가 필요한가?

지금까지 ShopEasy API 서버는 SQLite를 사용하고 있습니다. SQLite는 파일 하나에 모든 데이터를 저장하는 가벼운 데이터베이스입니다. 로컬 개발에는 편리하지만, 실제 서비스 운영에는 심각한 문제가 있습니다.

SQLite vs RDS MySQL 비교

항목 SQLite (현재) RDS MySQL (목표)
데이터 저장 위치 EC2 서버 안의 파일 1개 별도의 전용 DB 서버
서버가 죽으면? 데이터도 같이 사라짐 DB는 별도 서버라 안전
동시 접속 1명만 쓰기 가능 수천 명 동시 처리
백업 내가 직접 해야 함 AWS가 자동 백업
보안 패치 내가 직접 해야 함 AWS가 자동 패치
장애 복구 내가 직접 해야 함 Multi-AZ로 자동 복구
비유: 금고를 어디에 둘 것인가?

돈(데이터)을 보관하는 방법을 생각해 봅시다.

  • SQLite = 집 서랍에 현금을 넣어두는 것. 간편하지만, 도둑이 들거나 불이 나면 돈을 잃습니다.
  • RDS = 전문 금고 회사(은행)에 맡기는 것. 24시간 감시, 자동 백업, 재해 복구까지 전문가가 해줍니다.

RDS = AWS가 관리해주는 데이터베이스 서버입니다. 백업, 보안 패치, 장애 복구를 AWS가 자동으로 해주므로 우리는 데이터베이스 관리가 아닌 서비스 개발에 집중할 수 있습니다.

핵심 개념 정리
  • RDS (Relational Database Service) - AWS가 관리하는 관계형 데이터베이스. MySQL, PostgreSQL, MariaDB, Oracle, SQL Server 등 지원
  • DB 서브넷 그룹 - RDS가 사용할 서브넷 목록. 최소 2개 이상의 가용 영역(AZ)에 서브넷이 필요 (Multi-AZ 대비)
  • 프라이빗 서브넷에 배치 - DB는 외부에서 직접 접속하지 못하게 프라이빗 서브넷에 둠 (보안)
  • 보안 그룹 참조 - RDS 보안 그룹의 소스를 IP가 아닌 EC2 보안 그룹으로 지정하면, 해당 보안 그룹에 속한 EC2만 접근 가능

2. ShopEasy에서 RDS의 역할

ShopEasy 이커머스 앱에서 RDS MySQL은 회원 정보, 상품 목록, 주문 내역 등 핵심 비즈니스 데이터를 저장합니다. API 서버(EC2)가 퍼블릭 서브넷에서 사용자 요청을 받고, 프라이빗 서브넷에 있는 RDS에 데이터를 읽고 씁니다.

Chapter 03 - RDS 배치 아키텍처
사용자
웹 브라우저
↓ HTTP 요청
ShopEasy-VPC (10.0.0.0/16)
Public Subnet (10.0.1.0/24)
Internet Gateway
EC2
Node.js API 서버
포트 5000
SG: ShopEasy-EC2-SG
↓ MySQL 3306 (보안 그룹 참조)
Private Subnet (10.0.100.0/24, 10.0.101.0/24)
RDS MySQL
db.t3.micro
DB: ecommerce
SG: ShopEasy-RDS-SG
왜 프라이빗 서브넷인가?

데이터베이스에는 회원의 개인정보, 비밀번호, 주문 내역 등 민감한 데이터가 저장됩니다. 만약 DB를 퍼블릭 서브넷에 두고 퍼블릭 접근을 허용하면, 인터넷 어디서든 DB에 접근을 시도할 수 있습니다. 프라이빗 서브넷에 배치하면 인터넷에서 직접 접근하는 것이 원천적으로 불가능합니다. 오직 같은 VPC 안에 있는 EC2 서버만 DB에 접속할 수 있습니다.

보안 그룹 참조란?

일반적으로 보안 그룹의 소스에 IP 주소를 입력합니다 (예: 10.0.1.50/32). 하지만 EC2의 IP는 재시작하면 바뀔 수 있습니다. 대신 EC2 보안 그룹의 ID (예: sg-0abc1234)를 소스로 지정하면, 해당 보안 그룹이 적용된 모든 EC2 인스턴스가 자동으로 접근을 허용받습니다. IP가 바뀌어도, EC2가 추가되어도 보안 그룹만 같으면 자동으로 적용됩니다.

3. 실습: RDS MySQL 구축 및 연동

사전 준비
  • Chapter 01에서 생성한 VPC, 프라이빗 서브넷 2개 (서로 다른 AZ)
  • Chapter 02에서 생성한 EC2 인스턴스와 EC2 보안 그룹 (ShopEasy-EC2-SG)
  • EC2에 SSH 접속이 가능한 상태
실습 1: RDS용 보안 그룹 생성

RDS에 적용할 보안 그룹을 만듭니다. EC2 보안 그룹에서만 MySQL 포트(3306)에 접근할 수 있도록 설정합니다.

보안 그룹 생성

다음 설정으로 보안 그룹을 생성하세요.

항목
보안 그룹 이름 ShopEasy-RDS-SG
설명 ShopEasy RDS MySQL Security Group
VPC ShopEasy-VPC
인바운드 규칙 설정

인바운드 규칙을 추가하세요.

유형 포트 소스 설명
MySQL/Aurora 3306 ShopEasy-EC2-SG (보안 그룹 선택) EC2에서 RDS 접근 허용

소스를 지정할 때 IP가 아닌 "사용자 지정"을 선택하고, ShopEasy-EC2-SG의 보안 그룹 ID (sg-xxxx)를 입력합니다. 보안 그룹 ID를 입력하기 시작하면 자동완성 목록이 나타납니다.

실습 2: DB 서브넷 그룹 생성

RDS가 사용할 서브넷 목록을 DB 서브넷 그룹으로 묶어야 합니다. 서로 다른 가용 영역(AZ)에 있는 프라이빗 서브넷 2개를 선택합니다.

DB 서브넷 그룹 생성

다음 설정으로 DB 서브넷 그룹을 생성하세요.

항목
이름 shopeasy-db-subnet-group
설명 ShopEasy RDS DB Subnet Group
VPC ShopEasy-VPC
가용 영역 ap-northeast-2a, ap-northeast-2c
서브넷 프라이빗 서브넷 2개 (10.0.100.0/24, 10.0.101.0/24)

RDS 콘솔 → 서브넷 그룹 → DB 서브넷 그룹 생성에서 만듭니다. VPC를 선택하면 해당 VPC의 서브넷 목록이 나타납니다. 퍼블릭 서브넷이 아닌 프라이빗 서브넷을 선택해야 합니다. 서브넷의 CIDR 블록이나 이름 태그를 확인하세요.

왜 2개 이상의 AZ가 필요한가?

AWS RDS는 DB 서브넷 그룹에 최소 2개 이상의 가용 영역(AZ)을 요구합니다. 이는 나중에 Multi-AZ 배포(자동 장애 복구)를 활성화할 때를 대비한 것입니다. 하나의 AZ에 장애가 발생해도 다른 AZ에 있는 대기 DB로 자동 전환할 수 있습니다. 지금은 비용 절약을 위해 Single-AZ로 생성하지만, DB 서브넷 그룹은 2개 AZ가 필수입니다.

실습 3: RDS MySQL 인스턴스 생성

이제 실제 RDS MySQL 데이터베이스 인스턴스를 생성합니다. 비용 절약을 위해 프리 티어 옵션을 사용합니다.

RDS 인스턴스 생성 설정

다음 설정으로 RDS 인스턴스를 생성하세요.

엔진 옵션

항목
엔진 유형 MySQL
엔진 버전 MySQL 8.0.x (기본값)
템플릿 프리 티어

설정

항목
DB 인스턴스 식별자 shopeasy-db
마스터 사용자 이름 admin
마스터 암호 본인이 설정 (8자 이상, 기억할 것!)

인스턴스 구성

항목
DB 인스턴스 클래스 db.t3.micro

스토리지

항목
스토리지 유형 gp2 (범용 SSD)
할당된 스토리지 20 GiB
스토리지 자동 조정 비활성화

연결

항목
VPC ShopEasy-VPC
DB 서브넷 그룹 shopeasy-db-subnet-group
퍼블릭 액세스 아니요
VPC 보안 그룹 ShopEasy-RDS-SG (기존 항목 선택)
가용 영역 기본 설정 유지

추가 구성

항목
초기 데이터베이스 이름 ecommerce
자동 백업 비활성화 (실습용, 비용 절약)
암호화 기본값 유지
모니터링 기본값 유지
삭제 방지 비활성화 (실습 후 삭제 편의)
RDS 엔드포인트 확인

RDS 인스턴스가 "사용 가능(Available)" 상태가 되면 엔드포인트를 메모합니다. 이 엔드포인트가 DB 접속 주소입니다.

예시: shopeasy-db.cxxxxxxxxx.ap-northeast-2.rds.amazonaws.com

RDS 콘솔 → 데이터베이스 → shopeasy-db를 클릭하면 "연결 & 보안" 탭에서 엔드포인트를 확인할 수 있습니다. 엔드포인트는 RDS 인스턴스의 DNS 주소입니다.

RDS 생성에는 시간이 걸립니다!

RDS 인스턴스 생성에는 보통 5~15분이 소요됩니다. "생성 중(Creating)" 상태에서 "사용 가능(Available)"이 될 때까지 기다려야 합니다. 이 시간 동안 다음 단계의 내용을 미리 읽어두세요.

운영 환경에서는?
  • Multi-AZ 활성화 - 자동 장애 복구 (가용성)
  • 자동 백업 활성화 - 최대 35일 자동 백업 (데이터 보호)
  • 암호화 활성화 - 저장 데이터 암호화 (보안)
  • 삭제 방지 활성화 - 실수로 DB 삭제 방지
  • 비밀번호는 AWS Secrets Manager에 저장
실습 4: EC2에서 RDS 접속 테스트

EC2 인스턴스에 SSH로 접속한 뒤, MySQL 클라이언트를 설치하고 RDS에 접속해 봅니다.

EC2에 SSH 접속

로컬 PC에서 EC2에 SSH로 접속합니다.

bash
ssh -i your-key.pem ec2-user@EC2_PUBLIC_IP
MySQL 클라이언트 설치

EC2에 MySQL 클라이언트(MariaDB 클라이언트)를 설치합니다.

bash
sudo dnf install mariadb105 -y

Amazon Linux 2023에서는 mariadb105 패키지에 MySQL 호환 클라이언트가 포함되어 있습니다. mysql 명령어로 MySQL 서버에 접속할 수 있습니다.

RDS 접속 테스트

MySQL 클라이언트로 RDS에 접속합니다. 엔드포인트를 입력하세요.

bash
mysql -h RDS_ENDPOINT -u admin -p

비밀번호를 입력하면 MySQL 프롬프트가 나타납니다.

데이터베이스 확인

접속에 성공하면 데이터베이스 목록을 확인합니다.

sql
SHOW DATABASES;
USE ecommerce;
SHOW TABLES;
exit;

ecommerce 데이터베이스가 보이면 성공입니다. 아직 테이블은 없는 것이 정상입니다 (seed.js에서 생성 예정).

접속이 안 되면 다음을 확인하세요:

  • RDS 상태가 "사용 가능(Available)"인지 확인
  • RDS 보안 그룹 인바운드에 EC2 보안 그룹이 소스로 설정되어 있는지 확인
  • EC2와 RDS가 같은 VPC에 있는지 확인
  • 엔드포인트 주소가 올바른지 확인 (오타 주의)
실습 5: API 서버 .env 수정

ShopEasy API 서버가 SQLite 대신 RDS MySQL을 사용하도록 .env 파일을 수정합니다.

.env 파일 수정

EC2에서 ShopEasy API 서버 디렉터리로 이동하여 .env 파일을 수정합니다.

bash
cd ~/ecommerce-app/api-server
nano .env
데이터베이스 설정 변경

.env 파일에서 데이터베이스 관련 항목을 다음과 같이 변경합니다.

env
# 데이터베이스 설정 - SQLite에서 MySQL로 변경
DB_TYPE=mysql
DB_HOST=여기에_RDS_엔드포인트_입력
DB_PORT=3306
DB_USER=admin
DB_PASSWORD=여기에_설정한_비밀번호_입력
DB_NAME=ecommerce

DB_TYPEsqlite에서 mysql로 변경하고, DB_HOST에 RDS 엔드포인트를 입력하는 것이 핵심입니다.

nano 편집기 사용법:

  • 화살표 키로 이동
  • 텍스트를 직접 입력/삭제
  • Ctrl + O → Enter → 저장
  • Ctrl + X → 편집기 종료

또는 vi를 사용하려면: vi .envi로 입력 모드 → 수정 후 ESC:wq → Enter

실습 6: API 서버 재시작 & 시드 데이터 입력

.env를 변경했으면 API 서버를 재시작하고, node seed.js로 기본 데이터(테스트 사용자 + 상품 20개)를 입력합니다.

실행 중인 서버 종료

현재 실행 중인 API 서버를 종료합니다.

bash
# pm2를 사용 중인 경우
pm2 stop all

# 또는 프로세스를 직접 종료하는 경우
# Ctrl + C 로 종료
MySQL 드라이버 설치

Node.js에서 MySQL에 접속하기 위한 드라이버를 설치합니다.

bash
cd ~/ecommerce-app/api-server
npm install mysql2
시드 데이터 입력

seed.js를 실행하여 테이블을 생성하고 기본 데이터를 넣습니다.

bash
node seed.js

성공하면 "시드 데이터 생성 완료!" 메시지와 함께 테스트 계정(test@test.com / password123)과 상품 20개가 생성됩니다.

만약 ER_ACCESS_DENIED_ERROR 오류가 나면 .env의 DB_PASSWORD를 확인하세요. ECONNREFUSED 오류가 나면 DB_HOST가 올바른지, 보안 그룹 설정이 맞는지 확인하세요.

API 서버 재시작

서버를 다시 시작합니다.

bash
# pm2를 사용하는 경우
pm2 start server.js --name shopeasy-api
pm2 logs shopeasy-api

# 또는 직접 실행
node server.js

"Server running on port 5000" 메시지가 나오고, DB 연결 관련 오류가 없으면 성공입니다.

실습 7: API 테스트

RDS MySQL로 전환이 완료되었는지 curl 명령어로 API를 테스트합니다. 로컬 PC에서 새 터미널을 열고 테스트합니다.

회원가입 테스트

새로운 사용자를 등록합니다.

bash
curl -X POST http://EC2_PUBLIC_IP:5000/api/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","email":"test@test.com","password":"test1234"}'
로그인 테스트

등록한 사용자로 로그인합니다.

bash
curl -X POST http://EC2_PUBLIC_IP:5000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@test.com","password":"test1234"}'
상품 조회 테스트

시드 데이터로 넣은 상품 목록을 조회합니다.

bash
curl http://EC2_PUBLIC_IP:5000/api/products

상품 20개의 JSON 목록이 출력되면 RDS MySQL 연동 성공입니다!

EC2_PUBLIC_IP는 EC2 인스턴스의 퍼블릭 IP 주소로 교체하세요. 예시: curl http://3.34.xx.xx:5000/api/products

응답이 안 오면 EC2 보안 그룹에서 포트 5000이 열려있는지 확인하세요.

4. 확인 사항

아래 항목을 모두 확인했는지 체크하세요.

  • RDS 보안 그룹(ShopEasy-RDS-SG)이 생성되었고, 인바운드에 EC2 보안 그룹이 소스로 지정되어 있다
  • DB 서브넷 그룹(shopeasy-db-subnet-group)이 프라이빗 서브넷 2개로 생성되었다
  • RDS 인스턴스(shopeasy-db)가 "사용 가능(Available)" 상태이다
  • RDS의 퍼블릭 액세스가 "아니요"로 설정되어 있다
  • EC2에서 mysql 명령으로 RDS에 접속할 수 있다
  • API 서버 .env에서 DB_TYPE=mysql로 변경했다
  • node seed.js로 기본 데이터가 정상 입력되었다
  • curl로 회원가입, 로그인, 상품 조회 API가 정상 동작한다
이번 챕터에서 배운 것
  • RDS = AWS 관리형 데이터베이스 (백업, 패치, 복구 자동)
  • 프라이빗 서브넷 배치 = 외부에서 DB 직접 접근 차단 (보안)
  • DB 서브넷 그룹 = RDS가 사용할 서브넷 목록 (Multi-AZ 대비)
  • 보안 그룹 참조 = IP 대신 보안 그룹 ID로 접근 제어 (유연함)
  • .env 수정 = DB_TYPE을 변경하면 API 서버가 다른 DB를 사용