본문 바로가기

Node.js

Node.js(7) - HTTP, 동적 웹 페이지 구현

728x90
반응형

1) Node.js

   1-1) HTTP

   1-2) 동적 웹 페이지 구현

 

 

 

 

 

1) Node.js

1-1) HTTP

HTTP는 한 번 request와 response를 주고 받으면 연결을 끊기 때문에
다시 request와 response를 주고 받으려면 반드시 다시 연결(3-way Handshaking)을 먼저 한 뒤
request와 response를 주고 받아야 한다!!(HTTP의 "비연결성")

request와 response가 파일 단위로 계속 주고 받는다는 점을 명심할 것!!

 


lib
    - req.js
    - res.js

    - template.js
views
    - html 파일들...(index.html, list.html, write.html, modify.html, view.html)
server.js

 

코드 작업 순서
1. server 실행 부분부터 먼저 만듦!
2. 이후 응답주는 코드를 만듦!(여기서 응답에는 "send"와 "sendFile"이 있다!)

  • send
  • sendFile

3. 서버 측에서 요청 데이터를 받는 것을 만듦!

 

 

server.js

const net = require("net");
const resFn = require("./lib/res");
const reqFn = require("./lib/req");
const PORT = process.env.SERVER_PORT || 3000;
const HOST = process.env.SERVER_HOST || "127.0.0.1";

const server = net.createServer((client) => {
  client.setEncoding("utf8");

  client.on("data", (chunk) => {
    // 여기서 req, res가 잘 찍히는지 확인해야 함!!
    const res = resFn(client);
    const req = reqFn(chunk); // string을 줘서 이를 object로 반환하도록 함!!

    if (req.method === "GET" && req.path === "/") {
      res.sendFile("index.html");
    } else if (req.method === "GET" && req.path === "/list") {
      res.sendFile("list.html");
    } else if (req.method === "GET" && req.path === "/view") {
      res.sendFile("view.html");
    } else if (req.method === "GET" && req.path === "/write") {
      res.sendFile("write.html");
    } else if (req.method === "GET" && req.path === "/modify") {
      res.sendFile("modify.html");
    }
  });
});

server.on("connection", () => {
  console.log("connected to client");
});

server.listen(PORT, HOST, () => {
  console.log("server start");
});

 

req.js

const msg = `GET /user?name=sangbeom&age=27 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Content-Type: application/json

{
    "name":"sangbeom",
    "age":27
}`;

const getQuery = (queryString) => {
  if (queryString === undefined) return null;
  return queryString
    .split("&")
    .map((v) => v.split("="))
    .reduce((acc, value) => {
      const [key, val] = value;
      acc[key] = val;
      return acc;
    }, {});
};

const bodyParser = (body, contentType) => {
  if (contentType === undefined) return null;

  if (contentType.indexOf("application/json") !== -1) return JSON.parse(body);
  if (contentType.indexOf("application/x-www-form-urlencoded") !== -1)
    return getQuery(body);

  return body;
};

const getMessage = (message) => {
  let flag = false;
  let body = "";

  for (const key in message) {
    if (flag)
      body = message
        .splice(key)
        .map((v) => v.trim())
        .join("");
    if (message[key] === "") flag = true;
  }
  message.pop();

  const headers = message
    .map((v) => v.split(":"))
    .reduce((acc, value) => {
      const [key, val] = value;
      acc[key] = val;
      return acc;
    }, {});

  body = bodyParser(body, headers["Content-Type"]);

  return [headers, body];
};

const parser = (message) => {
  const header = message.split("\r\n");
  const [method, url, version] = header.shift().split(" ");
  const [path, queryString] = url.split("?");
  const query = getQuery(queryString);

  // header와 body 분리하기
  const [headers, body] = getMessage(header);

  return { method, url, version, path, queryString, query, headers, body };
};

// 결과 확인용 코드
// const result = parser(msg);
// console.log(result);

