
강의 목록 : https://www.youtube.com/playlist?list=PLSK4WsJ8JS4cQ-niGNum4bkK_THHOizTs
소스 : https://github.com/woorim960/login-lecture
https://github.com/braverokmc79/login-lecture
0. 로그인 & 회원가입 | 오리엔테이션 | 백엔드의 모든 것
1.개발환경 세팅
2.express로 서버 띄워보기
3.http로 서버 띄워보기
4.로그인 화면 만들기 | 리얼 하드코딩으로
5.로그인 뷰(view) 최적화 | MVC의 V(view) 분리하기
6.라우팅 분리
7.MVC의 C(controller) 컨트롤러 분리하기
8.app.listen() 모듈화 | 최적화에는 끝이 없다!?
9.package.json | package-lock.json | node_modules | npm start
10.깃과 깃허브에 노드 프로젝트 업로드 | 주의할 점
11.폴더 구조 최적화
12.프런트를 위한 JS 만들기 | public 폴더 연결
13.nodemon으로 서버 띄우기 | 개발 생산성 높이기
14.DOM으로 HTML 객체 제어하기 | 프런트 기능 구현
15.fetch | 프런트에서 서버로 데이터 보내기
16.로그인 API 만들기 in 서버 | 프런트의 요청데이터 파싱 | body-parser
17.로그인 인증 기능 만들기 in 서버 | 유저 데이터 만들기
18.서버의 응답데이터 처리 in 프런트
19.MVC의 모델(M) 만들기 | 객체지향프로그래밍(OOP) | UserStorage 클래스
20.User 모델 만들기 | 객체지향 프로그래밍 | 인스턴스화
21.로그인 화면 꾸미기 | 오픈소스 사용해보기 | 코드펜(codepen
22.회원가입 화면 만들기&꾸미기 | 오픈소스 사용해보기 | 코드펜(codepen)
23.회원가입 요청 구현 in 프런트 | fetch | ajax
24.회원가입 라우팅&기능구현 in 서버 | 깃 버전 관리 | tag
25.데이터 파일로 관리하기 | fs(파일시스템) | json
26.파일 DB로 로그인 구현 | promise와 async await 으로 비동기 최적화
27.파일 DB로 회원가입 구현 | promise와 async await 으로 비동기 최적화
28.서버 API 테스트 도구 | Postman | curl
29.AWS RDS 대여 | 과금 안되도록 주의하기 | 클라우드 | MySQL
30.AWS RDS 한글 설정 | 파라미터 옵션 | 클라우드 | MySQL
31.MySQL workbench | AWS RDS와 연동
32.AWS RDS로 DB 구축하기 | 로그인 구현 | MySQL
33.AWS RDS로 회원가입 구현 | 웹서버와 WAS | MySQL
34.환경 변수 관리 | 보안 향상 | dotenv
35.로그 관리 | morgan (1/4)
36.로그 관리 | winston (2/4)
37.로그 관리 | winston (3/4)
38.로그 관리 | 프로젝트 적용 | winston & morgan (4/4)
39.최적화 & HTTP 상태코드 (1/2)
40.최적화 & HTTP 상태코드 (2/2)
21.로그인 화면 꾸미기 | 오픈소스 사용해보기 | 코드펜(codepen)
코드펜
https://codepen.io/colorlib/pen/rxddKy
src/public/css/home/login.css
/* Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) */
@import url(https://fonts.googleapis.com/css?family=Roboto:300);
.login-page {
width: 360px;
padding: 8% 0 0;
margin: auto;
}
.form {
position: relative;
z-index: 1;
background: #FFFFFF;
max-width: 360px;
margin: 0 auto 100px;
padding: 45px;
text-align: center;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
.form input {
font-family: "Roboto", sans-serif;
outline: 0;
background: #f2f2f2;
width: 100%;
border: 0;
margin: 0 0 15px;
padding: 15px;
box-sizing: border-box;
font-size: 14px;
}
.form button {
font-family: "Roboto", sans-serif;
text-transform: uppercase;
outline: 0;
background: #4CAF50;
width: 100%;
border: 0;
padding: 15px;
color: #FFFFFF;
font-size: 14px;
-webkit-transition: all 0.3 ease;
transition: all 0.3 ease;
cursor: pointer;
}
.form button:hover,.form button:active,.form button:focus {
background: #43A047;
}
.form .message {
margin: 15px 0 0;
color: #b3b3b3;
font-size: 12px;
}
.form .message a {
color: #4CAF50;
text-decoration: none;
}
.form .register-form {
display: none;
}
.container {
position: relative;
z-index: 1;
max-width: 300px;
margin: 0 auto;
}
.container:before, .container:after {
content: "";
display: block;
clear: both;
}
.container .info {
margin: 50px auto;
text-align: center;
}
.container .info h1 {
margin: 0 0 15px;
padding: 0;
font-size: 36px;
font-weight: 300;
color: #1a1a1a;
}
.container .info span {
color: #4d4d4d;
font-size: 12px;
}
.container .info span a {
color: #000000;
text-decoration: none;
}
.container .info span .fa {
color: #EF3B3A;
}
body {
background: #76b852; /* fallback for old browsers */
background: rgb(141,194,111);
background: linear-gradient(90deg, rgba(141,194,111,1) 0%, rgba(118,184,82,1) 50%);
font-family: "Roboto", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
src/views/home/login.ejs
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/css/home/login.css">
<script src="/js/home/login.js" defer></script>
</head>
<body>
<div class="login-page">
<div class="form">
<form class="login-form">
<input type="text" id="id" name="id" placeholder="아이디" />
<input type="password" id="psword" name="psword" placeholder="비밀번호" />
<button>login</button>
<p class="message">Not registered? <a href="/register">Create an account</a></p>
</form>
</div>
</div>
</body>
</html>
<!--
Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy)
-->
22.회원가입 화면 만들기&꾸미기 |
오픈소스 사용해보기 | 코드펜(codepen)
src/routes/index.js
router.get("/register", ctrl.output.register);
src/routes/home/home.ctrl.js
register: (req, res) => {
res.render("home/register");
}
src/views/home/register.ejs
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/css/home/login.css">
<script src="/js/home/login.js" defer></script>
</head>
<body>
<div class="login-page">
<div class="form">
<form class="register-form">
<input type="text" id="id" name="id" placeholder="아이디" />
<input type="text" id="name" name="name" placeholder="이름" />
<input type="password" id="psword" name="psword" placeholder="비밀번호" />
<input type="password" id="confirm-psword" name="confirm-psword" placeholder="비밀번호 확인" />
<button>SIGN UP</button>
<p class="message">Already registered? <a href="/login">login</a></p>
</form>
</div>
</div>
</body>
</html>
<!--
Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy)
-->
23.회원가입 요청 구현 in 프런트 | fetch | ajax
src/views/hom/register.ejs
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/css/home/login.css">
<script src="/js/home/register.js" defer></script>
</head>
<body>
<div class="login-page">
<h1>회원 가입</h1>
<div class="form">
<form class="register-form">
<input type="text" id="id" name="id" placeholder="아이디" />
<input type="text" id="name" name="name" placeholder="이름" />
<input type="password" id="psword" name="psword" placeholder="비밀번호" />
<input type="password" id="confirm-psword" name="confirm-psword" placeholder="비밀번호 확인" />
<button type="button">SIGN UP</button>
<p class="message">Already registered? <a href="/login">login</a></p>
</form>
</div>
</div>
</body>
</html>
<!--
Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy)
-->
src/public/js/hom/register.js
"use strict"
const id = document.querySelector("#id");
const name = document.querySelector("#name");
const psword = document.querySelector("#psword");
const confirmPsword = document.querySelector("#confirm-psword");
const registerBtn = document.querySelector("button");
registerBtn.addEventListener("click", register);
function register() {
const req = {
id: id.value,
name: name.value,
psword: psword.value,
confirmPsword: confirmPsword.value
}
fetch("/register", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(req)
}).then(res => res.json())
.then(res => {
if (res.success) {
location.href = "/login";
} else {
alert(res.msg);
}
}).catch(err => console.log(new Error("회원가입 중 에러 발생")));
}
24.회원가입 라우팅&기능구현 in 서버 | 깃 버전 관리 | tag
1) 컨트롤
src/routes/index.js
router.post("/register", ctrl.process.register);
src/routes/home/home.ctrl.js
~
const process = {
login: (req, res) => {
const user = new User(req.body);
const response = user.login();
return res.json(response);
},
register: (req, res) => {
const user = new User(req.body);
const response = user.register();
return res.json(response);
}
}
~
2) 모델
src/models/User.js
~
register() {
const client = this.body;
return UserStorage.save(client);
}
~
src/models/UserStorage.js
~
static save(userInfo) {
try {
const users = this.#users;
users.id.push(userInfo.id);
users.name.push(userInfo.name);
users.psword.push(userInfo.psword);
console.log("회원가입 :", users);
return { success: true, msg: "회원 가입을 축합니다." }
} catch (err) {
console.error("에러 : ", err);
return { success: false, msg: "회원 가입에 실패했습니다." }
}
}
~
깃 버전 관리 | tag
1)태그부여
$ git tag v0.1.0-notDB
2) 태그확인
$ git tag
3)깃 로그 한줄로 보기
$ git log --oneline
4)깃에 저장
$ git push origin v0.1.0-notDB
5)전체 저장
$ git add --all
6) git 태그 삭제
$ git push origin :v0.1.0-notDB
7) 로컬에 저장된 태그 삭제
$ git tag -d v0.1.0-notDB
25.데이터 파일로 관리하기 | fs(파일시스템) | json
26.파일 DB로 로그인 구현 | promise와 async await 으로 비동기 최적화

