Express - 미들웨어

Express

Posted by kwon on 2020-09-12

MiddleWare

미들웨어는 express의 핵심이다. 요청과 응답의 중간에 위치하여 미들웨어라고 하며, 라우터와 에러 핸들로 또한 미들웨어의 일종이므로 미들웨어가 express의 전부라고 봐도 무방하다.

미들웨어 함수는 req(요청) 객체, res(응답) 객체, 그리고 애플리케이션 요청-응답 사이클 도중 그 다음의 미들웨어 함수에 대한 액세스 권한을 갖는 함수이다.

다음 미들웨어 함수에 대한 액세스는 next 함수를 이용해서 다음 미들웨어로 현재 요청을 넘길 수 있다. next라는 말에서 알 수 있듯이 next를 통해 미들웨어는 순차적으로 처리한다.(순서가 중요!)

next 함수는 인자의 종류로 구분이 가능한데, 인자를 아무것도 넣지 않으면 단순하게 다음 미들웨어로 넘어간다. next 함수의 인자로 route를 넣어 next('route') 와 같이 사용하면, 라우터에 연결된 나머지 미들웨어들을 건너뛰고 주소와 일치하는 다음 라우터로 넘어가게 된다. 그 외에 다른 값을 인자로 넣으면 다른 미들웨어나 라우터를 모두 건너 뛰고 바로 에러 핸들러로 이동하며 넣어준 값은 에러에 대한 내용으로 간주된다.

미들웨어는 주로 app.use와 함께 사용되고, 미들웨어는 use 메서드로 app에 장착한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.set('port', process.env.PORT || 3000);

app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser(process.env.COOKIE_SECRET));

반드시 미들웨어 안에서 next()를 호출해야 다음 미들웨어로 넘어간다. loggerexpress.json() , express.urlencode , cookieParser , express.static 모두 내부적으로는 next() 를 호출하기 때문에 다음 미들웨어로 넘어갈 수 있는 것이다.

미들웨어 종류

morgan

요청에 대한 정보를 콘솔에 기록해주는 미들웨어 app.use(logger('dev')) 에서 함수의 인자로 dev short common combined 등을 줄 수 있다. 개발 시에는 devshort 를 많이 사용하고, 배포 시에는 common 이나 combined 를 많이 사용한다.

body-parser

요청의 본문을 해석해주는 미들웨어이다. 보통 폼 데이터나 ajax 요청의 데이터를 처리한다.

1
2
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

express 4.16.0 버전부터는 body-parser 의 일부 기능이 express에 내장되어 따로 설치하지 않고 위와 같이 사용이 가능하다. body-parser 가 필요한 경우도 있는데, json, url-encoded 형식의 본문 외에도 raw, text 형식의 본문을 해석할 수 있기 때문이다.

jsonjson 형식의 데이터 전달 방식이고, URL-encoded 는 주소 형식으로 데이터를 보내는 방식이다.

보통 폼 전송이 URL-encoded 방식을 주로 사용한다. urlencode 메소드를 보면 { extended: false } 라는 옵션이 들어있는데, 이 옵션이 false 면 노드의 querystring 모듈을 사용하여 쿼리스트링을 해석하고, true 인 경우 qs 모듈을 사용하여 쿼리스트링을 해석한다. qs 모듈은 내장 모듈이 아니라 npm 패키지이며, querystring 모듈의 기능을 조금 더 확장한 모듈이다.

cookie-parser 는 요청에 동봉된 쿠키를 해석해준다. 쿠키는 name=kwon;age=25 와 같은 문자열 형식으로 오는데, 이것을 { name: 'kwon', age: '25' } 와 같은 객체로 파싱해준다.

app.use(cookieParser('secret code')) 와 같이 첫 번째 인자로 문자열을 넣을 수 있는데, 암호화된 쿠키가 있는 경우, 인자로 넣은 문자열을 키로 삼아 복호화할 수 있다.

static

static 미들웨어는 정적인 파일들을 제공해주는 express의 내장 미들웨어이다.

1
app.use(express.static(path.join(__dirname, 'public')));

함수의 인자로 정적 파일들이 담긴 폴더를 지정한다. 위와 같은 경우 실세 서버의 폴더 경로에는 public이 들어있지만, 요청 주소에는 public 이 포함되지 않기 때문에 외부에 실제 경로가 유출되지 않아 보안상의 이점도 있다.

