├── .gitignore ├── 1.Introduction ├── 1-1_hello.js └── README.md ├── 2.Variable ├── 2-1_declaration.js ├── 2-2_dataTypes.js ├── 2-3_string.js ├── 2-4_array.js ├── 2-5_student.js └── README.md ├── 3.Functions ├── 3-1_callbackExample.js └── README.md ├── 4.Promise ├── 4-1_address.js └── README.md ├── 5.Modules ├── README.md ├── add.js ├── app.js ├── calc.js ├── calc2.js ├── calcApp.js ├── calcApp2.js ├── input.txt ├── readFile.js └── writeFile.js ├── 6.Express ├── README.md ├── counterMain.html ├── firstSecond.js ├── handleMethods.js ├── index.html ├── myFirstApp.js ├── package-lock.json ├── package.json ├── redirection.js ├── sendHtmlFile.js └── sendHtmlTag.js ├── 7.Middlewares ├── README.md ├── app.js ├── basic.js ├── bodyparserExample.js ├── foo │ ├── bar.js │ ├── baz.js │ └── index.js ├── foobar.js ├── package-lock.json ├── package.json ├── qsExample.js ├── router.js ├── routerExample.js ├── static │ ├── css │ │ └── main.css │ ├── html │ │ ├── index.html │ │ ├── login.html │ │ ├── qsExample.html │ │ ├── signup.html │ │ └── staticPage.html │ └── image │ │ └── logo.png ├── staticExample.js └── wildcard.js ├── 8.Cookies & Session ├── README.md ├── cookieParser.js ├── package-lock.json ├── package.json ├── sessionExample.js └── static │ └── html │ └── index.html ├── 9.Examples ├── Crawler │ ├── README.md │ ├── baekjoon.js │ ├── naverNews.js │ ├── package-lock.json │ └── package.json ├── Database │ ├── MongoDB │ │ ├── .env │ │ ├── README.md │ │ ├── api │ │ │ ├── index.js │ │ │ └── user │ │ │ │ ├── index.js │ │ │ │ ├── info.js │ │ │ │ ├── login.js │ │ │ │ ├── logout.js │ │ │ │ └── signup.js │ │ ├── app.js │ │ ├── models │ │ │ └── user.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── routes.js │ │ ├── static │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── main.js │ │ └── views │ │ │ ├── login.html │ │ │ ├── main.html │ │ │ └── signup.html │ ├── MySQL │ │ ├── .env │ │ ├── README.md │ │ ├── api │ │ │ ├── index.js │ │ │ └── user │ │ │ │ ├── index.js │ │ │ │ ├── info.js │ │ │ │ ├── login.js │ │ │ │ ├── logout.js │ │ │ │ └── signup.js │ │ ├── app.js │ │ ├── models │ │ │ └── user.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── pool.js │ │ ├── routes.js │ │ ├── static │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── main.js │ │ └── views │ │ │ ├── login.html │ │ │ ├── main.html │ │ │ └── signup.html │ └── README.md ├── Electron │ ├── Calculator │ │ ├── .gitignore │ │ ├── README.md │ │ ├── app.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ │ ├── css │ │ │ └── layout.css │ │ │ ├── img │ │ │ ├── icon.ico │ │ │ └── icon.png │ │ │ ├── index.html │ │ │ └── js │ │ │ └── calculator.js │ └── README.md └── README.md ├── README.md └── img ├── 1.PNG ├── 2.PNG ├── 3.png ├── 4.PNG ├── 5.PNG └── 6.PNG /.gitignore: -------------------------------------------------------------------------------- 1 | # vs code 2 | .vscode 3 | 4 | # webstorm 5 | .idea/ 6 | 7 | # modules 8 | node_modules/ -------------------------------------------------------------------------------- /1.Introduction/1-1_hello.js: -------------------------------------------------------------------------------- 1 | console.log('Hello Node.js!'); 2 | -------------------------------------------------------------------------------- /1.Introduction/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/1.Introduction/README.md -------------------------------------------------------------------------------- /2.Variable/2-1_declaration.js: -------------------------------------------------------------------------------- 1 | let variable=1; 2 | variable=2; 3 | 4 | const constantVaraible=1; 5 | constantVaraible=2; -------------------------------------------------------------------------------- /2.Variable/2-2_dataTypes.js: -------------------------------------------------------------------------------- 1 | const str="Hello"; 2 | const decimal=10; 3 | const float=10.1; 4 | const bool=true; 5 | const arr=[1,2,3,4,5]; 6 | const obj={key1:str,key2:decimal}; 7 | 8 | console.log(str); 9 | console.log(decimal); 10 | console.log(float); 11 | console.log(bool); 12 | console.log(arr); 13 | console.log(arr[4]); 14 | console.log(obj); 15 | console.log(obj.key1); 16 | console.log(obj.key2); 17 | console.log(obj['key1']); 18 | console.log(obj['key2']); -------------------------------------------------------------------------------- /2.Variable/2-3_string.js: -------------------------------------------------------------------------------- 1 | const str="I Like Node.js"; 2 | console.log(str.length); 3 | 4 | const replaced=str.replace('I','You'); 5 | console.log(replaced); 6 | 7 | const splitted=str.split(' '); 8 | console.log(splitted); 9 | -------------------------------------------------------------------------------- /2.Variable/2-4_array.js: -------------------------------------------------------------------------------- 1 | const arr=['a','b','c','d','e']; 2 | 3 | console.log(arr.length); 4 | 5 | const exists=arr.includes('c'); 6 | console.log(exists); 7 | 8 | const idx=arr.indexOf('c'); 9 | console.log(arr[idx]); 10 | 11 | arr.push('d'); 12 | console.log(arr); -------------------------------------------------------------------------------- /2.Variable/2-5_student.js: -------------------------------------------------------------------------------- 1 | const students=[{name:'서주원',age:20,GPA:4.3}, 2 | {name:'송재혁',age:24,GPA:0.0}, 3 | {name:'조성완',age:17,GPA:2.38}]; 4 | 5 | //이곳에 코드를 작성하세요. -------------------------------------------------------------------------------- /2.Variable/README.md: -------------------------------------------------------------------------------- 1 | ## 2. Variable 2 | 이번 장에서는 Javascript의 변수형과 자료형에 대해 다뤄보도록 하겠습니다. 3 | 4 | --- 5 | 6 | ### (1) 변수형 7 | Javascript에서 변수는 `let`, 상수는 `const`를 이용하여 선언합니다. 8 | ``` javascript 9 | // 2-1_declaration.js 10 | let variable=1; 11 | variable=2; 12 | 13 | const constantVaraible=1; 14 | constantVaraible=2; // TypeError: Assignment to constant variable. 15 | ``` 16 | 변수로 선언된 variable은 값을 2로 대입했을 때 별 이상이 발생하지 않았지만, 17 | 상수로 선언된 constantVariable의 값을 2로 대입할 때 TypeError가 발생했음을 확인할 수 있습니다. 18 | 19 | 이를 통해 상수는 대입 연산자를 통한 변경이 불가능하고 변수는 변경이 가능하다는 점을 알 수 있습니다. 20 | 21 | 웹페이지의 script 부분이나 이전에 작성된 javascript 코드를 보면 변수가 `const`나 `let`이 아닌 `var`로 선언됐음을 쉽게 찾을 수 있습니다. 22 | 이전의 Javascript에서는 `var`를 통하여 변수를 선언하였습니다. 23 | `var`를 사용하여 변수를 사용할 경우, 많은 문제가 발생할 가능성이 있습니다. 24 | 25 | ```javascript 26 | { 27 | var x=3; 28 | } 29 | console.log(x); // 3 30 | ``` 31 | 앞서 1장에서 언급하였지만 `console.log()`는 콘솔에 출력을하는 함수입니다. 32 | 변수 x는 {}가 나타내는 블록 안에서 선언되었고 블록 밖에서 접근되었습니다. 33 | 변수가 선언된 지점과 접근된 지점이 다름에도 불구하고 정상적으로 접근되었습니다. 34 | 35 | ```javascript 36 | { 37 | let y=3; 38 | } 39 | console.log(y); // Uncaught ReferenceError: y is not defined 40 | ``` 41 | 그러나 변수를 `const`나 `let`으로 선언하게 되면 선언한 지점과 접근한 지점이 다를 때, Uncaught ReferenceError를 발생시킵니다. 42 | 43 | 또한 변수를 `var`로 선언하게 되면 예기치 못한 문제가 발생할 수 있습니다. 44 | ```javascript 45 | console.log(x); // undefined 46 | var x=1; 47 | ``` 48 | 기존의 다른언어에서는 상상도 못할 결과가 나타납니다. 49 | 다른 언어였다면 선언되지 않은 변수에 대한 접근이기 때문에 에러를 발생시켜야 하는데, 50 | 그저 모른다고(undefined)만 출력합니다. 51 | 이러한 특성은 프로그래머가 의도한 대로 작동하지 않을 여지가 분명하기에 많은 불편함을 초래할 수 있습니다. 52 | 53 | 이에 비해 변수를 `const`나 `let`으로 선언하게 되면, 54 | ```javascript 55 | console.log(y); // Uncaught ReferenceError: Cannot access 'y' before initialization 56 | const y=1; 57 | ``` 58 | 초기화되지 않은 변수에 대한 접근이기 때문에 정상적으로 오류가 출력되는 것을 확인할 수 있습니다. 59 | 60 | --- 61 | 62 | ### (2) 자료형 63 | 이번에는 Javascript에서 다루는 자료형에 대해 알아보도록 하겠습니다. 64 | 65 | 기본 자료형에는 `String`, `Number`, `Boolean`, `Array`, `Object` 등이 있습니다. 66 | Javascript에서는 다른 언어(C++, Java)와는 다르게 변수의 선언은 동일하게 합니다. 67 | 변수에 대입된 값으로 타입을 유추합니다. 68 | ```javascript 69 | //2-2_dataTypes.js 70 | const str="Hello"; 71 | const decimal=10; 72 | const float=10.1; 73 | const bool=true; 74 | const arr=[1,2,3,4,5]; 75 | const obj={key1:str,key2:decimal}; 76 | 77 | console.log(str); // Hello 78 | console.log(decimal); // 10 79 | console.log(float); // 10.1 80 | console.log(bool); // true 81 | console.log(arr); // [ 1, 2, 3, 4, 5 ] 82 | console.log(arr[4]); // 5 83 | console.log(obj); // { key1: 'Hello', key2: 10 } 84 | console.log(obj.key1); // Hello 85 | console.log(obj.key2); // 10 86 | console.log(obj['key1']); // Hello 87 | console.log(obj['key2']); // 10 88 | ``` 89 | 위 코드를 보면 아시겠지만, Javascript는 숫자 중에서도 정수형(타 언어에서는 주로 int)과 실수형(타 언어에서는 주로 float)를 따로 구분하지 않습니다. 90 | 91 | 배열은 대괄호 []로 선언하며, 각각의 원소는 0부터 시작하는 인덱스를 이용하여 접근할 수 있습니다. 92 | 93 | 또한 Python의 dict와 유사한 `Object` 자료형이 있습니다. 94 | 흔히 `JSON`(JavaScript Object Notation)이라고 불리는 이 자료형은 key, value의 쌍으로 이루어져 있습니다. 95 | value 자리에는 앞서 언급한 `String`,`Number`,`Array` 뿐만 아니라 `함수`도 들어갈 수 있습니다. 96 | 97 | 각각의 value는 `obj.key1`, `obj.key2` 처럼 `.`으로 접근하거나, 98 | 배열에서 인덱싱하듯이 접근할 수 있습니다. 99 | 100 | 이번엔 문자열과 관련된 여러 메소드들을 다뤄보도록 하겠습니다. 101 | ```javascript 102 | // 2-3_string.js 103 | const str="I Like Node.js"; 104 | console.log(str.length); // 14 105 | 106 | const replaced=str.replace('I','You'); 107 | console.log(replaced); // You Like Node.js 108 | 109 | const splitted=str.split(' '); 110 | console.log(splitted); // [ 'I', 'Like', 'Node.js' ] 111 | 112 | console.log(`length of str is ${str.length}`); // length of str is 14 113 | ``` 114 | `str.length`는 문자열 str의 길이를 반환합니다. 115 | `str.replace()`는 문자열 str에서 첫 인자(I)에 해당하는 부분을 두번째 인자(You)로 변경한 새 문자열을 반환합니다. 116 | `str.split()`은 주어진 인자를 구분자로 하여 문자열을 분리한 새 배열을 반환합니다. 117 | 그리고 ``로 감싼 문자열에는 ${변수}를 통해 변수의 값이 들어간 문자열을 생성할 수 있습니다. 118 | String 자료형에 대한 다른 메소드들은 [공식 문서](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String)에서 확인해 주세요. 119 | 120 | 이번에는 배열과 관련된 여러 메소드들을 다뤄보도록 하겠습니다. 121 | 122 | ```javascript 123 | // 2-4_array.js 124 | const arr=['a','b','c','d','e']; 125 | 126 | console.log(arr.length); // 5 127 | 128 | const exists=arr.includes('c'); 129 | console.log(exists); // true 130 | 131 | const idx=arr.indexOf('c'); 132 | console.log(arr[idx]); // c 133 | 134 | arr.push('d'); 135 | console.log(arr); // [ 'a', 'b', 'c', 'd', 'e', 'd' ] 136 | ``` 137 | `arr.length`는 배열 arr의 길이 5를 반환합니다. 138 | `arr.includes()`는 주어진 인자가 배열에 존재하는 지 반환합니다. 139 | `arr.push()`는 주어진 인자를 배열의 맨 끝에 삽입합니다. 140 | Array에 대한 다른 메소드들은 [공식 문서](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array)에서 확인해 주세요. 141 | 142 | --- 143 | 144 | ### (3) Assignment 145 | 다음 students를 아래 형식에 맞게 출력하는 코드를 작성하세요. 146 | ```javascript 147 | // 2-5_student.js 148 | const students=[{name:'서주원',age:20,GPA:4.3}, 149 | {name:'송재혁',age:24,GPA:0.0}, 150 | {name:'조성완',age:17,GPA:2.38}]; 151 | ``` 152 | 출력 형식 153 | ``` 154 | 이름: 서주원, 나이: 20, 평점: 4.3 155 | 이름: 송재혁, 나이: 24, 평점: 0.0 156 | 이름: 조성완, 나이: 17, 평점: 2.38 157 | ``` -------------------------------------------------------------------------------- /3.Functions/3-1_callbackExample.js: -------------------------------------------------------------------------------- 1 | const list=[1,2,3,4,5,6,7,8,9,10]; 2 | 3 | function callbackExample(items,callback){ 4 | setTimeout(function(){ 5 | let sum=0; 6 | for(let i=0;ia+b; 52 | 53 | const sum_arrow2=(a,b)=>{ 54 | const result=a+b; 55 | return result; 56 | } 57 | ``` 58 | 59 | 위의 `sum_arrow()`는 `sum()`와 `sum2()`을 그대로 arrow function으로 표현한 것입니다. 60 | `sum_arrow()`가 `sum()`나 `sum2()`에비해 눈에 띄게 간단하게 표현된 것을 확인할 수 있습니다. 61 | `sum_arrow2()`는 함수안에서 여러줄의 코드가 실행되는 경우 중괄호{}를 이용하여 표현했습니다. 62 | 함수의 매개변수가 없는 경우나 매개변수가 하나인 경우는 다음과 같이 표현할 수 있습니다. 63 | ```javascript 64 | const no_param=()=>console.log('no parameter'); 65 | 66 | const one_param=(a)=>console.log('one parameter :',a); 67 | 68 | const one_param2=a=>console.log('one parameter :',a); 69 | ``` 70 | 71 | `no_params()`는 매개변수가 없는 함수이고 빈 소괄호 ()를 이용하여 표현하였습니다. 72 | `one_param()`은 기존 arrow function과 비슷하게 소괄호 안에 하나의 매개변수를 입력하여 작성하였습니다. 73 | 매개변수가 하나인 경우는 소괄호를 생략한 채, `one_params2()`처럼 매개변수 하나만 달랑 입력하여 작성할 수도 있습니다. 74 | 75 | --- 76 | 77 | ### (2) Callback Function 78 | 이번에는 **callback function**에 대해 알아보도록 하겠습니다. 79 | 그 전에 javascript에서 함수의 성질에 대해 먼저 짚어보겠습니다. 80 | javascript에서의 함수는 일급 객체로, 변수에 대입하거나 함수에 매개변수로 전달할 수 있습니다. 81 | 82 | ```javascript 83 | const plus=(a,b)=>a+b; 84 | 85 | const minus=(a,b)=>a-b; 86 | 87 | let p=plus; 88 | 89 | console.log(typeof(p)); // function 90 | 91 | console.log(plus(11,22)); // 33 92 | console.log(p(11,22)); // 33 93 | 94 | const calculate=(a,b,func)=>func(a,b); 95 | 96 | console.log(calculate(11,22,plus)); // 33 97 | console.log(calculate(11,22,minus)); // -11 98 | ``` 99 | 100 | 위 코드를 살펴보면 101 | 두 수의 합을 반환하는 `plus()`와 두 수의 차를 반환하는 `minus()`를 선언했습니다. 102 | 그 다음 변수 `p`에 함수인 `plus`를 소괄호 없이 대입하였습니다. 103 | `typeof(`)로 `p`의 type을 출력해보면 function이 출력되는 것을 보아, 함수를 변수에 대입하는 것이 가능하다는 것을 알수있습니다. 104 | 105 | `p`에 `plus`를 대입했기에, `plus(11,22)`와 `p(11,22)`는 같은 값을 갖게 됩니다. 106 | 107 | 이번에는 함수의 매개변수에 함수를 전달하겠습니다. 108 | `calculate`의 매개변수인 `func`자리에는 함수가 들어가게됩니다. 109 | 매개변수로 `plus`를 전달하면 두 수 a, b의 합이 출력되고, `minus`를 전달하면 두 수 a, b의 차가 출력됩니다. 110 | 111 | 이제 **callback function**에 대해 알아보겠습니다. 112 | callback의 사전적 정의는 '답신 전화', '회신'이라는 뜻입니다. 113 | 114 | callback function은 특정 함수에 매개변수로 전달된 함수를 말하며, 특정 함수가 실행될 때 호출되는 방식으로 작동합니다. 115 | 116 | ```javascript 117 | const sum=(a,b)=>a+b; 118 | 119 | const printResult=(result)=>{ 120 | console.log("결과는",result,"입니다."); 121 | }; 122 | 123 | const calculateAndPrint=(calculationResult, callback)=>{ 124 | callback(calculationResult); 125 | }; 126 | 127 | calculateAndPrint(sum(10,20),printResult); // 결과는 30 입니다. 128 | ``` 129 | 130 | 위 코드의 마지막줄에서 `calculateAndPrint`의 인자로 `sum(10,20)`과 함수인 `printReuslt`를 넘겨 주었습니다. 131 | 132 | `sum(10,20)`의 값은 30이기 때문에, 133 | `calculateAndPrint`의 첫번째 인자는 30입니다. 134 | callback에 `printResult`을 넘겨주었기 때문에, 135 | `printResult(30)`에서 "결과는 30 입니다."가 출력됩니다. 136 | 137 | --- 138 | 139 | ### (3) Functions using Callback 140 | 이번 절에서는 Callback을 사용하는 여러 함수들을 살펴보겠습니다. 141 | 142 | #### 1) Filter 143 | 144 | 배열에서 특정 조건을 가진 데이터 또는 object를 추출하고 싶다면, `filter()`를 사용하는 것이 좋습니다. 145 | 146 | ```javascript 147 | const ages=[11,12,13,16,21,31]; 148 | 149 | const upper16=ages.filter(age=>age>16); // [21, 31] 150 | const under13=ages.filter(age=>age<13); // [11, 12] 151 | const between12And21=ages.filter(age=>age>12 && age <21); // [13, 16] 152 | ``` 153 | `filter()`는 array의 메소드입니다. 154 | `filter()`는 각각의 원소에 대해 callback 함수를 실행하여 해당 callback 함수가 true를 반환하는 경우에만 새로운 배열에 push합니다. 155 | 이때 age가 배열의 각 item에 해당합니다. 156 | 157 | #### 2) Map 158 | 159 | `map()`은 배열의 각각의 item에 대하여 매개변수로 들어간 함수를 적용시켜 배열을 리턴합니다. 160 | 161 | ```javascript 162 | const list = [1,2,3]; 163 | const squaredList=list.map(item=>item*item); // [1, 4, 9]; 164 | ``` 165 | `map()`에 callback function으로 item의 제곱을 계산하는 함수를 전달하였습니다. 166 | 167 | #### 3) Reduce 168 | 169 | `reduce()`는 배열의 가장 첫번째 item부터 마지막 item까지 매개변수로 들어간 함수를 누적 적용시킵니다. 170 | 171 | ```javascript 172 | const scores=[10,20,30,40,50]; 173 | 174 | const sum=scores.reduce((a,b)=>(a+b)); 175 | const sumWithInitValue=scores.reduce((a,b)=>(a+b),10); 176 | ``` 177 | sum 함수에서 178 | (10, 20) 179 | ((10+20), 30) 180 | ((10+20+30), 40) 181 | ... 182 | 와 같이 반복하여 함수가 실행됩니다. 183 | 184 | sumWithInitValue 함수는 초기값으로 10을 함수 인자로 넘겨주었습니다. 185 | 따라서 186 | (10, 10) 187 | ((10+10), 20) 188 | ... 189 | 와 같이 실행됩니다. 190 | 191 | #### 4) setTimeout 192 | `setTimeout()`은 주어진 callback 함수를 특정 ms뒤 실행하는 기능을 수행합니다. 193 | 194 | ```javascript 195 | setTimeout(()=>{console.log('1초 경과');},1000); 196 | ``` 197 | 198 | #### 5) forEach 199 | Javascript에서는 `forEach()`를 통해 배열을 순회하면서 특정 callback function을 실행시킬 수 있습니다. 200 | 201 | ```javascript 202 | let arr=[1,2,3,4,5,6,7,8]; 203 | arr.forEach((currentValue)=>{ 204 | console.log(currentValue); 205 | }); 206 | ``` 207 | 다만, `forEach()`는 단순히 array의 각각의 원소들에 대해 callback 함수를 실행 시켜 줄 뿐이지 각각의 callback함수가 동기적으로 실행된다고 보장하지 않습니다. 208 | 209 | ```javascript 210 | let arr=[1,2,3,4]; 211 | arr.forEach((currentValue)=>{ 212 | setTimeout(()=>{ 213 | console.log(currentValue); 214 | },1000); 215 | }); 216 | ``` 217 | 218 | 위 소스코드를 실행하면 arr의 원소들이 1000ms뒤에 한번에 출력됨을 확인할 수 있습니다. 219 | 간단히 생각하면 arr의 모든 원소를 출력하는 데 4000ms가 필요해야하는게 아닌가 싶지만, `forEach()`는 앞서 서술했듯이 그저 주어진 callback 함수를 각각의 원소에 대해서 실행해주는것이기 때문에 각각은 비동기식으로 작동합니다. 220 | 221 | --- 222 | 223 | ### (4) Assignment 224 | 다음의 3.Functions/3-1_callbackExample.js를 **arrow function** 형태로 바꾸어 봅시다. 225 | 또한 for문에 해당하는 부분을 오늘 배운 함수를 이용하여 변경해 봅시다. 226 | ```javascript 227 | const list=[1,2,3,4,5,6,7,8,9,10]; 228 | 229 | function callbackExample(items,callback){ 230 | setTimeout(function(){ 231 | let sum=0; 232 | for(let i=0;i{ 16 | resolve(1); 17 | }).then((result)=>{ 18 | console.log('first: ',result); 19 | return result+'hello'; 20 | }).then((result)=>{ 21 | console.log('second: ',result); 22 | return result+'nello'; 23 | }); 24 | 25 | promiseResult 26 | .then(result=>console.log(result)); 27 | 28 | // first: 1 29 | // second: 1hello 30 | // 1hellonello 31 | ``` 32 | `resolve()`는 이전 함수에서 return과 같습니다. 33 | 처음에 `resolve(1)`을 하면, 1이 `.then()`으로 이어 실행되는 함수의 인자로 들어갑니다. 34 | 그렇기에 first: 1이 출력됩니다. 35 | 36 | Promise는 또 다른 Promise 객체를 반환하기 때문에 마지막에 연산한 값을 `.then(result=>console.log(result));`로 출력해주었습니다. 37 | 38 | 여러개의 Promise 객체를 따로 선언한 후, 한번에 후처리 할수도 있습니다. 39 | 40 | ```javascript 41 | const promiseFirst=new Promise((resolve,reject)=>{ 42 | resolve(1); 43 | }).then(result=>result+10); 44 | 45 | const promiseSecond=new Promise((resolve,reject)=>{ 46 | resolve(2); 47 | }).then(result=>result+20); 48 | 49 | Promise.all([promiseFirst, promiseSecond]).then((result)=>{ 50 | console.log('result:', result); // result: [11,22] 51 | console.log('sum: ', result[0] + result[1]); // sum: 33 52 | }); 53 | ``` 54 | 55 | `promiseFirst`는 `resolve(1)`을 넣고 10을 더했고, `promiseSecond`는 `resolve(2)`를 넣고 20을 더했습니다. 56 | 이 두 Promise를 `Promise.all()`을 이용해 처리했습니다. 57 | 이때 return값은 배열이기 때문에 sum을 구하는 과정에서 result\[0\]과 result\[1\]을 이용해 접근했습니다. 58 | 59 | --- 60 | 61 | ### (2) Async/await 62 | **async/await**은 callback hell을 탈출하고자 생긴 Promise마저도 장황하게 느껴 ES2017(=ES8)버전에서 탄생했습니다. 63 | 소스코드와 함께 설명 드리겠습니다. 64 | ```javascript 65 | const promiseAdd10=(num)=>{ 66 | return new Promise((resolve,reject)=>{ 67 | resolve(num+10); 68 | }); 69 | } 70 | 71 | const promiseSubtract10=(num)=>{ 72 | return new Promise((resolve,reject)=>{ 73 | resolve(num-10); 74 | }); 75 | } 76 | ``` 77 | 위 코드는 함수의 매개변수로 주어진 값에 10을 더해서 Promise 객체를 리턴하는 함수 `promiseAdd10`와, 매개변수로 주어진 값에 10을 빼서 Promise 객체를 리턴하는 함수 `promiseSubtract10`을 구현해 놓았습니다. 78 | 두 함수를 순서대로 실행하고자 하면 다음과 같은 코드가 나옵니다. 79 | ```javascript 80 | const promiseAdd10=(num)=>{ 81 | return new Promise((resolve,reject)=>{ 82 | resolve(num+10); 83 | }); 84 | } 85 | 86 | const promiseSubtract10=(num)=>{ 87 | return new Promise((resolve,reject)=>{ 88 | resolve(num-10); 89 | }); 90 | } 91 | 92 | promiseAdd10(90) 93 | .then(promiseSubtract10) 94 | .then((result)=>{ 95 | console.log("결과는",result,"입니다."); // 결과는 90 입니다. 96 | }); 97 | ``` 98 | 99 | callback 형태에 비해서는 가독성이 많이 향상되었지만 코드가 길어진다면 여전히 보기 어려워지는 것은 마찬가지입니다. 100 | 이를 더욱 보기좋게 만들어 보겠습니다. 101 | ```javascript 102 | const promiseAdd10=(num)=>{ 103 | return new Promise((resolve,reject)=>{ 104 | resolve(num+10); 105 | }); 106 | } 107 | 108 | const promiseSubtract10=(num)=>{ 109 | return new Promise((resolve,reject)=>{ 110 | resolve(num-10); 111 | }); 112 | } 113 | 114 | const calculate=async ()=>{ 115 | try{ 116 | let sum=await promiseAdd10(90); 117 | let result=await promiseSubtract10(sum); 118 | console.log("결과는",result,"입니다."); 119 | } 120 | catch (err){ 121 | console.error(err); 122 | } 123 | } 124 | 125 | calculate(); // 결과는 90 입니다. 126 | ``` 127 | 실질적으로 계산을 하는 `calculate` 함수를 정의하였습니다. 128 | 여태 다뤄 본 함수들과는 차이가 있음을 확인할 수 있습니다. 129 | 바로 함수의 매개변수 앞에 **async**라는 단어가 붙은것과, **try,catch** 문이 추가 되었고, 130 | 함수를 실행하기 앞서 **await**이라는 단어가 붙었습니다. 131 | 132 | `await`은 Promise의 `resolve`를 일반 함수의 return처럼 취급하여, callback이나 then으로 이어지는 복잡한 코드를 133 | 동기(Synchronous) 형태로 만들어 주는 역할을 합니다. 134 | 135 | 단 여기서 주의할 점은 await은 무조건 **async 함수 안에서** 사용되어야 합니다. 136 | 또한, await 뒤에 실행할 함수는 **Promise 객체를 반환**하여야 합니다. 137 | 다음은 async 함수를 표현하는 여러가지 예시입니다. 138 | 139 | ```javascript 140 | const functionExpression=async function(){ 141 | console.log("함수 표현식"); 142 | } 143 | const arrowFunction = async () =>{ 144 | console.log("화살표 함수"); 145 | } 146 | const ITFE=(async ()=>{ 147 | console.log("즉시 실행 함수 표현식"); 148 | })(); 149 | ``` 150 | 만약 await 뒤의 함수의 실행중에 오류가 발생하면 어떻게 될까요? 151 | 아무 일도 일어나지 않습니다. 152 | 디버깅 과정에서 에러가 어느 곳에서 발생했는 지 파악하는 것은 매우 중요합니다. 153 | 이를 도와주는 것이 `try,catch`문입니다. 154 | `try` 문 안에서 발생한 오류는 catch 문의 인자 err 로 들어가게 됩니다. 155 | 156 | --- 157 | 158 | ### (3) Assignment 159 | 160 | 다음은 4.Promise/4-1_address.js의 일부분입니다. 161 | 162 | ```javascript 163 | let address=""; 164 | 165 | const country=(addr)=>{ 166 | addr+="대한민국 "; 167 | const province=(addr)=>{ 168 | addr+="경기도 "; 169 | const city=(addr)=>{ 170 | addr+="용인시 "; 171 | console.log("original : "+addr); 172 | } 173 | return city(addr); 174 | } 175 | return province(addr); 176 | } 177 | 178 | country(address) // original : 대한민국 경기도 용인시 179 | ``` 180 | 181 | 위 코드를 참고하여 기존 코드를 Promise 형태로 정의 후 호출하고, 182 | Promise들을 async/await 방식으로 호출해 봅니다. 183 | 184 | 출력 예시는 다음과 같습니다. 185 | ```bash 186 | original : 대한민국 경기도 용인시 187 | promise : 대한민국 경기도 용인시 188 | async/await : 대한민국 경기도 용인시 189 | ``` 190 | 191 | 제출은 이전과 마찬가지로 각자의 repository에 해주시면 됩니다. 192 | -------------------------------------------------------------------------------- /5.Modules/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/5.Modules/README.md -------------------------------------------------------------------------------- /5.Modules/add.js: -------------------------------------------------------------------------------- 1 | const add=(a,b)=>a+b; 2 | 3 | module.exports=add; 4 | -------------------------------------------------------------------------------- /5.Modules/app.js: -------------------------------------------------------------------------------- 1 | const add=require('./add'); 2 | 3 | console.log(add(1,2)); 4 | -------------------------------------------------------------------------------- /5.Modules/calc.js: -------------------------------------------------------------------------------- 1 | const add=(a,b)=>a+b; 2 | const subtract=(a,b)=>a-b; 3 | const multiply=(a,b)=>a*b; 4 | const divide=(a,b)=>a/b; 5 | 6 | module.exports={ 7 | add:add, 8 | subtract:subtract, 9 | multiply:multiply, 10 | divide:divide 11 | } -------------------------------------------------------------------------------- /5.Modules/calc2.js: -------------------------------------------------------------------------------- 1 | exports.add=(a,b)=>a+b; 2 | exports.subtract=(a,b)=>a-b; 3 | exports.multiply=(a,b)=>a*b; 4 | exports.divide=(a,b)=>a/b; -------------------------------------------------------------------------------- /5.Modules/calcApp.js: -------------------------------------------------------------------------------- 1 | const calc=require('./calc'); 2 | const add=calc.add; 3 | const subtract=calc.subtract; 4 | const multiply=calc.multiply; 5 | const divide=calc.divide; 6 | 7 | console.log(add(4,2)); 8 | console.log(subtract(4,2)); 9 | console.log(multiply(4,2)); 10 | console.log(divide(4,2)); -------------------------------------------------------------------------------- /5.Modules/calcApp2.js: -------------------------------------------------------------------------------- 1 | const calc=require('./calc2'); 2 | const add=calc.add; 3 | const subtract=calc.subtract; 4 | const multiply=calc.multiply; 5 | const divide=calc.divide; 6 | 7 | console.log(add(4,2)); // 6 8 | console.log(subtract(4,2)); // 2 9 | console.log(multiply(4,2)); // 8 10 | console.log(divide(4,2)); // 2 -------------------------------------------------------------------------------- /5.Modules/input.txt: -------------------------------------------------------------------------------- 1 | 2,*,3 -------------------------------------------------------------------------------- /5.Modules/readFile.js: -------------------------------------------------------------------------------- 1 | const fs=require('fs'); 2 | 3 | fs.readFile('dream.txt',(err,data)=>{ 4 | if (err) 5 | throw err; 6 | console.log(data.toString()); 7 | }); -------------------------------------------------------------------------------- /5.Modules/writeFile.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/5.Modules/writeFile.js -------------------------------------------------------------------------------- /6.Express/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/6.Express/README.md -------------------------------------------------------------------------------- /6.Express/counterMain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /6.Express/firstSecond.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | app.get('/',(req,res)=>{ 5 | res.send('Index Page'); 6 | }); 7 | 8 | app.get('/first',(req,res)=>{ 9 | res.send('First Page'); 10 | }); 11 | 12 | app.get('/second',(req,res)=>{ 13 | res.send('Second Page'); 14 | }); 15 | 16 | app.listen(3000,()=>{ 17 | console.log('Server is running on port 3000!'); 18 | }); -------------------------------------------------------------------------------- /6.Express/handleMethods.js: -------------------------------------------------------------------------------- 1 | const fs=require('fs'); 2 | const express=require('express'); 3 | const app=express(); 4 | 5 | app.get('/',(req,res)=>{ 6 | fs.readFile('./index.html',(err,data)=>{ 7 | if (err) 8 | throw err; 9 | res.writeHead(200,{'Content-Type':'text/html'}); 10 | res.end(data); 11 | }); 12 | }); 13 | 14 | app.get('/test',(req,res)=>{ 15 | console.log('GET /test'); 16 | res.send('GET'); 17 | }); 18 | 19 | app.post('/test',(req,res)=>{ 20 | console.log('POST /test'); 21 | res.send('POST'); 22 | }); 23 | 24 | app.listen(3000,()=>{ 25 | console.log('Server is running on port 3000!'); 26 | }); -------------------------------------------------------------------------------- /6.Express/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This is index page

