Nodejs

 

 

강의 목록  :  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

 

 

 

 

[AWS] RDS 사용하기

 

AWS RDS 외부 접속 허용

 

[AWS/RDS] 02. 파라미터를 통한 한글 설정

 

 

 

 

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)

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

지구상의 어떤 나라도 자국의 나쁜 정부보다 다른 나라의 좋은 정부를 원하지 않는다. -간디

댓글 ( 4)

댓글 남기기

작성