강의 목록 : 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)
댓글 남기기