본문 바로가기
Programming

[Server] express 서버 시작하는 방법, 미들웨어 종류

by 개발자 염상진 2022. 5. 27.

 

 

Express

 

Node.js에서도 HTTP 요청과 응답을 처리할 수 있는 HTTP 모듈을 제공한다. 그럼에도 불구하고 코드가 보기 힘들고, 확장성이 떨어지기 때문에 npm에서 서버를 제작하는 웹 서버 프레임워크 Express가 도입되었다. Node.js 환경에서 웹 서버를 생산하는 프레임워크는 Koa, hapi 등이 있지만 express 사용이 압도적이다.

 

Express 시작하기

프로젝트를 시작할 디렉토리를 생성하고 express를 설치해준다. 코드가 변경될 때마다 자동으로 서버를 재실행 해주는 nodemon도 함께 설치해준다.

$ mkdir test_server
$ cd test_server

$ npm i express
$ npm i -D nodemon

 

또는 애플리케이션 골격을 신속하게 완성하기 위해서는 express 제너레이터 도구를 사용한다.

$ npm install express-generator -g
$ express --view=pug test_server
$ cd test_server
$ npm install

 

express 제너레이터를 사용하면 아래와 같은 디렉토리가 자동으로 생성된다.

.
├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── index.js
│   └── users.js
└── views
    ├── error.pug
    ├── index.pug
    └── layout.pug

7 directories, 9 files

 

package.json

package.json에는 scripts->start 속성을 반드시 채워준다. 

{
  "dependencies": {
    "express": "^4.18.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.16"
  },
  "name": "test_server",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start" : "nodemon app",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "description": ""
}

 

app.js

Express 모듈을 express라는 변수에 담아준다. express 내부에는 http 모듈이 내장되어 있기 때문에 서버 역할을 수행할 수 있게 된다.

 

app.set()은 포트를 설정해준다. process.env의 PORT 속성을 이 서버의 포트로 지정한다. 

 

 app.get()은 GET HTTP 메소드가 요청되었을 때 동작한다. 매개변수로 받은 콜백함수에는 요청에 관한 정보 req와 응답에 관한 정보 res를 전달받는다. 

 

Node.js의 HTTP 모듈과는 다르게 res.wirte를 쓸 필요 없이 res.send()로 응답을 보낼 수 있다. 

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

const app = express();

app.set('port', process.env.PORT || 4998);


app.get('/', (req, res, next)=>{
    res.send("Hello Im Express");
})


app.listen(app.get('port'), ()=>{
    console.log(`Server is listening on ${app.get('port')}`);
})

 

index.html

서버가 응답할 때 정적 리소스인 HTML파일을 반환하기 위해서는 res.sendFile 메소드를 사용한다. app.js와 동일한 디렉토리에 위치한 index.html을 반환한다. 

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

const app = express();

app.set('port', process.env.PORT || 4998);


app.get('/', (req, res, next)=>{
    console.log("GET / 요청에서만 실행됨")
    res.sendFile(path.join(__dirname, '/index.html'));
)

app.listen(app.get('port'), ()=>{
    console.log(`Server is listening on ${app.get('port')}`);
})

 

 

express 미들웨어

 

미들웨어 함수는 express에서 핵심적인 역할을 수행한다. 라우터, 에러 핸들러, CORS 핸들러 모두 미들웨어에 속한다. 미들웨어를 통해 요청과 응답을 조작하고 기능을 추가하게 되며, 잘못된 요청을 필터링하는 역할을 수행한다. 

 

미들웨어는 express의 인스턴스인 app의 use 함수를 사용한다. 미들웨어는 app.use((req, res, next)) 꼴로 사용한다. use 함수에 특정 path를 지정해주지 않으면 서버에 들어오는 모든 요청에 대해 미들웨어가 실행된다. 또한 콜백함수로 전달받는 next를 수행하지 않으면 서버는 멈춰버린다.

 

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

const app = express();

app.set('port', process.env.PORT || 4998);


// 모든 요청에 대해 요청 수행
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)=>{
    console.error(err);
    res.status(500).send(err.message);
})

app.listen(app.get('port'), ()=>{
    console.log(`Server is listening on ${app.get('port')}`);
})

 

 

morgan 미들웨어

 

mogan 미들웨어는 요청과 응답에 대한 정보를 콘솔에 기록하는 미들웨어다. morgan 미들웨어 사용법은 다음과 같다. 모든 요청에 대해 morgan 미들웨어를 수행한다. 매개변수로는 dev, combined, short, tiny 등이 존재한다. 개발환경에서는 dev 매개변수를 사용하고 배포 환경에서는 combined 매개변수를 사용한다.

 

moran 미들웨어 설치

$ npm i morgan

 

morgan 미들웨어 사용

const morgan = require('morgan')
app.use(morgan('dev'));
GET / 304 25.361 ms - -

 

 

static 미들웨어

 

static 미들웨어는 express 내장 미들웨어기 때문에 따로 설치할 필요는 없다. 정적인 파일들을 제공하는 라우터 기능을 수행한다. 

