Web Server[기초]

Ethan KIM·2022년 8월 14일
0

Achievement Goals

목록 보기
2/7

node.js의 역할을 설명하시오.

기본적으로 자바스크립트는 Single Thread의 성질을 가진다.
Single Thread를 알아보기전에, 먼저 Thread의 개념을 알아보자.

Thread란, 프로세스가 할당받은 자원을 이용하는 실행의 단위를 말한다.
더 자세하게 말하면, 한 프로세스 내에서 동작되는 여러 흐름으로 Data, Heap, code 영역을 공유한다.
*(Multi-thread의 경우엔, 이 3영역을 공유한다. 멀티 스레드를 사용하려면 3영역을 동기화 해주어야하고, 컨텍스트 스위칭 방법으로 작동되며 이 방법이 굉장히 빠르게 일어나기 때문에, 동시에 일어나는것 처럼 보임.)

그래서 Single Thread를 살펴보면, (자세한 내용은 따로 블로그에 작성해 놓았으니, 참고해서 보자.)
일단, 하나의 Thread에서 모든게 실행된다. 따라서, context switching이 필요가 없고, 자원접근에서 각 영역의 동기화에서도 자유로워진다.

그럼 이제 node.js로 돌아와서, node.js는 멀티 스레드인가 싱글 스레드인가? 에 대한 질문부터 해결하고 node.js의 역할을 설명하고자한다.

앞서 말했듯, node.js는 기본적으로 Single Thread의 성질의 언어이다. 그렇다면, node.js도 Single Thread 성질이 아닌가 라고 생각할 순 있는데, 결론적으론 node.js는 멀티스레드이다. node.js를 구성하는 libuv가 C++ 언어로 작성됨. 단지, Javascript가 Single Thread이기 때문에 node.js도 하나의 thread안에서 처리할 뿐이다. 그리고 이 node.js의 Single Thread를 event loop 라고 부른다.
사실 이 이벤트 루프가 있기에, node.js가 비동기적으로 일처리를 해서, request에 대한 response가 있더라도, 그 상황이 queue에 들어가기 때문에 여러가지 일을 처리하는것 처럼 작동한다.

node는 이벤트 기반의 플렛폼이기 때문에, 이벤트가 발생할 때마다 미리 지정해둔 작업을 실행하는 방식으로 작동된다.
그래서 정리하자면, Node.js는 JavaScript를 real-time websites 처럼 만들기 위한 페키지 complication이다. non-blocking 과 event-driven I/O paradigm 툴을 제공한 것과 같다.
이 툴이 바로, HTTP 요청을 보내거나, 응답을 받을 수 있는 도구이며, HTTP 요청을 처리하고 응답을 보내주는 프로그램을 웹 서버 라고 부른다.

package.json 의 역할을 설명하시오.

All npm packages contain a file, usually in the project root, called package.json -

this file holds various metadata relevant to the project. This file is used to give information to npm that allows it to identify the project as well as handle the project's dependencies.

It can also contain other metadata such as a project description, the version of the project in a particular distribution, license information, even configuration data - all of which can be vital to both npm and to the end users of the package. The package.json file is normally located at the root directory of a Node.js project.
ref: node.js

