mysql2 패키지를 이용한 Node.js와 MySQL(MariaDB) 연동 가이드
1. mysql2 패키지란?
mysql2는 mysql 패키지와 API 호환성을 유지하면서 성능을 개선하고, 최신 JavaScript 기능인 Promise와 async/await를 기본적으로 지원하는 차세대 MySQL 드라이버입니다.
mysql의 콜백 지옥(Callback Hell) 문제를 해결하고 더 빠르고 안정적인 코드를 작성할 수 있어, 현재 Node.js 생태계에서 가장 널리 사용되는 MySQL 드라이버입니다.
2. 설치
npm install mysql2
3. mysql 대비 mysql2의 장점
| 기능 | mysql |
mysql2 |
설명 |
|---|---|---|---|
| 비동기 처리 | 콜백(Callback) 방식만 지원 | Promise & async/await 기본 지원 |
"콜백 지옥"을 해결하고, 코드를 동기식처럼 작성하여 가독성/유지보수성 극대화 |
| 성능 | 상대적으로 느림 | 더 빠름 | Prepared Statements를 더 효율적으로 처리하고, 더 빠른 프로토콜 파서를 사용 |
| Prepared Statements | 지원 | 향상된 지원 | SQL 인젝션 공격을 더 효과적으로 방지하고, 반복 실행 시 성능 이점 제공 |
| API 호환성 | - | 높음 | 기존 mysql 코드를 최소한의 수정으로 mysql2로 마이그레이션 가능 |
결론: 새로운 Node.js 프로젝트를 시작한다면, 특별한 이유가 없는 한 mysql2 패키지를 사용하는 것이 압도적으로 유리합니다.
4. mysql2의 두 가지 API 스타일
mysql2는 기존 mysql과의 호환성을 위해 두 가지 스타일의 API를 제공합니다.
- 콜백 API (
require('mysql2')):mysql패키지와 거의 동일한 방식으로 동작합니다. 마이그레이션 시 유용합니다. - Promise API (
require('mysql2/promise')): 모든 함수가Promise를 반환하여async/await와 완벽하게 호환됩니다. 이 방식을 사용하는 것을 강력히 권장합니다.
이 문서는 **Promise API (mysql2/promise)**를 기준으로 설명합니다.
5. mysql2/promise 사용법
1) 커넥션 풀 생성 (createPool)
mysql과 마찬가지로, 웹 애플리케이션에서는 createPool을 사용하는 것이 표준입니다.
주요 옵션
mysql 패키지의 createPool 옵션과 거의 동일하지만, 몇 가지 유용한 옵션이 추가되었습니다.
const mysql = require("mysql2/promise");
const pool = mysql.createPool({
host: process.env.DB_HOST || "localhost",
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
// --- 성능 및 안정성 관련 옵션 (권장) ---
connectionLimit: 10,
waitForConnections: true,
queueLimit: 0,
// --- mysql2 추가/변경 옵션 ---
enableKeepAlive: true, // 비활성 연결이 끊어지는 것을 방지 (기본값: false)
keepAliveInitialDelay: 0, // Keep-alive 패킷 전송 시작 전 대기 시간(ms) (기본값: 0)
});
2) 기본 쿼리 실행 예제 (async/await)
async/await를 사용하여 코드가 매우 간결하고 직관적으로 변합니다.
async function getUser(id) {
try {
const sql = "SELECT id, email, name FROM users WHERE id = ?";
// pool.query()는 [rows, fields] 형태의 배열을 반환합니다.
const [rows] = await pool.query(sql, [id]);
return rows[0]; // 첫 번째 행만 반환
} catch (error) {
console.error("Query Error:", error);
throw error; // 에러를 상위로 전파
}
}
// 함수 사용
(async () => {
const user = await getUser(1);
if (user) {
console.log("User Found:", user);
} else {
console.log("User not found.");
}
})();
6. CRUD 예제 (async/await 기반)
1) Create (데이터 삽입 - INSERT)
async function createUser(userData) {
try {
const sql = "INSERT INTO users SET ?";
// pool.execute()는 Prepared Statement를 사용하여 더 안전하고 빠릅니다.
const [result] = await pool.execute(sql, [userData]);
console.log("New user created! Inserted ID:", result.insertId);
return result.insertId;
} catch (error) {
console.error("INSERT Error:", error);
throw error;
}
}
// 사용 예시
createUser({ name: "Async User", email: "async@test.com" });
query()vsexecute()
query(): SQL 문을 직접 실행.execute(): SQL 문을 미리 컴파일(prepare)한 후 실행. 반복적인 쿼리에서 성능이 더 좋고, SQL 인젝션 방어에 더 효과적입니다. 가급적execute()사용을 권장합니다.
2) Read (데이터 조회 - SELECT)
async function getAllUsers() {
try {
const [rows] = await pool.query(
"SELECT id, name, email FROM users ORDER BY id DESC"
);
console.log(`Found ${rows.length} users.`);
return rows;
} catch (error) {
console.error("SELECT Error:", error);
throw error;
}
}
3) Update (데이터 수정 - UPDATE)
async function updateUser(id, dataToUpdate) {
try {
const sql = "UPDATE users SET ? WHERE id = ?";
const [result] = await pool.execute(sql, [dataToUpdate, id]);
console.log("Changed Rows:", result.changedRows);
return result.changedRows;
} catch (error) {
console.error("UPDATE Error:", error);
throw error;
}
}
4) Delete (데이터 삭제 - DELETE)
async function deleteUser(id) {
try {
const sql = "DELETE FROM users WHERE id = ?";
const [result] = await pool.execute(sql, [id]);
console.log("Deleted Rows:", result.affectedRows);
return result.affectedRows;
} catch (error) {
console.error("DELETE Error:", error);
throw error;
}
}
7. 트랜잭션(Transaction) 처리 예제
async/await 덕분에 "콜백 지옥" 없이 트랜잭션 로직을 매우 깔끔하게 작성할 수 있습니다.
async function createUserWithLog(userData) {
let connection;
try {
// 1. 풀에서 커넥션 가져오기
connection = await pool.getConnection();
// 2. 트랜잭션 시작
await connection.beginTransaction();
// 쿼리 1: 사용자 생성
const [userResult] = await connection.execute("INSERT INTO users SET ?", [
userData,
]);
const userId = userResult.insertId;
// 쿼리 2: 로그 기록
const logData = { user_id: userId, action: "created" };
await connection.execute("INSERT INTO logs SET ?", [logData]);
// 3. 모든 쿼리 성공 시 커밋
await connection.commit();
console.log("Transaction Complete. User created with ID:", userId);
return userId;
} catch (error) {
// 4. 에러 발생 시 롤백
if (connection) await connection.rollback();
console.error("Transaction Error:", error);
throw new Error("Failed to create user with log.");
} finally {
// 5. 커넥션 반납 (필수!)
if (connection) connection.release();
}
}
8. 버전별 주요 변경 사항 (mysql2)
mysql2는 지속적으로 발전해왔으며, 버전별로 몇 가지 중요한 변경점이 있습니다.
v2.x
mysql패키지와의 높은 API 호환성을 유지하며Promise지원을 도입한 초기 안정 버전입니다.- 대부분의 기본 기능이 이 버전에서 확립되었습니다.
createPool옵션에서db키를 사용해도 호환성을 위해 동작하는 경우가 많았지만, 문서는database를 권장했습니다.
v3.x (최신)
- 성능 개선: 내부 파서와 로직이 최적화되어
v2에 비해 더 나은 성능을 보입니다. BigInt지원: MySQL의BIGINT타입을 JavaScript의BigInt타입으로 직접 매핑하는 기능이 추가되었습니다 (supportBigNumbers: true, bigNumberStrings: false).- 더 엄격한 타입 및 옵션:
database옵션 사용이 표준화되었고, 일부 오래된 옵션들이 제거되거나 변경되었습니다. - ESM (ECMAScript Modules) 지원:
import구문을 더 잘 지원하도록 구조가 개선되었습니다. - 보안 강화: 종속성 업데이트 및 내부 로직 개선으로 보안이 강화되었습니다.
권장 사항:
- 새로운 프로젝트를 시작한다면 최신
v3.x버전을 사용하는 것이 좋습니다. - 기존
v2.x프로젝트를 운영 중이라면, 큰 호환성 문제 없이v3.x로 업그레이드할 수 있습니다.npm install mysql2@latest로 업데이트한 후 테스트를 진행해보는 것을 권장합니다.
Table of contents 목차
- mysql2 패키지를 이용한 Node.js와 MySQL(MariaDB) 연동 가이드