2022 12 25 WIL
우리밋_woorimIT Node.js 맛보기
로그인 & 회원가입 기능을 고퀄리티로 구축해보자.
사이트 : https://www.youtube.com/@_woorimit1343/featured
폴더 : kimminsoo -> woorimit -> node_base
_________________________________________________________________________________________________________
__________________________________________________
1-1 로그인 & 회원가입 서비스 구축 오리엔테이션
우리가 이번 코스를 통해서 배우게 될 것들.
익스프레스
모듈화. MVC 패턴. 모델, 컨트롤러, 뷰.
package.json 컨트롤 및 조작
클라이언트의 요청 로그를 콘솔에 출력. 모듈 사용할거야.
DB 연동하기. Mysql 을 aws - RDS 로 사용.
GUI - 워크밴치 사용
TDD 개념 활용해보기.
넓게, 얕게!
__________________________________________________
1-2 개발환경 세팅
1.
Visual Studio Code 에디터 사용
2.
node.js LTS 버전 사용
3.
npm 사용
__________________________________________________
1-3 express 로 서버 띄워보기
폴더 : kimminsoo -> woorimit -> node_base -> login-lecture
1.
app.js 만들기.
=> 참고.
터미널에 < pwd > 라고 입력하면 현재 디렉토리의 절대주소가 나와.
Npm init
Npm I express
__________________________________________________
1-4 http 로 서버 띄워보기
폴더 : kimminsoo -> woorimit -> node_base -> login-lecture
— express 를 사용하지 않고 서버를 구축해보자.
=> http 를 사용해서 맛보기만. 알고는 있어야 하잖아?
**
전체 주석처리
=> 블록 씌운 후 cmd + /
——
const http = require("http"); // 내장 모듈
const app = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8"});
// 내가 보낸 게 text 중에 html 이고 charset 은 utf-8 이니 이대로 해석해달라, 라고 브라우저에 전달.
console.log(req.url); // 루트 경로 이하에 있는 경로들을 우리가 파싱해 올 수 있다.
if (req.url === "/") {
res.end("여기는 루트 url 입니다."); // http 에서는 send 라는 메소드가 없어.
} else if (req.url === "/login") {
res.end('여기는 로그인 화면입니다.');
}
});
app.listen(3001, () => {
console.log("http로 가동된 서버임!")
})
——
— git init —
=> 우선 원격 저장소 login-lecture 생성.
1.
Git init
2.
nano README.md
=> 작성 후 ctrl + X, 그리고 yes의 Y 입력 후 엔터.
3.
nano .gitignore
=> 작성 후 control + X, 그리고 y 후 엔터
4.
Git add .
5.
git remote add origin git@github.com:msdou46/login-lecture.git
6.
git push -u origin master
7.
cd ..
=> 한 단계 윗 레벨의 폴더로.
8.
git clone git@github.com:msdou46/login-lecture.git login-lecture-2
=> clone 연습해보기.
9.
git reset HEAD .
=> 커밋 잘못 했을 때 함 해봐. 리셋.
10.
Sudo npm I nodemon -g
=> 글로벌에서 노드몬 사용.
=> 그다음,
현재 < kimminsoo@gimminsuui-MacBookPro app > 경로에서
< nodemon ./bin/www.js> 명령어 실행.
=> package.json 에서 “start” 값 바꿔주면 npm start 로 가능.
11.
git tag v0.1.0-notDB
=> 중요한 저장 내역은 태그로 관리해 주는 게 좋아.
git log --oneline << 확인해보기.
git push origin v0.1.0-notDB << 원격 저장소에 태그를 푸쉬.
— 파일 —
bin
=> 바이너리. 실행 파일들을 모아둔 곳.
— 참고 사이트 —
=> 로그인 화면 html 참고
— 클래스의 static 메소드 —
static 키워드는 클래스의 정적 메서드를 정의한다.
정적 메서드는 클래스의 인스턴스 없이 호출이 가능하며 클래스가 인스턴스화되면 호출할 수 없다.
정적 메서드는 종종 어플리케이션의 유틸리티 함수를 만드는데 사용된다.
동일한 클래스 내의 다른 정적 메서드 내에서 정적 메서드를 호출하는 경우 키워드 this를 사용할 수 있다.
참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes/static
— RDS mysql 한글 설정해주기 —
RDS -> 왼쪽 탭 “데이터베이스” -> DB 식별자 < express-database > 클릭해서 들어가기 -> 왼쪽 탭 “파라미터 그룹”
-> 우측 상단 < 파라미터 그룹 생성 > 오렌지 색 버튼 클릭 ->
파라미터 그룹생성.
파라미터 그룹 패밀리 - mysql8.0
유형 - DB Parameter Group
그룹 이름 - express-database
설명 - for utf8 setting
-> 생성된 파라미터 그룹 “ express-database “ 에 들어가기, 들어간 후 DB 옵션들을 변경해 보자.
-> “ char “ 라고 검색해서 나온 옵션값들의 “값” 을 모두 utf8 로 바꿔줄거야…
-> 우측 상단의 “ 파라미터 편집” 클릭. “값” 필드들을 uft8 로 바꿀 수 있는 것들은 모두 바꿔줘.
-> 아직 “변경 사항 저장” 누르지 말고, 검색창에 이번에는 “ colla” 라고 검색.
collation_connection, collation_server 이 두 개의 값을 “ utf8_general_ci “ 로 변경.
-> 변경 사항 저장
-> “데이터베이스” 탭으로 이동, 내가 생성한 인스턴스 “ express-database “ 클릭
-> 우측 상단 “수정” 버튼 클릭
-> “ 데이터베이스 옵션 “ 목록 중 “ DB 파라미터 그룹 “ 을 “express-database” 로 선택.
=> 이렇게 하면 DB의 기본 설정들이 해당 파라미터의 설정으로 변경될거야.
-> 그 다음 계속, 즉시 적용, 인스턴스 수정.
해당 Db 인스턴스의 상세 페이지에 들어가서 “구성” 탭을 선택해보면, 아래쪽에 현재 선택된 파라미터 그룹을 확인할 수 있어.
— 워크밴치에서 res mysql 연동하기 —
1.
워크밴치 실행
2.
Mysql connections 문구 옆의 + 버튼 클릭
3.
Connection name : express-database
=> 내 맘대로
Hostname : express-database.cop97vlzzz2l.ap-northeast-2.rds.amazonaws.com
=> DB 의 엔드포인트
port: 3306
Username : root
Password -> store in keychain : lololo46
— login-lecture 용 DB 만들기 —
1.
create database login_lecture
=> *****
sql문 실행 단축키 : command + enter
2.
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)
);
=> users 용 테이블 생성.
3.
show tables;
desc users;
=> 테이블 살펴보기.
— mysql 연동 전 자체 변수로 로그인, 회원가입 구현했을 때의 UserStorage.js --
——
"use strict"
const fs = require("fs").promises;
// fs.readFile 는 promise 를 반환하게 될 거야.
class UserStorage {
static #getUserInfo (data, id) {
const users = JSON.parse(data);
const idx = users.id.indexOf(id);
const userInfo = Object.keys(users).reduce((newUser, 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)) {
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 getUsers (isAll, ...fields) {
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);
if(users.id.includes(userInfo.id)) {
throw "이미 존재하는 아이디입니다.";
}
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 };
}
}
module.exports = UserStorage;
——
— 함수는 한 가지 기능만 수행하도록 구현해야 해—
Promise로 만들어주지 않으면 하나의 함수에서 DB를 조회하고, 로그인 정보를 검증하고,
클라이언트에 응답하지 해주는 ‘이도 저도 아닌 코드’ 가 만들어 짐.
클래스는 User 와 UserStorage 처럼 각자의 역할을 분명하게 구분시켜주는 것이 좋아.
UserStorage 에서는 DB를 CRUD 하는 역할만 수행하고, 해당 데이터를 가지고 검증 및 조작하는 것은
User 가 수행하도록 역할을 구분 짓는 거야.
해당 코드에 만족하지 말고, 보다 더 나은 리팩토링을 시도해보자.
— dotenv —
npm i dotenv
우리는 app 폴더 안에 .env 라는 파일을 만들어서 이 안에서 환경변수를 관리해 줄 거야.
*****
dotenv.config({path: 경로})
=> 이와 같은 식으로 .env의 파일 경로를 지정해줄 수도 있으며,
“env” 가 아닌 다른 파일명으로 사용할 수도 있어.
그러나 일반적으로 사용 되는 이름으로 하는 것이 의사소통에 있어서도 좋겠지.
— log 관리 —
=> Morgan, winston 이 둘에 대해서 알아볼 거야.
1.
morgan
=> npm I Morgan 으로 설치.
2.
——
const morgan = require("morgan");
const accessLogStream = fs.createWriteStream(
`${__dirname}/log/access.log`
, { flags: 'a' }
); // 해당 변수는 src/config/log.js 안에 저장해두고 모듈 익스포츠로 빼와.
// const accessLogStream = require("./src/config/log”) 이렇게.
app.use(morgan(":method :date[web]", { stream: accessLogStream})); // morgan 미들웨어 등록.
——
=> 미들웨어에 로그 형식을 등록할 수 있어.
관련 참고 자료는 https://www.npmjs.com/package/morgan
— src/config/log.js —
——
const fs = require("fs")
const appRoot = require("app-root-path")
const accessLogStream = fs.createWriteStream(
`${appRoot}/log/access.log`
, { flags: 'a' }
);
module.exports = accessLogStream
——
** 추가.
https://www.npmjs.com/package/morgan
해당 npm 공식 사이트에서 “ log file rotation “ 이라는 항목을 보면,
——
var express = require('express')
var morgan = require('morgan')
var path = require('path')
var rfs = require('rotating-file-stream') // version 2.x
var app = express()
// create a rotating write stream
var accessLogStream = rfs.createStream('access.log', {
interval: '1d', // rotate daily
path: path.join(__dirname, 'log')
})
// setup the logger
app.use(morgan('combined', { stream: accessLogStream }))
app.get('/', function (req, res) {
res.send('hello, world!')
})
——
=> 이런 부분이 있어.
rotating-file-stream 라는 모듈을 불러와서, rfs.createStream 으로 스트림을 열어주고,
interval: '1d', 라는 인터벌을 사용하면
하루에 한 개의 로그 파일만 생성하도록 만들 수 있나봐. 이거 좋은 거 같은데?
___________
그 다음으로, winston
1.
morgan 을 app.js 에서 설정해준 것과 달리, winston 은 바로 src > config 안에 logger.js 라는 파일을 만들어 줄거야.
Logger.js 에서 winston 에 대한 로깅 설정들을 작성해주고, app.js 에서는 그냥 로그를 남기기만 하기로.
우선 npm I winston 으로 npm 모듈을 다운로드.
—
const logger = require('./src/config/logger'); // winston 모듈을 불러온 js 파일.
logger.error("안녕 노드!");
—
=> 이런 식으로 로그를 남기고 싶다면 logger 를 불러와서 logger.info() 나 logger.error 이런 식으로 로그를 남기자.
——
< www.js >
"use strict";
const app = require("../app")
const logger = require("../src/config/logger");
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info(`${PORT} 포트에서 서버가 가동되었습니다.`)
});
——
=> 서버 가동 될 때에도 로그를 남기도록.
2.
Winston 을 사용해서 날짜별로 로그 파일을 남기는 법.
=> https://www.npmjs.com/package/winston-daily-rotate-file 참조.
Npm 모듈 중에 “ winston-daily-rotate-file “ 라는 게 있어.
이걸 사용해서 날짜별로 관리 가능.
옵션 란이 있는데, 여기 것들을 참조해서 만들어 보면 돼.
— 마지막으로, HTTP 상태 코드 붙여주면서 최적화 —
=> https://developer.mozilla.org/ko/docs/Web/HTTP/Status
=> http 상태코드 참고.
1.
우선 디렉토리 구성 정리.
bin
=> 노드 프로젝트가 실행되기 위한 실행 파일이 존재.
src
=> 작업한 source 코드들이 들어 있어.
이 안에서 MVC 패턴으로 코드를 작성.
model, controller, view
app.js
=> 메인 파일. 루트 파일.
.env
=> 환경 변수 관리