[데브코스 TIL] Day 8 - Node.js로 만드는 웹 서버 - 웹 풀 사이클 데브코스 TIL

김진환·2023년 11월 23일
0
post-thumbnail

'23.11.23(목) 웹 풀 사이클 데브코스 TIL

Node.js로 백엔드 서버 구축하기

백엔드의 구조

백엔드(Backend)는 지난 TIL에서 소개한 것처럼 프론트엔드(Frontend)의 반대 개념으로
사용자에게 보이지 않는 서버측(Server-side) 개발 분야를 의미한다.

백엔드는 크게 웹서버, 웹 어플리케이션 서버, 데이터 베이스로 구성되고 Client 컴퓨터와 함께 통신하는 관계로 이루어진다.

  • 웹 서버(Web Server)
    웹 서버는 화면 내 내용이나 데이터의 변동이 없는 정적 페이지에 대응하는 역할을 한다.

  • 웹 어플리케이션 서버(WAS)
    웹 어플리케이션 서버는 데이터 처리/연산을 통해 화면의 내용이나 데이터가 변하는 동적 페이지를 처리한다.

  • 데이터베이스(DB)
    데이터베이스는 웹에서 사용될 데이터의 저장공간으로, 내일 학습에서 자세하게 다룰 예정이다.

Web Server와 WAS를 왜 분리하는가?

Web Server와 WAS를 하나로 합쳐서 정적인 페이지를 보여줌과 동시에 동적인 처리를 수행하면 안되는가?
-> 가능하다.

그럼 왜 개념적으로 이를 분리할까?

  • 기능 분리를 통해 서버의 부하를 줄인다.
    -> 다양한 요청을 처리하기 위해 WAS에는 이미 부하가 큰 상태이기 때문에, Web Server를 분리해 빠르게 정적인 콘텐츠를 제공할 수 있도록 한다.

  • 물리적으로 분리하여 보안을 강화한다.

  • 여러 대의 WAS를 연결해 로드 밸런싱을 이용할 수 있다.
    -> 오류가 발생하거나 큰 트래픽으로 인한 중단을 방지할 수 있다.

  • 여러 언어의 웹 어플리케이션 서비스가 가능하다.
    -> 하나의 서버에서 다양한 웹 어플리케이션의 활용이 가능해진다.

Node.js

Node.js는 자바스크립트를 스크립트 언어를 넘어 프로그래밍이 가능하도록 지원하는 플랫폼이다.

Node.js로 Web Server와 WAS의 역할을 구현할 수 있고, 만든 WAS를 통해 DB와 통신하는 것도 가능하다.

즉, Node.js를 이용해 자바스크립트로 백엔드를 구현할 수 있다!

이제 Node.js 를 통해 백엔드의 기본적인 구조와 동작을 실습해 보자.

Node.js 에서 사용되는 함수와 개념을 직접 코드를 통해 학습해 보겠다.

웹 서버 만들기

//server.js
let http = require('http');

function onRequest(request, response){
    response.writeHead(200, {'Content-Type' : 'text/html'});
    response.write('Hello Node.js')
    response.end();
}
http.createServer(onRequest).listen(8888);
  • require() - 모듈을 불러오는 함수

  • request - 클라이언트가 서버에게 전달하려는 정보나 메시지를 담는 객체

  • response - 서버에서 클라이언트로 응답 메시지를 전송 시켜주는 객체

  • writeHead() - http response의 header를 설정하기 위한 http 모듈의 함수

  • response.write() - 서버가 브라우저에 response 객체를 전송하는 함수

  • response.end() - 응답을 종료하는 함수

  • http.createServer() - 서버 인스턴스를 생성하는 함수

  • listen() - 서버 생성 후 접속 포트를 할당하는 함수

위 코드를 해석하면
1. node의 http 모듈을 require()함수를 통해 불러온다.
2. onRequest()라는 함수를 만들어 http 요청에 응답한다.
-> 정상적으로 접속된 http 200 인 경우 text/html 타입의 컨텐츠를 보낸다.
-> 'Hello Node.js' 라는 컨텐츠(text/html 타입)를 담아 보낸다.
-> 응답을 종료한다
3. createServer().listen(8888)을 통해 서버를 열어 8888번 포트와 연결하고, 응답을 위해 만든 onRequest함수를 콜백함수로 전달한다.

위 코드를
터미널에 node server.js를 통해 실행하면

성공적으로 접속해 응답으로 보낸 메시지를 확인할 수 있다.

server.js 모듈화하기

다른 코드에서 서버의 동작을 제어할 수 있도록 server.js를 모듈화 해보자.

//server.js
let http = require('http');

function start() {
    function onRequest(request, response){
        // request와 response의 값은 node가 알아서 넣어줌
        // reponse를 가지고
        response.writeHead(200, {'Content-Type' : 'text/html'});
        response.write('Hello Node.js')
        response.end();
    }
    http.createServer(onRequest).listen(8888);
}
exports.openServer = start;
//index.js
let server = require('./server');

