이벤트 기반 코드
const http = require("http"); | |
const fs = require('fs'); | |
const server = http.createServer((req, res) => { | |
const url = req.url; | |
const method = req.method; | |
if(url === '/') { | |
res.write('<html>'); | |
res.write('<head><title>Enter Message</title></head>'); | |
res.write('<body><form action="/message" method="POST"><input type="text" name="message"><button type="submit">Send</button></form></body>'); | |
res.write('</html>'); | |
return res.end(); | |
} | |
if (url === '/message' && method === 'POST') { | |
const body = []; | |
req.on('data', (chunk) => { | |
console.log(chunk); | |
body.push(chunk); | |
}); | |
req.on('end', () => { | |
const parsedBody = Buffer.concat(body).toString(); | |
const message = parsedBody.split("=")[1]; | |
fs.writeFileSync('message.txt', message); | |
}); | |
res.statusCode = 302; | |
res.setHeader('Location', '/'); | |
return res.end(); | |
} | |
res.setHeader('Content-Type', 'text/html'); | |
res.write('<html>'); | |
res.write('<head><title>First Node.js</title></head>'); | |
res.write('<body><h1>Hello Node.js</h1></body>'); | |
res.write('</html>'); | |
res.end(); | |
}); | |
server.listen(3000); |
우선, 위 코드는 작성하는 순서대로 실행되는 것이 아님
예를 들어, 23번째 줄의 코드는 25 ~ 27번째 줄의 코드 실행 후에 실행
이 부분에서 매우 중요한 부분이 있는데,
첫 번째는 응답 발송은 이벤트 리스너 실행이 끝났다는 의미가 아니라는 점
즉, 응답이 발송된 후에도 이벤트 리스너는 계속해서 실행되고 있음
두 번째는 25 ~ 27번째 줄의 코드와 같이 이벤트 리스너의 응답에 영향을 줄 수 있는 어떠한 처리를 하는 것은 잘못되었음
이를 해결 하기 위해, 응답 코드를 이벤트 리스너에 포함시켜야 함
따라서, 아래와 같이 코드를 바꾸어야 함 ↓ ↓ ↓
const http = require("http"); | |
const fs = require('fs'); | |
const server = http.createServer((req, res) => { | |
const url = req.url; | |
const method = req.method; | |
if(url === '/') { | |
res.write('<html>'); | |
res.write('<head><title>Enter Message</title></head>'); | |
res.write('<body><form action="/message" method="POST"><input type="text" name="message"><button type="submit">Send</button></form></body>'); | |
res.write('</html>'); | |
return res.end(); | |
} | |
if (url === '/message' && method === 'POST') { | |
const body = []; | |
req.on('data', (chunk) => { | |
console.log(chunk); | |
body.push(chunk); | |
}); | |
req.on('end', () => { | |
const parsedBody = Buffer.concat(body).toString(); | |
const message = parsedBody.split("=")[1]; | |
fs.writeFileSync('message.txt', message); | |
res.statusCode = 302; | |
res.setHeader('Location', '/'); | |
return res.end(); | |
}); | |
} | |
res.setHeader('Content-Type', 'text/html'); | |
res.write('<html>'); | |
res.write('<head><title>First Node.js</title></head>'); | |
res.write('<body><h1>Hello Node.js</h1></body>'); | |
res.write('</html>'); | |
res.end(); | |
}); | |
server.listen(3000); |
이와 같이 Node.js는 함수를 함수 안에 넣으면 안에 넣은 함수를 나중에 실행하게되는데, 이를 비동기식이라고 부름 (함수 안에 넣은 함수가 항상 나중에 실행되는 것은 아님)
예를 들어, 위 코드에서 14 ~ 28번째 줄의 코드의 실행 절차는
1. 16 ~ 19 번째 줄의 핸들러와 20 ~ 28번째 줄의 핸들러 두 개를 등록함(두 핸들러를 바로 실행하지는 않음)
2. 두 핸들러가 내부적으로 이벤트 이미터에 등록됨
3. 29번째 줄의 코드로 넘어가게 됨
위의 코드를 실행 후 localhost에서 작업을 하게 된 후 터미널을 보게 되면 아래와 같은 오류를 볼 수 있음

