프로그래머스 데브코스 9일차(웹 서버, DB)

박상하·2023년 11월 25일
0
post-thumbnail

웹 서버를 내 컴퓨터에서 개발 👀

사실 9일차는 DB에 대한 학습이 주를 이루지만 필자는 DB도 물론 이해하기 좋았지만
더 관심이 가고 더욱 스스로 학습을 했던 부분은 웹 서버 개발이다.

일단 해당 DB에 Table을 생성하고 해당 Column에 값을 집어넣는 과정 후 (이따 배울것)
해당 DB를 첫 Front-end 프로젝트에 연결하는 시간이 이번 수업의 대부분을 차지했다.

하지만 필자는 8일차에 배웠던 어떤 back-end의 기본적인 흐름에 더욱 몰두해서 강의를 시청했다.
특히, 아직 제대로 이해가 되지 않았기 때문에 더욱 그 흐름을 최대한 이해하면서 강의를 수강하고자하였다.

사실 어떤 함수를 쓰고 어떤 매개변수를 넘겨주는지 외우는 건 중요하지 않다고 생각한다. (구글링가능)
이러한 흐름을 잘 파악하고 현업에 계신분들은 어떤 루트로 이러한 데이터를 보내는지가 더 중요하다고 판단하였고

그 흐름을따라가기위해 노력 했다. 이제 필자가 이해한 웹 서버를 이곳에 포스팅하며 공유하고 정리해보겠다.

일단 이해를 하고나니 뭔가 아 이렇게 웹 서버를 띄우는거구나 라는 생각이 들었다.

자 먼저, 웹서버란 무엇이었나?

웹 서버 복습 🧗

웹 서버란 정적 페이지를 보내줄 수 있는 컴퓨터라고 생각하면 된다.

하나씩 파헤쳐보자

이런식으로 사용자의 컴퓨터는 화면(정적 페이지)을 보기위해 어떤 행동을 하나 ??

사용자는 주소창에 http://www.domain을 검색한다.
이때 이미 사용자는 정적 페이지를 요청한다.

http => 클라이언트와 웹 서버가 데이터를 주고받을 때의 약속,규약으로 해당 형식을 통해 데이터를 주고받는다.

이미 클라이언트는 http를 통해 해당 웹 서버에 정적페이지를 요청하고 있는 것이다.

그럼 그 다음 www?
www는 월드와이드웹의 줄임말로 그냥 웹을 원한다는 말로 해석하면된다.

Web => 인터넷이라는 거대한 네트워크에 연결하여 컴퓨터간 소통 할 수 있는 장

자 그러면 domain은 무엇일까

domain은 사실 ip주소를 포장한 포장지와 같다.

IP주소는 해당 서버의 인터넷 주소를 의미한다.
인터넷 프로토콜"의 약자로, 컴퓨터 네트워크에서 각 기기를 식별하기 위한 주소 체계이다.

그럼 이렇게 해석할 수 있다.

"나 http 요청할게 위 ip주소에 있는 컴퓨터에서 정적페이지좀 받아와줘"
이렇게 되는 것이다.

그리고 이번 실습은 이러한 웹 서버를 개발하는 것이다.

내 컴퓨터가 서버가 된다 👀

모든 컴퓨터는 서버가 될 수 있다.(사양은 모두 다르겠지만)

내 컴퓨터를 서버로 사용하고 싶다면 내 IP주소와 띄우고자 하는 정적 페이지의 코드만 가지고 있으면 된다.

즉, 내 컴퓨터의 IP주소를 알 수 있지 않은가? IP주소에 http 요청만하면 내 컴퓨터는 서버가된다.

그렇게
을 검색하면 다음과 같이 화면이 잘 나온다.

여기서 다른 컴퓨터에서 내 컴퓨터에 해당 서버에 대한 요청을 하기 위해서는 보안적인 장치를 해제시켜주어야하는데 이는 천천히 배우게 될 것이다.

지금 이 실습에서 중요한 건 웹 서버를 만들고 그 서버를 해당 Front-end 코드를 결합해서 해당 웹 서버에 접속하면 정적페이지인 Front-end 코드를 보여주도록 하는 것이다.

코드로 하나한 처음부터 따라가보겠다!

