2019. 07. 15 수정


이번 글에서는 Node.js에서 MySQL을 연동하여 MySQL을 다루는 방법에 대해 알아보도록 하겠습니다.

  • 개발 환경

    • express-generator : 스켈레톤 생성

    • Windows / MySQL 설치

    • WebStorm



1. MySQL 모듈

Node.js에서 MySQL을 사용하려면 mysql 모듈을 설치해야 합니다.

# npm install mysql


물론 윈도우 또는 리눅스 등에도 MySQL이 설치되어 있어야 합니다.

그래야 mysql 모듈의 createConnection() 메서드를 통해 MySQL과 커넥션을 맺을 수 있습니다.


createConnection(options) 메서드를 호출할 때, 인자로 커넥션과 관련된 DB 정보를 넘겨주는데, options에 부여할 수 있는 속성은 다음과 같습니다.

  • host
    • 연결할 호스트
  • port
    • 연결할 포트
  • user
    • 사용자 이름
    • 필수 입력 값입니다.
  • password
    • 사용자 비밀번호
    • 필수 입력 값입니다.
  • database
    • 연결할 데이터베이스
  • debug
    • 디버그 모드를 사용할 것인지





2. MySQL 커넥션

먼저 MySQL을 사용하기 전에, 필요한 데이터들을 시스템에 추가하도록 하겠습니다.

// 1. 데이터 베이스 생성
CREATE DATABASE mysqltest;

// 2. products 테이블 생성
CREATE TABLE products(
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    modelnumber VARCHAR(15) NOT NULL,
    series VARCHAR(30) NOT NULL
);

// 3. 데이터 추가
INSERT INTO products (name, modelnumber, series) VALUES ("victolee", "1234", "1111");


다음으로 createConnection() 메서드를 호출하여 MySQL와 연동하는 코드를 작성해보도록 하겠습니다.

express-generator로 생성한 파일 중 /routes/index.js에서 아래와 같이 작성합니다.

/routes/index.js

const express = require('express');
const router = express.Router();
const mysql = require("mysql"); // mysql 모듈 require

// 커넥션 연결
let client = mysql.createConnection({
user: "root",
password: "비밀번호를 입력해주세요",
database: "mysqltest"
})

module.exports = router;

mysql 모듈객체의 createConnection() 메서드를 호출하여 반환된 객체를 client 변수에 할당합니다.

여기서 password 프로퍼티는 꼭 본인의 mysql 비밀번호를 입력해서 바꿔주셔야 합니다.

그러면 client 객체를 통해 쿼리를 작성할 수 있으며, 이는 뒤에서 살펴보도록 하겠습니다.





3. 데이터 조회 및 추가

이번에는 사용자가 HTML form 태그를 통해 데이터를 서버로 보냈을 때, 이 데이터를 받아 테이블에 추가하는 코드를 작성해보도록 하겠습니다.


먼저 views폴더에 create.ejs 파일을 생성하여 아래와 같이 HTML을 작성합니다.

/views/create.ejs

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>목록 추가하기</h1>
<hr>

<form action="/create" method="POST">
<table>
<tr>
<td><input type="text" name="name" placeholder="이름을 입력하세요."></td>
</tr>
<tr>
<td><input type="text" name="modelnumber" placeholder="모델넘버를 입력하세요."></td>
</tr>
<tr>
<td><input type="text" name="series" placeholder="시리즈를 입력하세요."></td>
</tr>
</table>
<input type="submit" value="전송하기">
</form>
</body>
</html>


위와 같이 form 요소가 추가되어, "전송하기" 버튼을 누르면 /create 경로에 POST 방식으로 요청을 보내게 됩니다.


그 전에, create.ejs를 렌더링 해주는 GET 요청을 처리해주는 라우터가 필요하고,

product 테이블에 데이터를 추가해주는 POST 요청을 처리해주는 라우터가 필요합니다.

/routes/index.js 파일에 이에 대한 라우터 함수를 추가적으로 등록해주도록 하겠습니다.


/routes/index.js

router.get('/create', function(req, res, next) {
client.query("SELECT * FROM products;", function(err, result, fields){
if(err){
console.log(err);
console.log("쿼리문에 오류가 있습니다.");
}
else{
res.render('create', {
results: result
});
}
});
});

