Day 08 - Node.js 개념/설치/웹서버 구현, npm/yarn, 실전 프로젝트 01 1/2

이유승·2024년 11월 7일
0

* 프로그래머스, 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 5기 강의 수강 내용을 정리하는 포스팅.

* 원활한 내용 이해를 위해 수업에서 제시된 자료 이외에, 개인적으로 조사한 자료 등을 덧붙이고 있음.

1. Node.js

  • 이전 포스팅의 Javascript의 역사에서 언급했던 내용인데..

  • JavaScript 런타임 환경. 브라우저 외부에서도 JavaScript를 실행할 수 있게 만들어주는 플랫폼.

  • 웹 브라우저, 프론트엔드 개발에만 사용되던 Javascript를 브라우저 바깥에서 사용하도록 만든 만큼, 주로 서버 사이드 개발에 사용된다.



Node.js의 특징

  • 크롬 브라우저에서 JavaScript를 처리하는 구글의 V8 JavaScript 엔진을 사용하여 JavaScript 코드를 빠르게 실행. 이를 활용해 JavaScript를 브라우저가 아닌 서버에서도 실행할 수 있도록 구현되어있다.

  • 비동기(non-blocking) / 단일 스레드로 작동. 특정 작업을 수행하는 동안 다른 작업이 동시에 처리될 수 있고, 이벤트 루프 등을 통해 많은 요청의 효율적인 처리를 수행한다.



npm (Node Package Manager)

  • Node.js의 패키지 관리 시스템. JavaScript 라이브러리와 패키지를 설치하고 관리할 수 있게 해준다.

  • 수많은 오픈소스 라이브러리와 패키지가 있어, 다양한 기능을 손쉽게 구현할 수 있다.



yarn

npm의 문제점

  • npm은 초기에 패키지를 직렬로 설치했기 때문에, 많은 패키지를 설치할 때 시간이 오래 걸렸다.
  • 설치할 때마다 의존성 트리에 변화가 생길 수 있어, 같은 프로젝트라도 다른 개발자 환경에서 서로 다른 패키지 버전이 설치될 위험도 있었고 외부 스크립트를 실행할 수 있는 기능이 있어 보안 문제를 일으킬 수도 있었다.
  • 더구나 캐싱 기능이 없어서 매 설치마다 네트워크 연결을 필요로 했다.
  • node_modules 구조가 복잡해져 의존성 트리에서 충돌이나 중복된 설치가 발생한다는 문제점까지.

yarn의 개선점

  • yarn은 병렬 설치 방식을 도입하여 여러 패키지를 동시에 설치할 수 있게 했고, 패키지를 설치한 후 캐싱하여 다음 설치 시 더 빠르게 진행할 수 있도록 개선했다.
  • 또한 yarn.lock 파일을 통해 프로젝트에 설치된 패키지와 정확한 버전을 기록하여 설치 시 일관성을 보장하여 모든 개발자가 동일한 의존성을 사용할 수 있도록 지원했다.
  • 보안을 강화하여 설치된 패키지들이 캐시에서 직접 설치될 수 있도록 하고,
  • 네트워크 요청이 아닌 캐시에서 파일을 직접 읽어오므로 보안성과 안정성을 높였다.
  • 의존성 트리를 평평하게 유지하여 중복 설치를 줄이고, 디스크 공간을 효율적으로 사용할 수 있도록 최적화했습니다.
  • 또한 Yarn 2부터는 node_modules 폴더 없이도 패키지를 관리할 수 있는 PnP(Plug’n’Play) 방식을 도입하여 성능을 더욱 향상시키기도 했다.

다만..

  • npm도 많은 개선을 거쳐 초기 문제들이 상당히 해결된 상태.

  • 5 버전 이후 성능과 의존성 관리에서 개선이 이루어졌고, 특히 최근의 npm 7과 8, 9 버전에서는 Yarn에 비해 큰 차이가 없을 정도로 발전했다.

  • 다만, 특정 기능(예: PnP, 오프라인 캐시, 모노레포 관리 등)에 대해 Yarn이 여전히 선호되는 경우가 있다. 따라서 npm과 yarn의 특징을 잘 살펴보고 어느 것을 선택할지 고민해봐야 한다.

  • 추가로 커뮤니티와 생태계 규모 측면에서는 npm쪽이 더 거대하다.



Node.js의 설치



Node.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);



let http = require('http');

  • http 모듈을 가져온다. 이 모듈은 Node.js에서 HTTP 서버를 생성하고 요청을 처리하기 위해 제공되는 기본 모듈이다.