먼저 front-end에서 개발한 정적 페이지(html,css,js)는 다음과 같다.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Tennis Market</title>
    <link rel="icon" href="data:," />
    <style>
      #TennisBox {
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      .TennisImg {
        width: 200px;
        height: 200px;
      }
      .OrderBtn {
        text-align: center;
      }
      h1 {
        text-align: center;
      }
      h2 {
        text-align: center;
      }
      a {
        display: block;
        text-align: center;
      }
    </style>
  </head>
  <body>
    <h1>Hello My Tennis Market</h1>
    <h2>enjoy your life</h2>
    <div id="TennisBox">
      <div>
        <img class="TennisImg" src="./img/redRacket.png" />
        <br />
        <input
          class="OrderBtn"
          type="button"
          value="주문하기"
          onclick="alert(1)"
        />
      </div>
      <div>
        <img class="TennisImg" src="./img/blueRacket.png" />
        <br />
        <input
          class="OrderBtn"
          type="button"
          value="주문하기"
          onclick="alert(1)"
        />
      </div>
      <div>
        <img class="TennisImg" src="./img/blackRacket.png" />
        <br />
        <input
          class="OrderBtn"
          type="button"
          value="주문하기"
          onclick="alert(1)"
        />
      </div>
    </div>
    <br />
    <a href="./order.html">ordrlist</a>
  </body>
</html>

자, 이제 이 main.html파일을 나의 Local을 웹 서버로 삼아 보자.
즉, 내 컴퓨터의 IP로 접속을 하면 해당 정적 페이지를 보여줄 수 있도록 해보자

실습 🧗

const http = require("http");
// http 연결을 할 수 있게 해주는 Node.js에서 제공하는 모듈
const onRequest = (req, res) => {
  res.writeHead(200, { "Content-Type": "text/html" });
  //http head 부분에 요청을 받고 통과(200)시에 Content-Type: text/html을 보낼게
  res.write("main");
  //그 다음 이 화면을 보여줘
  res.end();
  //응답 끝
};

http.createServer(onRequest).listen(2080);
//2080 포트로 열었다.

위 코드로 먼저 서버를 열었다.

근데 server가 너무 많은 일을 담당한다.
이제 Router와 requestHandler 그리고 index.js를 통해 해당 함수의 기능을 조금 나누어보자

그리고 pathname을 받아와 해당 사용자가 어떤 pathname으로 접속하냐에 따라 다른 html파일을 보내주자

먼저 server와 router를 분리해주었다.

//server.js
const http = require("http");
const url = require("url");
const { route } = require("./router");
const start = (handle) => {
  const onRequest = (req, res) => {
    const pathname = url.parse(req.url).pathname;

    route(pathname, res, handle);
  };

  http.createServer(onRequest).listen(2080);
};

exports.start = start;


// router.js
function route(pathname, res, handle) {
  console.log(pathname);
  if (pathname === "/favicon.ico") {
    res.end();
    return;
    // html 파일을 가져오지 않다보니 계속 html파일의 favicon을 찾는 브라우저..
  }
  if (typeof handle[pathname] === "function") {
    handle[pathname](res);
  } else {
    res.writeHead(404, { "Content-Type": "text/html" });
    res.write("Not Founded");
    res.end();
  }
}
exports.route = route;

그리고 requestHandler부분과 route의 부분을 분리시켰다.

//requestHandler.js 
function main(res) {
  res.writeHead(200, { "Content-Type": "text/html" });
  res.write("Main");
  res.end();
}

let handle = {};
handle["/"] = main;

exports.handle = handle

마지막으로 이러한 start 함수를 Index.js에서 사용하도록하였다.

//index.js
const { handle } = require("./requestHandler");
const { start } = require("./server");

start(handle);

사실 세 번째 실습인데 현재 하면서도 굉장히 버벅거리는 경우가 많았다.
실습하며 막혔던 부분을 잠깐 정리하자면 다음과 같다.

실습 중 막힌 상황 🤯

"/favicon.ico"

아니 이거 왜 계속떠!!!

html을 가져오게 되면 해당 favicon을 계속 요청을 하는데 서버는 이를 가져다 줄 수 없다
왜냐면 지금 html을 가져오는 것이 아니라 "main"이라는 text 파일을 가져오고 있지 않는가??

그래서 그냥 PathName에 대한 예외처리를 해서 상황을 진전시킬 수 있었다.

