본문 바로가기
Web/Nodejs

Nodejs(express) + Mysql 웹서버(게시판+로그인,회원가입) 만들기

by jh117jh 2023. 6. 14.
728x90

지난 한 달간 블로그에 글을 못 올렸는데 개발공부를 하면서 웹을 만들고 있었다.

웹서버를 만드는 과정을 하나하나 올려볼까 했는데 공부하는 중에는 아직 개념이 잡히지 않아 설명에 미흡한 점이 있을까 다 만들고 올리기로 했다.(생각해 보면 만드는 과정과 시행착오를 올리는 게 더 나을 것 같아 다음부턴 차근차근 올려야겠다 ㅠㅠ)

 

 

 

nodejs를 처음 다뤄보는 경험이었기에 생활코딩 WEB2-Node.js를 참고하여 만들었다.

https://opentutorials.org/course/3332

 

WEB2 - Node.js - 생활코딩

수업소개 이 수업은 JavaScript를 이용해서 Node.js를 제어해 동적으로 HTML 코드를 생성하는 웹애플리케이션을 만드는 방법에 대한 수업입니다.  수업대상 예를들어 1억개의 페이지로 이루어진 웹사

opentutorials.org

 

실습에서 Mysql,Express,passport.js등을 따로 다루기 때문에 저처럼 mysql+express-session+passport를 사용하여 만드실 분들은 참고하시면 좋을 것 같다.

(물론 저도 하면서 불필요한 요소는 빼고 제가 추가하고 싶은 기능을 넣은 부분도 있으니 참고해주세요 ㅎㅎ)

 

전체 소스코드는 깃허브에 업로드 해놨다.

https://github.com/Jeon-Ji-Hwan/nodejs-website-project

 

GitHub - Jeon-Ji-Hwan/nodejs-website-project

Contribute to Jeon-Ji-Hwan/nodejs-website-project development by creating an account on GitHub.

github.com

 

 

우선 nodejs와 각자의 DB, 테이블 생성을 했다는 가정하에 포스팅하겠습니다.

기본적으로 프로젝트 폴더에서 npm init를 입력해 package.json, package-lock.json파일을 생성해 주시고

npm init

사용할 모듈들을 다운로드해주시면 됩니다.

npm install express //ex) express 다운

 

코드구성

main.js 

웹페이지의 틀 역할로 각 페이지의 요청과 응답을 받아 출력해 준다.

가독성을 위해 각 페이지의 코드들은 topic.js에 넣어 모듈로 정리해 줬다.

로그인 기능은 passport를 사용했습니다.

더보기
var express = require('express')
var app = express()
var db = require('../lib/db');
var topic = require('../lib/topic');
var bodyParser = require('body-parser');
var session = require('express-session');
var MysqlStore = require('express-mysql-session')(session);
var flash = require('connect-flash');


app.use(session({
    secret: 'qwerasd32eafff4wfa2a',
    resave: false,
    saveUninitialized: true,
    store: new MysqlStore({ //세션스토어는 저장공간에 따라 형식이 다를수 있음, mariaDB사용
        host: '127.0.0.1',
        user: 'nodejs',
        password: '00wlghks**',
        database: 'opentutorials'
    })
}));

app.use(flash());
app.use(bodyParser.urlencoded({ extended: false }));

var passport = require('passport')
    , LocalStrategy = require('passport-local').Strategy;

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser(function(user, done) {
    done(null, user.email);
});
  
passport.deserializeUser(function(id, done) {
    db.query('SELECT * FROM user WHERE email = ?',[id],function(error,user){
        done(null,user[0]);
    })
});
  

passport.use(new LocalStrategy(  //로그인 기능 구현
    {
        usernameField: 'email',
        passwordField: 'password'
    },
    function (username, password, done) {
        db.query('SELECT * FROM user WHERE email=?',[username],function(error,result){
            console.log(result[0]);
            if(!result[0]) {
                return done(null, false, {
                    message: 'Incorrect username.'
                });
            }
            else if(username === result[0].email){
                if(password === result[0].password){
                    return done(null, result[0]);
                } else if(!result[0].password || password != result[0].password){
                    return done(null, false, {
                        message: 'Incorrect password.'
                    });
                }
            }
        })
    }
));
      
