
MySQL 서버가 외부에서의 접속을 받아들일 수 있도록 준비해야 했다.
보안상 기본적으로 MySQL은 자기 자신(localhost)의 접속만 허용한다.
이 설정을 변경하기 위해 설정 파일을 연다.
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
파일 안에서 bind-address = 127.0.0.1 부분을 찾아 IP 주소를 0.0.0.0으로 변경한다.
이는 모든 IP 주소로부터의 접속을 허용한다.
bind-address = 0.0.0.0
변경된 설정을 적용하기 위해 MySQL 서비스를 재시작한다.
sudo systemctl restart mysql
보안을 위해 root 계정을 사용하지 않고 Node.js 앱 전용 사용자를 만들었다.
root 사용자로 VM에서 접속한다.seungjun 이라는 이름의 사용자를 생성하고, 외부 IP(%)에서 접속할 수 있도록 허용한 뒤 모든 권한을 부여한다.-- 'seungjun' 사용자를 'Qwer1234!' 비밀번호로 생성하고, 모든 IP에서의 접속 허용
CREATE USER 'seungjun'@'%' IDENTIFIED BY 'Qwer1234!';
-- 모든 DB에 대한 모든 권한을 부여
GRANT ALL PRIVILEGES ON *.* TO 'seungjun'@'%';
-- 변경된 권한을 즉시 시스템에 적용
FLUSH PRIVILEGES;
MySQL 드라이버와 환경 변수 관리를 위한 라이브러리를 설치한다.
npm install mysql2 dotenv
mysql2: Promise를 지원해 async/await과 함께 쓰기 좋은 라이브러리dotenv: DB 비밀번호와 같은 민감 정보를 코드와 분리하여 안전하게 관리프로젝트 루트에 .env 파일을 만들고, Ubuntu VM의 IP 주소와 방금 만든 MySQL 사용자 정보를 입력한다.
이 파일은 절대 Git에 올리면 안된다 (.gitignore에 추가)!!!!!
DB_HOST=서버 IP 주소
DB_USER=DB 계정 ID
DB_PASSWORD=DB 비밀번호
DB_DATABASE=DB 이름
JWT_SECRET=JWT SECRET 키
실제 연결 로직을 작성한다.
웹 서버 환경에서는 요청이 있을 때마다 연결을 새로 만드는 것보다, 미리 만들어 둔 연결을 빌려 쓰고 반납하는 커넥션 풀 방식이 훨씬 효율적이다.
import mysql from 'mysql2/promise';
export const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
처음에 VM의 IP가 아닌 localhost로 접속을 시도하는 오류가 있었다.
AggregateError [ECONNREFUSED]:
at internalConnectMultiple (node:net:1139:18)
at afterConnectMultiple (node:net:1712:7) {
code: 'ECONNREFUSED',
fatal: true,
[errors]: [
Error: connect ECONNREFUSED ::1:3306
at createConnectionError (node:net:1675:14)
at afterConnectMultiple (node:net:1705:16) {
errno: -61,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '::1',
port: 3306
},
Error: connect ECONNREFUSED 127.0.0.1:3306
at createConnectionError (node:net:1675:14)
at afterConnectMultiple (node:net:1705:16) {
errno: -61,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 3306
}
]
}
원인은 dotenv가 환경변수를 로드하기 전에 DB 연결 모듈이 먼저 실행되어 process.env.DB_HOST 값이 비어있었기 때무니다.
이를 해결하기 위해 dotenv를 먼저 import하도록 순서를 바로잡았다.
// app.js
import 'dotenv/config'; // 👈 1. 가장 먼저 실행되어 환경 변수 설정
import { pool } from './db.js'; // 👈 2. 그 후에 DB 모듈을 불러옴
// ... (이하 코드)
회원가입 로직을 만들지 않았기 때문에 로그인을 테스트하기 위해 임의의 데이터를 DB에 작성시켰다.
이를 데이터베이스에 씨앗을 뿌린다는 의미로 Database Seeding이라고 한다.
// 1. 비밀번호를 bcrypt로 해싱
const hashedPassword = await bcrypt.hash(user.password, saltRounds);
console.log(`'${user.id}'의 비밀번호를 해싱하는 중...`);
// 2. SQL 쿼리 작성
const sql = 'INSERT INTO users (user_id, password) VALUES (?, ?)';
// 3. 해싱된 비밀번호와 함께 사용자 정보 DB에 저장
await connection.query(sql, [user.id, hashedPassword]);
console.log(`✅ 사용자 '${user.id}'가 성공적으로 추가되었습니다.`);

Workbench로 확인한 결과 데이터가 잘 들어가 있었다.