코딩공부/T.I.L

2022-02-10 Node.js Express

지구야 사랑해 2022. 2. 10. 09:59

Express.js 소개

 

MERN stack은 JavaScript 생태계에서 인기 있는 프레임워크인 MongoDB. Express, React, Node를 지칭하는 말임.

 

이 중에서 Express.js는 Node.js 환경에서 웹 서버, 또는 API 서버를 제작하기 위해 사용되는 인기 있는 프레임워크.

 

프레임워크는 어떠한 목적을 달성하기 위해 복잡하게 얽혀있는 문제를 해결하기 위한 구조며,
소프트웨어 개발에 있어 하나의 뼈대 역할을 한다. 라이브러리의 상위호환   by 갓무위키

 

- 이번에는 http 모듈로 작성했던 서버를, 프레임워크 Express를 이용하는 방식으로 리팩토링하자.

 

Express로 구현한 서버가 http 모듈로 작성한 서버와 다른점은

 

1. 미들웨어 추가가 편리하다. ( 이 미들웨어는 밑에서 설명드림!)

 

2. 자체 라우터를 제공한다.

 

 

 

Gettring started

스스로 프로젝트를 만들어 보고 Mini Node Server를 Express로 리팩토링하는 챕터입니다.

공식 문서를 따라 Express로 간단한 웹 서버를 만들 수 있습니다.

공식 문서를 따라 Mini Node Server를 Express를 이용하는 방식으로 리팩토링하세요.

1. Express 설치

공식 문서의 시작하기 -> 설치 를 참고하세요.

2. 간단한 웹 서버 만들기

공식 문서의 시작하기 -> Hello world 예제 를 참고하세요.

3. 라우팅: 메서드와 URL에 따라 분기(Routing)하기

메서드와 URL(/lower, /upper 등)로 분기점을 만드는 것을 라우팅(Routing)이라고 합니다.

클라이언트는 특정한 HTTP 요청 메서드(GET, POST 등)나 서버의 특정 URI(또는 경로)로 HTTP 요청을 보냅니다. 라우팅은 클라이언트의 요청에 해당하는 메서드와 Endpoint에 따라 서버가 응답하는 방법을 결정하는 것입니다.

추가적인 라이브러리를 사용하지 않고, 순수한 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 ...
      })
    }
  }
}
[코드] 순수한 node.js 코드로 라우팅을 구현합니다.

반면에 Express는 프레임워크 자체에서 라우터 기능을 제공합니다. Express의 라우터를 활용하면 아래와 같이 직관적인 코드를 작성할 수 있습니다.

const router = express.Router()

router.get('/lower', (req, res) =>{
  res.send(data)
})

router.post('/lower', (req, res) =>{
  // do something
})
[코드] Express의 라우터로 구현한 Mini Node Server

이 콘텐츠를 다 읽고 연습했다면, Mini Node Server를 리팩토링할 수 있습니다.

 

 

 

Exprees.js 에서 내가 자주 사용할 수 있어야 하는것 정리

참조 : 공식 문서

 

  • 기본라우팅
  • 라우팅

라우팅 분기 나누기 : https://velog.io/@dongchyeon/Node.js-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0

 

 

  • 미들웨어 작성

cors 관련 : https://falsy.me/nodejs-express-%ED%86%B5%EC%8B%A0-cors-cors-pre-flight-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0/

 

 

 

  • 미들웨어 사용

express.json() : https://blog.naver.com/PostView.nhn?blogId=dlaxodud2388&logNo=221708863522

  • 오류 처리

 

 

미들웨어

자동차 공장에서는 컨베이어 벨트 위에 올려진 자동차의 뼈대에 ,  각 공정마다 부품을 추가함.

 

모든 부품이 추가되면 완성된 자동차가, 반대로 어딘가에 문제가 있다면 불량품이 결과물로 나오게 됨.

 

미들웨어는 자동차 공장의 공정과 비슷함.

 

