지난 한 달간 블로그에 글을 못 올렸는데 개발공부를 하면서 웹을 만들고 있었다.
웹서버를 만드는 과정을 하나하나 올려볼까 했는데 공부하는 중에는 아직 개념이 잡히지 않아 설명에 미흡한 점이 있을까 다 만들고 올리기로 했다.(생각해 보면 만드는 과정과 시행착오를 올리는 게 더 나을 것 같아 다음부턴 차근차근 올려야겠다 ㅠㅠ)
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 문법등 개념을 다시 잡을 수 있었다.