static 미들웨어는 정적 파일들이 담겨 있는 디렉토리를 지정한다. static 폴더를 만들고 css, js, html 파일들을 담으면 브라우저에서 접근이 가능하다. 

서버 내의 파일 위치와 브라우저에서 요청하는 경로가 다르다. 이는 사용자가 서버의 구조를 알 수 없도록 캡슐화 하는 과정이며 보안에 도움이 된다.

만약 요청 경로에 해당 폴더가 존재하지 않으면 에러를 발생시키지 않고 바로 next 함수를 호출해서 다음 미들웨어로 제어권이 넘어가게 된다.

static 미들웨어 사용법

app.use('path', express.static('실제 경로'));
app.use('/', express.static(path.join(__dirname, 'static')))

 

 

body-parser 미들웨어

 

요청의 본문을 parsing 해서 req.body 객체로 만들어주는 미들웨어다. <form> 데이터나 Ajax로 요청되는 데이터를 담당한다. 다만 express 4.16.0 버전 부터는 body-parser 미들웨어의 기능이 express에 내장되어있기 때문에 추가적으로 설치할 필요는 없다.

 

만약 JSON이나 URL-encoded 형식의 데이터 뿐만 아니라 Raw(요청의 본문이 버퍼 데이터임), Text(요청의 본문이 텍스트 데이터 임) 형식의 데이터를 해석할 때는 body-parser 미들웨어를 추가로 설치해서 사용할 수 있다.

 

$ npm i body-parser
const bodyParser = require('body-parser');
app.use(bodyParser.raw());
app.use(bodyParser.text());

 

express.json()은 JSON 형식의 데이터를 requ.body로 변경한다. urlencoded는 주소 형식으로 데이터를 보내는 방식이다. <form> 태그에서의 요청이 주로 URL-encoded 방식을 사용한다. urlencoded 메소드 안에는 {extended : false} 객체가 전달된다. 만약 extended가 false인 경우 Node의 쿼리스트링 모듈을 사용해서 쿼리스트링을 해석한다.

 

만약 true인 경우 qs 모듈을 사용해서 쿼리 스트링을 해석하게 된다. qs 모듈은 쿼리 스트링 모듈의 기능을 확장한 확장 모듈이다.(npm 내장 모듈 아님)

 

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

 

 

cookie-parser 미들웨어

 

cookie-paser 미들웨어는 요청과 함께 전송되는 쿠키를 해석하고 req.cookie 객체를 생성한다. 해석된 쿠키들은 req.cookies에 저장된다. 유효기간이 지난 쿠키는 알아서 필터링 된다. 

 

cookie-parser 사용법

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

app.use(cookieParser(비밀키));

 

첫번째 매개변수로 비밀키를 받는다. 서명된 쿠키가 존재하면 제공한 비밀키를 통해 해당 쿠키가 생성된 곳이 자신의 서버임을 검증할 수 있게 된다. 

 

특히 쿠키는 클라이언트에서 위조 될 가능성이 높기 때문에 비밀키를 통해 생성한 비밀키를 쿠키 뒤에 붙이게 된다. 서명이 붙은 쿠키는 name=value.sign 형태로 생성된다. 서명된 쿠키는 req.cookie가 아니라 req.signedCookies 객체에 저장된다.

 

쿠키를 생성하기 위해서는 res.cookie(키, 값, 옵션)을 사용하고 제거하기 위해서는 res.clearCookie 메서드를 사용한다.  쿠키를 제거하기 위해서는 전달된 옵션 모두 일치해야 한다.

 

res.cookie('name', 'value', {
	expires : new Date(Date.now() + 1000000),
    httpOnly : true,
    secure : true,
})

res.clearCookie('name', 'value', {httpOnly : true, secure : true});

 

res.cookie() 옵션 중 signed 옵션을 true로 설정하면 쿠키 뒤에 서명이 붙게 된다. 자신의 서버가 만든 쿠키임을 검증할 수 있기 때문에, 대부분 서명 옵션을 true로 해두는 것이 좋다. 서명을 위한 비밀키를 cookieParser 미들웨어 인수에 넣는다.

res.cookie 옵션

domain String Domain name for the cookie. Defaults to the domain name of the app.
encode Function A synchronous function used for cookie value encoding. Defaults to encodeURIComponent.
expires Date Expiry date of the cookie in GMT. If not specified or set to 0, creates a session cookie.
httpOnly Boolean Flags the cookie to be accessible only by the web server.
maxAge Number Convenient option for setting the expiry time relative to the current time in milliseconds.
path String Path for the cookie. Defaults to “/”.
priority String Value of the “Priority” Set-Cookie attribute.
secure Boolean Marks the cookie to be used with HTTPS only.
signed Boolean Indicates if the cookie should be signed.
sameSite Boolean or String Value of the “SameSite” Set-Cookie attribute. More information at https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1.

 

 

express-session 미들웨어

 

말 그대로 세션을 관리하기 위한 미들웨어다. 로그인을 위해 세션을 구현하거나 특정 사용자를 위한 데이터를 임시적으로 저장하기 위해 사용한다. 세션은 사용자별로 req.sessoin 객체에 저장된다. 

 