app.post('/login_process',
passport.authenticate('local', {
    successRedirect: '/',
    failureRedirect: '/login',
    failureFlash: true
}));

app.get('/', function (request, response) {
    topic.home(request, response)
});

app.get('/page/:pageId', function (request, response) {
    topic.page(request, response);
});

app.get('/create', function (request, response) {
    topic.create(request, response);
})

app.post('/create_process', function (request, response) {
    topic.create_process(request, response);
})

app.get('/update/:pageId', function (request, response) {
    topic.update(request, response);
})

app.post('/update_process', function (request, response) {
    topic.update_process(request, response);
})

app.post('/delete_process', function (request, response) {
    topic.delete_process(request, response);
})

app.get('/login', function (request, response) {
    topic.login(request,response);
})

app.get('/logout_process', function (request, response) {
    topic.logout_process(request,response);
})

app.get('/register', function(request,response){
    topic.register(request,response);
})

app.post('/register_process',function(request,response){
    topic.register_process(request,response);
})
app.listen(3000, function () {
    console.log('yeah!')
});

topic.js

웹페이지의 실직적인 코드들을 정리해 놓은 파일이다.

더보기
var express = require('express');
var app = express()
var db = require('./db');
var template = require('./template');
var url = require('url');
var session = require('express-session');
var bodyParser = require('body-parser');
var auth = require('./auth');

app.use(bodyParser.urlencoded({ extended: false }));

exports.home = function(request,response){
    db.query('SELECT * FROM topic',function(error,topics){
        
        var title = 'Welcome';
        var description = 'Hello, Node.js';
        var list = template.List(topics);
        var html = template.HTML(title,list,`<h2>${title}</h2><p>${description}</p>`,`<a href="/create">create</a>`,auth.StatusUI(request,response));
        response.writeHead(200);
        response.end(html);
    });
}

exports.page = function(request,response){
    var _url = request.url;
    var queryData = url.parse(_url, true).query;
    db.query('SELECT * FROM topic',function(error,topics){
        if(error){
            throw error;
        }
        db.query(`SELECT * FROM topic LEFT JOIN user ON topic.user_id=user.id WHERE topic.id=?`,[request.params.pageId],function(error2,topic){
            if(error2){
                throw error2;
            }
            var title = topic[0].title;
            var description = topic[0].description;
            var list = template.List(topics);
            var html = template.HTML(title,list,`<h2>${title}</h2><p>${description}</p><p> by ${topic[0].displayName}`,
            `<a href="/create">create</a> 
            <a href="/update/${request.params.pageId}">update</a>
            <form action='/delete_process' method='post'>
                <input type='hidden' name='id' value='${request.params.pageId}'>
                <input type='submit' value='delete'> 
            </form>`,auth.StatusUI(request,response));
            response.send(html);
        })
    })
}

exports.create = function(request,response){
    if(!auth.IsOwner(request,response)){ //로그인을 하지 않을시 메인으로 이동
        response.redirect('/');
        return false;
    }
    db.query('SELECT * FROM topic',function(error,topics){
        db.query("SELECT * FROM user", function(error2,authors){

            var title = 'WEB-create';
            var list = template.List(topics);
            var html = template.HTML(title,list,
            `
            <form action="/create_process" method="post">
                <p><input type="text" name="title" placeholder="title"></p>
                <p>
                    <textarea name="description" placeholder="description"></textarea>
                </p>
                <input type='hidden' name='id' value=${request.user.id}>
                <p>
                    <input type="submit">
                </p>
            </form>
            `,`<a href="/create">create</a>`,auth.StatusUI(request,response));
            response.send(html);
        });
    })
}

