├── chapter06 ├── solution │ ├── solution6-1 │ └── solution6-2.js ├── ws │ ├── index.html │ ├── socket.js │ └── app.js ├── socket.io │ ├── index.html │ ├── socket.js │ └── app.js └── chat │ ├── app.js │ ├── index.html │ └── index.css ├── chapter07 ├── solution │ ├── solution7-2 │ └── solution7-1 │ │ ├── views │ │ └── index.ejs │ │ └── app.js ├── ejs │ ├── views │ │ ├── partials │ │ │ ├── footer.ejs │ │ │ ├── head.ejs │ │ │ └── header.ejs │ │ ├── index.ejs │ │ └── index2.ejs │ ├── sample.html │ ├── sample.ejs │ ├── index2.js │ └── index.js ├── facebook-clone │ ├── views │ │ ├── partials │ │ │ ├── footer.ejs │ │ │ └── header.ejs │ │ ├── posts │ │ │ ├── new.ejs │ │ │ ├── show.ejs │ │ │ └── index.ejs │ │ └── users │ │ │ ├── users.ejs │ │ │ ├── user.ejs │ │ │ └── chat.ejs │ ├── public │ │ ├── posts │ │ │ └── index.css │ │ ├── users │ │ │ ├── login.css │ │ │ └── user.css │ │ └── images │ │ │ └── profile.png │ ├── README.md │ ├── models │ │ ├── Comment.js │ │ ├── Post.js │ │ └── User.js │ ├── package.json │ ├── app.js │ └── routes │ │ ├── posts.js │ │ └── users.js └── ex_passport │ ├── index.html │ └── app.js ├── .gitignore ├── chapter04 ├── solution │ ├── solution4-2.txt │ ├── solution4-1_redis_v3.js │ └── solution4-1_redis_v4.js └── sample │ ├── uuid_apikey.js │ ├── colon_path.js │ ├── redis2.js │ ├── redis.js │ ├── board_api_test.js │ ├── crwaling.js │ ├── board_api_test.html │ ├── airkorea_axios.js │ ├── dotenv.js │ ├── naver_request.js │ ├── board_api.js │ ├── airkorea_axios2.js │ ├── board_api2.js │ ├── board_api3.js │ ├── redis3.js │ └── redis3_v4.js ├── chapter08 ├── facebook-clone-v2 │ ├── .gitignore │ ├── .dockerignore │ ├── views │ │ ├── partials │ │ │ ├── footer.ejs │ │ │ └── header.ejs │ │ ├── posts │ │ │ ├── new.ejs │ │ │ ├── show.ejs │ │ │ └── index.ejs │ │ └── users │ │ │ ├── users.ejs │ │ │ ├── user.ejs │ │ │ └── chat.ejs │ ├── public │ │ ├── posts │ │ │ └── index.css │ │ ├── users │ │ │ ├── login.css │ │ │ └── user.css │ │ └── images │ │ │ └── profile.png │ ├── Dockerfile │ ├── docker-compose.yml │ ├── models │ │ ├── Comment.js │ │ ├── Post.js │ │ └── User.js │ ├── config │ │ └── winston.js │ ├── package.json │ ├── app.js │ └── routes │ │ ├── posts.js │ │ └── users.js └── solution │ ├── solution8-2 │ └── solution8-1.js ├── chapter02 ├── sample │ ├── sample01.js │ ├── sample02-2.js │ ├── sample10.js │ ├── sample04.js │ ├── sample05-1.js │ ├── sample05-2.js │ ├── sample20.js │ ├── sample03.js │ ├── sample11.js │ ├── sample02-1.js │ ├── sample09.js │ ├── sample07.js │ ├── sample16.js │ ├── sample28-1.js │ ├── sample13.js │ ├── sample19.js │ ├── sample12-1.js │ ├── sample25.js │ ├── sample12-2.js │ ├── sample18.js │ ├── sample06.js │ ├── sample08.js │ ├── sample17.js │ ├── sample28-4.js │ ├── sample14.js │ ├── sample28-2.js │ ├── sample21-1.js │ ├── sample15.js │ ├── sample21-3.js │ ├── sample21-2.js │ ├── sample24.js │ ├── sample26.js │ ├── sample23.js │ ├── sample28-3.js │ ├── sample22.js │ └── sample27.js └── solution │ ├── solution2-1.js │ ├── solution2-3.js │ ├── solution2-2.js │ └── solution2-4.js ├── chapter03 ├── sample │ ├── A.js │ ├── B.js │ ├── B2.js │ ├── A2.js │ ├── simple_server1.js │ ├── simple_server3.js │ ├── simple_server4.js │ ├── cookie.js │ ├── fs_test.html │ ├── simple_server2.js │ ├── cookie-session.js │ ├── simple_server33.js │ └── fs_test.js ├── express │ ├── public │ │ └── sample.png │ ├── express_study1.js │ ├── express_study5.js │ ├── express_study3.js │ ├── index.html │ ├── express_study2.js │ ├── express_study4.js │ ├── index2.html │ ├── express_study6.js │ ├── express_study7.js │ └── express_study8.js └── solution │ ├── solution3-1 │ ├── a.js │ ├── b.js │ └── index.js │ └── solution3-2 │ └── solution3-2.js ├── chapter01 └── solution │ └── solution ├── chapter05 ├── solution │ ├── solution5-1 │ └── solution5-2.js ├── sequelize │ ├── config │ │ └── config.json │ ├── models │ │ ├── purchase.js │ │ ├── customer.js │ │ └── index.js │ ├── app.js │ ├── customer.html │ └── app2.js └── mongoose │ └── app.js ├── package.json └── README.md /chapter06/solution/solution6-1: -------------------------------------------------------------------------------- 1 | ws -------------------------------------------------------------------------------- /chapter07/solution/solution7-2: -------------------------------------------------------------------------------- 1 | serializeUser 2 | deserializeUser -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | old/ 3 | .env 4 | .vscode 5 | .DS_Store -------------------------------------------------------------------------------- /chapter04/solution/solution4-2.txt: -------------------------------------------------------------------------------- 1 | '오늘날씨'라는 키워드를 가진 게시글을 요청하는 API 2 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log 3 | .env -------------------------------------------------------------------------------- /chapter07/ejs/views/partials/footer.ejs: -------------------------------------------------------------------------------- 1 |

© Copyright 2020 Road Book

-------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .gitignore 3 | .git -------------------------------------------------------------------------------- /chapter07/facebook-clone/views/partials/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /chapter02/sample/sample01.js: -------------------------------------------------------------------------------- 1 | console.log(puppy); 2 | var puppy = "cute"; 3 | console.log(puppy); -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/views/partials/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /chapter07/ejs/views/partials/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | Hello Express 3 | -------------------------------------------------------------------------------- /chapter06/solution/solution6-2.js: -------------------------------------------------------------------------------- 1 | socket.on("from client", (data) => { 2 | console.log(data); 3 | }); -------------------------------------------------------------------------------- /chapter03/sample/A.js: -------------------------------------------------------------------------------- 1 | /* A.js */ 2 | 3 | const A = 'variable A from A.js'; 4 | 5 | module.exports = A; 6 | 7 | -------------------------------------------------------------------------------- /chapter02/sample/sample02-2.js: -------------------------------------------------------------------------------- 1 | const puppy = "cute"; 2 | const puppy = "so cute"; // 'puppy' has already been declared -------------------------------------------------------------------------------- /chapter02/sample/sample10.js: -------------------------------------------------------------------------------- 1 | function add(a, b) { 2 | return a + b; 3 | } 4 | 5 | console.log(add(1, 4)); // 5 -------------------------------------------------------------------------------- /chapter03/sample/B.js: -------------------------------------------------------------------------------- 1 | /* B.js */ 2 | 3 | const A = require('./A'); 4 | 5 | console.log(A + ' in B.js'); 6 | 7 | -------------------------------------------------------------------------------- /chapter02/sample/sample04.js: -------------------------------------------------------------------------------- 1 | let puppy = "cute"; 2 | { 3 | let puppy = "so cute"; 4 | } 5 | console.log(puppy); // cute -------------------------------------------------------------------------------- /chapter02/sample/sample05-1.js: -------------------------------------------------------------------------------- 1 | const puppy = "cute"; 2 | puppy = "so cute!!"; // TypeError: Assignment to constant variable. -------------------------------------------------------------------------------- /chapter04/sample/uuid_apikey.js: -------------------------------------------------------------------------------- 1 | const uuidAPIkey = require('uuid-apikey'); 2 | 3 | console.log(uuidAPIkey.create()); 4 | 5 | -------------------------------------------------------------------------------- /chapter02/sample/sample05-2.js: -------------------------------------------------------------------------------- 1 | let dog; 2 | console.log(dog); // undefined 3 | dog = "so lovely"; 4 | console.log(dog); // so lovely -------------------------------------------------------------------------------- /chapter03/express/public/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinkyungPark/roadbook-nodejs/HEAD/chapter03/express/public/sample.png -------------------------------------------------------------------------------- /chapter02/sample/sample20.js: -------------------------------------------------------------------------------- 1 | console.log(0); 2 | 3 | setTimeout(function () { 4 | console.log('Hello'); 5 | }, 0); 6 | 7 | console.log(1); 8 | -------------------------------------------------------------------------------- /chapter02/sample/sample03.js: -------------------------------------------------------------------------------- 1 | var puppy = "cute"; 2 | console.log(puppy); // cute 3 | { 4 | var puppy = "so cute"; 5 | } 6 | console.log(puppy); // so cute -------------------------------------------------------------------------------- /chapter02/sample/sample11.js: -------------------------------------------------------------------------------- 1 | const add = (a, b) => { 2 | return a + b; 3 | } 4 | 5 | console.log(add(1, 4)); // 5 6 | 7 | // const add = (a, b) => a + b; -------------------------------------------------------------------------------- /chapter07/facebook-clone/public/posts/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinkyungPark/roadbook-nodejs/HEAD/chapter07/facebook-clone/public/posts/index.css -------------------------------------------------------------------------------- /chapter07/facebook-clone/public/users/login.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinkyungPark/roadbook-nodejs/HEAD/chapter07/facebook-clone/public/users/login.css -------------------------------------------------------------------------------- /chapter02/sample/sample02-1.js: -------------------------------------------------------------------------------- 1 | let dog; 2 | dog = "happy"; 3 | console.log(dog); // happy 4 | let dog = "happy"; // Identifier 'dog' has already been declared 5 | -------------------------------------------------------------------------------- /chapter08/solution/solution8-2: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic 2 | RUN apt-get update 3 | RUN apt-get install -y git 4 | 5 | # 이미지 빌드 명령어 6 | $ docker build -t docker-git . -------------------------------------------------------------------------------- /chapter02/sample/sample09.js: -------------------------------------------------------------------------------- 1 | const animal = ['dog', 'cat']; 2 | 3 | let [first, second] = animal; 4 | 5 | console.log(first); // dog 6 | console.log(second); // cat -------------------------------------------------------------------------------- /chapter07/facebook-clone/public/images/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinkyungPark/roadbook-nodejs/HEAD/chapter07/facebook-clone/public/images/profile.png -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/public/posts/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinkyungPark/roadbook-nodejs/HEAD/chapter08/facebook-clone-v2/public/posts/index.css -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/public/users/login.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinkyungPark/roadbook-nodejs/HEAD/chapter08/facebook-clone-v2/public/users/login.css -------------------------------------------------------------------------------- /chapter08/solution/solution8-1.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | app.use(morgan('combined')); 3 | } else { 4 | app.use(morgan('dev')); 5 | } -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/public/images/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinkyungPark/roadbook-nodejs/HEAD/chapter08/facebook-clone-v2/public/images/profile.png -------------------------------------------------------------------------------- /chapter02/sample/sample07.js: -------------------------------------------------------------------------------- 1 | const country = { 2 | name: "Korea", 3 | population: "5178579", 4 | get_name: function () { 5 | return this.name; 6 | } 7 | }; -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | WORKDIR /usr/src/app 3 | COPY package*.json ./ 4 | RUN npm install 5 | COPY . . 6 | EXPOSE 3000 7 | CMD ["npm", "start"] -------------------------------------------------------------------------------- /chapter03/sample/B2.js: -------------------------------------------------------------------------------- 1 | /* B2.js */ 2 | 3 | const A = require('./A2'); 4 | const B = 'variable B from B2.js'; 5 | 6 | console.log(A + ' in B2.js'); 7 | 8 | module.exports = B; 9 | -------------------------------------------------------------------------------- /chapter02/sample/sample16.js: -------------------------------------------------------------------------------- 1 | function Animal() { } 2 | 3 | Animal.prototype.legs = 4; 4 | Animal.prototype.tail = 1; 5 | 6 | const dog = new Animal(); 7 | const cat = new Animal(); 8 | -------------------------------------------------------------------------------- /chapter02/sample/sample28-1.js: -------------------------------------------------------------------------------- 1 | async function myAsyncFunc() { 2 | return 'done'; 3 | } 4 | 5 | const result = myAsyncFunc(); 6 | console.log(result); // Promise { : "done" } 7 | -------------------------------------------------------------------------------- /chapter02/sample/sample13.js: -------------------------------------------------------------------------------- 1 | function func() { }; 2 | console.log(func.prototype); //func {} 3 | 4 | func.prototype.name = 'gildong'; 5 | console.log(func.prototype); // func { name: 'gildong' } -------------------------------------------------------------------------------- /chapter03/sample/A2.js: -------------------------------------------------------------------------------- 1 | /* A2.js */ 2 | 3 | const A = 'variable A from A2.js'; 4 | const B = require('./B2'); 5 | 6 | console.log(B + ' in A2.js'); 7 | 8 | module.exports = A; 9 | 10 | -------------------------------------------------------------------------------- /chapter01/solution/solution: -------------------------------------------------------------------------------- 1 | 1. 요청, 응답 2 | 2. 런타임 3 | 3. 논블로킹(NonBloking), 비동기(Async) 4 | 4. 쓰레드 5 | 5. 싱글 6 | 6. 이벤트 루프 7 | 7. 콜백큐(작업큐) 8 | 8. 콜스택 9 | 9. 콜백 함수 10 | 10. npm(node pacakge manager) -------------------------------------------------------------------------------- /chapter03/sample/simple_server1.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | http.createServer((req, res) => { 4 | }) 5 | .listen(8080, () => { 6 | console.log('8080포트에서 서버 연결 중 ..') 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /chapter03/solution/solution3-1/a.js: -------------------------------------------------------------------------------- 1 | console.log('a.js 진입..!'); 2 | 3 | const b = require('./b'); 4 | 5 | module.exports = { 6 | call: () => { 7 | console.log('a.js에서 b 호출 : ', b); 8 | } 9 | }; -------------------------------------------------------------------------------- /chapter03/solution/solution3-1/b.js: -------------------------------------------------------------------------------- 1 | console.log('b.js 진입..!'); 2 | 3 | const a = require('./a'); 4 | 5 | module.exports = { 6 | call: () => { 7 | console.log('b.js에서 a 호출 : ', a); 8 | } 9 | }; -------------------------------------------------------------------------------- /chapter02/sample/sample19.js: -------------------------------------------------------------------------------- 1 | function fakeSetTimeout(callback) { 2 | callback(); 3 | } 4 | 5 | console.log(0); 6 | 7 | fakeSetTimeout(function () { 8 | console.log('Hello'); 9 | }); 10 | 11 | console.log(1); -------------------------------------------------------------------------------- /chapter02/sample/sample12-1.js: -------------------------------------------------------------------------------- 1 | var people = { 2 | name: 'gildong', 3 | say: function () { 4 | console.log(this); 5 | } 6 | } 7 | 8 | people.say(); 9 | 10 | var sayPeople = people.say; 11 | sayPeople(); -------------------------------------------------------------------------------- /chapter04/sample/colon_path.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | app.get('/:type', (req, res) => { 5 | let { type } = req.params; 6 | res.send(type); 7 | }); 8 | 9 | app.listen(8080); -------------------------------------------------------------------------------- /chapter02/sample/sample25.js: -------------------------------------------------------------------------------- 1 | function sum(a, b) { 2 | if (typeof a !== 'number' || typeof b !== 'number') { 3 | throw 'type of arguments must be number type'; 4 | } 5 | console.log(a + b); 6 | } 7 | 8 | sum(1, '4'); -------------------------------------------------------------------------------- /chapter02/sample/sample12-2.js: -------------------------------------------------------------------------------- 1 | var people = { 2 | name: 'gildong', 3 | say: function () { 4 | console.log(this); 5 | } 6 | } 7 | 8 | people.say(); 9 | 10 | var sayPeople = people.say.bind(people); 11 | sayPeople(); -------------------------------------------------------------------------------- /chapter02/solution/solution2-1.js: -------------------------------------------------------------------------------- 1 | const order1 = new MakeOrder('오렌지 쥬스', '2500'); 2 | const order2 = new MakeOrder('토마토 주스', '3000'); 3 | 4 | // 정답 5 | function MakeOrder(name, price) { 6 | this.name = name; 7 | this.price = price; 8 | } -------------------------------------------------------------------------------- /chapter03/express/express_study1.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express() 3 | 4 | app.get('/', (req, res) => { 5 | res.send('Hello World!'); 6 | }); 7 | 8 | app.listen(8080, () => 9 | console.log('8080포트에서 서버 실행중')); -------------------------------------------------------------------------------- /chapter03/sample/simple_server3.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | http.createServer((req, res) => { 4 | console.log(req); 5 | console.log(res); 6 | }) 7 | .listen(8080, () => { 8 | console.log('8080포트에서 서버 연결') 9 | }); -------------------------------------------------------------------------------- /chapter07/solution/solution7-1/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% for (var i = 0; i < People.length; i++) { %> 5 |

Welcome to <%- People[i].name %>

6 | <% } %> 7 | 8 | -------------------------------------------------------------------------------- /chapter03/solution/solution3-1/index.js: -------------------------------------------------------------------------------- 1 | const a = require('./a'); 2 | const b = require('./b'); 3 | 4 | a.call(); 5 | b.call(); 6 | 7 | /* 8 | 결과 9 | 10 | a.js 진입..! 11 | b.js 진입..! 12 | a.js에서 b 호출 : {call: ƒ} 13 | b.js에서 a 호출 : {} 14 | 15 | */ -------------------------------------------------------------------------------- /chapter03/express/express_study5.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | app.use(function (err, req, res, next) { 5 | console.error(err.stack); 6 | res.status(500).send('Something broke!'); 7 | }); 8 | 9 | app.listen(3000); -------------------------------------------------------------------------------- /chapter02/sample/sample18.js: -------------------------------------------------------------------------------- 1 | setTimeout(() => { 2 | setTimeout(() => { 3 | console.log('todo: Second work!'); 4 | }, 2000); 5 | console.log('todo: First work!'); 6 | }, 3000); 7 | 8 | // 결과 9 | // todo: First work! 10 | // todo: Second work! 11 | -------------------------------------------------------------------------------- /chapter07/ejs/sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Title 5 | 6 | 7 | 8 |

Title

9 |

This is html file

10 | 11 | -------------------------------------------------------------------------------- /chapter02/sample/sample06.js: -------------------------------------------------------------------------------- 1 | function outer() { 2 | var a = 'A'; 3 | var b = 'B'; 4 | 5 | function inner() { 6 | var a = 'AA'; 7 | console.log(b); 8 | } 9 | return inner; 10 | } 11 | 12 | var outerFunc = outer(); 13 | outerFunc(); // B -------------------------------------------------------------------------------- /chapter02/sample/sample08.js: -------------------------------------------------------------------------------- 1 | const coffee = []; 2 | 3 | coffee.push({ name: 'Americano' }); 4 | coffee.push({ name: "Latte" }); 5 | 6 | console.log(coffee); // [ { name: 'Americano' }, { name: 'Latte' } ] 7 | console.log(coffee[0]); // { name: 'Americano' } 8 | console.log(coffee.length); // 2 -------------------------------------------------------------------------------- /chapter07/ejs/sample.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

This is ejs file

10 | 11 | 12 | -------------------------------------------------------------------------------- /chapter02/solution/solution2-3.js: -------------------------------------------------------------------------------- 1 | console.log('First Console'); 2 | 3 | setTimeout(function () { 4 | console.log('Second Console'); 5 | }, 0); 6 | 7 | console.log('Third Console'); 8 | 9 | 10 | // 실행 결과 11 | 12 | // First Console 13 | // Third Console 14 | // Second Console 15 | // 비동기 -------------------------------------------------------------------------------- /chapter03/sample/simple_server4.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | http.createServer((req, res) => { 4 | if (req.url === '/') { 5 | res.write('Hello'); 6 | res.end() 7 | } 8 | }) 9 | .listen(8080, () => { 10 | console.log('8080포트에서 서버 연결') 11 | }); -------------------------------------------------------------------------------- /chapter02/sample/sample17.js: -------------------------------------------------------------------------------- 1 | setTimeout(() => { // 내장 함수 setTimeout(callback, delayTime) 2 | console.log('todo: First work!'); 3 | }, 3000); 4 | 5 | setTimeout(() => { 6 | console.log('todo: Second work!'); 7 | }, 2000); 8 | 9 | // 결과 10 | // todo: Second work! 11 | // todo: First work! -------------------------------------------------------------------------------- /chapter07/facebook-clone/README.md: -------------------------------------------------------------------------------- 1 | ### Chapter7 facebook-clone 프로젝트 실행 방법 2 | 1. cd ./chapter07/facebook-clone 3 | 2. npm install 4 | 3. .env 파일 생성 5 | 5. https://cloudinary.com/ 회원가입 후 API키를 발급 6 | 6. cloudinary API key, secret, cloud_name을 .env에 작성(함께해봐요 7-27 참고) 7 | 7. mongodb 설정 8 | 8. $ npm start 9 | -------------------------------------------------------------------------------- /chapter02/sample/sample28-4.js: -------------------------------------------------------------------------------- 1 | async function myAsyncFunc() { 2 | consolejljalk.log(new Date()); // Uncaught 3 | const result = await wait(2).catch(e => { 4 | console.error(e) 5 | }); 6 | console.log(new Date()); 7 | } 8 | 9 | try { myAsyncFunc(); } catch (e) { } // ==> X 10 | myAsyncFunc().catch(e); // ==> O -------------------------------------------------------------------------------- /chapter03/express/express_study3.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | app.get('/', function (req, res) { 5 | res.send('Hello World!'); 6 | }); 7 | 8 | const myLogger = function (req, res) { 9 | console.log('LOGGED'); 10 | }; 11 | 12 | app.use(myLogger); 13 | 14 | app.listen(8080); 15 | -------------------------------------------------------------------------------- /chapter03/sample/cookie.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | http.createServer((req, res) => { 4 | res.writeHead(200, { 'Set-cookie': 'name=roadbook' }); 5 | console.log(req.headers.cookie); 6 | res.end('Cookie --> Header'); 7 | }) 8 | .listen(8080, () => { 9 | console.log('8080포트에서 서버 연결 중 ..'); 10 | }); 11 | -------------------------------------------------------------------------------- /chapter03/express/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | express로 웹 만들기 7 | 8 | 9 |

express로 웹 만들기

10 |

메인 페이지 입니다.

11 | 12 | 13 | -------------------------------------------------------------------------------- /chapter03/sample/fs_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |

Node.js로 서버만들기

10 |

3장 http모듈 공부중입니다.

