배열, map, set 차이점 찾아보기 -> js 객체 개념 공부
Nodejs 구현 공부 (챕터 1- 30~ 33장)
운영체제 챕터 5
SQL 기초 문제 리트코드나 프로그래머스에서 풀기
javascript 에서는 8가지 자료형이 있고, 그 중 7개는 원시형(primitive type), 나머지는 객체형(objective type) 이다.
자바스크립트에는 일반 객체 이외에도 다양한 종류의 객체가 있다.
Array – 정렬된 데이터 컬렉션을 저장할 때 쓰임
Date – 날짜와 시간 정보를 저장할 때 쓰임
Error – 에러 정보를 저장할 때 쓰임
기타 등등
https://ko.javascript.info/object
객체는 중괄호 {…}를 이용해 만들 수 있다. 중괄호 안에는 ‘키(key): 값(value)’ 쌍으로 구성된 프로퍼티(property) 를 여러 개 넣을 수 있는데, 키엔 문자형, 값엔 모든 자료형이 허용된다. 프로퍼티 키는 ‘프로퍼티 이름’ 이라고도 부른다.
// 객체 생성법
let user = new Object(); // '객체 생성자' 문법
let user = {}; // '객체 리터럴' 문법 -> 주로 사용
// 객체 표시
let user = {
name: "John",
age: 30,
"likes birds": true, // 복수의 단어는 따옴표로 묶어야 합니다.
};
__proto__
는 사용 할 수 없다. (해도 안먹힘)let codes = {
"+49": "독일",
"+41": "스위스",
"+44": "영국",
// ..,
"+1": "미국"
};
for (let code in codes) {
alert( +code ); // 49, 41, 44, 1
}
https://ko.javascript.info/array
배열은 순서가 있는 자료를 저장하는 용도로 만들어진 특수한 자료구조이자 객체
키를 사용해 식별할 수 있는 값을 담은 컬렉션은 객체라는 자료구조를 이용해 저장하는데, 객체만으로도 다양한 작업을 할 수 있다.
순서가 있는 컬렉션을 다뤄야 할 때 객체를 사용하면 순서와 관련된 메서드가 없어 그다지 편리하지 않다.
객체는 태생이 순서를 고려하지 않고 만들어진 자료구조이기 때문에 객체를 이용하면 새로운 프로퍼티를 기존 프로퍼티 ‘사이에’ 끼워 넣는 것도 불가능하다.
이럴 땐 순서가 있는 컬렉션을 저장할 때 쓰는 자료구조
인 배열을 사용!
다른 언어에서는 메모리 구조로 설명하던데 자스에서는 객체로 보는 것 같다.
살짝 찾아보니 파이썬 같은 경우에는 List로 구현한다고 하고, 자바에서는 선형 자료구조(Data Structure)중 하나로 동일한 타입의 연관된 데이터를 메모리에 연속적으로 저장하여 하나의 변수에 묶어서 관리하기 위한 자료 구조라고 한다!
그리고 자바스크립트 V8엔진에서 배열을 어떻게 처리하는지는 예전에 내가 여기서 정리해놨다.
반복 가능한(iterable, 이터러블) 객체는 배열을 일반화한 객체
for..of
을 사용할 수 있는 객체를 이터러블이라고 부른다.
배열과 문자열은 Symbol.iterator가 구현되어 있고 가장 광범위하게 쓰이는 내장 이터러블이다.
iterable
: iterator를 반환하는 Symbol.iterator을 가진 값
iterator
: {value, done}을 반환하는 next()를 가진 값
여기서 done:true은 반복이 끝났음을 의미하고 그렇지 않은 경우엔 value가 다음 값이 된다.
즉, iterable은 순회 가능한 자료 구조를 뜻하고, iterator는 순회 가능한 자료구조에서 요소를 탐색하기 위한 포인터 역할을 한다.
iterable[Symbol.iterator]
메서드를 소유한다.iterator
가 반환된다.next메서드
를 갖는다.{ value, done }프로퍼티
를 갖는 객체를 반환한다. 이를 iterator result라고 한다. 이 객체는 순회 가능한 객체에서 포인터 역할을 한다.반복 가능한 객체(iterable object)는 for...of 구문과 함께 ES2015에서 도입되었다. 반복 가능한 객체를 다른 객체와 구분짓는 특징은, 객체의 Symbol.iterator 속성에 특별한 형태의 함수가 들어있다는 것!
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Symbol.iterator 메소드를 포함하지 않으므로, for...of 방식으로 순회가 불가능하다. (for ...in 방식으로는 순회 가능)
const obj = { a: 1, b: 2 };
for (const char of obj) {
console.log(char);
// TypeError: obj is not iterable
}
for (const char in obj) {
console.log(char) // a, b
}
어떤 객체가 Iterable이라면, 그 객체에 대해서 아래의 기능들을 사용할 수 있다.
// `for...of`
for (let c of 'hello') {
console.log(c);
}
// spread 연산자
const characters = [...'hello'];
// 분해대입
const [c1, c2] = 'hello';
// `Array.from`은 iterable 혹은 array-like 객체를 인수로 받습니다.
Array.from('hello');
for..of
loop// an array is an iterable
var arr = [ 10, 20, 30 ];
for (let val of arr) {
console.log(`Array value: ${ val }`);
}
// Array value: 10
// Array value: 20
// Array value: 30
메서드 Symbol.iterator는 for..of에 의해 자동으로 호출되는데, 개발자가 명시적으로 호출하는 것도 가능하다.
Spread operator
사용// spread an iterator into an array,
// with each iterated value occupying
// an array element position.
var vals = [ ...it ];
전개 구문 (spread 프로퍼티인 경우 제외) 은 iterable 객체에만 적용
const arr = [1, 2, 3, 4, 5];
const arr2 = [11, 12, 13, 14, 15];
const arr3 = [...arr, 6, 7, 8, 9, 10, ...arr2];
console.log(arr3); // [1, 2, 3, 4, 5, ..., 15]
이터러블(iterable)
: 메서드 Symbol.iterator가 구현된 객체
유사 배열(array-like)
: 인덱스와 length 프로퍼티가 있어서 배열처럼 보이는 객체
문자열
은 이터러블 객체이면서 유사배열! (for..of 를 사용할 수 있음 + 숫자 인덱스와 length 프로퍼티가 있음)
이 경우는 유사 배열 객체이긴 하지만 이터러블 객체가 아니다.
let arrayLike = { // 인덱스와 length프로퍼티가 있음 => 유사 배열
0: "Hello",
1: "World",
length: 2
};
// Symbol.iterator가 없으므로 에러 발생
for (let item of arrayLike) {}
이터러블과 유사 배열은 대개 배열이 아니기 때문에 push, pop 등의 메서드를 지원하지 않는다.
어떻게 하면 이터러블과 유사 배열에 배열 메서드를 적용할 수 있을까?
-> Array.from
범용 메서드 사용
Array.from() 메서드는 유사 배열 객체(array-like object)나 반복 가능한 객체(iterable object)를 얕게 복사해 새로운 Array 객체를 만듭니다.
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World (메서드가 제대로 동작)
syntax: Array.from(arrayLike[, mapFn[, thisArg]])
forEach
랑 for of
차이https://stackoverflow.com/questions/18508786/for-each-vs-iterator-which-will-be-the-better-option
for-each is syntactic sugar
for using iterators 라고 한다!!! 그리고 for-each는 collection을 수정할 수 없고 Iterator는 collection 수정이 가능하니 데이터를 단순히 가져오는 용도라면 가독성이 더 좋기 때문에 for-each를 사용하라고 한다.
https://www.geeksforgeeks.org/iterator-vs-foreach-in-java/
var arr = [1, 2, 3];
var arr2 = [...arr]; // arr.slice() 와 유사
arr2.push(4);
// arr2 은 [1, 2, 3, 4] 이 됨
// arr 은 영향을 받지 않고 남아 있음
1 depth 까지는 확실하게 Deep copy.
2 depth 이상이면 Shallow copy.
나중에 getter, setter에 대해서도 찾아봐야겠다!
https://ko.javascript.info/map-set
위에서는 객체, 배열, iterator 개념에 대해서 알아보았다!
객체 – 키가 있는 컬렉션을 저장함
배열 – 순서가 있는 컬렉션을 저장함
맵(Map)은 키가 있는 데이터를 저장한다는 점에서 객체와 유사하다
다른 점:
맵은 객체와 달리 키를 문자형으로 변환하지 않는다. 키엔 자료형 제약이 없다. 심지어 키로 객체를 허용함
객체는 프로퍼티 순서를 기억하지 못하는 것과는 다르게 맵은 값이 삽입된 순서대로 순회를 실시한다.
주로 어떨 때 쓰냐면
1. Key: key값으로 primitive value을 써야할 때는 map을 사용 (number, object, boolean, NaN, etc)
2. Order: insertion order를 기억해야할 때는 map을 사용
3. Size: map에서는 쉽게 size를 얻을 수 있다. (Object에서는 Object.keys(obj).length로 접근가능)
let map = new Map();
map.set('1', 'str1'); // 문자형 키
map.set(1, 'num1'); // 숫자형 키
map.set(true, 'bool1'); // 불린형 키
/* 객체는 키를 문자형으로 변환
그와 달리 맵은 키의 타입을 변환시키지 않고 그대로 유지한다
따라서 아래의 코드는 출력되는 값이 다르다. */
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'
alert( map.size ); // 3
new Map() – 맵 객체 생성
map.set(key, value) – key를 이용해 value를 저장
map.get(key) – key에 해당하는 값을 반환 key가 존재하지 않으면 undefined를 반환
map.has(key) – key가 존재하면 true, 존재하지 않으면 false를 반환
map.delete(key) – key에 해당하는 값을 삭제
map.clear() – 맵 안의 모든 요소를 제거
map.size – 요소의 개수를 반환
이때 map.set
호출할 때마다 map 자기자신이 호출되므로 체이닝(chaining) 가능함
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
map[key]
는 Map을 쓰는 바른 방법이 아니다.let john = { name: "John" };
// 고객의 가게 방문 횟수를 세본다고 가정해 봅시다.
let visitsCountMap = new Map();
// john을 맵의 키로 사용하겠습니다.
visitsCountMap.set(john, 123);
alert( visitsCountMap.get(john) ); // 123
객체의 경우, 모든 키를 문자형으로 변형시키기 때문에 john 객체를 키값으로 삽입할 경우, "[object Object]"
로 바뀐다.
for..of
, forEach()
map.keys() – 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환
map.values() – 각 요소의 값을 모은 이터러블 객체를 반환
map.entries() – 요소의 [키, 값]을 한 쌍으로 하는 이터러블 객체를 반환.
이 이터러블 객체는 for..of반복문의 기초로 쓰임
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// 키(vegetable)를 대상으로 순회합니다.
for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomatoes, onion
}
// 값(amount)을 대상으로 순회합니다.
for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50
}
// [키, 값] 쌍을 대상으로 순회합니다.
for (let entry of recipeMap) {
// recipeMap.entries()와 동일합니다.
alert(entry); // cucumber,500 ...
}
객체 -> 맵
: Object.entries() mdn 문서
let obj = {
name: "John",
age: 30,
};
let map = new Map(Object.entries(obj));
console.log(map);
console.log(map.get("name")); // John
맵 -> 객체
: Object.fromEntries() mdn 문서
const array = [
["banana", 1],
["orange", 2],
["meat", 4],
];
const prices = Object.fromEntries(array);
console.log(prices);
// 출력: { banana: 1, orange: 2, meat: 4 }
참고 사이트: https://velog.io/@dolarge/Java-Script-Set-%EA%B3%BC-Map
https://joshua1988.github.io/vue-camp/js/collection.html#map-%E1%84%87%E1%85%A1%E1%86%AB%E1%84%87%E1%85%A9%E1%86%A8%E1%84%86%E1%85%AE%E1%86%AB
셋(Set)은 중복을 허용하지 않는 값을 모아놓은 특별한 컬렉션.
set 객체는 다음과 같은 특징을 가진다.
셋에 키가 없는 값이 저장된다.
동일한 값을 중복하여 포함할수 없다
요소 순서에 의미가 없다
인덱스로 요소에 접근할 수 없다.
Set을 통해 교집합, 합집합, 차집합 등 수학적 집합 표현이 가능하다
new Set(iterable)
– 셋을 만듭니다. 이터러블 객체를 전달받으면(대개 배열을 전달받음)
그 안의 값을 복사해 셋에 넣어준다.
set.add(value) – 값을 추가하고 셋 자신을 반환
set.delete(value) – 값을 제거
호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환
set.has(value) – 셋 내에 값이 존재하면 true, 아니면 false를 반환
set.clear() – 셋을 비운다
set.size – 셋에 몇 개의 값이 있는지 세준다.
맵과 셋에 반복 작업을 할 땐, 해당 컬렉션에 요소나 값을 추가한 순서대로 반복 작업이 수행된다. 따라서 이 두 컬렉션은 정렬이 되어있지 않다고 할 수는 없다. 그렇지만 컬렉션 내 요소나 값을 재 정렬하거나 (배열에서 인덱스를 이용해 요소를 가져오는 것처럼) 숫자를 이용해 특정 요소나 값을 가지고 오는 것은 불가능함.
let set = new Set(["oranges", "apples", "bananas"]);
console.log(set[Symbol.iterator]); // [Function: values]
set 도 iterator로 for ...of
, forEach
사용 가능함.
arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])
const set = new Set(["oranges", "apples", "bananas"]);
for (let value of set) {
console.log("for of", value);
}
// forEach를 사용해도 동일하게 동작합니다.
set.forEach((value, valueAgain, set) => {
console.log("forEach", value);
});
흥미로운 점! forEach에 쓰인 콜백 함수는 세 개의 인수를 받는데, 첫 번째는 값, 두 번째도 같은 값인 valueAgain, 세 번째는 목표하는 객체(셋)이다.
동일한 값이 인수에 두 번 등장하고 있는 이유는 맵과의 호환성 때문이다.
맵의 forEach에 쓰인 콜백이 세 개의 인수를 받을 때를 위해서임.
이상해 보이겠지만 이렇게 구현해 놓았기 때문에 맵을 셋으로 혹은 셋을 맵으로 교체하기가 쉽다고 한다 (이해 제대로 못함)
set.keys() – 셋 내의 모든 값을 포함하는 이터러블 객체를 반환
set.values() – set.keys와 동일한 작업을 합니다. 맵과의 호환성을 위해 만들어진 메서드
set.entries() – 셋 내의 각 값을 이용해 만든
[value, value] 배열을 포함하는 이터러블 객체를 반환
맵과의 호환성을 위해 만들어짐
let http = require("http");
let fs = require("fs");
let url = require("url");
function templateHTML(title, list, body) {
return `
<!doctype html>
<html>
<head>
<title>WEB1 - ${title}</title>
<meta charset="utf-8">
</head>
<body>
<h1><a href="/">WEB</a></h1>
${list}
<a href="/create">create</a>
${body}
</body>
</html>
`;
}
function templateList(filelist) {
let list = "<ul>";
for (let i = 0; i < filelist.length; i++) {
list = list + `<li><a href="/?id=${filelist[i]}">${filelist[i]}</a></li>`;
}
list = list + "</ul>";
return list;
}
let app = http.createServer(function (request, response) {
let _url = request.url;
let queryData = url.parse(_url, true).query;
let pathname = url.parse(_url, true).pathname;
if (pathname === "/") {
if (queryData.id === undefined) {
fs.readdir("./data", function (error, filelist) {
let title = "Welcome";
let description = "Hello, Node.js";
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`<h2>${title}</h2><p>${description}</p>`
);
response.writeHead(200);
response.end(template);
});
} else {
fs.readdir("./data", function (error, filelist) {
fs.readFile(
`data/${queryData.id}`,
"utf8",
function (err, description) {
let title = queryData.id;
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`<h2>${title}</h2><p>${description}</p>`
);
response.writeHead(200);
response.end(template);
}
);
});
}
} else if (pathname === "/create") {
fs.readdir("./data", function (error, filelist) {
let title = "WEB - create";
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`
<form action="http://localhost:3000/create_process" method="post">
<p><input type="text" name="title" placeholder="title"></p>
<p>
<textarea name="description" placeholder="description"></textarea>
</p>
<p>
<input type="submit">
</p>
</form>
`
);
response.writeHead(200);
response.end(template);
});
} else {
response.writeHead(404);
response.end("Not found");
}
});
app.listen(3000);
<form action="http://localhost:3000/create_process" method="post"> // 생략 </form>
submit 버튼 누르면 Network 탭에 create_process 이름의 행이 뜬다.
create_process는 아직 처리하지 않았기 때문에 상태코드 404 Not Found가 뜬다.
url에 나타나는 대신 payload에 title, description 값이 POST 값으로 전송되는 것을 볼 수 있다.
POST 방식으로 전송된 데이터를 가져와서 title과 description 변수에 담기
POST 형태의 요청은 GET과는 요청하는 측의 데이터 구조가 다르다.
GET
의 경우는 같이 주소의 끝에 ?(물음표)를 붙이고 뒤에 변수명=값 형태로 요청
POST
는 주소만 요청하고 변수와 값을 주소가 아닌 요청 BODY에 담아서 서버측에 요청한다.
https://stackoverflow.com/questions/4295782/how-to-process-post-data-in-node-js
let http = require("http");
let fs = require("fs");
let url = require("url");
let qs = require("querystring");
function templateHTML(title, list, body) {
return `
<!doctype html>
<html>
<head>
<title>WEB1 - ${title}</title>
<meta charset="utf-8">
</head>
<body>
<h1><a href="/">WEB</a></h1>
${list}
<a href="/create">create</a>
${body}
</body>
</html>
`;
}
function templateList(filelist) {
let list = "<ul>";
for (let i = 0; i < filelist.length; i++) {
list = list + `<li><a href="/?id=${filelist[i]}">${filelist[i]}</a></li>`;
}
list = list + "</ul>";
return list;
}
let app = http.createServer(function (request, response) {
let _url = request.url;
let queryData = url.parse(_url, true).query;
let pathname = url.parse(_url, true).pathname;
if (pathname === "/") {
if (queryData.id === undefined) {
fs.readdir("./data", function (error, filelist) {
let title = "Welcome";
let description = "Hello, Node.js";
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`<h2>${title}</h2><p>${description}</p>`
);
response.writeHead(200);
response.end(template);
});
} else {
fs.readdir("./data", function (error, filelist) {
fs.readFile(
`data/${queryData.id}`,
"utf8",
function (err, description) {
let title = queryData.id;
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`<h2>${title}</h2><p>${description}</p>`
);
response.writeHead(200);
response.end(template);
}
);
});
}
} else if (pathname === "/create") {
fs.readdir("./data", function (error, filelist) {
let title = "WEB - create";
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`
<form action="http://localhost:3000/create_process" method="post">
<p><input type="text" name="title" placeholder="title"></p>
<p>
<textarea name="description" placeholder="description"></textarea>
</p>
<p>
<input type="submit">
</p>
</form>
`
);
response.writeHead(200);
response.end(template);
});
} else if (pathname === "/create_process") {
let body = '';
request.on('data', function(data) {
// 조각조각 나눠서 데이터를 수신할 때마다 호출되는 콜백 함수
// 데이터를 처리하는 기능을 정의
});
request.on('end', function() {
// 더이상 수신할 정보가 없으면 호출되는 콜백함수
// 데이터 처리를 마무리하는 기능을 정의
});
response.writeHead(200);
response.end("Seccess");
} else {
response.writeHead(404);
response.end("Not found");
}
});
app.listen(3000);
let qs = require("querystring");
querystring 모듈에 있는 parse 기능을 사용하기 위해 querystring 모듈을 가져옴
submit 버튼 누르면 /creat_process 로 이동되고 POST 요청이 감
else if (pathname === "/create_process") {
let body = '';
request.on('data', function(data) {
// 조각조각 나눠서 데이터를 수신할 때마다 호출되는 콜백 함수
// 데이터를 처리하는 기능을 정의
});
request.on('end', function() {
// 더이상 수신할 정보가 없으면 호출되는 콜백함수 (수신 완료)
// 데이터 처리를 마무리하는 기능을 정의
});
response.writeHead(200);
response.end("Seccess");
}
빈 문자열로 body 변수 선언하고, request의 on 메서드를 2번 호출함!
request
는 main.js에서 createServer 함수의 콜백으로 전달한 인수에서 찾을 수 있음
(var라고 적힌 코드 임의로 let이라구 했는데 별 문제 없겠지..?)
Node js 공식문서 HTTP 트랜잭션 해부 꼭 같이 보기!!!
const http = require('http');
let app = http.createServer(function(request, response) {
// 내용
})
이 서버로 오는 HTTP 요청마다 createServer에 전달된 함수가 한 번씩 호출된다.
사실 createServer가 반환한 Server 객체는 EventEmitter
이고 여기서는 server 객체를 생성하고 리스너를 추가하는 축약 문법을 사용한 것!
Node.js에서 웹브라우저로 접속할 때마다 Node.js가 createServer의 콜백 함수를 호출한다.
이때 createServer로 2개의 인자를 전달하는데,
1. request
: 요청할 때 웹 브라우저가 보낸 정보가 담겨있음 -> POST방식으로 전달받은 정보가 담겨있음
2. response
: 응답할 때 웹 브라우저에 전송할 정보를 담음.
각 'data' 이벤트에서 발생시킨 청크는 Buffer입니다. 이 청크가 문자열 데이터라는 것을 알고 있다면 이 데이터를 배열에 수집한 다음 'end' 이벤트에서 이어 붙인 다음 문자열로 만드는 것이 가장 좋습니다.
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// 여기서 `body`에 전체 요청 바디가 문자열로 담겨있습니다.
});
주의: 이 코드가 약간 장황할 수도 있고 대부분은 실제로 그렇습니다. 다행히 npm에 concat-stream나 body 같은 모듈로 이 로직을 감출 수 있습니다. 이어서 읽기 전에 어떤 작업이 이뤄지는지 잘 이해하는 것이 중요!
else if (pathname === "/create_process") {
let body = "";
request.on("data", function (data) {
// 조각조각 나눠서 데이터를 수신할 때마다 호출되는 콜백 함수
// 데이터를 처리하는 기능을 정의
body = body + data;
// 콜백으로 전달 받은 인자 data에 담긴 내용을 변수 body에 누적해서 합친다.
});
request.on("end", function () {
// 더이상 수신할 정보가 없으면 호출되는 콜백함수
// 데이터 처리를 마무리하는 기능을 정의
let post = qs.parse(body);
console.log(post);
// qs모듈의 parse기능을 이용해 body에 누적한 내용을 post에 담음
});
response.writeHead(200);
response.end("Seccess");
}
웹페이지 새로고침하면 콘솔창에
[Object: null prototype] {
title: 'this is tite',
description: 'this is description'
}
출력된다!!
근데 예제코드 써보니까 vsc에서 qs모듈이 deprecate 되었다고 알려줬다 (취소선)
구글링해보니까 여기서 잘 알려주셨다.
https://www.npmjs.com/package/querystring 에서 알려줌!
let post = qs.parse(body);
코드 deprecate 되어있어 가지고
let post = new url.URLSearchParams(body)
로 바꿈
request.on("end", function () {
// 더이상 수신할 정보가 없으면 호출되는 콜백함수
// 데이터 처리를 마무리하는 기능을 정의
let post = new url.URLSearchParams(body);
console.log("title:", post.get("title"));
console.log("description:", post.get("description"));
});
-> POST 방식으로 전송된 데이터를 title과 description 변수에 저장해서 콘솔로 출력하는 것까지 성공!!!
사용자가 입력한 정보를 받아서 프로그램 방식으로 동적으로 data 디렉터리에 파일을 생성하고 리다이렉션
https://nodejs.org/api/fs.html#fswritefilefile-data-options-callback
writeFile()
함수 request.on("end", function () {
// 더이상 수신할 정보가 없으면 호출되는 콜백함수
// 데이터 처리를 마무리하는 기능을 정의
let post = new url.URLSearchParams(body);
let title = post.get("title");
let description = post.get("description");
fs.writeFile(`data/${title}`, description, 'utf-8', function(err) {
response.writeHead(200);
response.end('seccess');
})
});
첫 번째 argument
파일 이름 -> data 디렉터리 아래의 ${title}이 나타내는 값으로 파일 이름을 지정 (= 사용자로부터 전달받은 데이터 중 제목을 파일 이름으로 저장)
두 번째 argument
파일에 쓸 내용 -> 사용자로부터 전달받은 데이터 중 내용이 담긴 description을 그대로 전달
세 번째 argument
파일을 저장할 때 UTF-8 인코딩을 사용
네 번째 argument
콜백 전달 -> 파일 쓰기를 마쳤을 때 내부적으로 자동 호출되는 함수. 이때 err이라는 매개 변수를 전달하는데, 이는 에러가 있을 때 에러를 처리하는 방법을 제공하는 매개변수. 콜백이 실행됐다는 것은 파일에 저장이 끝났다는 의미고 파일 저장이 끝난 후에 seccess를 출력해야 하므로 웹 브라우저에 'seccess'를 응답하는 코드를 씀
=> 결과
대박!!!!!!!!!!!! 신기!!!!! data 디렉터리 안에 파일 생성된다!!!!!!!!!!!!!
https://stackoverflow.com/questions/4062260/nodejs-redirect-url
이 코드는 Header를 302로 응답하고, Location에 있는 주소로 이동하라는 의미!
302
는 사용자를 다른 페이지로 이동시키는데, 이 주소가 일시적으로 바뀐 것!
즉, 현재 서버가 다른 위치(/?id=Nodejs)에 있는 페이지로 요청에 응답하고 있지만, 사용자가 나중에 다시 요청할 때는 원래 위치 (create_process)를 계속 사용해야함을 나타낸다!
- 3xx (리다이렉션 완료)
301 (영구 이동)
: 요청한 페이지가 새 위치로 영구적으로 이동. GET 또는 HEAD 요청에 대한 응답으로 이 응답을 표시하면 자동으로 새 위치로 전달된다.
302 (임시 이동)
: 현재 서버가 다른 위치의 페이지로 요청에 응답하고 있지만, 요청자는 향후 요청할 때 원래 위치를 계속 사용해야 한다.
위 스택오버플로우 코드를 참고해서 우리 코드에서 파일 쓰기를 마쳤을 때 웹 페이지를 리다이렉션하게 수정해보자.
fs.writeFile(`data/${title}`, description, "utf-8", function (err) {
response.writeHead(302, { Location: `/?id=${title}` });
response.end();
});
writeHead()
메서드 인수 분석:
1. 첫 번째 인수: HTTP 상태 코드로서, 3xx 코드
는 리다이렉션을 의미!
2. 두 번째 인수: 리다이렉션하고자 하는 경로 -> 앞에서 생성한 Nodejs 파일을 보여주고 싶으므로 템플릿 리터럴을 이용해 id의 값으로 title을 지정한다.
헐~ 대박 신기하다
동적으로 페이지가 관리됨!!!
let http = require("http");
let fs = require("fs");
let url = require("url");
let qs = require("querystring");
const { title } = require("process");
function templateHTML(title, list, body) {
return `
<!doctype html>
<html>
<head>
<title>WEB1 - ${title}</title>
<meta charset="utf-8">
</head>
<body>
<h1><a href="/">WEB</a></h1>
${list}
<a href="/create">create</a>
${body}
</body>
</html>
`;
}
function templateList(filelist) {
let list = "<ul>";
for (let i = 0; i < filelist.length; i++) {
list = list + `<li><a href="/?id=${filelist[i]}">${filelist[i]}</a></li>`;
}
list = list + "</ul>";
return list;
}
let app = http.createServer(function (request, response) {
let _url = request.url;
let queryData = url.parse(_url, true).query;
let pathname = url.parse(_url, true).pathname;
if (pathname === "/") {
if (queryData.id === undefined) {
fs.readdir("./data", function (error, filelist) {
let title = "Welcome";
let description = "Hello, Node.js";
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`<h2>${title}</h2><p>${description}</p>`
);
response.writeHead(200);
response.end(template);
});
} else {
fs.readdir("./data", function (error, filelist) {
fs.readFile(
`data/${queryData.id}`,
"utf8",
function (err, description) {
let title = queryData.id;
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`<h2>${title}</h2><p>${description}</p>`
);
response.writeHead(200);
response.end(template);
}
);
});
}
} else if (pathname === "/create") {
fs.readdir("./data", function (error, filelist) {
let title = "WEB - create";
let list = templateList(filelist);
let template = templateHTML(
title,
list,
`
<form action="http://localhost:3000/create_process" method="post">
<p><input type="text" name="title" placeholder="title"></p>
<p>
<textarea name="description" placeholder="description"></textarea>
</p>
<p>
<input type="submit">
</p>
</form>
`
);
response.writeHead(200);
response.end(template);
});
} else if (pathname === "/create_process") {
let body = "";
request.on("data", function (data) {
// 조각조각 나눠서 데이터를 수신할 때마다 호출되는 콜백 함수
// 데이터를 처리하는 기능을 정의
body = body + data;
});
request.on("end", function () {
// 더이상 수신할 정보가 없으면 호출되는 콜백함수
// 데이터 처리를 마무리하는 기능을 정의
let post = new url.URLSearchParams(body);
let title = post.get("title");
let description = post.get("description");
fs.writeFile(`data/${title}`, description, "utf-8", function (err) {
response.writeHead(302, { Location: `/?id=${title}` });
response.end();
});
});
} else {
response.writeHead(404);
response.end("Not found");
}
});
app.listen(3000);
하루 한 것
배열, map, set 차이점 찾아보기 -> js 객체 개념 공부
Nodejs 구현 공부 (챕터 1- 30~ 33장)
못한 것
운영체제 챕터 5
SQL 기초 문제 리트코드나 프로그래머스에서 풀기
피드백
운영체제 복습 자꾸 밀린다..
내일 할 것
Nodejs 구현
운영체제 챕터 5
SQL 기초 문제 리트코드나 프로그래머스에서 풀기
알고리즘 문제 리트코드
https://leetcode.com/problems/longest-subsequence-with-limited-sum/
https://leetcode.com/problems/removing-stars-from-a-string/
풀어보고 싶음
자료구조.. 공부해야하는데 시간 남으면 보기