const session = require('express-session')

app.use(session({
    resave : false,
    saveUninitialized : false,
    secret : process.env.COOKIE_SECRET,
    cookie:{
        httpOnly : true,
        secure : false,
    },
    name : 'session-cookie',
}))

 

express-session 미들웨어는 인자로 세션에 대한 설정을 전달받는다. 

  • resave : 요청이 오는 경우 세션에 수정 사항이 생기지 않더라도 세션을 다시 저장할지 여부
  • saveUninitialized : 세션에 저장할 내역이 없더라도 처음부터 세션을 생성할지 여부
  • secret : 세션 관리 시 클라이언트에게 보내는 쿠키. 즉 세션 쿠키를 의미. 쿠키를 안전하게 전송하기 위해 서명을 추가해야 하고, 쿠키를 서명하는데 필요한 secret 값이 사용된다. 세션 쿠키의 이름은 connet.sid로 기본 설정된다.
  • cookie : 세션 쿠키에 관한 설정이다. 위에서 살펴본 cookie의 일반적인 옵션을 제공한다. 
  • store : 세션을 저장할 저장소를 설정한다. 일반적으로 레디스를 사용한다. 

 

 

express 미들웨어 정리

 

express 미들웨어는 req, res, next를 인자로 받는다. 에러 처리 미들웨어는 예외적으로 err, req, res, next 4개의 인자를 받는다. 미들웨어는 app.use, app.get, app.post 등으로 로드한다. 특정 메소드와 특정 path 에서 작동시킬 수 있다. 

 

app.get('/user', (req, res, next)=>{
	console.log("미들웨어 실행");
})

 

여러개의 미들웨어를 한번에 로드할 수도 있다. 다음 미들웨어로 넘어가기 위해서는 next 함수를 호출해야 한다. 모든 미들웨어들은 자체적으로 next 를 호출하기 때문에 연속적으로 사용할 수 있다. 

 

app.use(moargan('dev'),
		express.static(path.join(__dirname, 'public')),
		express.json(),
		urlencoded({extended: false}),
		cookieParser(process.env.COOKIE_SECRET),
		session({
            resave : false,
            saveUninitialized : false,
            secret : process.env.COOKIE_SECRET,
            cookie:{
                httpOnly : true,
                secure : false,
            },
            name : 'session-cookie',
		})
)

 

다만 express.static 미들웨어 는 정적 파일을 제공할 때 next 대신 res.sendFile을 사용하기 때문에 정적파일을 제공하는 경우 express.json, express.urlencoded, cookieParser 미들웨어는 실행되지 않는다. 즉, 미들웨어 연결 순서에 따라 미들웨어가 실행되지 않을 수도 있다는 말이다. 

 

만약 미들웨어 실행 후 next 함수가 호출되지 않으면 클라이언트는 어떤 응답도 받지 못한다. 

 

다음 미들웨어를 호출하는 next 함수에 인자를 전달하면 해당 미들웨어로 바로 이동한다. next(router)를 호출하면 Router 미들웨어로 바로 이동하고 다른 인자를 넣으면 ErrorHandler 미들웨어로 바로 이동한다. 라우터에서 에러가 발생하는 경우 에러를 next(err)를 통해 호출하면 ErrorHandler 함수의 인자로 전달된다.

 

 

미들웨어 간 데이터를 전달할 수 있다. 세션 미들웨어에 req.session 객체에 데이터를 넣을 수 있지만 요청이 끝날 때 까지만 데이터를 유지하고자 한다면 req 객체에 데이터를 넣어두고 사용할 수 있다. 현재 요청이 처리되는 동안 req.data를 통해 미들웨어 간 데이터를 공유할 수 있게 된다. 

 

app.use((req, res, next)=>{
	req.data = "임시 데이터";
    next();    
}, (req, res, next)=>{
	console.log(req, data);
    next();
})

 

 

Reference

 

 

 

[Web Server] fetch API 사용하는 방법

fetch API 사용하는 방법 fetch는 프론트에서 서버로 리소스를 요청하기 위해서 Javascript에 내장된 Promise 함수다. 기본적으로 비동기 태생을 지닌 녀석이기 때문에 Javascript가 아닌 Node.js나 웹 브라우

about-tech.tistory.com

 

 

[Node.js] Express 미들웨어 함수

미들웨어(Middleware) 함수 Express는 자체적인 최소한의 기능을 가진 라우팅 및 미들웨어 웹 프레임워크다. Express 애플리케이션은 일련의 미들웨어 함수를 호출한다. 미들웨어 함수란 요청 오브젝트

about-tech.tistory.com

 

 

[Programming] CORS란? (preflight, OPTIONS 메소드?)

CORS가 도입된 배경 CORS가 도입되기 전 클라이언트들은 서버에서 제공하는 리소스를 가지고만 서버와 통신을 진행했다. 이 경우 서버와 클라이언트의 origin은 동일했기 때문에 굳이 CORS를 도입하

about-tech.tistory.com

 

댓글