이는 상단의 코드를 먼저 실행하지 않기 때문에 26번째 줄의 return이 함수를 중지시키지 않고 콜백 함수로 등록한 후 29번째 줄로 넘어간 다음에 실행되기 때문에 Cannot set headers 오류가 발생하게 됨
즉, 요청 분석이 끝난 후 다시 코드를 실행해서 응답을 발송하려고 했기 때문에 오류가 발생함
따라서, 29번째 줄의 코드가 너무 빨리 실행되지 않게 하기 위해 20번째 줄에 return을 입력해서 해당되는 부분이 실행되도록 하고 하단 코드의 실행이 되지 않도록 해야함
따라서, 이 부분을 유의해서 코드를 다시 고쳐보게 되면 아래와 같이 코드를 수정해주어야 함 ↓ ↓ ↓
const http = require("http"); | |
const fs = require('fs'); | |
const server = http.createServer((req, res) => { | |
const url = req.url; | |
const method = req.method; | |
if(url === '/') { | |
res.write('<html>'); | |
res.write('<head><title>Enter Message</title></head>'); | |
res.write('<body><form action="/message" method="POST"><input type="text" name="message"><button type="submit">Send</button></form></body>'); | |
res.write('</html>'); | |
return res.end(); | |
} | |
if (url === '/message' && method === 'POST') { | |
const body = []; | |
req.on('data', (chunk) => { | |
console.log(chunk); | |
body.push(chunk); | |
}); | |
return req.on('end', () => { | |
const parsedBody = Buffer.concat(body).toString(); | |
const message = parsedBody.split("=")[1]; | |
fs.writeFileSync('message.txt', message); | |
res.statusCode = 302; | |
res.setHeader('Location', '/'); | |
return res.end(); | |
}); | |
} | |
res.setHeader('Content-Type', 'text/html'); | |
res.write('<html>'); | |
res.write('<head><title>First Node.js</title></head>'); | |
res.write('<body><h1>Hello Node.js</h1></body>'); | |
res.write('</html>'); | |
res.end(); | |
}); | |
server.listen(3000); |
블로킹, 논블로킹 코드
const http = require("http"); | |
const fs = require('fs'); | |
const server = http.createServer((req, res) => { | |
const url = req.url; | |
const method = req.method; | |
if(url === '/') { | |
res.write('<html>'); | |
res.write('<head><title>Enter Message</title></head>'); | |
res.write('<body><form action="/message" method="POST"><input type="text" name="message"><button type="submit">Send</button></form></body>'); | |
res.write('</html>'); | |
return res.end(); | |
} | |
if (url === '/message' && method === 'POST') { | |
const body = []; | |
req.on('data', (chunk) => { | |
console.log(chunk); | |
body.push(chunk); | |
}); | |
return req.on('end', () => { | |
const parsedBody = Buffer.concat(body).toString(); | |
const message = parsedBody.split("=")[1]; | |
fs.writeFileSync('message.txt', message); | |
res.statusCode = 302; | |
res.setHeader('Location', '/'); | |
return res.end(); | |
}); | |
} | |
res.setHeader('Content-Type', 'text/html'); | |
res.write('<html>'); | |
res.write('<head><title>First Node.js</title></head>'); | |
res.write('<body><h1>Hello Node.js</h1></body>'); | |
res.write('</html>'); | |
res.end(); | |
}); | |
server.listen(3000); |
23번째 줄의 writeFileSync는 이 파일이 생성되기 전까지 코드 실행을 막는 특별한 메서드
즉, 파일이 완료될 때까지 코드의 다음 라인이 실행되지 않도록 하는 동기화 모드를 의미함하지만, 만약 크기가 매우 큰 파일에 이 방법을 적용할 경우 매우 안좋은 방법
따라서, writeFileSync 대신 writeFile을 사용하는 방법이 더 나음
수정 후 코드 ↓ ↓ ↓
const http = require("http"); | |
const fs = require('fs'); | |
const server = http.createServer((req, res) => { | |
const url = req.url; | |
const method = req.method; | |
if(url === '/') { | |
res.write('<html>'); | |
res.write('<head><title>Enter Message</title></head>'); | |
res.write('<body><form action="/message" method="POST"><input type="text" name="message"><button type="submit">Send</button></form></body>'); | |
res.write('</html>'); | |
return res.end(); | |
} | |
if (url === '/message' && method === 'POST') { | |
const body = []; | |
req.on('data', (chunk) => { | |
console.log(chunk); | |
body.push(chunk); | |
}); | |
return req.on('end', () => { | |
const parsedBody = Buffer.concat(body).toString(); | |
const message = parsedBody.split("=")[1]; | |
fs.writeFile('message.txt', message); | |
res.statusCode = 302; | |
res.setHeader('Location', '/'); | |
return res.end(); | |
}); | |
} | |
res.setHeader('Content-Type', 'text/html'); | |
res.write('<html>'); | |
res.write('<head><title>First Node.js</title></head>'); | |
res.write('<body><h1>Hello Node.js</h1></body>'); | |
res.write('</html>'); | |
res.end(); | |
}); | |
server.listen(3000); |
writeFile의 첫 번째 인자는 경로, 두 번째 인자는 데이터, 세 번째 인자는 콜백함수로 완료된 후에 실행되는 함수
세 번째 인자는 오류 객체를 전달받게 되는데, 오류가 발생한 경우라면 오류 응답을 반환하고, 오류가 발생하지 않았다면 정상 응답을 반환하는 방식으로 처리할 수 있음
따라서, 위 코드를 보게되면, 24 ~ 26번째 줄의 코드는 파일 작업이 완료된 경우에만 전송되어야 하기 때문에 writeFile의 세 번째 인자에 옮겨주면 매우 깔끔해짐
수정 후 코드 ↓ ↓ ↓
const http = require("http"); | |
const fs = require('fs'); | |
const server = http.createServer((req, res) => { | |
const url = req.url; | |
const method = req.method; | |
if(url === '/') { | |
res.write('<html>'); | |
res.write('<head><title>Enter Message</title></head>'); | |
res.write('<body><form action="/message" method="POST"><input type="text" name="message"><button type="submit">Send</button></form></body>'); | |
res.write('</html>'); | |
return res.end(); | |
} | |
if (url === '/message' && method === 'POST') { | |
const body = []; | |
req.on('data', (chunk) => { | |
console.log(chunk); | |
body.push(chunk); | |
}); | |
return req.on('end', () => { | |
const parsedBody = Buffer.concat(body).toString(); | |
const message = parsedBody.split("=")[1]; | |
fs.writeFile('message.txt', message, err => { | |
res.statusCode = 302; | |
res.setHeader('Location', '/'); | |
return res.end(); | |
}); | |
}); | |
} | |
res.setHeader('Content-Type', 'text/html'); | |
res.write('<html>'); | |
res.write('<head><title>First Node.js</title></head>'); | |
res.write('<body><h1>Hello Node.js</h1></body>'); | |
res.write('</html>'); | |
res.end(); | |
}); | |
server.listen(3000); |
'NodeJS' 카테고리의 다른 글
NodeJS의 작업 수행 방법 (1) | 2024.09.12 |
---|---|
라우터 요청 & 요청 리디렉션 & 분석(데이터 스트림, 버퍼) (0) | 2024.04.05 |
응답 전송 (0) | 2024.03.26 |
Node 서버 생성과 라이프사이클 및 이벤트 루프 (0) | 2024.03.26 |
웹 작동 방식 (0) | 2024.03.25 |