[Node.js] MySQL 데이터베이스 사용하기 (회원가입 구현)

관계형 데이터베이스

관계형 데이터베이스는 데이터베이스 저장소 안에 테이블(Table)을 만들어서 데이터를 저장한다. 테이블에 어떤 타입(Type)의 데이터가 들어갈 지 정의한 후 데이터를 한 줄씩 입력하는데, 이 한 줄의 데이터를 레코드(Record)라고 부르며, 이 레코드가 들어 있는 테이블을 검색할 수 있다.

테이블 구조

다음은 MySQL 사용 방법을 간단히 4단계로 나눈 것과 SQL문의 예시이다.

1단계: 데이터베이스 연결
2단계: 테이블 생성 - CREATE TABLE users(id text, name text, password text, age int)
3단계: 레코드 추가 - INSERT INTO users(id, name, age, password) VALUES ('101', 'zini', 24, '1234')
4단계: 데이터 조회 - SELECT id, name FROM users WHERE age > 10

MySQL 시작하기

http://dev.mysql.com/downloads 에서 Community Server를 다운로드 후 설치한다. Developer Default 옵션으로 설치하고 나머지는 다 기본으로 설정한다.

설치가 완료되었다면 데이터베이스와 테이블을 만들어야 하는데 필자는 HeidiSQL 프로그램을 사용하여 test 데이터베이스users 테이블을 만들었다. users 테이블에는 id(text), name(text), age(int), password(text) 칼럼이 있다.

익스프레스 서버에서 MySQL 연결하기

노드에서 MySQL에 연결하려면 mysql 모듈을 사용하면 된다. cmd에서 프로젝트 폴더로 이동 후 npm i mysql --save 입력으로 설치한다.

관계형 데이터베이스에 연결할 때는 보통 커넥션 풀(Connection Pool)을 사용한다. 이것은 데이터베이스 연결 객체가 너무 많이 만들어지는 것을 막고 한번 만든 연결을 다시 사용할 수 있게 한다. 커넥션 풀은 연결 개수를 제한하므로 연결을 사용한 후에는 반드시 다시 풀에 넣어주어야 하는 제약이 있다. 커넥션 풀을 만들려면 mysql 모듈의 createPool 메소드를 호출해서 옵션 정보가 있는 객체를 넣어준다.

var mysql = require('mysql');
var pool = mysql.createPool({
   connectionLimit: 10,
    host: 'localhost',
    user: 'root',
    password: 'wjswls1',
    database: 'test',
    debug: false
});
cs

createPool 메소드를 호출하면 데이터베이스에 연결해서 커넥션 풀을 만들게 된다. 이제 pool 객체에서 연결 객체를 가져와 사용할 수 있다.

사용자 추가 기능 넣기

MySQL 데이터베이스에 연결하는 과정이 끝났으니 이제 사용자 추가 기능을 넣어보자. app.js에 들어갈 addUser 함수를 작성한다.

//사용자 등록 함수
var addUser = function(id, name, age, password, callback){
    console.log('addUser 호출됨');
    
    pool.getConnection(function(err, conn){
        if(err){
            if(conn){
                conn.release();
            }
            callback(err, null);
            return;
        }
        
        console.log('DB 연결 스레드 아이디: ' + conn.threadId);
        
        var data = {id:id, name:name, age:age, password:password};
        
        var exec = conn.query('insert into users set ?', data, function(err, result){
            conn.release();
            console.log('실행 대상 SQL: ' + exec.sql);
            
            if(err){
                console.log('SQL 실행 오류 발생');
                console.dir(err);
                callback(err, null);
                return;
            }
            
            callback(null, result);
        });
    });
}
cs

이전 몽고디비에서 사용하던 방식과 코드 구조는 다른게 없다.

addUser 함수에 4개의 요청 파라미터를 넣고 callback 함수를 추가한다. pool 객체의 getConnection 메소드를 호출하면 커넥션 풀에서 연결 객체를 하나 가져올 수 있다.

연결 객체를 성공적으로 가져오면 콜백 함수를 통해 conn 파라미터로 연결 객체가 전달된다. 이제 query 메소드에 SQL문을 상황에 맞게 넣어주면 된다.