11 | 12 | 13 | -------------------------------------------------------------------------------- /chapter02/sample/sample14.js: -------------------------------------------------------------------------------- 1 | const animal = { 2 | leg: 4, 3 | tail: 1, 4 | say() { 5 | console.log('I have 4 legs 1 tail'); 6 | } 7 | } 8 | 9 | const dog = { 10 | sound: 'wang' 11 | } 12 | 13 | const cat = { 14 | sound: 'yaong' 15 | } 16 | 17 | dog.__proto__ = animal; 18 | cat.__proto__ = animal; 19 | 20 | console.log(dog.leg); // 4 -------------------------------------------------------------------------------- /chapter03/express/express_study2.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | app.set('port', process.env.PORT || 8080); 5 | 6 | app.get('/', (req, res) => { 7 | res.sendFile(__dirname + '/index.html'); 8 | }); 9 | 10 | app.listen(app.get('port'), () => { 11 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /chapter03/sample/simple_server2.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | http.createServer((req, res) => { 4 | res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); 5 | res.write('

Node.js로 서버만들기

'); 6 | res.end('

3장 http모듈 공부중입니다.

') 7 | }) 8 | .listen(8080, () => { 9 | console.log('8080포트에서 서버 연결 중 ..'); 10 | }); -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | app: 4 | container_name: facebook-clone 5 | restart: always 6 | build: . 7 | ports: 8 | - '80:3000' 9 | links: 10 | - mongo 11 | mongo: 12 | container_name: mongo 13 | image: mongo 14 | ports: 15 | - '27017:27017' -------------------------------------------------------------------------------- /chapter03/express/express_study4.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | app.get('/', function (req, res, next) { 5 | res.send('Hello World!'); 6 | next(); 7 | }); 8 | 9 | const myLogger = function (req, res, next) { 10 | console.log('LOGGED'); 11 | next(); 12 | }; 13 | 14 | app.use(myLogger); 15 | 16 | app.listen(8080); 17 | -------------------------------------------------------------------------------- /chapter03/express/index2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | express로 웹 만들기 7 | 8 | 9 |

express로 웹 만들기

10 |

메인 페이지 입니다.

11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /chapter02/sample/sample28-2.js: -------------------------------------------------------------------------------- 1 | async function myAsyncFunc() { 2 | throw 'myAsyncFunc Error!'; 3 | } 4 | 5 | function myPromiseFunc() { 6 | return new Promise((resolve, reject) => { 7 | reject('myPromiseFunc Error!'); 8 | }); 9 | } 10 | 11 | const result = myAsyncFunc().catch(e => { console.log(e) }); 12 | const result2 = myPromiseFunc().catch(e => { console.log(e) }); -------------------------------------------------------------------------------- /chapter02/sample/sample21-1.js: -------------------------------------------------------------------------------- 1 | function work(sec, callback) { 2 | setTimeout(() => { 3 | callback(new Date().toISOString()); 4 | }, sec * 1000); 5 | }; 6 | 7 | work(1, (result) => { 8 | console.log('첫번째 작업', result); 9 | }); 10 | 11 | work(1, (result) => { 12 | console.log('두번째 작업', result); 13 | }); 14 | 15 | work(1, (result) => { 16 | console.log('세번째 작업', result); 17 | }); 18 | 19 | 20 | -------------------------------------------------------------------------------- /chapter03/express/express_study6.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | app.set('port', process.env.PORT || 8080); 5 | 6 | app.use(express.static(__dirname + '/public')); 7 | 8 | app.get('/', (req, res) => { 9 | res.sendFile(__dirname + '/index.html'); 10 | }); 11 | 12 | app.listen(app.get('port'), () => { 13 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 14 | }); 15 | -------------------------------------------------------------------------------- /chapter02/sample/sample15.js: -------------------------------------------------------------------------------- 1 | const animal = { 2 | leg: 4, 3 | tail: 1, 4 | say() { 5 | console.log('I have 4 legs 1 tail'); 6 | } 7 | } 8 | 9 | const dog = { 10 | sound: 'wang', 11 | happy: true 12 | } 13 | 14 | dog.__proto__ = animal; 15 | 16 | const cat = { 17 | sound: 'yaong' 18 | } 19 | 20 | cat.__proto__ = dog; 21 | 22 | console.log(cat.happy); // true 23 | console.log(cat.leg); // 4 -------------------------------------------------------------------------------- /chapter02/solution/solution2-2.js: -------------------------------------------------------------------------------- 1 | console.log('First Console'); 2 | 3 | function fakeSetTimeout(callback, delay) { 4 | callback(); 5 | } 6 | 7 | console.log('Second Console'); 8 | 9 | fakeSetTimeout(function() { 10 | console.log('Third Console'); 11 | }, 0); 12 | 13 | console.log('Fourth Console'); 14 | 15 | 16 | // 실행결과 17 | 18 | // First Console 19 | // Second Console 20 | // Third Console 21 | // Fourth Console -------------------------------------------------------------------------------- /chapter03/sample/cookie-session.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | const session = {}; 4 | const sessKey = new Date(); 5 | session[sessKey] = { name: 'roadbook' }; 6 | 7 | http.createServer((req, res) => { 8 | res.writeHead(200, { 'Set-cookie': `session=${sessKey}` }); 9 | res.end('Session-Cookie --> Header'); 10 | }) 11 | .listen(8080, () => { 12 | console.log('8080포트에서 서버 연결 중 ..'); 13 | }); 14 | -------------------------------------------------------------------------------- /chapter03/solution/solution3-2/solution3-2.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | app.set('port', process.env.PORT || 8080); 5 | 6 | app.use(express.static(__dirname + '/public')); 7 | 8 | app.get('/', (req, res) => { 9 | res.send('메인 페이지 입니다.'); 10 | }); 11 | 12 | app.get('/user/:id', (req, res) => { 13 | res.send(req.params.id + "님의 개인 페이지 입니다."); 14 | }); 15 | 16 | app.listen(app.get('port')); -------------------------------------------------------------------------------- /chapter02/sample/sample21-3.js: -------------------------------------------------------------------------------- 1 | function work(sec, callback) { 2 | setTimeout(() => { 3 | callback(new Date().toISOString()); 4 | }, sec * 1000); 5 | }; 6 | 7 | work(1, (result) => { 8 | console.log('첫번째 작업', result); 9 | 10 | work(1, (result) => { 11 | 12 | work(1, (result) => { 13 | console.log('세번째 작업', result); 14 | }); 15 | 16 | console.log('두번째 작업', result); 17 | }); 18 | }); -------------------------------------------------------------------------------- /chapter02/sample/sample21-2.js: -------------------------------------------------------------------------------- 1 | function work(sec, callback) { 2 | setTimeout(() => { 3 | callback(new Date().toISOString()); 4 | }, sec * 1000); 5 | }; 6 | 7 | work(1, (result) => { 8 | console.log('첫번째 작업', result); 9 | 10 | work(1, (result) => { 11 | console.log('두번째 작업', result); 12 | 13 | work(1, (result) => { 14 | console.log('세번째 작업', result); 15 | }); 16 | }); 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /chapter02/sample/sample24.js: -------------------------------------------------------------------------------- 1 | function workP(sec) { 2 | return new Promise((resolve, reject) => { 3 | setTimeout(() => { 4 | resolve('workP function'); 5 | }, sec * 1000); 6 | }); 7 | } 8 | 9 | async function asyncFunc() { 10 | const result_workP = await workP(3); 11 | console.log(result_workP); 12 | return 'async function'; 13 | } 14 | 15 | asyncFunc().then((result) => { 16 | console.log(result) 17 | }); -------------------------------------------------------------------------------- /chapter02/sample/sample26.js: -------------------------------------------------------------------------------- 1 | // catch 해주지 않은 부분은 실행되지 않음 2 | function f2() { 3 | console.log('this is f2 start'); 4 | throw new Error('에러'); // Error 객체 - 해당하는 콜스택 정보가 담겨있다. 5 | console.log('this is f2 end'); // 실행되지 않음. 6 | } 7 | 8 | function f1() { 9 | console.log('this is f1 start'); 10 | try { 11 | f2(); 12 | } catch (e) { 13 | console.log(e); 14 | } 15 | console.log('this is f1 end'); 16 | } 17 | 18 | f1(); -------------------------------------------------------------------------------- /chapter07/ejs/views/partials/header.ejs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chapter02/sample/sample23.js: -------------------------------------------------------------------------------- 1 | function workP(sec) { 2 | return new Promise((resolve, reject) => { 3 | setTimeout(() => { 4 | resolve(new Date().toISOString()); 5 | }, sec * 1000); 6 | }); 7 | } 8 | 9 | function justFunc() { 10 | return 'just Function'; 11 | } 12 | 13 | async function asyncFunc() { 14 | return 'async Fucntion'; 15 | } 16 | 17 | console.log(justFunc()); 18 | console.log(asyncFunc()); 19 | console.log(workP()); -------------------------------------------------------------------------------- /chapter07/ejs/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= title %> 8 | 9 | 10 |

<%= title %>

11 | <% for (var i = 0; i < People.length; i++) { %> 12 |

Welcome to <%- People[i].name %>

13 | <% } %> 14 | 15 | -------------------------------------------------------------------------------- /chapter07/ejs/views/index2.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- include('./partials/head'); %> 5 | 6 | 7 |
8 | <%- include('./partials/header'); %> 9 |
10 | 11 | 12 |

<%= menu %>

13 |

<%= menu %>의 본문 부분 입니다.

