AWS RDS 연동과 Parameter Store를 이용한 안전한 DB 구성

최정윤·2026년 8월 4일

Spring

목록 보기
29/39

로컬 H2 데이터베이스는 개발과 테스트에는 편리하지만, 서버가 종료되면 운영 데이터를 안정적으로 보존하기 어렵다. 또한 DB 주소와 비밀번호를 코드에 직접 작성하면 저장소를 통해 민감 정보가 노출될 수 있다.

이를 해결하기 위해 MySQL RDS를 어플리케이션 서버와 분리하여 구축하고, DB 접속 정보는 Parameter Store에 저장한 뒤 IAM Role 권한으로 조회하여 환경변수로 주입하였다.

이번 구성의 핵심은 단순한 DB 연결이 아니라, 어플리케이션·데이터·민감 정보를 서로 분리하여 관리하는 운영 구조를 만드는 것이었다.


전체 구조

Parameter Store
      │
      │ IAM Role 권한으로 조회
      ▼
     EC2
      │
      │ aws ssm → 환경변수 export
      ▼
Spring Boot (prod)
      │
      │ JDBC
      ▼
 MySQL RDS

핵심 구성 요소

구성 요소역할필요한 이유
RDS관리형 MySQL 데이터베이스EC2와 데이터를 분리하고 영속성 확보
DB Subnet GroupRDS가 배치될 네트워크 범위서로 다른 가용 영역의 서브넷 필요
RDS Security GroupDB 접근 제어허용된 EC2만 3306 포트 접근
Parameter Store접속 정보 저장URL·계정·비밀번호를 코드에서 분리
IAM RoleEC2에 AWS 접근 권한 제공Access Key 없이 Parameter Store 조회
환경변수설정값 전달 통로Spring Boot가 AWS 구현 방식에 의존하지 않음

DB 접속 정보가 주입되는 원리

Parameter Store에는 다음 값을 저장하였다.

/member-profile/prod/db-url
/member-profile/prod/db-username
/member-profile/prod/db-password
/member-profile/prod/team-name

비밀번호는 SecureString으로 저장하고, EC2에서 IAM Role 권한을 이용해 조회하였다.

Parameter Store에 저장
        ↓
EC2에서 aws ssm get-parameter 실행
        ↓
DB_URL, DB_USERNAME, DB_PASSWORD 환경변수 등록
        ↓
Spring Boot가 ${DB_URL} 형태로 참조

application-prod-yaml 에는 실제 비밀번호가 아니라 환경변수 이름만 작성하였다.

spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

따라서 소스 코드와 GitHub에는 실제 DB 접속 정보가 남지 않는다.


기술적 의사결정

1. Spring Boot 4.0.7을 유지하고 환경변수 주입 방식을 선택한 이유?

Parameter Store를 Spring Boot에서 직접 읽으려면 일반적으로 spring-cloud-aws를 사용할 수 있다. 그러나 당시 해당 라이브러리는 Spring Boot 3.x중심으로 사용되고 있어, 프로젝트의 Spring Boot 4.0.7과 호환 문제가 발생할 가능성이 있었다.

버전을 낮추는 대신 다음 방식을 선택하였다.

Parameter Store
      ↓
EC2의 AWS CLI
      ↓
환경변수
      ↓
Spring Boot

이 구조의 장점은 다음과 같았다.

  • Spring Boot 버전과 AWS 연동 라이브러리의 호환성에 덜 의존한다.
  • Spring Boot는 표준 환경변수만 읽으므로 코드가 단순하다.
  • IAM Role을 사용하므로 Access Key가 필요 없다.
  • 기존 ${DB_URL} 설정 구조를 그대로 사용할 수 있다.

이는 오류가 발생한 뒤 수정한 것이 아니라, 예상되는 호환성 문제를 피하기 위해 선택한 설계 방식이다.


2. RDS에 IP 대신 EC2 Security Group을 허용한 이유

RDS 인바운드 규칙에 EC2의 공인 IP를 등록하면 EC2를 중지했다가 다시 시작하여 IP가 변경될 때마다 규칙을 수젇해야 한다.

대신 소스를 EC2 Security Group으로 지정하였다.

EC2
member-profile-ec2-sg
        ↓ 허용
RDS
member-profile-rds-sg:3306

이를 통해 IP 주소가 바뀌더라도 해당 Security Group이 연결된 EC2만 RDS에 접근할 수 있다. 접근 대상을 주소가 아니라 리소스의 소속 관계로 관리한 것이다.


3. Access Key 대신 IAM Role을 사용한 이유

Access Key와 Secret Key를 코드나 EC2 내부 파일에 저장하면 유출 위험이 생긴다.

IAM Role을 EC2에 연결하면 AWS가 임시 자격 증명을 자동으로 제공한다. 어플리케이션과 AWS CLI는 자격 증명을 사용하므로 별도의 키를 저장하지 않아도 된다.

방식문제점 또는 장점
Access Key 저장키 유출·교체·관리 위험
IAM Role임시 자격 증명 사용, 키 저장 불필요

필요한 권한은 서버에 부여하되, 영구 자격 증명은 남기지 않는 방식을 선택하였다.


트러블슈팅

1. 로컬에서 RDS 접속이 실패한 문제

문제원인해결
DBeaver 접속 시 Public Key Retrieval is not allowed 또는 연결 실패MySQL 인증 방식과 SSL 설정이 클라이언트 설정과 맞지 않음allowPublicKeyRetrieval=true, useSSL=false 적용

