[Node.js] Express 미들웨어

LMH·2022년 12월 12일
0
post-thumbnail

Express 공식 사이트에 들어가면 미들웨어에 대한 설명이 나와있습니다. 요청과 응답의 중간에 작동하는 것들을 미들웨어라고 합니다. 익스프레스는 미들웨어가 핵심이며, 미들웨어의 사용법을 알아야 익스프레스를 더 효율적으로 사용할 수 있습니다.

미들웨어를 한 마디로 표현하자면, 요청과 응답 사이에 어떠한 기능을 하는 함수입니다.

미들웨어 사용법

미들웨어는 app.use와 함께 사용되며 use 메소드의 전달인자로 들어오는 것이 미들웨어 입니다. 아래의 코드 처럼 미들웨어를 직접 구현하여 사용할 수 있습니다.

// app.js
// port를 설정할 때 환경변수에 PORT가 있을 경우 그 값을 가져오고 아니면 3000번 포트로 설정합니다.
app.set('port', process.env.PORT || 3000)

app.use((req, res, next) => {
	console.log('모든 요청에 실행');
  	next();
});

app.get((req, res, next) => {
	console.log('GET / 요청에만 실행');
  	next();
}, (req, res) => {
 	throw new Error('에러는 에러 처리 미들웨어로 갑니다.');
});

// 에러 미들웨어는 네 가지 매개변수를 받습니다. 네 가지 모두 전달되지 않더라고 반드시 작성해 주어야 합니다.
app.use((err, req, res, next) => {
	res.status(500).send(err.message);
})

// app.set으로 설정한 port로 연결합니다.
app.listen(app.get('port'))

미들웨어는 위에서 아래로 순서대로 실행되며 실행되는 미들웨어의 코드 아래쪽에 작성된 코드에만 영향을 미칩니다. app.use의 세번째 인자로 전달 받은 next는 다음 미들웨어로 넘어가는 함수입니다. 만약 next를 실행해 주지 않으면 다음 미들웨어로 넘어가지 않습니다.

특정조건에서 미들웨어 적용

어떤 특정 조건에서 미들웨어를 사용할 것인지 미리 정해줄 수 있습니다. 그리고 미들웨어는 한 번에 여러개를 장착할 수 있습니다. 여러개인 경우에도 미들웨어 사이에 next를 호출해야합니다.

  • app.use(미들웨어) : 모든 요청에서 미들웨어를 실행
  • app.use('/abc', 미들웨어) : abc로 시작하는 요청에서 미들웨어를 실행
  • app.get('/abc', 미들웨어) : abc로 시작하는 get 요청에서 미들웨어를 실행
  • app.post('/abc', 미들웨어) : abc로 시작하는 post 요청에서 미들웨어를 실행

자주 사용하는 미들웨어

지금부터 정리하는 미들웨어는 많이 사용되는 미들웨어입니다. 이 미들웨어들을 사용할 때에는 별도로 next 함수를 실행시키지 않아도 됩니다. 그 이유는 미들웨어 내부에 next 함수를 호출하도록 구현되어 있기 때문입니다.

morgan

콘솔에 요청과 응답에 대한 정보를 출력해주는 미들웨어입니다. 인수로 'dev', 'combined', 'common', 'short', 'tiny' 등을 사용할 수 있습니다.

const morgan = require("morgan");

app.use(morgan(인수))

cors

cors 옵션을 쉽게 적용시킬 수 있는 미들웨어 입니다. cors에 대한 설정을 객체로 정의해두고 cors 객체에 전달인자로 전달하면 됩니다.

const cors = require("cors");
const corsOptions = {
    origin: 'https://www.domain.com',  // 특정 도메일 CORS 허용
    credentials: true  // 인증 정보 쿠키에 포함
}

app.use(cors(corsOptions))  

static

정적인 파일들을 제공하는 라우터 역할을 합니다. 기본적으로 제공되는 미들웨어로 따로 설치할 필요가 없습니다. 아래의 코드에서 루트 경로('/')로 요청할 경우 public 폴더를 참조하도록 지정하고 있습니다. 다음과 같이 지정할 경우 '/public/index.html'는 'localhost:3000/index.html'와 같게 됩니다.

이렇게 요청에 따른 특정 경로를 지정해 둘 경우, 서버의 폴도 경로와 요청 경로가 다르므로 외부인이 서버 구조를 쉽게 파악할 수 없다는 장점이 있습니다. 또한 요청할 경우 정적파일을 알아서 제공해주기 때문에 파일 시스템을 이용하여 직접 읽어서 전송할 필요가 없어 편리합니다.

app.use('요청 경로', express.static('실제 경로'));

app use('/', express.static(path.join(__dirname, 'public')))
// path.join은 전달인자로 받은 경로를 합쳐줍니다.
// '__dirname'은 현재 실행하는 파일의 절대경로를 의미합니다.
// localhost:{port}/ = ~~~/public/

body-parser

요청의 본문에 있는 데이터를 해석해서 req.body 객체로 만들어 주는 미들웨어 입니다. 보통 폼 데이터나 AJAX 데이터를 처리하고 이미지, 동영상, 파일 데이터는 처리하지 못 합니다. 이러한 멀티파트 파일들은 multer 모듈을 사용해서 처리합니다.

Express 4.16.0 버전부터 body-parser 미들웨어 일부기능이 내장되어 따로 설치할 필요는 없으나, 직접 설치해야하는 경우도 있습니다. JSON,URLencoded 형식의 데이터 외에도 Raw, Text 데이터를 추가로 해석할 수 있습니다.

