[Node.js] 서버생성
▶ 요청(request)과 응답(response) 이해하기
서버에선 요청을 받는 부분과 응답을 보내는 부분이 있어야 한다.
클라이언트로부터 요청이 왔을 때 어떤 작업을 수행할지 이벤트 리스너를 미리 등록해 놔야한다.
⭐아래 링크 꼭!!! 참고해서 공부하기 (완벽히 이해하기)
▷ 노드 서버 만들기
// http라는 객체를 생성하여 서버의 프로토콜을 http로 한다.
// requir를 통해 모듈을 사용할 수 있으며 c언어의 #include와 같다.
// http라는 모듈이 가진 라이브러리를 http에 담아 사용한다.
const http = require("http");
// 외부 서버가 아닌 host의 주소를 localhost(자기 자신)로 한다.
/* const hostname = '127.0.0.1'; //loop back (내부에서 루프를 도는 것,
즉 자기 자신(서버) 그 자체를 말한다.) */
const hostname = "localhost";
// server 객체에 이벤트를 연경한다.
/* HTTP Status Code 요청의 결과가 어떻게 되었는지를 알려주는 것 (요청(req)에 대한 상태)
ex) 요청처리중, 200 요청 성공, 404 요청오류
*/
const port = 3000; // 클라이언트가 http://localhost:3000 주소로 접속할 수 있다.
const server = http.createServer((req, res) => {
res.statusCode = 200; // 요청 성공
// charset=utf-8" 한국어 사용
res.setHeader("Content-Type", "text/html; charset=UTF-8");
res.write("Hello!!!!!!!!!!!!");
res.end("반가웠어!!!");
});
// listen을 통해 서버는 클라이언트가 언제 요청을 보낼지 듣고 있다.
// 즉,listen을 통해 클라이언트가 언제 요청을 보내는지 감시(모니터링)한다.
server.listen(port, hostname, () => {
console.log("Server running at http://${hostname}:${port}/");
});
// 서버 오류 발생시 오류 문구 출력 코드 추가
server.on("error", (error) => {
console.error("오류입니다", error);
});
- 아래와 같이 출력
- http 서버가 있어야 웹 브라우저의 요청을 처리할 수 있으므로 http 모듈을 사용했다.
- http 모듈에는 createServer 메서드가 있다.
- 서버에 접속할 때 두 개의 중요한 접속 정보 : ip 주소, port 주소
질문! text/html과 text/plain은 무슨 차이점이 있을까?
-
text/plain은 평문 텍스트 유형이므로 웹 페이지의 HTML 마크업이 아닌 이미지, CSS 코드 또는 HTML 태그가 없는 일반적인 텍스트를 반환하는 경우에 사용되는 반면,
-
text/html은 웹 페이지와 같은 구조화된 문서를 반환한다.
위 코드를 다음과 같이 축약할 수 있다.
const http = require("http");
const server = http.createServer((req, res) => {
res.setHeader(200, "Content-Type", "text/html; charset=UTF-8");
res.write("<h1>Hello!!!!!!!!!!!!</h1>");
res.end("반가웠어!!!");
});
server.listen(3000); //3000포트만 관심있다(듣는다)
// listening, error 이벤트 리스너 붙이기
server.on("listening", () => {
console.log("3000 포으테서 대기 중입니다!");
});
server.on("error", (error) => {
console.error("오류입니다", error);
});
★listen(3000);
▶ localhost와 포트
port localhost는 컴퓨터 내부 주소 -> 외부에서 접근 불가능 ★루프백주소: 밖으로 나가는게 아니고, 다시 나에게 돌아온다는 듯 ★localhost와 127.0.0.1은 동일하다.
▶ server 객체 메서드
- http 모듈의
createServer()
메서드를 사용하면 server 객체를 생성할 수 있다.
(! server 객체는 HTTP 서버를 나타낸다.)
- server 객체의
.listen()
메서드를 호출하면, 해당 포트에서 요청을 수신하는 HTTP 서버가 생성 → 이 객체를 통해 서버 측에서 클라이언트에게 응답을 보낼 수 있다. | server 객체의 메서드 | 설명 | | ————————– | ——— | | llisten(port[, callback])) | 서버 실행 | | close() | 서버 종료 |
const http = require("http");
// 웹 서버 생성
const server = http.createServer();
// 웹 서버 실행
// server.listen(port?:number, hostname?:string, backlog?:number, listeningListener:function)
server.listen();
▶ server 객체의 이벤트
server 객체의 이벤트 | 설명 |
---|---|
server.on(‘request’, callback) | 클라이언트가 요청할 때 발생하는 이벤트 |
server.on(‘connection’, callback) | 클라이언트가 접속할 때 발생하는 이벤트 |
server.on(‘close’, callback) | 서버가 종료될 떄 발생하는 이벤트 |
server.on(‘checkContinue’, callback) | 클라이언트가 지속적인 연결을 하고 있을 때 발생하는 이벤트 |
server.on(‘upgrade’, callback) | 클라이언트가 HTTP 업그레이드를 요청할 때 발생하는 이벤트 |
server.on(‘clientError’, callback) | 클라이언트에서 오류가 발생할 때 발생하는 이벤트 |
//모듈을 추출
const http = require("http");
//server 객체를 생성
const server = http.createServer();
//server 객체에 이벤트를 연결
server.on("request", function () {
console.log("클라이언트가 요청 중입니다.");
});
server.on("connection", function () {
console.log("클라이언트 접속하였습니다.");
});
server.on("close", function () {
console.log("서버가 종료되었습니다.");
});
// 서버 오류 발생시 오류 문구 출력 코드 추가
server.on("error", (error) => {
console.error("오류입니다", error);
});
//listen() 메서드를 실행
server.listen(1016, function () {
console.log("서버가 구동중입니다.");
});
▶ response 객체
- 클라이언트에 웹 페이지를 제공하기 위해response 객체를 사용
- response 객체는request 이벤트리스너의 두 번째 매개변수로 전달되는 객체
response 객체의 메서드 | 설명 |
---|---|
writeHead(statusCode, object) | 응답 헤더를 작성 |
end([data], [encoding]) | 응답 본문을 작성 |
require("http")
.createServer(function (request, response) {
//웹 서버를 생성하고 모듈을 추출합니다.
//응답합니다.
response.setHeader("Content-Type", "text/html"); //향후 검색 및 수정 가능성이 있는 경우 response.setHeader()대신 response.writeHead()
response.writeHead(200, { "Content-Type": "text/html" }); //writeHead는 한 번만 호출되어야 하며 end()가 호출되기 전에 호출되어야 함. 상태 코드는와 같은 3 자리 HTTP 상태 코드 404입니다.
response.write(); //이것은 응답 본문의 청크를 보냅니다. 이 방법은 신체의 연속적인 부분을 제공하기 위해 여러 번 호출 될 수 있습니다.
response.end("<h1>Hello Web Server with Node.js</h1>"); //데이터가 로드되었음을 서버에 알림
})
.listen(1016, function () {
//웹 서버를 실행합니다.
console.log("Server Running at localhost:1016");
});
질문! response.write()와 response.end() 차이점
-
response.write()는 여러 번 호출될 수 있기 때문에 큰 데이터를 보낼 때 사용
-
response.end()는 한 번 호출될 수 있기 때문에 데이터 전송을 완료하고 응답 종료할 대 사용
▶ html 읽어서 전송하기 (이해x)
write과 end에 문자열을 넣는 것은 비효율적 → 타이핑이 힘들기 때문
따라서 fs 모듈로 html 읽어서 전송하자 (fs모듈: FileSystem의 약자로 파일 처리와 관련된 모듈)
data로 html파일 읽어서 data를 돌려주는게 이 코드의 핵심 try catch구문: try블럭에서 error가 발생할 경우 error를 catch블럭으로 흐름이 넘어간다. (스크립트가 죽는걸 방지)
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번 포트에서 서버 대기 중입니다!");
});
▷ 여러 개의 서버 실행하기
createServer을 여러번 호출하면 된다.
단, 두 서버의 포트를 다르게 지정해야 한다.
▷ 서버 실행하기
- Ctrl +
또는 Ctrl + j로 터미널 실행한 후
node 파일명` 입력해 서버 실행하기 - 터미널 말고 파일이 폴더에 접근해 cmd를 사용하여
node 파일명
입력해 서버 실행이 가능하다.
▷ 서버 닫기
터미널에 exit
입력하면 된다.
▶ REST API
▷ REST 서버 만들기
restFront.js
const http = require("http");
const fs = require("fs").promises;
const path = require("path");
// 데이터 저장용 , 서버 메모리 확보(Database 용도... 메모리로 대체)
const users = {};
http
.createServer(async (req, res) => {
try {
console.log(req.method, req.url);
if (req.method === "GET") {
if (req.url === "/") {
// 이 파일을 읽을거야, 결과를 돌려줄거야 라는 의미
const data = await fs.readFile(
path.join(__dirname, "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(path.join(__dirname, "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)); //JSON포멧의 스트링변환처리
}
// /도 /about도 /users도 아니면
try {
const data = await fs.readFile(path.join(__dirname, 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("등록 성공");
});
}
} 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": "application/json; charset=utf-8",
});
return res.end(JSON.stringify(users));
});
}
} else if (req.method === "DELETE") {
if (req.url.startsWith("/user/")) {
const key = req.url.split("/")[2];
delete users[key];
res.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
});
return res.end(JSON.stringify(users));
}
}
res.writeHead(404);
return res.end("NOT FOUND");
} catch (err) {
console.error(err);
res.writeHead(500);
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
res.end(err);
}
})
.listen(8082, () => {
console.log("8082번 포트에서 서버 대기 중입니다");
});
코드설명
-
코드를 보면 req.method로 HTTP 요청 메서드를 구분하고 있다. 메서드가 GET이면 다시 req.url로 요청 주소를 구분한다.
-
주소가 /일 때는 restFront.html을 제공하고, 주소가 /about이면 about.html 파일을 제공하고 이외의 경우에는 주소에 적힌 파일을 제공한다..
-
/restFront.js라면 restFront.js 파일을 제공하고 /restFront.css라면 restFront.css 파일을 제공한다.
-
만약 존재하지 않는 파일을 요청했거나 GET 메서드 요청이 아닌 경우라면 404 NOT FOUND 에러가 응답으로 전송된다.
-
응답 과정 중에 예기치 못한 에러가 발생한 경우에는 500 에러가 응답으로 전송된다(실무에서는 500을 전송하는 경우는 드물다).
댓글남기기