module.exports = parser;

 

res.js

const readFile = require("./template"); // 결과값이 "function"이다!!
const message = (content) => {
  const body = Buffer.from(content);

  return `HTTP/1.1 200 OK
Connection:Close
Content-Type:text/html; charset=UTF-8
Content-Length:${body.length}

${body.toString()}`;
};

module.exports = (socket) => {
  return {
    send: (body) => {
      const response = message(body);
      socket.write(response);
    },
    sendFile: (filename) => {
      const body = readFile(filename);
      const response = message(body);
      socket.write(response);
    },
  };
};

 

template.js

const fs = require("fs");
const path = require("path");

module.exports = (filename, obj = {}, defaultDir = "../views") => {
  const target = path.join(__dirname, defaultDir, filename);
  const readline = fs.readFileSync(target, "utf8");
  return readline;
};

 

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    index 페이지 입니다.
  </body>
</html>

 

list.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    list 페이지 입니다.
  </body>
</html>

 

write.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    write 페이지 입니다.
  </body>
</html>

 

view.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    view 페이지 입니다.
  </body>
</html>

 

modify.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    modify 페이지 입니다.
  </body>
</html>

 

 

 

1-2) 동적 웹 페이지 구현

server.js

const net = require("net");
const resFn = require("./lib/res");
const reqFn = require("./lib/req");
const PORT = process.env.SERVER_PORT || 3000;
const HOST = process.env.SERVER_HOST || "127.0.0.1";

const server = net.createServer((client) => {
  client.setEncoding("utf8");

  client.on("data", (chunk) => {
    // 여기서 req, res가 잘 찍히는지 확인해야 함!!
    const req = reqFn(chunk); // string을 줘서 이를 object로 반환하도록 함!!
    const res = resFn(client, req);

    if (req.method === "GET" && req.path === "/") {
      // const { name } = req.query; // name 속성의 값이 없는 경우, 즉 "undefined"일 때 코드 에러가 나서 서버가 꺼진다는 문제가 있음!!
      const name = req.query?.name; // 위의 문제를 해결하기 위해 name 속성의 값이 없으면 "undefined"를 그대로 반환하게 하는 신문법 코드이다!!
      res.sendFile("index.html", { name, title: "메인 페이지입니다." });
    } else if (req.method === "GET" && req.path === "/list") {
      res.sendFile("list.html");
    } else if (req.method === "GET" && req.path === "/view") {
      res.sendFile("view.html");
    } else if (req.method === "GET" && req.path === "/write") {
      res.sendFile("write.html");
    } else if (req.method === "GET" && req.path === "/modify") {
      res.sendFile("modify.html");
    }
  });
});

server.on("connection", () => {
  console.log("connected to client");
});

server.listen(PORT, HOST, () => {
  console.log("server start");
});

 

req.js

const msg = `GET /user?name=sangbeom&age=27 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Content-Type: application/json

{
    "name":"sangbeom",
    "age":27
}`;

const getQuery = (queryString) => {
  if (queryString === undefined) return null;
  return queryString
    .split("&")
    .map((v) => v.split("="))
    .reduce((acc, value) => {
      const [key, val] = value;
      acc[key] = val;
      return acc;
    }, {});
};

const bodyParser = (body, contentType) => {
  if (contentType === undefined) return null;

  if (contentType.indexOf("application/json") !== -1) return JSON.parse(body);
  if (contentType.indexOf("application/x-www-form-urlencoded") !== -1)
    return getQuery(body);

  return body;
};

const getMessage = (message) => {
  let flag = false;
  let body = "";

  for (const key in message) {
    if (flag)
      body = message
        .splice(key)
        .map((v) => v.trim())
        .join("");
    if (message[key] === "") flag = true;
  }
  message.pop();

  const headers = message
    .map((v) => v.split(":"))
    .reduce((acc, value) => {
      const [key, val] = value;
      acc[key] = val;
      return acc;
    }, {});

  body = bodyParser(body, headers["Content-Type"]);

  return [headers, body];
};