Raw는 본문이 버퍼 데이터인 경우에 사용하며, Text는 텍스트 데이터일 때 해석합니다.

app.use(express.json())
app.use(express.json({strict: false}));
app.use(express.urlencoded({extented : false }))

// Raw, Text 의 경우
const bodyParser = require('body-parser'); // bodyParser 설치
app.use(bodyParser.raw());
app.use(bodyParser.text());

app.use(express.urlencoded({extented : false }))

urlencode 메소드의 extended 옵션이 true일 경우, 객체 형태로 전달된 데이터내에서 또다른 중첩된 객체를 허용하고 false인 경우에는 허용하지 않는 다는 의미입니다.

이 값이 false인 경우 node.js에 기본으로 내장된 queryString 기능을 사용하고 true인 경우 따로 qs 라이브러리 추가로 설치하여 사용해야 합니다.

queryString과 qs 라이브러리는 url의 쿼리 스트링을 해석해 준다는 공통점이 있지만 qs 라이브러리는 보안기능이 추가된 라이브러리이므로 필요할 경우 설치해서 사용하면 됩니다.

app.use(express.json({strict: false}))

json 메소드의 strict 옵션이 false 인경우에는 전달인자로 객체와 배열만 받는 다는 의미이며, true인 경우 문자열과 같은 다른 타입의 데이터를 받을 수 있습니다.

JSON 형식 vs URL-encoded 형식

JSON 형식으로 { name : 'lmh', age : '32' }를 전송할 경우 req.body로 그대로 전달 됩니다.

URL-encoded 형식으로 name=lmh&age=32를 전송할 경우 req.body에 { name : 'lmh', age : '32' }로 전달 됩니다.

cookie-parser는 요청에 들어있는 쿠키를 해석하여 req.cookies 객체로 만들어 줍니다.

app.use(cookieParser(비밀키))
app.use(cookieParser(process.env.COOKIE_SECRET)) // COOKIE_SECRET 이라는 환경변수 값
// 보안을 위해 사용되는 비밀 키는 환경변수에 보관 됩니다.(process.env.COOKIE_SECRET)
// id=lmh 라는 쿠키를 보내면 req.cookies는 { id : 'lmh' }가 됩니다.
res.cookie('name', 'lmh', {
	expires : new Date(Date.now() + 900000),
  	httpOnly : true,
  	secure : true,
})
res.clearCookie('name', 'lmh', { httpOnly : true, secure : true, }) // 쿠키 삭제

해석된 쿠키들은 req.cookies 객체에 들어가며, 유효기간이 지난 쿠키는 자동으로 필터링합니다. 서명된 쿠키가 있는 경우, 제공한 비밀 키를 통해 쿠키가 해당 서버에서 만든 쿠키밈을 검증할 수 있습니다.

signed라는 옵션을 true로 사용하면 쿠키의 위조 방지를 위해 비밀 키를 이용해서 만든 서명을 쿠기 값 뒤에 붙입니다.

쿠키의 생성과 삭제를 위해서는 res.cookie, res.clearCookie 메소드를 사용해야하며 삭제 시에는 키, 값, 옵션이 일치해야 삭제가 됩니다.
(단, expires, maxAge 옵션은 일치하지 않아도 됩니다.)

express-session

express-session은 인수로 세션에 대한 설정을 받습니다. resave는 요청이 올 때 세션에 수정 사항이 생기지 않아도 세션을 다시 저장할지에 대한 설정이고 saveUninitialized는 세션에 저장할 내역이 없어도 처음부터 세션을 생성할 것인지에 대한 설정입니다.

express-session은 세션 관리 시 클라이언트에게 쿠키를 보냅니다. 안전하게 쿠키를 보내기 위햇 서명에 필요한 비밀 키와 쿠키 설정을 작성합니다.

app.use(session({
	resave : false, // 저장 설정 
  	saveUninitialized : false, // 초기화 설정
  	secret : process.env.COOKIE_SECRET, // 비밀 키 -> cookie-paser의 비밀키와 같게 설정하는 것이 좋습니다.
  	cookie : {
    	httpOnly: true, // 클라이언트에서 쿠키 확인 불가(true)
      	secure : false, // http가 아닌 환경에서도 사용가능(false) -> 배포 시 true값으로 변경하는 것이 좋습니다.
    },
	name: 'session-cookie', // 세션 쿠키 이름 설정(안할 경우 'connect.sid' 가 초기값)
}))

store 옵션을 상용하면 세션은 메모리에 저장되며 서버를 재시작하기 전까지 유지 됩니다. 배포 시에는 store에 데이터 베이스를 연결하여 세션을 유지할 수 있습니다.

req.session.name = 'lmh';
req.sessionID; // 세션 아이디 확인
req.destroy(); // 세션 모두 제거

미들웨어 한번에 사용하기

여러가지 미들웨어를 아래의 코드처럼 한번에 사용할 수도 있습니다. 이렇게 미들웨어가 반복적으로 사용 될경우 next 함수를 통해 원하는 미들웨어로 바로 이동할 수 있습니다.

app.use(
  	express.static(path.join(__dirname, 'public')),
	express.json(),
	app.use(express.urlencoded({extented : false }))
);

Reference

profile
새로운 것을 기록하고 복습하는 공간입니다.

0개의 댓글