강의 목록 : https://www.youtube.com/playlist?list=PLSK4WsJ8JS4cQ-niGNum4bkK_THHOizTs
소스 : https://github.com/woorim960/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)
0.로그인 & 회원가입 | 오리엔테이션 | 백엔드의 모든 것
1.개발환경 세팅
1. Nodjs : https://nodejs.org/ko/
2. vscode : https://code.visualstudio.com/
3. 깃 : https://git-scm.com/
2.express로 서버 띄워보기
설치
$ npm install express -save
app.js
const express = require("express"); const app = express(); app.get("/", (req, res) => { res.send("여기는 루트 입니다."); }); app.get("/login", (req, res) => { res.send("여기는 로그인 화면입니다."); }) app.listen(3000, function () { console.log("서버 가동"); });
3.http로 서버 띄워보기
app.js
const http = require("http"); const app = http.createServer((req, res) => { console.log(req.url); res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" }) if (req.url === "/") { res.end("여기는 루트 입니다."); } else if (req.url === "/login") { res.end("여기는 로그인 화면 입니다."); } }); app.listen(3001, () => { console.log("http 로 가동된 서버입니다."); }) // const express = require("express"); // const app = express(); // app.get("/", (req, res) => { // res.send("여기는 루트 입니다."); // }); // app.get("/login", (req, res) => { // res.send("여기는 로그인 화면입니다."); // }) // app.listen(3000, function () { // console.log("서버 가동"); // });
4.로그인 화면 만들기 | 리얼 하드코딩으로
app.js
const express = require("express"); const app = express(); app.get("/", (req, res) => { res.send(` <!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> </head> <body> 여기는 루트 입니다. </body> </html> `); }); app.get("/login", (req, res) => { res.send(` <!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> </head> <body> <input type="text" placeholder="아이디" ><br> <input type="text" placeholder="비밀번호" ><br> <button>로그인</button> </body> </html> `); }) app.listen(3000, function () { console.log("서버 가동"); });
5.로그인 뷰(view) 최적화 | MVC의 V(view) 분리하기
$ npm install ejs -s
app.js
const express = require("express"); const app = express(); //앱 셋팅 app.set("views", "./views"); app.set("view engine", "ejs"); app.get("/", (req, res) => { res.render("home/index"); }); app.get("/login", (req, res) => { res.render("home/login"); }) app.listen(3000, function () { console.log("서버 가동"); });
views/home/index.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> </head> <body> 여기는 루트 입니다. </body> </html>
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> </head> <body> <input type="text" placeholder="아이디" ><br> <input type="text" placeholder="비밀번호" ><br> <button>로그인</button> </body> </html>
6.라우팅 분리
app.js
//모듈 const express = require("express"); const app = express(); const PORT = 3000; //라우터 const home = require("./routes/home"); //앱 셋팅 app.set("views", "./views"); app.set("view engine", "ejs"); app.use("/", home); //use ->미들웨어를 등록해 주는 메소드. app.listen(PORT, function () { console.log("서버 가동"); });
routes/home/index.js
"use strict"; const express = require("express"); const router = express.Router(); router.get("/", (req, res) => { res.render("home/index"); }); router.get("/login", (req, res) => { res.render("home/login"); }); module.exports = router;
7.MVC의 C(controller) 컨트롤러 분리하기
routes/home/index.js
"use strict"; const express = require("express"); const router = express.Router(); const ctrl = require("./home.ctrl"); router.get("/", ctrl.home); router.get("/login", ctrl.login); module.exports = router;
routes/hom/home.ctrl.js
"use strict" const home = (req, res) => { res.render("home/index"); }; const login = (req, res) => { res.render("home/login"); } module.exports = { home, login }
8.app.listen() 모듈화 | 최적화에는 끝이 없다!?
app.js
//모듈 const express = require("express"); const app = express(); //라우터 const home = require("./routes/home"); //앱 셋팅 app.set("views", "./views"); app.set("view engine", "ejs"); app.use("/", home); //use ->미들웨어를 등록해 주는 메소드. module.exports = app;
bin/www.js
"use strict"; const app = require("../app"); const PORT = 3000; app.listen(PORT, () => { console.log("서버 가동"); });
$node ./bin/www.js
9.package.json | package-lock.json | node_modules | npm start
1. 생성방법
npm init // 프로젝트명, 설명 등 작성할 내용이 있을 경우 npm init -y // 입력할 내용없이 package.json 생성 yarn init yarn init -y
2. package.json의 기본 정보
기본 정보란 package.json을 자동으로 생성할 때(npm init), -y를 명령어를 붙이지 않은 경우 입력하게 되는 것들을 나타낸다.
name, version, description, author, license 등을 입력할 수 있는데, 프로젝트에 대한 간략한 내용을 입력할 수 있다. 처음 생성할 때 입력하지 않은 경우에 추후에 package.json을 변경하여 입력할 수 있다.
3. 버전관리
의존성 모듈을 설치하게 되면 dependencies안에 해당 모듈의 버전과 이름이 추가된다.
{ "name": "tutorial", "version": "1.0.0", "description": "", "main": "index.js", "dependencies": { "react": "^17.0.2", // react 설치 결과 }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
1. 특정 버전인 경우
"react": "17.0.2"
2. Tilde Ranges(~)
"react": "~17.0.2"
Tilde Ranges(~)의 경우에는 마이너 버전이 명시되어 있으면 패치버전만 변경하며 버전을 적용한다.
예를 들어 ~17.0.2인 경우, 17.0.9까지 버전을 갱신한다. ~0일 경우는 0.0.0부터 0.9.9까지 버전을 갱신한다.
3. Caret Ranges(^)
"react": "^17.0.2"
Caret Ranges(^)의 경우에는 정식버전에서 마이너버전과 패치버전을 변경하며 버전을 적용한다.
예를 들어 ^17.0.2인 경우, 17.9.9까지 버전을 갱신하다. ^0일 경우는 0.0.0부터 0.0.9.까지 버전을 갱신한다.
4. 의존성 모듈 설치
package.json은 의존성을 모듈을 관리하는 하나의 파일이다. 즉, 모듈들이 차곡차곡 쌓일 수록 dependencies에는 다양한 내용들이 추가된다. 이 파일 하나로 프로젝트에 필요한 모듈들을 한번에 설치할 수 있다.
npm install
이 명령어를 통해 package.json - dependencies에 명시되어져 있는 모듈들의 버전에 따라 설치를 진행한
출처 : https://phsun102.tistory.com/28
10.깃과 깃허브에 노드 프로젝트 업로드 | 주의할 점
깃 이전 커밋 취소
git reset HEAD .
pull 오류시 강제 병합
git reset --hard && git pull
포트 충돌 킬
killall -9 node
https://github.com/braverokmc79/login-lecture
11.폴더 구조 최적화
12.프런트를 위한 JS 만들기 | public 폴더 연결
13.nodemon으로 서버 띄우기 | 개발 생산성 높이기
$ npm i nodemon -g
package.json
"scripts": { "start": "node ./bin/www.js", "dev": "nodemon ./bin/www.js", "test": "echo \"Error: no test specified\" && exit 1" },
$ npm run dev
14.DOM으로 HTML 객체 제어하기 | 프런트 기능 구현
async
async 속성을 사용하면 브라우저는 스크립트를 백그라운드에서 다운로드한다. (대충 비동기로 처리한다는 뜻)
async 스크립트를 사용하는 경우, HTML 파싱과 동시에 script를 다운받고 다운로드가 완료되는 즉시 해당 스크립트가 실행된다.
하지만 js 파일을 실행하는 동안에는 HTML 파싱이 멈춘다는 단점이 있다.
그렇기 때문에, 만약 스크립트 A가 실행되는 순간에 A에서 접근하는 DOM이 아직 렌더링되지 않았을 경우 오류가 발생할 수 있다.
따라서, async 속성은 DOM을 조작하지 않고 HTML 의존성이 없는 코드에만 사용하는 것이 바람직하다.
defer
defer 속성을 사용하면 async속성과 마찬가지로 브라우저는 스크립트를 백그라운드에서 다운로드한다.
마찬가지로 js 파일을 다운로드 하는 도중에도 HTML 파싱이 일어나므로 페이지 생성이 멈추는 현상이 발생하지 않는다.
따라서, defer는 async와는 달리 DOM을 조작하거나 HTML 의존성이 있을 때, 혹은 HTML을 모두 파싱한 후에 실행되어야 하는 코드인 경우에 사용할 수 있다.
출처 :https://guiyomi.tistory.com/101
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 } console.log(req); }
src/views/hoem/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> <script src="/js/home/login.js" defer></script> </head> <body> <input type="text" id="id" name="id" placeholder="아이디" ><br> <input type="password" id="psword" name="psword" placeholder="비밀번호" ><br> <button>로그인</button> </body> </html>
15.fetch | 프런트에서 서버로 데이터 보내기
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) }) }
16.로그인 API 만들기 in 서버
| 프런트의 요청데이터 파싱 | body-parser
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) }) }
src/routes/index.js
"use strict"; const express = require("express"); const router = express.Router(); const ctrl = require("./home.ctrl"); router.get("/", ctrl.output.home); router.get("/login", ctrl.output.login); router.post("/login", ctrl.process.login); module.exports = router;
src/routes/home/home.ctrl.js
"use strict" const output = { home: (req, res) => { res.render("home/index"); }, login: (req, res) => { res.render("home/login"); }, } const process = { login: (req, res) => { console.log(req.body); } } module.exports = { output, process }
17.로그인 인증 기능 만들기 in 서버 | 유저 데이터 만들기
src/routes/hom/home.ctrl.js
"use strict" const output = { home: (req, res) => { res.render("home/index"); }, login: (req, res) => { res.render("home/login"); }, } const users = { id: ["test1", "test2", " test3"], psword: ["1111", "1111", "1111"] } const process = { login: (req, res) => { const id = req.body.id; const psword = req.body.psword; if (users.id.includes(id)) { const idx = users.id.indexOf(id); if (users.psword[idx] === psword) { return res.json({ success: true }) } } return res.json({ success: false, msg: "로그인에 실패하였습니다." }) } } module.exports = { output, process }
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); async function login() { const req = { id: id.value, psword: psword.value } const result =await fetch("/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(req) }).then(res => res.json()) .then(res => res) console.log("result : ", result); }
18.서버의 응답데이터 처리 in 프런트
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 } const result = fetch("/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(req) }).then(res => res.json()) .then(res => { if (res.success) { location.href = "/"; } else { alert(res.msg); } }).catch(err => console.log(new Error("로그인 중 에러 발생"))); }
19.MVC의 모델(M) 만들기 |
객체지향프로그래밍(OOP) | UserStorage 클래스
src/models/UserStorage.js
"use strict" class UserStorage { static #users = { id: ["test1", "test2", " test3"], psword: ["1111", "1111", "1111"], name: ["홍길동", "이순신", "강감찬"] } static getUsers(...fields) { console.log("fields :", fields); 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; } } module.exports = UserStorage;
src/routes/hom/home.ctrl.js
"use strict" class UserStorage { static #users = { id: ["test1", "test2", " test3"], psword: ["1111", "1111", "1111"], name: ["홍길동", "이순신", "강감찬"] } static getUsers(...fields) { console.log("fields :", fields); 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; } } module.exports = UserStorage;
20.User 모델 만들기 | 객체지향 프로그래밍 | 인스턴스화
모델
src/models/UserStorage.js
"use strict" class UserStorage { static #users = { id: ["test1", "test2", " test3"], psword: ["1111", "1111", "1111"], name: ["홍길동", "이순신", "강감찬"] } 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) { const users = this.#users; const idx = users.id.indexOf(id); const userKeys = Object.keys(users); //=>[id,psword, name] => 키값만 배열로 변환처리 const userInfo = userKeys.reduce((newUser, info) => { newUser[info] = users[info][idx]; return newUser; }, {}); return userInfo; } } module.exports = UserStorage;
src/models/User.js
"use strict" const UserStorage = require("./UserStorage"); class User { constructor(body) { this.body = body; } login() { const body = this.body; const { id, psword } = UserStorage.getUserInfo(body.id); if (id) { if (id === body.id && psword === body.psword) { return { success: true }; } return { success: false, msg: "비밀번호가 틀렸습니다." }; } return { success: false, msg: "존재하지 않는 아이디 입니다." }; } } module.exports = User;
컨트롤
src/routes/home/home.ctrl.js
"use strict" const User = require("../../models/User"); const output = { home: (req, res) => { res.render("home/index"); }, login: (req, res) => { res.render("home/login"); }, } const process = { login: (req, res) => { const user = new User(req.body); const response = user.login(); return res.json(response); } } module.exports = { output, process }
뷰
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 } const result = fetch("/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(req) }).then(res => res.json()) .then(res => { if (res.success) { location.href = "/"; } else { alert(res.msg); } }).catch(err => console.log(new Error("로그인 중 에러 발생"))); }
댓글 ( 4)
댓글 남기기