컨베이어 벨트 위에 올라가 있는 request에 필요한 기능을 더하거나, 문제가 발견된 불량품을 밖으로 걷어내는 역할을 함.

 

Express는 자체적인 최소한의 기능을 갖춘 라우팅 및 미들웨어 웹 프레임워크이며,
Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출입니다.

미들웨어는 결국 함수란다...

 

이 미들웨어는 express의 가장 큰 장점. 

 

 

 

미들웨어를 자주 사용할 상황은?

 

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

 

2. POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)

 

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

 

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

 

미들웨어를 이용하면 node.js 만으로 구현한 서버에서는 다소 번거로울 수 있는 작업을 보다 쉽게 적용할 수 있음.

 

1번과 4번은 직접 만들어 볼 예정이고, Express로 구현한 서버에서 흔하게 사용하는 미들웨어인 2번과 3번의 경우를 살펴보겠음.

 

 

case 2:  POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)

 

순수 node.js로 HTTP body(payload)를 받을 떄에는 Buffer를 조합해서 다소 복잡한 방식으로 body를 얻을 수 있다.

 

let body = [];
request.on('data', (chunk) => {
  body.push(chunk);
}).on('end', () => {
  body = Buffer.concat(body).toString();
  // body 변수에는 문자열 형태로 payload가 담겨져 있습니다.
});

- 네트워크 상의 chunk를 합치고, buffer를 body로 변환하는 작업이 필요

 

body-parser 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있음.

body-parser에서 다룰 수 있는 바디 형식은 json, 기본 텍스트(text), buffer 등이 있음.

 

const jsonParser = express.json()

// 생략
app.post('/api/users', jsonParser, function (req, res) {
  // req.body에는 JSON의 형태로 payload가 담겨져 있습니다.
})
[코드] bodyParser를 이용해 요청받은 데이터를 json 형태로 변환합니다.

 

case 3: 모든 요청 / 응답에 CORS 헤더를 붙일 때

 

순수 node.js 코드에 CORS 헤더를 붙이려면, 응답 객체의 writeHead 메서드 이용함. 이런 메서드를 이용하더라도

Access-Control-Allow- 헤더를 매번 재정의 해야함. 그뿐만 아니라, OPTIONS 메서드에 대한 라우팅도 따로 구현해야함.

 

 

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
};

// 생략
if (req.method === 'OPTIONS') {
  res.writeHead(201, defaultCorsHeader);
  res.end()
}

 

 

- 허나 cors 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있음.

 

const cors = require('cors')

// 생략
app.use(cors()) // 모든 요청에 대해 CORS 허용

const cors = require('cors')

// 생략
// 특정 요청에 대해 CORS 허용
app.get('/products/:id', cors(), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for a Single Route'})
})

 

 

미들웨어 작동 원리 이해하기!

 

- case 1과 4에 해당하는 미들웨어를 직접 구현하면서 작동원리를 이해하자.

이어지는 내용에서는 작동원리를 간단하게 설명하고 있는데 더 자세한사항은 공식 문서에서 확인할 수 있음

 

- case 1: 모든 요청에 대해 url이나 메서드를 확인할 때

 

미들웨어는 말 그대로 프로세스 중간에 관여하여 특정 역할을 수행.

 

수많은 미들웨어가 있지만, 가장 단순한 미들웨어 로거(logger)를 예로 들자.

 

로거는 디버깅이나, 서버 관리에 도움이 되기 위해 console.log 로 적절한 데이터나 정보를 출력함.

 

데이터가 여러 미들웨어를 거치는 동안 응답할 결과를 만들어야 한다면, 미들웨어 사이사이에 로거를 삽입하여 현재 데이터를 확인

 

하거나, 디버깅에 사용할 수 있음. 이런 미들웨어는 일반적으로 다음과 같은 구성을 가지는데

 

[ 그림 ] 공식 문서에서 확인할 수 있는 미들웨어의 구성