router.post('/create', function(req, res, next) {
var body = req.body;

client.query("INSERT INTO products (name, modelnumber, series) VALUES (?, ?, ?)", [
body.name, body.modelnumber, body.series
], function(){
res.redirect("/create");
});
});

create.ejs를 렌더링하는 GET 라우터는 쉽게 작성할 수 있습니다.

쿼리를 작성하는 방법은 query() 메서드를 호출하여 인자로 쿼리 내용을 작성하면 됩니다.

GET 라우터 함수는 products 테이블의 모든 상품을 조회하여, 웹 페이지에 JSON으로 반환하는 코드입니다.


POST 방식의 데이터는 req.body를 통해 가져올 수 있습니다.

( Express 4부터는 body-parser가 내장 모듈로 등록되어 있네요. )


input 요소의 name 속성을 통해 POST 데이터를 가져올 수 있습니다.

예를 들어, body.name, body.modelnumber, body.series는 input태그의 각 name입니다.


이 때, 사용자가 어떤 입력 값을 전달할지 모르기 때문에 INSERT 쿼리를 작성 할 때 ? 키워드를 사용합니다.

각각의 ?는 client.query() 메서드의 두 번째 파라미터인 배열의 각 원소( input 태그의 각 name 속성 )가 대응되며,

어떤 column에 들어갈 것인지는 VALUES 앞에 있는 칼럼에 대응되어 값이 저장됩니다.

예를 들어, name 컬럼에는 body.name이 저장됩니다.



실제로 데이터가 잘 들어가는지 테스트하기 위해 브라우저에서 http://localhost:3000/create 으로 접속해서 데이터를 전송한 후,

DB에서 products 테이블을 조회하면 데이터가 추가된 것을 확인할 수 있습니다.



*** Error ***

ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client

서버 실행시 위와 같은 에러가 발생한다면, MySQL에서 아래의 명령어를 실행하면 해결됩니다. ( 참고 링크 )

아마 MySQL 버전이 8.x일 때 발생하는 문제 같습니다.

# ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '새로운 비밀번호';

# flush privileges;





4. products 목록 조회

이번에는 products 테이블에 있는 모든 데이터를 응답해보도록 하겠습니다.


먼저 응답 객체에서 nbsp;res.render() 메서드를 호출할 때 쿼리 결과를 클라이언트로 전송하기 위해,

/routes/index.js 파일에서 위에서 작성한 "/create" 경로 라우터 미들웨어를 아래와 같이 수정합니다.

/routes/index.js

router.get('/create', function(req, res, next) {
client.query("SELECT * FROM products;", function(err, result, fields){
if(err){
console.log("쿼리문에 오류가 있습니다.");
}
else{
res.render('create', {
results: result
});
}
});
});

render() 메서드를 호출 할 때, 쿼리 결과인 products 테이블의 모든 데이터를 콜백 함수에 result 인자로 넘겨주었습니다.

result에는 products 테이블의 데이터들이 배열의 형태로 담겨있습니다.



다음으로 데이터들을 반복문으로 조회하기 위해, /views/create.ejs 파일의 </form> 태그 아래에 아래의 코드를 추가합니다.

/views/create.ejs

<table>
<% for(let data of results) { %>
<tr>
<td><%= data.name %></td>
<td><%= data.modelnumber %></td>
<td><%= data.series %></td>
</tr>
<% } %>
</table>


이전처럼 테스트를 해보면, 반복문을 돌면서 조회가 잘 된다는 것을 확인할 수 있습니다.





이상으로 MySQL을 연동하여 데이터 조회, 추가 쿼리를 작성해보았습니다.

이 글에서 작성한 /route/index.js 파일의 코딩은 별로 바람직하지 않습니다.

DB 관련 설정 파일은 config 폴더를 만들어서 관리하는 것이 좋으며, 커넥션 풀 등 고려해야 할 것들이 많이 있습니다.

단지, 이런 식으로 MySQL 모듈을 사용한다는 점만 이해하시면 좋을 것 같네요.


참고로 query를 작성하는 것 말고, ORM을 이용하여 직관적인 메서드를 통해 DB 작업을 할 수 있습니다.

Node.js에서는 대표적으로 sequelize 라는 모듈을 통해 구현할 수 있는데, 이 모듈을 사용하면 코드가 간결해지고, 생산성이 높아집니다.

다음 글에서는 sequelize에 대해 알아보도록 하겠습니다.