function onRequest(request, response) {

  • onRequest라는 함수는 HTTP 요청이 들어올 때마다 호출되는 핸들러 함수.
  • request: 클라이언트로부터 들어온 HTTP 요청에 대한 정보를 담고 있는 객체.
  • response: 서버가 클라이언트에 응답을 보낼 때 사용하는 객체.

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

  • 응답 헤더를 설정합니다. 여기서 200은 상태 코드(성공)를 나타내며, Content-Type 헤더는 응답의 내용이 HTML 형식임을 지정한다.

  • 응답 코드? 상세 내용은 이전 포스팅 참조.

response.write('Hello Node.js');

  • 응답 본문에 Hello Node.js라는 텍스트를 작성한다.

response.end();

  • 응답을 종료. 이 명령을 호출해야 클라이언트가 응답을 받을 수 있다.

http.createServer(onRequest).listen(8888);

  • http 모듈의 createServer 메서드를 사용하여 HTTP 서버를 생성하고, 요청이 들어올 때마다 onRequest 함수를 실행한다.
  • 그리고 서버를 포트 8888에서 대기하도록 설정한다. 이에 따라 서버가 시작되면 localhost:8888에서 접근할 수 있게 된다.



Node.js로 간단 웹서버 만들기 - 추가

  • 서버에 어떤 URL 요청이 들어오면, 이를 구분하여 적절한 응답을 보내서 화면에 텍스트를 출력하기.

index.js

const { startServer } = require('./server');
const router = require('./routes/routes');
const { handle } = require('./requestHandler/requestHandler'); 

startServer(3000, router.route, handle);

server.js

const http = require('http');
const url = require('url');

function startServer(port = 3000, route, handle) {
  const server = http.createServer((req, res) => {

    // 요청 URL을 파싱하여 경로 추출
    let pathname = url.parse(req.url).pathname;

    // /favicon.ico 요청 무시
    if (pathname === '/favicon.ico') {
        res.writeHead(204); // No Content 응답 코드
        res.end();
        return;
      }

    route(pathname, handle, req, res); // 경로와 핸들러, 요청 및 응답 객체 전달
  });

  server.listen(port, () => {
    console.log(`웹 서버가 해당 주소에서 동작 중입니다. :: http://localhost:${port}`);
  });
}

module.exports = { startServer };

routes.js

function route(pathname, handle, req, res) {
    // 한글 URL도 정상 출력 하도록 디코딩
    const decodedPathname = decodeURIComponent(pathname);

    if (typeof handle[decodedPathname] == 'function') {
        handle[decodedPathname](req, res, decodedPathname);
    } else {
        res.writeHead(404, { 'Content-Type': 'text/plain; charset=UTF-8' });
        res.write('404 Not Found, 찾으시는 페이지는 존재하지 않는 페이지입니다.');
        res.end();

        console.log(`No handler found for ${decodedPathname}`);
    }
}

module.exports = { route };

requestHandler.js

function main(req, res) {
    console.log('main');
    
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=UTF-8' });
    res.write('Main 페이지');
    res.end();
}

function name(req, res, decodedPathname) {
    console.log('Requested pathname: ' + decodedPathname); // 요청된 경로를 콘솔에 출력

    res.writeHead(200, { 'Content-Type': 'text/plain; charset=UTF-8' });
    res.write(decodedPathname);
    res.end();
}

// 라우팅을 위한 매핑 객체
// URL 경로를 키(key)로, 각 경로에서 수행할 **핸들러 함수(값, value)**를 연결
let handle = {};
handle['/'] = main;
handle['/이유승'] = name;

module.exports = { handle };

파일 구성 및 역할

index.js - 서버 초기화 파일

  • 역할:
    - 서버를 시작하고 필요한 모듈을 불러옵니다. server.js의 startServer 함수에 포트 번호, route 함수, handle 객체를 인수로 전달하여 서버를 초기화합니다.

  • 작업 흐름:
    - index.js는 서버를 실행할 때 가장 먼저 호출되며, server.js의 startServer 함수로 제어를 넘깁니다.

server.js - 서버 구성 파일

  • 역할:
    - HTTP 서버를 생성하고 클라이언트 요청을 처리합니다. route 함수와 handle 객체를 사용하여 요청 URL을 기반으로 적절한 라우팅을 수행합니다.

  • 작업 흐름:
    - 클라이언트 요청이 들어오면 URL 경로를 추출하고, 해당 경로를 route 함수에 전달하여 콘솔에 경로를 출력하게 합니다. 이후 handle 객체를 사용하여 경로에 맞는 핸들러 함수를 호출해 요청을 처리합니다.

routes.js - 라우팅 로직 파일

  • 역할:
    - 서버가 요청된 URL 경로를 콘솔에 출력합니다. URL이 한글일 경우 인코딩된 URL을 디코딩하여 콘솔에 출력할 수 있도록 처리합니다.

  • 작업 흐름:
    - server.js에서 전달된 URL 경로를 decodedPathname으로 디코딩하고, handle 객체에 해당 경로가 있는지 확인합니다. 일치하는 핸들러가 없을 경우 콘솔에 "No handler found" 메시지를 출력합니다.

requestHandler.js - 요청 처리 로직 파일

  • 역할:
    - 각 URL 경로에 맞는 핸들러 함수들을 정의하고, handle 객체에 경로별로 매핑합니다. 요청된 경로에 맞는 함수를 통해 클라이언트에 응답을 보냅니다.

  • 작업 흐름:
    - handle 객체는 server.js에서 사용되어, 요청된 URL에 따라 매핑된 핸들러 함수가 실행됩니다. 각 핸들러 함수는 요청 URL을 출력하고, 필요한 경우 특정 HTML 페이지를 응답으로 전송하거나 데이터베이스 작업을 수행합니다.

구현 결과



실전 프로젝트 01 진행 1/2.

  • 테니스 라켓 쇼핑몰 만들어보기.
    - 메인 페이지와 주문 목록 페이지로 이루어진 초간단 실습 프로젝트.

  • 효율적인 프로젝트 관리를 위해, 관련 파일은 Git - Github로 관리하도록 진행하였음.

  • 메인 페이지 및 주문 목록 페이지 UI.

  • 이외에도 금일 구현한 Node.js 기반 백엔드 파트도 동일한 Github 저장소에서 관리함.

profile
프론트엔드 개발자를 준비하고 있습니다.

0개의 댓글