server.openServer();

exports.[객체 이름] = [모듈 이름] - 객체에 모듈을 담아 내보내는 함수

server.js 에서 onRequest()기능과 createServer() 기능을 합친start()라는 함수를 openServer이라는 이름에 담아 모듈로 내보낸다.

-> 실습을 진행하면서 관계를 명확히 이해할 수 있도록 함수 이름과 저장할 객체 이름을 다르게 설정하였다.

같은 디렉토리에 위치한 index.js에서 방금 만든 server.js 파일을 import해 openServer 메서드를 실행할 수 있는 구조로 만들었다.

터미널에 node index.js를 입력하면
아까와 같은 페이지 결과가 나오지만,

index.jsserver.js를 분리할 수 있게 되었다.

Router 분리하기

Route는 경로라는 뜻이고, Router는 경로를 잡아주는 길잡이라는 뜻이다.

웹에서 라우팅(Routing)은 요청에 따라 정해진 경로, 즉 요청된 페이지로 안내하는 방법이다.
일반적으로 웹 페이지를 띄워주는 동작 자체를 라우팅이라고 표현하기도 한다.

//server.js
let http = require('http');
let url = require('url')

function start(route) {
    function onRequest(request, response){
        let pathname = url.parse(request.url).pathname;
        route(pathname)
      
        response.writeHead(200, {'Content-Type' : 'text/html'});
        response.write('Hello Node.js')
        response.end();
    }
    http.createServer(onRequest).listen(8888);
}

exports.openServer = start;
//router.js
function route(pathname) {
    console.log('pathname : ' + pathname);
}

exports.sendPathname = route;
//index.js
let server = require('./server');
let router = require('./router');

server.openServer(router.sendPathname);

routing 하는 로직을 분리해 router.js에 만들어보자
우선 지금은 보여줄 페이지가 하나 이므로 router에서 URL을 콘솔에 출력할 수 있도록 route()함수를 만들어 테스트 해 보았다.

  1. router.jsroute() 함수를 sendPathname이라는 객체에 담아 export 한다.
  2. index.js에서 openServer 객체에 sendPathname을 전달한다.
  3. server.js에서 url.parse(request.url).pathname를 통해 url을 받아올 수 있게 설정한다.

node index.js를 입력한 뒤 주소창 URL 경로에 routeTest를 입력했을 때

입력한 /routeTest 경로가 콘솔창에 잘 출력됨을 확인할 수 있다.

요청에 따라 다른 페이지를 라우팅하기

이제 실제 상용되는 웹 페이지처럼 경로에 따라 다른 페이지를 라우팅 해보자.

//requestHandler.js
//url을 통해 접속이 되면 handle을 통해 main, login 함수가 호출되므로 response를 각 함수가 받도록 함
function main(response) {
    console.log('main');

    response.writeHead(200, {'Content-Type' : 'text/html'});
    response.write('Main Page')
    response.end();
}

function login(response) {
    console.log('login');

    response.writeHead(200, {'Content-Type' : 'text/html'});
    response.write('Login Page')
    response.end();
}

let handle = {}; // key: value
handle['/'] = main;
handle['/login'] = login;

exports.handle = handle;
//server.js
let http = require('http');
let url = require('url')

function start(route, handle) {
    function onRequest(request, response){
        let pathname = url.parse(request.url).pathname;
        route(pathname, handle, response)
    }
    http.createServer(onRequest).listen(8888);
}

exports.openServer = start;
//router.js
function route(pathname, handle, response) {
    console.log('pathname : ' + pathname);

    if (typeof handle[pathname] == 'function') {
        handle[pathname](response);
      } else {
        response.writeHead(404, {'Content-Type' : 'text/html'});
        response.write('<h1>404 Not found</h1>')
        response.end();
      }
}

exports.sendPathname = route;
//index.js
let server = require('./server');
let router = require('./router');
let requestHandler = require('./requestHandler')

server.openServer(router.sendPathname, requestHandler.handle);

mainlogin이라는 페이지 두 가지를 만들어 보자.

  1. 기존에 server.jsonRequest() 함수 내에 있던 route 로직을
    requestHandler.js라는 파일의 각 경로 별 라우팅 함수로 옮김
  2. reuquestHandler.jshandle이라는 딕셔너리에 경로가 키로 입력되면 각 라우팅 함수가 반환되도록 설정
  3. router.js에서 HTTP status가 404인 경우 Not found 메시지를 응답함.

    '/' 경로를 입력하면 메인 페이지가 라우팅된다.

    '/login' 경로를 입력하면 로그인 페이지가 라우팅된다.

    handle 딕셔너리에 존재하지 않는(라우팅 목록에 없는) 경로를 입력한 경우 404 Not Found 페이지가 라우팅된다.

이제 비로소 요청에 따라 다른 웹 페이지를 띄워주는 백엔드의 기초적인 모습을 만들 수 있게 되었다.

profile
개발자라는 틀에 얽매이지 않는 성장

0개의 댓글