Web Server 기초 Refactoring & Express

1Hoit·2023년 2월 7일
0

React 기초

목록 보기
11/12

지난 시간에 이어..

지난 포스팅에서 Node Server를 만들어 보았다.
이번 포스팅에서는 지난 시간에 만든 것을
프레임워크인 express를 사용해서 리팩토링 해보려고 한다.

Express

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

Node.js HTTP 모듈로 작성한 서버와 다른 점은 다음과 같다.

1. 미들웨어를 추가할 수 있다.
2. 라우터를 제공한다.

Express 시작하기

  1. npm install express 설치
  2. 간단한 서버 만들어 보기
//nodeJS환경에서는 commonJS를 사용 
//import가 아니라 require임
const express = require('express') 
const app = express()
const port = 3000

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

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

앱은 서버를 시작하며 3000번 포트에서 연결을 청취합니다. 앱은 루트 URL(/) 또는 라우트에 대한 요청에 “Hello World!”로 응답합니다. 다른 모든 경로에 대해서는 404 Not Found로 응답합니다.

  1. 라우팅 설정
    라우팅(Routing) : 메서드와 url(/lower, /upper 등)로 분기점을 만드는 것
  • 클라이언트는 특정한 HTTP 요청 메서드(GET, POST 등)와 함께 서버의 특정 URI(또는 경로)로 HTTP 요청을 보낸다.

  • 라우팅은 클라이언트의 요청에 해당하는 Endpoint에 따라 서버가 응답하는 방법을 결정하는 것이다.

  • 구조 : app.METHOD(PATH, HANDLER)

    • app은 express의 인스턴스.
    • METHOD는 HTTP 요청 메소드.
    • PATH는 서버에서의 경로.
    • HANDLER는 라우트가 일치할 때 실행되는 함수.
//라우팅 설정
const router = express.Router()

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

router.post('/lower', (req, res) => {
  // do something
})

참고 : express 간단한 서버 만들기 예제
참고 : Express 라우팅 기본


미들웨어

Express는 미들웨어를 추가 할 수 있다고 했다.
미들웨어는 Express의 핵심 이라고 할 수 있다.

미들웨어란 무엇인가?

요청과 응답의 중간(middle, 미들)에 위치하여
요청과 응답을 조작하여 기능을 추가하기도 하고, 나쁜 요청을 걸러내기도 한다.
즉, 프로세스 중간에 관여하여 특정 역할을 수행한다.

Express에서 미들웨어 사용

익스프레스 내에서 웹 요청과 응답에 대한 정보를 사용해서 필요한 처리를 진행할 수 있도록 분리된 독립적인 함수이다.

그리고 각각의 미들웨어는 next() 메소드를 호출해서 그 다음 미들웨어가 작업을 처리할 수 있도록 순서를 넘길 수 있다.

위 사진에서는 function(req, res, next) {} 부분이 바로 미들웨어 인 것이다.
이 경우, 위의 함수가 익스프레스 전용 미들웨어 함수이며
이 함수는 next()를 통해 작동한다.

간단한 미들웨어 사용 예제

위의 사진과 글만 보면 이해하기 어려울 수 있다.
아래 예제 코드를 보며 미들웨어가 무엇인지, 흐름을 파악해보자.

var express = require('express');
 
var app = express();
 
// app.use가 미들웨어가 아니라 app.use()의 인자안의 함수가 미들웨어
app.use(function (req, res, next) {
  req.requestTime = Date.now(); // req라는객체에 requestTime 키와 밸류를 래퍼로 등록. requestTime는 사용자가 정한 값이다.
  next(); // 다음 미들웨어 함수를 작동
});
 
// app.get이 미들웨어가 아니라 app.get()의 인자안의 함수가 미들웨어
app.get('/', function (req, res) { // 위에서 next()가 호출되면 이 콜백함수가 작동
  res.send(req.requestTime); // 위 미들웨어에서 requestTime 객체를 등록했고 next()를 사용했기 때문에 호출해서 데이터 사용 가능
});
 
// 서버 실행
app.listen(3000);

app.use() 참고

  • app.use() 는 Express 앱에서 항상 실행하는 미들웨어 역할
  • app.get(), app.post()등과 달리 요청 URL을 지정하지 않아도 app.use()를 사용할 수 있으며 해당 경우에는 URL에 상관없이 매번 실행된다.
    (공통 미들웨어를 만들어 처리를 하기 위해 사용된다는 말이다.)
  • app.use() 및 app.Method() 함수를 이용해 응용프로그램 수준의 미들웨어를 app객체의 인스턴스에 바인딩Method = get or post

미들웨어를 사용하는 상황의 경우
여러 경우가 있지만 아래를 살펴보자.

  1. POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)
  2. 모든 요청/응답에 CORS 헤더를 붙여야 할 때
  3. 모든 요청에 대해 url이나 메서드를 확인할 때
  4. 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때

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

