Nodejs + mysql2 설정 및 트랜잭션

개발하는 구황작물·2024년 3월 12일
1
  1. mysql2 다운로드
npm install mysql2 --save
  1. .env 파일 생성 및 dotenv 다운로드
npm install dotenv --save
# .env
DATABASE_USER="username"
DATABASE_PASSWORD="password"
DATABASE_NAME="dbname"
DATABASE_HOST="localhost"
  1. db connection을 설정한다.
const mysql = require('mysql2/promise');
const dotenv = require('dotenv');
dotenv.config();

const conn = mysql.createPool({ //풀 생성
    host: process.env.DATABASE_HOST,
    user: process.env.DATABASE_USER,
    password: process.env.DATABASE_PASSWORD,
    database: process.env.DATABASE_NAME,
    port: 3306,
    waitForConnections: true, // 풀에 있는 연결이 모두 사용중일 때 대기
    connectionLimit: 10, //풀에 넣을 수 있는 최대 연결 수 
    maxIdle: 10, // 최대 유휴 커넥션, default는 connectionLimit와 동일
    idleTimeout: 60000, // 유휴 커넥션 timeout(ms), default는 60000
    queueLimit: 0, //대기열에 넣을 수 있는 최대 요청 수
    enableKeepAlive: true, //TCP 연결에 keep-alive를 넣을지 결정
    keepAliveInitialDelay: 0 //keepAlive 패킷을 처음으로 보낼 때까지의 지연 시간
});
  1. 이제 위의 connection으로 query문을 실행하면 된다.
const mysql = require('mysql2/promise');
const dotenv = require('dotenv');
dotenv.config();

const conn = mysql.createPool({ //풀 생성
    ...
});
  
const getConnection = async () => { //connection 생성
    try {
        const connection = await conn.getConnection();
        return connection;
    } catch (error) {
        console.error(`connection error : ${error.message}`);
        return null;
    }
};
  
const result = async () => {
    const connection = await getConnection();
    try {
        await connection.beginTransaction(); //트랜잭션 begin
      	const query = `SELECT * FROM USER WHERE name = ? AND email = ?`;
      	const data = ['potato','potato@potato.com'];
      	const result = await connection.query(query, data);
   
        await connection.commit(); //commit

        return result;
    } catch (err) {
        console.log("rollback connection");
        await connection.rollback(); // rollback
        console.error("db error : ", err);
    } finally {
        console.log("release connection");
        connection.release(); //connection release
    }
}

... 이렇게 하면 기본은 끝나지만
Java, SpringBoot 를 사용했던 개발자라면 어딘가 불편할 것이다.

Spring 진영에는 AOP활용으로 @Transactional을 통해 트랜잭션 로직과 비즈니스 로직을 분리 할 수 있었지만

nodejs에서는 특정 ORM을 사용하지 않는 이상 트랜잭션 로직과 비즈니스 로직을 분리하는 방법을 찾을 수 없었다. (TypeORM을 사용하면 분리가 가능하다고 한다.)

(그냥 내가 못 찾은 거일 수도 있다만...방법을 알고 계신다면 제보 부탁드립니다.)

그래서 최대한 분리를 해본 결과 아래와 같은 결과가 나왔다.

dbConfig.js

const mysql = require('mysql2/promise');
const dotenv = require('dotenv');
dotenv.config();

const conn = mysql.createPool({ //풀 생성
    host: process.env.DATABASE_HOST,
    user: process.env.DATABASE_USER,
    password: process.env.DATABASE_PASSWORD,
    database: process.env.DATABASE_NAME,
    port: 3306,
    ...
});

const getConnection = async () => {
    try {
        const connection = await conn.getConnection();
        return connection;
    } catch (error) {
        console.error(`connection error : ${error.message}`);
        return null;
    }
};


exports.transaction = async (logic, req) => {
    const connection = await getConnection();
    try {
        await connection.beginTransaction();
        const result = await logic(connection, req);
        await connection.commit();

        return result;
    } catch (err) {
        console.log("rollback connection");
        await connection.rollback();
        console.error("db error : ", err);
    } finally {
        console.log("release connection");
        connection.release();
    }
}

repository.js

exports.findById = async (conn, id) => {
    try {
        const query = `SELECT * FROM users where id = ?`;

        const [result] = await conn.query(query, id);
        
        return result[0];
    } catch (err) {
        console.error("db err : ", err);
    }
}

service.js

const repository = require('../repository/repository');

exports.getUserData = async (conn, userId) => {
    return await repository.findById(conn, userId);
}

controller.js

const service = require('../service/service');
const { tx } = require('../db/dbConfig');

exports.getServiceBillings = async (req, res) => {
    const result =  await tx(repository.getUserData, 1);

    return res.status(200).send(result);
}

최대한 분리를 해본 결과 계층간 분리는 되었으나 파라미터로 connection을 넘겨줘야했다.
다른 방법을 계속 찾아봐야 할 것 같다.

profile
어쩌다보니 개발하게 된 구황작물

0개의 댓글