const parser = (message) => {
  const header = message.split("\r\n");
  const [method, url, version] = header.shift().split(" ");
  const [path, queryString] = url.split("?");
  const query = getQuery(queryString);

  const [headers, body] = getMessage(header);

  return { method, url, version, path, queryString, query, headers, body };
};

// 결과 확인용 코드
// const result = parser(msg);
// console.log(result);

module.exports = parser;

 

res.js

const readFile = require("./template"); // 결과값이 "function"이다!!
const message = (content) => {
  const body = Buffer.from(content);

  return `HTTP/1.1 200 OK
Connection:Close
Content-Type:text/html; charset=UTF-8
Content-Length:${body.length}

${body.toString()}`;
};

module.exports = (socket, req) => {
  return {
    send: (body) => {
      const response = message(body);
      socket.write(response);
    },
    sendFile: (filename, obj = {}) => {
      const body = readFile(filename, obj); //
      const response = message(body);
      socket.write(response);
    },
  };
};

 

template.js

const fs = require("fs");
const path = require("path");

// {name:'sangbeom'}
module.exports = (filename, obj = {}, defaultDir = "../views") => {
  // obj = {name:'sangbeom', title:"메인 페이지 입니다."}
  console.log(obj);
  const target = path.join(__dirname, defaultDir, filename);
  let readline = fs.readFileSync(target, "utf8"); // html string에 해당함!!
  // Javascript 스펙
  // String.prototype.replaceAll(찾을 단어, 바꿀 단어)
  // let text = readline.replaceAll("{{name}}", obj.name);
  // text = text.replaceAll("{{title}}", obj.title);
  for (const key in obj) {
    // key = name, obj[key] = 'sangbeom'
    readline = readline.replaceAll(`{{${key}}}`, obj[key]);
    // console.log(key, obj[key]); // 확인용 코드
  }

  return readline;
};

 

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{title}}</title>
  </head>
  <body>
    index 페이지 입니다. {{name}}
  </body>
</html>

 

 

CSS, Javascript 적용 버전 코드

server.js

const net = require("net");
const resFn = require("./lib/res");
const reqFn = require("./lib/req");
const PORT = process.env.SERVER_PORT || 3000;
const HOST = process.env.SERVER_HOST || "127.0.0.1";

const server = net.createServer((client) => {
  client.setEncoding("utf8");

  client.on("data", (chunk) => {
    // 여기서 req, res가 잘 찍히는지 확인해야 함!!
    const req = reqFn(chunk); // string을 줘서 이를 object로 반환하도록 함!!
    const res = resFn(client, req);

    if (req.method === "GET" && req.path === "/") {
      // const { name } = req.query;
      const name = req.query?.name;
      res.sendFile("index.html", { name:name, title: "메인 페이지입니다." });
    } else if (req.method === "GET" && req.path === "/list") {
      res.sendFile("list.html");
    } else if (req.method === "GET" && req.path === "/view") {
      res.sendFile("view.html");
    } else if (req.method === "GET" && req.path === "/write") {
      res.sendFile("write.html");
    } else if (req.method === "GET" && req.path === "/modify") {
      res.sendFile("modify.html");
    } else if (req.method === "GET" && req.path === "/css/index.css") {
      res.sendStatic("/css/index.css");
    } else if (req.method === "GET" && req.path === "/js/index.js") {
      res.sendStatic("/js/index.js");
    }
  });
});

server.on("connection", () => {
  console.log("connected to client");
});

server.listen(PORT, HOST, () => {
  console.log("server start");
});

 

req.js

const msg = `GET /user?name=sangbeom&age=27 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Content-Type: application/json

{
    "name":"sangbeom",
    "age":27
}`;

const getQuery = (queryString) => {
  if (queryString === undefined) return null;
  return queryString
    .split("&")
    .map((v) => v.split("="))
    .reduce((acc, value) => {
      const [key, val] = value;
      acc[key] = val;
      return acc;
    }, {});
};