위 그림은 endpoint가 / 이면서, 클라이언트로부터 GET 요청을 받았을 때 적용되는 미들웨어임.

 

파라미터의 순서에 유의해야함.

 

req, res는 우리가 잘 아는 요청 / 응답이고, next는 다음 미들웨어를 실행함. 맨 위의 그림에서 next()가 있을거임.

 

만약 특정 endpoint가 아니라 모든 요청에 동일한 미들웨어를 적용하려면 어떻게 해야 할까?

 

이떄에는 메서드 app.use를 사용함. 아래 코드를 직접 실행하면 모든 요청에 대해 LOGGED가 출력되는 걸 확인할 수 있음

 

const express = require('express');
const app = express();

const myLogger = function (req, res, next) {
  console.log('LOGGED'); // 이 부분을 req, res 객체를 이용해 고치면, 여러분들은 모든 요청에 대한 로그를 찍을 수 있습니다.
  next();
};

app.use(myLogger);

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000);

목표는 다음과 같이 모든 요청에 대해 메서드와 url을 출력하는 것. 직접 구현해 보자.

 

 

[ 그림 ] 모든 요청에 대해 메서드와 url을 출력하는 예시

 

- case 4: 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때(advanced challenges)

 

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

 

토큰(Token): 주로 사용자 인증에 사용하며, Authentication을 학습할 때 다룹니다.

app.use((req, res, next) => {
  // 토큰 있니? 없으면 받아줄 수 없어!
  if(req.headers.token){
    req.isLoggedIn = true;
    next()
  } else {
    res.status(400).send('invalid user')
  }
})

// 토큰을 통해 로그인 여부를 확인하는 미들웨어 예시

로그인 없이 웹사이트에 접근을 시도했을 때, 로그인 창 등으로 되돌려 보내는 경우를 경험해 본 적이 있을 거임.

 

서버에서는 요청에 포함된 데이터를 통해 미들웨어가 요구하는 조건에 맞지 않으면, 불량품으로 판단하고 돌려보내도록 구현할 수 있음

 

Gettring Started

 

스스로 프로젝트를 만들어 보고 Mini Node Server를 Express로 리팩토링하는 챕터입니다.

공식 문서를 따라 Express로 간단한 웹 서버를 만들 수 있습니다.

 

공식 문서를 따라 Mini Node Server를 Express를 이용하는 방식으로 리팩토링하세요.

1. Express 설치

공식 문서의 시작하기 -> 설치 를 참고하세요.

 

 

2. 간단한 웹 서버 만들기

공식 문서의 시작하기 -> Hello world 예제 를 참고하세요.

 

 

3. 라우팅: 메서드와 URL에 따라 분기(Routing)하기

메서드와 URL(/lower, /upper 등)로 분기점을 만드는 것을 라우팅(Routing)이라고 합니다.

클라이언트는 특정한 HTTP 요청 메서드(GET, POST 등)나 서버의 특정 URI(또는 경로)로 HTTP 요청을 보냅니다. 라우팅은 클라이언트의 요청에 해당하는 메서드와 Endpoint에 따라 서버가 응답하는 방법을 결정하는 것입니다.

추가적인 라이브러리를 사용하지 않고, 순수한 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 ...
      })
    }
  }
}
 
[코드] 순수한 node.js 코드로 라우팅을 구현합니다.

 

 

- 반면에 Express는 프레임워크 자체에서 라우터 기능을 제공합니다.
Express의 라우터를 활용하면 아래와 같이 직관적인 코드를 작성할 수 있습니다.

const router = express.Router()

router.get('/lower', (req, res) =>{
  res.send(data)
})

router.post('/lower', (req, res) =>{
  // do something
})
[코드] Express의 라우터로 구현한 Mini Node Server

이 콘텐츠를 다 읽고 연습했다면, Mini Node Server를 리팩토링할 수 있습니다.