SQL문이 실행되면 콜백 함수가 호출되면서 결과가 result 파라미터로 전달된다. SQL문을 실행한 후에는 연결 객체의 release 메소드를 호출하여 연결 객체를 커넥션 풀로 반드시 반환해야 한다.

이제 사용자 추가 요청을 처리하는 라우터를 작성하자.

//사용자 등록 라우터
router.route('/process/adduser').post(function(req, res){
    console.log('/process/adduser 호출됨');
    
    var paramId = req.body.id || req.query.id;
    var paramPw = req.body.password || req.query.password;
    var paramName = req.body.name || req.query.name;
    var paramAge = req.body.age || req.query.age;
    
    console.log('요청 파라미터: ' + paramId + ', ' + paramPw + ', ' + paramName + ', ' + paramAge);
    
    if(pool){
        addUser(paramId, paramName, paramAge, paramPw,
               function(err, addedUser){
            if(err){
                console.error('사용자 추가 중 오류: '+err.stack);
                res.writeHead(200, {
                    "Content-Type""text/html;charset=utf8"
                });
                res.write('<h1>에러발생</h1>');
                res.end();
                return;
            }
            
            if(addedUser){
                console.dir(addedUser);
                console.log('inserted ' + addedUser.affectedRows + ' rows');
                
                var insertId = addedUser.insertId;
                console.log('추가한 레코드 ID: ' + insertId);
                
                res.writeHead(200, {
                    "Content-Type""text/html;charset=utf8"
                });
                res.write('<h1>사용자 추가 성공</h1>');
                res.end();
                return;
            } else{
                res.writeHead(200, {
                    "Content-Type""text/html;charset=utf8"
                });
                res.write('<h1>사용자 추가 중 오류</h1>');
                res.end();
            }
        });
    } else{
        res.writeHead(200, {
            "Content-Type""text/html;charset=utf8"
        });
        res.write('<h1>DB 연결 실패</h1>');
        res.end();
    }
});
cs

이전에 만든 adduser 코드와 거의 같다. 웹 브라우저로부터 4개의 파라미터를 POST 방식으로 받고 처리 후 addUser 함수를 실행한 결과를 응답으로 보낸다.

이제 사용자 추가 HTML 페이지만 만들면 된다. /public/adduser2.html을 작성한다.

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>MySQL 사용자 등록</title>
</head>
<body>
    <h1>MySQL 사용자 등록</h1>
    <hr>
    <form method="post" action="/process/adduser">
        <table>
            <tr>
                <td><label>아이디</label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>비밀번호</label></td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td><label>이름</label></td>
                <td><input type="text" name="name"></td>
            </tr>
            <tr>
                <td><label>나이</label></td>
                <td><input type="text" name="age"></td>
            </tr>
        </table>
        <input type="submit" value="등록하기" name="">
    </form>
</body>
</html>
cs

이제 app.js를 실행 후 localhost:3000/public/adduser2.html 로 접속해서 사용자를 추가해보자.



추가 완료가 정상적으로 출력되었다면 MySQL의 데이터베이스를 확인해보자.


-

app.js 풀 코드

var express = require('express'),
    http = require('http'),
    path = require('path'),
    bodyParser = require('body-parser'),
    cookieParser = require('cookie-parser'),
    static = require('serve-static'),
    errorHandler = require('errorhandler'),
    expressErrorHandler = require('express-error-handler'),
    expressSession = require('express-session'),
    MongoClient = require('mongodb').MongoClient;