const bodyParser = (body, contentType) => {
  if (contentType === undefined) return null;

  if (contentType.indexOf("application/json") !== -1) return JSON.parse(body);
  if (contentType.indexOf("application/x-www-form-urlencoded") !== -1)
    return getQuery(body);

  return body;
};

const getMessage = (message) => {
  let flag = false;
  let body = "";

  for (const key in message) {
    if (flag)
      body = message
        .splice(key)
        .map((v) => v.trim())
        .join("");
    if (message[key] === "") flag = true;
  }
  message.pop();

  const headers = message
    .map((v) => v.split(":"))
    .reduce((acc, value) => {
      const [key, val] = value;
      acc[key] = val;
      return acc;
    }, {});

  body = bodyParser(body, headers["Content-Type"]);

  return [headers, body];
};

const parser = (message) => {
  const header = message.split("\r\n");
  const [method, url, version] = header.shift().split(" ");
  const [path, queryString] = url.split("?");
  const query = getQuery(queryString);

  const [headers, body] = getMessage(header);

  return { method, url, version, path, queryString, query, headers, body };
};

// 결과 확인용 코드
// const result = parser(msg);
// console.log(result);

module.exports = parser;

 

res.js

const readFile = require("./template"); // 결과값이 "function"이다!!
const message = (content, req) => {
  const body = Buffer.from(content);
  let contentType = "";
  if (req.headers.Accept.indexOf("text/html") !== -1) {
    contentType = "text/html";
  } else {
    contentType = req.headers.Accept;
  }

  return `HTTP/1.1 200 OK
Connection:Close
Content-Type:${contentType}; charset=UTF-8
Content-Length:${body.length}

${body.toString()}`;
};

module.exports = (socket, req) => {
  return {
    send: (body) => {
      const response = message(body, req);
      socket.write(response);
    },
    sendFile: (filename, obj = {}) => {
      const body = readFile(filename, obj); //
      const response = message(body, req);
      socket.write(response);
    },
    sendStatic: (filename) => {
      const defaultDir = "../public";
      const body = readFile(filename, {}, defaultDir);
      const response = message(body, req);
      socket.write(response);
    },
  };
};

 

template.js

const fs = require("fs");
const path = require("path");

// {name:'sangbeom'}
module.exports = (filename, obj = {}, defaultDir = "../views") => {
  // obj = {name:'sangbeom', title:"메인 페이지 입니다."}
  console.log(obj);
  const target = path.join(__dirname, defaultDir, filename);
  let readline = fs.readFileSync(target, "utf8"); // html string에 해당함!!
  // Javascript 스펙
  // String.prototype.replaceAll(찾을 단어, 바꿀 단어)
  // let text = readline.replaceAll("{{name}}", obj.name);
  // text = text.replaceAll("{{title}}", obj.title);
  for (const key in obj) {
    // key = name, obj[key] = 'sangbeom'
    readline = readline.replaceAll(`{{${key}}}`, obj[key]);
    // console.log(key, obj[key]); // 확인용 코드
  }

  return readline;
};

 

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{title}}</title>
    <link rel="stylesheet" type="text/css" href="/css/index.css" />
    <script type="text/javascript" src="/js/index.js"></script>
  </head>
  <body>
    index 페이지 입니다. {{name}}
  </body>
</html>

 

index.css

* {
  margin: 0;
  padding: 0;
}

body {
  background: #bee;
}

 

index.js

alert("success?");

 

 

CSS, Javascript 적용 버전 코드 - Refactoring

test.js

// fs(파일 시스템)

// fs.readdir => 비동기로 처리됨!
// fs.readdirSync => 동기적으로 처리되며, 이는 "ls"와 같다!!(파일명을 배열로 return해 줌!!)
const fs = require("fs");
const path = require("path");