typeof 하라니까 왜 함수를 실행시켜버리니

 if (typeof handle[pathname] === "function") {
    handle[pathname](res);
  } else {
    res.writeHead(404, { "Content-Type": "text/html" });
    res.write("Not Founded");
    res

이 코드와

 if (typeof handle[pathname](res) === "function") {
    handle[pathname](res);
  } else {
    res.writeHead(404, { "Content-Type": "text/html" });
    res.write("Not Founded");
    res

위 코드의 차이는 무엇일까?

typeof를 보면 handle[pathname]에 대한 타입을 검증해주고 예외처리를 해주려는거 같은데

필자는 그냥 타입을 고려하기도 전에

handle[pathname](res)

를 사용해 함수를 실행해 버린것이다... 이를 꼭 주의하고 타입을 검증할 땐 해당 함수를 실행하지 않도록 주의하자.

다시 이어서 실습 👀

현재는 pathname에 따라 text를 렌더링하고있다.

그게아닌 나의 Main.html파일을 연결해보자 그럼 Click을 했을 때 해당 html이 보여지고 이게 바로 웹서버의 역할이다.

// requestHandler.Js
const fs = require("fs");
function main(res) {
  const main_view = fs.readFileSync("./main.html", "utf-8");

  res.writeHead(200, { "Content-Type": "text/html" });
  res.write(main_view);
  res.end();
}

let handle = {};
handle["/"] = main;

exports.handle = handle;

위 코드에서 fs는 화면에 무언가를 띄우고싶다면, html을 띄우고 싶다면 사용할 수 있는 모듈이다.

해당 main.html을 읽어오도록 했다.

그런데 화면은 잘 가져왔지만 사진들이 깨지는 모습을 보았다.

아마 html파일을 가져오면서 해당 img파일에 대한 정보를 가져오지 못해서 그런거 같다.

pathname을 찍어보니 다음과 같이찍혔다.

왜 그럴까??

필자가 궁금했던 것은 html파일에 대한 요청을 한번 보냈는데 왜 console.log()에는 해당 이미지만큼의
request가 찍힌걸까??

구글링하고 Chat gpt에서 물어본 결과 내가 정의한 이유는 다음과 같다.

http요청을 난 한번만 했어도 클라이언트는 src만큼 또 http요청을 보낸다!

브라우저 즉 client에서 해당 html파일에 대한 http요청을 보내고
각 이미지 태그의 src(url)에 대한 http 요청을 각각 따로 요청을 또 받는다.
정확히는 각 Src에 대한 http요청을 보낸다.

그런데 지금 나의 코드에서는 해당 main.html에 대한 Request에 대한 처리는 이루어졌다.

function main(res) {
  const main_view = fs.readFileSync("./main.html", "utf-8");

  res.writeHead(200, { "Content-Type": "text/html" });
  res.write(main_view);
  res.end();
}

let handle = {};
handle["/"] = main;

하지만 다시 이미지에대한 request가 클라이언트에서 요청할 때 나의 서버(local)은 어떠한 대응도 해주고 있지 않다.

그래서 해당 Img에 대한 요구사항을 resonse해주어야한다!!

위 그림처럼 함수를 지정해주고 readFile을 통해 해당 request
즉, src에 대한 http 요청을 받아 res에 담아 보내주면 된다.

src의 갯수만큼 client에서 추가적인 http요청을 받을 수 있다.
그래서 onRequest라는 함수는 결국 4번 호출이 된것이다.

이렇게 잘 호출이 되었다

흐름을 정리하자면

Client => Web Server

나 html,css,js를 이용해서 정적페이지 보여줄래? Request보냄!
"http 프로토콜로 요청할게! 포트번호 8000맞지?" 보냈어.
어 근데 src 3개나있네 나 그것도 요청 보낼게

Web Server => Client
그래 8000맞아 req받았다. 내가 이거 요청 보고 내 local(server computer)에 있는 코드를
실행시켜서 너가 원하는 페이지 전달해줄게.
일단 너가 요청한 url을 분석해서 route한테 이걸보낼게
그럼 아마 route가 handler함수에 따라 너가 원하는 동작을 실행시켜서
이에 맞는 html파일을 보내줄거야
url에 대한 html여기있고 img는도 여기있어

드디어 DB 👀

사실 진도를 그냥 계속 나갈수도있었지만 이 앞선 흐름을 제대로 이해하고 DB를 배우고자해
조금 시간이 늦어졌다. 이제 DB를 정리해보겠다!!

먼저 DB는 Mariadb를 사용하였다.


귀엽게생겼다.

Mariadb는 Mysql을 모체로 나온 DB라고 한다.

그런데 어떤 DB든 SQL문법을 지원하는 DB툴이라면 정확히는 DBMS
"DataBase Management System"이라면 사용법은 대동소이하다!

그런데 앞에 정리한 부분이 너무 많아

게시글이 너무 길어질거 같아 여기서 끊고 다음 포스팅에서 DB에 대해 더 자세히 배워보도록하겠다!

그럼 다음 포스팅에 이어서!

0개의 댓글

관련 채용 정보