1
app.use('/files', express.static(path.join(__dirname, 'public')));

위와 같이 사용하면 /files 라는 가상 경로를 통해 접근하게 할 수 있다.

static 미들웨어는 요청에 부합하는 정적 파일을 발견한 경우 응답으로 해당 파일을 전송하는데, 이러한 경우 다음에 나오는 라우터가 실행되지 않고, 파일을 찾지 못한 경우 다음 라우터로 요청을 넘긴다. 이렇게 자체적으로 정적 파일 라우터 기능을 수행하기 때문에 최대한 위쪽에 배치하는 것이 서버가 쓸데없는 미들웨어 작업을 하는 것을 막을 수 있다. 따라서, 보통 morgan 다음에 배치하는 것이 좋은데 morgan 보다 위로 올리면 정적 파일 요청이 기록되지 않기 때문이다. json urlencoded cookie-parser 는 정적 파일을 제공하는데 영향을 끼치지 않기 때문에 굳이 이런 미들웨어를 거칠 필요가 없다.

path 모듈

위에서 사용된 path 는 경로에 관련된 모듈인데, 파일 경로를 작성할 때 문자열을 직접 자르고 합치는 것보다 path에서 제공하는 join함수를 사용하는 것이 편리하다. __dirname 은 node.js에서 제공하는 node파일의 경로를 담고 있는 글로벌 객체이다. path.join() 은 각 파라미터를 합쳐서 경로 문자열을 반환한다. 즉, 위와 같은 path.join(__dirname, 'public') 와 같은 경우 '현재 app.js 파일의 경로 + /public' 을 리턴한다.

express-session

세션 관리용 미들웨어이며, express-generator 로 설치되지는 않기 때문에 따로 설치해주어야 한다.

  • npm i express-session
1
2
3
4
5
6
7
8
9
10
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
}));

express-session 은 인자로 세션에 대한 설정을 받는다.

  • resave : 요청이 왔을 때 세션에 수정 사항이 생기지 않더라도 세션을 다시 저장할지에 대한 설정
  • saveUninitialized : 세션에 저장할 내역이 없더라도 세션을 저장할지에 대한 설정(보통 방문자 추적 시 사용)
  • secret : 필수 항목. cookie-parser 의 비밀키와 같은 역할
    • express-session 은 세션 관리 시 클라이언트에 쿠키를 보낸다. 이를 세션 쿠키라고 하는데, 안전하게 쿠키를 전송하려면 쿠키에 서명(암호화)를 해야하고 쿠키를 서명하는 데 secret 의 값이 필요하다. cookie-parser 와 같이 설정해야 한다. (위의 코드에서는 dotenv 라이브러리를 사용하여 값을 따로 관리했다. dotenv는 환경변수를 관리해주는 라이브러리인데, .env 파일에 전역적인 정보를 관리하고 .gitignore.env 파일을 추가하여 공개되지 않도록 해야한다.)
  • cookie : 세션 쿠키에 대한 설정 (일반적인 쿠키 옵션이 모두 제공)
    • httpOnly : 이 옵션을 사용하면 클라이언트에서 쿠키에 접근하지 못하도록 한다.
    • secure : falsehttps 가 아닌 환경에서도 사용 가능, truehttps 에서만 사용 가능
      • 배포 시에는 ture 로 바꿔서 배포하는 것이 좋음

connect-flash

일회성 메세지들을 웹 브라우저에 나타낼 때 유용한 미들웨어다.

  • npm i connect-flash

connect-flashcookie-parserexpress-session 을 사용하기 때문에 이 두 미들웨어보다 아래에 위치시켜야 한다. connect-flash 를 사용하면 req 객체에 req.flash() 메소드를 추가한다. req.flash(key, value) 의 형태로 해당하는 키에 값을 저장하고, req.flash(key) 로 해당 keyvalue 를 불러올 수 있다.

일회성 메세지이기 때문에 로그인이나 회원가입 에러 같은 일회성 경고 메세지 등에 유용할 것이다.

참조
https://psyhm.tistory.com/8
https://psyhm.tistory.com/6
https://nodejs.org/dist/latest-v8.x/docs/api/modules.html#modules_dirname
https://victorydntmd.tistory.com/23
Node.js 교과서