// 경로
const searchPath = path.join(__dirname, "public");
const dir = fs.readdirSync(searchPath);

console.log(dir); // [ 'css', 'images', 'js' ]

// fs.stat(경로) : 해당 경로에 있는 directory 또는 file의 정보를 나타내줌!(비동기)
// fs.statSync(경로) : 해당 경로에 있는 directory 또는 file의 정보를 나타내줌!(동기)
// fs.stat(경로)과 fs.statSync(경로)의 경우, return 값이 객체이며, 해당 객체 안에는 method들이 많다!
// 이 method들 중에 isFile() 메서드가 있는데 이를 통해 파일인지 아닌지 알 수 있음!!
for (const index in dir) {
  console.log(dir[index]);
}

const find = path.join(searchPath, "1.js");
// const fileStat = fs.statSync(find); // return 값이 객체(Object)이다!!
// console.log(fileStat.isFile()); // return 값이 boolean이다!!
const isFile = fs.statSync(find).isFile();
console.log(isFile); // return 값이 boolean이다!!


/*
// 아래 내용을 구현하려 함!!
{
    '/css/index.css':'index.css',
    '/css/header/index.css':'index.css',
    '/images/1.jpg':'1.jpg',
    '/js/index.js':'index.js',
    '/1.js':'1.js'
}
*/

// 해당 내용이 디렉토리일 경우,
// 해당 내용이 파일일 경우

 

server.js

const net = require("net");
const resFn = require("./lib/res");
const reqFn = require("./lib/req");
const static = require("./lib/static");
const PORT = process.env.SERVER_PORT || 3000;
const HOST = process.env.SERVER_HOST || "127.0.0.1";

const server = net.createServer((client) => {
  client.setEncoding("utf8");

  client.on("data", (chunk) => {
    // 여기서 req, res가 잘 찍히는지 확인해야 함!!
    const req = reqFn(chunk); // string을 줘서 이를 object로 반환하도록 함!!
    const res = resFn(client, req);

    for (const path in static) {
      if (req.method === "GET" && req.path === path) {
        res.sendStatic(path);
      }
    }

    if (req.method === "GET" && req.path === "/") {
      // const { name } = req.query;
      const name = req.query?.name;
      res.sendFile("index.html", { name, title: "메인 페이지입니다." });
    } else if (req.method === "GET" && req.path === "/list") {
      res.sendFile("list.html");
    } else if (req.method === "GET" && req.path === "/view") {
      res.sendFile("view.html");
    } else if (req.method === "GET" && req.path === "/write") {
      res.sendFile("write.html");
    } else if (req.method === "GET" && req.path === "/modify") {
      res.sendFile("modify.html");
    } else if (req.method === "POST" && req.path === "/write") {
      console.log(req.body.subject);
    }
  });
});

server.on("connection", () => {
  console.log("connected to client");
});

server.listen(PORT, HOST, () => {
  console.log("server start");
});

 

req.js

const msg = `GET /user?name=sangbeom&age=27 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Content-Type: application/json

{
    "name":"sangbeom",
    "age":27
}`;

const getQuery = (queryString) => {
  if (queryString === undefined) return null;
  return queryString
    .split("&")
    .map((v) => v.split("="))
    .reduce((acc, value) => {
      const [key, val] = value;
      acc[key] = val;
      return acc;
    }, {});
};

const bodyParser = (body, contentType) => {
  if (contentType === undefined) return null;

  if (contentType.indexOf("application/json") !== -1) return JSON.parse(body);
  if (contentType.indexOf("application/x-www-form-urlencoded") !== -1)
    return getQuery(body);

  return body;
};

const getMessage = (message) => {
  let flag = false;
  let body = "";

  for (const key in message) {
    if (flag)
      body = message
        .splice(key)
        .map((v) => v.trim())
        .join("");
    if (message[key] === "") flag = true;
  }
  message.pop();

  const headers = message
    .map((v) => v.split(":"))
    .reduce((acc, value) => {
      const [key, val] = value;
      acc[key] = val;
      return acc;
    }, {});

  body = bodyParser(body, headers["Content-Type"]);

  return [headers, body];
};

