생활코딩 express 강의 1강~22강을 듣고 정리한 내용입니다. 

https://youtu.be/hwknmhLKgYg

<목차>

1. Middleware(미들웨어) + Third-party middleware(제3자가 만든 미들웨어)

2. Routing(라우팅)

3. Error Handling(에러 처리하기)

4. Static contents(정적 컨텐츠 제공하기)

 

 

1. Middleware

1) 미들웨어란?

다른 사람(express 공식 개발팀 또는 제 3자)이 개발한 모듈 코드를 외부에서 가져와서 사용하는 것. 
미들웨어는 코드의 재사용성을 높여주고 코드를 간결하게 짤 수 있도록 도와준다. 

 

2) 미들웨어의 원리

const 변수 = require('미들웨어 이름');

app.use(미들웨어());

 

1. 미들웨어() 생성자를 통해서 미들웨어가 리턴된다. 
2. 리턴된 미들웨어는 app.use()를 통해서 앱에 장착된다. (= 앱에서 사용할 수 있다.)

 

(예시): helmet(웹 보안 문제의 취약점을 막아주는 미들웨어)

$ npm install helmet --save
const helmet = require('helmet');
app.use(helmet());

 

3) 미들웨어의 세분화

1) url

app.use('미들웨어 실행시킬 url', function); 

: 맨 왼쪽에는 해당 미들웨어를 실행시킬 url을 명시할 수 있다. 

app.use('/user', helmet());

: helmet() 미들웨어는 경로가 '/user'로 시작하는 경우에만 실행된다. 

('/user' 뿐만 아니라 '/user/password' 등의 '/user'을 포함한 하위 url에도 적용된다.)

2) http method

 

모든 메소드에 해당 미들웨어를 사용하고 싶다면 app.use(), 

특정 http method 요청이 들어왔을 때 해당 미들웨어를 사용하고 싶다면 메소드에 맞게 app.get(), app.post() 등을 사용하면 된다. 

 

app.use('/user', helmet());

위의 코드는 helmet() 미들웨어를 url이 '/user'로 시작하는 경우 실행하는 반면, 

아래의 코드는 url이 '/custom'으로 시작하면서 http get 방식의 요청이 들어온 경우에만 helmet() 미들웨어를 실행한다. 

app.get('/custom', helmet());

+

지금까지 라우팅에 사용하던

app.get('/url', (req, res)=>{}));

 

이 함수 역시 미들웨어의 종류 중 하나였다!

 

4) 미들웨어 연속적으로 주기

미들웨어 하나를 매개변수로 준 다음, 바로 다음에 실행될 함수를 추가 매개변수로 줄 수 있다. 
이 경우 매개변수로 주어진 순서대로 실행이 된다. 

app.get('*', (req, res, next)=>{
  console.log('first middleware')
  }),
  (req, res, next)=>{
    console.log('second middleware')
  }
});

 

5) 미들웨어 실행 순서

순서대로, 맨 위부터 실행한다. 
next()의 경우 다음에 실행될 미들웨어를 호출한다. 

만약 현재 실행되고 있는 미들웨어의 다음 매개변수로 주어진 미들웨어도 있고, 같은 url과 http method에 호출되는 미들웨어도 있는 경우:

app.get('/node/js', (req, res, next)=>{
  console.log('first middleware');
  next();		// option 1
  next('route');	// option 2
},
(req, res)=>{
  console.log('next middleware');	// option 1 result
}
);

app.get('/node/js', (req, res)=>{
  console.log('next route middleware');	// option 2 result
});

(둘 다 실행할 수 없다.)

 

 

1. next()를 실행하면 같은 미들웨어의 다음 매개변수로 주어진 미들웨어 실행
2. next('route')를 실행하면 다음 미들웨어가 실행된다.

 

+제3자가 만든 미들웨어(Third-party middleware)

사용하는 방법:

1) npm으로 패키지 설치

$ npm install cookie-parser

 

2) require를 통해 패키지 불러오기

const cookieParser = require('cookie-parser');

 

3) app.use()를 통해  해당 미들웨어를 app 인스턴스에 장착하기

app.use(cookieParser);