이전 포스트에서 Node.js로 HTTP body(payload)를 받을 때에는 Buffer를 조합해서 다소 복잡한 방식으로 body를 얻었다.
네트워크 상의 chunk를 합치고, buffer를 문자열로 변환하는 작업이 필요했었다.

하지만
1. body-parser 미들웨어를 사용하거나

// npm install body-parser 설치가 필요
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
// 생략
app.post('/users', jsonParser, function (req, res) {
})
  1. Express v4.16.0 의 Express 내장 미들웨어인 express.json()을 사용하면 쉽게 구조화 할 수 있다.
const jsonParser = express.json();
// 생략
app.post('/api/users', jsonParser, function (req, res) {
})

만약 express.json() 미들웨어 사용에 에러가 난다면? express.json([options])의 options에 해답이 있다.

Object 형태가 아닌 String 형태도 받을 수 있게 하려면 아래처럼 처리를 해줘야 한다.
왜냐하면 express.json 속성 중 strict의 디폴트 값은 true로 되어있어 Object만 허용되어 있는 상태이기 때문이다.

const jsonParser = express.json({strict:false})

추가적으로 res.json 과 res.send([body])의 차이
둘의 공통점은 클라이언트의 request에 대한 데이터를 포함해서 response를 보내는 기능이다.
단, send는 json과 다르게 보낼 수 있는 종류의 제한이 적다.

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

Node.js HTTP 모듈을 이용한 코드에 CORS 헤더를 붙일 때 응답 객체의 writeHead 메서드를 이용 했었다.
Node.js에서는 이 메서드 등을 이용하여 라우팅마다 헤더를 매번 넣어주어야 하며 OPTIONS 메서드에 대한 라우팅도 따로 구현해야 했었다.

하지만 cors 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.

//npm install cors 설치 필요
const cors = require("cors");
const app = express();

//모든 도메인
app.use(cors());

//특정 도메인
const options = {
  origin: "https://corsxxx.com", // 접근 권한을 부여하는 도메인
  credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
  optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};

app.use(cors(options));

//특정 요청
app.get("/example/:id", cors(), function (req, res, next) {
  res.json({ msg: "example" });
});

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

위에서 살펴 보았듯 특정 enpoint가 아니라, 모든 요청에 동일한 미들웨어를 적용하려면 어떻게 해야 app.use 를 사용하면 된다.

간단하게 단순한 미들웨어 로거(logger)를 예로 들어보자.

아래 코드는 모든 요청에 대해 메서드와 url을 출력하기 위한 미들웨어를 작성한 코드이다.

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

const myLogger = function (req, res, next) {
  console.log(`http request method is ${req.method}, url is ${req.url}`); // 이 부분을 req, res 객체를 이용해 고치면, 모든 요청에 대한 로그를 찍을 수 있다.
  next();
};

app.use(myLogger);

app.get('/', function (req, res) {
  res.send('Hello World!');
});
app.get('/favicon.ico', function (req, res) {
  res.send('GET favicon.ico');
});
app.get('/upper', function (req, res) {
  res.send('GET upper');
});
app.options('/upper', function(req, res){
  res.send('OPTIONS upper');
})
app.post('/upper', function(req, res){
  res.send('POST upper');
})


app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

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

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

app.use((req, res, next) => {
  // 토큰이 있는지 확인, 없으면 받아줄 수 없음.
  if(req.headers.token){
    req.isLoggedIn = true;
    next();
  } else {
    res.status(400).send('invalid user')
  }
})

이전 포스팅의 Node.js HTTP 모듈을 이용한 미니서버를 리팩토링 해보자

시작전
npm install express express 설치
npm install cors 설치

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

const app = express(); //express 사용
app.use(cors()); // 모든 요청/응답에 CORS 처리
app.use(express.json({ strict: false })); // 자동 바디 파싱

const PORT = 4999;
const ip = 'localhost';

app.post('/upper', function (req, res) {
  let result = req.body;
  result = result.toUpperCase();
  res.json(result); // 다시 데이터를 보내줄 때 json화 해서 보내주는 메소드
});
app.post('/lower', function (req, res) {
  let result = req.body;
  result = result.toLowerCase();
  res.json(result);
});

app.use(function (req, res, next) {
  // 400에러 즉, 클라이언트의 잘못으로 인한 에러는 인자에 err를 넣지 않는다.
  res.status(404).send('404 Error');
});

app.use(function (err, req, res, next) {
  //에러 처리
  console.error(err.stack);
  res.status(500).send('500 Error');
});

app.listen(PORT, ip, () => {
  console.log(`http server listen on ${ip}:${PORT}`);
});
profile
프론트엔드 개발자를 꿈꾸는 원호잇!

0개의 댓글