http λͺ¨λ
μ μ
Node.jsμ http λͺ¨λμ HTTP νλ‘ν μ½μ μ¬μ©νμ¬ μλ² λ° ν΄λΌμ΄μΈνΈλ₯Ό μμ±νλ λ° μ¬μ©λλ ν΅μ¬ λͺ¨λμ
λλ€. HTTP μλ²λ₯Ό λ§λ€κ³ μμ²μ μ²λ¦¬νλ©°, ν΄λΌμ΄μΈνΈλ‘λΆν° μμ²μ λ§λ€μ΄ μλ²λ‘ μ μ‘νλ λ° μ¬μ©λ©λλ€.
λ¬Έλ²
const http = require('http');
JavaScript
볡μ¬
μ£Όμ μμ± λ° λ©μλ
μμ±/λ©μλ | μ€λͺ
|
http.createServer() | HTTP μλ² μΈμ€ν΄μ€λ₯Ό μμ±ν©λλ€. |
server.listen() | μλ²λ₯Ό μ§μ λ ν¬νΈμ νΈμ€νΈλ‘ λ°μΈλ©νκ³ μ°κ²°μ μμ νκΈ° μμν©λλ€. |
server.close() | μλ²κ° μλ‘μ΄ μ°κ²°μ μλ½νλ κ²μ μ€λ¨ν©λλ€. |
server.on(event, callback) | μλ² μ΄λ²€νΈμ λν 리μ€λλ₯Ό λ±λ‘ν©λλ€. |
request κ°μ²΄ | μλ²λ‘ λ€μ΄μ€λ HTTP μμ²μ λνλ
λλ€. |
response κ°μ²΄ | μλ²μμ λκ°λ HTTP μλ΅μ λνλ
λλ€. |
μμ±/λ©μλ | μ€λͺ
|
req.url | μμ²μ URLμ ν¬ν¨ν©λλ€. |
req.method | μμ²μ HTTP λ©μλλ₯Ό ν¬ν¨ν©λλ€(GET, POST λ±). |
req.headers | μμ²μ ν€λλ₯Ό ν¬ν¨ν©λλ€. |
μμ±/λ©μλ | μ€λͺ
|
res.writeHead(statusCode, [headers]) | μλ΅ ν€λλ₯Ό μ§μ λ μν μ½λμ ν€λλ‘ μμ±ν©λλ€. |
res.write(data) | λ°μ΄ν°λ₯Ό μλ΅ λ³Έλ¬Έμ μμ±ν©λλ€. |
res.end([data]) | μλ΅ νλ‘μΈμ€λ₯Ό μ’
λ£νκ³ ν΄λΌμ΄μΈνΈμ λ°μ΄ν°λ₯Ό μ μ‘ν©λλ€. |
res.statusCode | μλ΅μ HTTP μν μ½λλ₯Ό κ°μ Έμ€κ±°λ μ€μ ν©λλ€. |
res.setHeader(name, value) | μλ΅μ λ¨μΌ ν€λλ₯Ό μ€μ ν©λλ€. |
res.getHeader(name) | μλ΅μμ μ§μ λ ν€λμ κ°μ κ°μ Έμ΅λλ€. |
μμ μ½λ
const http = require('http');
// HTTP μλ² μμ±
const server = http.createServer((req, res) => {
// μμ²μ λν μ²λ¦¬ λ‘μ§
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello, World!\n');
});
// μλ²κ° 3000λ² ν¬νΈμμ λμνλλ‘ λ¦¬μ€λ
server.listen(3000, '127.0.0.1', () => {
console.log('Server is listening on port 3000');
});
JavaScript
볡μ¬
μ¬μ© λͺ©μ
β’
μΉ μλ² κ°λ°: HTTP λͺ¨λμ μ¬μ©νμ¬ Node.jsμμ κ°λ¨νκ² μΉ μλ²λ₯Ό λ§λ€κ³ μμ²μ μ²λ¦¬ν μ μμ΅λλ€.
β’
API μλ² κ΅¬μΆ: HTTP λͺ¨λμ νμ©νμ¬ RESTful APIλ₯Ό μ 곡νλ μλ²λ₯Ό ꡬνν μ μμ΅λλ€.
β’
ν΄λΌμ΄μΈνΈ μμ² μ²λ¦¬: HTTP ν΄λΌμ΄μΈνΈλ₯Ό ν΅ν΄ λ€λ₯Έ μλ²λ‘ μμ²μ 보λ΄κ³ μλ΅μ μ²λ¦¬ν μ μμ΅λλ€.
localhost
β’
μ μ: "localhost"λ νμ¬ μ¬μ© μ€μΈ μ»΄ν¨ν°λ₯Ό λνλ΄λ νΉλ³ν νΈμ€νΈ μ΄λ¦μ
λλ€. μΌλ°μ μΌλ‘ μ΄κ²μ 루νλ°± μ£Όμ(Loopback address)μΈ IPv4 μ£ΌμμΈ "127.0.0.1"κ³Ό IPv6 μ£ΌμμΈ "::1"μ κ°λ¦¬ν΅λλ€. μ΄λ μ»΄ν¨ν° μ체λ₯Ό κ°λ¦¬ν€λ©°, λ€νΈμν¬λ₯Ό ν΅νμ§ μκ³ μκΈ° μμ μκ² λ°μ΄ν°λ₯Ό λ³΄λΌ μ μμ΅λλ€.
β’
μ©λ: μ£Όλ‘ κ°λ° λ° ν
μ€νΈ λͺ©μ μΌλ‘ λ‘컬 μ»΄ν¨ν°μμ μ€ν μ€μΈ μλ²μ μ κ·Όν λ μ¬μ©λ©λλ€.
port
β’
μ μ: ν¬νΈλ μ»΄ν¨ν° λ΄μ λ€νΈμν¬ μλΉμ€λ₯Ό μλ³νκΈ° μν μ«μμ
λλ€. ν¬νΈλ IP μ£Όμ λ€μ μ½λ‘ (:)μ λΆμ¬ λνλ
λλ€. μλ₯Ό λ€μ΄, "http://localhost:3000"μμ "3000"μ ν¬νΈ λ²νΈμ
λλ€.
β’
μ©λ: μ¬λ¬ μλΉμ€κ° ν μ»΄ν¨ν°μμ λμμ μ€νλ μ μλλ‘ νλ©°, ν μλ²μμ μ¬λ¬ μλΉμ€λ₯Ό μ 곡νκ±°λ μ¬λ¬ κ°μ μλ²λ₯Ό μ΄μν λ ν¬νΈλ μλΉμ€λ₯Ό μλ³νλ λ° μ¬μ©λ©λλ€.
μμ μ½λ
Hello Node (8080)
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λ² ν¬νΈμμ μλ² λκΈ° μ€μ
λλ€!');
});
JavaScript
볡μ¬
μ΄λ²€νΈ 리μ€λ
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);
});
JavaScript
볡μ¬
μ¬λ¬ μλ² μ€ν
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λ² ν¬νΈμμ μλ² λκΈ° μ€μ
λλ€!');
});
JavaScript
볡μ¬
μμΈ μ²λ¦¬
β’
js
const http = require('http');
const fs = require('fs').promises;
http.createServer(async (req, res) => {
try {
const data = await fs.readFile('./index.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(8080, () => {
console.log('8080λ² ν¬νΈμμ μλ² λκΈ° μ€μ
λλ€!');
});
JavaScript
볡μ¬
β’
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Hello Node.js</title>
</head>
<body>
<h1>Hello Node.js</h1>
<p>Main</p>
</body>
</html>
JavaScript
볡μ¬
REST (Representational State Transfer)
: μν νν μ μ‘ μν€ν
μ²
present [prΙͺΛzent]
: 보μ¬μ£Όλ€
represent [ΛreprΙͺΛzent]
: λ€μ 보μ¬μ£Όλ€, λμ νννλ€
re-(λ€μ) + present(보μ¬μ£Όλ€) λ€μ 보μ¬μ£Όλ€, λννλ€, λλ³νλ€
representational [ ΛreprΙͺzenΛteΙͺΚnl ]
: νννλ, 보μ¬μ£Όλ
REST
URI + HTTPλ©μλ λ‘ μμμ νννλ μν€ν
μ²
μν νν μ μ‘ μν€ν μ²
μμμ μνλ₯Ό νννμ¬ μ λ¬νλ μν€ν
μ²
URI λ‘ μμμ λμ νμ¬ νννκ³ , μλ²κ° ν΄μΌ ν νμ(μλ²μ μν)λ₯Ό HTTP λ©μλλ‘ νννλ κ²μ μμΉμΌλ‘ νλ μν€ν
μ²
β’
λ€νΈμν¬ μν€ν
μ² μ€μ νλμ
λλ€.
β’
νλ‘ νΈμλ λ° λ°±μλμ λ
립μ±κ³Ό νμ₯μ± ν₯μμν΅λλ€.
URI (Uniform Resource Identifier)
μμ²ν μμμ μμΉλ₯Ό λνλ΄λ λ¬Έμμ΄
: μΈν°λ· μμμ νΉμ 리μμ€λ₯Ό μλ³νκ³ μμΉλ₯Ό μ§μ νλ λ¬Έμμ΄
κ΅¬μ± μμ
scheme:[//authority]path[?query][#fragment]
JavaScript
볡μ¬
κ΅¬μ± μμ | μ€λͺ
| μμ |
Scheme | 리μμ€μ μ κ·Όνλ λ°©λ²μ λνλ | http, https, ftp |
Authority | 리μμ€μ μμΉλ μμ μλ₯Ό λνλ | |
Path | 리μμ€μ κ²½λ‘λ₯Ό λνλ | /path/to/resource |
Query | 리μμ€μ μ λ¬λλ 맀κ°λ³μλ₯Ό λνλ | ?key1=value1&key2=value2 |
Fragment | 리μμ€ λ΄μμ νΉμ λΆλΆμ κ°λ¦¬ν€λ λΆλΆ | #section1 |
https://www.example.com/path/to/resource?key1=value1&key2=value2#section1
JavaScript
볡μ¬
β’
Schemeμ "https"
β’
β’
Pathλ "/path/to/resource"
β’
Queryλ "?key1=value1&key2=value2"
β’
Fragmentλ "#section1"μ
λλ€.
HTTP λ©μλ
- ν΄λΌμ΄μΈνΈκ° μλ²κ° ν΄μΌν νμλ₯Ό μλ €μ£Όλ λͺ
λ Ήμ΄
- μμ² λͺ©μ μ μλ €μ£Όλ λͺ
λ Ήμ΄
λ©μλ | μ€λͺ
|
GET | 리μμ€λ₯Ό μμ²νκΈ° μν΄ μ¬μ©λλ λ©μλ. μ£Όλ‘ μ 보λ₯Ό μμ²νλ©°, λ°μ΄ν°λ URLμ ν¬ν¨λκ±°λ 쿼리 맀κ°λ³μλ‘ μ λ¬λ¨. |
POST | μλ²μ μλ‘μ΄ λ°μ΄ν°λ₯Ό μ μΆν λ μ¬μ©λλ λ©μλ. μ£Όλ‘ HTML μμμ ν΅ν΄ λ°μ΄ν°λ₯Ό μλ²λ‘ μ μΆν λ μ¬μ©λ¨. |
PUT | λͺ©μ 리μμ€μ νΉμ μμΉμ μλ‘μ΄ λ°μ΄ν°λ₯Ό μ μ₯νλλ‘ μμ²νλ λ©μλ. κΈ°μ‘΄ λ°μ΄ν°λ₯Ό λ체νκ±°λ μμΌλ©΄ μλ‘μ΄ λ°μ΄ν°λ₯Ό μμ±ν¨. |
DELETE | νΉμ 리μμ€λ₯Ό μμ νκΈ° μν΄ μ¬μ©λλ λ©μλ. |
PATCH | 리μμ€μ μΌλΆλ₯Ό μ
λ°μ΄νΈνκΈ° μν΄ μ¬μ©λλ λ©μλ. PUTκ³Ό μ μ¬νμ§λ§, 리μμ€ μ 체λ₯Ό μ
λ°μ΄νΈνλ λμ μΌλΆλ₯Ό μμ ν¨. |
HEAD | GET λ©μλμ μ μ¬νμ§λ§, μλ²λ μλ΅ λ³Έλ¬Έμ μ 곡νμ§ μκ³ ν€λλ§μ λ°νν¨. μ£Όλ‘ λ¦¬μμ€μ λ©νλ°μ΄ν°λ₯Ό νμΈνκΈ° μν΄ μ¬μ©λ¨. |
OPTIONS | μλ²μμ μ§μνλ λ©μλλ€μ νμΈνκΈ° μν΄ μ¬μ©λ¨. ν΄λΌμ΄μΈνΈλ μλ²μκ² μ§μλλ λ©μλλ€μ λ¬Όμ΄λ³΄κ³ , μλ²λ κ·Έμ λν μλ΅μ μ 곡ν¨. |
TRACE | ν΄λΌμ΄μΈνΈκ° λ³΄λΈ μμ² λ©μμ§λ₯Ό μλ²κΉμ§ μΆμ νκΈ° μν΄ μ¬μ©λ¨. μ£Όλ‘ λλ²κΉ
λͺ©μ μΌλ‘ μ¬μ©λλ©°, μΌλ°μ μΌλ‘ 보μμμ μ΄μ λ‘ λΉνμ±νλ¨. |
REST κΈ°λ° μλ² μ£Όμ μμ
HTTP λ©μλ | μ£Όμ | μν |
GET | / | index.html νμΌ μ 곡 |
GET | /users | μ¬μ©μ λͺ©λ‘ |
GET | /users/id | ν΄λΉ idμ μ¬μ©μ μ 보 μ 곡 |
POST | /users | μ¬μ©μ λ±λ‘ |
PUT | /users/id | ν΄λΉ idμ μ¬μ©μ μμ |
DELETE | /users/id | ν΄λΉ idμ μ¬μ©μ μμ |
GET | /about | about.html νμΌ μ 곡 |
Overview
β’
REST μμΈν μμ보기
β’
RESTful
β’
RESTful API
β’
CRUD
β’
RESTful CRUD
β’
REST μν€ν
μ²λ₯Ό μ¬μ©νλ μ΄μ
β’
Server Side Rendering (SSR)
β’
Client Side Rendering (CSR)
β’
Non-RESTful VS RESTful
β’
REST μν€ν
μ²μ 6κ°μ§ μμΉ
REST μμΈν μμ보기
2000 λ
λ‘μ΄ νλ© (Roy Fielding) μ λ°μ¬ λ
Όλ¬Έμμ μκ°λ κ°λ
μ΄λ€.
REST μν€ν
μ²λ₯Ό μ μν ν΅μ¬ μΈλ¬Ό μ€ ν λͺ
μΌλ‘, HTTP νλ‘ν μ½κ³Ό μλ μμ΄λ μΉ (WWW)μ ν΅μ¬ νμ€νμ κΈ°μ¬νμ΅λλ€. νλ©μ κ·Έμ λ°μ¬νμ λ
Όλ¬Έμμ REST(Representational State Transfer) μν€ν
μ²λ₯Ό μκ°νμ¬, λΆμ° νμ΄νΌλ―Έλμ΄ μμ€ν
μ μν μ€κ³ μμΉμ μ μνμ΅λλ€. μ΄ν λ§μ μΉ μλΉμ€μ APIκ° REST μν€ν
μ² μ€νμΌμ μ μ©νμ¬ κ΅¬μΆλμμ΅λλ€.
RESTλ Representational State Transferμ μ½μλ‘, μΉ κ°λ°μμ λ€νΈμν¬ μ ν리μΌμ΄μ
μ μ€κ³νλ λ° μμ£Ό μ¬μ©λλ μν€ν
μ² μ€νμΌμ
λλ€. RESTμ μμΉμ μ€μνλ RESTful APIλ λ€λ₯Έ μμ€ν
κ°μ HTTPλ₯Ό ν΅ν΄ ν΅μ νκ³ λ°μ΄ν°λ₯Ό κ΅νν μ μκ² ν΄μ€λλ€.
RESTλ κ°λ¨νκ³ μ μ°ν μν€ν
μ²λ‘, μμμ URI(Uniform Resource Identifier)λ‘ νννκ³ , HTTP λ©μλ(GET, POST, PUT, DELETE λ±)λ₯Ό μ¬μ©νμ¬ ν΄λΉ μμμ λν μ‘μ
μ μνν©λλ€. λν, RESTful APIλ μνλ₯Ό κ΄λ¦¬νμ§ μκ³ , μμ²μ νμν λͺ¨λ μ 보λ₯Ό μμ² μ체μ ν¬ν¨μν΅λλ€.
μ΄λ¬ν RESTful APIμ μ₯μ μ λ€μκ³Ό κ°μ΅λλ€:
β’
κ°κ²°μ±: μ§κ΄μ μ΄κ³ κ°κ²°ν μΈν°νμ΄μ€λ₯Ό μ 곡ν©λλ€.
β’
νμ₯μ±: μμ€ν
μ λμ¨νκ² κ²°ν©νμ¬ νμ₯μ΄ μ©μ΄ν©λλ€.
β’
κ°μμ±: μμμ λν νμ€νλ μΈν°νμ΄μ€λ₯Ό μ 곡νλ―λ‘, κ°λ°μλ€μ΄ APIλ₯Ό μ΄ν΄νκ³ μ¬μ©νκΈ° μ½μ΅λλ€.
λ°λΌμ, RESTful APIλ νλμ μΈ μΉ κ°λ°μμ λ§€μ° μ€μν μν μ λ΄λΉνκ³ μμΌλ©°, λ§μ μΉ μ ν리μΌμ΄μ
κ³Ό μλΉμ€κ° RESTλ₯Ό κΈ°λ°μΌλ‘ ꡬμΆλκ³ μμ΅λλ€.
RESTful
: REST μν€ν
μ²λ₯Ό λ°λ₯΄λ μμ€ν
λλ μλΉμ€λ₯Ό κ°λ¦¬ν€λ μ©μ΄μ
λλ€
RESTful API
REST μν€ν
μ² μμΉμ λ°λ₯΄λ APIλ‘, RESTful μλΉμ€λ₯Ό μν μΈν°νμ΄μ€μ
λλ€. μ΄ APIλ μΉ μλΉμ€λ₯Ό ꡬμΆνκ±°λ ν΅ν©νλ λ° μ¬μ©λ©λλ€.
μ£Όλ‘ HTTPλ₯Ό κΈ°λ°μΌλ‘ νλ©°, μμμ URLλ‘ νννκ³ HTTP λ©μλ(GET, POST, PUT, DELETE λ±)λ₯Ό μ¬μ©νμ¬ μμμ κ΄λ¦¬νλ©°, JSON λλ XMLκ³Ό κ°μ λ°μ΄ν° νμμ ν΅ν΄ λ°μ΄ν°λ₯Ό κ΅νν©λλ€.
RESTful APIλ κ°λ¨νκ³ μ§κ΄μ μ΄λ©°, μλ²μ ν΄λΌμ΄μΈνΈ μ¬μ΄μ ν΅μ μ ν¨μ¨μ μΌλ‘ μννλ λ° λμμ΄ λ©λλ€.
CRUD
CRUDλ λ°μ΄ν°μ κΈ°λ³Έμ μΈ μμ
μ λνλ΄λ μ½μ΄λ‘, Create(μμ±), Read(μ½κΈ°), Update(μμ ), Delete(μμ )μ λ€ κ°μ§ κΈ°λ³Έ μμ
μ λ§ν©λλ€. μ΄ λ€ κ°μ§ μμ
μ μ£Όλ‘ λ°μ΄ν°λ² μ΄μ€μ κ΄λ ¨λ μμ
μμ μ¬μ©λ©λλ€:
RESTful CRUD
CRUD μμ
| μμ² λ©μλ | μ€λͺ
|
Create | POST | μλ‘μ΄ λ°μ΄ν° λ μ½λλ₯Ό μμ± |
Read | GET | λ°μ΄ν°λ₯Ό μ½κ±°λ μ‘°ν |
Update | PUT/PATCH | λ°μ΄ν° λ μ½λλ₯Ό μμ λλ κ°±μ |
Delete | DELETE | λ°μ΄ν°λ₯Ό μμ |
RESTful APIλ μ΄λ¬ν HTTP λ©μλλ₯Ό μ¬μ©νμ¬ μλ²μ μμμ κ΄λ¦¬νλ λ° νμ©λ©λλ€. μλ₯Ό λ€μ΄, POST μμ²μ ν΅ν΄ μλ‘μ΄ λ°μ΄ν°λ₯Ό μμ±νκ³ , GET μμ²μ ν΅ν΄ λ°μ΄ν°λ₯Ό μ½κ±°λ μ‘°ννλ©°, PUT λλ PATCH μμ²μ ν΅ν΄ λ°μ΄ν°λ₯Ό μμ νκ³ , DELETE μμ²μ ν΅ν΄ λ°μ΄ν°λ₯Ό μμ ν μ μμ΅λλ€. μ΄λ¬ν HTTP λ©μλλ₯Ό μ΄μ©νμ¬ κ°κ°μ CRUD μμ
μ μννλ κ²μ΄ RESTful APIμ μ£Όμ νΉμ§ μ€ νλμ
λλ€.
REST μν€ν μ²λ₯Ό μ¬μ©νλ μ΄μ
RESTful APIλ μΌλ°μ μΌλ‘ HTTP νλ‘ν μ½μ λ°λ₯΄κ³ , μμ²κ³Ό μλ΅μ νμ€νλ λ°©μμΌλ‘ μ²λ¦¬ν¨μΌλ‘μ¨, λ€μν μΈμ΄λ νλ«νΌ κ°μ μνΈμ΄μ©μ±μ 보μ₯ν©λλ€. μ΄λ νλ‘ νΈμλμ λ°±μλλ₯Ό λΆλ¦¬νκ³ , κ° μμμ λ
립μ μΌλ‘ κ°λ°ν λ λ§€μ° μ μ©ν©λλ€. λ°λΌμ νλ‘ νΈμλμ λ°±μλλ₯Ό λ
립μ μΌλ‘ κ°λ°νλ κ²½μ° RESTful APIλ₯Ό μ¬μ©νλ κ²μ΄ μΌλ°μ μΌλ‘ κΆμ₯λ©λλ€.
REST λ λ€νΈμν¬ μν€ν
μ²μ μΌμ’
μ
λλ€.
μ£Όμ λ€νΈμν¬ μν€ν μ² μ’ λ₯
1.
κ³μΈ΅νλ μν€ν
μ²(Layered Architecture)
: λ€νΈμν¬ μμ€ν
μ΄ μ¬λ¬ κ³μΈ΅μΌλ‘ ꡬμ±λμ΄ μμΌλ©°, κ° κ³μΈ΅μ νΉμ ν μν κ³Ό μ±
μμ κ°μ΅λλ€. OSI 7κ³μΈ΅ λͺ¨λΈμ΄ μ΄λ¬ν κ³μΈ΅ν μν€ν
μ²μ ν μμ
λλ€.
2.
RESTful μν€ν
μ²(Representational State Transfer)
: RESTλ ν΄λΌμ΄μΈνΈμ μλ² κ°μ ν΅μ μ μν μΉ μν€ν
μ²μ ν ννλ‘, 리μμ€λ₯Ό URLμ ν΅ν΄ μλ³νκ³ HTTP λ©μλλ₯Ό μ¬μ©νμ¬ λ¦¬μμ€μ μ κ·Όνλ μν€ν
μ² μμΉμ
λλ€.
3.
λ§μ΄ν¬λ‘μλΉμ€ μν€ν
μ²(Microservices Architecture)
: μ΄ μν€ν
μ²λ μ ν리μΌμ΄μ
μ μ¬λ¬ μμ μλΉμ€λ‘ λΆν νμ¬ κ° μλΉμ€κ° λ
립μ μΌλ‘ ꡬμ±λκ³ μ€νλλλ‘ νλ ꡬ쑰λ₯Ό λ§ν©λλ€.
Server Side Rendering (SSR)
μλ² μΈ‘ λ λλ§(Server-Side Rendering, SSR)μ μ¬μ©μκ° μΉνμ΄μ§μ μ μνμ λ, μλ²μμ ν΄λΉ νμ΄μ§λ₯Ό μμ ν λ λλ§νκ³ μμ±λ HTML λ¬Έμλ‘ ν΄λΌμ΄μΈνΈμκ² μ 곡νλ κΈ°μ μ
λλ€. ν΄λΌμ΄μΈνΈμμλ μλ²λ‘λΆν° μ λ¬λ μμ±λ HTMLμ λ°μ νλ©΄μ νμνλ λ°©μμ
λλ€.
Client Side Rendering (CSR)
ν΄λΌμ΄μΈνΈ μΈ‘ λ λλ§ (Client-Side Rendering λλ CSR)μ μΉ μ ν리μΌμ΄μ
μ μ¬μ©μμ λΈλΌμ°μ μμ JavaScriptλ₯Ό μ¬μ©νμ¬ μμ ν λ λλ§νλ νλ‘μΈμ€λ₯Ό μλ―Έν©λλ€. μ΄ λ°©μμ μΉ μ ν리μΌμ΄μ
μ μ μ νμΌμ λ‘λνκ³ μ¬μ©μ μνΈ μμ©μ λ°λΌ APIμμ λ°μ΄ν°λ₯Ό κ°μ Έμ DOMμ μ‘°μνμ¬ λ΄μ©μ λμ μΌλ‘ νμν©λλ€.
CSR(Client-Side Rendering)μμλ μ£Όλ‘ JSON λλ XMLκ³Ό κ°μ λ°μ΄ν°λ₯Ό λ°μ μλ°μ€ν¬λ¦½νΈλ‘ λ λλ§νκ³ , React, Vue, Angular λ±μ νλ μμν¬ λλ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νμ¬ ν΄λΌμ΄μΈνΈ μΈ‘μμ μΉ μ ν리μΌμ΄μ
μ λ λλ§μ λ΄λΉν©λλ€. νμ΄μ§ λ‘λ© ν, νλ‘ νΈμλ λΌμ΄λΈλ¬λ¦¬κ° μλ²μμ λ°μ λ°μ΄ν°λ₯Ό λμ μΌλ‘ λ λλ§νμ¬ μ¬μ©μμκ² λ³΄μ¬μ€λλ€. μ΄ κ³Όμ μ ν΄λΌμ΄μΈνΈ μΈ‘μμ μ΄λ£¨μ΄μ§λ―λ‘ λΈλΌμ°μ μμ λΉ λ₯΄κ² νλ©΄μ νμν μ μμ΅λλ€.
Non-RESTful μμ€ν
Non-RESTful μμ€ν
μ REST μν€ν
μ²μ μμΉμ λ°λ₯΄μ§ μλ μμ€ν
μ μλ―Έν©λλ€. μ΄λ¬ν μμ€ν
μ RESTμ κΈ°λ³Έ μμΉ μ€ μΌλΆ λλ μ ν μ μ©λμ§ μμ μ μμ΅λλ€.
λΉRESTful μμ€ν
μμλ μμμ URIλ‘ νννλ κ²μ΄ λΆλͺ
ννκ±°λ μΌκ΄μ±μ΄ μμ μ μμ΅λλ€. λν, HTTP λ©μλλ₯Ό μ¬λ°λ₯΄κ² μ¬μ©νμ§ μκ±°λ, Hypermediaλ₯Ό νμ©νμ§ μλ λ± REST μν€ν
μ²μμ κ·μ ν μμΉμ λ°λ₯΄μ§ μλ νΉμ±μ λ³΄μΌ μ μμ΅λλ€.
RESTful μμ€ν
RESTful μμ€ν
μ RESTμ κΈ°λ³Έ μμΉμΈ μμ μλ³, μκΈ°μμ ν λ©μμ§, HATEOAS(νμ΄νΌλ―Έλμ΄ μ μ΄ μν) λ±μ μ μ©νλ μμ€ν
μ κ°λ¦¬ν΅λλ€. μ΄λ¬ν μμ€ν
μ μΌκ΄λ URI, HTTP λ©μλμ μ¬μ©, Hypermediaμ μ κ·Ήμ μΈ νμ©κ³Ό κ°μ REST μν€ν
μ²μ μμΉμ λ°λ₯΄λ©°, μ΄λ₯Ό ν΅ν΄ νμ₯μ±κ³Ό μ μ§λ³΄μμ±μ΄ ν₯μλ©λλ€.
Non-RESTful VS RESTful
REST μν€ν μ²μ 6κ°μ§ μμΉ
1.
μΈν°νμ΄μ€ μΌκ΄μ± (Uniform Interface)
2.
무μν (Stateless)
3.
μΊμ± κ°λ₯ (Cacheable)
4.
κ³μΈ΅ν (Layered System)
5.
Code on Demand
6.
ν΄λΌμ΄μΈνΈ/μλ² κ΅¬μ‘° (Client/Server Architecture)
μΈν°νμ΄μ€ μΌκ΄μ± (Uniform Interface)
μΌκ΄μ μΈ μΈν°νμ΄μ€λ‘ λΆλ¦¬λμ΄μΌ νλ€.
REST μΈν°νμ΄μ€λ μΌκ΄μ±μ κ°μ ΈμΌ ν©λλ€. μ΄λ 리μμ€μ μ κ·Όνλ λ°©λ²μ΄ μΌκ΄λμ΄μΌ ν¨μ μλ―Έν©λλ€. 리μμ€μ λν μ 보λ 리μμ€ μλ³μμ ν¨κ» μμ²λλ©°, μλ²λ 리μμ€ μνλ₯Ό ν΄λΌμ΄μΈνΈμκ² μ λ¬ν©λλ€.
무μν (Stateless)
κ° μμ² κ° ν΄λΌμ΄μΈνΈμ μ½ν
μ€νΈκ° μλ²μ μ μ₯λμ΄μλ μ λλ€
μμ² κ° μν μ 보λ₯Ό μ μ§νμ§ μλ μμΉμ
λλ€. κ° μμ²μ κ·Έ μμ² μ체μ νμν λͺ¨λ μ 보λ₯Ό ν¬ν¨ν΄μΌ νλ©°, μλ²λ κ° μμ²μ λ³κ°μ μμ²μΌλ‘ μ·¨κΈν΄μΌ ν©λλ€.
μΊμ± κ°λ₯ (Cacheable)
WWWμμμ κ°μ΄ ν΄λΌμ΄μΈνΈλ μλ΅μ μΊμ±ν μ μμ΄μΌ νλ€.
ν΄λΌμ΄μΈνΈλ μλ΅μ μΊμ±ν μ μμ΄μΌ ν©λλ€. μλ²λ μλ΅μ μΊμ μ μ΄ ν€λλ₯Ό μΆκ°νμ¬ μΊμ± λμμ μ‘°μ ν μ μμ΅λλ€.
μΊμ±
μΊμ±μ μμ£Ό μ¬μ©λλ λ°μ΄ν°λ₯Ό 미리 μ μ₯ν΄λκ³ νμν λλ§λ€ λΉ λ₯΄κ² μ¬μ©νλλ‘ νλ κΈ°μ μ
λλ€.
κ³μΈ΅ν (Layered System)
ν΄λΌμ΄μΈνΈλ λμ μλ²μ μ§μ μ°κ²°λμλμ§, μ°κ²°λμλμ§λ₯Ό μ μ μλ€.
RESTλ κ³μΈ΅νλ ꡬ쑰λ₯Ό κ°μ§λλ€. μλ²λ λ€μν κ³μΈ΅μΌλ‘ λλμ΄ μμΌλ©°, ν΄λΌμ΄μΈνΈλ μλ²μ μ§μ ν΅μ νμ§λ§, μ€μ μλ²κ° μ²λ¦¬λλ μμΉλ₯Ό μ νμκ° μμ΅λλ€.
Code on Demand
μλ²κ° ν΄λΌμ΄μΈνΈκ° μ€νμν¬ μ μλ λ‘μ§μ μ μ‘νμ¬ κΈ°λ₯μ νμ₯μν¬ μ μλ€.
"Code on Demand"λ 리μμ€κ° μλ²μμ ν΄λΌμ΄μΈνΈλ‘ 보λ΄μ§λ λμμ ν΄λΌμ΄μΈνΈμκ² μ€ν κ°λ₯ν μ½λλ₯Ό μ μ‘νμ¬, ν΄λΌμ΄μΈνΈκ° μ€νν μ μλ κΈ°λ₯μ νμ₯νλ μμΉμ κ°λ¦¬ν΅λλ€. ν΄λΌμ΄μΈνΈλ μ΄ μ½λλ₯Ό μ€νν μ μμ΄μΌ νλ©°, μΌλ°μ μΌλ‘ JavaScriptκ° κ°μ₯ μΌλ°μ μΈ μ¬μ© μ¬λ‘μ
λλ€.
ν΄λΌμ΄μΈνΈ/μλ² κ΅¬μ‘° (Client/Server Architecture)
ν΄λΌμ΄μΈνΈ-μλ²μ κ° ννΈκ° λ
립μ μΌλ‘ κ°μ λ μ μλλ‘ ν΄μ€λ€.
RESTλ ν΄λΌμ΄μΈνΈμ μλ²κ° λΆλ¦¬λμ΄μΌ νλ€λ μμΉμ
λλ€. μλ²λ λ°μ΄ν° μ μ₯, κ΄λ¦¬, λ°±μλ λ‘μ§μ μ²λ¦¬νκ³ , ν΄λΌμ΄μΈνΈλ μ¬μ©μ μΈν°νμ΄μ€λ₯Ό μ 곡νκ³ μ¬μ©μ μμ²μ μλ²λ‘ μ μ‘ν©λλ€.
REST μν€ν μ²μ URL λͺ λͺ κ·μΉ
λͺ μ¬ μ¬μ©
: URLμ 리μμ€λ₯Ό λνλ΄λ©°, 리μμ€μ λν λμμ HTTP λ©μλλ₯Ό ν΅ν΄ μνλλ―λ‘, λͺ
μ¬λ₯Ό μ¬μ©νμ¬ λ¦¬μμ€λ₯Ό λνλ΄λ κ²μ΄ μ’μ΅λλ€.
볡μν μ¬μ©
: 리μμ€λ₯Ό λνλΌ λ 볡μνμ μ¬μ©νλ κ²μ΄ μ’μ΅λλ€. μλ₯Ό λ€μ΄, /users, /postsμ κ°μ΄ 리μμ€ λ³΅μνμ μ¬μ©ν©λλ€.
νμ 리μμ€λ₯Ό μν μ€μ²© URL μ¬μ©
: κ΄κ³κ° μλ 리μμ€μ κ²½μ°, μ€μ²© URL ꡬ쑰 (/super/sub) λ₯Ό μ¬μ©νμ¬ κ΄κ³λ₯Ό λͺ
νν ν©λλ€.
νμ΄ν(-) λμ μΈλμ€μ½μ΄(_) μ¬μ©
: κ°λ
μ±μ λμ΄κ³ μλ³μ μ½κ² νκΈ° μν΄ μΈλ μ€μ½μ΄(_)λ₯Ό μ¬μ©νλ κ²μ΄ μ’μ΅λλ€.
(λ¨, 리μμ€ μ΄λ¦μ λμ΄μ°κΈ°λ νμ΄ν(-) μ¬μ©)
λ¨μνκ³ μ§κ΄μ μΈ URL
: URLμ 리μμ€λ₯Ό μλ³νλ λ° μ¬μ©λλ©°, μ΄κ²μ μ§κ΄μ μ΄κ³ λͺ
νν΄μΌ ν©λλ€. νλμ 리μμ€μ νλμ URL μ λμνλ κ²μ΄ μ’μΌλ©° νμΌ νμ₯μ (.html, .jsp λ±)λ₯Ό μ¬μ©νμ§ μμ΅λλ€.
λΌμ°ν (Routing)
ν΄λΌμ΄μΈνΈμ μμ²μ μ²λ¦¬ν μ²λ¦¬κΈ°(Handler, method) λ₯Ό κ²°μ νλ κ²
β’
μΉ κ°λ°μμ λΌμ°ν
μ νΉμ URL κ²½λ‘μ λν μμ²μ΄ μ΄λ€ μ²λ¦¬κΈ°(Handler)λ‘ μ λ¬λμ΄μΌ νλμ§λ₯Ό κ²°μ νλ κ²μ μλ―Έν©λλ€.
β’
ν΄λΌμ΄μΈνΈκ° νΉμ URLμ μμ²μ 보λ΄λ©΄, μλ²λ ν΄λΉ μμ²μ μ²λ¦¬ν μ μ ν νΈλ€λ¬λ 컨νΈλ‘€λ¬λ‘ λΌμ°ν
νμ¬ μ μ ν μλ΅μ μμ±ν©λλ€.
β’
μ΄λ μ£Όλ‘ μΉ μ΄ν리μΌμ΄μ
νλ μμν¬μμ ꡬνλλ©°, κ° URL κ²½λ‘μ λμλλ 컨νΈλ‘€λ¬λ νΈλ€λ¬λ₯Ό λ±λ‘νκ³ κ΄λ¦¬ν©λλ€.
λΌμ°ν μμ
HTTP λ©μλ | μ£Όμ | μν |
GET | / | index.html νμΌ μ 곡 |
GET | /users | μ¬μ©μ λͺ©λ‘ |
GET | /users/id | ν΄λΉ idμ μ¬μ©μ μ 보 μ 곡 |
POST | /users | μ¬μ©μ λ±λ‘ |
PUT | /users/id | ν΄λΉ idμ μ¬μ©μ μμ |
DELETE | /users/id | ν΄λΉ idμ μ¬μ©μ μμ |
GET | /about | about.html νμΌ μ 곡 |
μμ μ½λ
const http = require('http');
const fs = require('fs');
// μλ² μμ±
const server = http.createServer((req, res) => {
// μμ² λ©μλμ URL νμ±
const method = req.method;
const url = req.url;
// μ£Όμ λ° λ©μλμ λ°λ₯Έ μλ΅ μ²λ¦¬
if (method === 'GET' && url === '/') {
// GET / - index.html νμΌ μ 곡
fs.readFile('index.html', (err, data) => {
if (err) {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('Internal Server Error');
} else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data);
}
});
} else if (method === 'GET' && url === '/users') {
// GET /users - μ¬μ©μ λͺ©λ‘
res.writeHead(200, { 'Content-Type': 'application/json' });
const users = [{ id: 1, name: 'User 1' }, { id: 2, name: 'User 2' }];
res.end(JSON.stringify(users));
} else if (method === 'GET' && url.startsWith('/users/')) {
// GET /users/id - ν΄λΉ idμ μ¬μ©μ μ 보 μ 곡
const userId = url.split('/')[2];
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`User information for ID ${userId}`);
} else if (method === 'POST' && url === '/users') {
// POST /users - μ¬μ©μ λ±λ‘
res.writeHead(201, { 'Content-Type': 'text/plain' });
res.end('User created successfully');
} else if (method === 'PUT' && url.startsWith('/users/')) {
// PUT /users/id - ν΄λΉ idμ μ¬μ©μ μμ
const userId = url.split('/')[2];
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`User information updated for ID ${userId}`);
} else if (method === 'DELETE' && url.startsWith('/users/')) {
// DELETE /users/id - ν΄λΉ idμ μ¬μ©μ μμ
const userId = url.split('/')[2];
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`User ${userId} deleted`);
} else if (method === 'GET' && url === '/about') {
// GET /about - about.html νμΌ μ 곡
fs.readFile('about.html', (err, data) => {
if (err) {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('Internal Server Error');
} else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data);
}
});
} else {
// κ·Έ μΈμ κ²½μ° 404 Not Found
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
// μλ²λ₯Ό 3000 ν¬νΈμμ μ€ν
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
JavaScript
볡μ¬
μΏ ν€λ? (cookie)
: μΉ μλ²μ ν΄λΌμ΄μΈνΈ κ°μ μν μ 보λ₯Ό μ μ₯νκ³ μ£Όκ³ λ°μ μ μλ μμ λ°μ΄ν° μ‘°κ°
μΏ ν€λ ν΄λΌμ΄μΈνΈμ λ‘컬 λΈλΌμ°μ μ μ μ₯λλ μμ λ°μ΄ν° μ‘°κ°μΌλ‘, μλ²μμ ν΄λΌμ΄μΈνΈλ‘ μ μ‘λμ΄ μΉ μ¬μ΄νΈμμ μν μ 보λ₯Ό μ μ§νκ±°λ μ¬μ©μλ₯Ό μλ³νλ λ° μ¬μ©λ©λλ€. μ£Όλ‘ μΈμ
μ μ§, μ¬μ©μ μΆμ , μ¬μ©μ μ€μ λ±μ λͺ©μ μΌλ‘ νμ©λ©λλ€.
μ£Όμ νΉμ§:
β’
ν΄λΌμ΄μΈνΈ μΈ‘μ μ μ₯λλ―λ‘ λ³΄μμ μ·¨μ½ν μ μμ.
β’
μ£Όλ‘ ν
μ€νΈ νμμΌλ‘ μ μ₯λλ©°, μ΄λ¦κ³Ό κ°μ μμΌλ‘ ꡬμ±λ¨.
β’
λ§λ£ λ μ§ λ° κ²½λ‘ μ€μ μ ν΅ν΄ μ§μμ μ΄κ±°λ μΌμμ μΈ μ±κ²©μ μΏ ν€λ₯Ό μμ±ν μ μμ.
μΏ ν€ μμ± λ° μ μ₯
1.
μΏ ν€ μμ± λ¨κ³
μΏ ν€λ μ²μμ μλ² μΈ‘μμ μμ±λμ΄ μλ΅μ λ΄κ²¨μ μ μ‘λ¨
2.
μΏ ν€ μ μ₯ λ¨κ³
ν΄λΌμ΄μΈνΈλ μλ΅λ°μ μΏ ν€λ₯Ό μΏ ν€ μ μ₯μμ μ μ₯ν¨
3.
μΏ ν€ μ μ‘ λ¨κ³
ν΄λΌμ΄μΈνΈκ° κ°μ κ²½λ‘(URL) μμ² μ, μΏ ν€λ₯Ό ν¬ν¨νμ¬ μμ² μ μ‘ν¨
μΏ ν€ μμ± λ° μ μ₯
β’
μΏ ν€λ μλ² μΈ‘μμ μμ±
β’
μΏ ν€λ ν΄λΌμ΄μΈνΈ μΈ‘μ μ μ₯
μΏ ν€λ₯Ό μ¬μ©νλ μ£Όμ κΈ°λ₯
β’
μ¬μ©μ μΈμ¦ λ° λ‘κ·ΈμΈ κ΄λ¦¬
β’
μ₯λ°κ΅¬λ λ° μ£Όλ¬Έ μ²λ¦¬
β’
μμ΄λ μ μ₯
β’
μΈμ΄ λ° μ§μ μ€μ
β’
λ€ν¬ λͺ¨λ
μμ μ½λ
β’
μΏ ν€ λ±λ‘
- Cookie μ΄λ¦ : mycookie
- Cookie κ° : test
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(8080, () => {
console.log('8080λ² ν¬νΈμμ μλ² λκΈ° μ€μ
λλ€!');
});
JavaScript
볡μ¬
β’
μΏ ν€ μ¬μ© - js
const http = require('http');
const fs = require('fs').promises;
const path = require('path');
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 url = new URL(req.url, 'http://localhost:8080');
const name = url.searchParams.get('name');
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(path.join(__dirname, '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(8080, () => {
console.log('8080λ² ν¬νΈμμ μλ² λκΈ° μ€μ
λλ€!');
});
JavaScript
볡μ¬
β’
μΏ ν€ μ¬μ© - 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>
JavaScript
볡μ¬
μΈμ μ΄λ? (session)
: ν΄λΌμ΄μΈνΈμ μΉ μλ² κ°μ μνλ₯Ό μ§μμ μΌλ‘ μ μ§νλ λ°©λ²
μΈμ
μ μλ² μΈ‘μμ μ¬μ©μμ μνλ₯Ό μΆμ νλ κΈ°μ λ‘, ν΄λΌμ΄μΈνΈμ μλ² κ°μ μ 보λ₯Ό μ μ₯νλ λ°©μμ
λλ€. μ£Όλ‘ λ‘κ·ΈμΈ μν μ μ§, μ¬μ©μ μΈμ¦ λ±μ νμ©λ©λλ€.
μ£Όμ νΉμ§
β’
μλ²μ μ μ₯λλ―λ‘ μλμ μΌλ‘ μμ ν¨.
β’
μΈμ
IDλ₯Ό μ¬μ©νμ¬ ν΄λΌμ΄μΈνΈλ₯Ό μλ³νκ³ , μλ²μ ν΄λΉ μΈμ
κ³Ό κ΄λ ¨λ μ 보λ₯Ό μ μ₯ν¨.
β’
μΌλ°μ μΌλ‘ μΏ ν€λ₯Ό κΈ°λ°μΌλ‘ νμ§λ§, μΏ ν€λ³΄λ€λ 보μμ μΈ μΈ‘λ©΄μμ κ°λ ₯ν¨.
μΈμ μ μ¬μ©νλ μ£Όμ κΈ°λ₯
β’
μ¬μ©μ μΈμ¦ λ° λ‘κ·ΈμΈ κ΄λ¦¬
β’
μ₯λ°κ΅¬λ λ° μ£Όλ¬Έ μ²λ¦¬
μΈμ μλͺ μ£ΌκΈ°
μΈμ
μ ν΄λΌμ΄μΈνΈκ° μ¬μ΄νΈμ μ μν μκ°λΆν° λΈλΌμ°μ λ₯Ό μ’
λ£νλ μμ κΉμ§ μ μ§λ©λλ€.
β’
λ‘κ·ΈμΈ μ 보λ μΈμ
μ λ±λ‘λκΈ° λλ¬Έμ λ‘κ·ΈμΈ ν λΈλΌμ°μ μ λͺ¨λ νμ μ’
λ£νκ³ λ€μ μ¬μ΄νΈμ μ μνλ©΄ λ‘κ·Έμμμ΄ λμ΄ μλ κ²μ λ³Ό μ μμ΅λλ€.
β’
ν¬λ‘¬ λΈλΌμ°μ μ κ²½μ°, μ μν¬λ¦Ώ μ°½μ μ΄λ©΄ λ³λμ μλ‘μ΄ μΈμ
μΌλ‘ ν
μ€νΈ ν΄λ³Ό μ μμ΅λλ€.
(β¦ > μ μν¬λ¦Ώ μ°½ (Ctrl + Shift + N)
1.
μμ± (Creation)
: ν΄λΌμ΄μΈνΈκ° μΉ μ ν리μΌμ΄μ
μ μ΅μ΄λ‘ μ μνλ©΄, μλ²λ ν΄λΉ ν΄λΌμ΄μΈνΈλ₯Ό μν μλ‘μ΄ μΈμ
μ μμ±ν©λλ€. μ΄λ κ³ μ ν μΈμ
μλ³μ(ID)κ° μμ±λκ³ , μ΄ IDλ₯Ό μ¬μ©νμ¬ μΈμ
μ μλ³ν©λλ€.
2.
νμ±ν (Activation)
: μΈμ
μ΄ μμ±λλ©΄ νμ± μνκ° λλ©°, ν΄λΌμ΄μΈνΈμ μλ² κ°μ λ°μ΄ν° κ΅νμ νμ©ν©λλ€. ν΄λΌμ΄μΈνΈκ° μΉ μ ν리μΌμ΄μ
μ μμ²μ λ³΄λΌ λλ§λ€ μΈμ
μ νμ±ν μνλ₯Ό μ μ§ν©λλ€.
3.
λΉνμ±ν (Inactivation)
: μΈμ
μ μΌμ μκ° λμ νμ±ν μνλ₯Ό μ μ§ν ν, ν΄λΌμ΄μΈνΈμ μμ²μ΄ μμ κ²½μ° λΉνμ±ν μνλ‘ μ νλ©λλ€. μ΄λ, μΈμ
μ λ©λͺ¨λ¦¬μμ ν΄μ λμ§ μκ³ , μ¬νμ±νλ λκΉμ§ λκΈ°ν©λλ€.
4.
μ’
λ£ (Expiration)
: μΈμ
μ΄ λ§λ£λλ©΄ μ’
λ£λ©λλ€. λ§λ£λ λ κ°μ§ λ°©λ²μΌλ‘ λ°μν μ μμ΅λλ€. 첫 λ²μ§Έ λ°©λ²μ μΈμ
μ λ§λ μΉ μ ν리μΌμ΄μ
μ μ€μ μ μ§μ λ μκ°(μΈμ
νμμμ)μ΄ κ²½κ³Όν κ²½μ°μ
λλ€. λ λ²μ§Έ λ°©λ²μ μΈμ
μ νλ‘κ·Έλλ°μ μΌλ‘ 무ν¨ν(invalidate)νλ κ²½μ°μ
λλ€.
5.
κ°μ μ’
λ£ (Forced Termination)
: μΉ μ ν리μΌμ΄μ
κ°λ°μλ κ΄λ¦¬μκ° νμν κ²½μ° μΈμ
μ μ§μ 무ν¨νν μ μμ΅λλ€. μ΄λ‘ μΈν΄ μΈμ
μ μ¦μ μ’
λ£λλ©°, ν΄λΌμ΄μΈνΈλ λ€μ μΈμ
μ μμ±ν΄μΌ ν©λλ€.
μμμ½λ
β’
μΈμ
μ¬μ© - js
const http = require('http');
const fs = require('fs').promises;
const path = require('path');
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 url = new URL(req.url, 'http://localhost:8080');
const name = url.searchParams.get('name');
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(path.join(__dirname, '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(8080, () => {
console.log('8080λ² ν¬νΈμμ μλ² λκΈ° μ€μ
λλ€!');
});
JavaScript
볡μ¬
β’
μΈμ
μ¬μ© - 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>
JavaScript
볡μ¬
HTTP(HTTP/1.1)
μΉμμ λ°μ΄ν°λ₯Ό μμ²νκ³ μλ΅νκΈ° μν μ½μ
β’
HTTPλ "Hypertext Transfer Protocol"μ μ½μλ‘, μΉμμ λ°μ΄ν°λ₯Ό μ£Όκ³ λ°κΈ° μν νλ‘ν μ½μ
λλ€.
β’
ν΄λΌμ΄μΈνΈμ μλ² κ°μ μμ²(Request)κ³Ό μλ΅(Response)μ κ·μ νκ³ μμ΅λλ€.
β’
κΈ°λ³Έμ μΌλ‘ ν
μ€νΈ κΈ°λ°μ νλ‘ν μ½μ΄λ©°, TCPλ₯Ό μ¬μ©νμ¬ λ°μ΄ν°λ₯Ό μ μ‘ν©λλ€.
β’
κ° μμ²λ§λ€ μλ‘μ΄ TCP μ°κ²°μ μμ±νμ¬ ν΅μ ν©λλ€.
μμ μ½λ
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!\n');
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
JavaScript
볡μ¬
HTTP/2
μΉ μ±λ₯μ ν₯μμν€κΈ° μν λ€μ€ν, ν€λ μμΆ, μλ² νΈμ λ±μ κΈ°λ₯μ κ°μΆ HTTPμ μ΅μ λ²μ
β’
HTTP/2λ HTTP/1.1μ νμ λ²μ μΌλ‘, μ±λ₯μ ν₯μμν€κΈ° μν΄ μ€κ³λμμ΅λλ€.
β’
λ€μ€ν(Multiplexing): νλμ TCP μ°κ²°μ ν΅ν΄ μ¬λ¬ μμ²κ³Ό μλ΅μ λμμ μ²λ¦¬ν μ μμ΄ λ ν¨μ¨μ μΈ ν΅μ μ΄ κ°λ₯ν©λλ€.
β’
ν€λ μμΆ(Header Compression): ν€λ μ 보μ μμΆμ ν΅ν΄ λμνμ μ μ½ν©λλ€.
β’
μλ² νΈμ(Server Push): ν΄λΌμ΄μΈνΈμ μμ² μμ΄ μλ²κ° νμν 리μμ€λ₯Ό 미리 보λ΄μ€ μ μμ΅λλ€.
HTTP/2λ₯Ό μ¬μ©νκΈ° μν΄μλ HTTPSλ₯Ό μ¬μ©ν΄μΌ νλ―λ‘, ν€(server-key.pem)μ μΈμ¦μ(server-cert.pem) νμΌμ΄ νμν©λλ€.
μμμ½λ
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
}, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, HTTP/2!\n');
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
JavaScript
볡μ¬
ν€μ μΈμ¦μ νμΌ λ§λλ λ°©λ²
β’
ν€(server-key.pem)
β’
μΈμ¦μ(server-cert.pem)
1.
OpenSSL μ€μΉ
2.
ν€ λ° μΈμ¦μ μμ±
3.
ν€μ μΈμ¦μ νμΈ
OpenSSL μ€μΉ
β’
Windows
νκ²½λ³μ μ€μ
C:\Program Files\OpenSSL-Win64\bin
JSON
볡μ¬
β’
Linux, Ubuntu
sudo apt-get update
sudo apt-get install openssl
JavaScript
볡μ¬
β’
CentOS
sudo yum install openssl
JavaScript
볡μ¬
β’
macOS (homebrew)
brew install openssl
JavaScript
볡μ¬
ν€ λ° μΈμ¦μ μμ±
openssl req -x509 -newkey rsa:2048 -nodes -keyout server-key.pem -out server-cert.pem -days 365
JavaScript
볡μ¬
ν€ λ° μΈμ¦μ μμ± μ λ ₯
β’
KR
β’
Seoul
β’
Gangnam-gu
β’
aloha
β’
test
β’
127.0.0.1
β’
wwwaloha@kakao.com
Country Name (2 letter code) [AU]:KR
State or Province Name (full name) [Some-State]:Seoul
Locality Name (eg, city) []:Gangnam-gu
Organization Name (eg, company) [Internet Widgits Pty Ltd]:aloha
Organizational Unit Name (eg, section) []:test
Common Name (e.g. server FQDN or YOUR name) []:127.0.0.1
Email Address []:wwwaloha@kakao.com
JSON
볡μ¬
μμ μΈμ¦μ μμ±
λ£¨νΈ μΈμ¦μ μμ±
contoso.crt
1.
λ£¨νΈ ν€ λ§λ€κΈ°
2.
λ£¨νΈ μΈμ¦μ λ§λ€κΈ° λ° μ체 μλͺ
3.
μΈμ¦μλ₯Ό μμ±
λ£¨νΈ ν€ λ§λ€κΈ°
openssl ecparam -out contoso.key -name prime256v1 -genkey
Shell
볡μ¬
λ£¨νΈ μΈμ¦μ λ§λ€κΈ° λ° μ체 μλͺ
openssl req -new -sha256 -key contoso.key -out contoso.csr
Shell
볡μ¬
μΈμ¦μλ₯Ό μμ±
openssl x509 -req -sha256 -days 365 -in contoso.csr -signkey contoso.key -out contoso.crt
Shell
볡μ¬
μλ² μΈμ¦μ μμ±
fabrikam.crt
1.
μΈμ¦μμ ν€ λ§λ€κΈ°
2.
CSR λ§λ€κΈ°(μΈμ¦μ μλͺ
μμ²)
3.
μΈμ¦μ μμ±
μΈμ¦μμ ν€ λ§λ€κΈ°
openssl req -new -sha256 -key fabrikam.key -out fabrikam.csr
Shell
볡μ¬
CSR λ§λ€κΈ°(μΈμ¦μ μλͺ μμ²)
openssl x509 -req -in fabrikam.csr -CA contoso.crt -CAkey contoso.key -CAcreateserial -out fabrikam.crt -days 365 -sha256
Shell
볡μ¬
μΈμ¦μ μμ±
openssl x509 -in fabrikam.crt -text -noout
Shell
볡μ¬
μμμ½λ
const http2 = require('http2');
const fs = require('fs');
http2.createSecureServer({
cert: fs.readFileSync('server-cert.pem'), // μΈμ¦ νμΌ
key: fs.readFileSync('server-key.pem'), // ν€ νμΌ
ca: [
fs.readFileSync('contoso.crt'), // λ£¨νΈ μΈμ¦μ
fs.readFileSync('fabrikam.crt'), // μλ² μΈμ¦μ
],
}, (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λ² ν¬νΈμμ μλ² λκΈ° μ€μ
λλ€!');
});
JavaScript
볡μ¬
ν΄λ¬μ€ν° (Cluster)
μ±κΈ νλ‘μΈμ€λ‘ λμνλ λ
Έλλ₯Ό μ¬λ¬ CPU μ½μ΄λ₯Ό μ¬μ©ν μ μκ² ν΄μ£Όλ λͺ¨λ
Node.jsμ cluster λͺ¨λμ λ©ν° νλ‘μΈμ±μ μ§μνμ¬, λ€μ€ CPU μ½μ΄λ₯Ό νμ©νμ¬ λμ μ±λ₯μ μλ²λ₯Ό λ§λ€ μ μλλ‘ λμμ£Όλ λͺ¨λμ
λλ€. μ΄ λͺ¨λμ λ¨μΌ μ€λ λμμ λμνλ Node.js μ ν리μΌμ΄μ
μ μ¬λ¬ νλ‘μΈμ€λ‘ λλκ³ , κ° νλ‘μΈμ€κ° λ€λ₯Έ CPU μ½μ΄μμ λ³λ ¬λ‘ μ€νλλλ‘ ν©λλ€.
μ£Όμ μμ±
μμ± | μ€λͺ
|
cluster.isMaster | νμ¬ νλ‘μΈμ€κ° λ§μ€ν° νλ‘μΈμ€μΈμ§ μ¬λΆλ₯Ό λνλ΄λ λΆμΈ κ°μ
λλ€. |
cluster.isWorker | νμ¬ νλ‘μΈμ€κ° μ컀 νλ‘μΈμ€μΈμ§ μ¬λΆλ₯Ό λνλ΄λ λΆμΈ κ°μ
λλ€. |
cluster.settings | ν΄λ¬μ€ν° μ€μ μ ν¬ν¨νλ κ°μ²΄μ
λλ€. |
μ£Όμ λ©μλ
λ©μλ | μ€λͺ
|
cluster.fork([env]) | μλ‘μ΄ μ컀 νλ‘μΈμ€λ₯Ό μμ±ν©λλ€. |
cluster.setupMaster([settings]) | λ§μ€ν° νλ‘μΈμ€μ μ€μ μ λ³κ²½ν©λλ€. |
cluster.on('fork', (worker) => { }) | μλ‘μ΄ μ컀 νλ‘μΈμ€κ° μμ±λ λ λ°μνλ μ΄λ²€νΈ νΈλ€λ¬μ
λλ€. |
cluster.on('online', (worker) => { }) | μ컀 νλ‘μΈμ€κ° μ¨λΌμΈ μνλ‘ μ νλ λ λ°μνλ μ΄λ²€νΈ νΈλ€λ¬μ
λλ€. |
cluster.on('exit', (worker, code, signal) => { }) | μ컀 νλ‘μΈμ€κ° μ’
λ£λ λ λ°μνλ μ΄λ²€νΈ νΈλ€λ¬μ
λλ€. |
μμ μ½λ
// Node.js λͺ¨λμ κ°μ Έμ΅λλ€.
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
// νμ¬ νλ‘μΈμ€κ° λ§μ€ν° νλ‘μΈμ€μΈμ§ νμΈν©λλ€.
if (cluster.isMaster) {
// λ§μ€ν° νλ‘μΈμ€μ PIDλ₯Ό μΆλ ₯ν©λλ€.
console.log(`Master ${process.pid} is running`);
// CPU μ½μ΄ μλ§νΌ μ컀 νλ‘μΈμ€λ₯Ό μμ±ν©λλ€.
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
// μ컀 νλ‘μΈμ€κ° μ’
λ£λ λμ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό λ±λ‘ν©λλ€.
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
});
} else {
// κ° μ컀 νλ‘μΈμ€λ TCP μ°κ²°μ 곡μ ν μ μμ΅λλ€.
// μ΄ μμ μμλ HTTP μλ²λ‘ μ¬μ©λ©λλ€.
http.createServer((req, res) => {
// HTTP μλ΅ ν€λλ₯Ό μ€μ ν©λλ€.
res.writeHead(200);
// ν΄λΌμ΄μΈνΈμκ² 'Hello World'λ₯Ό μλ΅μΌλ‘ μ μ‘ν©λλ€.
res.end('Hello World\n');
}).listen(8000); // 8000λ² ν¬νΈμμ μλ²λ₯Ό μμν©λλ€.
// νμ¬ μ컀 νλ‘μΈμ€μ PIDλ₯Ό μΆλ ₯ν©λλ€.
console.log(`Worker ${process.pid} started`);
}
JavaScript
볡μ¬