src/databases/users.json
{
"id": [
"test1",
"test2",
"test3"
],
"psword": [
"1111",
"1111",
"1111"
],
"name": [
"홍길동",
"이순신",
"강감찬"
]
}
컨트롤
src/routes/home/home.ctrl.js
"use strict"
const User = require("../../models/User");
~
const process = {
login: async (req, res) => {
const user = new User(req.body);
const response = await user.login();
console.log("user.login() 값은:", response);
return res.json(response);
},
~
}
module.exports = {
output,
process
}
모델
src/models/User.js
"use strict"
const UserStorage = require("./UserStorage");
class User {
constructor(body) {
this.body = body;
}
async login() {
const client = this.body;
const { id, psword } = await UserStorage.getUserInfo(client.id);
if (id) {
if (id === client.id && psword === client.psword) {
return { success: true };
}
return { success: false, msg: "비밀번호가 틀렸습니다." };
}
return { success: false, msg: "존재하지 않는 아이디 입니다." };
}
register() {
const client = this.body;
return UserStorage.save(client);
}
}
module.exports = User;
fs.readFileSync 으로 동기식 처리가 가능하나 promises 를 적용하여 asyc ~ await 동기식 처리를 함
src/models/UserStorage.js
"use strict"
const fs = require("fs").promises;
class UserStorage {
static getUsers(...fields) {
~
}
static async getUserInfo(id) {
console.log(" getUserInfo(id) : ", i"use strict"
const fs = require("fs").promises;
class UserStorage {
static #getUserInfo(data, id) {
const users = JSON.parse(data);
const idx = users.id.indexOf(id);
const userKeys = Object.keys(users); //=>[id,psword, name] => 키값만 배열로 변환처리
console.log(" userKeys(id) : ", userKeys);
const userInfo = userKeys.reduce((newUser, info) => {
console.log(" info : ", info);
newUser[info] = users[info][idx];
return newUser;
}, {});
return userInfo;
}
static getUsers(...fields) {
// console.log("fields :", fields); //[id,psword, name]
// const users = this.#users;
// const newUsers = fields.reduce((newUsers, field) => {
// if (users.hasOwnProperty(field)) {
// console.log(" users.hasOwnProperty(field) : ", users[field]);
// newUsers[field] = users[field];
// }
// return newUsers
// }, {});
// return newUsers;
}
static getUserInfo(id) {
return fs.readFile("./src/databases/users.json").
then((data) => {
return this.#getUserInfo(data, id);
})
.catch(console.error);
}
static save(userInfo) {
// try {
// const users = this.#users;
// users.id.push(userInfo.id);
// users.name.push(userInfo.name);
// users.psword.push(userInfo.psword);
// console.log("회원가입 :", users);
// return { success: true, msg: "회원 가입을 축합니다." }
// } catch (err) {
// console.error("에러 : ", err);
// return { success: false, msg: "회원 가입에 실패했습니다." }
// }
}
}
module.exports = UserStorage;d);
return await fs.readFile("./src/databases/users.json").
then((data) => {
const users = JSON.parse(data);
const idx = users.id.indexOf(id);
const userKeys = Object.keys(users); //=>[id,psword, name] => 키값만 배열로 변환처리
console.log(" userKeys(id) : ", userKeys);
const userInfo = userKeys.reduce((newUser, info) => {
console.log(" info : ", info);
newUser[info] = users[info][idx];
return newUser;
}, {});
return userInfo;
})
.catch(console.error);
}
static save(userInfo) {
~
}
}
module.exports = UserStorage;
뷰
src/views/home/login.ejs
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/css/home/login.css">
<script src="/js/home/login.js" defer></script>
</head>
<body>
<div class="login-page">
<div class="form">
<form class="login-form">
<input type="text" id="id" name="id" placeholder="아이디" />
<input type="password" id="psword" name="psword" placeholder="비밀번호" />
<button type="button">login</button>
<p class="message">Not registered? <a href="/register">Create an account</a></p>
</form>
</div>
</div>
</body>
</html>
<!--
Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy)
-->
src/public/js/home/login.js
"use strict"
const id = document.querySelector("#id");
const psword = document.querySelector("#psword");
const loginBtn = document.querySelector("button");
loginBtn.addEventListener("click", login);
function login() {
const req = {
id: id.value,
psword: psword.value
}
fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(req)
}).then(res => res.json())
.then(res => {
console.log(res);
if (res.success) {
location.href = "/";
} else {
alert(res.msg);
}
}).catch(err => console.log(new Error("로그인 중 에러 발생")));
}
27.파일 DB로 회원가입 구현
| promise와 async await 으로 비동기 최적화
컨트롤
src/routes/home/home.ctrl.js
~
register: async (req, res) => {
const user = new User(req.body);
const response = await user.register();
return res.json(response);
}
~
모델
src/models/User.js
~
async register() {
const client = this.body;
return await UserStorage.save(client);
}
~
src/models/UserStorage.js
~
static #getUsers(data, isAll, fields) {
const users = JSON.parse(data);
if (isAll) return users;
const newUsers = fields.reduce((newUsers, field) => {
if (users.hasOwnProperty(field)) {
console.log(" users.hasOwnProperty(field) : ", users[field]);
newUsers[field] = users[field];
}
return newUsers
}, {});
return newUsers;
}
static getUsers(isAll, ...fields) {
console.log("fields :", fields); //[id,psword, name]
return fs.readFile("./src/databases/users.json").then((data) => {
return this.#getUsers(data, isAll, fields);
}).catch(console.error);
}
static async save(userInfo) {
const users = await this.getUsers(true);
console.log(" save :", users);
if (users.id.includes(userInfo.id)) {
console.log(" 아이디 존재");
return { success: false, msg: "이미 존재하는 아이디입니다." };
}
users.id.includes(userInfo.id)
users.id.push(userInfo.id);
users.name.push(userInfo.name);
users.psword.push(userInfo.psword);
fs.writeFile("./src/databases/users.json", JSON.stringify(users));
return { success: true }
}
~
28.서버 API 테스트 도구 | Postman | curl
$ curl http://localhost:3000/login -X POST -d '{"id":"test1", "psword":"1111"}' -H "Content-Type: application/json"
{"success":true}
29.AWS RDS 대여 | 과금 안되도록 주의하기 | 클라우드 | MySQL

