간단한 api 를 만들었다. 코멘트로 설명한다.
const http = require("http");
//http 는 문자열을 기본으로 하기 때문에 숫자를 쓰려면 특정한 방식이 필요하다.
//id:=19 --> 이와 같이 한다. 이 방법을 따르지 않는다면 id : "19" 와 같이 나온다.
//httpie 는 출력할 때 일정한 순서를 따르기 때문에 내가 쓴 순서와 달라질 수 있다. 넘어가자.
const users = [
{
id: 1,
name: "Rebekah Johnson",
email: "Glover12345@gmail.com",
password: "123qwe",
},
{
id: 2,
name: "Fabian Predovic",
email: "Connell29@gmail.com",
password: "password",
},
];
const posts = [
{
id: 1,
title: "간단한 HTTP API 개발 시작!",
content: "Node.js에 내장되어 있는 http 모듈을 사용해서 HTTP server를 구현.",
userId: 1,
},
{
id: 2,
title: "HTTP의 특성",
content: "Request/Response와 Stateless!!",
userId: 1,
},
];
const server = http.createServer();
const httpRequestListener = (request, response) => {
//request,response 는 무조건 특정 객체가 인자로 주어진다.
//가장 아래 있는 server.on("request", callback) 때문이다.
//서버는 특정 포트에서 이벤트를 기다리다가 request 가 발생하면 콜백 함수를 실행한다.
const { url, method } = request;
//구조 분해 할당. request 안에 있는 url, method 의 프로퍼티 값을 url 과 method 의 value 로 준다.
//따라서 식의 오른쪽에 있는 객체(request)에 동일한 이름의 key 값이 있어야 한다.
if (method === "GET") { //ping pong. 메소드
if (url === "/ping") {
//url 은 http -v GET 127.0.0.1 :8000/ping... 에서 포트 번호 뒤에 있는 위치를 말함.
//convention : (REST) GET,POST,DELETE,PATCH 등은 컴퓨터에게 시키는 동작. 뒤에 url 은
//그 동작의 목적지다. 목적지는 구체적으로 해야 한다. 예를 들어서 ports/1 처럼 한다. 1은 posts 의
//postid 를 의미한다.
response.writeHead(200, { "Content-Type": "application/json" });
//response 객체의 writeHead 프로퍼티에 status code 를 먼저 넣어준다.
//상황에 맞게 넣어줘야 프론트와 평화를 유지할 수 있다.
response.end(JSON.stringify({ message: "pong" }));
//response.end() 는 함수다. 이벤트 기반이 아니다. 따라서 순차 실행된다. 모든 이벤트가 끝났을 때 실행된다.
//함수는 response 를 요청이 들어온 곳으로 돌려준다. 함수안의 값은 없어도 된다.
} else if (url === "/posts") {
response.writeHead(200, { "Content-Type": "application/json" });
response.end(JSON.stringify({ posts: posts }));
} else if (url.startsWith("/users")) {
//url 문자열이 /users 로 시작된다면 true 를 반환. 아니라면 false.
//str.startsWith(searchingString) 이 형식이다.
const userId = parseInt(url.split("/")[2]); //url 은 문자열이기 때문에 반드시 숫자로 바꿔줘야 아래 함수들이 의도한대로 작동한다.
const user = users.find((user) => user.id === userId);
//find 는 true 를 만나면 중지됨. 콜백 함수의 값을 반환한다. 따라서 불필요한 연산을 막을 수 있다.
const results = posts.filter((post) => post.userId === userId);
//filter 는 참값인 원소만 모아서 새로운 배열로 반환한다.
user.posts = results;
//results 는 filter 함수에서 받아온 배열을 가리킨다.
response.writeHead(200, { "Content-Type": "application/json" });
response.end(JSON.stringify({ data: user })); //끝을 내기 위해 필수임. 요청이 들어왔던 곳으로 respose 를 날려준다. 함수다. 이벤트 기반 아님.
}
} else if (method === "POST") { //user 신규 등록
if (url === "/users/signup") {
let rawData = "";
request.on("data", (chunk) => {
rawData += chunk;
});
//조각조각 들어오는 데이터를 모으고 합친다. 버퍼와 스트림 참조.
request.on("end", () => {
const user = JSON.parse(rawData); //모은 데이터를 자바스크립트 객체로 바꾼다. 통신상의 데이터는 상당수가 JSON 이다.
//request.on() 이벤트를 등록. data 가 들어오는 이벤트가 끝난 후 end 이벤트가 시작된다.
users.push({
id: user.id,
name: user.name,
email: user.email,
password: user.password,
});
response.writeHead(201, { "Content-Type": "application/json" });
response.end(JSON.stringify({ message: "SUCCESS" }));
//위에 썼듯이 작업을 완전히 종료시키기 위해서 response.end() 는 반드시 필요하다.
//response.writeHead 의 status code 를 정확히 등록해야 프론트가 작업하기 좋다.
});
} else if (url === "/posts") { //게시글 등록. 메소드는 POST
let rawData = "";
request.on("data", (chunk) => {
rawData += chunk;
});
request.on("end", () => {
const post = JSON.parse(rawData);
const user = users.find((user) => user.id === post.userId);
if (user) { //만약 없는 user 라면 실행이 안된다.
posts.push({
id: post.id,
name: post.title,
content: post.content,
userId: post.userId,
});
response.writeHead(201, { "Content-Type": "application/json" });
response.end(JSON.stringify({ message: "SUCCESS" }));
} else { //user 가 없을 경우 실행 된다.
response.writeHead(404, { "Content-Type": "application/json" });
response.end(JSON.stringify({ message: "NOT FOUND" }));
}
});
}
} else if (method === "PATCH") {
if (url.startsWith("/posts")) {
let rawData = "";
request.on("data", (chunk) => {
rawData += chunk;
});
request.on("end", () => {
const postId = parseInt(url.split("/")[2]); // posts/id/1
const data = JSON.parse(rawData);
const post = posts.find((post) => post.id === postId);
post.title = data.title; //수정이 목적이기 때문에.
post.content = data.content;
response.writeHead(200, { "Content-Type": "application/json" });
response.end(JSON.stringify({ post: post }));
});
}
} else if (method === "DELETE") { //특정 게시물 삭제
if (url.startsWith("/posts")) {
const postId = parseInt(url.split("/")[2]);
const indexOfPostId = posts.findIndex((post) => post.id === postId);
delete posts[indexOfPostId]; //delete 는 null 을 남긴다. 다른 삭제 방법인 splice 를 쓰자.
//posts.splice(indexOfPostId, 1);
response.writeHead(204, { "Content-Type": "application/json" });
response.end(
JSON.stringify({
message: "NO_CONTENT",
})
);
}
}
};
server.on("request", httpRequestListener);
server.listen(8000, "127.0.0.1", function () {
console.log(`Listening to request on 127.0.0.1:8000`);
});
//on 은 이벤트 등록을 한다. server.on("request", callback) 은 request 를 간절히 기다린다.
//req 가 왔을 경우 콜백 함수를 실행한다. listen 은 특정 포트에서 이벤트를 듣는다는 의미다. 하세요