const parser = (message) => {
  const header = message.split("\r\n");
  const [method, url, version] = header.shift().split(" ");
  const [path, queryString] = url.split("?");
  const query = getQuery(queryString);

  const [headers, body] = getMessage(header);

  return { method, url, version, path, queryString, query, headers, body };
};

// 결과 확인용 코드
// const result = parser(msg);
// console.log(result);

module.exports = parser;

 

res.js

const readFile = require("./template"); // 결과값이 "function"이다!!
const message = (content, req) => {
  const body = Buffer.from(content);
  let contentType = "";
  if (req.headers.Accept.indexOf("text/html") !== -1) {
    contentType = "text/html";
  } else {
    contentType = req.headers.Accept;
  }

  return `HTTP/1.1 200 OK
Connection:Close
Content-Type:${contentType}; charset=UTF-8
Content-Length:${body.length}

${body.toString()}`;
};

module.exports = (socket, req) => {
  return {
    send: (body) => {
      const response = message(body, req);
      socket.write(response);
    },
    sendFile: (filename, obj = {}) => {
      const body = readFile(filename, obj); //
      const response = message(body, req);
      socket.write(response);
    },
    sendStatic: (filename) => {
      const defaultDir = "../public";
      const body = readFile(filename, {}, defaultDir);
      const response = message(body, req);
      socket.write(response);
    },
  };
};

 

template.js

const fs = require("fs");
const path = require("path");

// {name:'sangbeom'}
module.exports = (filename, obj = {}, defaultDir = "../views") => {
  // obj = {name:'sangbeom', title:"메인 페이지 입니다."}
  console.log(obj);
  const target = path.join(__dirname, defaultDir, filename);
  let readline = fs.readFileSync(target, "utf8"); // html string에 해당함!!
  // Javascript 스펙
  // String.prototype.replaceAll(찾을 단어, 바꿀 단어)
  // let text = readline.replaceAll("{{name}}", obj.name);
  // text = text.replaceAll("{{title}}", obj.title);
  for (const key in obj) {
    // key = name, obj[key] = 'sangbeom'
    readline = readline.replaceAll(`{{${key}}}`, obj[key]);
    // console.log(key, obj[key]); // 확인용 코드
  }

  return readline;
};

 

static.js

const fs = require("fs");
const path = require("path");

const root = "../public";
const rootDir = path.join(__dirname, root);

let result = {};
const find = (currentPath) => {
  // "css js images 1.js" 찍기
  const directory = fs.readdirSync(currentPath);
  for (const index in directory) {
    const findPath = path.join(currentPath, directory[index]);
    const isFile = fs.statSync(findPath).isFile(); // file일 경우: true, directory일 경우: false

    if (!isFile) {
      // directory일 경우
      find(findPath);
    } else {
      // file일 경우
      const key =
        currentPath === rootDir ? "/" : currentPath.replaceAll(rootDir, "");
      const httpPath = path.join(key, directory[index]);
      result[httpPath] = directory[index];
      // directory[index] = 파일명
      // findPath = currentPath + directory[index]
    }
  }

  return result;
};

// const a = find(rootDir);
// console.log(a);

module.exports = find(rootDir); // 결과값으로 "객체({})"를 반환해 줌!!

 

write.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    write 페이지 입니다.
    <form method="post" action="/write">
      <input type="text" name="subject" />
      <button type="submit">버튼~~~</button>
    </form>
  </body>
</html>

 

 

+) 용어 정리

중간에 낀 서버 혹은 코드가 "미들웨어"이다!!
조건문처럼 어떤 요청이 들어왔을 때 어떤 것을 실행할지 어떤 것을 보낼지 결정하는 것(코드)을 "Router"라 한다!!