위 단계를 거치면, 각 라우터 미들웨어 안에다 부가적인 코드를 작성하지 않아도 미들웨어에 해당하는 http 방식과 url로 요청이 들어올 때 미들웨어가 활성화된다. 

 

[1] bodyParser

 

이 미들웨어는 클라이언트가 post방식으로 보낸 http request 중에서, body 부분을 parameter로 추출해 준다. 
그래서 post 방식에서 사용할 수 있다(사용자가 보낸 데이터가 있어야 한다)

// 필요한 코드가 생략된 sudo code
const fs = require('fs');
const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({extended: false}));

app.get('*', (req, res, next)=>{
  fs.readdir('./data', function(err, filelist){
    req.list = filelist;
    next();
  });
});

예시 코드에서, app.get() 라우터 미들웨어가 실행되기 전에 이미 body-parser 미들웨어가 실행되었고, 그 미들웨어는 filelist 변수에 실행값을 리턴한 상황이다. 

 

따라서 filelist 변수의 값을 request 객체의 list 속성에 추가하면(list 속성은 원래 있던 속성이 아니고, 임의로 추가한 속성이다), 후에 request 객체의 list 속성에 접근해서 body-parser 미들웨어가 추출한 값을 사용할 수 있다. 

 

[2] compression

웹서버가 클라이언트의 요청에 응답할 때 그 응답을 압축해서 보내는 것을 도와주는 미들웨어이다. (데이터의 양이 많으면 http 요청으로 보내기 어려울 수 있다.)


압축된 데이터는 기존 데이터에 비해서 크기가 작고, '개발자 도구'의 '네트워크' 탭에서 보면 어떤 방식으로 압축되었는지도 알 수 있다. 클라이언트(브라우저)는 해당 데이터를 받아서, 그 데이터가 압축된 방식을 사용해서 반대로 그 데이터를 해제(=압축 풀기)한 다음에 원본 파일을 볼 수 있다. 

 

 

+미들웨어 만들기

직접 미들웨어를 만드는 것도 가능하다. 
미들웨어는 함수이다. 다만 request, response, next 라는 3개의 매개변수를 받아야 한다. 

app.use((req, res, next)=>{

});

 

이렇게 입력해 주면 미들웨어를 만들고 만든 미들웨어를 앱 어플리케이션에 장착한 것이다. (=바로 사용할 수 있다)

req(request): 클라이언트가 보낸 요청

res(response): 서버가 보낼 응답

next(): 해당 미들웨어의 실행이 끝난 다음에 불러올 미들웨어이다. 
서버의 응답을 해당 미들웨어에서 끝낼 것이 아니라면, 반드시 next()를 통해 다음 미들웨어를 호출해 주어야 한다. 

 

 

2. Routing(라우팅)

1) 정의

데이터 통신에서의 라우팅(Routing)이란 네트워크상에서 주소를 이용하여 목적지까지 메시지를 전달하는 방법을 체계적으로 결정하는 경로선택 과정을 말한다. 이 과정을 능동적으로 수행하는 장치를 라우터(Router)라고 한다. 

 

즉 웹 서버로 http 요청이 들어오면, 해당 요청의 url 주소를 이용하여 요청을 체계적으로 처리하는 과정을 라우팅이라고 한다. 또한 이를 처리하는 미들웨어를 Router(라우터)라고 한다. 

(출처: 라우팅 (Routing) : 네이버 블로그 (naver.com))

 

2) url에 parameter를 실어서 보내는 방법

app.get('/:parameter-이름', (req, res)=>{
res.send(req.params);
});
: 후에 localhost:port-번호/parameter-값 url로 들어가면, 요청(request)의 매개변수(parameter/params)속성으로 json 타입의 객체가 반환되는 것을 볼 수 있다. 

이렇게(↓).
{"parameter 이름": "parameter 값"}

res.send() 또는 res.end()는 말 그대로 서버에서 응답을 종료한다는 의미이다. 

클라이언트에서 요청을 보내면 서버가 요청에 대해 응답하는 방식으로 통신이 일어난다. 

요청이 원활하게 이뤄지려면 서버는 응답을 완료한 뒤 응답이 끝났다고 명시, 표현해 줘야 한다. 

그 표시가 res.send() 또는 res.end() 메소드이다. 