//DB 객체를 위한 변수선언
let database;
//DB 연결
function connectDB() {
    const databaseUrl = 'mongodb://localhost:27017';
    MongoClient.connect(databaseUrl, {useNewUrlParser: true, useUnifiedTopology: true}, function(err, client) {
        if (err) throw err;
        console.log('DB 연결됨 : ' + databaseUrl);
        //database 변수에 할당
        let db = client.db("local");
        database = db
    })
}
var app = express();
app.set('port', process.env.PORT || 3000);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use('/public', express.static(__dirname + '/public'));
app.use(cookieParser());
app.use(expressSession({
    secret: 'my key',
    resave: true,
    saveUninitialized: true
}));
var router = express.Router();
//로그인 처리 라우터
router.route('/process/login').post(
    function (req, res) {
        console.log('process/login 호출됨');
        var paramId = req.body.id || req.query.id;
        var paramPassword = req.body.password || req.query.password;
        console.log('paramId : ' + paramId + ', paramPW : ' + paramPassword);
        if (database) {
            authUser(database, paramId, paramPassword,
                function (err, docs) {
                    if (err) {
                        console.log('Error!');
                        res.writeHead(200, {
                            "Content-Type""text/html;charset=utf8"
                        });
                        res.write('<h1>에러발생</h1>');
                        res.end();
                        return;
                    }
                    if (docs) {
                        console.dir(docs);
                        res.writeHead(200, {
                            "Content-Type""text/html;charset=utf8"
                        });
                        res.write('<h1>로그인 성공</h1><hr>');
                        res.write(docs[0].id + '/' + docs[0].name);
                        res.write('<br><a href="/public/login.html">다시 로그인 </a>');
                        res.end();
                    } else {
                        console.log('Error(empty)');
                        res.writeHead(200, {
                            "Content-Type""text/html;charset=utf8"
                        });
                        res.write('<h1>사용자 없음</h1><hr>');
                        res.write('<a href="/public/login.html">다시 로그인</a>');
                        res.end();
                    }
                });
        } else {
            console.log('DB 연결 안됨');
            res.writeHead(200, {
                "Content-Type""text/html;charset=utf8"
            });
            res.write('<h1>데이터베이스 연결 안됨</h1>');
            res.end();
        }
    }
);
//사용자 추가 라우터
router.route('/process/adduser').post(function(req, res){
    console.log('/process/adduser 호출됨');
    
    var paramId = req.body.id || req.query.id;
    var paramPassword = req.body.password || req.query.password;
    var paramName = req.body.name || req.query.name;
    
    if(database){
        addUser(database, paramId, paramPassword, paramName, function(err, result){
            if(err) throw err;
            if(result && result.insertedCount > 0){
                console.dir(result);
                res.writeHead(200, {
                    "Content-Type""text/html;charset=utf8"
                });
                res.write('<h1>사용자 추가 성공</h1><hr>');
                res.write('<p>name: ' + paramName + '</p>');
                res.write('<br><a href="/public/login.html">다시 로그인 </a>');
                res.end();
            } else{
                res.writeHead(200, {
                    "Content-Type""text/html;charset=utf8"
                });
                res.write('<h1>사용자 추가 실패<h1>');
                res.write('<br><a href="/public/login.html">다시 로그인 </a>');
                res.end();
            }
        });
    } else{
        res.writeHead(200, {
            "Content-Type""text/html;charset=utf8"
        });
        res.write('<h1>DB 연결 실패<h1>');
        res.write('<br><a href="/public/login.html">재접속</a>');
        res.end();
    }
});
app.use('/', router);
//사용자 조회 함수
var authUser = function (db, id, password, callback) {
    console.log("authUser 호출됨");
    var users = db.collection('users');
    var result = users.find({
        "id": id,
        "password": password
    });
    result.toArray(
        function (err, docs) {
            if (err) {
                callback(err, null);
                return;
            }
            if (docs.length > 0) {
                console.log('아이디 [%s], 비밀번호 [%s] 일치 사용자 찾음', id, password);
                callback(null, docs);
            } else {
                console.log('일치 사용자 없음');
                callback(nullnull);
            }
        });
}
//사용자 추가 함수
var addUser = function(db, id, password, name, callback){
    console.log('addUser 호출됨: ' + id + ', ' + password + ', ' + name);
    
    var users = db.collection('users');
    users.insertMany([{"id": id, "password": password, "name"name}], function(err, result){
        if(err){
            callback(err, null);
            return;
        }
        
        if(result.insertedCount > 0){
            console.log('사용자 추가됨: ' + result.insertedCount);
        } else{
            console.log('추가된 레코드 없음');
        }
        callback(null, result);
    });
}
// 404 에러 페이지 처리
var errorHandler = expressErrorHandler({
    static: {
        '404''./public/404.html'
    }
});
app.use(expressErrorHandler.httpError(404));
app.use(errorHandler);
//웹서버 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express server started with port ' + app.get('port'));
        connectDB(); //DB 연결 , DB 연결 먼저해도 상관 없음
    }
);
cs

댓글 없음:

Powered by Blogger.