exports.create_process = function(request,response){
    if(!auth.IsOwner(request,response)){ //로그인을 하지 않을시 메인으로 이동
        response.redirect('/');
        return false;
    }
    var post = request.body;
    db.query(`
        INSERT INTO topic (title, description, created, user_id) 
        VALUES(?, ?, NOW(), ?)`,
        [post.title, post.description, post.id], 
        function(error, result){
            if(error){
                throw error;
            }
            response.redirect(`/?id=${result.insertId}`);
        }
    )
}

exports.update = function(request,response){
    if(!auth.IsOwner(request,response)){ //로그인을 하지 않을시 메인으로 이동
        response.redirect('/');
        return false;
    }
    var _url = request.url;
    var queryData = url.parse(_url, true).query;
    db.query('SELECT * FROM topic',function(error,topics){
        if(error){
            throw error;
        }
        db.query(`SELECT * FROM topic WHERE id=?`,[request.params.pageId],function(error2,topic){
            if(error2){
                throw error2;
            }
            db.query("SELECT * FROM user", function(error2,authors){
                var list = template.List(topics);
                var html = template.HTML(topic[0].title,list,
                `
                <form action="/update_process" method="post">
                <input type='hidden' name='id' value='${topic[0].id}'>
                <p><input type="text" name="title" placeholder="title" value='${topic[0].title}'></p>
                <p>
                    <textarea name="description" placeholder="description">${topic[0].description}</textarea>
                </p>
                <p>
                    <input type="submit">
                </p>
                </form>
                `,`<a href="/create">create</a> <a href="/update?id=${topic[0].id}">update</a>`,auth.StatusUI(request,response));
                response.send(html);
            })
        })
    })
}

exports.update_process = function(request,response){
    if(!auth.IsOwner(request,response)){ //로그인을 하지 않을시 메인으로 이동
        response.redirect('/');
        return false;
    }
    var post = request.body;
    db.query('UPDATE topic SET title=?, description=? WHERE id=?', [post.title, post.description, post.id], function(error, result){
        response.redirect(`/?id=${post.id}`);
    })

}

exports.delete_process = function(request,response){
    if(!auth.IsOwner(request,response)){ //로그인을 하지 않을시 메인으로 이동
        response.redirect('/');
        return false;
    }
    var post = request.body;
    db.query('DELETE FROM topic WHERE id=?',[post.id],function(error,result){
        if(error){
            throw error;
        }
        response.redirect('/');
    })
}

exports.login = function(request,response){
    db.query('SELECT * FROM topic', function (error, topics) {
        var fmsg = request.flash();
        var feedback = '';
        if(fmsg.error){
            feedback = fmsg.error[0];
        }

        var title = 'Login';
        var list = template.List(topics);
        var html = template.HTML(title, list,
            `
            <div style="color:red;">${feedback}</div>
            <form action="/login_process" method="post">
                <p><input type="text" name="email" placeholder="email"></p>
                <p><input type="password" name="password" placeholder="password"></p>
                <p><input type="submit" value='login'></p>
            </form>
            `, `<a href="/create">create</a>`, "");
        response.writeHead(200);
        response.end(html);
    });
}



exports.logout_process = function(request,response){
    request.logout(function (err) {
        if (err) {
            return next(err);
        }
        response.redirect("/");
    });
}

exports.register = function(request,response){
    db.query('SELECT * FROM topic', function (error, topics) {
        var fmsg = request.flash();
        var feedback = '';
        if(fmsg.error){
            feedback = fmsg.error[0];
        }

        var title = 'Login';
        var list = template.List(topics);
        var html = template.HTML(title, list,
            `
            <div style="color:red;">${feedback}</div>
            <form action="/register_process" method="post">
                <p><input type="text" name="email" placeholder="email"></p>
                <p><input type="password" name="password" placeholder="password"></p>
                <p><input type="password" name="password2" placeholder="password check"></p>
                <p><input type="text" name="displayName" placeholder="display name"></p>
                <p><input type="submit" value='register'></p>
            </form>
            `, `<a href="/create">create</a>`, "");
        response.writeHead(200);
        response.end(html);
    });
}

