์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- ios
- rxcocoa
- ํ๋ก๊ทธ๋๋จธ์ค
- Flutter
- MVVM
- Lv2
- arkit
- SwiftUI
- visionOS
- CollectionView
- SnapKit
- designpattern
- combine
- Xcode
- Kuring
- node.js
- tableView
- ํจ์คํธ์บ ํผ์ค
- UIKit
- RxSwift
- TCA
- XCTest
- BOJ
- swift
- raywenderlich
- BFS
- realm
- Swfit
- reactorkit
- ๋ฐฑ์ค
- Today
- Total
lgvv98
[Node.js] #4 http ๋ชจ๋๋ก ์๋ฒ ๋ง๋ค๊ธฐ ๋ณธ๋ฌธ
http ๋ชจ๋๋ก ์๋ฒ ๋ง๋ค๊ธฐ
#์์ฒญ๊ณผ ์๋ต ์ดํดํ๊ธฐ
์ ์ด์ ๋ถํฐ ์๋ฒ๋ฅผ ๋ง๋ค์ด๋ณด์
์๋ฒ์์๋ ์์ฒญ์ ๋ด์ฉ์ ์ฝ๊ณ ์ฒ๋ฆฌํ ๋ค ํด๋ผ์ด์ธํธ์ ์๋ต์ ๋ณด๋ธ๋ค.
๊ทธ๋ฌ๋๊น ์ผ๋จ ์์ฒญ์ ๋ฐ๋ ๋ถ๋ถ๊ณผ ์๋ต์ ๋ณด๋ด๋ ๋ถ๋ถ์ด ์์ด์ผ ํ๋ค.
const http = require('http');
http.createServer((req, res) => {
// ์ฌ๊ธฐ์ ์ด๋ป๊ฒ ์๋ตํ ์ง ์ ์ด์ค๋๋ค.
});
http ์๋ฒ๊ฐ ์์ด์ผ ์น ๋ธ๋ผ์ฐ์ ์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์์ผ๋ฏ๋ก http ๋ชจ๋์ ์ฌ์ฉํ๋ค.
http๋ชจ๋์๋ createServer ๋ฉ์๋๊ฐ ์๋ค.
์์ฒญ์ด ๋ค์ด์ฌ ๋๋ง๋ค ์ฝ๋ฐฑ ํจ์๊ฐ ์คํ๋๋ค.
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
})
.listen(8080, () => { // ์๋ฒ ์ฐ๊ฒฐ
console.log('8080๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
// ์ถ๋ ฅ๊ฒฐ๊ณผ
8080๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!
์ด๋ ๊ฒํ๋ฉด 8080ํฌํธ์์ ์๋ต์ค๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆผ.
res๊ฐ์ฒด๋ฅผ ๋ณด์.
writeHead๋ ์๋ต์ ๋ํ ์ ๋ณด๋ฅผ ๊ธฐ๋กํ๋ ๋ฉ์๋
์ฒซ ๋ฒ์งธ ์ธ์๋ก ์ฑ๊ณต์ ์ธ ์์ฒญ์์ ์๋ฏธํ๋ 200์
๋ ๋ฒ์งธ ์ธ์๋ก ์๋ต์ ๋ํ ์ ๋ณด๋ฅผ ๋ณด๋ด๋๋ฐ ์ฝํ ์ธ ์ ํ์์ด HTML์์ ๊ฐ๋ฅดํค๊ณ ์์. ๋ํ ํ๊ธ ํ์๋ฅผ ์ํด charset์ uft-8๋ก ์ง์ .
์ด ์ ๋ณด๊ฐ ๊ธฐ๋ก๋๋ ๋ถ๋ถ์ header๋ผ๊ณ ํ๋ค.
write๋ฉ์๋์๋
์ฒซ ๋ฒ์งธ ์ธ์๋ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋ผ ๋ฐ์ดํฐ๋ค.
์ง๊ธ์ HTML๋ฌธ์์ด์ ๋ณด๋์ง๋ง ๋ฒํผ๋ฅผ ๋ณด๋ผ ์๋ ์๋ค.
๋ํ ์ฌ๋ฌ ๋ฒ ํธ์ถํด์ ๋ฐ์ดํฐ๋ฅผ ์ฌ๋ฌ ๊ฐ ๋ณด๋ด๋ ๋๋ค.
๋ฐ์ดํฐ๊ฐ ๊ธฐ๋ก๋๋ ๋ถ๋ถ์ body๋ผ๊ณ ํ๋ค.
end๋ฉ์๋๋ ์๋ต์ ์ข ๋ฃํ๋ ๋ฉ์๋์ด๋ค.
๋ง์ฝ ์ธ์๊ฐ ์๋ค๋ฉด ๊ทธ ๋ฐ์ดํฐ๋ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋ด๊ณ ์๋ต์ ์ข ๋ฃ
์ ์์ ๋ res.write์์ write๋ก ๋ฐ๋๋ฌธ์ end๋ก ์ ๋ฌธ๊ตฌ๋ฅผ ๋ณด๋ด๋ฉด์ ์๋ต์ด ์ข ๋ฃ๋ ๊ฒ์ด๋ค.
๋ธ๋ผ์ฐ์ ๋ ์๋ต ๋ด์ฉ์ ๋ฐ์์ ๋ ๋๋งํฉ๋๋ค.
listen ๋ฉ์๋์ ์ฝ๋ฐฑ ํจ์๋ฅผ ๋ฃ๋ ๋์ ,ใท ใ ์๊ณผ ๊ฐ์ด ์๋ฒ์ listening ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ๋ถ์ฌ๋ ๋ฉ๋๋ค. ์ถ๊ฐ๋ก error ๋ฆฌ์ค๋๋ ๋ถ์ฌ๋ดค์ต๋๋ค.
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
});
server.listen(8080);
server.on('listening', () => {
console.log('8080๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
server.on('error', (error) => {
console.error(error);
});
์๋ฒ๋ ์ฐธ๊ณ ๋ก ๋ณ๊ฒฝ์ฌํญ์ ์ฆ๊ฐ ๋ฐ์ํ์ง ์์. ์ข ๋ฃํ๋ค๊ฐ ๋ค์ ์คํํด์ผ ํ๋ค.
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
})
.listen(8080, () => { // ์๋ฒ ์ฐ๊ฒฐ
console.log('8080๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
})
.listen(8081, () => { // ์๋ฒ ์ฐ๊ฒฐ
console.log('8081๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
// ๋ง์ฝ ํฌํธ ๋ฒํธ๊ฐ ๊ฐ๋ค๋ฉด ์๋ฒ๊ฐ ์ฃฝ๋๋ค.
์๋ฒ๋ฅผ ์ฌ๋ฌ๊ฐ ๋ง๋ค ์๋ ์๋ค.
์๋ฒ ํฌํธ๊ฐ ๊ฐ์ผ๋ฉด ์๋ฒ๊ฐ ์ฃฝ๋๋ค.
์ค๋ฌด์์ ์๋ฒ๋ฅผ ์ฌ๋ฌ๊ฐ ๋์ฐ๋ ์ผ์ ๋๋ฌผ๋ค.
write์ end์ ์ผ์ผํ HTML์ ์ ๋๊ฑฐ๋ ๋นํจ์จ ์ ์ด๋ฏ๋ก HTML ํ์ผ์ ๋ง๋ค์ด ๋๋๊ฒ์ด ๋ฐ๋์งํ๋ค
๊ทธ HTML ํ์ผ์ fs๋ชจ๋๋ก ์ฝ์ด์ ์ ์กํ ์ ์๋ค.
// server2.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Node.js ์น ์๋ฒ</title>
</head>
<body>
<h1>Node.js ์น ์๋ฒ</h1>
<p>๋ง๋ค ์ค๋น๋์
จ๋์?</p>
</body>
</html>
// server2.js
const http = require('http');
const fs = require('fs').promises;
http.createServer(async (req, res) => {
try {
const data = await fs.readFile('./server2.html');
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(data);
} catch (err) {
console.error(err);
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(err.message);
}
})
.listen(8081, () => {
console.log('8081๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
์์ฒญ ๋ค์ด์ค๋ฉด ๋จผ์ fs ๋ชจ๋๋ก HTML ํ์ผ์ ์ฝ์ต๋๋ค.
data์ ์ ์ฅ๋ ๋ฒํผ๋ฅผ ๊ทธ๋๋ก ํด๋ผ์ด์ธํธ์ ๋ณด๋ด๋ฉด ๋๋ค.
์ด์ ์๋ ๋ฌธ์์ด์ ๋ณด๋์ง๋ง, ์ ๋ ๊ฒ ๋ฒํผ๋ฅผ ๋ณด๋ผ ์๋ ์๋ค.
์๊ธฐ์น ๋ชปํ ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ์๋ ์๋ฌ ๋ฉ์์ง๋ฅผ ์๋ต
์๋ฌ ๋ฉ์์ง๋ ์ผ๋ฐ ๋ฌธ์์ด์ด๋ฏ๋ก text/plain ์ฌ์ฉ
HTTP ์ํ์ฝ๋ ๊ฐ๋จ์ ๋ฆฌ
- 2XX: ์ฑ๊ณต์๋ฆฌ๋ ์ฝ๋ 200(์ฑ๊ณต), 201(์์ฑ๋จ)
- 3XX: ๋ฆฌ๋ค์ด๋ ์ (๋ค๋ฅธ ํ์ด์ง ์ด๋)์ ์๋ฆฌ๋ ์ฝ๋ ์ด๋ค ์ฃผ์๋ฅผ ์ ๋ ฅํ๋๋ฐ ๋ค๋ฅธํ์ด์ง๋ก ๋์ด๊ฐ ๋
301(์๊ตฌ์ด๋) 302(์์์ด๋)
304(์์ ๋์ง ์์): ์์ฒญ์ ์๋ต์ผ๋ก ์บ์๋ฅผ ์ฌ์ฉ
- 4XX: ์์ฒญ์ค๋ฅ ์์ฒญ ์์ฒด์ ์ค๋ฅ
400(์๋ชป๋ ์์ฒญ) 401(๊ถํ ์์) 403(๊ธ์ง๋จ) 404(์ฐพ์ ์ ์์)
- 5XX: ์๋ฒ ์ค๋ฅ๋ฅผ ๋ํ๋ ์์ฒญ์ ์ ๋๋ก ์์ง๋ง ์๋ฒ์ ์ค๋ฅ๊ฐ ๋ฐ์
res.writeHead๋ก ํด๋ผ์ด์ธํธ์ ์ง์ ๋ณด๋ด๋ ๊ฒฝ์ฐ๋ ๊ฑฐ์ ์๊ณ , ์๊ธฐ์น ๋ชปํ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด์ ์๋ฒ๊ฐ ์์์ 5XX๋ ์ฝ๋๋ฅผ ๋ณด๋ธ๋ค.
500(๋ด๋ถ ์๋ฒ ์ค๋ฅ) 502(๋ถ๋ ๊ฒ์ดํธ์จ์ด) 503(์๋น์ค๋ฅผ ์ฌ์ฉํ ์ ์์)
์์ฒญ ๊ณผ์ ์์ ์ค๋ฅ ๋ฐ์ํ๋ค๊ณ ์๋ต์ ๋ณด๋ด์ง ์์ผ๋ฉด ์๋๋ค. ์์ฒญ์ด ์ฑ๊ณตํ๋ ์คํจํ๋ ์๋ต์ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋ด์ ์์ฒญ์ด ๋ง๋ฌด๋ฆฌ ๋์์์ ์๋ฆฐ๋ค.
์๋ต์ ๋ณด๋ด์ง ์๋๋ค๋ฉด ํด๋ผ์ด์ธํธ๋ ์๋ฒ๋ก๋ถํฐ ์๋ต์ด ์ค๊ธธ ํ์ผ์์ด ๊ธฐ๋ค๋ฆฌ๋ค๊ฐ ์๊ฐ์ด๊ณผ๋ฅผ ๋ด์ผํ๋ค.
#REST์ ๋ผ์ฐํ ์ฌ์ฉํ๊ธฐ
REST๋?
REpresentational State Transfer์ ์ค์๋ง๋ก ์๋ฒ์ ์์ ์ ์ํ๊ณ ์์์ ๋ํ ์ฃผ์๋ฅผ ์ง์ ํ๋ ๋ฐฉ๋ฒ์ ๊ฐ๋ฆฌํจ๋ค.
์ผ์ข ์ ์ฝ์์ด๋ผ๊ณ ๋ด๋ ๋ฌด๋ฐฉ.
์์์ด๋ผ๊ณ ํด์ ๊ผญ ํ์ผ์ผ ํ์๋ ์๊ณ ์๋ฒ๊ฐ ํํ ์ ์๋ ๊ฒ๋ค์ ํตํ์ด์ ์๋ฏธํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
- get: ์๋ฒ ์์ ๊ฐ์ ธ์ฌ ๋, ๋ฐ๋ ๋ฃ์ง ์๊ณ ํด๋ฆฌ์คํธ๋ง ์ฌ์ฉ
- post: ์๋ฒ์ ์์์ ์๋ก ๋ฑ๋กํ๊ณ ์ ํ ๋, ์์ฒญ์ ๋ณธ๋ฌธ์ ์๋ก ๋ฑ๋กํ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด ๋ณด๋ธ๋ค.
- put: ์๋ฒ ์์์ ์ผ๋ถ๋ง ์์ ํ๊ณ ์ ํ ๋. ์์ฒญ์ ๋ณธ๋ฌธ์ ์นํํ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด ๋ณด๋ธ๋ค.
- patch: ์๋ฒ ์์์ ์ผ๋ถ๋ง ์์ ํ๊ณ ์ ํ ๋. ์์ฒญ์ ๋ณธ๋ฌธ์ ์ผ๋ถ ์์ ํ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด ๋ณด๋ธ๋ค.
- delete: ์๋ฒ์ ์์์ ์ญ์ ํ๊ณ ์ ํ ๋, ์์ฒญ์ ๋ณธ๋ฌธ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ง ์๋๋ค.
- options: ์์ฒญ์ ํ๊ธฐ ์ ์ ํต์ ์ต์ ์ ์ค๋ช ํ๊ธฐ ์ํด ์ฌ์ฉ
์ฃผ์ ํ๋๊ฐ ์์ฒญ ๋ฉ์๋ ์ฌ๋ฌ๊ฐ๋ฅผ ๊ฐ์ง ์ ์์.
์ด์ ๋ REST๋ฅผ ์ฌ์ฉํ ์ฃผ์ ์ฒด๊ณ๋ก RESTfulํ ์น ์๋ฒ๋ฅผ ๋ง๋ค์ด๋ณด์.
REST๋ฅผ ๋ฐ๋ฅด๋ ์๋ฒ๋ฅผ 'RESTful'ํ๋ค๊ณ ํํํ๋ค.
// restFront.css
a { color: blue; text-decoration: none; }
// restFront.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>RESTful SERVER</title>
<link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<div>
<form id="form">
<input type="text" id="username">
<button type="submit">๋ฑ๋ก</button>
</form>
</div>
<div id="list"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="./restFront.js"></script>
</body>
</html>
async function getUser() { // ๋ก๋ฉ ์ ์ฌ์ฉ์ ๊ฐ์ ธ์ค๋ ํจ์
try {
const res = await axios.get('/users');
const users = res.data;
const list = document.getElementById('list');
list.innerHTML = '';
// ์ฌ์ฉ์๋ง๋ค ๋ฐ๋ณต์ ์ผ๋ก ํ๋ฉด ํ์ ๋ฐ ์ด๋ฒคํธ ์ฐ๊ฒฐ
Object.keys(users).map(function (key) {
const userDiv = document.createElement('div');
const span = document.createElement('span');
span.textContent = users[key];
const edit = document.createElement('button');
edit.textContent = '์์ ';
edit.addEventListener('click', async () => { // ์์ ๋ฒํผ ํด๋ฆญ
const name = prompt('๋ฐ๊ฟ ์ด๋ฆ์ ์
๋ ฅํ์ธ์');
if (!name) {
return alert('์ด๋ฆ์ ๋ฐ๋์ ์
๋ ฅํ์
์ผ ํฉ๋๋ค');
}
try {
await axios.put('/user/' + key, { name });
getUser();
} catch (err) {
console.error(err);
}
});
const remove = document.createElement('button');
remove.textContent = '์ญ์ ';
remove.addEventListener('click', async () => { // ์ญ์ ๋ฒํผ ํด๋ฆญ
try {
await axios.delete('/user/' + key);
getUser();
} catch (err) {
console.error(err);
}
});
userDiv.appendChild(span);
userDiv.appendChild(edit);
userDiv.appendChild(remove);
list.appendChild(userDiv);
console.log(res.data);
});
} catch (err) {
console.error(err);
}
}
// restFront.js
window.onload = getUser; // ํ๋ฉด ๋ก๋ฉ ์ getUser ํธ์ถ
// ํผ ์ ์ถ(submit) ์ ์คํ
document.getElementById('form').addEventListener('submit', async (e) => {
e.preventDefault();
const name = e.target.username.value;
if (!name) {
return alert('์ด๋ฆ์ ์
๋ ฅํ์ธ์');
}
try {
await axios.post('/user', { name });
getUser();
} catch (err) {
console.error(err);
}
e.target.username.value = '';
});
// about.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>RESTful SERVER</title>
<link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<div>
<h2>์๊ฐ ํ์ด์ง์
๋๋ค.</h2>
<p>์ฌ์ฉ์ ์ด๋ฆ์ ๋ฑ๋กํ์ธ์!</p>
</div>
</body>
</html>
// restServer.js
const http = require('http');
const fs = require('fs').promises;
const users = {}; // ๋ฐ์ดํฐ ์ ์ฅ์ฉ
http.createServer(async (req, res) => {
try {
if (req.method === 'GET') {
if (req.url === '/') {
const data = await fs.readFile('./restFront.html');
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
return res.end(data);
} else if (req.url === '/about') {
const data = await fs.readFile('./about.html');
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
return res.end(data);
} else if (req.url === '/users') {
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
return res.end(JSON.stringify(users));
}
// /๋ /about๋ /users๋ ์๋๋ฉด
try {
const data = await fs.readFile(`.${req.url}`);
return res.end(data);
} catch (err) {
// ์ฃผ์์ ํด๋นํ๋ ๋ผ์ฐํธ๋ฅผ ๋ชป ์ฐพ์๋ค๋ 404 Not Found error ๋ฐ์
}
} else if (req.method === 'POST') {
if (req.url === '/user') {
let body = '';
// ์์ฒญ์ body๋ฅผ stream ํ์์ผ๋ก ๋ฐ์
req.on('data', (data) => {
body += data;
});
// ์์ฒญ์ body๋ฅผ ๋ค ๋ฐ์ ํ ์คํ๋จ
return req.on('end', () => {
console.log('POST ๋ณธ๋ฌธ(Body):', body);
const { name } = JSON.parse(body);
const id = Date.now();
users[id] = name;
res.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('ok');
});
}
} else if (req.method === 'PUT') {
if (req.url.startsWith('/user/')) {
const key = req.url.split('/')[2];
let body = '';
req.on('data', (data) => {
body += data;
});
return req.on('end', () => {
console.log('PUT ๋ณธ๋ฌธ(Body):', body);
users[key] = JSON.parse(body).name;
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
return res.end('ok');
});
}
} else if (req.method === 'DELETE') {
if (req.url.startsWith('/user/')) {
const key = req.url.split('/')[2];
delete users[key];
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
return res.end('ok');
}
}
res.writeHead(404);
return res.end('NOT FOUND');
} catch (err) {
console.error(err);
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(err.message);
}
})
.listen(8082, () => {
console.log('8082๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค');
});
restServer.js๊ฐ ํต์ฌ
res.end ์์ return์ ๋ถ์ด๋ ์ด์ ๋ end๋ฅผ ํธ์ถํ๋ฉด ํจ์๊ฐ ์ข ๋ฃ๋๋ค๊ณ ์ฐฉ๊ฐํ๊ณค ํ๋ค. ๋ ธ๋๋ ์ผ๋ฐ์ ์ธ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฌธ๋ฒ์ ๋ฐ๋ฅด๋ฏ๋ก return์ ๋ถ์ด์ง ์๋ ํ ํจ์๊ฐ ์ข ๋ฃ๋์ง ์๋๋ค. ๋ฐ๋ผ์ ๋ค์์ ์ฝ๋๊ฐ ์ด๋ฌ์ง๋ ๊ฒฝ์ฐ์๋ return๋ก ์ข ๋ฃํ๋ค.
๋ง์ฝ end๊ฐ ์ฌ๋ฌ ๋ฒ ์คํ๋๋ค๋ฉด Error๊ฐ ๋ฐ์ํ๋ค.
ํด๋น ์์ ์์๋ ๋ฐ์ดํฐ๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋์ด์ ์๋ฒ ์ข ๋ฃํ๋ฉด ์์ค๋๋ค๋๊ฑฐ, 7,8 ์ฅ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ค๋ฃฐ๊ฑฐ์.
# ์ฟ ํค์ ์ธ์ ์ดํดํ๊ธฐ
ํด๋ผ์ด์ธํธ์๊ฒ ๋ณด๋ด๋ ์์ฒญ์๋ ํ๊ฐ์ง ํฐ ๋จ์ ์ด ์๋๋ฐ, ๋ฐ๋ก ๋๊ฐ ๋ณด๋ด๋์ง ๋ชจ๋ฅธ๋ค๋ ๊ฒ์ด๋ค.
๋ฌผ๋ก ์์ฒญ์ ๋ณด๋ด๋ IP์ฃผ์๋ ๋ธ๋ผ์ฐ์ ์ ์ ๋ณด๋ฅผ ๋ฐ์์ฌ ์๋ ์๋ค. ํ์ง๋ง ์ฌ๋ฌ ์ปดํจํฐ๊ฐ ๊ณตํต์ผ๋ก IP์ฃผ์๋ฅผ ๊ฐ๊ฑฐ๋ ํ ์ปดํจํฐ๋ฅผ ์ฌ๋ฌ์ฌ๋์ด ์ฌ์ฉํ ์ ์๋ค.
๋ก๊ทธ์ธ์ ๊ตฌํํ๋ ค๋ฉด ์ฟ ์ผ์ ์ธ์ ์ ์๊ณ ์์ด์ผ ํ๋ค.
๋๊ตฌ์ธ์ง ๊ธฐ์ตํ๊ธฐ ์ํด ์๋ฒ๋ ์์ฒญ์ ๋ํ ์๋ต์ ํ ๋ ์ฟ ํค๋ผ๋ ๊ฒ์ ๊ฐ์ด ๋ณด๋.
์ฟ ํค๋ ์ ํจ ๊ธฐ๊ฐ์ด ์์ผ๋ฉฐ name=lgvv์ฒ๋ผ ๋จ์ํ 'ํค-๊ฐ'์ด๋ค.
์๋ฒ๋ก๋ถํฐ ์ฟ ํค๊ฐ ์ค๋ฉด ์น ๋ธ๋ผ์ฐ์ ๋ ์ฟ ํค๋ฅผ ์ ์ฅํด๋๋ค๊ฐ ๋ค์์ ์์ฒญํ ๋๋ง๋ค ์ฟ ํค๋ฅผ ๋๋ดํด์ ๋ณด๋
์๋ฒ๋ ์์ฒญ์ ์๋ ์ฟ ํค๋ฅผ ์ฝ์ด์ ๋๊ตฌ์ธ์ง ํ๋จํจ.
๋ธ๋ผ์ฐ์ ๋ ์ฟ ํค๋ฅผ ์์์ ์๋์ผ๋ก ๋๋ดํด์ ๋ณด๋ด์ค.
์๋ฒ์์ ๋ธ๋ผ์ฐ์ ๋ก ๋ณด๋ผ๋๋ง ์ฝ๋ ์์ฑํ๋ฉด ๋๋ค.
์ฟ ํค๋ ์์ฒญ์ ํค๋์ ๋ด๊ฒจ ํจ๊ผ ์ ์ก๋๋ค. ํค์ด(Set-Cookie) ์ฌ๋ถ์ ๋ฐ๋ผ ์ฟ ํค๋ฅผ ์ ์ฅํ๋ค.
const http = require('http');
http.createServer((req, res) => {
console.log(req.url, req.headers.cookie);
res.writeHead(200, { 'Set-Cookie': 'mycookie=test' });
res.end('Hello Cookie');
})
.listen(8083, () => {
console.log('8083๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
์ฟ ํค๋ ๋ฌธ์์ด ํ์์ผ๋ก ์กด์ฌ
name=lgvv;year=2000 ํํ๋ก ์กด์ฌ. ์ฟ ํค ๊ฐ์๋ ์ธ๋ฏธ์ฝ๋ก ์ ๋ฃ์ด ๊ฐ๊ฐ์ ๊ตฌ๋ถ
createServer ๋ฉ์๋์ ์ฝ๋ฐฑ์์๋ req ๊ฐ์ฒด์ ๋ด๊ฒจ ์๋ ์ฟ ํค๋ฅผ ๊ฐ์ ธ์จ๋ค. ์ฟ ํค๋ req.headers.cookie์ ๋ค์ด์์. req.headers ๋ฉ์๋๋ฅผ ์ฌ์ฉ. ์กฐ๊ธ ์ ์ ์ฟ ํค๋ ์์ฒญ๊ณผ ์๋ต์ ํค๋๋ฅผ ํตํด ์ค๊ฐ๋ค.
์๋ต์ ํค๋์ ์ฟ ํค๋ฅผ ๊ธฐ๋กํด์ผ ํ๋ค. res.writeHead๋ฅผ ์์ฑ Set-Cookie๋ ๋ธ๋ผ์ฐ์ ํํ ๋ค์๊ณผ ๊ฐ์ ๊ฐ์ ์ฟ ํค๋ฅผ ์ ์ฅํ๋ผ๋ ์๋ฏธ.
ํ๋นํ์ด๋ ์น ์ฌ์ดํธ์ ํญ์ ๋ณด์ด๋ ์ด๋ฏธ์ง๋ฅผ ๋งํจ.
๋ค์์๋ ๊ทธ ์ฟ ํค๊ฐ ๋์ธ์ง ์๋ณํด์ฃผ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์.
// cookie2.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>์ฟ ํค&์ธ์
์ดํดํ๊ธฐ</title>
</head>
<body>
<form action="/login">
<input id="name" name="name" placeholder="์ด๋ฆ์ ์
๋ ฅํ์ธ์" />
<button id="login">๋ก๊ทธ์ธ</button>
</form>
</body>
</html>
// cookie2.js
const http = require('http');
const fs = require('fs').promises;
const url = require('url');
const qs = require('querystring');
const parseCookies = (cookie = '') =>
cookie
.split(';')
.map(v => v.split('='))
.reduce((acc, [k, v]) => {
acc[k.trim()] = decodeURIComponent(v);
return acc;
}, {});
http.createServer(async (req, res) => {
const cookies = parseCookies(req.headers.cookie); // { mycookie: 'test' }
// ์ฃผ์๊ฐ /login์ผ๋ก ์์ํ๋ ๊ฒฝ์ฐ
if (req.url.startsWith('/login')) {
const { query } = url.parse(req.url);
const { name } = qs.parse(query);
const expires = new Date();
// ์ฟ ํค ์ ํจ ์๊ฐ์ ํ์ฌ์๊ฐ + 5๋ถ์ผ๋ก ์ค์
expires.setMinutes(expires.getMinutes() + 5);
res.writeHead(302, {
Location: '/',
'Set-Cookie': `name=${encodeURIComponent(name)}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
});
res.end();
// name์ด๋ผ๋ ์ฟ ํค๊ฐ ์๋ ๊ฒฝ์ฐ
} else if (cookies.name) {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`${cookies.name}๋ ์๋
ํ์ธ์`);
} else {
try {
const data = await fs.readFile('./cookie2.html');
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(data);
} catch (err) {
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(err.message);
}
}
})
.listen(8084, () => {
console.log('8084๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
์ด ๊ฒฝ์ฐ์๋ ์ฟ ํค๊ฐ ์ฑ๋จ์์ ๋ ธ์ถ๋์ด์ ์ํํจ
const http = require('http');
const fs = require('fs').promises;
const url = require('url');
const qs = require('querystring');
const parseCookies = (cookie = '') =>
cookie
.split(';')
.map(v => v.split('='))
.reduce((acc, [k, v]) => {
acc[k.trim()] = decodeURIComponent(v);
return acc;
}, {});
const session = {};
http.createServer(async (req, res) => {
const cookies = parseCookies(req.headers.cookie);
if (req.url.startsWith('/login')) {
const { query } = url.parse(req.url);
const { name } = qs.parse(query);
const expires = new Date();
expires.setMinutes(expires.getMinutes() + 5);
const uniqueInt = Date.now();
session[uniqueInt] = {
name,
expires,
};
res.writeHead(302, {
Location: '/',
'Set-Cookie': `session=${uniqueInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
});
res.end();
// ์ธ์
์ฟ ํค๊ฐ ์กด์ฌํ๊ณ , ๋ง๋ฃ ๊ธฐ๊ฐ์ด ์ง๋์ง ์์๋ค๋ฉด
} else if (cookies.session && session[cookies.session].expires > new Date()) {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`${session[cookies.session].name}๋ ์๋
ํ์ธ์`);
} else {
try {
const data = await fs.readFile('./cookie2.html');
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(data);
} catch (err) {
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(err.message);
}
}
})
.listen(8085, () => {
console.log('8085๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
์ข ์๋ผ์ง๊ฒ ์ฟ ํค์ ์ด๋ฆ ๋ด์์ ๋ณด๋ด๋ ๋์ uniqueInt๋ผ๋ ์ซ์๊ฐ์ ๋ณด๋. ์ฌ์ฉ์์ ์ด๋ฆ๊ณผ ๋ง๋ฃ ์๊ฐ์ uniqueInt ์์ฑ๋ช ์๋์ ์๋ session์ด๋ผ๋ ๊ฐ์ฒด์ ๋์ ์ ์ฅํจ.
# https์ http2
https ๋ชจ๋์ ์น ์๋ฒ์ SSL ์ํธํ๋ฅผ ์ถ๊ฐ.
GET, POST ํ ๋ ๋ฐ์ดํฐ๋ฅผ ์ํธํํด์ ์ค๊ฐ์ ์ฑ๊ฐ๋ ํ์ธํ ์ ์๊ฒ ๋ง๋ค์ด๋ฒ๋ฆผ.
๋ก๊ทธ์ธ์ด๋ ๊ฒฐ์ ์์ ํ์๋ก ํ์ํ ์ด์ .
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
})
.listen(8080, () => { // ์๋ฒ ์ฐ๊ฒฐ
console.log('8080๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
์ธ์ฆ ๊ธฐ๊ด์์ SSL์ ๊ตฌ์ ํด์ผ ํ๋๋ฐ, Let's Encrpyt ๊ฐ์ ๊ธฐ๊ด์์ ๋ฌด๋ฃ๋ก ๋ฐ๊ธํด์ฃผ๊ธฐ๋ ํจ.
const https = require('https');
const fs = require('fs');
https.createServer({
cert: fs.readFileSync('๋๋ฉ์ธ ์ธ์ฆ์ ๊ฒฝ๋ก'),
key: fs.readFileSync('๋๋ฉ์ธ ๋น๋ฐํค ๊ฒฝ๋ก'),
ca: [
fs.readFileSync('์์ ์ธ์ฆ์ ๊ฒฝ๋ก'),
fs.readFileSync('์์ ์ธ์ฆ์ ๊ฒฝ๋ก'),
],
}, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
})
.listen(443, () => {
console.log('443๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
http2 ์ฌ์ฉํ๋ฉด ์๋๋ ๋ ๊ฐ์ ๋๋ค.
http1.1์์๋ ํ์ดํ๋ผ์ธ ๊ธฐ์ ์ ์ ์ฉํ๋ฏ๋ก ์ ์ ๋๋ก ์ฐจ์ด๊ฐ ๋์ง๋ ์์ง๋ง http/2๊ฐ ๋ ํจ์จ์ ์ธ ๊ฒ์ ๋ถ๋ช
์๋๋ http/2๋ฅผ ์ ์ฉํ๊ฑฐ์.
const http2 = require('http2');
const fs = require('fs');
http2.createSecureServer({ // ์ฌ๊ธฐ๋ง ๋ฐ๊พธ๋ฉด ๋๋ค.
cert: fs.readFileSync('๋๋ฉ์ธ ์ธ์ฆ์ ๊ฒฝ๋ก'),
key: fs.readFileSync('๋๋ฉ์ธ ๋น๋ฐํค ๊ฒฝ๋ก'),
ca: [
fs.readFileSync('์์ ์ธ์ฆ์ ๊ฒฝ๋ก'),
fs.readFileSync('์์ ์ธ์ฆ์ ๊ฒฝ๋ก'),
],
}, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
})
.listen(443, () => {
console.log('443๋ฒ ํฌํธ์์ ์๋ฒ ๋๊ธฐ ์ค์
๋๋ค!');
});
์ฝ๋๋ ๋๊ฐ์๋ฐ ์ ๊ธฐ๋ง ๋ค๋ฅด๋ค!
# Cluster
cluster ๋ชจ๋์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฑ๊ธ ํ๋ก์ธ์ค๋ก ๋์ํ๋ ๋ ธ๋๊ฐ CPU ์ฝ์ด๋ฅผ ๋ชจ๋ ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ๋ชจ๋.
์์ฒญ์ด ๋ค์ด์ค๋ฉด ๋ณ๋ ฌ๋ก ์คํ๋ ์๋ฒ์ ๊ฐ์๋งํผ ์์ฒญ์ด ๋ถ์ฐ๋๊ฒ ํ ์ ์์.
์๋ฒ์ ๋ฌด๋ฆฌ๊ฐ ๊ทธ๋งํผ ๋ ์๊น.
๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ณต์ ํ์ง ๋ชปํ๋ ๋จ์ ๋ ์กด์ฌ ๋ฐ๋ผ์ ์ธ์ ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ๋ ๊ฒฝ์ฐ ๋ฌธ์ ๊ฐ ๋ ์ ์์. ์ด๋ ๋ ๋์ค ๋ฑ์ ์๋ฒ๋ฅผ ๋์ ํด ํด๊ฒฐํ ์ ์์.
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`๋ง์คํฐ ํ๋ก์ธ์ค ์์ด๋: ${process.pid}`);
// CPU ๊ฐ์๋งํผ ์์ปค๋ฅผ ์์ฐ
for (let i = 0; i < numCPUs; i += 1) {
cluster.fork();
}
// ์์ปค๊ฐ ์ข
๋ฃ๋์์ ๋
cluster.on('exit', (worker, code, signal) => {
console.log(`${worker.process.pid}๋ฒ ์์ปค๊ฐ ์ข
๋ฃ๋์์ต๋๋ค.`);
console.log('code', code, 'signal', signal);
cluster.fork();
});
} else {
// ์์ปค๋ค์ด ํฌํธ์์ ๋๊ธฐ
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Cluster!</p>');
setTimeout(() => { // ์์ปค ์กด์ฌ๋ฅผ ํ์ธํ๊ธฐ ์ํด 1์ด๋ง๋ค ๊ฐ์ ์ข
๋ฃ
process.exit(1);
}, 1000);
}).listen(8086);
console.log(`${process.pid}๋ฒ ์์ปค ์คํ`);
}
์์ปค ์ค๋ ๋๋ ๋น์ทํจ. ํ์ง๋ง ํ๋ก์ธ์ค์
์์ฒญ์ด ๋ค์ด์ค๋ฉด ๋งใท๋ฅด์ด์ง ์์ปค ํ๋ก์ธ์ค์ ์์ฒญ์ ๋ถ๋ฐฐํจ.
์์ฒญํ๋ ์ฃผ์ ๋ง์์ง๋ฉด if๋ฌธ ๋ง์์ ธ์ ๊ด๋ฆฌ ๋ณต์กํด์ง
Express ๋ชจ๋์ ์ด์ ์์๋ณด์ #5์์
'๐ฐ๏ธ Node.js' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Node.js] #6 ์ต์คํ๋ ์ค ์น ์๋ฒ ๋ง๋ค๊ธฐ (0) | 2023.07.23 |
---|---|
[Node.js] #5 ํจํค์ง ๋งค๋์ (0) | 2023.07.23 |
[Node.js] #3 ๋ ธ๋ ๊ธฐ๋ฅ ์์๋ณด๊ธฐ (0) | 2023.07.22 |
[Node.js] #2 ์์๋ฌ์ผ ํ ์๋ฐ์คํฌ๋ฆฝํธ (0) | 2023.07.17 |
[Node.js] #1 ํต์ฌ๊ฐ๋ ์ดํดํ๊ธฐ (0) | 2023.07.16 |