14 | 15 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /chapter02/sample/sample28-3.js: -------------------------------------------------------------------------------- 1 | function wait(sec) { 2 | return new Promise((resolve, reject) => { 3 | setTimeout(() => { 4 | reject('throw Error!'); 5 | }, sec * 1000); 6 | }); 7 | } 8 | 9 | async function myAsyncFunc() { 10 | console.log(new Date()); 11 | try { 12 | await wait(2); // Promise를 기다리는 중... 13 | } catch (e) { 14 | console.error(e); 15 | } 16 | console.log(new Date()); 17 | } 18 | 19 | const result = myAsyncFunc(); 20 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/models/Comment.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | let CommentSchema = new mongoose.Schema({ // 댓글용 스키마 4 | content: String, 5 | likes: Number, 6 | creator: { 7 | _id: { 8 | type: mongoose.Schema.Types.ObjectId, 9 | ref: "User" 10 | }, 11 | firstName: String, 12 | lastName: String 13 | } 14 | }); 15 | 16 | let Comment = mongoose.model("Comment", CommentSchema); 17 | 18 | module.exports = Comment; 19 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/models/Comment.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | let CommentSchema = new mongoose.Schema({ // 댓글용 스키마 4 | content: String, 5 | likes: Number, 6 | creator: { 7 | _id: { 8 | type: mongoose.Schema.Types.ObjectId, 9 | ref: "User" 10 | }, 11 | firstName: String, 12 | lastName: String 13 | } 14 | }); 15 | 16 | let Comment = mongoose.model("Comment", CommentSchema); 17 | 18 | module.exports = Comment; 19 | -------------------------------------------------------------------------------- /chapter04/sample/redis2.js: -------------------------------------------------------------------------------- 1 | const redis = require('redis'); 2 | const client = redis.createClient(6379, '127.0.0.1'); 3 | 4 | /* redis version 3.1.2 */ 5 | /* npm install redis@^3.1.2 */ 6 | client.rpush('myKey', 0); 7 | client.rpush('myKey', 1); 8 | client.rpush('myKey', 2); 9 | 10 | /* if redis version over 4 */ 11 | // async function run() { 12 | // await client.connect(); 13 | // } 14 | // run(); 15 | 16 | // client.rPush('myKey', '0'); 17 | // client.rPush('myKey', '1'); 18 | // client.rPush('myKey', '2'); 19 | -------------------------------------------------------------------------------- /chapter03/sample/simple_server33.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | const server = http.createServer((req, res) => { 4 | res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); 5 | res.write('

Node.js로 서버만들기

'); 6 | res.end('

3장 http모듈 공부중입니다.

') 7 | }) 8 | .listen(8080); 9 | 10 | /* Listening Event Listener */ 11 | server.on('listening', () => { 12 | console.log('8080포트에서 서버 연결 중 ..'); 13 | }); 14 | 15 | /* Error Event Listener */ 16 | server.on('error', () => { 17 | console.error(error); 18 | }); -------------------------------------------------------------------------------- /chapter05/solution/solution5-1: -------------------------------------------------------------------------------- 1 | > INSERT INTO roadbook.purchase(customer_id, book_name) VALUES(1, '엔지니어를 위한 문장의 기술'); 2 | > INSERT INTO roadbook.purchase(customer_id, book_name) VALUES(2, '개발자를 위한 글쓰기 가이드'); 3 | > INSERT INTO roadbook.purchase(customer_id, book_name) VALUES(3, '엔지니어를 위한 문장의 기술'); 4 | > INSERT INTO roadbook.purchase(customer_id, book_name) VALUES(3, '백견불여일타 딥러닝 입문'); 5 | > INSERT INTO roadbook.purchase(customer_id, book_name) VALUES(4, '엔지니어를 위한 문장의 기술'); 6 | > INSERT INTO roadbook.purchase(customer_id, book_name) VALUES(4, '백견불여일타 머신러닝 데이터 전처리 입문 '); -------------------------------------------------------------------------------- /chapter04/sample/redis.js: -------------------------------------------------------------------------------- 1 | const redis = require('redis'); 2 | const client = redis.createClient(6379, '127.0.0.1'); 3 | 4 | /* redis version 3.1.2 */ 5 | /* npm install redis@^3.1.2 */ 6 | 7 | client.get('myKey', (err, value) => { 8 | console.log(value); 9 | }); 10 | 11 | 12 | /* if redis version over 4 */ 13 | //async function run(){ 14 | // await client.connect() 15 | // } 16 | 17 | // run(); 18 | 19 | // async function getVal(key){ 20 | // const item = await client.lRange(key, 0, -1); 21 | // console.log(item); 22 | // } 23 | 24 | // getVal("myKey"); 25 | -------------------------------------------------------------------------------- /chapter02/sample/sample22.js: -------------------------------------------------------------------------------- 1 | function workP(sec) { 2 | // promise의 인스턴스를 리턴하고 3 | // then에서 리턴한 것을 받는다. 4 | return new Promise((resolve, reject) => { 5 | // Promise 생성시 넘기는 callback = resolve, reject 6 | // resolve 동작 완료시 호출, 에러 났을 경우 reject 7 | setTimeout(() => { 8 | resolve(new Date().toISOString()); 9 | }, sec * 1000); 10 | }); 11 | } 12 | 13 | workP(1).then((result) => { 14 | console.log('첫번째 작업', result); 15 | return workP(1); 16 | }).then((result) => { 17 | console.log('두번째 작업', result); 18 | }); 19 | -------------------------------------------------------------------------------- /chapter05/sequelize/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "username": "root", 4 | "password": "4557", 5 | "database": "roadbook", 6 | "host": "127.0.0.1", 7 | "dialect": "mysql" 8 | }, 9 | "test": { 10 | "username": "root", 11 | "password": null, 12 | "database": "database_test", 13 | "host": "127.0.0.1", 14 | "dialect": "mysql" 15 | }, 16 | "production": { 17 | "username": "root", 18 | "password": null, 19 | "database": "database_production", 20 | "host": "127.0.0.1", 21 | "dialect": "mysql" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/config/winston.js: -------------------------------------------------------------------------------- 1 | const winston = require('winston') 2 | 3 | const logger = winston.createLogger({ 4 | level: 'info', 5 | format: winston.format.json(), 6 | transports: [ 7 | new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), 8 | new winston.transports.File({ filename: 'logs/combined.log' }) 9 | ] 10 | }); 11 | 12 | if (process.env.NODE_ENV !== 'production') { 13 | logger.add(new winston.transports.Console({ 14 | format: winston.format.simple() 15 | })); 16 | } 17 | 18 | module.exports = logger -------------------------------------------------------------------------------- /chapter04/sample/board_api_test.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const axios = require('axios'); 3 | const express = require('express'); 4 | const app = express(); 5 | 6 | app.set('port', 3000); 7 | 8 | /* 공통 미들웨어 */ 9 | app.use(morgan('dev')); 10 | app.use(express.json()); 11 | app.use(express.urlencoded({ extended: true })); 12 | 13 | /* axios 요청 */ 14 | app.get('/', (req, res) => { 15 | res.sendFile(__dirname + "/board_api_test.html"); 16 | }); 17 | 18 | /* 서버와 포트 연결.. */ 19 | app.listen(app.get('port'), () => { 20 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 21 | }); 22 | 23 | -------------------------------------------------------------------------------- /chapter03/express/express_study7.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | app.set('port', process.env.PORT || 8080); 5 | 6 | app.use(express.static(__dirname + '/public')); 7 | 8 | app.get('/', (req, res) => { 9 | const output = ` 10 |

express로 웹 만들기


11 |

메인 페이지 입니다.


12 | 13 | ` 14 | res.send(output); 15 | }); 16 | 17 | app.get('/user/:id', (req, res) => { 18 | res.send(req.params.id + "님의 개인 페이지 입니다."); 19 | }); 20 | 21 | app.listen(app.get('port'), () => { 22 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 23 | }); -------------------------------------------------------------------------------- /chapter03/sample/fs_test.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const fs = require('fs').promises; 3 | 4 | http.createServer(async (req, res) => { 5 | try { 6 | const f = await fs.readFile('./fs_test.html'); 7 | res.writeHead(200, { 'Content-Type': 'text.html; charset=utf-8' }); // 200이면 요청 성공 8 | res.end(f); // 요청 종료 9 | } catch (err) { // 에러 처리 10 | console.error(err); // 요청에 실패했을 경우 에러 출력 11 | res.writeHead(500, { 'Content-Type': 'text.html; charset=utf-8' }); // 500이면 서버에 오류 발생 12 | res.end(err.message); // 에러 메세지와 함게 요청 종료 13 | } 14 | }) 15 | .listen(8080, () => { 16 | console.log('8080포트에서 서버 연결 중 ..') 17 | }); -------------------------------------------------------------------------------- /chapter05/sequelize/models/purchase.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | const newPurchase = sequelize.define("new_purchase", { 3 | customer_id: { 4 | type: DataTypes.INTEGER, 5 | allowNull: false, 6 | }, 7 | book_name: { 8 | type: DataTypes.STRING(20), 9 | allowNull: false 10 | }, 11 | purchase_date: { 12 | type: 'TIMESTAMP', 13 | defaultValue: sequelize.literal('CURRENT_TIMESTAMP'), 14 | allowNull: false 15 | }, 16 | }, { 17 | freezeTableName: true, 18 | timestamps: false 19 | }); 20 | return newPurchase; 21 | }; 22 | -------------------------------------------------------------------------------- /chapter05/sequelize/app.js: -------------------------------------------------------------------------------- 1 | const { sequelize } = require('./models/index.js'); 2 | 3 | const driver = () => { 4 | sequelize.sync().then(() => { 5 | console.log('초기화 완료'); 6 | }).catch((err) => { 7 | console.error('초기화 실패'); 8 | console.error(err); 9 | }); 10 | }; 11 | driver(); 12 | 13 | 14 | /* // async/aswait 15 | const { sequelize } = require('./models/index.js'); 16 | 17 | const driver = async () => { 18 | try { 19 | await sequelize.sync(); 20 | } catch (err) { 21 | console.error('초기화 실패'); 22 | console.error(err); 23 | return; 24 | } 25 | 26 | console.log('초기화 완료'); 27 | }; 28 | driver(); 29 | */ 30 | 31 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/models/Post.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | let PostSchema = new mongoose.Schema({ // 게시물 스키마 4 | content: String, 5 | time: Date, 6 | likes: Number, 7 | image: String, 8 | creator: { 9 | _id: { 10 | type: mongoose.Schema.Types.ObjectId, 11 | ref: "User" 12 | }, 13 | firstName: String, 14 | lastName: String, 15 | profile: String 16 | }, 17 | comments: [ 18 | { 19 | type: mongoose.Schema.Types.ObjectId, 20 | ref: "Comment" 21 | } 22 | ] 23 | }); 24 | 25 | let Post = mongoose.model("Post", PostSchema); 26 | module.exports = Post; 27 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/models/Post.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | let PostSchema = new mongoose.Schema({ // 게시물 스키마 4 | content: String, 5 | time: Date, 6 | likes: Number, 7 | image: String, 8 | creator: { 9 | _id: { 10 | type: mongoose.Schema.Types.ObjectId, 11 | ref: "User" 12 | }, 13 | firstName: String, 14 | lastName: String, 15 | profile: String 16 | }, 17 | comments: [ 18 | { 19 | type: mongoose.Schema.Types.ObjectId, 20 | ref: "Comment" 21 | } 22 | ] 23 | }); 24 | 25 | let Post = mongoose.model("Post", PostSchema); 26 | module.exports = Post; 27 | -------------------------------------------------------------------------------- /chapter07/solution/solution7-1/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const app = express(); 4 | 5 | app.set('port', process.env.PORT || 8080); 6 | app.set('views', path.join(__dirname, 'views')); 7 | app.set('view engine', 'ejs'); 8 | 9 | app.get('/', (req, res) => { 10 | res.render('index', { 11 | "People" : [ 12 | { "name" : "Cho-rong" }, 13 | { "name" : "Seul-gi" }, 14 | { "name" : "Jin-kyung" }, 15 | { "name" : "Hyun-jeong" }, 16 | { "name" : "Ah-reum" } 17 | ] 18 | }); 19 | }); 20 | 21 | app.listen(app.get('port'), () => { 22 | console.log(app.get('port'),'번 포트에서 서버 실행 중..') 23 | }); -------------------------------------------------------------------------------- /chapter02/sample/sample27.js: -------------------------------------------------------------------------------- 1 | function wait(sec) { 2 | return new Promise((resolve, reject) => { 3 | setTimeout(() => { 4 | reject('error!'); 5 | }, sec * 1000); 6 | }); 7 | } 8 | 9 | wait(3).catch(e => { 10 | console.log('1st catch ', e); 11 | }); 12 | 13 | /* chain은 같은 객체를 리턴할 때만 가능하다.*/ 14 | wait(3).catch(e => { 15 | console.log('1st catch ', e); 16 | }) // wait함수의 에러를 받음 17 | .catch(e => { 18 | console.log('1st catch ', e); 19 | }); // 위 catch 구문의 상태를 받음. 에러를 잘 받았으므로 에러가 발생하지 X 20 | 21 | 22 | /* chain 을 하고 싶을 땐. */ 23 | wait(3).catch(e => { 24 | console.log('1st catch ', e); 25 | throw e; 26 | }) 27 | .catch(e => { 28 | console.log('1st catch ', e); 29 | }); 30 | -------------------------------------------------------------------------------- /chapter07/ex_passport/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 |
22 |
23 | 24 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/views/posts/new.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 |

Create A New Post

4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 | 14 |
15 | 16 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/views/posts/new.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 |

Create A New Post

4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 | 14 |
15 | 16 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter06/ws/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebSocket 8 | 9 | 10 |
ws 모듈로 웹 소켓을 알아봅시다.
11 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /chapter05/sequelize/models/customer.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, DataTypes) => { 2 | const newCustomer = sequelize.define("new_customer", { // 테이블 이름 3 | name: { // 컬럼 생성 4 | type: DataTypes.STRING(20), // 데이터 타입 정의 5 | allowNull: false // Null 허용 여부 정의 6 | }, 7 | age: { 8 | type: DataTypes.INTEGER, 9 | allowNull: false 10 | }, 11 | sex: { 12 | type: DataTypes.STRING(10), 13 | allowNull: false 14 | }, 15 | joined_date: { 16 | type: 'TIMESTAMP', 17 | defaultValue: sequelize.literal('CURRENT_TIMESTAMP'), 18 | allowNull: false 19 | }, 20 | }, { 21 | timestamps: false 22 | }); 23 | return newCustomer; 24 | }; 25 | -------------------------------------------------------------------------------- /chapter07/ejs/index2.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | app.set('port', process.env.PORT || 3000); 5 | app.set('views', __dirname + '/views') 6 | app.set('view engine', 'ejs'); 7 | 8 | /* GET home page. */ 9 | app.get('/', function (req, res, next) { 10 | res.render('index2', { menu: "Home" }); 11 | }); 12 | 13 | app.get('/menu1', function (req, res, next) { 14 | res.render('index2', { menu: "Menu1" }); 15 | }); 16 | 17 | app.get('/menu2', function (req, res, next) { 18 | res.render('index2', { menu: "Menu2" }); 19 | }); 20 | 21 | app.get('/menu3', function (req, res, next) { 22 | res.render('index2', { menu: "Menu3" }); 23 | }); 24 | 25 | 26 | app.listen(app.get('port'), () => { 27 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 28 | }); -------------------------------------------------------------------------------- /chapter02/solution/solution2-4.js: -------------------------------------------------------------------------------- 1 | // 문제 2 | 3 | // function work(sec, callback) { 4 | // setTimeout(() => { 5 | // callback(new Date().toISOString()); 6 | // }, sec*1000); 7 | // }; 8 | 9 | // work(1, (result) => { 10 | // console.log('첫번째 작업', result); 11 | 12 | // work(1, (result) => { 13 | // console.log('두번째 작업', result); 14 | // }); 15 | 16 | // }); 17 | 18 | 19 | 20 | // 정답 21 | function work(sec) { 22 | return new Promise((resolve, reject) => { 23 | setTimeout(() => { 24 | resolve(new Date().toISOString()); 25 | }, sec*1000); 26 | }); 27 | }; 28 | 29 | work(1).then((result) => { 30 | console.log('첫번째 작업', result); 31 | return work(1); 32 | }).then((result) => { 33 | console.log('두번째 작업', result); 34 | }); 35 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "facebook_clone", 3 | "version": "1.0.0", 4 | "description": "Facebook Clone Coding", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "npx nodemon app.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.19.0", 13 | "cloudinary": "^1.25.0", 14 | "connect-flash": "^0.1.1", 15 | "cookie-parser": "^1.4.5", 16 | "dotenv": "^8.2.0", 17 | "ejs": "^3.1.6", 18 | "express": "^4.17.1", 19 | "express-session": "^1.17.1", 20 | "mongoose": "^5.11.18", 21 | "multer": "^1.4.2", 22 | "passport": "^0.4.1", 23 | "passport-local": "^1.0.0", 24 | "passport-local-mongoose": "^6.1.0", 25 | "socket.io": "^3.1.2" 26 | }, 27 | "devDependencies": { 28 | "nodemon": "^2.0.7" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter05/sequelize/customer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 회원가입 8 | 9 | 10 |

정보를 입력하세요.

11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /chapter06/socket.io/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Socket.io 8 | 9 | 10 |
socket.io 모듈로 웹 소켓을 알아봅시다.
11 | 12 | 13 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /chapter04/sample/crwaling.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | const cheerio = require("cheerio"); 3 | 4 | const getHtml = async () => { 5 | try { 6 | return await axios.get("https://roadbook.co.kr/category/%EC%8B%A0%EA%B0%84%EC%86%8C%EA%B0%9C"); 7 | } catch (error) { 8 | console.error(error); 9 | } 10 | }; 11 | 12 | getHtml() 13 | .then(html => { 14 | let ulList = []; 15 | const $ = cheerio.load(html.data); 16 | const $bodyList = $("div#searchList ol").children("li"); 17 | 18 | $bodyList.each(function (i, elem) { 19 | ulList[i] = { 20 | bookList: $(this).find('a').text(), 21 | url: $(this).find('a').attr('href'), 22 | }; 23 | }); 24 | 25 | const data = ulList.filter(n => n.bookList); 26 | return data; 27 | }) 28 | .then(res => console.log(res)); 29 | -------------------------------------------------------------------------------- /chapter07/ejs/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.set('port', process.env.PORT || 3000); 6 | app.set('views', __dirname + '/views') 7 | app.set('view engine', 'ejs'); 8 | 9 | app.get('/', (req, res) => { 10 | res.render('index', { 11 | "People": 12 | [ 13 | { 14 | "name": "Gildong", 15 | "age": "15" 16 | }, 17 | { 18 | "name": "Jinsu", 19 | "age": "27" 20 | }, 21 | { 22 | "name": "Hyena", 23 | "age": "25" 24 | } 25 | ] 26 | , title: "Express" 27 | }); 28 | }); 29 | 30 | app.listen(app.get('port'), () => { 31 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 32 | }); -------------------------------------------------------------------------------- /chapter06/ws/socket.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require('ws'); // npm install -g ws@7.4.3 2 | 3 | module.exports = (server) => { 4 | const wss = new WebSocket.Server({ server }); 5 | 6 | wss.on('connection', (ws, req) => { // Connection Generate 7 | const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; 8 | console.log('New Client : ', ip); 9 | ws.on('message', (message) => { // 클라이언트로부터 메세지 10 | console.log(message); 11 | }); 12 | ws.on('error', (err) => { // 에러처리 13 | console.error(err); 14 | }); 15 | ws.on('close', () => { // 종료 16 | console.log('클라이언트 접속 해제', ip); 17 | clearInterval(ws.interval); 18 | }); 19 | 20 | ws.interval = setInterval(() => { // 서버에서 메세지 21 | if (ws.readyState === ws.OPEN) { 22 | ws.send('Message From Server.'); 23 | } 24 | }, 3000); 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /chapter04/sample/board_api_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Board API TEST 8 | 9 | 10 |
11 | 12 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /chapter05/mongoose/app.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | /* Connecting */ 4 | mongoose 5 | .connect("mongodb://127.0.0.1:27017/roadbook", { 6 | useNewUrlParser: true, 7 | useCreateIndex: true, 8 | }) 9 | .then(() => { 10 | console.log("Connected to MongoDB"); 11 | }) 12 | .catch((err) => { 13 | console.log(err); 14 | }); 15 | 16 | /* Defining Schema */ 17 | const customerSchema = mongoose.Schema({ 18 | name: 'string', 19 | age: 'number', 20 | sex: 'string' 21 | }, 22 | { 23 | collection: 'newCustomer' 24 | } 25 | ); 26 | 27 | /* Schema -> Model */ 28 | const Customer = mongoose.model('Schema', customerSchema); 29 | 30 | /* Generate Instance */ 31 | const customer1 = new Customer({ name: '홍길동', age: 30, sex: '남' }); 32 | 33 | /* Save Data into MongoDB */ 34 | customer1.save() 35 | .then(() => { 36 | console.log(customer1); 37 | }) 38 | .catch((err) => { 39 | console.log('Error : ' + err); 40 | }); -------------------------------------------------------------------------------- /chapter06/socket.io/socket.js: -------------------------------------------------------------------------------- 1 | const SocketIO = require("socket.io"); 2 | 3 | module.exports = (server) => { 4 | const io = SocketIO(server, { path: "/socket.io" }); // index.js의 path와 동일하게 5 | 6 | io.on("connection", (socket) => { 7 | const req = socket.request; 8 | const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; 9 | console.log( 10 | `New Client : ${ip}, socket.id : ${socket.id}` 11 | ); 12 | 13 | socket.on("disconnect", () => { 14 | console.log(`Client Out : ${ip}, socket.id : ${socket.id}`); 15 | clearInterval(socket.interval); 16 | }); 17 | 18 | socket.on("error", (error) => { }); 19 | 20 | socket.on("from client", (data) => { // 클라이언트가 넘긴 데이터 21 | console.log(data); 22 | }); 23 | 24 | socket.interval = setInterval(() => { // send 대신 emit으로 메세지를 보냄 25 | socket.emit("from server", "Message From Server"); 26 | }, 3000); 27 | }); 28 | }; -------------------------------------------------------------------------------- /chapter05/solution/solution5-2.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const models = require('./models'); 3 | 4 | const express = require('express'); 5 | const app = express(); 6 | 7 | /* 포트 설정 */ 8 | app.set('port', process.env.PORT || 8080); 9 | 10 | /* 공통 미들웨어 */ 11 | app.use(morgan('dev')); 12 | app.use(express.json()); 13 | app.use(express.urlencoded({ extended: true })); 14 | 15 | app.put('/customer/:id', async (req, res) => { 16 | const customer = await models.newCustomer.update({ 17 | where: { id: req.params.id }, 18 | name: body.name, 19 | age: body.age, 20 | sex: body.sex, 21 | }); 22 | return res.json(customer); 23 | }); 24 | 25 | app.delete('/customer/:id', async (req, res) => { 26 | const customer = await models.newCustomer.destroy({ 27 | where: { id: req.params.id }, 28 | }); 29 | return res.json(customer); 30 | }); 31 | 32 | /* 서버와 포트 연결.. */ 33 | app.listen(app.get('port'), () => { 34 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 35 | }); -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "facebook_clone", 3 | "version": "1.0.0", 4 | "description": "Facebook Clone Coding", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "dev": "cross-env NODE_ENV=production PORT=3000 node app.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.19.0", 14 | "cloudinary": "^1.25.0", 15 | "connect-flash": "^0.1.1", 16 | "cookie-parser": "^1.4.5", 17 | "cross-env": "^7.0.3", 18 | "csurf": "^1.11.0", 19 | "dotenv": "^8.2.0", 20 | "ejs": "^3.1.6", 21 | "express": "^4.17.1", 22 | "express-session": "^1.17.1", 23 | "helmet": "^4.4.1", 24 | "hpp": "^0.2.3", 25 | "mongoose": "^5.11.18", 26 | "morgan": "^1.10.0", 27 | "multer": "^1.4.2", 28 | "passport": "^0.4.1", 29 | "passport-local": "^1.0.0", 30 | "passport-local-mongoose": "^6.1.0", 31 | "sanitize-html": "^2.3.3", 32 | "socket.io": "^3.1.2", 33 | "winston": "^3.3.3" 34 | }, 35 | "devDependencies": { 36 | "nodemon": "^2.0.7" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chapter06/chat/app.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const express = require("express"); 3 | const app = express(); 4 | 5 | app.use(express.static(__dirname)); 6 | 7 | const server = http.Server(app); 8 | const io = require("socket.io")(server); 9 | let users = []; 10 | 11 | server.listen(8080, () => { 12 | console.log("8080포트에서 서버 실행 중..."); 13 | }); 14 | 15 | app.get("/", (req, res) => { 16 | res.sendFile(__dirname + "/index.html"); 17 | }); 18 | 19 | io.on("connection", (socket) => { 20 | let name = ""; 21 | socket.on("has connected", (username) => { // 이벤트 : has connected 22 | name = username; 23 | users.push(username); 24 | io.emit("has connected", { username: username, usersList: users }); 25 | }); 26 | 27 | socket.on("disconnect", () => { // 이벤트 : has disconnected 28 | users.splice(users.indexOf(name), 1); 29 | io.emit("has disconnected", { username: name, usersList: users }); 30 | }) 31 | 32 | socket.on("new message", (data) => { // 이벤트 : new message 33 | io.emit("new message", data); // 모든 소켓에 메세지를 보냄 34 | }); 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const passportLocalMongoose = require("passport-local-mongoose"); 3 | 4 | let UserSchema = new mongoose.Schema({ // 사용자 스키마 5 | username: String, 6 | firstName: String, 7 | lastName: String, 8 | password: String, 9 | profile: String, 10 | posts: [ 11 | { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: "Post" 14 | } 15 | ], 16 | 17 | liked_posts: [ 18 | { 19 | type: mongoose.Schema.Types.ObjectId, 20 | ref: "Post" 21 | } 22 | ], 23 | 24 | liked_comments: [ 25 | { 26 | type: mongoose.Schema.Types.ObjectId, 27 | ref: "Post" 28 | } 29 | ], 30 | friends: [ 31 | { 32 | type: mongoose.Schema.Types.ObjectId, 33 | ref: "User" 34 | } 35 | ], 36 | friendRequests: [ 37 | { 38 | type: mongoose.Schema.Types.ObjectId, 39 | ref: "User" 40 | } 41 | ] 42 | }); 43 | 44 | UserSchema.plugin(passportLocalMongoose); 45 | let User = mongoose.model("User", UserSchema); 46 | module.exports = User; 47 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const passportLocalMongoose = require("passport-local-mongoose"); 3 | 4 | let UserSchema = new mongoose.Schema({ // 사용자 스키마 5 | username: String, 6 | firstName: String, 7 | lastName: String, 8 | password: String, 9 | profile: String, 10 | posts: [ 11 | { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: "Post" 14 | } 15 | ], 16 | 17 | liked_posts: [ 18 | { 19 | type: mongoose.Schema.Types.ObjectId, 20 | ref: "Post" 21 | } 22 | ], 23 | 24 | liked_comments: [ 25 | { 26 | type: mongoose.Schema.Types.ObjectId, 27 | ref: "Post" 28 | } 29 | ], 30 | friends: [ 31 | { 32 | type: mongoose.Schema.Types.ObjectId, 33 | ref: "User" 34 | } 35 | ], 36 | friendRequests: [ 37 | { 38 | type: mongoose.Schema.Types.ObjectId, 39 | ref: "User" 40 | } 41 | ] 42 | }); 43 | 44 | UserSchema.plugin(passportLocalMongoose); 45 | let User = mongoose.model("User", UserSchema); 46 | module.exports = User; 47 | -------------------------------------------------------------------------------- /chapter05/sequelize/app2.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const models = require('./models'); 3 | 4 | const express = require('express'); 5 | const app = express(); 6 | 7 | /* 포트 설정 */ 8 | app.set('port', process.env.PORT || 8080); 9 | 10 | /* 공통 미들웨어 */ 11 | app.use(morgan('dev')); 12 | app.use(express.json()); 13 | app.use(express.urlencoded({ extended: true })); 14 | 15 | app.get('/', (req, res, next) => { // READ 16 | models.newCustomer.findAll() 17 | .then((customers) => { 18 | res.send(customers); 19 | }) 20 | .catch((err) => { 21 | console.error(err); 22 | next(err); 23 | }); 24 | }); 25 | 26 | app.get('/customer', (req, res) => { 27 | res.sendFile(__dirname + '/customer.html'); 28 | }); 29 | 30 | app.post('/customer', (req, res) => { // CREATE 31 | let body = req.body; 32 | 33 | models.newCustomer.create({ 34 | name: body.name, 35 | age: body.age, 36 | sex: body.sex, 37 | }).then(result => { 38 | console.log('customer created..!'); 39 | res.redirect('/customer'); 40 | }).catch(err => { 41 | console.log(err); 42 | }) 43 | }); 44 | 45 | /* 서버와 포트 연결.. */ 46 | app.listen(app.get('port'), () => { 47 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 48 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roadbook_nodejs", 3 | "version": "1.0.0", 4 | "description": "roadbook nodejs examples", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start:dev": "nodemon index.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/MinkyungPark/roadbook_nodejs.git" 13 | }, 14 | "author": "MinkyungPark", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/MinkyungPark/roadbook_nodejs/issues" 18 | }, 19 | "homepage": "https://github.com/MinkyungPark/roadbook_nodejs#readme", 20 | "dependencies": { 21 | "axios": "^0.21.1", 22 | "cookie-parser": "^1.4.5", 23 | "cors": "^2.8.5", 24 | "dotenv": "^8.2.0", 25 | "ejs": "^3.1.5", 26 | "express": "^4.17.1", 27 | "express-session": "^1.17.1", 28 | "mongoose": "^5.11.17", 29 | "morgan": "^1.10.0", 30 | "mysql2": "^2.2.5", 31 | "passport": "^0.4.1", 32 | "passport-local": "^1.0.0", 33 | "redis": "^3.0.2", 34 | "request": "^2.88.2", 35 | "sequelize": "^6.5.0", 36 | "sequelize-cli": "^6.2.0", 37 | "socket.io": "^3.1.1", 38 | "url": "^0.11.0", 39 | "uuid-apikey": "^1.5.1", 40 | "ws": "^7.4.3" 41 | }, 42 | "devDependencies": { 43 | "nodemon": "^2.0.7" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /chapter04/sample/airkorea_axios.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const axios = require('axios'); 3 | const express = require('express'); 4 | const app = express(); 5 | 6 | /* 포트 설정 */ 7 | app.set('port', process.env.PORT || 8080); 8 | 9 | /* 공통 미들웨어 */ 10 | app.use(morgan('dev')); 11 | app.use(express.json()); 12 | app.use(express.urlencoded({ extended: true })); 13 | 14 | /* 라우팅 설정 */ 15 | app.get('/airkorea', async (req, res) => { 16 | const serviceKey = "일반인증키(Encoding)"; 17 | const airUrl = "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty?"; 18 | 19 | let parmas = encodeURI('serviceKey') + '=' + serviceKey; 20 | parmas += '&' + encodeURI('numOfRows') + '=' + encodeURI('1'); 21 | parmas += '&' + encodeURI('pageNo') + '=' + encodeURI('1'); 22 | parmas += '&' + encodeURI('dataTerm') + '=' + encodeURI('DAILY'); 23 | parmas += '&' + encodeURI('ver') + '=' + encodeURI('1.3'); 24 | parmas += '&' + encodeURI('stationName') + '=' + encodeURI('마포구'); 25 | parmas += '&' + encodeURI('returnType') + '=' + encodeURI('json') 26 | 27 | const url = airUrl + parmas; 28 | 29 | try { 30 | const result = await axios.get(url); 31 | res.json(result.data); // .data 32 | } catch (error) { 33 | console.log(error); 34 | } 35 | }); 36 | 37 | /* 서버와 포트 연결.. */ 38 | app.listen(app.get('port'), () => { 39 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 40 | }); 41 | 42 | -------------------------------------------------------------------------------- /chapter06/ws/app.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const cookieParser = require('cookie-parser'); 3 | const session = require('express-session'); 4 | const express = require('express'); 5 | const app = express(); 6 | 7 | const webSocket = require('./socket.js'); 8 | 9 | /* 포트 설정 */ 10 | app.set('port', process.env.PORT || 8080); 11 | 12 | /* 공통 미들웨어 */ 13 | app.use(morgan('dev')); 14 | app.use(express.json()); 15 | app.use(express.urlencoded({ extended: true })); 16 | app.use(cookieParser('wsExample')); 17 | app.use(session({ 18 | resave: false, 19 | saveUninitialized: false, 20 | secret: 'wsExample', 21 | cookie: { 22 | httpOnly: true, 23 | secure: false 24 | } 25 | })); 26 | 27 | /* 라우터 설정 */ 28 | app.get('/', (req, res) => { 29 | res.sendFile(__dirname + '/index.html'); 30 | }); 31 | 32 | /* 404 에러처리 */ 33 | app.use((req, res, next) => { 34 | const error = new Error(`${req.method} ${req.url} 해당 주소가 없습니다.`); 35 | error.status = 404; 36 | next(error); 37 | }); 38 | 39 | /* 에러처리 미들웨어 */ 40 | app.use((err, req, res, next) => { 41 | res.locals.message = err.message; 42 | res.locals.error = process.env.NODE_ENV !== 'production' ? err : {}; 43 | res.status(err.status || 500); 44 | res.send('error Occurred'); 45 | }) 46 | 47 | /* 서버와 포트 연결.. */ 48 | const server = app.listen(app.get('port'), () => { 49 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 50 | }); 51 | 52 | webSocket(server); // ws와 http 포트 공유 -------------------------------------------------------------------------------- /chapter06/socket.io/app.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const cookieParser = require('cookie-parser'); 3 | const session = require('express-session'); 4 | const express = require('express'); 5 | const app = express(); 6 | 7 | const webSocket = require('./socket.js'); 8 | 9 | /* 포트 설정 */ 10 | app.set('port', process.env.PORT || 8080); 11 | 12 | /* 공통 미들웨어 */ 13 | app.use(morgan('dev')); 14 | app.use(express.json()); 15 | app.use(express.urlencoded({ extended: true })); 16 | app.use(cookieParser('wsExample')); 17 | app.use(session({ 18 | resave: false, 19 | saveUninitialized: false, 20 | secret: 'wsExample', 21 | cookie: { 22 | httpOnly: true, 23 | secure: false 24 | } 25 | })); 26 | 27 | /* 라우터 설정 */ 28 | app.get('/', (req, res) => { 29 | res.sendFile(__dirname + '/index.html'); 30 | }); 31 | 32 | /* 404 에러처리 */ 33 | app.use((req, res, next) => { 34 | const error = new Error(`${req.method} ${req.url} 해당 주소가 없습니다.`); 35 | error.status = 404; 36 | next(error); 37 | }); 38 | 39 | /* 에러처리 미들웨어 */ 40 | app.use((err, req, res, next) => { 41 | res.locals.message = err.message; 42 | res.locals.error = process.env.NODE_ENV !== 'production' ? err : {}; 43 | res.status(err.status || 500); 44 | res.send('error Occurred'); 45 | }) 46 | 47 | /* 서버와 포트 연결.. */ 48 | const server = app.listen(app.get('port'), () => { 49 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 50 | }); 51 | 52 | webSocket(server); // ws와 http 포트 공유 -------------------------------------------------------------------------------- /chapter04/sample/dotenv.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const dotenv = require('dotenv'); 3 | dotenv.config({ path: path.resolve(__dirname, "../../.env") }); 4 | const morgan = require('morgan'); 5 | const axios = require('axios'); 6 | const express = require('express'); 7 | const app = express(); 8 | 9 | /* 포트 설정 */ 10 | app.set('port', process.env.PORT); 11 | 12 | /* 공통 미들웨어 */ 13 | app.use(morgan('dev')); 14 | app.use(express.json()); 15 | app.use(express.urlencoded({ extended: true })); 16 | 17 | /* 라우팅 설정 */ 18 | app.get('/airkorea', async (req, res) => { 19 | const serviceKey = "자신의서비스키"; 20 | const airUrl = "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty?"; 21 | 22 | let parmas = encodeURI('serviceKey') + '=' + serviceKey; 23 | parmas += '&' + encodeURI('numOfRows') + '=' + encodeURI('1'); 24 | parmas += '&' + encodeURI('pageNo') + '=' + encodeURI('1'); 25 | parmas += '&' + encodeURI('dataTerm') + '=' + encodeURI('DAILY'); 26 | parmas += '&' + encodeURI('ver') + '=' + encodeURI('1.3'); 27 | parmas += '&' + encodeURI('stationName') + '=' + encodeURI('마포구'); 28 | parmas += '&' + encodeURI('returnType') + '=' + encodeURI('json') 29 | 30 | const url = airUrl + parmas; 31 | 32 | try { 33 | const result = await axios.get(url); 34 | res.json(result.data); // .data 35 | } catch (error) { 36 | console.log(error); 37 | } 38 | }); 39 | 40 | /* 서버와 포트 연결.. */ 41 | app.listen(app.get('port'), () => { 42 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 43 | }); 44 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/views/users/users.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 |
5 | <% users.forEach(function(person) { %> 6 |
7 | Profile 8 |
9 |

<%= person.firstName + " " + person.lastName %>

10 |

Test

11 | Profile 12 | <% if (!user._id.equals(person._id)) { %> 13 | 14 | <% if (user.friends.find(o => o.equals(person._id))) { %> 15 |

You and <%= person.firstName %> are friends

16 | 17 | <% } else if (user.friendRequests.find(o => o.equals(person._id))) { %> 18 |

<%= person.firstName %> has sent you a friend request. Go to your profile to accept it

19 | 20 | <% } else if (person.friendRequests.find(o => o.equals(user._id))) { %> 21 |

You already sent <%= person.firstName %> a friend request

22 | <% } else { %> 23 | Add Friend 24 | <% } %> 25 | <% } %> 26 |
27 |
28 | <% }); %> 29 |
30 | 31 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/views/users/users.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 |
5 | <% users.forEach(function(person) { %> 6 |
7 | Profile 8 |
9 |

<%= person.firstName + " " + person.lastName %>

10 |

Test

11 | Profile 12 | <% if (!user._id.equals(person._id)) { %> 13 | 14 | <% if (user.friends.find(o => o.equals(person._id))) { %> 15 |

You and <%= person.firstName %> are friends

16 | 17 | <% } else if (user.friendRequests.find(o => o.equals(person._id))) { %> 18 |

<%= person.firstName %> has sent you a friend request. Go to your profile to accept it

19 | 20 | <% } else if (person.friendRequests.find(o => o.equals(user._id))) { %> 21 |

You already sent <%= person.firstName %> a friend request

22 | <% } else { %> 23 | Add Friend 24 | <% } %> 25 | <% } %> 26 |
27 |
28 | <% }); %> 29 |
30 | 31 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter03/express/express_study8.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const morgan = require('morgan'); 3 | const cookieParser = require('cookie-parser'); 4 | const session = require('express-session'); 5 | const app = express(); 6 | 7 | /* 포트 설정 */ 8 | app.set('port', process.env.PORT || 8080); 9 | 10 | /* 공통 미들웨어 */ 11 | app.use(express.static(__dirname + '/public')); 12 | app.use(morgan('dev')); 13 | app.use(cookieParser('secret@1234')); // 암호화된 쿠키를 사용하기 위한 임의의 문자 전송 14 | app.use(session({ 15 | secret: 'secret@1234', // 암호화 16 | resave: false, // 새로운 요청시 세션에 변동 사항이 없어도 다시 저장할지 설정 17 | saveUninitialized: true, // 세션에 저장할 내용이 없어도 저장할지 설정 18 | cookie: { // 세션 쿠키 옵션 들 설정 httpOnly, expires, domain, path, secure, sameSite 19 | httpOnly: true, // 로그인 구현시 필수 적용, javascript로 접근 할 수 없게 하는 기능 20 | }, 21 | // name: 'connect.sid' // 세션 쿠키의 Name지정 default가 connect.sid 22 | })); 23 | app.use(express.json()); 24 | app.use(express.urlencoded({ extended: true })); 25 | 26 | /* 라우팅 설정 */ 27 | app.get('/', (req, res) => { 28 | if (req.session.name) { 29 | const output = ` 30 |

로그인한 사용자님


31 |

${req.session.name}님 안녕하세요.


32 | ` 33 | res.send(output); 34 | } else { 35 | const output = ` 36 |

로그인하지 않은 사용자입니다.


37 |

로그인 해주세요.


38 | ` 39 | res.send(output); 40 | } 41 | }); 42 | 43 | app.get('/login', (req, res) => { // 실제 구현시 post 44 | console.log(req.session); 45 | // 쿠키를 사용할 경우 쿠키에 값 세팅 46 | // res.cookie(name, value, options) 47 | // 세션 쿠키를 사용할 경우 48 | req.session.name = '로드북'; 49 | res.end('Login Ok') 50 | }); 51 | 52 | app.get('/logout', (req, res) => { 53 | res.clearCookie('connect.sid'); // 세션 쿠키 삭제 54 | res.end('Logout Ok'); 55 | }); 56 | 57 | /* 서버와 포트 연결.. */ 58 | app.listen(app.get('port'), () => { 59 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 60 | }); 61 | 62 | -------------------------------------------------------------------------------- /chapter04/sample/naver_request.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const request = require('request'); 3 | const express = require('express'); 4 | const app = express(); 5 | 6 | /* 포트 설정 */ 7 | app.set('port', process.env.PORT || 8080); 8 | 9 | /* 공통 미들웨어 */ 10 | app.use(morgan('dev')); 11 | app.use(express.json()); 12 | app.use(express.urlencoded({ extended: true })); 13 | 14 | /* 라우팅 설정 */ 15 | app.get('/naver/news', (req, res) => { 16 | const client_id = '발급받은 client id'; 17 | const client_secret = '발급받은 client secret'; 18 | const api_url = 'https://openapi.naver.com/v1/search/news?query=' + encodeURI('코스피'); //encodeURI(req.query.query); 19 | const option = { 20 | }; 21 | const options = { 22 | url: api_url, 23 | qs: option, 24 | headers: { 'X-Naver-Client-Id': client_id, 'X-Naver-Client-Secret': client_secret }, 25 | }; 26 | 27 | request.get(options, (error, response, body) => { 28 | if (!error && response.statusCode == 200) { 29 | let newsItem = JSON.parse(body).items; // items - title, link, description, pubDate 30 | 31 | const newsJson = { 32 | title: [], 33 | link: [], 34 | description: [], 35 | pubDate: [] 36 | } 37 | 38 | for (let i = 0; i < newsItem.length; i++) { 39 | newsJson.title.push(newsItem[i].title.replace(/(<([^>]+)>)|"|'/ig, "")); 40 | newsJson.link.push(newsItem[i].link); 41 | newsJson.description.push(newsItem[i].description.replace(/(<([^>]+)>)|"|'/ig, "")); 42 | newsJson.pubDate.push(newsItem[i].pubDate); 43 | } 44 | res.json(newsJson); 45 | } else { 46 | res.status(response.statusCode).end(); 47 | console.log('error = ' + response.statusCode); 48 | } 49 | }); 50 | }); 51 | 52 | /* 서버와 포트 연결.. */ 53 | app.listen(app.get('port'), () => { 54 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 55 | }); 56 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/views/posts/show.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 |
5 |
6 | 7 | 11 |
12 | <% if (post.image) { %> 13 |
14 | 15 |
16 | <% } %> 17 |

<%= post.content %>

18 | 19 | <% post.comments.forEach(comment => { %> 20 |
21 |

<%= comment.creator.firstName + " " + comment.creator.lastName %> - <%= comment.content %>

22 | <% if(user.liked_comments.find(o => o.equals(comment._id))) { %> 23 |

<%= comment.likes %> Likes

24 | <% } else { %> 25 | <%= comment.likes %> Likes 26 | <% } %> 27 |
28 | <% }) %> 29 |
30 |
31 | 32 | 33 | 34 |
35 |
36 |
37 | 38 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/views/posts/show.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 |
5 |
6 | 7 | 11 |
12 | <% if (post.image) { %> 13 |
14 | 15 |
16 | <% } %> 17 |

<%= post.content %>

18 | 19 | <% post.comments.forEach(comment => { %> 20 |
21 |

<%= comment.creator.firstName + " " + comment.creator.lastName %> - <%= comment.content %>

22 | <% if(user.liked_comments.find(o => o.equals(comment._id))) { %> 23 |

<%= comment.likes %> Likes

24 | <% } else { %> 25 | <%= comment.likes %> Likes 26 | <% } %> 27 |
28 | <% }) %> 29 |
30 |
31 | 32 | 33 | 34 |
35 |
36 |
37 | 38 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter04/sample/board_api.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | 3 | /* express app generate */ 4 | const express = require('express'); 5 | const app = express(); 6 | 7 | /* 포트 설정 */ 8 | app.set('port', process.env.PORT || 8080); 9 | 10 | /* 공통 미들웨어 */ 11 | app.use(morgan('dev')); 12 | app.use(express.json()); 13 | app.use(express.urlencoded({ extended: true })); 14 | 15 | /* 테스트를 위한 게시글 데이터 */ 16 | let boardList = []; 17 | let numOfBoard = 0; 18 | 19 | /* 라우팅 설정 */ 20 | app.get('/', (req, res) => { 21 | res.send('This is api.js'); 22 | }); 23 | 24 | /* 게시글 API */ 25 | app.get('/board', (req, res) => { 26 | res.send(boardList); 27 | }); 28 | 29 | app.post('/board', (req, res) => { 30 | const board = { 31 | "id": ++numOfBoard, 32 | "user_id": req.body.user_id, 33 | "date": new Date(), 34 | "title": req.body.title, 35 | "content": req.body.content 36 | }; 37 | boardList.push(board); 38 | 39 | res.redirect('/board'); 40 | }); 41 | 42 | app.put('/board/:id', (req, res) => { 43 | // req.params.id 값 찾아 리스트에서 삭제 44 | const findItem = boardList.find((item) => { 45 | return item.id == +req.params.id 46 | }); 47 | 48 | const idx = boardList.indexOf(findItem); 49 | boardList.splice(idx, 1); 50 | 51 | // 리스트에 새로운 요소 추가 52 | const board = { 53 | "id": +req.params.id, 54 | "user_id": req.body.user_id, 55 | "date": new Date(), 56 | "title": req.body.title, 57 | "content": req.body.content 58 | }; 59 | boardList.push(board); 60 | 61 | res.redirect('/board'); 62 | }); 63 | 64 | app.delete('/board/:id', (req, res) => { 65 | // req.params.id 값 찾아 리스트에서 삭제 66 | const findItem = boardList.find((item) => { 67 | return item.id == +req.params.id 68 | }); 69 | const idx = boardList.indexOf(findItem); 70 | boardList.splice(idx, 1); 71 | 72 | res.redirect('/board'); 73 | }); 74 | 75 | /* 서버와 포트 연결.. */ 76 | app.listen(app.get('port'), () => { 77 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 78 | }); 79 | 80 | //localhost:8080/board/3CCHAAM-C0ZMC86-QB498ZA-QRDE88V/search?keyword=호호 -------------------------------------------------------------------------------- /chapter05/sequelize/models/index.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | const env = process.env.NODE_ENV || 'development'; 4 | const config = require(__dirname + '/../config/config.json')[env]; 5 | const db = {}; 6 | 7 | let sequelize; 8 | if (config.use_env_variable) { 9 | sequelize = new Sequelize(process.env[config.use_env_variable], config); 10 | } else { 11 | sequelize = new Sequelize(config.database, config.username, config.password, config); 12 | } 13 | 14 | db.sequelize = sequelize; 15 | db.Sequelize = Sequelize; 16 | 17 | db.newCustomer = require('./customer')(sequelize, Sequelize); 18 | db.newPurchase = require('./purchase')(sequelize, Sequelize); 19 | 20 | db.newCustomer.hasMany(db.newPurchase, { foreignKey: 'customer_id', sourceKey: 'id' }); 21 | db.newPurchase.belongsTo(db.newCustomer, { foreignKey: 'customer_id', sourceKey: 'id' }); 22 | 23 | module.exports = db; 24 | 25 | 26 | 27 | // 'use strict'; 28 | 29 | // const fs = require('fs'); 30 | // const path = require('path'); 31 | // const Sequelize = require('sequelize'); 32 | // const basename = path.basename(__filename); 33 | // const env = process.env.NODE_ENV || 'development'; 34 | // const config = require(__dirname + '/../config/config.json')[env]; 35 | // const db = {}; 36 | 37 | // let sequelize; 38 | // if (config.use_env_variable) { 39 | // sequelize = new Sequelize(process.env[config.use_env_variable], config); 40 | // } else { 41 | // sequelize = new Sequelize(config.database, config.username, config.password, config); 42 | // } 43 | 44 | // fs 45 | // .readdirSync(__dirname) 46 | // .filter(file => { 47 | // return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 48 | // }) 49 | // .forEach(file => { 50 | // const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes); 51 | // db[model.name] = model; 52 | // }); 53 | 54 | // Object.keys(db).forEach(modelName => { 55 | // if (db[modelName].associate) { 56 | // db[modelName].associate(db); 57 | // } 58 | // }); 59 | 60 | // db.sequelize = sequelize; 61 | // db.Sequelize = Sequelize; 62 | 63 | // module.exports = db; 64 | 65 | 66 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/views/posts/index.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 | <% if (login) { %> 5 | <% var firstName = user.firstName %> 6 |

Hello <%= user.firstName %>

7 | <% } else { %> 8 |

Hello guest. Would you like to register or login?

9 | <% } %> 10 | 11 | <% if (posts) { %> 12 |
13 |
14 | <% posts.reverse().forEach(post => { %> 15 |
16 |
17 | 18 | 22 |
23 | <% if (post.image) { %> 24 |
25 | 26 |
27 | <% } %> 28 |
29 | <% if(user.liked_posts.find(o => o.equals(post._id))) { %> 30 |

<%= post.likes %> Likes

31 | <% } else { %> 32 | Like <%= post.likes %> 33 | <% } %> 34 |
35 | <% if (post.content.length > 30) { %> 36 | <%= post.content.substring(0, 30) %> 37 | <% } else { %> 38 | <%= post.content %> 39 | <% } %> 40 |
41 | <% }) %> 42 |
43 | 44 |
45 | <% } %> 46 | 47 | <% include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/views/posts/index.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 | <% if (login) { %> 5 | <% var firstName = user.firstName %> 6 |

Hello <%= user.firstName %>

7 | <% } else { %> 8 |

Hello guest. Would you like to register or login?

9 | <% } %> 10 | 11 | <% if (posts) { %> 12 |
13 |
14 | <% posts.reverse().forEach(post => { %> 15 |
16 |
17 | 18 | 22 |
23 | <% if (post.image) { %> 24 |
25 | 26 |
27 | <% } %> 28 |
29 | <% if(user.liked_posts.find(o => o.equals(post._id))) { %> 30 |

<%= post.likes %> Likes

31 | <% } else { %> 32 | Like <%= post.likes %> 33 | <% } %> 34 |
35 | <% if (post.content.length > 30) { %> 36 | <%= post.content.substring(0, 30) %> 37 | <% } else { %> 38 | <%= post.content %> 39 | <% } %> 40 |
41 | <% }) %> 42 |
43 | 44 |
45 | <% } %> 46 | 47 | <% include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter04/sample/airkorea_axios2.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const axios = require('axios'); 3 | const express = require('express'); 4 | const app = express(); 5 | 6 | /* 포트 설정 */ 7 | app.set('port', process.env.PORT || 8080); 8 | 9 | /* 공통 미들웨어 */ 10 | app.use(morgan('dev')); 11 | app.use(express.json()); 12 | app.use(express.urlencoded({ extended: true })); 13 | 14 | /* 라우팅 설정 */ 15 | app.get('/airkorea/detail', async (req, res) => { 16 | const serviceKey = ""; 17 | const airUrl = "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty?"; 18 | 19 | let parmas = encodeURI('serviceKey') + '=' + serviceKey; 20 | parmas += '&' + encodeURI('numOfRows') + '=' + encodeURI('1'); 21 | parmas += '&' + encodeURI('pageNo') + '=' + encodeURI('1'); 22 | parmas += '&' + encodeURI('dataTerm') + '=' + encodeURI('DAILY'); 23 | parmas += '&' + encodeURI('ver') + '=' + encodeURI('1.3'); 24 | parmas += '&' + encodeURI('stationName') + '=' + encodeURI('마포구'); 25 | parmas += '&' + encodeURI('returnType') + '=' + encodeURI('json') 26 | 27 | const url = airUrl + parmas; 28 | 29 | try { 30 | const result = await axios.get(url); 31 | const airItem = { 32 | //"location": result.data.ArpltnInforInqireSvcVo['stationName'], // stationName 을 응답 메시지로 보내주지 않습니다. (최근 변경) 33 | location: '마포구', //locaition을 직접 명시 34 | time: result.data.response.body.items[0]['dataTime'], // 시간대 35 | pm10: result.data.response.body.items[0]['pm10Value'], // pm10 수치 36 | pm25: result.data.response.body.items[0]['pm25Value'], // pm25 수치 37 | } 38 | const badAir = []; 39 | // pm10은 미세먼지 수치 40 | if (airItem.pm10 <= 30) { 41 | badAir.push("좋음😀"); 42 | } else if (airItem.pm10 > 30 && airItem.pm10 <= 80) { 43 | badAir.push("보통😐"); 44 | } else { 45 | badAir.push("나쁨😡"); 46 | } 47 | 48 | //pm25는 초미세먼지 수치 49 | if (airItem.pm25 <= 15) { 50 | badAir.push("좋음😀"); 51 | } else if (airItem.pm25 > 15 && airItem.pm10 <= 35) { 52 | badAir.push("보통😐"); 53 | } else { 54 | badAir.push("나쁨😡"); 55 | } 56 | 57 | res.send(`관측 지역: ${airItem.location} / 관측 시간: ${airItem.time}
58 | 미세먼지 ${badAir[0]} 초미세먼지 ${badAir[1]} 입니다.`); 59 | } catch (error) { 60 | console.log(error); 61 | } 62 | }); 63 | 64 | /* 서버와 포트 연결.. */ 65 | app.listen(app.get('port'), () => { 66 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 67 | }); 68 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/public/users/user.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | vertical-align: middle; 4 | position: relative; 5 | transition: 0.2s ease-in-out; 6 | } 7 | 8 | html, 9 | body { 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | .Profile { 15 | max-width: 1024px; 16 | margin: 0 auto; 17 | } 18 | 19 | .cover { 20 | height: 250px; 21 | background-size: cover; 22 | background-position: 0 center; 23 | } 24 | 25 | .midBar { 26 | background-color: #fefffe; 27 | height: 50px; 28 | } 29 | .midBar .personImg { 30 | background-position: center; 31 | width: 130px; 32 | height: 120px; 33 | background-size: 130px; 34 | border: 3px solid #fefffe; 35 | top: -100px; 36 | left: 5%; 37 | } 38 | .midBar .personImg .name { 39 | left: 120%; 40 | top: 50%; 41 | transform: translateY(-50%); 42 | } 43 | .midBar .personImg .name h2, 44 | .midBar .personImg .name h4 { 45 | margin: 0; 46 | /* color: #FEFFFE; */ 47 | color: #000; 48 | letter-spacing: 1px; 49 | } 50 | .midBar .personImg .name h4 { 51 | font-weight: normal; 52 | } 53 | 54 | .bottomBar { 55 | background-color: #fefffe; 56 | width: 40%; 57 | padding: 20px; 58 | margin-top: 10px; 59 | } 60 | .bottomBar p { 61 | font-size: 13px; 62 | color: #434347; 63 | } 64 | .bottomBar p .profileTitle { 65 | font-size: 20px; 66 | } 67 | .bottomBar i { 68 | width: 20px; 69 | } 70 | 71 | .mainBar { 72 | width: 60%; 73 | position: absolute; 74 | top: 0; 75 | left: 40%; 76 | } 77 | 78 | .post { 79 | border: 1px solid lightgrey; 80 | padding: 5px; 81 | border-radius: 4px; 82 | } 83 | 84 | .profile-image { 85 | width: 50px; 86 | height: 50px; 87 | background-size: 50px; 88 | background-position: center; 89 | } 90 | 91 | .post-image img { 92 | width: auto; 93 | height: auto; 94 | max-width: 550px; 95 | max-height: 350px; 96 | } 97 | 98 | .request { 99 | border: 1px solid black; 100 | } 101 | 102 | @media screen and (max-width: 800px) { 103 | .cover { 104 | height: 220px; 105 | } 106 | 107 | .midBar { 108 | height: 120px; 109 | } 110 | .midBar .personImg { 111 | left: 50%; 112 | transform: translateX(-50%); 113 | width: 150px; 114 | height: 150px; 115 | background-size: 150px; 116 | } 117 | .midBar .personImg .name { 118 | left: initial; 119 | top: 130%; 120 | } 121 | .midBar .personImg .name h2, 122 | .midBar .personImg .name h4 { 123 | color: #434347; 124 | text-align: center; 125 | } 126 | 127 | .bottomBar .mainBar { 128 | margin-top: initial; 129 | width: 40%; 130 | } 131 | .bottomBar p { 132 | border-top: 2px solid #dddddf; 133 | padding: 20px; 134 | font-size: 15px; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/public/users/user.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | vertical-align: middle; 4 | position: relative; 5 | transition: 0.2s ease-in-out; 6 | } 7 | 8 | html, 9 | body { 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | .Profile { 15 | max-width: 1024px; 16 | margin: 0 auto; 17 | } 18 | 19 | .cover { 20 | height: 250px; 21 | background-size: cover; 22 | background-position: 0 center; 23 | } 24 | 25 | .midBar { 26 | background-color: #fefffe; 27 | height: 50px; 28 | } 29 | .midBar .personImg { 30 | background-position: center; 31 | width: 130px; 32 | height: 120px; 33 | background-size: 130px; 34 | border: 3px solid #fefffe; 35 | top: -100px; 36 | left: 5%; 37 | } 38 | .midBar .personImg .name { 39 | left: 120%; 40 | top: 50%; 41 | transform: translateY(-50%); 42 | } 43 | .midBar .personImg .name h2, 44 | .midBar .personImg .name h4 { 45 | margin: 0; 46 | /* color: #FEFFFE; */ 47 | color: #000; 48 | letter-spacing: 1px; 49 | } 50 | .midBar .personImg .name h4 { 51 | font-weight: normal; 52 | } 53 | 54 | .bottomBar { 55 | background-color: #fefffe; 56 | width: 40%; 57 | padding: 20px; 58 | margin-top: 10px; 59 | } 60 | .bottomBar p { 61 | font-size: 13px; 62 | color: #434347; 63 | } 64 | .bottomBar p .profileTitle { 65 | font-size: 20px; 66 | } 67 | .bottomBar i { 68 | width: 20px; 69 | } 70 | 71 | .mainBar { 72 | width: 60%; 73 | position: absolute; 74 | top: 0; 75 | left: 40%; 76 | } 77 | 78 | .post { 79 | border: 1px solid lightgrey; 80 | padding: 5px; 81 | border-radius: 4px; 82 | } 83 | 84 | .profile-image { 85 | width: 50px; 86 | height: 50px; 87 | background-size: 50px; 88 | background-position: center; 89 | } 90 | 91 | .post-image img { 92 | width: auto; 93 | height: auto; 94 | max-width: 550px; 95 | max-height: 350px; 96 | } 97 | 98 | .request { 99 | border: 1px solid black; 100 | } 101 | 102 | @media screen and (max-width: 800px) { 103 | .cover { 104 | height: 220px; 105 | } 106 | 107 | .midBar { 108 | height: 120px; 109 | } 110 | .midBar .personImg { 111 | left: 50%; 112 | transform: translateX(-50%); 113 | width: 150px; 114 | height: 150px; 115 | background-size: 150px; 116 | } 117 | .midBar .personImg .name { 118 | left: initial; 119 | top: 130%; 120 | } 121 | .midBar .personImg .name h2, 122 | .midBar .personImg .name h4 { 123 | color: #434347; 124 | text-align: center; 125 | } 126 | 127 | .bottomBar .mainBar { 128 | margin-top: initial; 129 | width: 40%; 130 | } 131 | .bottomBar p { 132 | border-top: 2px solid #dddddf; 133 | padding: 20px; 134 | font-size: 15px; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /chapter04/sample/board_api2.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const url = require('url'); 3 | const uuidAPIkey = require('uuid-apikey'); 4 | 5 | /* express app generate */ 6 | const express = require('express'); 7 | const app = express(); 8 | 9 | /* 포트 설정 */ 10 | app.set('port', process.env.PORT || 8080); 11 | 12 | /* 공통 미들웨어 */ 13 | app.use(morgan('dev')); 14 | app.use(express.json()); 15 | app.use(express.urlencoded({ extended: true })); 16 | 17 | /* 테스트를 위한 API키 */ 18 | const key = { 19 | apiKey: '3CCHAAM-C0ZMC86-QB498ZA-QRDE88V', 20 | uuid: '1b19152a-603f-4620-bac8-947dbe1ae423' 21 | }; 22 | 23 | /* 테스트를 위한 게시글 데이터 */ 24 | let boardList = []; 25 | let numOfBoard = 0; 26 | 27 | /* 라우팅 설정 */ 28 | app.get('/', (req, res) => { 29 | res.send('This is api.js'); 30 | }); 31 | 32 | /* 게시글 API */ 33 | app.get('/board', (req, res) => { 34 | res.send(boardList); 35 | }); 36 | 37 | app.post('/board', (req, res) => { 38 | const board = { 39 | "id": ++numOfBoard, 40 | "user_id": req.body.user_id, 41 | "date": new Date(), 42 | "title": req.body.title, 43 | "content": req.body.content 44 | }; 45 | boardList.push(board); 46 | 47 | res.redirect('/board'); 48 | }); 49 | 50 | app.put('/board/:id', (req, res) => { 51 | // req.params.id 값 찾아 리스트에서 삭제 52 | const findItem = boardList.find((item) => { 53 | return item.id == +req.params.id 54 | }); 55 | 56 | const idx = boardList.indexOf(findItem); 57 | boardList.splice(idx, 1); 58 | 59 | // 리스트에 새로운 요소 추가 60 | const board = { 61 | "id": +req.params.id, 62 | "user_id": req.body.user_id, 63 | "date": new Date(), 64 | "title": req.body.title, 65 | "content": req.body.content 66 | }; 67 | boardList.push(board); 68 | 69 | res.redirect('/board'); 70 | }); 71 | 72 | app.delete('/board/:id', (req, res) => { 73 | // req.params.id 값 찾아 리스트에서 삭제 74 | const findItem = boardList.find((item) => { 75 | return item.id == +req.params.id 76 | }); 77 | const idx = boardList.indexOf(findItem); 78 | boardList.splice(idx, 1); 79 | 80 | res.redirect('/board'); 81 | }); 82 | 83 | /* 게시글 검색 API using uuid-key */ 84 | app.get('/board/:apikey/:type', (req, res) => { 85 | let { type, apikey } = req.params; 86 | const queryData = url.parse(req.url, true).query; 87 | 88 | if (uuidAPIkey.isAPIKey(apikey) && uuidAPIkey.check(apikey, key.uuid)) { 89 | if (type === 'search') { // 키워드로 게시글 검색 90 | const keyword = queryData.keyword; 91 | const result = boardList.filter((e) => { 92 | return e.title.includes(keyword) 93 | }) 94 | res.send(result); 95 | } 96 | else if (type === 'user') { // 닉네임으로 게시글 검색 97 | const user_id = queryData.user_id; 98 | const result = boardList.filter((e) => { 99 | return e.user_id === user_id; 100 | }); 101 | res.send(result); 102 | } 103 | else { 104 | res.send('Wrong URL') 105 | } 106 | } else { 107 | res.send('Wrong API Key'); 108 | } 109 | }); 110 | 111 | /* 서버와 포트 연결.. */ 112 | app.listen(app.get('port'), () => { 113 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 114 | }); -------------------------------------------------------------------------------- /chapter04/solution/solution4-1_redis_v3.js: -------------------------------------------------------------------------------- 1 | /* redis 모듈 버전 v3 일 때 */ 2 | const morgan = require('morgan'); 3 | const request = require('request'); 4 | const express = require('express'); 5 | const app = express(); 6 | 7 | /******* 레디스 연결 *******/ 8 | const redis = require('redis'); 9 | const client = redis.createClient(6379, '127.0.0.1'); 10 | client.on('error', (err) => { 11 | console.log('Redis Error : ' + err); 12 | }); 13 | 14 | /* 포트 설정 */ 15 | app.set('port', process.env.PORT || 8080); 16 | 17 | /* 공통 미들웨어 */ 18 | app.use(morgan('dev')); 19 | app.use(express.json()); 20 | app.use(express.urlencoded({ extended: true })); 21 | 22 | /* 라우팅 설정 */ 23 | app.get('/naver/news', async (req, res) => { /* async */ 24 | await client.lrange('naverNewsItems', 0, -1, async (err, cachedItems) => { 25 | if (err) throw err; 26 | /******* 데이터가 캐시되어 있을 경우 레디스에서 바로 로드 *******/ 27 | if (cachedItems.length) { 28 | res.send(` 데이터가 캐시에 있습니다.
${cachedItems}`); 29 | 30 | /******* 데이터가 캐시되어 있지 않을 경우 api call *******/ 31 | } else { 32 | const client_id = '발급받은 client id'; 33 | const client_secret = '발급받은 client secret'; 34 | const api_url = 'https://openapi.naver.com/v1/search/news?query=' + encodeURI('코스피'); //encodeURI(req.query.query); 35 | const option = { 36 | }; 37 | const options = { 38 | url: api_url, 39 | qs: option, 40 | headers: { 'X-Naver-Client-Id': client_id, 'X-Naver-Client-Secret': client_secret }, 41 | }; 42 | 43 | request.get(options, (error, response, body) => { 44 | if (!error && response.statusCode == 200) { 45 | let newsItem = JSON.parse(body).items; // items - title, link, description, pubDate 46 | 47 | const newsJson = { 48 | title: [], 49 | link: [], 50 | description: [], 51 | pubDate: [] 52 | } 53 | 54 | for (let i = 0; i < newsItem.length; i++) { 55 | newsJson.title.push(newsItem[i].title.replace(/(<([^>]+)>)|"/ig, "")); 56 | newsJson.link.push(newsItem[i].link); 57 | newsJson.description.push(newsItem[i].description.replace(/(<([^>]+)>)|"/ig, "")); 58 | newsJson.pubDate.push(newsItem[i].pubDate); 59 | } 60 | 61 | /******* api call 결과 redis에 저장 *******/ 62 | const newsItems = [newsJson.title, newsJson.link, newsJson.description, newsJson.pubDate] 63 | newsItems.forEach((val) => { 64 | client.rpush('naverNewsItems', val); 65 | }); 66 | client.expire('naverNewsItems', 60 * 60); 67 | res.send(`캐시된 데이터가 없습니다. 새로고침을 해주세요.`); 68 | } else { 69 | res.status(response.statusCode).end(); 70 | console.log('error = ' + response.statusCode); 71 | } 72 | }); 73 | } 74 | }) 75 | }); 76 | 77 | /* 서버와 포트 연결.. */ 78 | app.listen(app.get('port'), () => { 79 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 80 | }); 81 | -------------------------------------------------------------------------------- /chapter04/sample/board_api3.js: -------------------------------------------------------------------------------- 1 | const morgan = require('morgan'); 2 | const url = require('url'); 3 | const uuidAPIkey = require('uuid-apikey'); 4 | const cors = require('cors'); 5 | 6 | /* express app generate */ 7 | const express = require('express'); 8 | const app = express(); 9 | 10 | /* 포트 설정 */ 11 | app.set('port', process.env.PORT || 8080); 12 | 13 | /* 공통 미들웨어 */ 14 | app.use(morgan('dev')); 15 | app.use(express.json()); 16 | app.use(express.urlencoded({ extended: true })); 17 | app.use(cors()); 18 | 19 | /* 테스트를 위한 API키 */ 20 | const key = { 21 | apiKey: '3CCHAAM-C0ZMC86-QB498ZA-QRDE88V', 22 | uuid: '1b19152a-603f-4620-bac8-947dbe1ae423' 23 | }; 24 | 25 | /* 테스트를 위한 게시글 데이터 */ 26 | let boardList = []; 27 | let numOfBoard = 0; 28 | 29 | /* 라우팅 설정 */ 30 | app.get('/', (req, res) => { 31 | res.send('This is api.js'); 32 | }); 33 | 34 | /* 게시글 API */ 35 | app.get('/board', (req, res) => { 36 | res.send(boardList); 37 | }); 38 | 39 | app.post('/board', (req, res) => { 40 | const board = { 41 | "id": ++numOfBoard, 42 | "user_id": req.body.user_id, 43 | "date": new Date(), 44 | "title": req.body.title, 45 | "content": req.body.content 46 | }; 47 | boardList.push(board); 48 | 49 | res.redirect('/board'); 50 | }); 51 | 52 | app.put('/board/:id', (req, res) => { 53 | // req.params.id 값 찾아 리스트에서 삭제 54 | const findItem = boardList.find((item) => { 55 | return item.id == +req.params.id 56 | }); 57 | 58 | const idx = boardList.indexOf(findItem); 59 | boardList.splice(idx, 1); 60 | 61 | // 리스트에 새로운 요소 추가 62 | const board = { 63 | "id": +req.params.id, 64 | "user_id": req.params.user_id, 65 | "date": new Date(), 66 | "title": req.body.title, 67 | "content": req.body.content 68 | }; 69 | boardList.push(board); 70 | 71 | res.redirect('/board'); 72 | }); 73 | 74 | app.delete('/board/:id', (req, res) => { 75 | // req.params.id 값 찾아 리스트에서 삭제 76 | const findItem = boardList.find((item) => { 77 | return item.id == +req.params.id 78 | }); 79 | const idx = boardList.indexOf(findItem); 80 | boardList.splice(idx, 1); 81 | 82 | res.redirect('/board'); 83 | }); 84 | 85 | /* 게시글 검색 API using uuid-key */ 86 | app.get('/board/:apikey/:type', (req, res) => { 87 | let { type, apikey } = req.params; 88 | const queryData = url.parse(req.url, true).query; 89 | 90 | if (uuidAPIkey.isAPIKey(apikey) && uuidAPIkey.check(apikey, key.uuid)) { 91 | if (type === 'search') { // 키워드로 게시글 검색 92 | const keyword = queryData.keyword; 93 | const result = boardList.filter((e) => { 94 | return e.title.includes(keyword) 95 | }) 96 | res.send(result); 97 | } 98 | else if (type === 'user') { // 닉네임으로 게시글 검색 99 | const user_id = queryData.user_id; 100 | const result = boardList.filter((e) => { 101 | return e.user_id === user_id; 102 | }); 103 | res.send(result); 104 | } 105 | else { 106 | res.send('Wrong URL') 107 | } 108 | } else { 109 | res.send('Wrong API Key'); 110 | } 111 | }); 112 | 113 | /* 서버와 포트 연결.. */ 114 | app.listen(app.get('port'), () => { 115 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 116 | }); 117 | 118 | -------------------------------------------------------------------------------- /chapter04/solution/solution4-1_redis_v4.js: -------------------------------------------------------------------------------- 1 | /* redis 모듈 버전 v4 일 때 */ 2 | const morgan = require('morgan'); 3 | const request = require('request'); 4 | const express = require('express'); 5 | const app = express(); 6 | 7 | /******* 레디스 모듈 추가 *******/ 8 | const redis = require('redis'); 9 | 10 | /* 포트 설정 */ 11 | app.set('port', process.env.PORT || 8080); 12 | 13 | /* 공통 미들웨어 */ 14 | app.use(morgan('dev')); 15 | app.use(express.json()); 16 | app.use(express.urlencoded({ extended: true })); 17 | 18 | 19 | /* 라우팅 설정 */ 20 | app.get('/naver/news', async (req, res) => { 21 | await (async () => { 22 | /******* 레디스 연결 *******/ 23 | const client = redis.createClient(6379, '127.0.0.1'); 24 | client.on('error', (err) => console.log('Redis Client Error', err)); 25 | await client.connect(); 26 | 27 | const cachedItems = await client.lRange('naverNewsItems', 0, -1); 28 | 29 | /******* 데이터가 캐시되어 있을 경우 레디스에서 바로 로드 *******/ 30 | if (cachedItems.length) { 31 | res.send(` 데이터가 캐시에 있습니다.
${cachedItems}`); 32 | 33 | /******* 데이터가 캐시되어 있지 않을 경우 api call *******/ 34 | } else { 35 | const client_id = '발급받은 client id'; 36 | const client_secret = '발급받은 client secret'; 37 | const api_url = 'https://openapi.naver.com/v1/search/news?query=' + encodeURI('코스피'); //encodeURI(req.query.query); 38 | const option = { 39 | }; 40 | const options = { 41 | url: api_url, 42 | qs: option, 43 | headers: { 'X-Naver-Client-Id': client_id, 'X-Naver-Client-Secret': client_secret }, 44 | }; 45 | 46 | request.get(options, (error, response, body) => { 47 | if (!error && response.statusCode == 200) { 48 | let newsItem = JSON.parse(body).items; // items - title, link, description, pubDate 49 | const newsJson = { 50 | title: [], 51 | link: [], 52 | description: [], 53 | pubDate: [] 54 | } 55 | 56 | for (let i = 0; i < newsItem.length; i++) { 57 | newsJson.title.push(newsItem[i].title.replace(/(<([^>]+)>)|"/ig, "")); 58 | newsJson.link.push(newsItem[i].link); 59 | newsJson.description.push(newsItem[i].description.replace(/(<([^>]+)>)|"/ig, "")); 60 | newsJson.pubDate.push(newsItem[i].pubDate); 61 | } 62 | 63 | /******* api call 결과 redis에 저장 *******/ 64 | const newsItems = [newsJson.title, newsJson.link, newsJson.description, newsJson.pubDate] 65 | newsItems.forEach((val) => { 66 | client.rPush('naverNewsItems', val); // redis에 저장 67 | }); 68 | client.expire('naverNewsItems', 60 * 60); 69 | res.send(`캐시된 데이터가 없습니다. 새로고침을 해주세요.`); 70 | } else { 71 | res.status(response.statusCode).end(); 72 | console.log('error = ' + response.statusCode); 73 | } 74 | }); 75 | } 76 | })(); 77 | }); 78 | 79 | /* 서버와 포트 연결.. */ 80 | app.listen(app.get('port'), () => { 81 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 82 | }); 83 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const mongoose = require("mongoose"); 3 | const session = require("express-session"); 4 | const cookieParser = require('cookie-parser'); 5 | const passport = require("passport"); 6 | const LocalStrategy = require("passport-local"); 7 | const socket = require("socket.io"); 8 | const dotenv = require("dotenv"); 9 | const flash = require("connect-flash"); 10 | const Post = require("./models/Post"); 11 | const User = require("./models/User"); 12 | 13 | const port = process.env.PORT || 3000; 14 | const onlineChatUsers = {}; 15 | 16 | dotenv.config(); 17 | 18 | const postRoutes = require("./routes/posts"); 19 | const userRoutes = require("./routes/users"); 20 | const app = express(); 21 | 22 | app.set("view engine", "ejs"); 23 | 24 | /* Middleware */ 25 | app.use(cookieParser(process.env.SECRET)) 26 | app.use(session({ 27 | secret: process.env.SECRET, 28 | resave: false, 29 | saveUninitialized: false 30 | }) 31 | ); 32 | app.use(flash()); 33 | 34 | /* Passport setup */ 35 | app.use(passport.initialize()); 36 | app.use(passport.session()); 37 | passport.use(new LocalStrategy(User.authenticate())); 38 | passport.serializeUser(User.serializeUser()); 39 | passport.deserializeUser(User.deserializeUser()); 40 | 41 | /* Middleware */ 42 | app.use(express.json()); 43 | app.use(express.urlencoded({ extended: true })); 44 | app.use(express.static("public")); 45 | 46 | /* MongoDB Connection */ 47 | mongoose 48 | .connect("mongodb://127.0.0.1:27017/facebook_clone", { 49 | useNewUrlParser: true, 50 | useCreateIndex: true, 51 | useUnifiedTopology: true 52 | }) 53 | .then(() => { 54 | console.log("Connected to MongoDB"); 55 | }) 56 | .catch((err) => { 57 | console.log(err); 58 | }); 59 | 60 | /* Template 파일에 변수 전송 */ 61 | app.use((req, res, next) => { 62 | res.locals.user = req.user; 63 | res.locals.login = req.isAuthenticated(); 64 | res.locals.error = req.flash("error"); 65 | res.locals.success = req.flash("success"); 66 | next(); 67 | }); 68 | 69 | /* Routers */ 70 | app.use("/", userRoutes); 71 | app.use("/", postRoutes); 72 | 73 | const server = app.listen(port, () => { 74 | console.log("App is running on port " + port); 75 | }); 76 | 77 | /* WebSocket setup */ 78 | const io = socket(server); 79 | 80 | const room = io.of("/chat"); 81 | room.on("connection", socket => { 82 | console.log("new user : ", socket.id); 83 | 84 | room.emit("newUser", { socketID: socket.id }); 85 | 86 | socket.on("newUser", data => { 87 | if (!(data.name in onlineChatUsers)) { 88 | onlineChatUsers[data.name] = data.socketID; 89 | socket.name = data.name; 90 | room.emit("updateUserList", Object.keys(onlineChatUsers)); 91 | console.log("Online users: " + Object.keys(onlineChatUsers)); 92 | } 93 | }); 94 | 95 | socket.on("disconnect", () => { 96 | delete onlineChatUsers[socket.name]; 97 | room.emit("updateUserList", Object.keys(onlineChatUsers)); 98 | console.log(`user ${socket.name} disconnected`); 99 | }); 100 | 101 | socket.on("chat", data => { 102 | console.log(data); 103 | if (data.to === "Global Chat") { 104 | room.emit("chat", data); 105 | } else if (data.to) { 106 | room.to(onlineChatUsers[data.name]).emit("chat", data); 107 | room.to(onlineChatUsers[data.to]).emit("chat", data); 108 | } 109 | }); 110 | }); -------------------------------------------------------------------------------- /chapter07/facebook-clone/views/partials/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Facebook Clone 5 | 6 | 7 | 8 | 9 | 10 | 58 | 59 |
60 | <% if(error && error.length > 0) { %> 61 | 64 | <% } %> 65 | <% if(success && success.length > 0) { %> 66 | 69 | <% } %> -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/views/partials/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Facebook Clone 5 | 6 | 7 | 8 | 9 | 10 | 58 | 59 |
60 | <% if(error && error.length > 0) { %> 61 | 64 | <% } %> 65 | <% if(success && success.length > 0) { %> 66 | 69 | <% } %> -------------------------------------------------------------------------------- /chapter04/sample/redis3.js: -------------------------------------------------------------------------------- 1 | /* redis version 3.1.2 */ 2 | /* npm install redis@^3.1.2 */ 3 | 4 | const path = require('path'); 5 | const dotenv = require('dotenv'); 6 | dotenv.config({ path: path.resolve(__dirname, "../../.env") }); const morgan = require('morgan'); 7 | const axios = require('axios'); 8 | 9 | /* express app generate */ 10 | const express = require('express'); 11 | const app = express(); 12 | 13 | /* redis connect */ 14 | const redis = require('redis'); 15 | const client = redis.createClient(6379, '127.0.0.1'); 16 | client.on('error', (err) => { 17 | console.log('Redis Error : ' + err); 18 | }); 19 | 20 | /* 포트 설정 */ 21 | app.set('port', process.env.PORT); 22 | 23 | /* 공통 미들웨어 */ 24 | app.use(morgan('dev')); 25 | app.use(express.json()); 26 | app.use(express.urlencoded({ extended: true })); 27 | 28 | /* 라우팅 설정 */ 29 | app.get('/airkorea', async (req, res) => { 30 | await client.lrange('airItems', 0, -1, async (err, cachedItems) => { 31 | if (err) throw err; 32 | if (cachedItems.length) { // data in cache 33 | res.send(` 데이터가 캐시에 있습니다.
34 | 관측 지역: ${cachedItems[0]} / 관측 시간: ${cachedItems[1]}
35 | 미세먼지 ${cachedItems[2]} 초미세먼지 ${cachedItems[3]} 입니다.`); 36 | 37 | } else { // data not in cache 38 | const serviceKey = process.env.airServiceKey; 39 | const airUrl = "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty?"; 40 | 41 | let parmas = encodeURI('serviceKey') + '=' + serviceKey; 42 | parmas += '&' + encodeURI('numOfRows') + '=' + encodeURI('1'); 43 | parmas += '&' + encodeURI('pageNo') + '=' + encodeURI('1'); 44 | parmas += '&' + encodeURI('dataTerm') + '=' + encodeURI('DAILY'); 45 | parmas += '&' + encodeURI('ver') + '=' + encodeURI('1.3'); 46 | parmas += '&' + encodeURI('stationName') + '=' + encodeURI('마포구'); 47 | parmas += '&' + encodeURI('returnType') + '=' + encodeURI('json') 48 | 49 | const url = airUrl + parmas; 50 | 51 | try { 52 | const result = await axios.get(url); 53 | const airItem = { 54 | "location": result.data.ArpltnInforInqireSvcVo["stationName"], // 지역 55 | "time": result.data.list[0]['dataTime'], // 시간대 56 | "pm10": result.data.list[0]['pm10Value'], // pm10 수치 57 | "pm25": result.data.list[0]['pm25Value'] // pm25 수치 58 | } 59 | const badAir = []; 60 | // pm10은 미세먼지 수치 61 | if (airItem.pm10 <= 30) { 62 | badAir.push("좋음😀"); 63 | } else if (pm10 > 30 && pm10 <= 80) { 64 | badAir.push("보통😐"); 65 | } else { 66 | badAir.push("나쁨😡"); 67 | } 68 | 69 | //pm25는 초미세먼지 수치 70 | if (airItem.pm25 <= 15) { 71 | badAir.push("좋음😀"); 72 | } else if (pm25 > 15 && pm10 <= 35) { 73 | badAir.push("보통😐"); 74 | } else { 75 | badAir.push("나쁨😡"); 76 | } 77 | 78 | const airItems = [airItem.location, airItem.time, badAir[0], badAir[1]]; 79 | airItems.forEach((val) => { 80 | client.rpush('airItems', val); // redis에 저장 81 | }); 82 | client.expire('airItems', 60 * 60); 83 | 84 | res.send(`캐시된 데이터가 없습니다.`); 85 | } catch (error) { 86 | console.log(error); 87 | } 88 | } 89 | }) 90 | }); 91 | 92 | /* 서버와 포트 연결.. */ 93 | app.listen(app.get('port'), () => { 94 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 95 | }); 96 | -------------------------------------------------------------------------------- /chapter07/ex_passport/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const morgan = require('morgan'); 3 | const cookieParser = require('cookie-parser'); 4 | const session = require('express-session'); 5 | const passport = require('passport'); 6 | const LocalStrategy = require('passport-local').Strategy; 7 | 8 | const app = express(); 9 | 10 | /* 포트 설정 */ 11 | app.set('port', process.env.PORT || 8080); 12 | 13 | /* 가상 데이터 */ 14 | let fakeUser = { 15 | username: 'test@test.com', 16 | password: 'test@1234' 17 | } 18 | 19 | /* 공통 미들웨어 */ 20 | app.use(morgan('dev')); 21 | app.use(express.json()); 22 | app.use(express.urlencoded({ extended: true })); 23 | app.use(cookieParser('passportExample')); 24 | app.use(session({ 25 | resave: false, 26 | saveUninitialized: false, 27 | secret: 'passportExample', 28 | cookie: { 29 | httpOnly: true, 30 | secure: false 31 | } 32 | })); 33 | 34 | /* passport middleware */ 35 | app.use(passport.initialize()); // passport 초기화 36 | app.use(passport.session()); // passport session 연동 37 | 38 | // 세션 처리 - 로그인에 성공했을 경우 딱 한번 호출되어 사용자의 식별자를 session에 저장 39 | passport.serializeUser(function (user, done) { 40 | console.log('serializeUser', user); 41 | done(null, user.username); 42 | }); 43 | 44 | // 세션 처리 - 로그인 후 페이지 방문 마다 사용자의 실제 데이터 주입 45 | passport.deserializeUser(function (id, done) { 46 | console.log('deserializeUser', id); 47 | done(null, fakeUser); // req.user에 전달 48 | }); 49 | 50 | passport.use(new LocalStrategy( 51 | function (username, password, done) { 52 | if (username === fakeUser.username) { // username OK 53 | if (password === fakeUser.password) { // password OK 54 | return done(null, fakeUser); 55 | } else { 56 | return done(null, false, { message: "password incorrect" }); 57 | } 58 | } else { 59 | return done(null, false, { message: "username incorrect" }); 60 | } 61 | } 62 | )); 63 | 64 | /* 라우터 설정 */ 65 | app.get('/', (req, res) => { 66 | if (!req.user) { // 로그인 아직 하지 않았을 때 67 | res.sendFile(__dirname + '/index.html'); 68 | } else { // 로그인 성공시 세션에 req.user 저장 69 | const user = req.user.username; 70 | const html = ` 71 | 72 | 73 | 74 | 75 | 76 | Document 77 | 78 | 79 |

${user}님 안녕하세요!

80 | 81 | 82 | 83 | ` 84 | res.send(html); 85 | } 86 | }); 87 | 88 | /* Passport Login : Strategy-Local */ 89 | /* Authenticate Requests */ 90 | app.post('/login', 91 | passport.authenticate('local', { failureRedirect: '/' }), 92 | function (req, res) { 93 | res.send('Login success..!') 94 | }); 95 | 96 | app.get('/logout', function (req, res) { 97 | req.logout(); 98 | res.redirect('/'); 99 | }); 100 | 101 | /* 404 에러처리 */ 102 | app.use((req, res, next) => { 103 | const error = new Error(`${req.method} ${req.url} 해당 주소가 없습니다.`); 104 | error.status = 404; 105 | next(error); 106 | }); 107 | 108 | /* 에러처리 미들웨어 */ 109 | app.use((err, req, res, next) => { 110 | res.locals.message = err.message; 111 | res.locals.error = process.env.NODE_ENV !== 'development' ? err : {}; 112 | res.status(err.status || 500); 113 | res.send('error Occurred'); 114 | }); 115 | 116 | /* 서버와 포트 연결.. */ 117 | app.listen(app.get('port'), () => { 118 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 119 | }); -------------------------------------------------------------------------------- /chapter06/chat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 실시간 채팅 13 | 14 | 15 |
16 |

Username 등록

17 |
18 |
19 | 25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 |

Online users:

33 |
34 |
35 |
    36 |
    37 |
    38 | 39 |
    40 |
    41 |
      42 |
      43 | 44 |
      45 |
      46 | 52 | 53 |
      54 |
      55 |
      56 |
      57 | 58 | 59 | 117 | 118 | -------------------------------------------------------------------------------- /chapter04/sample/redis3_v4.js: -------------------------------------------------------------------------------- 1 | /* if redis version over 4 */ 2 | 3 | const path = require('path'); 4 | const dotenv = require('dotenv'); 5 | dotenv.config({ path: path.resolve(__dirname, "../../.env") }); const morgan = require('morgan'); 6 | const axios = require('axios'); 7 | 8 | /* express app generate */ 9 | const express = require('express'); 10 | const app = express(); 11 | 12 | /* redis connect */ 13 | const redis = require('redis'); 14 | 15 | /* 포트 설정 */ 16 | app.set('port', process.env.PORT); 17 | 18 | /* 공통 미들웨어 */ 19 | app.use(morgan('dev')); 20 | app.use(express.json()); 21 | app.use(express.urlencoded({ extended: true })); 22 | 23 | /* 라우팅 설정 */ 24 | app.get('/airkorea', async (req, res) => { 25 | await (async () => { 26 | /******* 레디스 연결 *******/ 27 | const client = redis.createClient(6379, '127.0.0.1'); 28 | client.on('error', (err) => console.log('Redis Client Error', err)); 29 | await client.connect(); 30 | 31 | const cachedItems = await client.lRange('airItems', 0, -1); 32 | 33 | if (cachedItems.length) { // data in cache 34 | res.send(` 데이터가 캐시에 있습니다.
      35 | 관측 지역: ${cachedItems[0]} / 관측 시간: ${cachedItems[1]}
      36 | 미세먼지 ${cachedItems[2]} 초미세먼지 ${cachedItems[3]} 입니다.`); 37 | 38 | } else { // data not in cache 39 | const serviceKey = process.env.airServiceKey; 40 | const airUrl = "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty?"; 41 | 42 | let parmas = encodeURI('serviceKey') + '=' + serviceKey; 43 | parmas += '&' + encodeURI('numOfRows') + '=' + encodeURI('1'); 44 | parmas += '&' + encodeURI('pageNo') + '=' + encodeURI('1'); 45 | parmas += '&' + encodeURI('dataTerm') + '=' + encodeURI('DAILY'); 46 | parmas += '&' + encodeURI('ver') + '=' + encodeURI('1.3'); 47 | parmas += '&' + encodeURI('stationName') + '=' + encodeURI('마포구'); 48 | parmas += '&' + encodeURI('returnType') + '=' + encodeURI('json') 49 | 50 | const url = airUrl + parmas; 51 | 52 | try { 53 | const result = await axios.get(url); 54 | const airItem = { 55 | "location": result.data.ArpltnInforInqireSvcVo["stationName"], // 지역 56 | "time": result.data.list[0]['dataTime'], // 시간대 57 | "pm10": result.data.list[0]['pm10Value'], // pm10 수치 58 | "pm25": result.data.list[0]['pm25Value'] // pm25 수치 59 | } 60 | const badAir = []; 61 | // pm10은 미세먼지 수치 62 | if (airItem.pm10 <= 30) { 63 | badAir.push("좋음😀"); 64 | } else if (pm10 > 30 && pm10 <= 80) { 65 | badAir.push("보통😐"); 66 | } else { 67 | badAir.push("나쁨😡"); 68 | } 69 | 70 | //pm25는 초미세먼지 수치 71 | if (airItem.pm25 <= 15) { 72 | badAir.push("좋음😀"); 73 | } else if (pm25 > 15 && pm10 <= 35) { 74 | badAir.push("보통😐"); 75 | } else { 76 | badAir.push("나쁨😡"); 77 | } 78 | 79 | const airItems = [airItem.location, airItem.time, badAir[0], badAir[1]]; 80 | airItems.forEach((val) => { 81 | client.rPush('airItems', val); // redis에 저장 82 | }); 83 | client.expire('airItems', 60 * 60); 84 | res.send(`캐시된 데이터가 없습니다. 새로고침을 해주세요.`); 85 | } catch (error) { 86 | console.log(error); 87 | } 88 | } 89 | })(); 90 | }); 91 | 92 | /* 서버와 포트 연결.. */ 93 | app.listen(app.get('port'), () => { 94 | console.log(app.get('port'), '번 포트에서 서버 실행 중 ..') 95 | }); 96 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/views/users/user.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 |
      5 |
      6 |
      7 |
      8 |
      9 |

      <%= userData.firstName + " " + userData.lastName %>

      10 |
      11 |
      12 |
      13 |
      14 |
      15 |

      16 | <% if (userData._id.equals(user._id)) { %> 17 | <% if (userData.friendRequests.length > 0) { %> 18 | <% userData.friendRequests.forEach(request => { %> 19 |

      20 |
      Request from <%= request.firstName + " " + request.lastName %>
      21 | Accept 22 | Decline 23 |
      24 |
      25 | <% }) %> 26 | <% } %> 27 | <% } %> 28 | <%= userData.firstName %>'s friends 29 |
      30 | <% userData.friends.forEach(friend => { %> 31 | <%= friend.firstName + " " + friend.lastName %> 32 |
      33 | <% }) %> 34 |

      35 |
      36 |
      37 | <% userData.posts.forEach(post => { %> 38 |
      39 |
      40 | 41 | 45 |
      46 |
      47 | <% if (post.image) { %> 48 |
      49 | 50 |
      51 | <% } %> 52 | <% if(user.liked_posts.find(o => o.equals(post._id))) { %> 53 |

      <%= post.likes %> Likes

      54 | <% } else { %> 55 | Like <%= post.likes %> 56 | <% } %> 57 |
      58 |

      <%= post.content %>

      59 |
      60 |
      61 |
      62 | <% }) %> 63 |
      64 |
      65 |
      66 | 67 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/views/users/user.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 |
      5 |
      6 |
      7 |
      8 |
      9 |

      <%= userData.firstName + " " + userData.lastName %>

      10 |
      11 |
      12 |
      13 |
      14 |
      15 |

      16 | <% if (userData._id.equals(user._id)) { %> 17 | <% if (userData.friendRequests.length > 0) { %> 18 | <% userData.friendRequests.forEach(request => { %> 19 |

      20 |
      Request from <%= request.firstName + " " + request.lastName %>
      21 | Accept 22 | Decline 23 |
      24 |
      25 | <% }) %> 26 | <% } %> 27 | <% } %> 28 | <%= userData.firstName %>'s friends 29 |
      30 | <% userData.friends.forEach(friend => { %> 31 | <%= friend.firstName + " " + friend.lastName %> 32 |
      33 | <% }) %> 34 |

      35 |
      36 |
      37 | <% userData.posts.forEach(post => { %> 38 |
      39 |
      40 | 41 | 45 |
      46 |
      47 | <% if (post.image) { %> 48 |
      49 | 50 |
      51 | <% } %> 52 | <% if(user.liked_posts.find(o => o.equals(post._id))) { %> 53 |

      <%= post.likes %> Likes

      54 | <% } else { %> 55 | Like <%= post.likes %> 56 | <% } %> 57 |
      58 |

      <%= post.content %>

      59 |
      60 |
      61 |
      62 | <% }) %> 63 |
      64 |
      65 |
      66 | 67 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter06/chat/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | width: 100%; 4 | margin: 0; 5 | padding: 0; 6 | font-family: Verdana, Geneva, Tahoma, sans-serif; 7 | } 8 | 9 | body { 10 | height: 100%; 11 | width: 100%; 12 | margin: 0; 13 | padding: 0; 14 | overflow: hidden; 15 | } 16 | 17 | #login-area { 18 | width: 500px; 19 | height: 300px; 20 | border: 1px solid #000; 21 | margin: 0 auto; 22 | margin-top: 150px; 23 | } 24 | 25 | #login-area #login-text { 26 | width: 100%; 27 | margin-top: 70px; 28 | text-align: center; 29 | } 30 | 31 | #login-area #login-form-area { 32 | width: 100%; 33 | margin-top: 40px; 34 | text-align: center; 35 | } 36 | 37 | #login-area #login-form-area #login-form #user-name { 38 | height: 50px; 39 | width: 300px; 40 | padding: 0px 5px; 41 | font-size: 26px; 42 | outline: none; 43 | outline-offset: 0; 44 | } 45 | 46 | #login-area #login-form-area #login-form #login-submit { 47 | font-size: 26px; 48 | background-color: transparent; 49 | border: 1px solid #000; 50 | height: 52px; 51 | width: 100px; 52 | cursor: pointer; 53 | outline: none; 54 | outline-offset: 0; 55 | } 56 | 57 | #login-area #login-form-area #login-form #login-submit:hover { 58 | background-color: #000; 59 | color: #fff; 60 | } 61 | 62 | #chat-area { 63 | display: none; 64 | widows: 100%; 65 | height: 100%; 66 | } 67 | 68 | #chat-area #users-area { 69 | width: 20%; 70 | height: 100%; 71 | border-top: 1px solid transparent; 72 | border-right: 1px solid black; 73 | float: left; 74 | background-color: #ddd; 75 | } 76 | 77 | #chat-area #users-area #online-users-text { 78 | margin: 0; 79 | padding: 0; 80 | margin-top: 50px; 81 | text-align: center; 82 | } 83 | 84 | #chat-area #users-area #online-users-text h1 { 85 | margin: 0; 86 | padding: 0; 87 | } 88 | 89 | #chat-area #users-area #online-users { 90 | margin-top: 50px; 91 | } 92 | 93 | #chat-area #users-area #online-users #users { 94 | text-align: center; 95 | padding: 0; 96 | margin: 0; 97 | } 98 | 99 | #chat-area #users-area #online-users #users li { 100 | list-style-type: none; 101 | font-weight: bold; 102 | margin-top: 10px; 103 | } 104 | 105 | #chat-area #message-area { 106 | width: calc(80% - 1px); 107 | height: 100%; 108 | float: right; 109 | background-color: #aaa; 110 | } 111 | 112 | #chat-area #message-area #display-message-area { 113 | width: 100%; 114 | height: 90%; 115 | border-top: 1px solid transparent; 116 | } 117 | 118 | #chat-area #message-area #display-message-area #messages { 119 | margin: 0; 120 | padding: 0; 121 | } 122 | 123 | #chat-area #message-area #display-message-area #messages li { 124 | list-style-type: none; 125 | padding: 10px; 126 | margin: 0; 127 | } 128 | 129 | #chat-area #message-area #display-message-area #messages li:nth-child(odd) { 130 | background-color: #eee; 131 | } 132 | 133 | #chat-area #message-area #message-form-area { 134 | width: 100%; 135 | height: 10%; 136 | border-top: 1px solid black; 137 | } 138 | #chat-area #message-area #message-form-area #message-form #message { 139 | height: 90px; 140 | width: 90%; 141 | font-size: 28px; 142 | padding: 0 5px; 143 | outline: none; 144 | outline-offset: 0; 145 | background-color: transparent; 146 | color: #fff; 147 | border: 0; 148 | } 149 | 150 | #chat-area 151 | #message-area 152 | #message-form-area 153 | #message-form 154 | #message::placeholder { 155 | color: #fff; 156 | } 157 | 158 | #chat-area #message-area #message-form-area #message-form #message-submit { 159 | height: 95px; 160 | width: calc(10% - 20px); 161 | border: 1px solid #000; 162 | font-size: 28px; 163 | background-color: transparent; 164 | } 165 | 166 | #chat-area 167 | #message-area 168 | #message-form-area 169 | #message-form 170 | #message-submit:hover { 171 | color: #fff; 172 | background-color: #000; 173 | } 174 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const morgan = require('morgan') 3 | const winston = require('./config/winston') 4 | const mongoose = require("mongoose"); 5 | const session = require("express-session"); 6 | const cookieParser = require('cookie-parser'); 7 | const passport = require("passport"); 8 | const LocalStrategy = require("passport-local"); 9 | const socket = require("socket.io"); 10 | const dotenv = require("dotenv"); 11 | const flash = require("connect-flash"); 12 | const Post = require("./models/Post"); 13 | const User = require("./models/User"); 14 | const helmet = require('helmet'); 15 | const hpp = require('hpp'); 16 | 17 | const port = process.env.PORT || 3000; 18 | const onlineChatUsers = {}; 19 | 20 | dotenv.config(); 21 | 22 | const postRoutes = require("./routes/posts"); 23 | const userRoutes = require("./routes/users"); 24 | const app = express(); 25 | 26 | app.set("view engine", "ejs"); 27 | 28 | /* Middleware */ 29 | if (process.env.NODE_ENV === 'production') { 30 | // app.enable('trust proxy'); 31 | app.use(morgan('combined')); 32 | app.use(helmet({ contentSecurityPolicy: false })); 33 | app.use(hpp()); 34 | } else { 35 | app.use(morgan('dev')); 36 | } 37 | app.use(cookieParser(process.env.SECRET)) 38 | const sessOptions = { 39 | secret: process.env.SECRET, 40 | resave: false, 41 | saveUninitialized: false, 42 | cookie: { 43 | httpOnly: true, 44 | secure: false, 45 | }, 46 | }; 47 | if (process.env.NODE_ENV === 'production') { 48 | // sessOptions.proxy = true; 49 | // sessOptions.cookie.secure = true; 50 | } 51 | app.use(session(sessOptions)); 52 | app.use(flash()); 53 | 54 | /* Passport setup */ 55 | app.use(passport.initialize()); 56 | app.use(passport.session()); 57 | passport.use(new LocalStrategy(User.authenticate())); 58 | passport.serializeUser(User.serializeUser()); 59 | passport.deserializeUser(User.deserializeUser()); 60 | 61 | /* Middleware */ 62 | app.use(express.json()); 63 | app.use(express.urlencoded({ extended: true })); 64 | app.use(express.static("public")); 65 | 66 | /* MongoDB Connection */ 67 | mongoose 68 | .connect("mongodb://127.0.0.1:27017/facebook_clone", { // 69 | useNewUrlParser: true, 70 | useCreateIndex: true, 71 | useUnifiedTopology: true 72 | }) 73 | .then(() => { 74 | console.log("Connected to MongoDB"); 75 | }) 76 | .catch((err) => { 77 | winston.error(err); 78 | }); 79 | 80 | /* Template 파일에 변수 전송 */ 81 | app.use((req, res, next) => { 82 | res.locals.user = req.user; 83 | res.locals.login = req.isAuthenticated(); 84 | res.locals.error = req.flash("error"); 85 | res.locals.success = req.flash("success"); 86 | next(); 87 | }); 88 | 89 | /* Routers */ 90 | app.use("/", userRoutes); 91 | app.use("/", postRoutes); 92 | 93 | const server = app.listen(port, () => { 94 | winston.info(`App is running on port ${port}`); 95 | }); 96 | 97 | /* WebSocket setup */ 98 | const io = socket(server); 99 | 100 | const room = io.of("/chat"); 101 | room.on("connection", socket => { 102 | winston.info("new user : ", socket.id); 103 | 104 | room.emit("newUser", { socketID: socket.id }); 105 | 106 | socket.on("newUser", data => { 107 | if (!(data.name in onlineChatUsers)) { 108 | onlineChatUsers[data.name] = data.socketID; 109 | socket.name = data.name; 110 | room.emit("updateUserList", Object.keys(onlineChatUsers)); 111 | winston.info("Online users: " + Object.keys(onlineChatUsers)); 112 | } 113 | }); 114 | 115 | socket.on("disconnect", () => { 116 | delete onlineChatUsers[socket.name]; 117 | room.emit("updateUserList", Object.keys(onlineChatUsers)); 118 | winston.info(`user ${socket.name} disconnected`); 119 | }); f 120 | 121 | socket.on("chat", data => { 122 | winston.info(data); 123 | if (data.to === "Global Chat") { 124 | room.emit("chat", data); 125 | } else if (data.to) { 126 | room.to(onlineChatUsers[data.name]).emit("chat", data); 127 | room.to(onlineChatUsers[data.to]).emit("chat", data); 128 | } 129 | }); 130 | }); -------------------------------------------------------------------------------- /chapter07/facebook-clone/views/users/chat.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 | 5 | 55 | 56 |
      57 | 58 | <% if (login) { %> 59 | <% var firstName = user.firstName %> 60 | <% } %> 61 | 62 |
      63 |
      64 | <% // if(userData.friends.length > 0) { %> 65 |
        66 | <% // } %> 67 |
        68 |
        69 |

        Chat Chat 🥳

        70 |
        71 | 72 |
        73 |
        74 |
        75 | 76 |
        77 |
        78 | 79 |
        80 |
        81 |
        82 |
        83 | 84 |
        85 | 86 | 153 | 154 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/views/users/chat.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../partials/header') %> 2 | 3 | 4 | 5 | 55 | 56 |
        57 | 58 | <% if (login) { %> 59 | <% var firstName = user.firstName %> 60 | <% } %> 61 | 62 |
        63 |
        64 | <% // if(userData.friends.length > 0) { %> 65 |
          66 | <% // } %> 67 |
          68 |
          69 |

          Chat Chat 🥳

          70 |
          71 | 72 |
          73 |
          74 |
          75 | 76 |
          77 |
          78 | 79 |
          80 |
          81 |
          82 |
          83 | 84 |
          85 | 86 | 153 | 154 | <%- include('../partials/footer') %> -------------------------------------------------------------------------------- /chapter07/facebook-clone/routes/posts.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Post = require("../models/Post"); 3 | const User = require("../models/User"); 4 | const Comment = require("../models/Comment"); 5 | const multer = require("multer"); 6 | const cloudinary = require("cloudinary"); 7 | const router = express.Router(); 8 | 9 | // Multer setup 10 | const storage = multer.diskStorage({ 11 | filename: (req, file, callback) => { 12 | callback(null, Date.now() + file.originalname); 13 | } 14 | }); 15 | 16 | const imageFilter = (req, file, callback) => { 17 | if (!file.originalname.match(/\.(jpg|jpeg|png)$/i)) { 18 | return callback(new Error("Only image files are allowed!"), false); 19 | } 20 | callback(null, true); 21 | }; 22 | 23 | const upload = multer({ storage: storage, fileFilter: imageFilter }); 24 | 25 | /* Cloudinary setup */ 26 | cloudinary.config({ 27 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 28 | api_key: process.env.CLOUDINARY_API_KEY, 29 | api_secret: process.env.CLOUDINARY_API_SECRET 30 | }); 31 | 32 | /* Middleware */ 33 | const isLoggedIn = (req, res, next) => { 34 | if (req.isAuthenticated()) { 35 | return next(); 36 | } 37 | req.flash("error", "You need to be logged in to do that!"); 38 | res.redirect("/user/login"); 39 | }; 40 | 41 | /* Routers */ 42 | router.get("/", isLoggedIn, (req, res) => { 43 | User.findById(req.user._id) // 친구들의 게시글 44 | .populate({ 45 | path: "friends", 46 | populate: { 47 | path: "posts", 48 | model: "Post" 49 | } 50 | }) 51 | .populate("posts") // 현재 사용자의 게시글 52 | .exec((err, user) => { 53 | if (err) { 54 | console.log(err); 55 | req.flash( 56 | "error", 57 | "There has been an error finding all posts." 58 | ); 59 | res.render("posts/index"); 60 | } else { 61 | let posts = []; 62 | for (var i = 0; i < user.friends.length; i++) { 63 | for (var j = 0; j < user.friends[i].posts.length; j++) { 64 | posts.push(user.friends[i].posts[j]); 65 | } 66 | } 67 | for (var i = 0; i < user.posts.length; i++) { 68 | posts.push(user.posts[i]); 69 | } 70 | if (posts) { 71 | res.render("posts/index", { 72 | posts: posts 73 | }); 74 | } else { 75 | res.render("posts/index", { posts: null }); 76 | } 77 | } 78 | }); 79 | }); 80 | 81 | router.get("/post/:id/like", isLoggedIn, (req, res) => { 82 | User.findById(req.user._id, (userErr, user) => { 83 | if (userErr) { 84 | console.log(userErr); 85 | req.flash( 86 | "There has been an error trying to like this post, are you logged in?" 87 | ); 88 | rse.redirect("back"); 89 | } else { 90 | Post.findById(req.params.id, (postErr, post) => { 91 | if (postErr) { 92 | console.log(postErr); 93 | req.flash( 94 | "There has been an error trying to like this post, are you sure you are in the correct URL?" 95 | ); 96 | res.redirect("back"); 97 | } else { 98 | for (let i = 0; i < user.liked_posts.length; i++) { // 이미 좋아요 했는지 체크 99 | if (user.liked_posts[i].equals(post._id)) { 100 | req.flash("error", "You already liked this post"); 101 | return res.redirect("back"); 102 | } 103 | } 104 | post.likes = post.likes + 1; // 좋아요 105 | post.save(); 106 | user.liked_posts.push(post._id); 107 | user.save(); 108 | req.flash( 109 | "success", 110 | `You successfully liked ${post.creator.firstName 111 | }'s post` 112 | ); 113 | res.redirect("back"); 114 | } 115 | }); 116 | } 117 | }); 118 | }); 119 | 120 | router.get("/post/:postid/comments/:commentid/like", isLoggedIn, (req, res) => { 121 | User.findById(req.user._id, (userErr, user) => { 122 | if (userErr) { 123 | console.log(userErr); 124 | req.flash( 125 | "error", 126 | "There has been an error trying to like this post" 127 | ); 128 | res.redirect("back"); 129 | } else { 130 | Comment.findById(req.params.commentid, (commentErr, comment) => { 131 | if (commentErr) { 132 | console.log(commentErr); 133 | req.flash( 134 | "error", 135 | "There has been an error trying to find the comment, are you sure the URL is correct?" 136 | ); 137 | res.redirect("back"); 138 | } else { 139 | comment.likes = comment.likes + 1; 140 | comment.save(); 141 | user.liked_comments.push(comment._id); 142 | user.save(); 143 | req.flash( 144 | "success", 145 | `You successfully liked ${comment.creator.firstName 146 | }'s comment` 147 | ); 148 | res.redirect("back"); 149 | } 150 | }); 151 | } 152 | }); 153 | }); 154 | 155 | router.get("/post/new", isLoggedIn, (req, res) => { 156 | res.render("posts/new"); 157 | }); 158 | 159 | router.post("/post/new", isLoggedIn, upload.single("image"), (req, res) => { 160 | if (req.body.content) { 161 | let newPost = {}; 162 | if (req.file) { 163 | cloudinary.uploader.upload(req.file.path, result => { 164 | newPost.image = result.secure_url; 165 | newPost.creator = req.user; 166 | newPost.time = new Date(); 167 | newPost.likes = 0; 168 | newPost.content = req.body.content; 169 | return createPost(newPost, req, res); 170 | }); 171 | } else { 172 | newPost.image = null; 173 | newPost.creator = req.user; 174 | newPost.time = new Date(); 175 | newPost.likes = 0; 176 | newPost.content = req.body.content; 177 | return createPost(newPost, req, res); 178 | } 179 | } 180 | }); 181 | 182 | function createPost(newPost, req, res) { 183 | Post.create(newPost, (err, post) => { 184 | if (err) { 185 | console.log(err); 186 | } else { 187 | req.user.posts.push(post._id); 188 | req.user.save(); 189 | res.redirect("/"); 190 | } 191 | }); 192 | } 193 | 194 | router.get("/post/:id", isLoggedIn, (req, res) => { 195 | Post.findById(req.params.id) 196 | .populate("comments") 197 | .exec((err, post) => { 198 | if (err) { 199 | console.log(err); 200 | req.flash("error", "There has been an error finding this post"); 201 | res.redirect("back"); 202 | } else { 203 | res.render("posts/show", { post: post }); 204 | } 205 | }); 206 | }); 207 | 208 | router.post("/post/:id/comments/new", isLoggedIn, (req, res) => { 209 | Post.findById(req.params.id, (err, post) => { 210 | if (err) { 211 | console.log(err); 212 | req.flash("error", "There has been an error posting your comment"); 213 | res.redirect("back"); 214 | } else { 215 | Comment.create({ content: req.body.content }, (err, comment) => { 216 | if (err) { 217 | console.log(err); 218 | req.flash( 219 | "error", 220 | "Something went wrong with posting your comment" 221 | ); 222 | res.redirect("back"); 223 | } else { 224 | comment.creator._id = req.user._id; 225 | comment.creator.firstName = req.user.firstName; 226 | comment.creator.lastName = req.user.lastName; 227 | comment.likes = 0; 228 | comment.save(); 229 | post.comments.push(comment); 230 | post.save(); 231 | req.flash("success", "Successfully posted your comment"); 232 | res.redirect("/post/" + post._id); 233 | } 234 | }); 235 | } 236 | }); 237 | }); 238 | 239 | module.exports = router; 240 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/routes/posts.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Post = require("../models/Post"); 3 | const User = require("../models/User"); 4 | const Comment = require("../models/Comment"); 5 | const multer = require("multer"); 6 | const cloudinary = require("cloudinary"); 7 | const router = express.Router(); 8 | const sanitize = require('sanitize-html'); 9 | 10 | // Multer setup 11 | const storage = multer.diskStorage({ 12 | filename: (req, file, callback) => { 13 | callback(null, Date.now() + file.originalname); 14 | } 15 | }); 16 | 17 | const imageFilter = (req, file, callback) => { 18 | if (!file.originalname.match(/\.(jpg|jpeg|png)$/i)) { 19 | return callback(new Error("Only image files are allowed!"), false); 20 | } 21 | callback(null, true); 22 | }; 23 | 24 | const upload = multer({ storage: storage, fileFilter: imageFilter }); 25 | 26 | /* Cloudinary setup */ 27 | cloudinary.config({ 28 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 29 | api_key: process.env.CLOUDINARY_API_KEY, 30 | api_secret: process.env.CLOUDINARY_API_SECRET 31 | }); 32 | 33 | /* Middleware */ 34 | const isLoggedIn = (req, res, next) => { 35 | if (req.isAuthenticated()) { 36 | return next(); 37 | } 38 | req.flash("error", "You need to be logged in to do that!"); 39 | res.redirect("/user/login"); 40 | }; 41 | 42 | /* Routers */ 43 | router.get("/", isLoggedIn, (req, res) => { 44 | User.findById(req.user._id) // 친구들의 게시글 45 | .populate({ 46 | path: "friends", 47 | populate: { 48 | path: "posts", 49 | model: "Post" 50 | } 51 | }) 52 | .populate("posts") // 현재 사용자의 게시글 53 | .exec((err, user) => { 54 | if (err) { 55 | console.log(err); 56 | req.flash( 57 | "error", 58 | "There has been an error finding all posts." 59 | ); 60 | res.render("posts/index"); 61 | } else { 62 | let posts = []; 63 | for (var i = 0; i < user.friends.length; i++) { 64 | for (var j = 0; j < user.friends[i].posts.length; j++) { 65 | posts.push(user.friends[i].posts[j]); 66 | } 67 | } 68 | for (var i = 0; i < user.posts.length; i++) { 69 | posts.push(user.posts[i]); 70 | } 71 | if (posts) { 72 | res.render("posts/index", { 73 | posts: posts 74 | }); 75 | } else { 76 | res.render("posts/index", { posts: null }); 77 | } 78 | } 79 | }); 80 | }); 81 | 82 | router.get("/post/:id/like", isLoggedIn, (req, res) => { 83 | User.findById(req.user._id, (userErr, user) => { 84 | if (userErr) { 85 | console.log(userErr); 86 | req.flash( 87 | "There has been an error trying to like this post, are you logged in?" 88 | ); 89 | rse.redirect("back"); 90 | } else { 91 | Post.findById(req.params.id, (postErr, post) => { 92 | if (postErr) { 93 | console.log(postErr); 94 | req.flash( 95 | "There has been an error trying to like this post, are you sure you are in the correct URL?" 96 | ); 97 | res.redirect("back"); 98 | } else { 99 | for (let i = 0; i < user.liked_posts.length; i++) { // 이미 좋아요 했는지 체크 100 | if (user.liked_posts[i].equals(post._id)) { 101 | req.flash("error", "You already liked this post"); 102 | return res.redirect("back"); 103 | } 104 | } 105 | post.likes = post.likes + 1; // 좋아요 106 | post.save(); 107 | user.liked_posts.push(post._id); 108 | user.save(); 109 | req.flash( 110 | "success", 111 | `You successfully liked ${post.creator.firstName 112 | }'s post` 113 | ); 114 | res.redirect("back"); 115 | } 116 | }); 117 | } 118 | }); 119 | }); 120 | 121 | router.get("/post/:postid/comments/:commentid/like", isLoggedIn, (req, res) => { 122 | User.findById(req.user._id, (userErr, user) => { 123 | if (userErr) { 124 | console.log(userErr); 125 | req.flash( 126 | "error", 127 | "There has been an error trying to like this post" 128 | ); 129 | res.redirect("back"); 130 | } else { 131 | Comment.findById(req.params.commentid, (commentErr, comment) => { 132 | if (commentErr) { 133 | console.log(commentErr); 134 | req.flash( 135 | "error", 136 | "There has been an error trying to find the comment, are you sure the URL is correct?" 137 | ); 138 | res.redirect("back"); 139 | } else { 140 | comment.likes = comment.likes + 1; 141 | comment.save(); 142 | user.liked_comments.push(comment._id); 143 | user.save(); 144 | req.flash( 145 | "success", 146 | `You successfully liked ${comment.creator.firstName 147 | }'s comment` 148 | ); 149 | res.redirect("back"); 150 | } 151 | }); 152 | } 153 | }); 154 | }); 155 | 156 | router.get("/post/new", isLoggedIn, (req, res) => { 157 | res.render("posts/new"); 158 | }); 159 | 160 | router.post("/post/new", isLoggedIn, upload.single("image"), (req, res) => { 161 | if (req.body.content) { 162 | let newPost = {}; 163 | if (req.file) { 164 | cloudinary.uploader.upload(req.file.path, result => { 165 | newPost.image = result.secure_url; 166 | newPost.creator = req.user; 167 | newPost.time = new Date(); 168 | newPost.likes = 0; 169 | newPost.content = sanitize(req.body.content); 170 | return createPost(newPost, req, res); 171 | }); 172 | } else { 173 | newPost.image = null; 174 | newPost.creator = req.user; 175 | newPost.time = new Date(); 176 | newPost.likes = 0; 177 | newPost.content = sanitize(req.body.content); 178 | return createPost(newPost, req, res); 179 | } 180 | } 181 | }); 182 | 183 | function createPost(newPost, req, res) { 184 | Post.create(newPost, (err, post) => { 185 | if (err) { 186 | console.log(err); 187 | } else { 188 | req.user.posts.push(post._id); 189 | req.user.save(); 190 | res.redirect("/"); 191 | } 192 | }); 193 | } 194 | 195 | router.get("/post/:id", isLoggedIn, (req, res) => { 196 | Post.findById(req.params.id) 197 | .populate("comments") 198 | .exec((err, post) => { 199 | if (err) { 200 | console.log(err); 201 | req.flash("error", "There has been an error finding this post"); 202 | res.redirect("back"); 203 | } else { 204 | res.render("posts/show", { post: post }); 205 | } 206 | }); 207 | }); 208 | 209 | router.post("/post/:id/comments/new", isLoggedIn, (req, res) => { 210 | Post.findById(req.params.id, (err, post) => { 211 | if (err) { 212 | console.log(err); 213 | req.flash("error", "There has been an error posting your comment"); 214 | res.redirect("back"); 215 | } else { 216 | Comment.create({ content: req.body.content }, (err, comment) => { 217 | if (err) { 218 | console.log(err); 219 | req.flash( 220 | "error", 221 | "Something went wrong with posting your comment" 222 | ); 223 | res.redirect("back"); 224 | } else { 225 | comment.creator._id = req.user._id; 226 | comment.creator.firstName = req.user.firstName; 227 | comment.creator.lastName = req.user.lastName; 228 | comment.likes = 0; 229 | comment.save(); 230 | post.comments.push(comment); 231 | post.save(); 232 | req.flash("success", "Successfully posted your comment"); 233 | res.redirect("/post/" + post._id); 234 | } 235 | }); 236 | } 237 | }); 238 | }); 239 | 240 | module.exports = router; 241 | -------------------------------------------------------------------------------- /chapter07/facebook-clone/routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const User = require("../models/User"); 3 | const passport = require("passport"); 4 | const multer = require("multer"); 5 | const cloudinary = require("cloudinary"); 6 | const router = express.Router(); 7 | 8 | /* Multer setup */ 9 | const storage = multer.diskStorage({ 10 | filename: (req, file, callback) => { 11 | callback(null, Date.now() + file.originalname); 12 | } 13 | }); 14 | 15 | const imageFilter = (req, file, callback) => { 16 | if (!file.originalname.match(/\.(jpg|jpeg|png)$/i)) { 17 | return callback(new Error("Only image files are allowed!"), false); 18 | } 19 | callback(null, true); 20 | }; 21 | 22 | const upload = multer({ storage: storage, fileFilter: imageFilter }); 23 | 24 | /* Cloudinary setup */ 25 | cloudinary.config({ 26 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 27 | api_key: process.env.CLOUDINARY_API_KEY, 28 | api_secret: process.env.CLOUDINARY_API_SECRET 29 | }); 30 | 31 | /* Middleware */ 32 | const isLoggedIn = (req, res, next) => { 33 | if (req.isAuthenticated()) { 34 | return next(); 35 | } 36 | req.flash("error", "You need to be logged in to do that!"); 37 | res.redirect("/user/login"); 38 | }; 39 | 40 | /* Routers */ 41 | 42 | /* User Routers */ 43 | router.post("/user/register", upload.single("image"), (req, res) => { 44 | if ( 45 | req.body.username && 46 | req.body.firstname && 47 | req.body.lastname && 48 | req.body.password 49 | ) { 50 | let newUser = new User({ 51 | username: req.body.username, 52 | firstName: req.body.firstname, 53 | lastName: req.body.lastname 54 | }); 55 | if (req.file) { 56 | cloudinary.uploader.upload(req.file.path, result => { 57 | newUser.profile = result.secure_url; 58 | return createUser(newUser, req.body.password, req, res); 59 | }); 60 | } else { 61 | newUser.profile = process.env.DEFAULT_PROFILE_PIC; 62 | return createUser(newUser, req.body.password, req, res); 63 | } 64 | } 65 | }); 66 | 67 | function createUser(newUser, password, req, res) { 68 | User.register(newUser, password, (err, user) => { 69 | if (err) { 70 | req.flash("error", err.message); 71 | res.redirect("/"); 72 | } else { 73 | passport.authenticate("local")(req, res, function () { 74 | console.log(req.user); 75 | req.flash( 76 | "success", 77 | "Success! You are registered and logged in!" 78 | ); 79 | res.redirect("/"); 80 | }); 81 | } 82 | }); 83 | } 84 | 85 | // Login 86 | router.get("/user/login", (req, res) => { 87 | res.render("users/login"); 88 | }); 89 | 90 | router.post( 91 | "/user/login", 92 | passport.authenticate("local", { 93 | successRedirect: "/", 94 | failureRedirect: "/user/login" 95 | }), 96 | (req, res) => { } 97 | ); 98 | 99 | // All users 100 | router.get("/user/all", isLoggedIn, (req, res) => { 101 | User.find({}, (err, users) => { 102 | if (err) { 103 | console.log(err); 104 | req.flash( 105 | "error", 106 | "There has been a problem getting all users info." 107 | ); 108 | res.redirect("/"); 109 | } else { 110 | res.render("users/users", { users: users }); 111 | } 112 | }); 113 | }); 114 | 115 | // Logout 116 | router.get("/user/logout", (req, res) => { 117 | req.logout(); 118 | res.redirect("back"); 119 | }); 120 | 121 | // User Profile 122 | router.get("/user/:id/profile", isLoggedIn, (req, res) => { 123 | User.findById(req.params.id) 124 | .populate("friends") 125 | .populate("friendRequests") 126 | .populate("posts") 127 | .exec((err, user) => { 128 | if (err) { 129 | console.log(err); 130 | req.flash("error", "There has been an error."); 131 | res.redirect("back"); 132 | } else { 133 | console.log(user); 134 | res.render("users/user", { userData: user }); 135 | } 136 | }); 137 | }); 138 | 139 | // Add Friend 140 | router.get("/user/:id/add", isLoggedIn, (req, res) => { 141 | User.findById(req.user._id, (err, user) => { 142 | if (err) { 143 | console.log(err); 144 | req.flash( 145 | "error", 146 | "There has been an error adding this person to your friends list" 147 | ); 148 | res.redirect("back"); 149 | } else { 150 | User.findById(req.params.id, (err, foundUser) => { 151 | if (err) { 152 | console.log(err); 153 | req.flash("error", "Person not found"); 154 | res.redirect("back"); 155 | } else { 156 | if ( 157 | foundUser.friendRequests.find(o => 158 | o._id.equals(user._id) 159 | ) 160 | ) { 161 | req.flash( 162 | "error", 163 | `You have already sent a friend request to ${user.firstName 164 | }` 165 | ); 166 | return res.redirect("back"); 167 | } else if ( 168 | foundUser.friends.find(o => o._id.equals(user._id)) 169 | ) { 170 | req.flash( 171 | "error", 172 | `The user ${foundUser.firstname 173 | } is already in your friends list` 174 | ); 175 | return res.redirect("back"); 176 | } 177 | let currUser = { 178 | _id: user._id, 179 | firstName: user.firstName, 180 | lastName: user.lastName 181 | }; 182 | foundUser.friendRequests.push(currUser); 183 | foundUser.save(); 184 | req.flash( 185 | "success", 186 | `Success! You sent ${foundUser.firstName 187 | } a friend request!` 188 | ); 189 | res.redirect("back"); 190 | } 191 | }); 192 | } 193 | }); 194 | }); 195 | 196 | // Accept friend request 197 | router.get("/user/:id/accept", isLoggedIn, (req, res) => { 198 | User.findById(req.user._id, (err, user) => { 199 | if (err) { 200 | console.log(err); 201 | req.flash( 202 | "error", 203 | "There has been an error finding your profile, are you connected?" 204 | ); 205 | res.redirect("back"); 206 | } else { 207 | User.findById(req.params.id, (err, foundUser) => { 208 | let r = user.friendRequests.find(o => 209 | o._id.equals(req.params.id) 210 | ); 211 | if (r) { 212 | let index = user.friendRequests.indexOf(r); 213 | user.friendRequests.splice(index, 1); 214 | let friend = { 215 | _id: foundUser._id, 216 | firstName: foundUser.firstName, 217 | lastName: foundUser.lastName 218 | }; 219 | user.friends.push(friend); 220 | user.save(); 221 | 222 | let currUser = { 223 | _id: user._id, 224 | firstName: user.firstName, 225 | lastName: user.lastName 226 | }; 227 | foundUser.friends.push(currUser); 228 | foundUser.save(); 229 | req.flash( 230 | "success", 231 | `You and ${foundUser.firstName} are now friends!` 232 | ); 233 | res.redirect("back"); 234 | } else { 235 | req.flash( 236 | "error", 237 | "There has been an error, is the profile you are trying to add on your requests?" 238 | ); 239 | res.redirect("back"); 240 | } 241 | }); 242 | } 243 | }); 244 | }); 245 | 246 | // Decline friend Request 247 | router.get("/user/:id/decline", isLoggedIn, (req, res) => { 248 | User.findById(req.user._id, (err, user) => { 249 | if (err) { 250 | console.log(err); 251 | req.flash("error", "There has been an error declining the request"); 252 | res.redirect("back"); 253 | } else { 254 | User.findById(req.params.id, (err, foundUser) => { 255 | if (err) { 256 | console.log(err); 257 | req.flash( 258 | "error", 259 | "There has been an error declining the request" 260 | ); 261 | res.redirect("back"); 262 | } else { 263 | // remove request 264 | let r = user.friendRequests.find(o => 265 | o._id.equals(foundUser._id) 266 | ); 267 | if (r) { 268 | let index = user.friendRequests.indexOf(r); 269 | user.friendRequests.splice(index, 1); 270 | user.save(); 271 | req.flash("success", "You declined"); 272 | res.redirect("back"); 273 | } 274 | } 275 | }); 276 | } 277 | }); 278 | }); 279 | 280 | /* Chat Routers */ 281 | router.get("/chat", isLoggedIn, (req, res) => { 282 | User.findById(req.user._id) 283 | .populate("friends") 284 | .exec((err, user) => { 285 | if (err) { 286 | console.log(err); 287 | req.flash( 288 | "error", 289 | "There has been an error trying to access the chat" 290 | ); 291 | res.redirect("/"); 292 | } else { 293 | res.render("users/chat", { userData: user }); 294 | } 295 | }); 296 | }); 297 | 298 | module.exports = router; 299 | -------------------------------------------------------------------------------- /chapter08/facebook-clone-v2/routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const User = require("../models/User"); 3 | const passport = require("passport"); 4 | const multer = require("multer"); 5 | const cloudinary = require("cloudinary"); 6 | const router = express.Router(); 7 | const csrf = require('csurf'); 8 | const csrfProtection = csrf({ cookie: true }); 9 | 10 | /* Multer setup */ 11 | const storage = multer.diskStorage({ 12 | filename: (req, file, callback) => { 13 | callback(null, Date.now() + file.originalname); 14 | } 15 | }); 16 | 17 | const imageFilter = (req, file, callback) => { 18 | if (!file.originalname.match(/\.(jpg|jpeg|png)$/i)) { 19 | return callback(new Error("Only image files are allowed!"), false); 20 | } 21 | callback(null, true); 22 | }; 23 | 24 | const upload = multer({ storage: storage, fileFilter: imageFilter }); 25 | 26 | /* Cloudinary setup */ 27 | cloudinary.config({ 28 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 29 | api_key: process.env.CLOUDINARY_API_KEY, 30 | api_secret: process.env.CLOUDINARY_API_SECRET 31 | }); 32 | 33 | /* Middleware */ 34 | const isLoggedIn = (req, res, next) => { 35 | if (req.isAuthenticated()) { 36 | return next(); 37 | } 38 | req.flash("error", "You need to be logged in to do that!"); 39 | res.redirect("/user/login"); 40 | }; 41 | 42 | /* Routers */ 43 | 44 | /* User Routers */ 45 | router.post("/user/register", upload.single("image"), (req, res) => { 46 | if ( 47 | req.body.username && 48 | req.body.firstname && 49 | req.body.lastname && 50 | req.body.password 51 | ) { 52 | let newUser = new User({ 53 | username: req.body.username, 54 | firstName: req.body.firstname, 55 | lastName: req.body.lastname 56 | }); 57 | if (req.file) { 58 | cloudinary.uploader.upload(req.file.path, result => { 59 | newUser.profile = result.secure_url; 60 | return createUser(newUser, req.body.password, req, res); 61 | }); 62 | } else { 63 | newUser.profile = process.env.DEFAULT_PROFILE_PIC; 64 | return createUser(newUser, req.body.password, req, res); 65 | } 66 | } 67 | }); 68 | 69 | function createUser(newUser, password, req, res) { 70 | User.register(newUser, password, (err, user) => { 71 | if (err) { 72 | req.flash("error", err.message); 73 | res.redirect("/"); 74 | } else { 75 | passport.authenticate("local")(req, res, function () { 76 | console.log(req.user); 77 | req.flash( 78 | "success", 79 | "Success! You are registered and logged in!" 80 | ); 81 | res.redirect("/"); 82 | }); 83 | } 84 | }); 85 | } 86 | 87 | // Login 88 | router.get("/user/login", csrfProtection, (req, res) => { 89 | res.render("users/login", { csrfToken: req.csrfToken() }); 90 | }); 91 | 92 | router.post( 93 | "/user/login", 94 | csrfProtection, 95 | passport.authenticate("local", { 96 | successRedirect: "/", 97 | failureRedirect: "/user/login" 98 | }), 99 | (req, res) => { } 100 | ); 101 | 102 | // All users 103 | router.get("/user/all", isLoggedIn, (req, res) => { 104 | User.find({}, (err, users) => { 105 | if (err) { 106 | console.log(err); 107 | req.flash( 108 | "error", 109 | "There has been a problem getting all users info." 110 | ); 111 | res.redirect("/"); 112 | } else { 113 | res.render("users/users", { users: users }); 114 | } 115 | }); 116 | }); 117 | 118 | // Logout 119 | router.get("/user/logout", (req, res) => { 120 | req.logout(); 121 | res.redirect("back"); 122 | }); 123 | 124 | // User Profile 125 | router.get("/user/:id/profile", isLoggedIn, (req, res) => { 126 | User.findById(req.params.id) 127 | .populate("friends") 128 | .populate("friendRequests") 129 | .populate("posts") 130 | .exec((err, user) => { 131 | if (err) { 132 | console.log(err); 133 | req.flash("error", "There has been an error."); 134 | res.redirect("back"); 135 | } else { 136 | console.log(user); 137 | res.render("users/user", { userData: user }); 138 | } 139 | }); 140 | }); 141 | 142 | // Add Friend 143 | router.get("/user/:id/add", isLoggedIn, (req, res) => { 144 | User.findById(req.user._id, (err, user) => { 145 | if (err) { 146 | console.log(err); 147 | req.flash( 148 | "error", 149 | "There has been an error adding this person to your friends list" 150 | ); 151 | res.redirect("back"); 152 | } else { 153 | User.findById(req.params.id, (err, foundUser) => { 154 | if (err) { 155 | console.log(err); 156 | req.flash("error", "Person not found"); 157 | res.redirect("back"); 158 | } else { 159 | if ( 160 | foundUser.friendRequests.find(o => 161 | o._id.equals(user._id) 162 | ) 163 | ) { 164 | req.flash( 165 | "error", 166 | `You have already sent a friend request to ${user.firstName 167 | }` 168 | ); 169 | return res.redirect("back"); 170 | } else if ( 171 | foundUser.friends.find(o => o._id.equals(user._id)) 172 | ) { 173 | req.flash( 174 | "error", 175 | `The user ${foundUser.firstname 176 | } is already in your friends list` 177 | ); 178 | return res.redirect("back"); 179 | } 180 | let currUser = { 181 | _id: user._id, 182 | firstName: user.firstName, 183 | lastName: user.lastName 184 | }; 185 | foundUser.friendRequests.push(currUser); 186 | foundUser.save(); 187 | req.flash( 188 | "success", 189 | `Success! You sent ${foundUser.firstName 190 | } a friend request!` 191 | ); 192 | res.redirect("back"); 193 | } 194 | }); 195 | } 196 | }); 197 | }); 198 | 199 | // Accept friend request 200 | router.get("/user/:id/accept", isLoggedIn, (req, res) => { 201 | User.findById(req.user._id, (err, user) => { 202 | if (err) { 203 | console.log(err); 204 | req.flash( 205 | "error", 206 | "There has been an error finding your profile, are you connected?" 207 | ); 208 | res.redirect("back"); 209 | } else { 210 | User.findById(req.params.id, (err, foundUser) => { 211 | let r = user.friendRequests.find(o => 212 | o._id.equals(req.params.id) 213 | ); 214 | if (r) { 215 | let index = user.friendRequests.indexOf(r); 216 | user.friendRequests.splice(index, 1); 217 | let friend = { 218 | _id: foundUser._id, 219 | firstName: foundUser.firstName, 220 | lastName: foundUser.lastName 221 | }; 222 | user.friends.push(friend); 223 | user.save(); 224 | 225 | let currUser = { 226 | _id: user._id, 227 | firstName: user.firstName, 228 | lastName: user.lastName 229 | }; 230 | foundUser.friends.push(currUser); 231 | foundUser.save(); 232 | req.flash( 233 | "success", 234 | `You and ${foundUser.firstName} are now friends!` 235 | ); 236 | res.redirect("back"); 237 | } else { 238 | req.flash( 239 | "error", 240 | "There has been an error, is the profile you are trying to add on your requests?" 241 | ); 242 | res.redirect("back"); 243 | } 244 | }); 245 | } 246 | }); 247 | }); 248 | 249 | // Decline friend Request 250 | router.get("/user/:id/decline", isLoggedIn, (req, res) => { 251 | User.findById(req.user._id, (err, user) => { 252 | if (err) { 253 | console.log(err); 254 | req.flash("error", "There has been an error declining the request"); 255 | res.redirect("back"); 256 | } else { 257 | User.findById(req.params.id, (err, foundUser) => { 258 | if (err) { 259 | console.log(err); 260 | req.flash( 261 | "error", 262 | "There has been an error declining the request" 263 | ); 264 | res.redirect("back"); 265 | } else { 266 | // remove request 267 | let r = user.friendRequests.find(o => 268 | o._id.equals(foundUser._id) 269 | ); 270 | if (r) { 271 | let index = user.friendRequests.indexOf(r); 272 | user.friendRequests.splice(index, 1); 273 | user.save(); 274 | req.flash("success", "You declined"); 275 | res.redirect("back"); 276 | } 277 | } 278 | }); 279 | } 280 | }); 281 | }); 282 | 283 | /* Chat Routers */ 284 | router.get("/chat", isLoggedIn, (req, res) => { 285 | User.findById(req.user._id) 286 | .populate("friends") 287 | .exec((err, user) => { 288 | if (err) { 289 | console.log(err); 290 | req.flash( 291 | "error", 292 | "There has been an error trying to access the chat" 293 | ); 294 | res.redirect("/"); 295 | } else { 296 | res.render("users/chat", { userData: user }); 297 | } 298 | }); 299 | }); 300 | 301 | module.exports = router; 302 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### [Node.js로 서버만들기](https://search.shopping.naver.com/book/catalog/32480599296?cat_id=50010881&frm=PBOKPRO&query=nodejs%EB%A1%9C+%EC%84%9C%EB%B2%84%EB%A7%8C%EB%93%A4%EA%B8%B0&NaPm=ct%3Dl9msgzrc%7Cci%3D94bf3b23bb92a5c57472e29ca10d815cd4226eaa%7Ctr%3Dboknx%7Csn%3D95694%7Chk%3Ddb4f5ce6100001517584eb86299dcf2847ebe22a) [로드북]의 Repository 입니다. 2 | 3 |
          4 | 5 | ### ⭐️ 실습 진행시 모듈 버전은 package.json에 있는 버전과 맞춰주세요! (단 Redis 모듈은 v3 버전과 v4버전 코드를 따로 업데이트 하였습니다. chapter04/ 참고) 6 | ### ⭐️ 또는 package.json을 다운로드 받고 **$ npm install** 명령어로 package.json에 있는 모듈을 모두 설치해주세요! 7 | 8 | ``` 9 | 1장. Node.js 첫걸음 10 | 1.1 Node.js 첫걸음 11 | 웹 서버와 Node.js의 관계 12 | Node.js가 동작하는 방식 13 | 1.2 실습을 위한 개발환경 구축 14 | Node.js 설치 15 | IDE(통합개발환경) 설치-비주얼 스튜디오 코드(Visual Studio Code, VS Code) 16 | 정리해봅시다 17 | 나의 이해도를 측정하자 18 | 19 | 2장. 자바스크립트 리마인드 20 | 2.1 자바스크립트 기본 문법 21 | 변수, 호이스팅, 클로저 22 | 객체와 배열 23 | 함수 24 | 프로토타입과 상속 25 | 2.2 자바스크립트의 비동기 처리 26 | 콜백 함수 27 | Promise 28 | async/await 29 | 비동기 상황에서의 예외 처리 30 | 정리해봅시다 31 | 나의 이해도를 측정하자 32 | 33 | [함께해봐요 2-1] 변수 호이스팅 (sample01.js) 34 | [함께해봐요 2-2] let을 사용한 변수 호이스팅 문제 해결 (sample02-1.js) 35 | [함께해봐요 2-3] const를 사용한 변수 호이스팅 문제 해결 (sample02-2.js) 36 | [함께해봐요 2-4] function-level-scope의 사용 ① (sample03.js) 37 | [함께해봐요 2-5] function-level-scope의 사용 ② (sample04.js) 38 | [함께해봐요 2-6] const의 특징 (sample05-1.js) 39 | [함께해봐요 2-7] let의 특징 (sample05-1.js) 40 | [함께해봐요 2-8] 클로저의 개념 (sample06.js) 41 | [함께해봐요 2-9] 객체와 프로퍼티 (sample07.js) 42 | [함께해봐요 2-10] 객체 배열 생성 (sample08.js) 43 | [함께해봐요 2-11] 구조 분해 할당 (sample09.js) 44 | [함께해봐요 2-12] 함수의 선언 (sample10.js) 45 | [함께해봐요 2-13] 화살표 함수의 선언 (sample11.js) 46 | [함께해봐요 2-14] this의 사용 (sample12.js) 47 | [함께해봐요 2-15] bind 함수 사용 (sample12-2.js) 48 | [함께해봐요 2-16] 프로토타입을 이용한 객체 생성 (sample13.js) 49 | [함께해봐요 2-17] 프로토타입과 상속 (sample14.js) 50 | [함께해봐요 2-18] Prototype Chaining (sample15.js) 51 | [함께해봐요 2-19] 프로토타입을 클래스처럼 사용해보기 (sample16.js) 52 | [함께해봐요 2-20] 콜백 함수의 비동기 처리 (sample17.js) 53 | [함께해봐요 2-21] 콜백 함수의 동기 처리 (sample18.js) 54 | [함께해봐요 2-22] 사용자 정의 함수의 동기 처리 (sample19.js) 55 | [함께해봐요 2-23] API의 비동기적 처리 (sample20.js) 56 | [함께해봐요 2-24] 일반 비동기 함수 (sample21-1.js) 57 | [함께해봐요 2-25] 동기적 처리 ① (sample21-2.js) 58 | [함께해봐요 2-26] 동기적 처리 ② (sample21-3.js) 59 | [함께해봐요 2-27] Promise의 사용 (sample22.js) 60 | [함께해봐요 2-28] Promise 객체와 async/await (sample23.js) 61 | [함께해봐요 2-29] async/await의 사용 (sample24.js) 62 | [함께해봐요 2-30] 사용자 정의 오류 (sample25.js) 63 | [함께해봐요 2-31] 일반적인 예외 처리 (sample26.js) 64 | [함께해봐요 2-32] .catch( )의 이용 (sample27.js) 65 | [함께해봐요 2-33] .then( )의 이용 (sample27.js) 66 | [함께해봐요 2-34] async/await의 예외 처리 ① (sample28-1.js) 67 | [함께해봐요 2-35] async/await의 예외 처리 ② (sample28-2.js) 68 | [함께해봐요 2-36] async/await의 예외 처리 ③ (sample28-3.js) 69 | [함께해봐요 2-37] async/await의 예외 처리 ④ (sample28-4.js) 70 | 71 | 3장. 5줄로 만드는 서버 72 | 3.1 프로젝트의 시작 73 | 프로젝트 설정하기 74 | NPM 명령어 75 | 3.2 Node.js의 모듈과 객체 76 | 모듈 시스템이란? 77 | 모듈의 종류 78 | 3.3 http 모듈로 서버 만들기 79 | 5줄로 서버를 만들어보자 80 | 요청 객체(req), 응답 객체(res) 81 | 3.4 express 모듈을 사용해 서버 만들기 82 | express란? 83 | express 설치와 사용 84 | http 요청 메서드-GET, POST, PUT, PATCH, DELETE 85 | 3.5 express와 미들웨어 86 | 미들웨어란? 87 | 자주 사용하는 미들웨어 88 | 정리해봅시다 89 | 나의 이해도를 측정하자 90 | 91 | [함께해봐요 3-1] A.js 소스 코드 (chapter03/sample/A.js) 92 | [함께해봐요 3-2] B.js 소스 코드 (chapter03/sample/B.js) 93 | [함께해봐요 3-3] 순환 참조 ① (chapter03/sample/A2.js) 94 | [함께해봐요 3-4] 순환 참조 ② (chapter03/sample/B2.js) 95 | [함께해봐요 3-5] 5줄로 만드는 서버 (chapter03/sample/simple_server.js) 96 | [함께해봐요 3-6] 웹 페이지의 요청에 대한 응답 (chapter03/sample/simple_server2.js) 97 | [함께해봐요 3-7] 문자열을 보내는 응답 코드 (chapter03/sample/simple_server3.js) 98 | [함께해봐요 3-8] fs-test.html 작성 (chapter03/sample/fs_test.html) 99 | [함께해봐요 3-9] 파일을 보내는 응답 코드 (chapter03/sample/fs_test.js) 100 | [함께해봐요 3-10] request와 response 확인 (chapter03/sample/simple_sever3.js) 101 | [함께해봐요 3-11] REST를 통한 페이지 생성 (chapter03/sample/simple_sever4.js) 102 | [함께해봐요 3-12] express 사용법 ① (chapter03/express/express_study1.js) 103 | [함께해봐요 3-13] express로 웹 페이지 만들기 (chapter03/express/index.html) 104 | [함께해봐요 3-14] express 사용법 ② (chapter03/express/express_study2.js) 105 | [함께해봐요 3-15] 미들웨어 사용법 ① (chapter03/express/express-study3.js) 106 | [함께해봐요 3-16] 미들웨어 사용법 ② (chapter03/express/express_study4.js) 107 | [함께해봐요 3-17] 오류 처리를 위한 미들웨어 함수 (chapter03/express/express_study5.js) 108 | [함께해봐요 3-18] static 미들웨어 사용 ① (chapter03/express/express-study6.js) 109 | [함께해봐요 3-19] static 미들웨어 사용 ② (chapter03/express/index2.html) 110 | [함께해봐요 3-20] 미들웨어를 이용한 예제 ① (chapter03/express/express_study7.js) 111 | [함께해봐요 3-21] 쿠키 전달 (chapter03/sample/cookie.js) 112 | [함께해봐요 3-22] 세션을 통한 키 값 생성 (chapter03/sample/cookie-session.js) 113 | [함께해봐요 3-23] 미들웨어 통합 테스트 (chapter03/express/express_study8.js) 114 | 115 | 4장. 통신을 구현해보자 116 | 4.1 API 117 | API란? 118 | Open API 활용 ①-request 119 | Open API 활용 ②-axios 120 | dotenv 사용하기 121 | 4.2 캐싱 구현하기 122 | Redis란? 123 | 4.3 API 서버를 직접 만드는 방법 124 | REST API 125 | API 서버 만들기 126 | API 서버 테스트 : CORS 127 | 4.4 웹 파싱 128 | 크롤링, 스크래핑, 파싱 129 | 정리해봅시다 130 | 나의 이해도를 측정하자 131 | 132 | [함께해봐요 4-1] request 모듈로 네이버 API 사용해보기 (chapter04/sample/naver_request.js) 133 | [함깨해봐요 4-2] axios 모듈로 에어코리아 API 사용해보기 (chapter04/sample/airkorea_axios.js) 134 | [함께해봐요 4-3] 에어코리아 API 응답 결과 가져오기 (chapter04/sample/airkorea_axios2.js) 135 | [함께해봐요 4-4] [함께해봐요 4-3]에 dotenv 모듈 적용 (chapter04/sample/airkorea_dotenv.js) 136 | [함께해봐요 4-5] Redis 서버 테스트 ① (chapter04/sample/redis.js) 137 | [함께해봐요 4-6] Redis 서버 테스트 ② (chapter04/sample/redis2.js) 138 | [함께해봐요 4-7] [함께해봐요 4-3]에 캐시 적용하기 (chapter04/sample/redis3.js) 139 | [함께해봐요 4-8] 내 API 서버 만들기 (chapter04/sample/colon_path.js) 140 | [함께해봐요 4-9] 간단한 게시판 API 서버 만들기 (chapter04/sample/board_api.js) 141 | [함께해봐요 4-10] uuid-apikey 모듈 사용 (chapter04/sample/uuid_apikey.js) 142 | [함께해봐요 4-11] 게시판에 uuid-apikey 추가하기 (chapter04/sample/board_api2.js) 143 | [함께해봐요 4-12] 게시판 API 서버 테스트 (chapter04/sample/board_api_test.js) 144 | [함께해봐요 4-13] 게시판 API 서버 테스트 코드 작성 (chapter04/sample/board_api_test.html) 145 | [함께해봐요 4-14] cors 모듈 설치 (chapter04/sample/board_api3.js) 146 | [함께해봐요 4-15] 웹 페이지 크롤링 (chapter04/sample/crawling.js) 147 | 148 | 5장. Node.js와 데이터베이스 149 | 5.1 SQL과 NoSQL 150 | SQL 151 | NoSQL 152 | 5.2 SQL : MySQL 153 | 개발환경 설정 154 | 쿼리 기본 사용법 155 | 쿼리문 작성하기 156 | ORM :Sequelize 157 | 5.3 NoSQL : MongoDB 158 | MongoDB 159 | 정리해봅시다 160 | 나의 이해도를 측정하자 161 | [함께해봐요 5-1] 데이터베이스 정보 저장 (chapter05/sequelize/config/config.json) 162 | [함께해봐요 5-2] customer 객체를 ORM로 작성하기 (chapter05/sequelize/models/customer.js) 163 | [함께해봐요 5-3] purchase 모델 생성 (chapter05/sequlieze/models/purchase.js) 164 | [함께해봐요 5-4] index.js 수정 (chapter05/sequelize/models/index.js) 165 | [함께해봐요 5-5] 테이블 관계 생성 (chapter05/sequelize/app.js ) 166 | [함께해봐요 5-6] 클라이언트 화면 생성 (chapter05/sequelize/customer.html) 167 | [함께해봐요 5-7] 정보 입력창 서버 코드 (chapter05/sequelize/app2.js) 168 | [함께해봐요 5-8] mongoose와 MongoDB 연결하기 (chapter05/mongoose/app.js) 169 | 170 | 6장. 실시간 통신을 구현해보자 171 | 6.1 웹 소켓 172 | HTTP와 AJAX 173 | 웹 소켓 174 | 6.2 WS 모듈로 웹 소켓 구현하기 175 | 6.3 socket.io로 실시간 채팅 구현하기 176 | 6.4 실시간 채팅 구현하기 177 | 정리해봅시다 178 | 나의 이해도를 측정하자 179 | [함께해봐요 6-1] ws 모듈을 이용한 WebSocket 구현 (chapter06/ws/socket.js) 180 | [함께해봐요 6-2] 클라이언트 코드 작성 (chapter06/ws/index.html) 181 | [함께해봐요 6-3] WebSocket 서버 코드 (chapter06/ws/app.js) 182 | [함께해봐요 6-4] socket.io 모듈 불러오기 (chapter06/socket.io/app.js) 183 | [함께해봐요 6-5] SocketIO 인스턴스 생성 (chapter06/socket.io/socket.js) 184 | [함께해봐요 6-6] SocketIO 클라이언트 코드 (chapter06/socket.io/index.html) 185 | [함께해봐요 6-7] SocketIO 클라이언트 코드에 polling 추가 (chapter06/socket.io/index.html) 186 | [함께해봐요 6-8] 실시간 채팅창 구현하기 (chapter06/chat/app.js) 187 | [함께해봐요 6-9] CSS 파일 생성 (chapter06/chat/index.css) 188 | [함께해봐요 6-10] 실시간 채팅창 클라이언트 코드 ① (chapter06/chat/index.html) 189 | [함께해봐요 6-11] 실시간 채팅창 클라이언트 코드 ② (chapter06/chat/index.html) 190 | 191 | 7장. 토이 프로젝트 : 페이스북 클론 코딩 192 | 7.1 passport 193 | passport란? 194 | passport 사용하기 195 | 7.2 템플릿 엔진 196 | 정적 파일과 동적 파일 197 | 템플릿 엔진 198 | 7.3 토이 프로젝트 : 페이스북 클론 코딩 199 | 정리해봅시다 200 | 나의 이해도를 측정하자 201 | 202 | [함께해봐요 7-1] 회원가입 화면 코드 (chapter07/ex_passport/index.html) 203 | [함께해봐요 7-2] passport를 이용한 회원가입 서버 코드 (chapter07/ex_passport/app.js 1~32행) 204 | [함께해봐요 7-3] passport를 이용한 회원가입 서버 코드 수정 ① (chapter07/ex_passport/app.js 34~62행) 205 | [함께해봐요 7-4] passport를 이용한 회원가입 서버 코드 수정 ② (chapter07/ex_passport/app.js 64~119행) 206 | [함께해봐요 7-5] index.js 파일 생성 (chapter07/ejs/index.js) 207 | [함께해봐요 7-6] index.ejs 파일 생성 (chapter07/ejs/views/index.ejs) 208 | [함께해봐요 7-7] head.js 파일 작성 (Chapter07/ejs/views/partials/head.ejs) 209 | [함께해봐요 7-8] header.ejs 파일 작성 (Chapter07/ejs/views/partials/header.ejs) 210 | [함께해봐요 7-9] footer.ejs 파일 작성 (Chapter07/ejs/views/partials/footer.ejs) 211 | [함께해봐요 7-10] 조각난 파일을 하나로 모으기 (Chapter07/ejs/views/index2.ejs) 212 | [함께해봐요 7-11] 메뉴의 라우터를 index2.js에서 생성 (Chapter07/ejs/index2.js) 213 | [함께해봐요 7-12] package.json 파일 수정 (facebook-clone/package.json 6~8행) 214 | [함께해봐요 7-13] app.js 파일 수정 ① (facebook-clone/app.js 1~11행) 215 | [함께해봐요 7-14] app.js 파일 수정 ② (facebook-clone/app.js 13~22행) 216 | [함께해봐요 7-15] app.js 파일 수정 ③ (facebook-clone/app.js 24~32행) 217 | [함께해봐요 7-16] .env 파일 생성 (facebook-clone/.env) 218 | [함께해봐요 7-17] app.js 파일 수정 ④ (facebook-clone/app.js 34~44행) 219 | [함께해봐요 7-18] app.js 파일 수정 ⑤ (facebook-clone/app.js 46~58행) 220 | [함께해봐요 7-19] app.js 파일 수정 ⑥ (facebook-clone/app.js 60~67행) 221 | [함께해봐요 7-20] app.js 파일 수정 ⑦ (facebook-clone/app.js 69~75행) 222 | [함께해봐요 7-21] app.js 파일 수정 ⑧ (facebook-clone/app.js 77~110행) 223 | [함께해봐요 7-22] User.js 파일 작성 (facebook-clone/models/User.js) 224 | [함께해봐요 7-23] Post.js 파일 작성 (facebook-clone/models/Post.js) 225 | [함께해봐요 7-24] Comment.js 파일 작성 (facebook-clone/models/Comment.js) 226 | [함께해봐요 7-25] User.js 파일 수정 ① (facebook-clone/routes/user.js 1~22행) 227 | [함께해봐요 7-26] User.js 파일 수정 ② (facebook-clone/routes/users.js 24~29행) 228 | [함께해봐요 7-27] .env 파일 작성 (facebook-clone/.env) 229 | [함께해봐요 7-28] User.js 파일 수정 ③ (fackebook-clone/routes/users.js 31~38행) 230 | [함께해봐요 7-29] User.js 파일 수정 ④ (facebook-clone/routes/users.js 40~83행) 231 | [함께해봐요 7-30] User.js 파일 수정 ⑤ (facebook-clone/routes/users.js 85~119행) 232 | [함께해봐요 7-31] User.js 파일 수정 ⑥ (facebook-clone/routes/users.js 121~278행) 233 | [함께해봐요 7-32] User.js 파일 수정 ⑦ (facebook-clone/routes/users.js 280~298행) 234 | [함께해봐요 7-33] post.js 파일 작성 (facebook-clone/routes/posts.js) 235 | 236 | 8장. 서버를 배포해보자 237 | 8.1 기본 준비 238 | cross-env 설정하기 239 | morgan, winston 설정하기 240 | express-session 수정하기 241 | 보안 관련 모듈 추가하기 242 | 깃, 깃허브 사용하기 243 | 더나아가기 : pm2와 메모리 DB 244 | 8.2 클라우드 서비스 245 | 8.3 클라우드 호스팅 서비스 : Heroku 246 | 8.4 도커 사용하기 247 | 도커란? 248 | 도커 기본 사용법 249 | 8.5 클라우드 인프라 서비스 : AWS EC2 250 | AWS 계정 생성 251 | EC2 인스턴스 생성 252 | 도커와깃허브를 이용해서 EC2에 배포하기 253 | 정리해봅시다 254 | 나의 이해도를 측정하자 255 | 256 | [함께해봐요 8-1] package.json 파일 수정 ① (facebook-clone/package.json) 257 | [함께해봐요 8-2] winston.js 파일 수정 (facebook-clone/config/winston.js) 258 | [함께해봐요 8-3] app.js 파일 수정 ① (facebook-clone/app.js) 259 | [함께해봐요 8-4] app.js 파일 수정 ② (facebook-clone/app.js) 260 | [함께해봐요 8-5] app.js 파일 수정 ③ (facebook-clone/app.js) 261 | [함께해봐요 8-6] post.js 파일 수정 (facebook-clone/routs/posts.js) 262 | [함께해봐요 8-7] users.js 파일 수정 (facebook-clone/routes/users.js) 263 | [함께해봐요 8-8] login.ejs 파일 수정 (facebook-clone/views/users/login.ejs) 264 | [함께해봐요 8-9] .env 파일 수정 (facebook-clone/.env) 265 | [함께해봐요 8-10] app.js 파일 수정 ④ (facebook-clone/app.js) 266 | [함께해봐요 8-11] package.json 파일 수정 ② (facebook-clone/package.json) 267 | ``` 268 | --------------------------------------------------------------------------------