30.AWS RDS 한글 설정 | 파라미터 옵션 | 클라우드 | MySQL
31.MySQL workbench | AWS RDS와 연동
src/databases/schema.sql
drop table users;
create table users(
id varchar(30) not null,
name varchar(30) not null,
psword varchar(30) not null,
in_date datetime default current_timestamp,
primary key (id)
);
show tables;
desc users;
select * from users;
insert into users(id, name, psword)
values("test1" , "홍길동", "1111"),
("test2" , "이순신", "1111"),
("test3" , "강감찬", "1111"),
("test4" , "김민종", "1111"),
("test5" , "최수정", "1111");
32.AWS RDS로 DB 구축하기 | 로그인 구현 | MySQL
mysql 설치
$ npm install mysql
src/config/db.js
const mysql = require("mysql");
const db = mysql.createConnection({
host: "호스트주소(amazonaws.com)",
user: "admin",
password: "비번",
database: "login_lecture"
});
db.connect();
module.exports = db;
모델
src/models/User.js
~
async login() {
const client = this.body;
const { id, psword } = await UserStorage.getUserInfo(client.id);
if (id) {
if (id === client.id && psword === client.psword) {
return { success: true };
}
return { success: false, msg: "비밀번호가 틀렸습니다." };
}
return { success: false, msg: "존재하지 않는 아이디 입니다." };
}
~
src/models/UserStorage.js
~
static async getUserInfo(id) {
return new Promise((resolove, rejects) => {
db.query("SELECT * FROM users WHERE id=?", [id], function (err, data) {
if (err) rejects(err);
resolove(data[0]);
})
});
}
~
src/routes/home/home.ctrl.js
~
login: async (req, res) => {
const user = new User(req.body);
const response = await user.login();
return res.json(response);
},
~
33.AWS RDS로 회원가입 구현 | 웹서버와 WAS | MySQL
모델
src/models/User.js
"use strict"
const UserStorage = require("./UserStorage");
class User {
constructor(body) {
this.body = body;
}
async login() {
try {
const client = this.body;
const { id, psword } = await UserStorage.getUserInfo(client.id);
console.log(" id ", id);
if (id) {
if (id === client.id && psword === client.psword) {
return { success: true };
}
return { success: false, msg: "비밀번호가 틀렸습니다." };
}
return { success: false, msg: "존재하지 않는 아이디 입니다." };
} catch (err) {
console.log(" 에러 :", err);
return { success: false, msg: err.toString() }
}
}
async register() {
try {
const client = this.body;
return await UserStorage.save(client);
} catch (err) {
return { success: false, msg: err.toString() }
}
}
}
module.exports = User;
src/models/UserStorage.js
"use strict"
const fs = require("fs").promises;
const { rejects } = require("assert");
const db = require("../config/db");
class UserStorage {
/*
static #getUserInfo(data, id) {
const users = JSON.parse(data);
const idx = users.id.indexOf(id);
const userKeys = Object.keys(users); //=>[id,psword, name] => 키값만 배열로 변환처리
console.log(" userKeys(id) : ", userKeys);
const userInfo = userKeys.reduce((newUser, info) => {
console.log(" info : ", info);
newUser[info] = users[info][idx];
return newUser;
}, {});
return userInfo;
}
static #getUsers(data, isAll, fields) {
const users = JSON.parse(data);
if (isAll) return users;
const newUsers = fields.reduce((newUsers, field) => {
if (users.hasOwnProperty(field)) {
console.log(" users.hasOwnProperty(field) : ", users[field]);
newUsers[field] = users[field];
}
return newUsers
}, {});
return newUsers;
}
static getUsers(isAll, ...fields) {
console.log("fields :", fields); //[id,psword, name]
return fs.readFile("./src/databases/users.json").then((data) => {
return this.#getUsers(data, isAll, fields);
}).catch(console.error);
}
*/
static async getUserInfo(id) {
return new Promise((resolove, rejects) => {
const query = "SELECT * FROM users WHERE id= ?";
db.query(query, [id], function (err, data) {
if (err) rejects(err);
resolove(data.length === 0 ? false : data[0]);
})
});
}
static async save({ id, name, psword }) {
return new Promise((resolove, rejects) => {
const query = "INSERT INTO users(id, name, psword) VALUES(?, ?,? ) ;";
db.query(query, [id, name, psword], function (err) {
if (err) rejects(`${err}`);
resolove({ success: true });
})
});
}
}
module.exports = UserStorage;
34.환경 변수 관리 | 보안 향상 | dotenv
설치
$ npm install dotenv
app.js
//모듈
const express = require("express");
const bodyParser = require("body-parser");
const dotenv = require("dotenv");
dotenv.config();
~
.env
PORT=3000 DB_HOST="호스트주소" DB_USER="유저명" DB_PASSWORD="패스워드" DB_DATABASE="login_lecture"
src/config/db.js
const mysql = require("mysql");
const db = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE
});
db.connect();
module.exports = db;
35.로그 관리 | morgan (1/4)
https://www.npmjs.com/package/morgan
설치
$ npm i morgan $ npm i rotating-file-stream
src/conifg/log.js
const rfs = require('rotating-file-stream') // version 2.x
const appRoot = require("app-root-path");
// create a rotating write stream
const accessLogStream = rfs.createStream(`${appRoot}/log/access.log`, {
interval: '1d', // rotate daily
})
module.exports = accessLogStream;
app.js
const morgan = require('morgan')
const accessLogStream = require("./src/config/log");
app.use(morgan('dev'));
app.use(morgan('common', { stream: accessLogStream }));
//모듈
const express = require("express");
const bodyParser = require("body-parser");
const dotenv = require("dotenv");
const morgan = require('morgan')
const app = express();
const accessLogStream = require("./src/config/log");
dotenv.config();
//라우터
const home = require("./src/routes/home");
// setup the logger
app.use(morgan('combined', { stream: accessLogStream }))
//앱 셋팅
app.set("views", "./src/views");
app.set("view engine", "ejs");
app.use(express.static(`${__dirname}/src/public`));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }))
app.use(morgan('dev'));
app.use(morgan('common', { stream: accessLogStream }));
app.use("/", home); //use ->미들웨어를 등록해 주는 메소드.
module.exports = app;
36.로그 관리 | winston (2/4)
37.로그 관리 | winston (3/4)
38.로그 관리 | 프로젝트 적용 | winston & morgan (4/4)
깃 허브 문서 : https://github.com/winstonjs/winston
설치 :
$ npm install winston $ npm install winston-daily-rotate-file
.env 개발시 dev 운영서버 반영시 production
NODE_ENV ="dev"
winston.log.js 파일과 logger.js 파일을 저장한다.
1) src/config/winston.log.js
const winston = require("winston");
const winstonDaily = require("winston-daily-rotate-file");
const { combine, timestamp, printf, colorize, label } = winston.format;
const logDir = "logs"; // logs 디렉토리 하위에 로그 파일 저장
const logFormat = printf((info) => {
return `${info.timestamp} ${info.level}: ${info.message}`;
});
const productionWinstonDail = [
new winstonDaily({
level: "warn",
datePattern: "YYYY-MM-DD",
dirname: logDir + "/warn",
filename: `%DATE%.warn.log`, // file 이름 날짜로 저장
maxFiles: 30, // 30일치 로그 파일 저장
zippedArchive: true,
}),
// error 레벨 로그를 저장할 파일 설정
new winstonDaily({
level: "error",
datePattern: "YYYY-MM-DD",
dirname: logDir + "/error", // error.log 파일은 /logs/error 하위에 저장
filename: `%DATE%.error.log`,
maxFiles: 30,
zippedArchive: true,
})
]
const devWinstonDail = [...productionWinstonDail];
devWinstonDail.push( // info 레벨 로그를 저장할 파일 설정
new winstonDaily({
level: "info",
datePattern: "YYYY-MM-DD",
dirname: logDir + "/info",
filename: `%DATE%.info.log`, // file 이름 날짜로 저장
maxFiles: 30, // 30일치 로그 파일 저장
zippedArchive: true,
}));
/*
* Log Level
* error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6
*/
const winstonLog = winston.createLogger({
format: combine(
timestamp({
format: "YYYY-MM-DD HH:mm:ss",
}),
logFormat
),
transports: process.env.NODE_ENV !== "production" ? devWinstonDail : productionWinstonDail
});
winstonLog.stream = {
// morgan wiston 설정
write: (message) => {
logger.info(message);
},
};
// Production 환경이 아닌 경우(dev 등) 배포 환경에서는
// 최대한 자원을 안잡아 먹는 로그를 출력해야함
if (process.env.NODE_ENV !== "production") {
winstonLog.add(
// new winston.transports.Console({
// format: combine(
// colorize({ all: true }), // console 에 출력할 로그 컬러 설정 적용함
// logFormat // log format 적용
// ),
// });
new winston.transports.Console({
name: 'debug-console',
colorize: true,
level: "debug",
format: combine(
label({ label: '백엔드 맛보기' }),
colorize(),
timestamp({
format: "YYYY-MM-DD HH:mm:ss"
}),
printf(
info => `${info.timestamp} | [${info.label}] | (${info.level}) : ${info.message} `
)
),
showlevel: true,
json: false,
})
);
}
module.exports = winstonLog;
2) src/config/logger.js
const winstonLog = require("./winston.log");
const parseurl = require('parseurl');
//로그 처리 및 결과값 반환처리
const logger = (response, req, res) => {
const url = {
method: req.method,
path: parseurl(req).pathname,
status: (response !== null ? response.err : false) ? 400 : 200,
};
if (response === null) {
winstonLog.info(`${url.method} | ${url.path} 화면 | Request Query : ${JSON.stringify(req.query)} `);
return;
}
let result = false;
if (response.err) {
winstonLog.error(
`${url.method} ${url.path} ${url.status} Response: ${response.success} | Request Body : ${JSON.stringify(req.body)} | msg | ${response.err}`
);
result = true;
} else {
if (url.method === "GET") {
winstonLog.info(`${url.method} | ${url.path} 화면 | Request Query : ${JSON.stringify(req.query)} | msg | ${response.msg || ""} `);
} else {
winstonLog.info(
`${url.method} ${url.path} ${url.status} Response: ${response.success} | Request Body : ${JSON.stringify(req.body)} | msg | ${response.msg || ""}`
);
}
result = false;
}
if (result) return res.status(400).json({ msg: response.err.toString() });
return res.status(200).json(response);
};
module.exports = logger;
사용방법
1) 모델에서 에러반환 처리 또는 try~catch 처리로 에러 또는 메시지 반환 처리를 한다.
src/models/UserStorage.js
static async getUserInfo(id) {
return new Promise((resolove, rejects) => {
const query = "SELECT * FROM users WHERE id= ?";
db.query(query, [id], function (err, data) {
if (err) return rejects(err);
resolove(data.length === 0 ? false : data[0]);
})
});
}
src/models/User.js
async login() {
try {
const client = this.body;
const { id, psword } = await UserStorage.getUserInfo(client.id);
if (id) {
if (id === client.id && psword === client.psword) {
return { success: true };
}
return { success: false, msg: "비밀번호가 틀렸습니다." };
}
return { success: false, msg: "존재하지 않는 아이디 입니다." };
} catch (err) {
return { success: false, err }
}
}
2) 컨트롤에서 get 방식 post 방식에 따라 맞게 logger 함수를 사용한다.
"use strict"
const User = require("../../models/User");
const logger = require("../../config/logger");
const output = {
home: (req, res) => {
logger(null, req, res);
res.render("home/index");
},
login: (req, res) => {
logger(null, req, res);
res.render("home/login");
},
register: (req, res) => {
logger(null, req, res);
res.render("home/register");
}
}
const process = {
login: async (req, res) => {
const user = new User(req.body);
const response = await user.login();
logger(response, req, res);
},
register: async (req, res) => {
const user = new User(req.body);
const response = await user.register();
logger(response, req, res);
}
}
module.exports = {
output,
process
}
39.최적화 & HTTP 상태코드 (1/2)
40.최적화 & HTTP 상태코드 (2/2)














댓글 ( 4)
댓글 남기기