4 |
5 | 6 |
7 |
8 | 9 |
10 | -------------------------------------------------------------------------------- /6.Express/myFirstApp.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | app.get('/',(req,res)=>{ 5 | res.send('Hello My First Server'); 6 | }); 7 | 8 | app.listen(3000,()=>{ 9 | console.log('Server is running on port 3000!'); 10 | }); -------------------------------------------------------------------------------- /6.Express/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "6.express", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.19.0", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 24 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 25 | "requires": { 26 | "bytes": "3.1.0", 27 | "content-type": "~1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "~1.1.2", 30 | "http-errors": "1.7.2", 31 | "iconv-lite": "0.4.24", 32 | "on-finished": "~2.3.0", 33 | "qs": "6.7.0", 34 | "raw-body": "2.4.0", 35 | "type-is": "~1.6.17" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.1.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 41 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.3", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 46 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 47 | "requires": { 48 | "safe-buffer": "5.1.2" 49 | } 50 | }, 51 | "content-type": { 52 | "version": "1.0.4", 53 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 54 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 55 | }, 56 | "cookie": { 57 | "version": "0.4.0", 58 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 59 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 60 | }, 61 | "cookie-signature": { 62 | "version": "1.0.6", 63 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 64 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 65 | }, 66 | "debug": { 67 | "version": "2.6.9", 68 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 69 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 70 | "requires": { 71 | "ms": "2.0.0" 72 | } 73 | }, 74 | "depd": { 75 | "version": "1.1.2", 76 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 77 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 78 | }, 79 | "destroy": { 80 | "version": "1.0.4", 81 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 82 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 83 | }, 84 | "ee-first": { 85 | "version": "1.1.1", 86 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 87 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 88 | }, 89 | "encodeurl": { 90 | "version": "1.0.2", 91 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 92 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 93 | }, 94 | "escape-html": { 95 | "version": "1.0.3", 96 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 97 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 98 | }, 99 | "etag": { 100 | "version": "1.8.1", 101 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 102 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 103 | }, 104 | "express": { 105 | "version": "4.17.1", 106 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 107 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 108 | "requires": { 109 | "accepts": "~1.3.7", 110 | "array-flatten": "1.1.1", 111 | "body-parser": "1.19.0", 112 | "content-disposition": "0.5.3", 113 | "content-type": "~1.0.4", 114 | "cookie": "0.4.0", 115 | "cookie-signature": "1.0.6", 116 | "debug": "2.6.9", 117 | "depd": "~1.1.2", 118 | "encodeurl": "~1.0.2", 119 | "escape-html": "~1.0.3", 120 | "etag": "~1.8.1", 121 | "finalhandler": "~1.1.2", 122 | "fresh": "0.5.2", 123 | "merge-descriptors": "1.0.1", 124 | "methods": "~1.1.2", 125 | "on-finished": "~2.3.0", 126 | "parseurl": "~1.3.3", 127 | "path-to-regexp": "0.1.7", 128 | "proxy-addr": "~2.0.5", 129 | "qs": "6.7.0", 130 | "range-parser": "~1.2.1", 131 | "safe-buffer": "5.1.2", 132 | "send": "0.17.1", 133 | "serve-static": "1.14.1", 134 | "setprototypeof": "1.1.1", 135 | "statuses": "~1.5.0", 136 | "type-is": "~1.6.18", 137 | "utils-merge": "1.0.1", 138 | "vary": "~1.1.2" 139 | } 140 | }, 141 | "finalhandler": { 142 | "version": "1.1.2", 143 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 144 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 145 | "requires": { 146 | "debug": "2.6.9", 147 | "encodeurl": "~1.0.2", 148 | "escape-html": "~1.0.3", 149 | "on-finished": "~2.3.0", 150 | "parseurl": "~1.3.3", 151 | "statuses": "~1.5.0", 152 | "unpipe": "~1.0.0" 153 | } 154 | }, 155 | "forwarded": { 156 | "version": "0.2.0", 157 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 158 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 159 | }, 160 | "fresh": { 161 | "version": "0.5.2", 162 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 163 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 164 | }, 165 | "http-errors": { 166 | "version": "1.7.2", 167 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 168 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 169 | "requires": { 170 | "depd": "~1.1.2", 171 | "inherits": "2.0.3", 172 | "setprototypeof": "1.1.1", 173 | "statuses": ">= 1.5.0 < 2", 174 | "toidentifier": "1.0.0" 175 | } 176 | }, 177 | "iconv-lite": { 178 | "version": "0.4.24", 179 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 180 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 181 | "requires": { 182 | "safer-buffer": ">= 2.1.2 < 3" 183 | } 184 | }, 185 | "inherits": { 186 | "version": "2.0.3", 187 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 188 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 189 | }, 190 | "ipaddr.js": { 191 | "version": "1.9.1", 192 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 193 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 194 | }, 195 | "media-typer": { 196 | "version": "0.3.0", 197 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 198 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 199 | }, 200 | "merge-descriptors": { 201 | "version": "1.0.1", 202 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 203 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 204 | }, 205 | "methods": { 206 | "version": "1.1.2", 207 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 208 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 209 | }, 210 | "mime": { 211 | "version": "1.6.0", 212 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 213 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 214 | }, 215 | "mime-db": { 216 | "version": "1.48.0", 217 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", 218 | "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" 219 | }, 220 | "mime-types": { 221 | "version": "2.1.31", 222 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", 223 | "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", 224 | "requires": { 225 | "mime-db": "1.48.0" 226 | } 227 | }, 228 | "ms": { 229 | "version": "2.0.0", 230 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 231 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 232 | }, 233 | "negotiator": { 234 | "version": "0.6.2", 235 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 236 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 237 | }, 238 | "on-finished": { 239 | "version": "2.3.0", 240 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 241 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 242 | "requires": { 243 | "ee-first": "1.1.1" 244 | } 245 | }, 246 | "parseurl": { 247 | "version": "1.3.3", 248 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 249 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 250 | }, 251 | "path-to-regexp": { 252 | "version": "0.1.7", 253 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 254 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 255 | }, 256 | "proxy-addr": { 257 | "version": "2.0.7", 258 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 259 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 260 | "requires": { 261 | "forwarded": "0.2.0", 262 | "ipaddr.js": "1.9.1" 263 | } 264 | }, 265 | "qs": { 266 | "version": "6.7.0", 267 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 268 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 269 | }, 270 | "range-parser": { 271 | "version": "1.2.1", 272 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 273 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 274 | }, 275 | "raw-body": { 276 | "version": "2.4.0", 277 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 278 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 279 | "requires": { 280 | "bytes": "3.1.0", 281 | "http-errors": "1.7.2", 282 | "iconv-lite": "0.4.24", 283 | "unpipe": "1.0.0" 284 | } 285 | }, 286 | "safe-buffer": { 287 | "version": "5.1.2", 288 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 289 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 290 | }, 291 | "safer-buffer": { 292 | "version": "2.1.2", 293 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 294 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 295 | }, 296 | "send": { 297 | "version": "0.17.1", 298 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 299 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 300 | "requires": { 301 | "debug": "2.6.9", 302 | "depd": "~1.1.2", 303 | "destroy": "~1.0.4", 304 | "encodeurl": "~1.0.2", 305 | "escape-html": "~1.0.3", 306 | "etag": "~1.8.1", 307 | "fresh": "0.5.2", 308 | "http-errors": "~1.7.2", 309 | "mime": "1.6.0", 310 | "ms": "2.1.1", 311 | "on-finished": "~2.3.0", 312 | "range-parser": "~1.2.1", 313 | "statuses": "~1.5.0" 314 | }, 315 | "dependencies": { 316 | "ms": { 317 | "version": "2.1.1", 318 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 319 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 320 | } 321 | } 322 | }, 323 | "serve-static": { 324 | "version": "1.14.1", 325 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 326 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 327 | "requires": { 328 | "encodeurl": "~1.0.2", 329 | "escape-html": "~1.0.3", 330 | "parseurl": "~1.3.3", 331 | "send": "0.17.1" 332 | } 333 | }, 334 | "setprototypeof": { 335 | "version": "1.1.1", 336 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 337 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 338 | }, 339 | "statuses": { 340 | "version": "1.5.0", 341 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 342 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 343 | }, 344 | "toidentifier": { 345 | "version": "1.0.0", 346 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 347 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 348 | }, 349 | "type-is": { 350 | "version": "1.6.18", 351 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 352 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 353 | "requires": { 354 | "media-typer": "0.3.0", 355 | "mime-types": "~2.1.24" 356 | } 357 | }, 358 | "unpipe": { 359 | "version": "1.0.0", 360 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 361 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 362 | }, 363 | "utils-merge": { 364 | "version": "1.0.1", 365 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 366 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 367 | }, 368 | "vary": { 369 | "version": "1.1.2", 370 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 371 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 372 | } 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /6.Express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "6.express", 3 | "version": "1.0.0", 4 | "description": "���������� Node.js�� Ư¡�� ���õ� ������ ���� �˾ƺ��ҽ��ϴ�. \r �̹� ���Ǵ� Node.js�� �� Express�� ���� �˾ƺ��ڽ��ϴ�.", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /6.Express/redirection.js: -------------------------------------------------------------------------------- 1 | const fs=require('fs'); 2 | const express=require('express'); 3 | const app=express(); 4 | 5 | app.get('/',(req,res)=>{ 6 | fs.readFile('./index.html',(err,data)=>{ 7 | if (err) 8 | throw err; 9 | res.writeHead(200,{'Content-Type':'text/html'}); 10 | res.end(data); 11 | }); 12 | }); 13 | 14 | app.get('/test',(req,res)=>{ 15 | console.log('GET /test'); 16 | res.send('GET'); 17 | }); 18 | 19 | app.post('/test',(req,res)=>{ 20 | console.log('POST /test'); 21 | res.redirect('/'); 22 | }); 23 | 24 | app.listen(3000,()=>{ 25 | console.log('Server is running on port 3000!'); 26 | }); -------------------------------------------------------------------------------- /6.Express/sendHtmlFile.js: -------------------------------------------------------------------------------- 1 | const fs=require('fs'); 2 | const express=require('express'); 3 | const app=express(); 4 | 5 | app.get('/',(req,res)=>{ 6 | fs.readFile('./index.html',(err,data)=>{ 7 | if (err) 8 | throw err; 9 | res.writeHead(200,{'Content-Type':'text/html'}); 10 | res.end(data); 11 | }); 12 | }); 13 | 14 | app.listen(3000,()=>{ 15 | console.log('Server is running on port 3000!'); 16 | }); -------------------------------------------------------------------------------- /6.Express/sendHtmlTag.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | app.get('/bold',(req,res)=>{ 5 | res.send('bold'); 6 | }); 7 | 8 | app.get('/italic',(req,res)=>{ 9 | res.send('italic'); 10 | }); 11 | 12 | app.listen(3000,()=>{ 13 | console.log('Server is running on port 3000!'); 14 | }); -------------------------------------------------------------------------------- /7.Middlewares/README.md: -------------------------------------------------------------------------------- 1 | ## 7. Middlewares 2 | 이번 강의에서는 **미들웨어**에 대해 알아보도록 하겠습니다. 3 | 본격적으로 시작하기에 앞서 미들웨어가 무엇인지 간략히 설명하고 넘어가겠습니다. 4 | 5 | 미들웨어는 단어에서도 유추 가능하듯이 중간에 위치한 소프트웨어입니다. 6 | 어느 곳의 중간이냐면, 요청(**request**)와 응답(**response**)의 중간입니다. 7 | 8 | 이번 강의의 예제들은 이전과 같이 강의자료에 제공이 되겠지만, 9 | 많은 파일을 사용하지 않을 예정이기 때문에 10 | 강의와 함께 처음부터 작성해 나가는것을 추천드립니다. 11 | 12 | --- 13 | 14 | ### (1) Middleware basic 15 | npm을 통해서 다양한 미들웨어를 설치하여 이용할수 있습니다. 16 | 17 | 하지만 그 전에, 미들웨어가 어떻게 작동되는 지 감을 잡아보도록 하겠습니다. 18 | 19 | ``` javascript 20 | //basic.js 21 | const fs=require('fs'); 22 | const express=require('express'); 23 | const app=express(); 24 | 25 | app.use((req,res,next)=>{ 26 | console.log(req.headers["user-agent"]); 27 | next(); 28 | }); 29 | 30 | app.get('/',(req,res)=>{ 31 | fs.readFile('./static/html/index.html',(err,data)=>{ 32 | if(err) 33 | throw err; 34 | res.writeHead(200,{'Content-Type':'text/html'}); 35 | res.end(data); 36 | }); 37 | }); 38 | 39 | app.listen(3000,()=>{ 40 | console.log('Server is running on port 3000!'); 41 | }); 42 | ``` 43 | 44 | 다른 부분은 이전 express 강의에서 다루었기 때문에 생략하고, 45 | 이번 예제에서 처음 등장한 `app.use`에 대해서만 설명하도록 하겠습니다. 46 | 47 | app.use의 사용법은 다음과 같습니다. 48 | `app.use(미들웨어)` 49 | 50 | 지금은 미들웨어로 콘솔창에 user-agent(=브라우저 정보)를 출력하는 함수를 사용했습니다. 51 | 52 | 미들웨어를 사용하는데 주의하실 점은 app.use의 위치입니다. 53 | app.use는 app.get 혹은 app.post보다 **먼저** 위치해 있어야 하며, 54 | app.use 사이에도 **쓰인 순서대로** 실행되기 때문에 순서가 중요합니다. 55 | 또한, 사용자 정의 미들웨어인 경우 callback함수의 매개변수가 `req`,`res`,`next` 세개가 존재하며 56 | 미들웨어의 마지막에는 `next();`로 끝내주셔야 다음 미들웨어 혹은 `app.get` / `app.post`가 실행된다는 점을 유의해 주세요. 57 | 58 | --- 59 | 60 | ### (2) Router 61 | **라우터**를 배우기 이전에 라우팅이 무엇인지 짚어보도록 하겠습니다. 62 | 사실 우리는 이미 라우팅을 사용하고 있었습니다. 63 | 라우팅은 클라이언트(=브라우저)에서 요청하는 주소에 따라 다른 처리를 하는 것을 말합니다. 64 | 6강 Express에서 `app.get()`, `app.post()` 함수를 작성하는 것도 라우팅을 한 것이죠. 65 | Router 미들웨어는 이러한 라우팅을 더욱 편하게 도와줍니다. 66 | ```javascript 67 | //router.js 68 | const fs=require('fs'); 69 | const express=require('express'); 70 | const router=express.Router(); 71 | 72 | router.get('/',(req,res)=>{ 73 | fs.readFile('./static/html/index.html',(err,data)=>{ 74 | if(err) 75 | throw err; 76 | res.writeHead(200,{'Content-Type':'text/html'}); 77 | res.end(data); 78 | }); 79 | }); 80 | 81 | module.exports=router; 82 | ``` 83 | 84 | ```javascript 85 | //routerExample.js 86 | const express=require('express'); 87 | const app=express(); 88 | 89 | const router=require('./router'); 90 | 91 | app.use((req,res,next)=>{ 92 | console.log(req.headers["user-agent"]); 93 | next(); 94 | }); 95 | 96 | app.use('/',router); 97 | 98 | app.listen(3000,()=>{ 99 | console.log('Server is running on port 3000!'); 100 | }); 101 | ``` 102 | 103 | router.js에서는 `const router=express.Router();`로 라우터를 하나 생성한 다음, 104 | 기존 example.js의 `app.get`이 수행했던 동작을 `router.get`으로 변경하여 수행되도록 했습니다. 105 | 마지막엔 `router`를 모듈으로 export했습니다. 106 | 107 | example.js에서는 router.js에서 export했던 모듈을 const router로 가져왔습니다. 108 | 그리고 기존 `app.get`이 존재했던 부분을 `app.use('/',router);`로 '/' 경로에 대해서는 router모듈로 처리하라는 미들웨어를 등록했습니다. 109 | 110 | example.js를 실행시키고 localhost:3000에 접속하면 이전 예제와 똑같은 결과가 나타나는 것을 확인할 수 있습니다. 111 | 112 | 이전까지는 localhost:3000/foo 이런식의 라우팅만 구성했습니다. 113 | 만약에 localhost:3000/foo/bar의 요청을 처리하려면 어떻게 해야 할까요?? 114 | 115 | 물론 `app.get('/foo/bar',...)` 혹은 `router.get('/foo/bar',...)`의 방식으로 처리가 가능은 할겁니다. 116 | 만약에 이렇게 라우팅한 상황에서 localhost:3000/foo/baz 라는 주소를 추가로 처리하려면 117 | `app.get('/foo/baz',...)` 혹은 `router.get('/foo/baz',...)`를 또 작성하여 처리해야할까요?? 118 | 119 | 물론 아닙니다. 120 | 두가지의 방법이 있습니다. 121 | 122 | 첫번째는 **와일드 카드**의 방법입니다. 123 | 프로그래밍을 접하면서 주로 쓰일 와일드 카드는 ? 이나 * 등이 있습니다. 124 | ?의 의미는 아무거나 한 글자를 의미하고, *는 아무거나를 의미합니다. 125 | 즉, 와일드 카드는 아무거나를 말합니다. 126 | 이런 와일드 카드를 라우팅에도 사용할수 있습니다. 127 | 128 | ```javascript 129 | //wildcard.js 130 | const express=require('express'); 131 | const app=express(); 132 | 133 | app.get('/id/:number',(req,res)=>{ 134 | const num=req.params.number; 135 | res.send(num); 136 | }); 137 | 138 | app.listen(3000,()=>{ 139 | console.log('Server is running on port 3000!'); 140 | }); 141 | ``` 142 | 143 | wildcard.js를 실행시키고 144 | localhost:3000/id/(아무문자나숫자)에 접속하면 입력한 문자나 숫자가 출력되는 것을 확인할 수 있습니다. 145 | app.get의 주소에 **콜론**(:)을 이용해 와일드 카드 number를 등록해 두었습니다. 146 | 그 다음 `req.params`로 주소의 number자리에 입력된 값을 num에 저장한후 res.send해 주었습니다. 147 | 148 | 이로써, localhost:3000/id/(아무문자나숫자)에 대한 라우팅은 해결되었습니다. 149 | 150 | 두번째는 아까 배운 router 미들웨어를 활용하는 방법입니다. 151 | 152 | 예제를 진행하기 앞서 디렉토리 구성은 다음과 같이 되어있습니다. 153 | 154 | foobar.js 155 | foo/ 156 | \-index.js 157 | \-bar.js 158 | \-baz.js 159 | 160 | ```javascript 161 | // foobar.js 162 | const express=require('express'); 163 | const app=express(); 164 | 165 | const foo=require('./foo') 166 | 167 | app.use((req,res,next)=>{ 168 | console.log(req.headers["user-agent"]); 169 | next(); 170 | }); 171 | 172 | app.use('/foo',foo); 173 | 174 | app.listen(3000,()=>{ 175 | console.log('Server is running on port 3000!'); 176 | }); 177 | ``` 178 | ```javascript 179 | // foo/index.js 180 | const express=require('express'); 181 | const router=express.Router(); 182 | 183 | const bar=require('./bar'); 184 | const baz=require('./baz'); 185 | 186 | router.get('/bar',bar.Bar); 187 | router.get('/baz',baz.Baz); 188 | 189 | module.exports=router; 190 | ``` 191 | ```javascript 192 | // foo/bar.js 193 | exports.Bar=(req,res)=>{ 194 | res.send("bar"); 195 | } 196 | ``` 197 | ```javascript 198 | // foo/baz.js 199 | exports.Baz=(req,res)=>{ 200 | res.send("baz"); 201 | } 202 | ``` 203 | 204 | foobar.js에서는 `app.use('/foo',foo)` '/foo' 경로로 들어오는 요청은 foo라는 모듈로 처리되도록 하였습니다. 205 | 하지만 foo는 js 파일이 아닌 디렉토리입니다. 이런 경우에는 index.js가 대신하게 됩니다. 206 | 따라서 `require('./foo')`는 /foo/index.js를 import하게 됩니다. 207 | 208 | index.js는 `router`를 사용하고, 이 라우터를 export합니다. 209 | 또한, 같은 foo 디렉토리안의 bar와 baz를 import합니다. 210 | /bar로 get요청이 들어오면 bar.Bar를 실행하고, 211 | /baz로 get요청이 들어오면 baz.Baz를 실행한다는 `router.get` 함수를 작성해놓았습니다. 212 | 213 | 그러나 여기서 유의하셔야 할것은 이 /foo/index.js에 접근하기 위해서는 우선 example.js에서 '/foo'에 대한 접근이 이루어져야 합니다. 214 | 그 다음에 /foo/index.js에 접근되기 때문에 215 | /foo/index.js에서 `router.get`의 '/bar', '/baz'는 216 | 실제로는 localhost:3000/foo/bar 와 localhost:3000/foo/baz의 경우에 해당합니다. 217 | 218 | bar.js와 baz.js는 bar에선 bar, baz에서는 baz를 출력합니다. 219 | 220 | 와일드카드를 사용하는 방법에 비해 router 미들웨어를 활용하는 방법이 훨씬 어려운것을 알수있습니다. 221 | 222 | 두 방법이 사용되는 경우는 상이합니다. 223 | **와일드카드**는 후술하겠지만 query string이나, request body처럼 클라이언트에서 서버로 데이터를 전송할 때, url(uri)에 데이터를 담아 전송할 때 사용됩니다. 224 | 예를 들자면 특정 게시물을 출력하는 주소가 /board/view일 때 /board/view?boardId=1234로 1234번 게시물의 내용을 보게 할 수도 있고 와일드카드를 이용하여 225 | /board/view/1234로 라우팅을 설정할 수도 있습니다. 226 | 이처럼 와일드 카드는 게시물의 내용을 반환한다는 큰 틀은 유지한 채 어떤 게시물을 출력하는 정도에 미세조정에 사용됩니다. 227 | 228 | ***Router*** 미들웨어는 와일드카드에 비해 융통성은 다소 떨어집니다. 229 | 그러나 API를 구성하는 것 처럼 url(uri)간에 기능이 서로 많이 다르고, 나올 수 있는 url를 미리 파악할 수 있을 때 사용하기 적절합니다. 230 | 소스코드를 분리하고, Router를 적절하게 사용하면 소스코드의 유지보수가 매우 편리해집니다. 231 | 232 | --- 233 | 234 | ### (3) express.static 235 | 이번에는 정적인 파일을 제공하는데 도움을 주는 **express.static** 미들웨어에 대해 알아보도록 하겠습니다. 236 | 정적인 파일은 파일을 수정하지 않는 한 일정한 결과를 보여줍니다. 237 | 이미지, .html, .css, .js파일 등이 그에 해당하고 238 | 동적인 파일은 정적인 파일과 반대로 항상 같은 결과를 보여주지는 않습니다. 239 | 다룰 예정은 아니지만 node.js의 view engine인 ejs가 이에 해당합니다. 240 | (ejs, https://araikuma.tistory.com/454) 241 | 242 | `express.static`의 사용법은 다음과 같습니다. 243 | 244 | ```javascript 245 | app.use(express.static(__dirname+'static')); 246 | ``` 247 | 248 | `express.static`에 접근 허용하고자 하는 파일의 경로를 입력해 주시면 됩니다. 249 | 250 | 예제를 살펴보겠습니다. 251 | 디렉토리 구성은 다음과 같이 되어있습니다. 252 | staticExample.js 253 | static/ 254 | \-html/ 255 | \--index.html 256 | \--staticPage.html 257 | \-css/ 258 | \--main.css 259 | \-image/ 260 | \--logo.png 261 | 262 | ```javascript 263 | //staticExample.js 264 | const express=require('express'); 265 | const app=express(); 266 | 267 | app.use(express.static(__dirname+'/static')); 268 | 269 | app.listen(3000,()=>{ 270 | console.log('Server is running on port 3000!'); 271 | }); 272 | ``` 273 | 274 | staticExample.js를 실행시킨 후 localhost:3000/html/index.html에 접속해 봅시다. 275 | 라우팅설정을 하나도 하지 않았지만 index.html페이지가 로드되는 것을 알수있습니다. 276 | 이번에는 localhost:3000/html/staticPage.html에 접속해 봅시다. 277 | staticPage.html이 로드되는 것을 알수있습니다. 278 | html파일이 로드됨과 동시에 static 폴더 내부에 있는 css과 image도 같이 성공적으로 로드되는것을 알 수 있습니다. 279 | 280 | 특정 경로에서만 `express.static`이 작동하게 할수도 있습니다. 281 | ```javascript 282 | app.use('/myhtml',express.static(__dirname+'/static'+'/html')); 283 | ``` 284 | 285 | 위 예제 코드를 다음과 같이 수정한다면 localhost:3000/myhtml/index.html이나 localhost:3000/myhtml/staticPage.html로 접근가능합니다. 286 | 287 | 이때 express.static에는 /static/html까지만 입력되었으므로 css나 image의 경로는 새로 `express.static` 미들웨어를 이용하여 추가해야 합니다. 288 | 289 | --- 290 | 291 | ### (4) body-parser 292 | 이번에는 **body-parser** 미들웨어를 이용하여 클라이언트로부터 데이터를 받고 처리하는 과정을 배워보도록 하겠습니다. 293 | 294 | body-parser 미들웨어는 주로 POST를 통한 데이터를 받을 때 사용됩니다. 295 | 그러면 GET을 이용한 데이터는 어떻게 받을까요? 296 | 297 | GET과 POST는 데이터 전송에있어 차이가 있습니다. 298 | GET방식으로 데이터를 전송하면 주소에 **쿼리스트링**(QueryString)형식으로 데이터가 담아집니다. 299 | 이에 반해 POST 방식으로 데이터를 전송하면 데이터가 **request body**에 담겨 집니다. 300 | GET 방식에 비해 한층 싸여져 있는 셈이죠. 301 | 302 | 쿼리스트링은 다음과 같은 구조를 지닙니다. 303 | 304 | 네이버에 경희대학교를 검색한 후 주소를 복사해 오겠습니다. 305 | 306 | https://search.naver.com/search.naver?sm=top_hty&fbm=1&ie=utf8&query=경희대학교 307 | 308 | search.naver.com/search.naver 이후 ?로 시작하여 =과 &이 반복되는 것을 확인할수있습니다. 309 | 310 | ?가 하는 것은 ?이후로는 쿼리스트링이라고 알려주는 것입니다. 311 | 쿼리스트링은 다음과 같은 구조가 반복됩니다. 312 | key1=value1&key2=value2&key3=value3&.... 313 | 위 주소로 다시 돌아가면 쿼리스트링은 314 | sm은 top_hty라는 값을 가지고, fbm은 1, ie는 utf8, query는 검색어인 경희대학교라는 값을 가진다는 것을 알수있습니다. 315 | 316 | 이를 서버단에서 처리하려면 어떻게 할까요?? 317 | 318 | 예제가 실행될 디렉토리 구성은 다음과 같습니다. 319 | 320 | qsExample.js 321 | static/ 322 | \-html/ 323 | \--qsExample.html 324 | 325 | ```javascript 326 | //qsExample.js 327 | const express=require('express'); 328 | const app=express(); 329 | 330 | app.use(express.static(__dirname+'/static')); 331 | 332 | app.get('/send',(req,res)=>{ 333 | const text=req.query.text; 334 | console.log("Received text:"+text); 335 | res.send('text='+text); 336 | }); 337 | 338 | app.listen(3000,()=>{ 339 | console.log('Server in running on port 3000!'); 340 | }); 341 | ``` 342 | 343 | `app.get('/send')` 내부의 `req.query.text`를 통해 이름이 text인 값의 value를 가져왔습니다. 344 | 여기서 key값을 결정하는 것은 qsExample.html파일의 input 태그의 name 속성입니다. 이부분은 POST에서도 쓰이니 꼭 기억해 주세요. 345 | 346 | qsExample.js를 실행시키고 localhost:3000/html/qsExample.html에 접속한 다음 아무 텍스트나 입력하고 확인 버튼을 눌러 봅시다. 347 | 348 | 입력한 값이 /send뒤에 쿼리스트링으로 들어가고 콘솔창과 브라우저 화면에 입력한 값이 출력되는 것을 확인할수있습니다. 349 | 350 | 이번에는 POST의 경우를 다뤄보겠습니다. 351 | 차이점은 `express.json()`, `express.urlencoded()` 미들웨어를 등록해 주어야 하고 352 | GET에서는 쿼리스트링으로 데이터가 주어지기 때문에 `req.query`로 가져왔지만, 353 | POST에서는 body에 담겨 오기 때문에 `req.body`로 접근해주시면 됩니다. 354 | 355 | 356 | 357 | ```javascript 358 | //bodyparserExample.js 359 | const express=require('express'); 360 | const app=express(); 361 | 362 | app.use(express.static(__dirname+'/static')); 363 | app.use(express.json()); 364 | app.use(express.urlencoded({extended:false})); 365 | 366 | app.post('/send',(req,res)=>{ 367 | const text=req.body.text; 368 | console.log('Received text:'+text); 369 | res.send(text); 370 | }); 371 | 372 | app.listen(3000,()=>{ 373 | console.log('Server is running on port 3000!'); 374 | }); 375 | ``` 376 | 377 | `app.use`에서 json과 urlencoded 방식으로 request body를 받기 위해 미들웨어 `express.json()`과 `express.urlencoded()`를 등록하였고, 378 | (urlencoded, https://weicomes.tistory.com/10) 379 | `app.post`를 이용하여 라우팅하였습니다. 380 | 이전 예제와 다르게 데이터에 `req.body`로 접근했음을 유의해주세요. 381 | 382 | localhost:3000/html/index.html에 접속하고 텍스트를 입력한 다음 확인을 누르면 이전 예제와 마찬가지로 입력한 문자가 브라우저와 콘솔창에 출력이 됩니다. 383 | 384 | --- 385 | 386 | ### (5) Assignment 387 | 회원가입을 하는 signup.html 페이지와 로그인을 하는 login.html 페이지가 강의 자료에 있습니다. 388 | fs 모듈을 사용하셔도 무방하고 express.static 미들웨어를 사용하셔도 상관은 없습니다. 389 | 다음 기능을 하는 app.js를 만들어 주세요. 390 | 391 | 1. localhost:3000에 접속하면 바로 회원가입 페이지로 redirect 된다. 392 | 2. 회원 가입은 아이디(userId)와 비밀번호(password)로만 필요로 한다. 393 | 3. 회원 가입 창에서 아이디와 비밀번호를 입력하고 회원 가입 버튼을 누르면 '/signup'의 주소로 데이터가 담긴 채 POST 요청이 간다. 394 | 4. 서버에는 users라는 json array를 가지고 있다. 회원 가입 시 전송된 아이디와 비밀번호중 아이디가 users에 이미 존재하는지 확인한다. 395 | 5. 존재하면 `res.send('User already exists');`를 실행하고 존재하지 않으면 users에 새로운 JSON을 추가하고 로그인 페이지로 redirect 시킨다. 396 | 6. 로그인창은 마찬가지로 아이디와 비밀번호를 받는다. 로그인 버튼을 누르면 '/login'의 주소로 POST 요청이 간다. 397 | 7. 서버는 users 배열에서 해당 아이디가 있는지 확인하고 만약 없다면 `res.send('ID wrong');`을 실행하고 아이디가 있지만 비밀번호가 일치하지 않으면 `res.send('Password wrong');`을 실행하고 아이디와 비밀번호가 모두 올바르면 `res.send("Welcome "+userId+"!");`를 실행하여 환영문구를 작성한다. 398 | -------------------------------------------------------------------------------- /7.Middlewares/app.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | app.listen(3000,()=>{ 5 | console.log('Server is running on port 3000!'); 6 | }); -------------------------------------------------------------------------------- /7.Middlewares/basic.js: -------------------------------------------------------------------------------- 1 | const fs=require('fs'); 2 | const express=require('express'); 3 | const app=express(); 4 | 5 | app.use((req,res,next)=>{ 6 | console.log(req.headers["user-agent"]); 7 | next(); 8 | }); 9 | 10 | app.get('/',(req,res)=>{ 11 | fs.readFile('./static/html/index.html',(err,data)=>{ 12 | if(err) 13 | throw err; 14 | res.writeHead(200,{'Content-Type':'text/html'}); 15 | res.end(data); 16 | }); 17 | }); 18 | 19 | app.listen(3000,()=>{ 20 | console.log('Server is running on port 3000!'); 21 | }); -------------------------------------------------------------------------------- /7.Middlewares/bodyparserExample.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | app.use(express.static(__dirname+'/static')); 5 | app.use(express.json()); 6 | app.use(express.urlencoded({extended:false})); 7 | 8 | app.post('/send',(req,res)=>{ 9 | const text=req.body.text; 10 | console.log('Received text:'+text); 11 | res.send(text); 12 | }); 13 | 14 | app.listen(3000,()=>{ 15 | console.log('Server is running on port 3000!'); 16 | }); -------------------------------------------------------------------------------- /7.Middlewares/foo/bar.js: -------------------------------------------------------------------------------- 1 | exports.Bar=(req,res)=>{ 2 | res.send("bar"); 3 | } -------------------------------------------------------------------------------- /7.Middlewares/foo/baz.js: -------------------------------------------------------------------------------- 1 | exports.Baz=(req,res)=>{ 2 | res.send("baz"); 3 | } -------------------------------------------------------------------------------- /7.Middlewares/foo/index.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const router=express.Router(); 3 | 4 | const bar=require('./bar'); 5 | const baz=require('./baz'); 6 | 7 | router.get('/bar',bar.Bar); 8 | router.get('/baz',baz.Baz); 9 | 10 | module.exports=router; -------------------------------------------------------------------------------- /7.Middlewares/foobar.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | const foo=require('./foo') 5 | 6 | app.use((req,res,next)=>{ 7 | console.log(req.headers["user-agent"]); 8 | next(); 9 | }); 10 | 11 | app.use('/foo',foo); 12 | 13 | app.listen(3000,()=>{ 14 | console.log('Server is running on port 3000!'); 15 | }); -------------------------------------------------------------------------------- /7.Middlewares/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "7.middlewares", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.19.0", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 24 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 25 | "requires": { 26 | "bytes": "3.1.0", 27 | "content-type": "~1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "~1.1.2", 30 | "http-errors": "1.7.2", 31 | "iconv-lite": "0.4.24", 32 | "on-finished": "~2.3.0", 33 | "qs": "6.7.0", 34 | "raw-body": "2.4.0", 35 | "type-is": "~1.6.17" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.1.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 41 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.3", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 46 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 47 | "requires": { 48 | "safe-buffer": "5.1.2" 49 | } 50 | }, 51 | "content-type": { 52 | "version": "1.0.4", 53 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 54 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 55 | }, 56 | "cookie": { 57 | "version": "0.4.0", 58 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 59 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 60 | }, 61 | "cookie-signature": { 62 | "version": "1.0.6", 63 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 64 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 65 | }, 66 | "debug": { 67 | "version": "2.6.9", 68 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 69 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 70 | "requires": { 71 | "ms": "2.0.0" 72 | } 73 | }, 74 | "depd": { 75 | "version": "1.1.2", 76 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 77 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 78 | }, 79 | "destroy": { 80 | "version": "1.0.4", 81 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 82 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 83 | }, 84 | "ee-first": { 85 | "version": "1.1.1", 86 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 87 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 88 | }, 89 | "encodeurl": { 90 | "version": "1.0.2", 91 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 92 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 93 | }, 94 | "escape-html": { 95 | "version": "1.0.3", 96 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 97 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 98 | }, 99 | "etag": { 100 | "version": "1.8.1", 101 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 102 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 103 | }, 104 | "express": { 105 | "version": "4.17.1", 106 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 107 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 108 | "requires": { 109 | "accepts": "~1.3.7", 110 | "array-flatten": "1.1.1", 111 | "body-parser": "1.19.0", 112 | "content-disposition": "0.5.3", 113 | "content-type": "~1.0.4", 114 | "cookie": "0.4.0", 115 | "cookie-signature": "1.0.6", 116 | "debug": "2.6.9", 117 | "depd": "~1.1.2", 118 | "encodeurl": "~1.0.2", 119 | "escape-html": "~1.0.3", 120 | "etag": "~1.8.1", 121 | "finalhandler": "~1.1.2", 122 | "fresh": "0.5.2", 123 | "merge-descriptors": "1.0.1", 124 | "methods": "~1.1.2", 125 | "on-finished": "~2.3.0", 126 | "parseurl": "~1.3.3", 127 | "path-to-regexp": "0.1.7", 128 | "proxy-addr": "~2.0.5", 129 | "qs": "6.7.0", 130 | "range-parser": "~1.2.1", 131 | "safe-buffer": "5.1.2", 132 | "send": "0.17.1", 133 | "serve-static": "1.14.1", 134 | "setprototypeof": "1.1.1", 135 | "statuses": "~1.5.0", 136 | "type-is": "~1.6.18", 137 | "utils-merge": "1.0.1", 138 | "vary": "~1.1.2" 139 | } 140 | }, 141 | "finalhandler": { 142 | "version": "1.1.2", 143 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 144 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 145 | "requires": { 146 | "debug": "2.6.9", 147 | "encodeurl": "~1.0.2", 148 | "escape-html": "~1.0.3", 149 | "on-finished": "~2.3.0", 150 | "parseurl": "~1.3.3", 151 | "statuses": "~1.5.0", 152 | "unpipe": "~1.0.0" 153 | } 154 | }, 155 | "forwarded": { 156 | "version": "0.2.0", 157 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 158 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 159 | }, 160 | "fresh": { 161 | "version": "0.5.2", 162 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 163 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 164 | }, 165 | "http-errors": { 166 | "version": "1.7.2", 167 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 168 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 169 | "requires": { 170 | "depd": "~1.1.2", 171 | "inherits": "2.0.3", 172 | "setprototypeof": "1.1.1", 173 | "statuses": ">= 1.5.0 < 2", 174 | "toidentifier": "1.0.0" 175 | } 176 | }, 177 | "iconv-lite": { 178 | "version": "0.4.24", 179 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 180 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 181 | "requires": { 182 | "safer-buffer": ">= 2.1.2 < 3" 183 | } 184 | }, 185 | "inherits": { 186 | "version": "2.0.3", 187 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 188 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 189 | }, 190 | "ipaddr.js": { 191 | "version": "1.9.1", 192 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 193 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 194 | }, 195 | "media-typer": { 196 | "version": "0.3.0", 197 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 198 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 199 | }, 200 | "merge-descriptors": { 201 | "version": "1.0.1", 202 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 203 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 204 | }, 205 | "methods": { 206 | "version": "1.1.2", 207 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 208 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 209 | }, 210 | "mime": { 211 | "version": "1.6.0", 212 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 213 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 214 | }, 215 | "mime-db": { 216 | "version": "1.48.0", 217 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", 218 | "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" 219 | }, 220 | "mime-types": { 221 | "version": "2.1.31", 222 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", 223 | "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", 224 | "requires": { 225 | "mime-db": "1.48.0" 226 | } 227 | }, 228 | "ms": { 229 | "version": "2.0.0", 230 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 231 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 232 | }, 233 | "negotiator": { 234 | "version": "0.6.2", 235 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 236 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 237 | }, 238 | "on-finished": { 239 | "version": "2.3.0", 240 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 241 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 242 | "requires": { 243 | "ee-first": "1.1.1" 244 | } 245 | }, 246 | "parseurl": { 247 | "version": "1.3.3", 248 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 249 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 250 | }, 251 | "path-to-regexp": { 252 | "version": "0.1.7", 253 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 254 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 255 | }, 256 | "proxy-addr": { 257 | "version": "2.0.7", 258 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 259 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 260 | "requires": { 261 | "forwarded": "0.2.0", 262 | "ipaddr.js": "1.9.1" 263 | } 264 | }, 265 | "qs": { 266 | "version": "6.7.0", 267 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 268 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 269 | }, 270 | "range-parser": { 271 | "version": "1.2.1", 272 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 273 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 274 | }, 275 | "raw-body": { 276 | "version": "2.4.0", 277 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 278 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 279 | "requires": { 280 | "bytes": "3.1.0", 281 | "http-errors": "1.7.2", 282 | "iconv-lite": "0.4.24", 283 | "unpipe": "1.0.0" 284 | } 285 | }, 286 | "safe-buffer": { 287 | "version": "5.1.2", 288 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 289 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 290 | }, 291 | "safer-buffer": { 292 | "version": "2.1.2", 293 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 294 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 295 | }, 296 | "send": { 297 | "version": "0.17.1", 298 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 299 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 300 | "requires": { 301 | "debug": "2.6.9", 302 | "depd": "~1.1.2", 303 | "destroy": "~1.0.4", 304 | "encodeurl": "~1.0.2", 305 | "escape-html": "~1.0.3", 306 | "etag": "~1.8.1", 307 | "fresh": "0.5.2", 308 | "http-errors": "~1.7.2", 309 | "mime": "1.6.0", 310 | "ms": "2.1.1", 311 | "on-finished": "~2.3.0", 312 | "range-parser": "~1.2.1", 313 | "statuses": "~1.5.0" 314 | }, 315 | "dependencies": { 316 | "ms": { 317 | "version": "2.1.1", 318 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 319 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 320 | } 321 | } 322 | }, 323 | "serve-static": { 324 | "version": "1.14.1", 325 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 326 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 327 | "requires": { 328 | "encodeurl": "~1.0.2", 329 | "escape-html": "~1.0.3", 330 | "parseurl": "~1.3.3", 331 | "send": "0.17.1" 332 | } 333 | }, 334 | "setprototypeof": { 335 | "version": "1.1.1", 336 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 337 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 338 | }, 339 | "statuses": { 340 | "version": "1.5.0", 341 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 342 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 343 | }, 344 | "toidentifier": { 345 | "version": "1.0.0", 346 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 347 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 348 | }, 349 | "type-is": { 350 | "version": "1.6.18", 351 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 352 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 353 | "requires": { 354 | "media-typer": "0.3.0", 355 | "mime-types": "~2.1.24" 356 | } 357 | }, 358 | "unpipe": { 359 | "version": "1.0.0", 360 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 361 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 362 | }, 363 | "utils-merge": { 364 | "version": "1.0.1", 365 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 366 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 367 | }, 368 | "vary": { 369 | "version": "1.1.2", 370 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 371 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 372 | } 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /7.Middlewares/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "7.middlewares", 3 | "version": "1.0.0", 4 | "description": "이번 강의에서는 미들웨어에 대해 알아보도록 하겠습니다. \r 본격적으로 시작하기에 앞서 미들웨어가 무엇인지 간략히 설명하고 넘어가겠습니다.", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /7.Middlewares/qsExample.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | app.use(express.static(__dirname+'/static')); 5 | 6 | app.get('/send',(req,res)=>{ 7 | const text=req.query.text; 8 | console.log("Received text:"+text); 9 | res.send('text='+text); 10 | }); 11 | 12 | app.listen(3000,()=>{ 13 | console.log('Server in running on port 3000!'); 14 | }); -------------------------------------------------------------------------------- /7.Middlewares/router.js: -------------------------------------------------------------------------------- 1 | const fs=require('fs'); 2 | const express=require('express'); 3 | const router=express.Router(); 4 | 5 | router.get('/',(req,res)=>{ 6 | fs.readFile('./static/html/index.html',(err,data)=>{ 7 | if(err) 8 | throw err; 9 | res.writeHead(200,{'Content-Type':'text/html'}); 10 | res.end(data); 11 | }); 12 | }); 13 | 14 | module.exports=router; -------------------------------------------------------------------------------- /7.Middlewares/routerExample.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | const router=require('./router'); 5 | 6 | app.use((req,res,next)=>{ 7 | console.log(req.headers["user-agent"]); 8 | next(); 9 | }); 10 | 11 | app.use('/',router); 12 | 13 | app.listen(3000,()=>{ 14 | console.log('Server is running on port 3000!'); 15 | }); -------------------------------------------------------------------------------- /7.Middlewares/static/css/main.css: -------------------------------------------------------------------------------- 1 | a { 2 | color:green; 3 | } 4 | -------------------------------------------------------------------------------- /7.Middlewares/static/html/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 |

This is index page

8 |
9 | 10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /7.Middlewares/static/html/login.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Login 6 | 7 | 8 |

Login

9 |
10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /7.Middlewares/static/html/qsExample.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 |

This is index page

8 |
9 | 10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /7.Middlewares/static/html/signup.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Sign Up 6 | 7 | 8 |

Sign Up

9 |
10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /7.Middlewares/static/html/staticPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /7.Middlewares/static/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/7.Middlewares/static/image/logo.png -------------------------------------------------------------------------------- /7.Middlewares/staticExample.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | app.use(express.static(__dirname+'/static')); 5 | 6 | app.listen(3000,()=>{ 7 | console.log('Server is running on port 3000!'); 8 | }); -------------------------------------------------------------------------------- /7.Middlewares/wildcard.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const app=express(); 3 | 4 | app.get('/id/:number',(req,res)=>{ 5 | const num=req.params.number; 6 | res.send(num); 7 | }); 8 | 9 | app.listen(3000,()=>{ 10 | console.log('Server is running on port 3000!'); 11 | }); -------------------------------------------------------------------------------- /8.Cookies & Session/README.md: -------------------------------------------------------------------------------- 1 | ## 8. Cookies & Session 2 | 이번 강의는 **쿠키**와 **세션**에 대해 다뤄보도록 하겠습니다. 3 | 4 | 쿠키와 세션이 무엇인지 알아보기 전에 쿠키와 세션이 필요하게 된 배경에 대해서 알아보도록하겠습니다. 5 | HTTP 통신은 두가지의 특성을 지닙니다. 6 | - 비연결 (Connectionless) 7 | - 무상태 (Stateless) 8 | 9 | **Connectionless**는 클라이언트(=브라우저)가 서버에게 request를 보내고, 서버는 클라이언트에게 response를 보내면 더 이상의 연결을 하지 않는다는 뜻입니다. 10 | 이를 확인할 수 있는 부분은, 우리가 한 사이트에 접속하여 완전한 페이지를 받았으면 다른 사이트로 이동하지 않는 이상 인터넷과 연결을 해제해도 사이트 화면이 그대로 유지됩니다. 11 | 만약 서버와 계속 통신하고 있는 상태였다면 인터넷이 끊긴 즉시 이미 불러온 페이지도 볼 수 없게 됩니다. 12 | 13 | **Stateless**는 상태정보를 저장하지 않아 이전 request와 무관한 request를 독립적으로 처리한다는 의미입니다. 14 | Connectionless에 의해 request와 response를 주고 받아 통신이 끊어진다면 이전 상태 정보는 저장하지 않습니다. 15 | 16 | 하지만 우리가 웹서비스를 이용하면서 사용하는 17 | - 자동 로그인 18 | - 쇼핑몰의 최근 본 상품 목록 19 | - 오늘 더이상 팝업을 보지 않음 20 | 21 | 등은 우리가 자동 로그인에 체크를 한 것과, 어떤 상품을 보았는지, 팝업 안보겠다고 체크한 것까지 누군가(클라이언트 혹은 서버)는 기억을 했기에 구현되는 기능입니다. 22 | 23 | HTTP 프로토콜은 이런 기억을 못합니다. 24 | 그렇다면 누가 기억하는 걸까요? 25 | 바로 **쿠키**와 **세션**입니다. 26 | 27 | 쿠키와 세션은 금붕어의 기억력을 가진 HTTP 프로토콜의 단점을 보완하기 위해 등장했습니다. 28 | 29 | 쿠키와 세션의 역할은 비슷하나 차이점은 누가 기억하는지에 따라 달려있습니다. 30 | 31 | 쿠키는 클라이언트와 서버가 사용하며 **클라이언트가 기억**하는 것이고, 세션은 서버가 사용하며 **서버가 기억하는 것**입니다. 32 | 33 | --- 34 | 35 | ### (1) Cookies 36 | 일단 쿠키를 눈으로 직접 찾아 보겠습니다. 37 | 이전 강의와 동일하게 네이버에 접속해서 F12를 눌러보도록 하겠습니다. 38 | 39 | 6강 Express에서는 network 탭을 들여다 보았지만 이번에는 Application 탭을 눌러봅시다. 40 | 그러고 개발자 도구의 좌측을 보면 Storage의 Cookies를 찾을 수 있습니다. 41 | 42 | 그 다음에 https://www.naver.com 을 보면 다음과 같은 창이 출력됩니다. 43 | 44 | 45 | 46 | 위 화면에서 알 수 있는 것은 7개의 쿠키가 존재하며, 각각의 값과 도메인, 경로, 만료일 등이 나타나있습니다. 47 | 이러한 쿠키들은 클라이언트에서 서버로 request를 보낼 때 자동으로 포함이 됩니다. 48 | 쿠키들이 어떻게 포함되었나 확인해 보시려면 개발자 도구의 network 탭에서 클라이언트가 서버로 request를 보낼 때 49 | request headers를 확인하면 cookie: 하고 포함된 쿠키들이 name=value; 가 반복된 형태로 들어가게 됩니다. 50 | 51 | 이러한 쿠키들은 어떻게 만들어지는 걸까요? 52 | response headers의 set-cookie를 통해 name=value;형식으로 입력해 준다면 클라이언트에 쿠키가 생성됩니다. 53 | 또는 클라이언트에서 자바스크립트를 이용해 다음과 같이 쿠키를 생성할수도 있습니다. 54 | ```javascript 55 | document.cookie="name=value"; 56 | ``` 57 | 58 | 이번엔 cookie-parser 미들웨어를 이용해서 쿠키를 생성하고 호출하는 예제를 진행하도록 하겠습니다. 59 | 60 | cookie-parser 모듈을 설치해 봅시다. 61 | ```bash 62 | npm install cookie-parser --save 63 | ``` 64 | ```javascript 65 | //cookieParser.js 66 | const cookieParser=require('cookie-parser'); 67 | const fs=require('fs'); 68 | const express=require('express'); 69 | 70 | const app=express(); 71 | 72 | app.use(cookieParser()); 73 | app.use(express.json()); 74 | app.use(express.urlencoded({extended:false})); 75 | 76 | app.get('/',(req,res)=>{ 77 | fs.readFile('./static/html/index.html',(err,data)=>{ 78 | if(err) throw err; 79 | res.writeHead(200,{'Content-Type':'text/html'}); 80 | res.end(data); 81 | }); 82 | }); 83 | 84 | app.get('/get',(req,res)=>{ 85 | res.send(req.cookies); 86 | }); 87 | 88 | app.post('/set',(req,res)=>{ 89 | const value=req.body.value; 90 | res.cookie('fruit',value,{maxAge:2*60*1000}); // expires after 2 min. 91 | res.redirect('/'); 92 | }); 93 | 94 | app.listen(3000,()=>{ 95 | console.log('Server is running on port 3000!'); 96 | }); 97 | ``` 98 | `app.use(cookieParser());`를 통해 cookie-parser 미들웨어를 등록해 줍니다. 99 | cookie-parser를 사용하면 `req.cookies`로 cookie 값을 읽어올 수 있습니다. 100 | /get으로 GET 요청을 보내면 현재 브라우저에 저장된 쿠키들을 출력해 줍니다. 101 | /set으로 POST 요청을 보내면 fruit이라는 이름의 쿠키의 value를 설정해 줍니다. 102 | 103 | cookieParser.js를 실행하고 localhost:3000에 접속해 봅시다. 104 | fruit에 넣을 value를 입력하고 submit 버튼을 눌러봅시다. 105 | 그 다음에 localhost:3000/get에 접속하면 방금 입력한 fruit의 값이 반영된 쿠키들을 조회할 수 있습니다. 106 | localhost:3000 으로 되돌아가서 새로운 값을 입력하고 localhost:3000/get에 접속하면 fruit의 값이 바뀐것을 알 수 있습니다. 107 | 108 | --- 109 | 110 | ### (2) Session 111 | 쿠키는 웹 브라우저에 데이터를 저장하는 방식이었습니다. 112 | 그런데, 로그인 상태를 유지할 때도 쿠키를 사용하면 어떻게 될까요? 113 | 쿠키에 아이디와 비밀번호를 저장한다면 우리가 개발자 도구를 통하여 보았듯이 누구나 볼수 있고, 또한 수정도 가능하게 될 것입니다. 114 | 보안상 매우 취약해 진 것이죠. 115 | 그래서 로그인 상태와 같은 보안상 중요한 정보는 웹 브라우저가 아닌 웹 서버에 저장 됩니다. 116 | 이를 세션이라고 합니다. 117 | 118 | 그렇다면 서버는 어떻게 여러 사용자를 구분할 수 있을까요? 119 | 서버는 세션을 저장함과 동시에 클라이언트에게 세션을 구분할 수 있는 세션 아이디를 쿠키에 저장하게 합니다. 120 | 클라이언트가 서버에게 request를 할 때 담겨오는 쿠키의 세션 아이디를 통해 사용자를 식별할 수 있습니다. 121 | 122 | 세션을 이용하기 위해서는 express-session이라는 모듈을 사용해야합니다. 123 | 우선 설치해 봅시다. 124 | ```bash 125 | npm install express-session --save 126 | ``` 127 | 128 | ```javascript 129 | //sessionExapmle.js 130 | const express=require('express'); 131 | const fs=require('fs'); 132 | const app=express(); 133 | const session=require('express-session'); 134 | 135 | app.use(express.urlencoded({extended:false})); 136 | app.use(express.json()); 137 | app.use(session({ 138 | secret:'keyboard cat', 139 | resave:false, 140 | saveUninitialized:true 141 | })); 142 | 143 | app.get('/',(req,res)=>{ 144 | fs.readFile('./static/html/index.html',(err,data)=>{ 145 | if(err) throw err; 146 | res.writeHead(200,{'Content-Type':'text/html'}); 147 | res.end(data); 148 | }); 149 | }); 150 | 151 | app.get('/get',(req,res)=>{ 152 | if(!req.session.fruit){ 153 | res.send('No session!'); 154 | } 155 | else{ 156 | res.send(req.session.fruit); 157 | } 158 | }); 159 | 160 | app.post('/set',(req,res)=>{ 161 | const fruit=req.body.value; 162 | req.session.fruit=fruit; 163 | res.redirect('/'); 164 | }); 165 | 166 | app.get('/delete',(req,res)=>{ 167 | req.session.destroy((err)=>{ 168 | if(err) throw err; 169 | res.redirect('/'); 170 | }); 171 | }); 172 | 173 | app.listen(3000,()=>{ 174 | console.log('Server is running on port 3000!'); 175 | }); 176 | ``` 177 | const session=require('express-session');으로 express-session 모듈을 불러왔습니다. 178 | app.use(session({...}));으로 미들웨어를 등록했습니다. 179 | 이 안의 option들을 간단히 설명하자면 180 | - secret : 쿠키를 임의로 변조하는 것을 방지하기 위해, 세션 암호화에 사용되는 값. 181 | - resave : 세션을 언제나 저장할지 결정. 182 | - saveUninitialized : 세션이 저장되기 전에 uninitialized 상태로 저장할 지. 183 | 184 | (express-session options, https://www.npmjs.com/package/express-session#options) 185 | 186 | 이제 req.session.(name)으로 세션에 접근할 수 있습니다. 187 | 188 | '/get'으로 GET 요청을 보내면 우선 session에 fruit이 있는지 확인합니다. 189 | fruit 정의되어 있으면 fruit의 값을 req.session.fruit을 출력해 줍니다. 190 | 만약 fruit이 정의되지 않았으면 'No session!'을 출력해 줍니다. 191 | 192 | '/set'으로 POST 요청을 보내면 req.session.fruit의 값을 설정하거나 변경해 줍니다. 193 | 194 | '/delete'로 GET 요청을 보내면 세션을 삭제하는 req.session.destory을 실행하고, callback 함수를 실행합니다. 195 | 196 | localhost:3000에 접속하여 fruit의 값을 입력하여 submit 버튼을 눌러 줍니다. 197 | localhost:3000/get에 접속하면 방금 입력된 값을 확인할 수 있습니다. 198 | 이전의 쿠키에서의 예제와 비슷한 기능을 합니다. 199 | 차이점을 한번 살펴보겠습니다. 200 | 개발자도구(F12) - Application - Stroage - Cookie를 확인해 봅시다. 201 | 202 | 별다른 설정을 하지 않았으면 connect.sid라는 쿠키가 생성된 것을 알 수 있습니다. 203 | 웹 서버는 세션을 생성하고 이 세션의 식별자인 session id(=sid)를 암호화하여 웹브라우저에 전달해 줍니다. 204 | 서버는 웹 브라우저의 세션 아이디로 세션을 식별할 수 있습니다. 205 | 206 | localhost:3000/delete를 실행하면 세션이 삭제되고, localhost:3000/get에 접속하면 No session이라고 출력이 되는것을 확인할 수 있습니다. 207 | 208 | ### (3) Assignment 209 | 7강 Middlewares의 과제 app.js에 다음 추가기능을 구현해 봅시다. 210 | 1. signup.html에 학과, 이름 등 4가지 회원정보 추가. login.html은 이전과 동일. 211 | 1. app.js의 users array를 1.에서 추가한 회원정보의 맞게 수정, 회원 가입시 추가된 회원정보도 함께 고려. 212 | 1. 이전과 동일하게 /login 으로 POST 요청을 보내면 로그인 진행후, req.session.userId에 회원 아이디(userId) 저장 후 '/profile'로 redirect 213 | 1. '/profile'에 GET 요청이 들어오면 res.session.userId를 바탕으로 users array에서 회원을 찾은 후, 비밀번호를 제외한 회원정보를 자유롭게 출력. 214 | -------------------------------------------------------------------------------- /8.Cookies & Session/cookieParser.js: -------------------------------------------------------------------------------- 1 | const cookieParser=require('cookie-parser'); 2 | const fs=require('fs'); 3 | const express=require('express'); 4 | 5 | const app=express(); 6 | 7 | app.use(cookieParser()); 8 | app.use(express.json()); 9 | app.use(express.urlencoded({extended:false})); 10 | 11 | app.get('/',(req,res)=>{ 12 | fs.readFile('./static/html/index.html',(err,data)=>{ 13 | if(err) throw err; 14 | res.writeHead(200,{'Content-Type':'text/html'}); 15 | res.end(data); 16 | }); 17 | }); 18 | 19 | app.get('/get',(req,res)=>{ 20 | res.send(req.cookies); 21 | }); 22 | 23 | app.post('/set',(req,res)=>{ 24 | const value=req.body.value; 25 | res.cookie('fruit',value,{maxAge:2*60*1000}); // expires after 2 min. 26 | res.redirect('/'); 27 | }); 28 | 29 | app.listen(3000,()=>{ 30 | console.log('Server is running on port 3000!'); 31 | }); 32 | -------------------------------------------------------------------------------- /8.Cookies & Session/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "8.cookies", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.19.0", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 24 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 25 | "requires": { 26 | "bytes": "3.1.0", 27 | "content-type": "~1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "~1.1.2", 30 | "http-errors": "1.7.2", 31 | "iconv-lite": "0.4.24", 32 | "on-finished": "~2.3.0", 33 | "qs": "6.7.0", 34 | "raw-body": "2.4.0", 35 | "type-is": "~1.6.17" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.1.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 41 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.3", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 46 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 47 | "requires": { 48 | "safe-buffer": "5.1.2" 49 | } 50 | }, 51 | "content-type": { 52 | "version": "1.0.4", 53 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 54 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 55 | }, 56 | "cookie": { 57 | "version": "0.4.0", 58 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 59 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 60 | }, 61 | "cookie-parser": { 62 | "version": "1.4.5", 63 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", 64 | "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", 65 | "requires": { 66 | "cookie": "0.4.0", 67 | "cookie-signature": "1.0.6" 68 | } 69 | }, 70 | "cookie-signature": { 71 | "version": "1.0.6", 72 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 73 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 74 | }, 75 | "debug": { 76 | "version": "2.6.9", 77 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 78 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 79 | "requires": { 80 | "ms": "2.0.0" 81 | } 82 | }, 83 | "depd": { 84 | "version": "1.1.2", 85 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 86 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 87 | }, 88 | "destroy": { 89 | "version": "1.0.4", 90 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 91 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 92 | }, 93 | "ee-first": { 94 | "version": "1.1.1", 95 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 96 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 97 | }, 98 | "encodeurl": { 99 | "version": "1.0.2", 100 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 101 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 102 | }, 103 | "escape-html": { 104 | "version": "1.0.3", 105 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 106 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 107 | }, 108 | "etag": { 109 | "version": "1.8.1", 110 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 111 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 112 | }, 113 | "express": { 114 | "version": "4.17.1", 115 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 116 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 117 | "requires": { 118 | "accepts": "~1.3.7", 119 | "array-flatten": "1.1.1", 120 | "body-parser": "1.19.0", 121 | "content-disposition": "0.5.3", 122 | "content-type": "~1.0.4", 123 | "cookie": "0.4.0", 124 | "cookie-signature": "1.0.6", 125 | "debug": "2.6.9", 126 | "depd": "~1.1.2", 127 | "encodeurl": "~1.0.2", 128 | "escape-html": "~1.0.3", 129 | "etag": "~1.8.1", 130 | "finalhandler": "~1.1.2", 131 | "fresh": "0.5.2", 132 | "merge-descriptors": "1.0.1", 133 | "methods": "~1.1.2", 134 | "on-finished": "~2.3.0", 135 | "parseurl": "~1.3.3", 136 | "path-to-regexp": "0.1.7", 137 | "proxy-addr": "~2.0.5", 138 | "qs": "6.7.0", 139 | "range-parser": "~1.2.1", 140 | "safe-buffer": "5.1.2", 141 | "send": "0.17.1", 142 | "serve-static": "1.14.1", 143 | "setprototypeof": "1.1.1", 144 | "statuses": "~1.5.0", 145 | "type-is": "~1.6.18", 146 | "utils-merge": "1.0.1", 147 | "vary": "~1.1.2" 148 | } 149 | }, 150 | "express-session": { 151 | "version": "1.17.2", 152 | "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", 153 | "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", 154 | "requires": { 155 | "cookie": "0.4.1", 156 | "cookie-signature": "1.0.6", 157 | "debug": "2.6.9", 158 | "depd": "~2.0.0", 159 | "on-headers": "~1.0.2", 160 | "parseurl": "~1.3.3", 161 | "safe-buffer": "5.2.1", 162 | "uid-safe": "~2.1.5" 163 | }, 164 | "dependencies": { 165 | "cookie": { 166 | "version": "0.4.1", 167 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", 168 | "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" 169 | }, 170 | "depd": { 171 | "version": "2.0.0", 172 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 173 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 174 | }, 175 | "safe-buffer": { 176 | "version": "5.2.1", 177 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 178 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 179 | } 180 | } 181 | }, 182 | "finalhandler": { 183 | "version": "1.1.2", 184 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 185 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 186 | "requires": { 187 | "debug": "2.6.9", 188 | "encodeurl": "~1.0.2", 189 | "escape-html": "~1.0.3", 190 | "on-finished": "~2.3.0", 191 | "parseurl": "~1.3.3", 192 | "statuses": "~1.5.0", 193 | "unpipe": "~1.0.0" 194 | } 195 | }, 196 | "forwarded": { 197 | "version": "0.2.0", 198 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 199 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 200 | }, 201 | "fresh": { 202 | "version": "0.5.2", 203 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 204 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 205 | }, 206 | "http-errors": { 207 | "version": "1.7.2", 208 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 209 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 210 | "requires": { 211 | "depd": "~1.1.2", 212 | "inherits": "2.0.3", 213 | "setprototypeof": "1.1.1", 214 | "statuses": ">= 1.5.0 < 2", 215 | "toidentifier": "1.0.0" 216 | } 217 | }, 218 | "iconv-lite": { 219 | "version": "0.4.24", 220 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 221 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 222 | "requires": { 223 | "safer-buffer": ">= 2.1.2 < 3" 224 | } 225 | }, 226 | "inherits": { 227 | "version": "2.0.3", 228 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 229 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 230 | }, 231 | "ipaddr.js": { 232 | "version": "1.9.1", 233 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 234 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 235 | }, 236 | "media-typer": { 237 | "version": "0.3.0", 238 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 239 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 240 | }, 241 | "merge-descriptors": { 242 | "version": "1.0.1", 243 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 244 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 245 | }, 246 | "methods": { 247 | "version": "1.1.2", 248 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 249 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 250 | }, 251 | "mime": { 252 | "version": "1.6.0", 253 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 254 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 255 | }, 256 | "mime-db": { 257 | "version": "1.48.0", 258 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", 259 | "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" 260 | }, 261 | "mime-types": { 262 | "version": "2.1.31", 263 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", 264 | "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", 265 | "requires": { 266 | "mime-db": "1.48.0" 267 | } 268 | }, 269 | "ms": { 270 | "version": "2.0.0", 271 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 272 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 273 | }, 274 | "negotiator": { 275 | "version": "0.6.2", 276 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 277 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 278 | }, 279 | "on-finished": { 280 | "version": "2.3.0", 281 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 282 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 283 | "requires": { 284 | "ee-first": "1.1.1" 285 | } 286 | }, 287 | "on-headers": { 288 | "version": "1.0.2", 289 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 290 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 291 | }, 292 | "parseurl": { 293 | "version": "1.3.3", 294 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 295 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 296 | }, 297 | "path-to-regexp": { 298 | "version": "0.1.7", 299 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 300 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 301 | }, 302 | "proxy-addr": { 303 | "version": "2.0.7", 304 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 305 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 306 | "requires": { 307 | "forwarded": "0.2.0", 308 | "ipaddr.js": "1.9.1" 309 | } 310 | }, 311 | "qs": { 312 | "version": "6.7.0", 313 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 314 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 315 | }, 316 | "random-bytes": { 317 | "version": "1.0.0", 318 | "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", 319 | "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" 320 | }, 321 | "range-parser": { 322 | "version": "1.2.1", 323 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 324 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 325 | }, 326 | "raw-body": { 327 | "version": "2.4.0", 328 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 329 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 330 | "requires": { 331 | "bytes": "3.1.0", 332 | "http-errors": "1.7.2", 333 | "iconv-lite": "0.4.24", 334 | "unpipe": "1.0.0" 335 | } 336 | }, 337 | "safe-buffer": { 338 | "version": "5.1.2", 339 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 340 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 341 | }, 342 | "safer-buffer": { 343 | "version": "2.1.2", 344 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 345 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 346 | }, 347 | "send": { 348 | "version": "0.17.1", 349 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 350 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 351 | "requires": { 352 | "debug": "2.6.9", 353 | "depd": "~1.1.2", 354 | "destroy": "~1.0.4", 355 | "encodeurl": "~1.0.2", 356 | "escape-html": "~1.0.3", 357 | "etag": "~1.8.1", 358 | "fresh": "0.5.2", 359 | "http-errors": "~1.7.2", 360 | "mime": "1.6.0", 361 | "ms": "2.1.1", 362 | "on-finished": "~2.3.0", 363 | "range-parser": "~1.2.1", 364 | "statuses": "~1.5.0" 365 | }, 366 | "dependencies": { 367 | "ms": { 368 | "version": "2.1.1", 369 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 370 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 371 | } 372 | } 373 | }, 374 | "serve-static": { 375 | "version": "1.14.1", 376 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 377 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 378 | "requires": { 379 | "encodeurl": "~1.0.2", 380 | "escape-html": "~1.0.3", 381 | "parseurl": "~1.3.3", 382 | "send": "0.17.1" 383 | } 384 | }, 385 | "setprototypeof": { 386 | "version": "1.1.1", 387 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 388 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 389 | }, 390 | "statuses": { 391 | "version": "1.5.0", 392 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 393 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 394 | }, 395 | "toidentifier": { 396 | "version": "1.0.0", 397 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 398 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 399 | }, 400 | "type-is": { 401 | "version": "1.6.18", 402 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 403 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 404 | "requires": { 405 | "media-typer": "0.3.0", 406 | "mime-types": "~2.1.24" 407 | } 408 | }, 409 | "uid-safe": { 410 | "version": "2.1.5", 411 | "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", 412 | "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", 413 | "requires": { 414 | "random-bytes": "~1.0.0" 415 | } 416 | }, 417 | "unpipe": { 418 | "version": "1.0.0", 419 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 420 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 421 | }, 422 | "utils-merge": { 423 | "version": "1.0.1", 424 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 425 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 426 | }, 427 | "vary": { 428 | "version": "1.1.2", 429 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 430 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 431 | } 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /8.Cookies & Session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "8.cookies", 3 | "version": "1.0.0", 4 | "description": "이번 강의는 쿠키와 세션에 대해 다뤄보도록 하겠습니다.", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "cookie-parser": "^1.4.5", 13 | "express": "^4.17.1", 14 | "express-session": "^1.17.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /8.Cookies & Session/sessionExample.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const fs=require('fs'); 3 | const app=express(); 4 | const session=require('express-session'); 5 | 6 | app.use(express.urlencoded({extended:false})); 7 | app.use(express.json()); 8 | app.use(session({ 9 | secret:'keyboard cat', 10 | resave:false, 11 | saveUninitialized:true 12 | })); 13 | 14 | app.get('/',(req,res)=>{ 15 | fs.readFile('./static/html/index.html',(err,data)=>{ 16 | if(err) throw err; 17 | res.writeHead(200,{'Content-Type':'text/html'}); 18 | res.end(data); 19 | }); 20 | }); 21 | 22 | app.get('/get',(req,res)=>{ 23 | if(!req.session.fruit){ 24 | res.send('No session!'); 25 | } 26 | else{ 27 | res.send(req.session.fruit); 28 | } 29 | }); 30 | 31 | app.post('/set',(req,res)=>{ 32 | const fruit=req.body.value; 33 | req.session.fruit=fruit; 34 | res.redirect('/'); 35 | }); 36 | 37 | app.get('/delete',(req,res)=>{ 38 | req.session.destroy((err)=>{ 39 | if (err) throw err; 40 | res.redirect('/'); 41 | }); 42 | }); 43 | 44 | app.listen(3000,()=>{ 45 | console.log('Server is running on port 3000!'); 46 | }); -------------------------------------------------------------------------------- /8.Cookies & Session/static/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /9.Examples/Crawler/README.md: -------------------------------------------------------------------------------- 1 | # Cralwer Example 2 | ## Files 3 | - naverNews.js : 네이버 뉴스 크롤링 4 | - baekjoon.js : 백준 사용자 정보 및 최근 제출 크롤링 5 | ## Installation 6 | ```bash 7 | npm install 8 | ``` 9 | ## Basic Usage 10 | ### axios 11 | [NPM](https://www.npmjs.com/package/axios) 12 | 1. GET 13 | ```javascript 14 | // in async function 15 | const res=await axios.get(URL,config); 16 | ``` 17 | 2. POST 18 | ```javascript 19 | // in async function 20 | const res=await axios.post(URL,data,config); 21 | ``` 22 | ### puppeteer 23 | [NPM](https://www.npmjs.com/package/puppeteer) 24 | 1. Launch browser 25 | ```javascript 26 | // both in async function 27 | const browser=await puppeteer.launch(); // headless mode 28 | const browser=await puppeteer.launch({headless:false}); // non-headless mode 29 | ``` 30 | 2. Create and Move page 31 | ```javascript 32 | // both in async function 33 | const page=await browser.newPage(); 34 | await page.goto(URL); 35 | ``` 36 | 3. Get source 37 | ```javascript 38 | // in async function 39 | const source=await page.content(); 40 | ``` 41 | ### cheerio 42 | [NPM](https://www.npmjs.com/package/cheerio) 43 | 1. Load HTML 44 | ```javascript 45 | const $=cheerio.load(htmlContent); 46 | ``` 47 | 2. Select 48 | ```javascript 49 | const someTags=$(selector); 50 | ``` 51 | 3. Get text 52 | ```javascript 53 | // one element 54 | const text=$(oneTag).text(); 55 | // many elements 56 | let textArr=[]; 57 | for(let tag of someTags){ 58 | textArr.push($(tag).text()); 59 | } 60 | ``` -------------------------------------------------------------------------------- /9.Examples/Crawler/baekjoon.js: -------------------------------------------------------------------------------- 1 | // 모듈 임포트 2 | const puppeteer=require('puppeteer'); 3 | const cheerio=require('cheerio'); 4 | const axios=require('axios'); 5 | 6 | const userId=''; // 백준 아이디 7 | const password=''; // 백준 비밀번호 8 | 9 | // puppeteer를 이용해 로그인하고 쿠키를 리턴 10 | const Login=()=>{ 11 | return new Promise(async(resolve,reject)=>{ 12 | try{ 13 | const browser=await puppeteer.launch({headless:false}); // 간혹가다 recaptcha를 풀어야하기 때문에 headless는 false로 설정 14 | const page=await browser.newPage(); // 새로운 탭 생성 15 | await page.goto('https://www.acmicpc.net/login?next=%2F'); // 백준 로그인창으로 이동 16 | await page.type('input[name=login_user_id]',userId); // 아이디 입력 17 | await page.type('input[name=login_password]',password); // 비밀번호 입력 18 | await page.click('#submit_button'); // 로그인 버튼 클릭 19 | await page.waitForSelector('a.username',{timeout:120000}); // recaptcha를 풀어야 하는 경우, 로그인이 완료될 때 까지 최대 2분 기다림 20 | const cookies=await page.cookies(); // 쿠키를 가져옴 21 | await browser.close(); // 브라우저 닫음 22 | resolve(cookies); // 쿠키 반환 23 | } 24 | catch(err){ 25 | reject(err); 26 | } 27 | }); 28 | } 29 | 30 | // 받은 쿠키를 이용해 사용자 정보 출력 31 | const GetUserInfo=(cookies)=>{ 32 | return new Promise(async(resolve,reject)=>{ 33 | try{ 34 | let value=''; 35 | // 쿠키중 OnlineJudge 부분만 따로 저장 36 | for(let cookie of cookies){ 37 | if(cookie.name==='OnlineJudge'){ 38 | value=cookie.value; 39 | } 40 | } 41 | const option={headers:{Cookie:`OnlineJudge=${value};`}}; // 쿠키를 request header에 담음 42 | const res=await axios.get(`https://acmicpc.net/user/${userId}`,option); // 사용자 정보 페이지 이동 43 | const $=cheerio.load(res.data); // html을 load하여 jQuery와 유사하게 사용 가능 44 | const trs=$('table#statics > tbody > tr'); // 사용자 정보 부분 select 45 | // 각 행에 대해 정보 출력 46 | for(let tr of trs){ 47 | console.log(`${$(tr).find('th').text()}: ${$(tr).find('td').text().trim()}`); 48 | } 49 | resolve(option); // request option 반환 50 | } 51 | catch(err){ 52 | reject(err); 53 | } 54 | }); 55 | } 56 | 57 | // 로그인한 유저의 가장 최근 제출한 소스코드 출력 58 | const GetCurrentSolution=(option)=>{ 59 | return new Promise(async(resolve,reject)=>{ 60 | try{ 61 | const params={user_id:userId,language_id:-1,result_id:4}; // user_id: 사용자, 언어는 신경 x, 맞은 결과만 출력 62 | option.params=params; // option.params에 대입 63 | const resSubmission=await axios.get('https://www.acmicpc.net/status',option); // 제출 목록 request 64 | let $=cheerio.load(resSubmission.data); // html을 load하여 jQuery와 유사하게 사용 가능 65 | const submissions=$('tr'); // 제출 목록에 해당하는 tr select 66 | // 푼 문제가 없을 경우, throw 67 | if(submissions.length===1){ 68 | throw '푼 문제가 없습니다.'; 69 | } 70 | else{ 71 | const url=$('tr:nth-child(2) > td:nth-child(7) > a:nth-child(2)').attr('href'); // 가장 위에 있는 제출의 소스코드 수정에 해당하는 href select 72 | delete option.params; // 이전에 사용했던 params 삭제 73 | const resCode=await axios.get('https://www.acmicpc.net'+url,option); // 소스코드 페이지 request 74 | $=cheerio.load(resCode.data); // html을 load하여 jQuery와 유사하게 사용 가능 75 | console.log('\n문제번호 :',url.split('/')[2]); // 문제번호 출력 76 | console.log($('textarea').text()); // 소스코드에 해당하는 textarea select하여 출력 77 | } 78 | } 79 | catch(err){ 80 | reject(err); 81 | } 82 | }); 83 | } 84 | 85 | Login() 86 | .then(GetUserInfo) 87 | .then(GetCurrentSolution) 88 | .catch((err)=>{ 89 | console.error(err); 90 | }); 91 | -------------------------------------------------------------------------------- /9.Examples/Crawler/naverNews.js: -------------------------------------------------------------------------------- 1 | // 모듈 임포트 2 | const axios=require('axios'); 3 | const cheerio=require('cheerio'); 4 | 5 | const query=''; // 검색어 6 | 7 | // 검색결과 중 뉴스탭의 html를 반환 8 | const Search=(query)=>{ 9 | return new Promise(async (resolve,reject)=>{ 10 | try{ 11 | const params={where:'news',query:query} // 검색어와 뉴스탭 12 | const res=await axios.get(`https://search.naver.com/search.naver`,{params:params}); // params가 url의 query string 형식으로 들어감 13 | resolve(res.data) // html 반환 14 | } 15 | catch(err){ 16 | reject(err); 17 | } 18 | }); 19 | } 20 | 21 | // html을 가지고 기사의 제목과 링크를 출력 22 | const Parse=(content)=>{ 23 | return new Promise(async(resolve,reject)=>{ 24 | try{ 25 | const $=cheerio.load(content); // html을 load하여 jQuery와 유사하게 사용 가능 26 | const titles=$('div.news_area > a.news_tit'); // 뉴스의 제목들을 select 27 | // 각각의 타이틀에 대해 28 | for (let title of titles){ 29 | console.log(`${$(title).attr('title')} (${$(title).attr('href')})`); // 뉴스 제목 (링크) 형식으로 출력 30 | } 31 | } 32 | catch(err){ 33 | reject(err); 34 | } 35 | }); 36 | } 37 | 38 | Search(query) 39 | .then(Parse) 40 | .catch((err)=>{ 41 | console.error(err); 42 | }); -------------------------------------------------------------------------------- /9.Examples/Crawler/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crawler", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "15.12.4", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", 10 | "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==", 11 | "optional": true 12 | }, 13 | "@types/yauzl": { 14 | "version": "2.9.1", 15 | "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", 16 | "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", 17 | "optional": true, 18 | "requires": { 19 | "@types/node": "*" 20 | } 21 | }, 22 | "agent-base": { 23 | "version": "6.0.2", 24 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 25 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 26 | "requires": { 27 | "debug": "4" 28 | } 29 | }, 30 | "axios": { 31 | "version": "0.21.1", 32 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", 33 | "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", 34 | "requires": { 35 | "follow-redirects": "^1.10.0" 36 | } 37 | }, 38 | "balanced-match": { 39 | "version": "1.0.2", 40 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 41 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 42 | }, 43 | "base64-js": { 44 | "version": "1.5.1", 45 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 46 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 47 | }, 48 | "bl": { 49 | "version": "4.1.0", 50 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 51 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 52 | "requires": { 53 | "buffer": "^5.5.0", 54 | "inherits": "^2.0.4", 55 | "readable-stream": "^3.4.0" 56 | } 57 | }, 58 | "boolbase": { 59 | "version": "1.0.0", 60 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 61 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" 62 | }, 63 | "brace-expansion": { 64 | "version": "1.1.11", 65 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 66 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 67 | "requires": { 68 | "balanced-match": "^1.0.0", 69 | "concat-map": "0.0.1" 70 | } 71 | }, 72 | "buffer": { 73 | "version": "5.7.1", 74 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 75 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 76 | "requires": { 77 | "base64-js": "^1.3.1", 78 | "ieee754": "^1.1.13" 79 | } 80 | }, 81 | "buffer-crc32": { 82 | "version": "0.2.13", 83 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 84 | "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" 85 | }, 86 | "cheerio": { 87 | "version": "1.0.0-rc.10", 88 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", 89 | "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", 90 | "requires": { 91 | "cheerio-select": "^1.5.0", 92 | "dom-serializer": "^1.3.2", 93 | "domhandler": "^4.2.0", 94 | "htmlparser2": "^6.1.0", 95 | "parse5": "^6.0.1", 96 | "parse5-htmlparser2-tree-adapter": "^6.0.1", 97 | "tslib": "^2.2.0" 98 | } 99 | }, 100 | "cheerio-select": { 101 | "version": "1.5.0", 102 | "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", 103 | "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", 104 | "requires": { 105 | "css-select": "^4.1.3", 106 | "css-what": "^5.0.1", 107 | "domelementtype": "^2.2.0", 108 | "domhandler": "^4.2.0", 109 | "domutils": "^2.7.0" 110 | } 111 | }, 112 | "chownr": { 113 | "version": "1.1.4", 114 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 115 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" 116 | }, 117 | "concat-map": { 118 | "version": "0.0.1", 119 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 120 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 121 | }, 122 | "css-select": { 123 | "version": "4.1.3", 124 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", 125 | "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", 126 | "requires": { 127 | "boolbase": "^1.0.0", 128 | "css-what": "^5.0.0", 129 | "domhandler": "^4.2.0", 130 | "domutils": "^2.6.0", 131 | "nth-check": "^2.0.0" 132 | } 133 | }, 134 | "css-what": { 135 | "version": "5.0.1", 136 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz", 137 | "integrity": "sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==" 138 | }, 139 | "debug": { 140 | "version": "4.3.1", 141 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 142 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 143 | "requires": { 144 | "ms": "2.1.2" 145 | } 146 | }, 147 | "devtools-protocol": { 148 | "version": "0.0.883894", 149 | "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.883894.tgz", 150 | "integrity": "sha512-33idhm54QJzf3Q7QofMgCvIVSd2o9H3kQPWaKT/fhoZh+digc+WSiMhbkeG3iN79WY4Hwr9G05NpbhEVrsOYAg==" 151 | }, 152 | "dom-serializer": { 153 | "version": "1.3.2", 154 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", 155 | "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", 156 | "requires": { 157 | "domelementtype": "^2.0.1", 158 | "domhandler": "^4.2.0", 159 | "entities": "^2.0.0" 160 | } 161 | }, 162 | "domelementtype": { 163 | "version": "2.2.0", 164 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", 165 | "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" 166 | }, 167 | "domhandler": { 168 | "version": "4.2.0", 169 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", 170 | "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", 171 | "requires": { 172 | "domelementtype": "^2.2.0" 173 | } 174 | }, 175 | "domutils": { 176 | "version": "2.7.0", 177 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", 178 | "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", 179 | "requires": { 180 | "dom-serializer": "^1.0.1", 181 | "domelementtype": "^2.2.0", 182 | "domhandler": "^4.2.0" 183 | } 184 | }, 185 | "end-of-stream": { 186 | "version": "1.4.4", 187 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 188 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 189 | "requires": { 190 | "once": "^1.4.0" 191 | } 192 | }, 193 | "entities": { 194 | "version": "2.2.0", 195 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", 196 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" 197 | }, 198 | "extract-zip": { 199 | "version": "2.0.1", 200 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", 201 | "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", 202 | "requires": { 203 | "@types/yauzl": "^2.9.1", 204 | "debug": "^4.1.1", 205 | "get-stream": "^5.1.0", 206 | "yauzl": "^2.10.0" 207 | } 208 | }, 209 | "fd-slicer": { 210 | "version": "1.1.0", 211 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", 212 | "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", 213 | "requires": { 214 | "pend": "~1.2.0" 215 | } 216 | }, 217 | "find-up": { 218 | "version": "4.1.0", 219 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 220 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 221 | "requires": { 222 | "locate-path": "^5.0.0", 223 | "path-exists": "^4.0.0" 224 | } 225 | }, 226 | "follow-redirects": { 227 | "version": "1.14.1", 228 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", 229 | "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" 230 | }, 231 | "fs-constants": { 232 | "version": "1.0.0", 233 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 234 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" 235 | }, 236 | "fs.realpath": { 237 | "version": "1.0.0", 238 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 239 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 240 | }, 241 | "get-stream": { 242 | "version": "5.2.0", 243 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 244 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", 245 | "requires": { 246 | "pump": "^3.0.0" 247 | } 248 | }, 249 | "glob": { 250 | "version": "7.1.7", 251 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", 252 | "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", 253 | "requires": { 254 | "fs.realpath": "^1.0.0", 255 | "inflight": "^1.0.4", 256 | "inherits": "2", 257 | "minimatch": "^3.0.4", 258 | "once": "^1.3.0", 259 | "path-is-absolute": "^1.0.0" 260 | } 261 | }, 262 | "htmlparser2": { 263 | "version": "6.1.0", 264 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", 265 | "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", 266 | "requires": { 267 | "domelementtype": "^2.0.1", 268 | "domhandler": "^4.0.0", 269 | "domutils": "^2.5.2", 270 | "entities": "^2.0.0" 271 | } 272 | }, 273 | "https-proxy-agent": { 274 | "version": "5.0.0", 275 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", 276 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", 277 | "requires": { 278 | "agent-base": "6", 279 | "debug": "4" 280 | } 281 | }, 282 | "ieee754": { 283 | "version": "1.2.1", 284 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 285 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 286 | }, 287 | "inflight": { 288 | "version": "1.0.6", 289 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 290 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 291 | "requires": { 292 | "once": "^1.3.0", 293 | "wrappy": "1" 294 | } 295 | }, 296 | "inherits": { 297 | "version": "2.0.4", 298 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 299 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 300 | }, 301 | "locate-path": { 302 | "version": "5.0.0", 303 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 304 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 305 | "requires": { 306 | "p-locate": "^4.1.0" 307 | } 308 | }, 309 | "minimatch": { 310 | "version": "3.0.4", 311 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 312 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 313 | "requires": { 314 | "brace-expansion": "^1.1.7" 315 | } 316 | }, 317 | "minimist": { 318 | "version": "1.2.5", 319 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 320 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 321 | }, 322 | "mkdirp": { 323 | "version": "0.5.5", 324 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 325 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 326 | "requires": { 327 | "minimist": "^1.2.5" 328 | } 329 | }, 330 | "ms": { 331 | "version": "2.1.2", 332 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 333 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 334 | }, 335 | "node-fetch": { 336 | "version": "2.6.1", 337 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 338 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 339 | }, 340 | "nth-check": { 341 | "version": "2.0.0", 342 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", 343 | "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", 344 | "requires": { 345 | "boolbase": "^1.0.0" 346 | } 347 | }, 348 | "once": { 349 | "version": "1.4.0", 350 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 351 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 352 | "requires": { 353 | "wrappy": "1" 354 | } 355 | }, 356 | "p-limit": { 357 | "version": "2.3.0", 358 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 359 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 360 | "requires": { 361 | "p-try": "^2.0.0" 362 | } 363 | }, 364 | "p-locate": { 365 | "version": "4.1.0", 366 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 367 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 368 | "requires": { 369 | "p-limit": "^2.2.0" 370 | } 371 | }, 372 | "p-try": { 373 | "version": "2.2.0", 374 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 375 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" 376 | }, 377 | "parse5": { 378 | "version": "6.0.1", 379 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", 380 | "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" 381 | }, 382 | "parse5-htmlparser2-tree-adapter": { 383 | "version": "6.0.1", 384 | "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", 385 | "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", 386 | "requires": { 387 | "parse5": "^6.0.1" 388 | } 389 | }, 390 | "path-exists": { 391 | "version": "4.0.0", 392 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 393 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" 394 | }, 395 | "path-is-absolute": { 396 | "version": "1.0.1", 397 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 398 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 399 | }, 400 | "pend": { 401 | "version": "1.2.0", 402 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 403 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" 404 | }, 405 | "pkg-dir": { 406 | "version": "4.2.0", 407 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", 408 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", 409 | "requires": { 410 | "find-up": "^4.0.0" 411 | } 412 | }, 413 | "progress": { 414 | "version": "2.0.1", 415 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", 416 | "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==" 417 | }, 418 | "proxy-from-env": { 419 | "version": "1.1.0", 420 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 421 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 422 | }, 423 | "pump": { 424 | "version": "3.0.0", 425 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 426 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 427 | "requires": { 428 | "end-of-stream": "^1.1.0", 429 | "once": "^1.3.1" 430 | } 431 | }, 432 | "puppeteer": { 433 | "version": "10.0.0", 434 | "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-10.0.0.tgz", 435 | "integrity": "sha512-AxHvCb9IWmmP3gMW+epxdj92Gglii+6Z4sb+W+zc2hTTu10HF0yg6hGXot5O74uYkVqG3lfDRLfnRpi6WOwi5A==", 436 | "requires": { 437 | "debug": "4.3.1", 438 | "devtools-protocol": "0.0.883894", 439 | "extract-zip": "2.0.1", 440 | "https-proxy-agent": "5.0.0", 441 | "node-fetch": "2.6.1", 442 | "pkg-dir": "4.2.0", 443 | "progress": "2.0.1", 444 | "proxy-from-env": "1.1.0", 445 | "rimraf": "3.0.2", 446 | "tar-fs": "2.0.0", 447 | "unbzip2-stream": "1.3.3", 448 | "ws": "7.4.6" 449 | } 450 | }, 451 | "readable-stream": { 452 | "version": "3.6.0", 453 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 454 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 455 | "requires": { 456 | "inherits": "^2.0.3", 457 | "string_decoder": "^1.1.1", 458 | "util-deprecate": "^1.0.1" 459 | } 460 | }, 461 | "rimraf": { 462 | "version": "3.0.2", 463 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 464 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 465 | "requires": { 466 | "glob": "^7.1.3" 467 | } 468 | }, 469 | "safe-buffer": { 470 | "version": "5.2.1", 471 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 472 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 473 | }, 474 | "string_decoder": { 475 | "version": "1.3.0", 476 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 477 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 478 | "requires": { 479 | "safe-buffer": "~5.2.0" 480 | } 481 | }, 482 | "tar-fs": { 483 | "version": "2.0.0", 484 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", 485 | "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", 486 | "requires": { 487 | "chownr": "^1.1.1", 488 | "mkdirp": "^0.5.1", 489 | "pump": "^3.0.0", 490 | "tar-stream": "^2.0.0" 491 | } 492 | }, 493 | "tar-stream": { 494 | "version": "2.2.0", 495 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 496 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 497 | "requires": { 498 | "bl": "^4.0.3", 499 | "end-of-stream": "^1.4.1", 500 | "fs-constants": "^1.0.0", 501 | "inherits": "^2.0.3", 502 | "readable-stream": "^3.1.1" 503 | } 504 | }, 505 | "through": { 506 | "version": "2.3.8", 507 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 508 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 509 | }, 510 | "tslib": { 511 | "version": "2.3.0", 512 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", 513 | "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" 514 | }, 515 | "unbzip2-stream": { 516 | "version": "1.3.3", 517 | "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", 518 | "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", 519 | "requires": { 520 | "buffer": "^5.2.1", 521 | "through": "^2.3.8" 522 | } 523 | }, 524 | "util-deprecate": { 525 | "version": "1.0.2", 526 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 527 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 528 | }, 529 | "wrappy": { 530 | "version": "1.0.2", 531 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 532 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 533 | }, 534 | "ws": { 535 | "version": "7.4.6", 536 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 537 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" 538 | }, 539 | "yauzl": { 540 | "version": "2.10.0", 541 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", 542 | "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", 543 | "requires": { 544 | "buffer-crc32": "~0.2.3", 545 | "fd-slicer": "~1.1.0" 546 | } 547 | } 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /9.Examples/Crawler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crawler", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "axios": "^0.21.1", 13 | "cheerio": "^1.0.0-rc.10", 14 | "puppeteer": "^10.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/.env: -------------------------------------------------------------------------------- 1 | SERVER_PORT=3000 2 | SESSION_SECRET=secret 3 | DB_URL=mongodb://localhost:27017/sample 4 | REPEAT_NUM=1357 -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/README.md: -------------------------------------------------------------------------------- 1 | # MongoDB Example 2 | ## Develop Environment 3 | - Node.js 14.16.1 4 | - Npm 7.19.0 5 | - MongoDB 4.4.0 6 | ## How To Run 7 | 1. Prerequsites 8 | MongoDB가 설치되어 있어야 합니다. 9 | 2. Install modules 10 | ```bash 11 | npm install 12 | ``` 13 | 3. Run 14 | ```bash 15 | node app.js 16 | ``` 17 | ## APIs 18 | - POST /api/user/signup : 사용자 회원가입 API 19 | - POST /api/user/login : 사용자 로그인 API 20 | - POST /api/user/logout : 사용자 로그아웃 API 21 | - GET /api/user/info : 사용자 정보 API 22 | 23 | ## Modules 24 | - express : 웹 프레임워크 25 | - mongoose : MongoDB ODM 라이브러리 26 | - dotenv : .env 파일에 configuration 작성하여 process.env에 로드 27 | (**주의** .env파일은 원래 .gitignore에 등록돼야 함.) 28 | - crypto-js : 암호화 라이브러리 29 | - express-session : 세션 라이브러리 30 | - connect-mongo : 세션을 서버 메모리가 아닌 데이터베이스에 저장 31 | - morgan : 로깅(logging) 미들웨어 32 | -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/api/index.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const router=express.Router(); 3 | 4 | router.use('/user',require('./user')); 5 | 6 | module.exports=router; -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/api/user/index.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const router=express.Router(); 3 | 4 | router.post('/signup',require('./signup')); 5 | router.post('/login',require('./login')); 6 | router.post('/logout',require('./logout')); 7 | router.get('/info',require('./info')); 8 | 9 | module.exports=router; -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/api/user/info.js: -------------------------------------------------------------------------------- 1 | const Info=(req,res)=>{ 2 | if(!req.session.user){ 3 | res.status(403).json({message:'Not Logined!'}); 4 | } 5 | else{ 6 | res.status(200).json({message:'success',data:req.session.user}); 7 | } 8 | } 9 | 10 | module.exports=Info; -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/api/user/login.js: -------------------------------------------------------------------------------- 1 | const CryptoJS=require('crypto-js'); 2 | const User=require('../../models/user'); 3 | 4 | const Login=(req,res)=>{ 5 | const userId=req.body.userId; 6 | const password=req.body.password; 7 | 8 | const DataCheck=()=>{ 9 | return new Promise((resolve,reject)=>{ 10 | if(!userId || !password){ 11 | reject({message:'Request Body Error!'}); 12 | } 13 | else{ 14 | resolve(); 15 | } 16 | }); 17 | } 18 | 19 | const UserCheck=()=>{ 20 | return new Promise(async(resolve,reject)=>{ 21 | try{ 22 | const user=await User.find({userId:userId}); 23 | if(user.length===0){ 24 | reject({message:'userId Error!'}); 25 | } 26 | else{ 27 | resolve(user[0]); 28 | } 29 | } 30 | catch(e){ 31 | reject(e); 32 | } 33 | 34 | }); 35 | } 36 | 37 | const PasswordCheck=(user)=>{ 38 | return new Promise(async(resolve,reject)=>{ 39 | const salt=user.salt; 40 | const hashedPassword=CryptoJS.PBKDF2(password,salt,{keySize:16,iterations:Number(process.env.REPEAT_NUM)}).toString(); 41 | console.log(hashedPassword); 42 | console.log(user.hashedPassword); 43 | if(hashedPassword!==user.hashedPassword){ 44 | reject({message:'Password Error!'}); 45 | } 46 | else{ 47 | resolve(user); 48 | } 49 | }); 50 | } 51 | 52 | const SetSession=(user)=>{ 53 | return new Promise((resolve,reject)=>{ 54 | req.session.user={userId:user.userId,name:user.name}; 55 | req.session.save(()=>{ 56 | resolve(); 57 | }); 58 | }); 59 | } 60 | 61 | DataCheck() 62 | .then(UserCheck) 63 | .then(PasswordCheck) 64 | .then(SetSession) 65 | .then(()=>{ 66 | res.status(200).json({message:'Success'}); 67 | }) 68 | .catch((err)=>{ 69 | console.error(err); 70 | res.status(500).json(err); 71 | }); 72 | } 73 | 74 | module.exports=Login; -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/api/user/logout.js: -------------------------------------------------------------------------------- 1 | const Logout=(req,res)=>{ 2 | req.session.destroy(()=>{ 3 | res.status(200).json({message:'success'}); 4 | }); 5 | } 6 | 7 | module.exports=Logout; -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/api/user/signup.js: -------------------------------------------------------------------------------- 1 | const CryptoJS=require('crypto-js'); 2 | const User=require('../../models/user'); 3 | 4 | const Signup=(req,res)=>{ 5 | const {userId, name, email, password}=req.body; 6 | 7 | const DataCheck=()=>{ 8 | return new Promise((resolve,reject)=>{ 9 | if(!userId || !name || !email){ 10 | reject({message:'Request Body Error!'}); 11 | } 12 | else{ 13 | resolve(); 14 | } 15 | }); 16 | } 17 | 18 | const UserCheck=()=>{ 19 | return new Promise(async(resolve,reject)=>{ 20 | const user=await User.find({userId:userId}); 21 | if(user.length!==0){ 22 | reject({message:'Duplicated ID Error!'}); 23 | } 24 | else{ 25 | resolve(); 26 | } 27 | }); 28 | } 29 | 30 | const GeneratePassword=()=>{ 31 | return new Promise(async(resolve,reject)=>{ 32 | try{ 33 | const salt=CryptoJS.lib.WordArray.random(16).toString(); 34 | const hashedPassword=CryptoJS.PBKDF2(password,salt,{keySize:16,iterations:Number(process.env.REPEAT_NUM)}).toString(); 35 | const user=new User({ 36 | userId:userId, 37 | name:name, 38 | email:email, 39 | hashedPassword:hashedPassword, 40 | salt:salt 41 | }); 42 | await user.save(); 43 | resolve(); 44 | } 45 | catch(err){ 46 | reject(err); 47 | } 48 | }); 49 | } 50 | 51 | DataCheck() 52 | .then(UserCheck) 53 | .then(GeneratePassword) 54 | .then(()=>{ 55 | res.status(200).json({message:'Success'}); 56 | }) 57 | .catch((err)=>{ 58 | console.error(err); 59 | res.status(500).json(err); 60 | }) 61 | } 62 | 63 | module.exports=Signup; -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/app.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const session=require('express-session'); 3 | const morgan=require('morgan'); 4 | const cookieParser=require('cookie-parser'); 5 | const mongoose=require('mongoose'); 6 | const MongoStore=require('connect-mongo'); 7 | const path=require('path'); 8 | require('dotenv').config(); 9 | const app=express(); 10 | 11 | app.use(morgan('combined')); 12 | app.use(express.json()); 13 | app.use(express.urlencoded({extended:false})); 14 | app.use(cookieParser()); 15 | app.use(session({ 16 | secret:process.env.SESSION_SECRET, 17 | resave:false, 18 | saveUninitialized:true, 19 | maxAge:7*24*60*60*1000, 20 | store:MongoStore.create({ 21 | mongoUrl:process.env.DB_URL, 22 | collection:"sessions" 23 | }) 24 | })); 25 | app.use('/js',express.static(path.join('static','js'))); 26 | app.use('/css',express.static(path.join('static','js'))); 27 | 28 | app.use('/api',require('./api')); 29 | app.use('/',require('./routes')); 30 | 31 | mongoose 32 | .connect(process.env.DB_URL,{ 33 | useNewUrlParser:true, 34 | useFindAndModify:false, 35 | useUnifiedTopology:true, 36 | }) 37 | .then(()=>{ 38 | console.log('Connected to MongoDB'); 39 | }) 40 | .catch((e)=>{ 41 | console.error(e); 42 | }); 43 | 44 | app.listen(process.env.SERVER_PORT,()=>{ 45 | console.log(`Server is running on port ${process.env.SERVER_PORT}`); 46 | }); -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose=require('mongoose'); 2 | 3 | const Schema=mongoose.Schema; 4 | 5 | const UserSchema=new Schema({ 6 | userId: {type: String}, 7 | name: {type: String}, 8 | email: {type: String}, 9 | hashedPassword: {type: String}, 10 | salt: {type: String} 11 | },{ 12 | collection:'User' 13 | }); 14 | 15 | const User=mongoose.model('User',UserSchema); 16 | 17 | module.exports=User; -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongodb", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "connect-mongo": "^4.4.1", 13 | "cookie-parser": "^1.4.5", 14 | "crypto-js": "^4.0.0", 15 | "dotenv": "^10.0.0", 16 | "express": "^4.17.1", 17 | "express-session": "^1.17.2", 18 | "mongoose": "^5.12.15", 19 | "morgan": "^1.10.0", 20 | "path": "^0.12.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/routes.js: -------------------------------------------------------------------------------- 1 | const fs=require('fs'); 2 | const express=require('express'); 3 | const router=express.Router(); 4 | 5 | router.get('/',(req,res)=>{ 6 | res.redirect('/main'); 7 | }); 8 | 9 | router.get('/main',(req,res)=>{ 10 | fs.readFile('./views/main.html',(err,data)=>{ 11 | if(err){ 12 | console.error(err); 13 | res.status(500).json({message:'Failed to Load Web Page'}); 14 | } 15 | else{ 16 | res.header({'content-type':'text/html'}); 17 | res.status(200).end(data.toString()); 18 | } 19 | }); 20 | }); 21 | 22 | router.get('/login',(req,res)=>{ 23 | fs.readFile('./views/login.html',(err,data)=>{ 24 | if(err){ 25 | console.error(err); 26 | res.status(500).json({message:'Failed to Load Web Page'}); 27 | } 28 | else{ 29 | res.header({'content-type':'text/html'}); 30 | res.status(200).end(data.toString()); 31 | } 32 | }); 33 | }); 34 | 35 | router.get('/signup',(req,res)=>{ 36 | fs.readFile('./views/signup.html',(err,data)=>{ 37 | if(err){ 38 | console.error(err); 39 | res.status(500).json({message:'Failed to Load Web Page'}); 40 | } 41 | else{ 42 | res.header({'content-type':'text/html'}); 43 | res.status(200).end(data.toString()); 44 | } 45 | }); 46 | }); 47 | 48 | module.exports=router; -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/static/css/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/9.Examples/Database/MongoDB/static/css/main.css -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/static/js/main.js: -------------------------------------------------------------------------------- 1 | const login=function(){ 2 | const userId=$('input[name=userId]').val(); 3 | const password=$('input[name=password]').val(); 4 | $.ajax({ 5 | type:'POST', 6 | url:'/api/user/login', 7 | data:{ 8 | userId:userId, 9 | password:password 10 | }, 11 | dataType:'json' 12 | }) 13 | .done(function(){ 14 | location.href='/main'; 15 | }) 16 | .fail(function(err){ 17 | console.error(err); 18 | alert('로그인 실패!'); 19 | return false; 20 | }); 21 | } 22 | 23 | const signup=function(){ 24 | const userId=$('input[name=userId]').val(); 25 | const password=$('input[name=password]').val(); 26 | const password2=$('input[name=password2]').val(); 27 | if(password!==password2){ 28 | alert('패스워드가 일치하지 않습니다.'); 29 | return false; 30 | } 31 | const name=$('input[name=name]').val(); 32 | const email=$('input[name=email]').val(); 33 | $.ajax({ 34 | type:'POST', 35 | url:'/api/user/signup', 36 | data:{ 37 | userId:userId, 38 | password:password, 39 | email:email, 40 | name:name 41 | }, 42 | dataType:'json' 43 | }) 44 | .done(function(){ 45 | alert('회원가입 성공!'); 46 | location.href='/login'; 47 | }) 48 | .fail(function(err){ 49 | console.error(err); 50 | alert('회원가입 실패!'); 51 | return false; 52 | }); 53 | } 54 | 55 | const logout=function(){ 56 | $.ajax({ 57 | type:'POST', 58 | url:'/api/user/logout' 59 | }) 60 | .done(function(){ 61 | location.href='/login'; 62 | }) 63 | .fail(function(){ 64 | return false; 65 | }); 66 | } -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/views/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Login 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/views/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Main 5 | 6 | 7 | 8 | 9 | 10 | 27 | -------------------------------------------------------------------------------- /9.Examples/Database/MongoDB/views/signup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sign Up 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/.env: -------------------------------------------------------------------------------- 1 | SERVER_PORT=3000 2 | SESSION_SECRET=secret 3 | DB_HOST=localhost 4 | DB_PORT=3306 5 | DB_USER=root 6 | DB_PASSWORD=123456 7 | DB_DATABASE=sample 8 | REPEAT_NUM=1357 -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/README.md: -------------------------------------------------------------------------------- 1 | # MongoDB Example 2 | ## Develop Environment 3 | - Node.js 14.16.1 4 | - Npm 7.19.0 5 | - MySQL 8.0.21 6 | ## How To Run 7 | 1. Prerequsites 8 | MySQL이 설치되어 있어야 합니다. 9 | sample.users 테이블 구조 10 | ```bash 11 | mysql>desc users; 12 | +----------------+--------------+------+-----+---------+----------------+ 13 | | Field | Type | Null | Key | Default | Extra | 14 | +----------------+--------------+------+-----+---------+----------------+ 15 | | id | int | NO | PRI | NULL | auto_increment | 16 | | userId | varchar(45) | NO | | NULL | | 17 | | hashedPassword | varchar(128) | NO | | NULL | | 18 | | salt | varchar(128) | NO | | NULL | | 19 | | name | varchar(45) | YES | | NULL | | 20 | | email | varchar(45) | YES | | NULL | | 21 | +----------------+--------------+------+-----+---------+----------------+ 22 | ``` 23 | 2. Install modules 24 | ```bash 25 | npm install 26 | ``` 27 | 3. Run 28 | ```bash 29 | node app.js 30 | ``` 31 | ## APIs 32 | - POST /api/user/signup : 사용자 회원가입 API 33 | - POST /api/user/login : 사용자 로그인 API 34 | - POST /api/user/logout : 사용자 로그아웃 API 35 | - GET /api/user/info : 사용자 정보 API 36 | 37 | ## Modules 38 | - express : 웹 프레임워크 39 | - mysql2/promise : mysql을 연결하는 라이브러리, mysql2는 promise가 지원된다. 40 | - dotenv : .env 파일에 configuration 작성하여 process.env에 로드 41 | (**주의** .env파일은 원래 .gitignore에 등록돼야 함.) 42 | - crypto-js : 암호화 라이브러리 43 | - express-session : 세션 라이브러리 44 | - express-mysql-session : 세션을 서버 메모리가 아닌 데이터베이스에 저장 45 | - morgan : 로깅(logging) 미들웨어 46 | -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/api/index.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const router=express.Router(); 3 | 4 | router.use('/user',require('./user')); 5 | 6 | module.exports=router; -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/api/user/index.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const router=express.Router(); 3 | 4 | router.post('/signup',require('./signup')); 5 | router.post('/login',require('./login')); 6 | router.post('/logout',require('./logout')); 7 | router.get('/info',require('./info')); 8 | 9 | module.exports=router; -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/api/user/info.js: -------------------------------------------------------------------------------- 1 | const Info=(req,res)=>{ 2 | if(!req.session.user){ 3 | res.status(403).json({message:'Not Logined!'}); 4 | } 5 | else{ 6 | res.status(200).json({message:'success',data:req.session.user}); 7 | } 8 | } 9 | 10 | module.exports=Info; -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/api/user/login.js: -------------------------------------------------------------------------------- 1 | const CryptoJS=require('crypto-js'); 2 | const User=require('../../models/user'); 3 | 4 | const Login=(req,res)=>{ 5 | const userId=req.body.userId; 6 | const password=req.body.password; 7 | 8 | const DataCheck=()=>{ 9 | return new Promise((resolve,reject)=>{ 10 | if(!userId || !password){ 11 | reject({message:'Request Body Error!'}); 12 | } 13 | else{ 14 | resolve(); 15 | } 16 | }); 17 | } 18 | 19 | const UserCheck=()=>{ 20 | return new Promise(async(resolve,reject)=>{ 21 | try{ 22 | const user=await User.getUserByUserId(userId); 23 | if(user.length===0){ 24 | reject({message:'userId Error!'}); 25 | } 26 | else{ 27 | resolve(user[0]); 28 | } 29 | } 30 | catch(e){ 31 | reject(e); 32 | } 33 | 34 | }); 35 | } 36 | 37 | const PasswordCheck=(user)=>{ 38 | return new Promise(async(resolve,reject)=>{ 39 | const salt=user.salt; 40 | const hashedPassword=CryptoJS.PBKDF2(password,salt,{keySize:16,iterations:Number(process.env.REPEAT_NUM)}).toString(); 41 | if(hashedPassword!==user.hashedPassword){ 42 | reject({message:'Password Error!'}); 43 | } 44 | else{ 45 | resolve(user); 46 | } 47 | }); 48 | } 49 | 50 | const SetSession=(user)=>{ 51 | return new Promise((resolve,reject)=>{ 52 | req.session.user={userId:user.userId,name:user.name}; 53 | req.session.save(()=>{ 54 | resolve(); 55 | }); 56 | }); 57 | } 58 | 59 | DataCheck() 60 | .then(UserCheck) 61 | .then(PasswordCheck) 62 | .then(SetSession) 63 | .then(()=>{ 64 | res.status(200).json({message:'Success'}); 65 | }) 66 | .catch((err)=>{ 67 | console.error(err); 68 | res.status(500).json(err); 69 | }); 70 | } 71 | 72 | module.exports=Login; -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/api/user/logout.js: -------------------------------------------------------------------------------- 1 | const Logout=(req,res)=>{ 2 | req.session.destroy(()=>{ 3 | res.status(200).json({message:'success'}); 4 | }); 5 | } 6 | 7 | module.exports=Logout; -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/api/user/signup.js: -------------------------------------------------------------------------------- 1 | const CryptoJS=require('crypto-js'); 2 | const User=require('../../models/user'); 3 | 4 | const Signup=(req,res)=>{ 5 | const {userId, name, email, password}=req.body; 6 | 7 | const DataCheck=()=>{ 8 | return new Promise((resolve,reject)=>{ 9 | if(!userId || !name || !email){ 10 | reject({message:'Request Body Error!'}); 11 | } 12 | else{ 13 | resolve(); 14 | } 15 | }); 16 | } 17 | 18 | const UserCheck=()=>{ 19 | return new Promise(async(resolve,reject)=>{ 20 | const user=await User.getUserByUserId(userId); 21 | if(user.length!==0){ 22 | reject({message:'Duplicated ID Error!'}); 23 | } 24 | else{ 25 | resolve(); 26 | } 27 | }); 28 | } 29 | 30 | const GeneratePassword=()=>{ 31 | return new Promise(async(resolve,reject)=>{ 32 | try{ 33 | const salt=CryptoJS.lib.WordArray.random(16).toString(); 34 | const hashedPassword=CryptoJS.PBKDF2(password,salt,{keySize:16,iterations:Number(process.env.REPEAT_NUM)}).toString(); 35 | await User.insertUser(userId,hashedPassword,email,name,salt); 36 | resolve(); 37 | } 38 | catch(err){ 39 | reject(err); 40 | } 41 | }); 42 | } 43 | 44 | DataCheck() 45 | .then(UserCheck) 46 | .then(GeneratePassword) 47 | .then(()=>{ 48 | res.status(200).json({message:'Success'}); 49 | }) 50 | .catch((err)=>{ 51 | console.error(err); 52 | res.status(500).json(err); 53 | }) 54 | } 55 | 56 | module.exports=Signup; -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/app.js: -------------------------------------------------------------------------------- 1 | const express=require('express'); 2 | const session=require('express-session'); 3 | const morgan=require('morgan'); 4 | const cookieParser=require('cookie-parser'); 5 | const path=require('path'); 6 | const mysqlStore=require('express-mysql-session')(session); 7 | require('dotenv').config(); 8 | const app=express(); 9 | 10 | app.use(morgan('combined')); 11 | app.use(express.json()); 12 | app.use(express.urlencoded({extended:false})); 13 | app.use(cookieParser()); 14 | app.use(session({ 15 | secret:process.env.SESSION_SECRET, 16 | resave:false, 17 | saveUninitialized:true, 18 | maxAge:7*24*60*60*1000, 19 | store:new mysqlStore({ 20 | host:process.env.DB_HOST, 21 | port:process.env.DB_PORT, 22 | user:process.env.DB_USER, 23 | password:process.env.DB_PASSWORD, 24 | database:process.env.DB_DATABASE 25 | }) 26 | })); 27 | app.use('/js',express.static(path.join('static','js'))); 28 | app.use('/css',express.static(path.join('static','js'))); 29 | 30 | app.use('/api',require('./api')); 31 | app.use('/',require('./routes')); 32 | 33 | app.listen(process.env.SERVER_PORT,()=>{ 34 | console.log(`Server is running on port ${process.env.SERVER_PORT}`); 35 | }); -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/models/user.js: -------------------------------------------------------------------------------- 1 | const pool=require('../pool'); 2 | 3 | 4 | const getUserByUserId=(userId)=>{ 5 | return new Promise(async(resolve,reject)=>{ 6 | try{ 7 | const connection=await pool.getConnection(async conn=>conn); 8 | try{ 9 | const [rows]=await connection.query(`SELECT * FROM users WHERE userId=?;`,[userId]); 10 | connection.release(); 11 | resolve(rows); 12 | } 13 | catch(err){ 14 | connection.release(); 15 | reject(err); 16 | } 17 | } 18 | catch(err){ 19 | connection.release(); 20 | reject(err); 21 | } 22 | }); 23 | } 24 | 25 | const insertUser=(userId,hashedPassword,email,name,salt)=>{ 26 | return new Promise(async(resolve,reject)=>{ 27 | try{ 28 | const connection=await pool.getConnection(async conn=>conn); 29 | try{ 30 | const [rows]=await connection.query(`INSERT INTO users(userId,hashedPassword,email,name,salt) VALUES(?,?,?,?,?);`,[userId,hashedPassword,email,name,salt]); 31 | connection.release(); 32 | resolve(rows); 33 | } 34 | catch(err){ 35 | connection.release(); 36 | reject(err); 37 | } 38 | } 39 | catch(err){ 40 | connection.release(); 41 | reject(err); 42 | } 43 | }); 44 | } 45 | 46 | module.exports={ 47 | getUserByUserId:getUserByUserId, 48 | insertUser:insertUser, 49 | } -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mysql", 3 | "version": "1.0.0", 4 | "description": "- Node.js 14.16.1\r - Npm 7.19.0\r - MongoDB 4.4.0\r ## How To Run\r 1. Prerequsites \r MongoDB가 설치되어 있어야 합니다.\r 2. Install modules\r ```bash\r npm install\r ```\r 3. Run\r ```bash\r node app.js\r ```\r ## APIs\r - POST /api/user/signup : 사용자 회원가입 API\r - POST /api/user/login : 사용자 로그인 API\r - POST /api/user/logout : 사용자 로그아웃 API\r - GET /api/user/info : 사용자 정보 API", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "cookie-parser": "^1.4.5", 13 | "crypto-js": "^4.0.0", 14 | "dotenv": "^10.0.0", 15 | "express": "^4.17.1", 16 | "express-mysql-session": "^2.1.6", 17 | "express-session": "^1.17.2", 18 | "morgan": "^1.10.0", 19 | "mysql2": "^2.2.5", 20 | "path": "^0.12.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/pool.js: -------------------------------------------------------------------------------- 1 | const mysql=require('mysql2/promise'); 2 | 3 | const pool=mysql.createPool({ 4 | host:process.env.DB_HOST, 5 | port:process.env.DB_PORT, 6 | user:process.env.DB_USER, 7 | password:process.env.DB_PASSWORD, 8 | database:process.env.DB_DATABASE, 9 | }); 10 | 11 | module.exports=pool; -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/routes.js: -------------------------------------------------------------------------------- 1 | const fs=require('fs'); 2 | const express=require('express'); 3 | const router=express.Router(); 4 | 5 | router.get('/',(req,res)=>{ 6 | res.redirect('/main'); 7 | }); 8 | 9 | router.get('/main',(req,res)=>{ 10 | fs.readFile('./views/main.html',(err,data)=>{ 11 | if(err){ 12 | console.error(err); 13 | res.status(500).json({message:'Failed to Load Web Page'}); 14 | } 15 | else{ 16 | res.header({'content-type':'text/html'}); 17 | res.status(200).end(data.toString()); 18 | } 19 | }); 20 | }); 21 | 22 | router.get('/login',(req,res)=>{ 23 | fs.readFile('./views/login.html',(err,data)=>{ 24 | if(err){ 25 | console.error(err); 26 | res.status(500).json({message:'Failed to Load Web Page'}); 27 | } 28 | else{ 29 | res.header({'content-type':'text/html'}); 30 | res.status(200).end(data.toString()); 31 | } 32 | }); 33 | }); 34 | 35 | router.get('/signup',(req,res)=>{ 36 | fs.readFile('./views/signup.html',(err,data)=>{ 37 | if(err){ 38 | console.error(err); 39 | res.status(500).json({message:'Failed to Load Web Page'}); 40 | } 41 | else{ 42 | res.header({'content-type':'text/html'}); 43 | res.status(200).end(data.toString()); 44 | } 45 | }); 46 | }); 47 | 48 | module.exports=router; -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/static/css/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/9.Examples/Database/MySQL/static/css/main.css -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/static/js/main.js: -------------------------------------------------------------------------------- 1 | const login=function(){ 2 | const userId=$('input[name=userId]').val(); 3 | const password=$('input[name=password]').val(); 4 | $.ajax({ 5 | type:'POST', 6 | url:'/api/user/login', 7 | data:{ 8 | userId:userId, 9 | password:password 10 | }, 11 | dataType:'json' 12 | }) 13 | .done(function(){ 14 | location.href='/main'; 15 | }) 16 | .fail(function(err){ 17 | console.error(err); 18 | alert('로그인 실패!'); 19 | return false; 20 | }); 21 | } 22 | 23 | const signup=function(){ 24 | const userId=$('input[name=userId]').val(); 25 | const password=$('input[name=password]').val(); 26 | const password2=$('input[name=password2]').val(); 27 | if(password!==password2){ 28 | alert('패스워드가 일치하지 않습니다.'); 29 | return false; 30 | } 31 | const name=$('input[name=name]').val(); 32 | const email=$('input[name=email]').val(); 33 | $.ajax({ 34 | type:'POST', 35 | url:'/api/user/signup', 36 | data:{ 37 | userId:userId, 38 | password:password, 39 | email:email, 40 | name:name 41 | }, 42 | dataType:'json' 43 | }) 44 | .done(function(){ 45 | alert('회원가입 성공!'); 46 | location.href='/login'; 47 | }) 48 | .fail(function(err){ 49 | console.error(err); 50 | alert('회원가입 실패!'); 51 | return false; 52 | }); 53 | } 54 | 55 | const logout=function(){ 56 | $.ajax({ 57 | type:'POST', 58 | url:'/api/user/logout' 59 | }) 60 | .done(function(){ 61 | location.href='/login'; 62 | }) 63 | .fail(function(){ 64 | return false; 65 | }); 66 | } -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/views/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Login 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/views/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Main 5 | 6 | 7 | 8 | 9 | 10 | 27 | -------------------------------------------------------------------------------- /9.Examples/Database/MySQL/views/signup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sign Up 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /9.Examples/Database/README.md: -------------------------------------------------------------------------------- 1 | # Database Example 2 | - MongoDB 3 | - MySQL -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/README.md: -------------------------------------------------------------------------------- 1 | # Calculator Example 2 | 3 | ## Usage 4 | 1. install electron & electron-builder 5 | ``` 6 | npm install -g electron electron-builder 7 | ``` 8 | 9 | 2. npm run start (실행) 10 | 11 | 3. npm run build (설치 파일로 빌드) -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/app.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow } = require("electron"); 2 | 3 | let win; 4 | 5 | const createWindow = () => { 6 | win = new BrowserWindow({ 7 | width: 300, 8 | height: 400, 9 | icon: __dirname + "/src/img/icon.png", 10 | }); 11 | win.loadURL(`file://${__dirname}/src/index.html`); 12 | 13 | win.on("closed", () => { 14 | win = null; 15 | }); 16 | }; 17 | 18 | app.on("ready", createWindow); 19 | 20 | app.on("window-all-closed", () => { 21 | if (process.platform !== "darwin") app.quit(); 22 | }); 23 | 24 | app.on("activate", () => { 25 | if (win === null) createWindow(); 26 | }); 27 | -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calculator", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "electron .", 8 | "build": "electron-builder" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "electron": "^13.1.5", 14 | "electron-builder": "^22.11.7" 15 | }, 16 | "type": "commonjs", 17 | "build":{ 18 | "asar":true, 19 | "appId":"calculator.club.dcom", 20 | "productName":"Calculator", 21 | "win":{ 22 | "target":[ 23 | { 24 | "target":"nsis", 25 | "arch":[ 26 | "x64", 27 | "ia32" 28 | ] 29 | } 30 | ], 31 | "icon":"./src/img/icon.ico" 32 | }, 33 | "nsis":{ 34 | "oneClick":true, 35 | "perMachine":false, 36 | "installerIcon":"./src/img/icon.ico", 37 | "createDesktopShortcut":true 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/src/css/layout.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | height: 100%; 6 | } 7 | 8 | .container { 9 | height: 100%; 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | 14 | .result { 15 | display: flex; 16 | flex-direction: column; 17 | justify-content: flex-end; 18 | height: 30%; 19 | padding: 10px; 20 | box-sizing: border-box; 21 | text-align: right; 22 | overflow-x: auto; 23 | } 24 | 25 | #cur { 26 | font-size: 2em; 27 | } 28 | 29 | .buttons { 30 | flex: 1 0 0; 31 | display: flex; 32 | flex-direction: column; 33 | } 34 | 35 | .row { 36 | flex: 1 0 0; 37 | display: flex; 38 | } 39 | 40 | button { 41 | flex: 1 0 0; 42 | border: 1px solid #ddd; 43 | background-color: #fff; 44 | border-radius: 0; 45 | } 46 | 47 | button.number { 48 | font-size: 1.3em; 49 | font-weight: bold; 50 | } 51 | 52 | button:hover { 53 | background-color: #ccc; 54 | } 55 | -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/src/img/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/9.Examples/Electron/Calculator/src/img/icon.ico -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/src/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/9.Examples/Electron/Calculator/src/img/icon.png -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 계산기 5 | 6 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | 30 | 31 |
32 |
33 | 34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /9.Examples/Electron/Calculator/src/js/calculator.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("DOMContentLoaded", () => { 2 | let prev = ""; 3 | let cur = ""; 4 | let prev_op = ""; 5 | let isBeingNumber = false; 6 | 7 | document.querySelectorAll("button").forEach((button) => { 8 | button.addEventListener("click", function () { 9 | if (this.classList.contains("number")) { 10 | cur = parseInt(cur + this.innerHTML); 11 | document.querySelector("#cur").innerHTML = cur; 12 | isBeingNumber = true; 13 | } else { 14 | const op = this.innerHTML; 15 | if (op === "C") { 16 | prev = ""; 17 | cur = ""; 18 | prev_op = ""; 19 | document.querySelector("#cur").innerHTML = ""; 20 | document.querySelector("#prev").innerHTML = ""; 21 | } else if (isBeingNumber) { 22 | if (!!prev_op) { 23 | switch (prev_op) { 24 | case "+": 25 | prev += cur; 26 | break; 27 | case "-": 28 | prev -= cur; 29 | break; 30 | case "*": 31 | prev *= cur; 32 | break; 33 | case "/": 34 | if (cur !== 0) prev /= cur; 35 | break; 36 | } 37 | document.querySelector("#cur").innerHTML = prev; 38 | } else { 39 | prev = cur; 40 | } 41 | cur = ""; 42 | if (op === "=") { 43 | prev_op = ""; 44 | document.querySelector("#prev").innerHTML = ""; 45 | } else { 46 | prev_op = op; 47 | document.querySelector("#prev").innerHTML = `${prev} ${prev_op}`; 48 | } 49 | } else { 50 | if (op === "=") { 51 | prev_op = ""; 52 | document.querySelector("#prev").innerHTML = ""; 53 | } else { 54 | prev_op = op; 55 | document.querySelector("#prev").innerHTML = `${prev} ${prev_op}`; 56 | } 57 | } 58 | 59 | isBeingNumber = false; 60 | } 61 | console.log(cur, prev, prev_op); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /9.Examples/Electron/README.md: -------------------------------------------------------------------------------- 1 | # Electron 2 | 3 | node.js를 이용한 크로스 플랫폼 데스크톱 앱 만들기 4 | 5 | ## Examples 6 | - Calculator -------------------------------------------------------------------------------- /9.Examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | Node.js 다양한 예시 프로젝트 모음 3 | - Cralwer : 웹 크롤러 예제 4 | - Database : Node.js Express 서버와 DB(MongoDB,MySQL) 연결 후 간단한 회원 관리(로그인, 회원가입 등) 5 | - Electron : 데스크톱 앱 예제 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js-Tutorial 2 | ## Courses 3 | 1. Introduction 4 | 2. Variable 5 | 3. Functions 6 | 4. Promise, async/await 7 | 5. Modules 8 | 6. Express 9 | 7. Middlewares 10 | 8. Cookies & Session 11 | 9. Examples 12 | ## How To Contribute 13 | 1. 해당 repository fork. 14 | 2. fork된 repository에서 commit and push 15 | 3. 이 repository로 pull request 16 | 17 | **오탈자 수정**이나 **9.Example** 예제 추가 환영 -------------------------------------------------------------------------------- /img/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/img/1.PNG -------------------------------------------------------------------------------- /img/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/img/2.PNG -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/img/3.png -------------------------------------------------------------------------------- /img/4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/img/4.PNG -------------------------------------------------------------------------------- /img/5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/img/5.PNG -------------------------------------------------------------------------------- /img/6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dcom-KHU/Node.js-Tutorial/21bce3e3132832427189c41a16f742a8b900361a/img/6.PNG --------------------------------------------------------------------------------