Search

http λͺ¨λ“ˆ

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"
β€’
AuthorityλŠ” "www.example.com"
β€’
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
볡사