보안 그룹과 RDS의 Public 연결이 정상이어도 MySQL 인증 단계에서 접속이 실패할 수 있다.

이를 통해 DB 접속은 다음 두 단계를 구분해서 확인해야 한다는 점을 이해하였다.

① 네트워크 연결
   RDS Endpoint · 3306 포트 · Security Group

② 데이터베이스 인증
   Username · Password · MySQL Driver 설정

2. RDS 생성에 필요한 서브넷이 부족했던 문제

문제원인해결
DB Subnet Group 구성 및 RDS 생성 불가서로 다른 가용 영역의 서브넷이 2개 이상 필요ap-northeast-2b에 Public Subnet 추가

기존 네트워크에는 RDS가 요구하는 가용 영역 수를 충족한 서브넷이 없었다. 이에 두번째 Public Subnet을 다른 가용 영역에 추가하고, 두 서브넷을 하나의 DB Subnet Group으로 묶었다.

Public Subnet A
ap-northeast-2a
        +
Public Subnet B
ap-northeast-2b
        ↓
DB Subnet Group
        ↓
MySQL RDS

RDS가 여러 가용 영역의 서브넷을 요구하는 장애 발생 시 다른 가용 영역을 활용 할 수 있는 기반을 확보하기 위해서다.

다른 가용 영역에 추가한 Public Subnet
RDS의 DB Subnet Group 구성 조건을 충족하기 위해 ap-northeast-2b에 추가한 서브넷이다.

서로 다른 가용 영역으로 구성한 DB Subnet Group
두 개의 Public Subnet을 하나의 그룹으로 묶어 RDS가 배치될 네트워크 범위를 구성하였다.


3. 로컬 접속 테스트와 최종 보안 요구사항이 충돌한 문제

문제원인해결
로컬 접속에는 내 IP가 필요하지만 최종 규칙에는 EC2 SG만 허용해야 함테스트 상태와 운영 상태의 접근 주체가 다름내 IP 임시 허용 → 접속 검증 → 규칙 삭제

로컬 DBeaver에서 접속하려면 현재 PC의 공인 IP를 허용해야 했다. 그러나 최종 상태에서는 발제 조건과 보안을 위해 EC2 Security Group만 남겨야 했다.

따라서 보안 규칙을 다음처럼 단계적으로 관리하였다.

테스트 단계
내 IP + EC2 Security Group 허용
                ↓
DBeaver 접속 검증
                ↓
최종 단계
내 IP 삭제 + EC2 Security Group만 유지

이는 보안 설정을 처음부터 무조건 고정하는 것이 아니라, 검증을 위해 최소한으로 열고 목적을 달성하면 다시 닫는 방식이다.

EC2 Security Group만 허용한 RDS 최종 인바운드 규칙
로컬 테스트용 IP를 삭제하고, MySQL 3306 포트의 접근 주체를 EC2 Security Group으로 제한하였다.


구현 및 검증 결과

다음 구성을 직접 연결하였다.

  • 서로 다른 두 가용 영역에 Public Subnet 구성
  • DB Subnet Group과 MySQL RDS 생성
  • EC2 Security Group만 허용하는 보안 그룹 체이닝 적용
  • Parameter Store에 DB URL·계정·비밀번호 저장
  • 비밀번호를 SecureString으로 암호화
  • EC2에 IAM Role 연결
  • Parameter Store 값을 환경변수로 주입
  • Spring Boot prod 프로필에서 RDS 연결
  • /actuator/info를 통한 team-name 출력 확인

Parameter Store 값의 어플리케이션 주입 결과
Parameter Store에 저장한 team-name이 /actuator/info에서 cat-team으로 출력되었다.
이를 통해 IAM Role 조회부터 환경변수 주입, Spring Boot 설정 반영까지의 전체 흐름을 검증하였다.


실무에서의 활용

운영 환경에서는 어플리케이션 코드, 데이터, 민감 정보를 각각 분리하는 것이 중요하다.

관리 대상저장 위치
애플리케이션EC2
운영 데이터RDS
DB 접속 정보Parameter Store
AWS 접근 권한IAM Role

이 구조는 EC2뿐 아니라 ECS나 EKS에서도 활용할 수 있다. 민감도가 더 높은 비밀번호의 자동 교체가 필요하다면 Secrets Manager로 확장할 수 있지만, 핵심 원리는 실행 환경에 권한을 부여하고 필요한 값을 실행 시점에 주입하는 것으로 동일하다.


가장 중요하게 생각하는 내용

어플리케이션은 DB 비밀번호를 직접 소유할 필요가 없다.

Parameter Store가 값을 보관하고, IAM Role을 가진 실행 환경이 이를 조회해 환경변수로 전달하면 된다. 이 구조를 사용하면 소스 코드와 저장소에서 민감 정보를 제거하면서도 어플리케이션은 일반적인 설정값처럼 DB에 연결할 수 있다.


마무리

MySQL RDS를 연결하는 데서 끝나지 않고, 애플리케이션·데이터·민감 정보를 분리한 운영 구조를 구성하였다.

특히 Spring Boot 4.0.7을 유지하면서 spring-cloud-aws 대신 환경변수 주입 방식을 선택하여 호환성 위험을 피했고, Security Group 체이닝과 IAM Role을 적용해 IP와 Access Key에 대한 의존성도 제거하였다.

이를 통해 안전한 DB 연결은 단순히 비밀번호를 숨기는 문제가 아니라, 네트워크 접근 권한과 설정 전달 경로 전체를 함께 설계하는 작업임을 이해하였다.

profile
콩떡

0개의 댓글