다음 순서는 RDS로 데이터베이스를 배포하는 것. EC2 만큼은 아니었지만 이것도 상당한 고통을 수반하였다..
모범 사례라고 해도, 내 개발 환경에 맞는 설정인지를 알 수가 없어 일단 표준 생성으로 선택하였다.
데이터베이스 엔진은 내가 MySQL을 사용할 것이므로 적절히 선택.
인스턴스 크기 설정에서는 친절하게 프리티어 선택지를 따로 마련해주고 있으므로 고민 없이 선택.
인스턴스 식별자는 적절하게 작성해주면 된다.
인스턴스의 마스터 ID와 PWD 설정. 편한대로 설정하되, PWD의 경우 아래 원칙을 준수하면 된다.
길이: 암호는 최소 8자 이상이어야 합니다.
복잡성: 대문자, 소문자, 숫자 및 허용되는 특수문자를 조합합니다.
예측 불가: 일반적으로 사용되는 단어나 쉽게 추측할 수 있는 조합은 피합니다.
유니크함: 다른 계정이나 서비스에서 사용되는 암호와 다른 것을 사용합니다.
딱 내가 원하는 옵션이 존재한다. EC2 백엔드에서 RDS 데이터베이스를 연결해야하는데, 아예 전용 연결 설정이 존재한다.
그런데.. EC2 인스턴스를 앞서 만들고, 정상 동작하는 것도 확인했는데 RDS 인스턴스 설정 상에서 EC2가 잡히지 않는다! 이 문제의 원인을 알아본 결과, 아래와 같은 요인들이 존재한다고 한다.
VPC 설정: RDS 인스턴스와 EC2 인스턴스가 서로 다른 VPC에 위치해 있을 수 있습니다. RDS와 EC2가 같은 VPC 내에 있어야 통신할 수 있습니다.
서브넷 그룹: RDS가 특정 서브넷 그룹에 속해 있지 않거나, EC2 인스턴스와 다른 서브넷 그룹에 있을 수 있습니다. 두 인스턴스가 같은 서브넷 또는 피어링된 서브넷 내에 있어야 합니다.
보안 그룹 규칙: EC2 인스턴스의 보안 그룹이 RDS 인스턴스와의 통신을 허용하도록 설정되어 있지 않을 수 있습니다. 특히, RDS 인스턴스의 입/출력 포트(기본적으로 MySQL은 3306 포트 사용)가 올바르게 설정되어 있어야 합니다.
RDS 인스턴스 상태: RDS 인스턴스가 아직 완전히 생성되지 않았거나, 생성 과정에 있을 수 있습니다. 인스턴스 상태를 확인하여 'available' 상태인지 확인해야 합니다.
AWS 콘솔 오류: 때때로 AWS 콘솔은 최신 정보를 바로 반영하지 않을 수 있습니다. 페이지를 새로고침하거나 몇 분 후에 다시 시도해 보세요.
IAM 권한: 사용 중인 AWS 계정에 RDS 인스턴스를 보거나 관리할 수 있는 충분한 권한이 없을 수도 있습니다.
가장 먼저 각 인스턴스의 리전과 VPC를 확인해보았다. 이건 각각의 인스턴스가 동일한 지역의 네트워크에 존재하느냐를 점검하는 것 같은데, EC2와 RDS 인스턴스를 생성할 때 리전은 '아시아 태평양 서울'이 되도록 설정했으니 이건 문제가 되지 않을 것이다.
VPC도 확인해보니 두 인스턴스가 동일한 VPC ID를 보유하고있었다. 즉, 리전과 VPC 문제는 아니다.
그렇다면 EC2 <-> 로컬 프론트엔드 연결 테스트에서 문제를 일으켰던 보안 그룹이 의심되는데.. 아니나 다를까 여기가 문제였다. RDS 인스턴스의 포트인 3306에 대한 보안 규칙이 설정되어 있지 않았다. 외부에서 연결할 수 있는 경로가 없으니 인스턴스를 찾지 못했던 것이다.
문제를 해결하였고.. RDS DB 인스턴스를 성공적으로 생성할 수 있었다.
이제 EC2 백엔드에서 RDS 데이터베이스를 연결해주어야 한다. EC2 인스턴스로 돌아와서 데이터베이스 호출에 필요한 소프트웨어를 설치하고 서버 파일에 연결 코드를 작성해주어야 한다.
npm install mysql2
const express = require('express');
const mysql = require('mysql2');
const cors = require('cors');
require('dotenv').config();
const app = express();
app.use(cors()); // CORS 에러 방지
app.use(express.json()); // JSON 본문 파싱
// 환경 변수를 사용하여 MySQL 데이터베이스와 연결 설정
const dbPool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
// DB 연결 확인용 임시 기능 코드.
app.post('/api/create', (req, res) => {
const testData = req.body.data; // 예제 데이터
dbPool.query('INSERT INTO test_table (column_name) VALUES (?)', [testData], (err, results) => {
if (err) {
console.error('데이터 삽입 중 에러 발생', err);
res.status(500).json({ message: '데이터를 삽입하는 동안 문제가 발생했습니다', error: err.message });
} else {
res.status(200).json({ message: '데이터가 성공적으로 삽입되었습니다', data: results });
}
});
});
app.get('/api/getAll', (req, res) => {
dbPool.query('SELECT * FROM test_table', (err, results) => {
if (err) {
console.error('데이터를 불러오는 중 에러 발생', err);
res.status(500).json({ message: '데이터를 불러오는 중 문제가 발생했습니다', error: err.message });
} else {
res.status(200).json({ message: '데이터 불러오기 성공', data: results });
}
});
});
// 서버 시작
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`서버가 ${PORT} 포트에서 정상 동작중입니다.`);
});
createConnection에 인자로 들어가는 데이터베이스 정보들은 민감한 내용을 포함하고 있으니 환경변수로 빼주어야 한다. 이미 .env 파일을 백엔드에 생성해두었으니 여기서 키와 값을 작성해주면 된다. 연결 테스트용 임시 코드를 작성해주고, 테스트를 해보면..
그럼 그렇지.. 순탄하게 진행될 리가 없다. 오류의 원인은 사용자의 접근이 거부되었다는 것인데, 이는 보통 사용자 ID 혹은 PWD가 잘못되었을 때 발생한다. .env 파일을 다시 잘 살펴보니 nano 편집기 사용 중 오타를 발생시킨 것이 원인이었다.
다시 테스트를 해보니 이번에는 '1049' 오류가 발생하였다. 알아보니 RDS 인스턴스 내부의 DB에 스키마가 존재하지 않아서 발생하는 오류라고 하는데, 생각해보니 RDS에서 DB 인스턴스를 생성하기만 하고 스키마를 만들어주지 않았었다. EC2에서 PuTTY를 사용했던것 처럼, RDS에서는 MySQL 워크벤치를 통해 인스턴스에 접근해야한다.
....
RDS도 EC2만큼 사람을 화나게 한다. 이 오류는 메시지는 간단한데, 원인은 참 다양했다.
잘못된 호스트명: 에러 메시지에서 "Unable to connect to localhost"라는 부분이 나타나는데, 이는 로컬 호스트에 연결하려고 시도하고 있다는 의미일 수 있습니다. AWS RDS 인스턴스에 연결하려면 'Hostname' 필드에 RDS 인스턴스의 퍼블릭 DNS 주소를 정확하게 입력해야 합니다. "localhost"는 로컬 시스템을 가리키므로, 워크벤치 연결 설정에서 RDS 인스턴스의 엔드포인트를 사용하고 있는지 다시 확인해야 합니다.
보안 그룹 설정: AWS RDS 인스턴스의 보안 그룹 설정이 워크벤치에서 연결을 시도하는 로컬 컴퓨터의 IP 주소로부터의 인바운드 연결을 허용하도록 설정되어 있는지 확인해야 합니다. 보안 그룹이 워크벤치를 실행하는 시스템의 IP 주소로부터 3306 포트에 대한 접근을 허용해야 합니다.
RDS 인스턴스 설정: RDS 인스턴스가 퍼블릭 액세스를 허용하도록 설정되어 있는지, 그리고 올바른 VPC 내에 구성되어 있는지 확인해야 합니다.
네트워크 문제: 로컬 네트워크 또는 인터넷 서비스 제공업체(ISP)에 의한 방화벽이나 네트워크 정책으로 인해 연결이 차단되지 않았는지 확인해야 합니다.
RDS 인스턴스 상태: RDS 인스턴스가 현재 실행 중이고 올바르게 작동하고 있는지 AWS 콘솔을 통해 확인합니다.
패스워드 오류: 사용자 이름과 패스워드가 RDS 데이터베이스 인스턴스의 마스터 사용자 자격 증명과 일치하는지 확인하세요.
호스트 이름, 포트 번호, 사용자 ID와 PWD를 몇 번이고 확인하였다. RDS 인스턴스도 정상적으로 생성하여 가동중이고, 내 인터넷 연결에도 문제가 없으니..
또 보안 그룹이 문제다!
문제의 원인은, 내 로컬 컴퓨터에서 RDS로 접근할 수 있는 규칙이 존재하지 않았던 것이다. 규칙을 추가하고.. 다시 테스트를 해보니..
???
보안 그룹에 문제가 있었던 것은 맞다. 그렇다면 이 오류의 원인이 둘 이상이라는 뜻이다.
VPC나 서브넷 설정이 문제라고 하면, 이건 내 역량을 초월하는 문제인데..
https://aeliketodo.tistory.com/96
한동안 머리를 싸매다가, 구글링으로 알아본 끝에 해결 방법을 찾기는 했다. AWS Virtual Private Cloud(VPC) 내에서 Private 서브넷의 라우팅 테이블을 수정하여 Public 서브넷처럼 동작하도록 변경하는 것인데, 연결 문제가 해결되었지만 뭔가 위험한 방법이 아닌가 싶어서 알아보니 이 방법은 장기적인 관점에서 보안 문제를 야기할 수 있다고 한다.
해당 서브넷에 있는 리소스(이 경우 RDS 인스턴스)는 인터넷 게이트웨이를 통해 인터넷에 연결할 수 있게 되어, 외부에서 RDS 인스턴스에 접근할 수 있게 됩니다. 이는 보안상의 위험을 증가시킬 수 있으므로, 실제로 필요한 경우에만 이 방법을 사용해야 하며, 사용 후에는 접근 제한을 위해 보안 그룹 및 네트워크 ACL 설정을 재검토하는 것이 중요합니다.
일단 연결이 우선이니 부득이하지만 보안 문제는 잠시 뒤로 미뤄두기로 했다..
다시 테스트해보니.. 드디어 워크벤치에서 RDS 인스턴스로 접속이 성공했다!
테스트용 데이터 삽입 기능이 동작하지 않는다. 이건 RDS에서 생성한 DB의 이름과, 내 백엔드 코드에서 작성한 DB의 이름이 일치하지 않아서 발생한 문제였다. 다시 살펴보니 RDS에서 인스턴스를 생성할 때 작성한 이름과 환경변수에서 사용한 DB 이름이 서로 다르게 입력되어 있었다. 어처구니 없는 실수..
로컬 프론트엔드 -> EC2 백엔드 -> RDS 데이터베이스
프론트엔드에서 요청을 보내고, 백엔드에서 이를 받아 데이터베이스로, 데이터베이스에서는 내 테스트용 코드대로 데이터가 삽입되는 것을 확인했다!