HTTP.

  • HTTP 요청/응답을 브라우저를 통해 확인할 수 있고, 해당 내용을 읽을 수 있다.
  • HTTP 다양한 요청 방식과, 응답 코드에 대해 이애할 수 있다.
  • node.js 모듈 사용.

  • node.js의 내장 http 모듈을 사용할 수 있다.
  • 앞서 이야기했듯, node.js 는 HTTP 요청을 보내거나 응답을 받을 수 있는 도구를 제공한다. 우리가 node.js의 fs 모듈을 사용했듯, HTTP 요청과 응답을 다루기 위한 http 모듈을 사용할 수 있다.
    HTTP 모듈 링크.
    위의 링크에 가면 확인할 수 있듯, 수많은 모듈들이 있다. 나중엔 다 알아야겠지만, 지금은 http 모듈을 사용해보자.

    request

    request는 IncommingMessage의 인스턴스이며, ReadableStream이다.

    서버 생성.

    일단, node.js HTTP 처리 과정을 이해해보면, 먼저 서버를 생성하는 방법은 다음과 같다.

    const http = require('http'); // http 모듈을 불러온다.
    const server = http.createServer((request, response) => {
    	// 여기서 작업진행.
    }).listen(port)

    이 서버로 오는 HTTP 요청 마다 createServer에 전달된 함수가 한번씩 호출된다.
    사실 createServer 가 반환한 Server 객체는 EventEmitter 이고 위의 코드는 server 객체를 생성하고 리스터를 추가하는 축약 문법을 사용했다는 점을 알아두자. 이를 풀어서 쓰면 아래의 코드와 같다.
    listen 메서드는 해당 Server 객체에서 호출되고, PORT를 listen(port)에 전달하면된다.

    const server = http.createServer();
    server.on('request', (request, response) => {
    	//여기서 작업진행.
    }).listen(port)

    메서드, URL, 헤더

    요청을 처리할 때, 메서드와 URL을 확인한 후 이와 관련된 적절한 작업을 실행해야한다. Node 가 request 객체에 유용한 프로퍼티를 넣어두었다.

    이게 다른게 아니고 HTTP verb/ URI or URL/ HEADER 이거임.

    여기서 request 객체는, IncommingMessage의 인스턴스 인데 IncommingMessage란 서버로 전송된 HTTP message를 말한다.
    헤더도 request에 headers라는 전용 객체가 있으므로, 설정하는 방법은,

    const { headers } = request;
    const userAgent = headers['user-agent']

    rawHeader를 사용할때에는, 일부 헤더를 반복해서 설정할 때, 헤더에 따라 덮어씌워지거나 콤마로 구분된 문자여롤 합쳐지기 때문에, 이럴때 rawHeader를 사용.


    요청 바디

    POST나 PUT 요청을 받을 때 어플리케이션에 요청 바디는 중요하다. 핸들러에 전달된 request 객체는 ReadableStream 인터페이스를 구현하고 있는데, 이 스트림에 이벤트 리스너를 등록하거나 다른 스트림에 파이프로 연결 할 수 있다. 스트림의 'data'와 'end'이벤트에 이벤트 리스너를 등록해서 데이터를 받을 수 있다.
    각 'data' 이벤트에서 발생시킨 청크는 Buffer인데, 이 청크가 문자열 데이터라는 것을 알고 있다면 이 데이터를 배열에 수집한 다음 'end'이벤트에서 이어붙이고 다음 문자열로 만드는 것이 가장 좋다.
    이게 무슨말이냐면, 코드로 작성하면서 알아보자.

    let body = []; // body 배열을 만들어
    request.on('data', (chunk) => { // data' request에서 발생시킨 청크(Buffer)
      body.push(chunk); // body에 push하여 데이터 수집화.
    }).on('end', () => { // 다음에 end이벤트에서 이어 붙히고
      body = Buffer.concat(body).toString(); // 다음 문자열로 만드는것.
    })

    결국엔, request.on('data', callback).on('end', callback) 이게되는데, request에서 'data'이벤트를 요청받을때 마다 청크(Buffer)가 생성되고, 이거를 body 라는 배열에 넣어주는거야. 그리고 다음 이벤트 on('end', callback) 'end' 이벤트에서 이어 붙인 후 문자열로 만들어 주는 과정임.

    error handling.

    에러 핸들링은 request 스트림의 오류가 발생했을때 스트림에서 'errer'이벤트가 발생하며 오류를 전달하므로, Node.js 프로그램을 종료시킬 수도 있는 오류를 던질 수 있다. 따라서 'error'리스너를 추가해 줌으로서 에러 핸들링이 가능하다.

    지금까지 살펴본 내용을 바탕으로 작성한 request HTTP 모듈에서 요청을 처리한 결과이다. 이제 이 요청에 대한 응답을 서버쪽에서 보내주는 코드를 작성해보자.

    To The Top

    response

    response객체는 ServerResponse의 인스턴스이고 WritableStream이다.

    HTTP 상태 코드를 따로 설정하지 않으면 응답의 HTTP 상태 코드는 항상 200이다. 상태 코드를 변경하려면, response.statusCode = 404; 처럼 statusCode 를 설정해주어야함.

    응답 헤더 설정.

    setHeader 메서드로 헤더를 설정해 주면됨. 이때 헤더를 여러 번 설정한다면 마지막에 설정한 겂을 보냄.

    response.setHeader('Content-Type', 'application/json');
    response.setHeader('X-Powered-By', 'bacon');

    위와 달리 명시적인 헤더 데이터 전송 방법도 있는데,
    writeHead 메서드를 사용하여 명시적 헤더 데이터 전송을 할 수 있다.

    response.writeHead(200, {
      'Content-Type': 'application/json',
      'X-Powered-By': 'bacon'
    });

    이렇게 헤더를 설정하고 나면, 응답 데이터를 전송할 준비가 된 것이다.


    응답 바디 전송

    reponse 객체는 WritableStream이므로 클라이언트로 보내는 응답 바디는 일반적인 스트림 메서드를 사용해서 작성한다. 아래와 같이 작성할 수도 있고,

    response.write('<html>');
    response.write('<body>');
    response.write('<h1>Hello, World!</h1>');
    response.write('</body>');
    response.write('</html>');
    response.end();

    스트림의 end함수에 보낼 데이터의 마지막 비트를 선택적으로 전달할 수 있으므로

    response.end('<html><body><h1>Hello, World!</h1></body></html>');

    이런식으로도 작성 할 수 있다.

    응답 에러핸들링.

    요청과 마찬가지로 'error' 이벤트 처리를 해주면 됨. 'error'리스너를 이용하여. 아래 이미지는 HTTP module 을 request response 처리 해준 결과이다.
    To The Top

  • http 모듈 사용시에 서버에 CORS 설정을 할 수 있다.
  • HTTP 모듈 사용시에 서버에 CORS 설정을 하는 방법에 대해 알아보면, express 미들웨어를 사용하지 않는한, request.method === OPTIONS 라는 조건으로 CORS 를 걸러줄 수 있다.

    예시를 들자면, 아래의 코드처럼 request.method를 걸러서 CORS 요청에 대한 응답을 설정 해 줄 수 있다.

    const server = http.createServer((request, response) => {
      if (request.method === 'OPTIONS') {
        response.writeHead(200, defaultCorsHeader);
        response.end();
    }

    사실 node.js 에서 기본적으로 제공하는 HTTP 모듈을 사용해서 CORS 설정과 HTTP 요청을 처리할 수 있지만, Express 미들웨어를 사용하면 훨신 간편하고, 이해하기 쉽게 HTTP 요청 처리를 가능하게 한다. express 에 대해선, 밑에서 다루니 참고하길 바란다.

  • CommonJS를 이용한 모듈 내보내기/불러오기를 할 수 있다.
  • 자바스크립트 개발 하다보면, CommonJS 키워드를 많이 보게되는데 require, import 키워드로 외부 라이브러리를 불러오는 키워드를 많이 보았을 것이다. 여기서 import는 ES6에서 새롭게 도입된 키워드로서, 두 개 키워드 모두 하나의 파일에서 임의의 파일 코드를 불러오는 동일한 목적을 가짐. 하지만, 문법이 다름.

    const moment = require("moment"); // Ruby 처럼 require을 사용하여 모듈을 불러옴
    import moment from "moment"; // 좀더 명시적으로 모듈을 불러옴.

    두개의 목적은 같으나 문법이 상이한것을 볼 수 있다.
    ES6 의 Import 키워드가 추가되므로, 명시적인 코드 작성이 가능해 졌지만, 항상 Import를 사용할 수 없기 때문에, commonJS 를 필수적으로 사용해야 할 상황이 생긴다.

    CommonJS 방식으로 모듈 내보내기/불러오기를 할떄엔, ES6의 import 처럼 명시적인 코드를 작성하는 것이 아닌, 특정 변수나 그 변수의 속성으로 내보낼 객체를 세팅해 주어야한다.
    exports 변수와 module.exports 변수를 상황에 맞게 잘 사용해야한다는 이야기.

    여러개의 객체를 보낼 경우 exports 변수 속성으로 할당하고
    exports.obj = obj 여러개를 이렇게 보내주면된다.

    단 하나의 객체만 보낼경우 module.exports 변수 자체에 할당한다.
    이경우엔, 여러개의 객체를 새로운 객체에 합쳐서 보낸다. module.exprots = combinedObj

    불러낼 때에는, const Identifier = require(FILE_ROOT)로 불러낼 수 있고, 사용할때에는 Identifier.obj 이런식으로 사용하면됨.

    라우팅과 API

  • 라우팅(조건에 따른 분기)을 이해하고, 이를 서버 코드에서 구현할 수 있다.
  • 클라이언트가 사용할 수 있도록, 서버 API 문서를 직접 작성할 수 있다.
  • Express 라이브러리

    express 라이브러리를 소개하기 전에, MERN 스택 부터 알아보자.
    MERN stack 이란 자바스크립트 생태계에서 인기있는 프레임 워크들이다. 순서대로, MongoDB, Express, React, Node 가 되겠고, Express.js는 Node.js 환경에서, 웹 서버 또는 API 서버를 제작하기 위해 사용되는 인기 있는 프레임 워크임.
    Express 로 구현한 서버가 http 모듈로 작성한 서버와 다른점은
    1. 미들웨어 추가가 편하다는점
    2. 자체 라우터를 제공한다는 점이 되곘다.

  • express 라이브러리가 어떤 작업을 단순하게 만드는지 이해할 수 있다.
  • express 라이브러리의 가장 큰 장점은 자체 라우터를 제공하는점이다. 메소드와 URL(/urlpath)로 분기점을 만드는 것을 라우팅이라고 함. 예시로 설명하면 이해가 빠를것이다.

    순수한 Node.js 코드로 작성한 라우팅.

    const requestHandler = (req, res) => {
      if (req.url === '/lower') {
      	if (req.method === 'GET') {
      	  res.end(data)
      	} else if (req.method === 'POST') {
      	  req.on('data', (req, res) => {
      	  	// do something...
      	  })	
      	}
      }
    }

    다음은 express.js 로 작성한 라우팅.

    const router = express.Router()
    router.get('/lower', (req, res) => {
      res.send(data);  
    })
    router.post('/lower', (rea, res) => {
      // do something.
    })

    이처럼 express.js 로 작성할 경우 명시적이고 직관적이게 되므로, 많은 개발자들이 사용한다.

  • 미들웨어의 개념을 이해할 수 있다.
  • 미들웨어는 자동차 장의 공정과 비슷하다.
    컨베이어 벨트 위에 올라가 있는 request에 필요한 기능을 더하거나, 문제가 발견된 불량품을 밖으로 걷어내는 역할을 한다.

    자주 사용하는 미들웨어엔 다음 4가지가 있다.

    1. 모든 요청에 대해 url이나 메소드를 확인할 때.

    미들웨어는, 말 그대로 프로세스 중간에 관여하여 특정 역할을 수행한다. 미들웨어 로거(logger)은 디버깅이나, 서버 관리에 도움이 되기 위해 console.log로 적절한 데이터나 정보를 출력한다.
    특정 endpoint를 설정 해줄 수 있지만, 모든 요청에 동일한 미들웨어를 적용하려면, app.use를 사용할 수 있다. 이럼 모든 요청에 대해 app.use() 가 실행됨.

    2. POST 요청 등에 포함된 body를 구조화할때.

    순수 node.js로 HTTP body를 받으려면 Buffer를 조합해서 다소 복잡한 방식으로 body 를 얻어야함.

    express.json([options])를 이용하면 간단하게 처리 가능.

    app.use(express.json());
    app.use(express.urlenceded ({extended : false}));
    //생략
    app.post('/api/users', (req, res) => {
    	req.body 는 JSON의 형태로 payload가 담겨짐.  
    })

    3. 모든 요청/응답에 CORS 헤더를 붙여야 할 때.

    cors 미들웨어의 사용으로

    const cors = require('cors');
    app.use(cors())

    이런식으로 작성. 모든 요청에 대해 CORS 를 허용한다.

    4. 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때.

    HTTP 요청에서 토큰이 있는지 여부를 판단하여, 이미 로그인한 사용자일 경우 성공, 아닐 경우 에러를 보내는 미들웨어.
    token 미들웨어임.

    app.use((req, res, next) => {
      if (req.header.token) {
        req.isLoggedIn = true;
        next()
      } else {
        res.status(400).send('invaild user')
      }
    })

    로그인 없이 웹사이트 접근 시도했을때 로그인 창 으로 되돌려 보내는 경우에 쓰임.

    서버 개발과 디버깅.

  • CRUD를 수행하는 웹 서버 개발 방법을 익힐 수 있다.
  • 서버 개발을 돕는 다양한 툴들을 익힐 수 있다.
  • nodemon 을 사용하도록 하자.

      npm -g install nodemon.

    nodemon 은 postman 과 함께 사용하면 효율이 올라간다.
    사용 방법은, package.json 디버깅란에, 'start'나 뭐 다른 어떤 Npm 구문중 node 가 되어있는것을 nodemon 으로 바꿔준다.
    nodemon은 fetch가 될때, 실시간으로 작동해주기 때문에, 많이 유용하다.

    nodemon을 사용하지 않고 node에 --inpect 옵션을 넣어도 디버깅이 가능하다.
    inpect option을 사용하면 크롬 개발자 도구에 node.js 아이콘이 생성되고, 포스트맨으로 요청으 했을시, 좀더 알아 볼 수 있게 나온다. method header 등등 Incomming Message 의 세세한 속성들 까지도 볼 수 있음.

    node --inspect-brk 를 하면, 시작하자 마자 break 포인트가 걸려있음. debugger을 사용하는것과 동일.

    VS 코드에서도 디버그를 사용할 수 있음.

    profile
    좋아하는것만 함

    0개의 댓글