만약 응답이 끝냈다고 명시해주지 않으면, 응답이 완료되지 않고 쭉 늘어지는 상황(hanging)이 발생한다. 

 

3) Router(라우터) - 주소 체계의 변경

복잡한 웹 서비스의 경우 수백 개의 api가 존재할 수 있는데, 그 api를 하나의 파일에 담는 것은 무리이다. 
여러 개의 api를 적절한 경로로 나눠서 복잡한 구조를 간단하게 하고 싶을 때 라우팅 작업을 사용한다. 

 

라우팅 처리하는 순서:

1. 공통된 url 경로를 가진 미들웨어들을 별도의 파일에 분리한다. 
2. app.js 파일(웹 어플리케이션이 실행되는 메인 역할의 파일)에다가 1번 파일의 주소를 변수로 불러온다.

(예시)

const indexRouter = require('./routes/index');
const topicRouter = require('./routes/topic');

 

3. 2번에서 선언한 변수(indexRouter, topicRouter)를 app.use()를 사용해서 app.js 파일에서 미들웨어로 사용하겠다고 선언한다. 이때 app.use('라우터와 연결할 url', 사용할 변수 이름); 으로 입력한다. 

app.use('/', indexRouter);
app.use('/topic', topicRouter);

 

4. 1번 파일에다가 express.Router(); 값을 갖는 변수를 선언한다. 이 변수가 라우터 역할을 할 것이다. 

const express = require('express');	// router 사용하기 위해서 필요한 express module 불러오기
const router = express.Router();

 

5. 1번 파일의 각 api(라우터 역할을 하는 미들웨어)에서 app으로 시작하는 미들웨어의 시작 부분을 2번 변수의 이름으로 바꾼다. 

 

(1~5번을 합한 예시)

# 공통된 url 경로를 가진 미들웨어들을 분리한 별도의 파일

const express = require('express');
const router = express.Router();
// 다른 미들웨어(express 공식 미들웨어/제 3자가 작성한 미들웨어)

router.get('/', (req, res, next)=>{
	// 미들웨어 코드
});
// 내가 작성한 기타 미들웨어 코드

module.exports = router;

# app.js 파일

const express = require('express');
const app = express();
const exampleRouter = require('./example/path');

app.use('/example/path', exampleRouter); // 다른 파일에서 사용한 라우터를 호출하는 코드
// app.js에 명시된 다른 미들웨어들

 

*단, app.js 파일에서도 미들웨어는 위에서부터 호출되기 때문에, app.use()로 다른 파일에서 사용한 라우터를 호출하는 코드는 위쪽에 배치되어야 한다. 

+

redirection 메소드:  주어진 url에 해당하는 페이지로 단순 이동하는 역할을 한다.

: response.redirect('이동하려는 url');

response.redirect('/example/url');

 

3. 에러 처리

express에서 에러를 처리하는 방법은 크게 두 가지가 있다. 


1. 코드의 맨 아래에다 에러를 처리할 미들웨어 작성하기.

미들웨어는 순서대로 실행되므로, 반드시 파일의 끝 부분에 작성해야 함!

이때 해당 미들웨어는 매개변수로 (err, req, res, next) 네 개의 매개변수를 받는다. 

(express에서는 그래야만 해당 미들웨어가 에러를 처리하는 역할을 하도록 정해 놓았다.)

// other middlewares

app.use((err, req, res, next)=>{
  // error handling
});

 

2. express에서 기본으로 제공하는 에러 처리 미들웨어 사용하기.

next() 함수의 매개변수로 err(에러)을 넣어 주면 된다. : next(err)

express에서는 별도의 처리를 하지 않아도, 해당 에러를 처리해 주는 미들웨어를 제공한다. 

router.get('/example/url', (req, res, next)=>{
  // code
  if(err){
    next(err);
  } else {
    // code
  }
});


4. 정적 파일(static file) 제공하기

express.static('정적인 파일이 있는 폴더의 경로');
: 해당 디렉토리에 해당하는 파일들은 정적인 파일들로 불러올 수 있다. 

app.use(express.static('정적인 파일이 있는 폴더의 경로'));
: 해당 디렉토리 안의 파일들을 웹 어플리케이션에서 정적 파일들로 사용할 수 있다. 

+ Recent posts