exports.register_process = function(request,response){
    var post = request.body;
    var email = post.email;
    var password = post.password;
    var password2 = post.password2;
    var displayName = post.displayName;
    
    if (email && password && password2) {
        db.query('SELECT * FROM user WHERE email = ?', [email], function(error, results, fields) { // DB에 같은 이름의 회원아이디가 있는지 확인
            if (error) throw error;
            if (results.length <= 0 && password == password2) {     // DB에 같은 이름의 회원아이디가 없고, 비밀번호가 올바르게 입력된 경우 
                db.query('INSERT INTO user (email, password, displayName, created) VALUES(?,?,?,NOW())', [email, password,displayName], function (error, data) {
                    if (error) throw error;
                    response.send(`<script type="text/javascript">alert("회원가입이 완료되었습니다!");
                    document.location.href="/";</script>`);
                });
            } else if (password != password2) {                     // 비밀번호가 올바르게 입력되지 않은 경우
                response.send(`<script type="text/javascript">alert("입력된 비밀번호가 서로 다릅니다."); 
                document.location.href="/register";</script>`);    
            }
            else {                                                  // DB에 같은 이름의 회원아이디가 있는 경우
                response.send(`<script type="text/javascript">alert("이미 존재하는 이메일 입니다."); 
                document.location.href="/register";</script>`);    
            }            
        });

    } else {        // 입력되지 않은 정보가 있는 경우
        response.send(`<script type="text/javascript">alert("입력되지 않은 정보가 있습니다."); 
        document.location.href="/register";</script>`);
    }
}

template.js

반복되는 웹페이지의 기본적인 구성을 사용하기 쉽게 모듈화 한 파일이다.

더보기
module.exports = {
    HTML:function(title,list,body,control,authStatusUI ){
        return`
        <!doctype html>
        <html>
        <head>
            <title>WEB1 - ${title}</title>
            <meta charset="utf-8">
        </head>
        <body>
            <h1><a href="/">WEB</a></h1>
            ${authStatusUI}
            ${list}
            ${control}
            ${body}
        </body>
        </html>
        `;
    },List:function(topics){
        var list = '<ul>';
        var i = 0;
        while(i < topics.length){
            list = list + `<li><a href="/page/${topics[i].id}">${topics[i].title}</a></li>`;
            i = i + 1;
        }
        list = list+'</ul>';
        return list;
    }
}

db.js

db정보를 담은 파일로 내용은 개인에 맞게 입력하면 된다.

저는 mariaDB를 사용했습니다.

더보기
var mysql = require('mysql');
var db = mysql.createConnection({
    host     : '', //로컬일경우 localhost 또는 127.0.0.1
    user     : '', //db 아이디
    password : '',  //db 비밀번호
    database : '' //db 이름

});
db.connect();
module.exports = db;

auth.js

사용자의 로그인확인과 표시를 모듈화한 파일이다.

더보기
module.exports = {
    IsOwner:function(request,response){
        if(request.user){
            return true;
        }
        else{
            return false;
        }
    },
    StatusUI:function(request,response){
        var authStatusUI = '<a href="/login">login</a> | <a href="/register">Register</a>';
        if(this.IsOwner(request,response)){
            authStatusUI = `${request.user.displayName} | <a href="/logout_process">logout</a> | <a href="/register">Register</a>`
        }
        return authStatusUI;
    }
}

 

 

웹페이지 기능

 

주요 기능

  • 로그인
  • 회원가입
  • 글 생성, 삭제, 업데이트
  • 로그인하지 않은 상태에서는 생성, 삭제, 수정 불가
  • 로그인 세션 저장

 

메인 페이지

 

로그인, 회원가입, 글 생성 버튼과 게시판 목록 출력

 

로그인 페이지

 

회원가입 페이지

email 중복체크, password 확인

글 생성, 수정, 삭제

 

생성 전
수정
수정 후
삭제

 

혹시 제 코드를 사용하실 분들을 위한 DB테이블 구성입니다.

 

topic

user

 

 

nodejs를 접해보고 javascript를 사용해 보는 좋은 경험이 되었고

웹을 만들면서 post get 전송방식, sql 문법등 개념을 다시 잡을 수 있었다.

728x90