아래 내용은 제가 공부한 내용을 정리한 내용들입니다. 틀린 사항이 있다면 지적 부탁드립니다! 😚
Express
는 Javascript
에서 인기있는 프레임워크 4가지(MongoDB, Express, React, Node) 중 하나에 속할 정도로 유명한 프레임워크입니다.
NodeJS
환경에서 웹 서버 또는 API 서버를 제작하기 위해 사용되는 프레임워크 입니다.
NodeJS 환경에서 HTTP 모듈로만 사용하는 것은 코드의 가독성이 떨어지며, 구현하기 귀찮은 작업들이 다소 있기 때문입니다.(제가 짧게 배운 경험상..)
그렇다면, Express는 HTTP 모듈로 구현하는 것과 어떤 차이가 있길래 Express가 JS의 인기 프레임워크가 됐을까요?
첫 번째로, Middleware
미들웨어 추가가 편리합니다.
두 번째로, 자체 라우터를 제공합니다.
미들 웨어 : 미들웨어는 양 쪽을 연결하여 데이터를 주고 받을 수 있도록 중간에서 매개 역할을 하는 소프트웨어, 네트워크를 통해서 연결된 여러 개의 컴퓨터에 있는 많은 프로세스들에게 어떤 서비스를 사용할 수 있도록 연결해 주는 소프트웨어를 말한다. 3계층 클라이언트/서버 구조에서 미들웨어가 존재한다. 웹 브라우저에서 데이터베이스로부터 데이터를 저장하거나 읽어올 수 있게 중간에 미들웨어가 존재하게 된다. - 출처
미들웨어를 우리 일상에서 찾아보자면, 자동차 제작 공정이 있습니다.
자동차 생산제조 공정은
프레스 - 차체조립 - 도장 - 의장조립 - 검사
이렇게 크게 나눌 수 있고, 모듈 세부적으로 들어가보면
주조 - 단조 - 소결 - 열처리 - 기계 가공 - 조립
등 나뉘어져 있습니다.
완성차를 만들기 위한 일련의 흐름, 하나의 공정들을 미들웨어라고 생각할 수 있을 것 같습니다.
한 마디로, 우리는 온전한 서버를 구현하기 위해 그 서버에 필요한 여러 기능들을 미들웨어로 사용하고, 이 미들웨어의 작동은 항상 다음 미들웨어를 실행합니다.
자주 사용하는 미들웨어는
가 있습니다.
작업 중인 디렉토리의 터미널에 $ npm install cors
를 입력해 설치하여 사용할 수 있습니다.
const cors = require('cors') // 설치한 cors 미들웨어를 가져옵니다.
...
app.use(cors()) // 모든 요청에 대해 CORS 헤더를 적용합니다.
위와 같이 반복적인 헤더 추가 작업을 거치지 않아 생산성을 향상할 수 있습니다.
express.json()
을 사용합니다.
이 미들웨어의 기능은 수신 요청(req
)를 JSON 페이로드로 구조화합니다.
옵션 값을 설정하는 것으로 다양한 방법으로 사용할 수 있습니다.
제가 사용해본 옵션은 아래와 같습니다.
옵션 | 기술 | 기본값 |
---|---|---|
strict | 배열과 객체만 허용 | true |
const express = require('express')
const app = express();
const cors = require('cors');
app.use(cors());
app.use(express.json({strict: false}))
...
app.listen(5000);
위 처럼, 배열과 객체만 JSON으로 구조화 하는 것이 아니라, false 값으로 옵션을 주어 모든 타입의 데이터를 JSON으로 구조화를 했습니다.
이 미들웨어를 사용하면, 예를 들어 이미 로그인 한 사용자인 경우 다음 미들웨어를 실행하고,
아니라면 에러를 보내 회원 가입 페이지로 안내하거나, 로그인 페이지로 돌려보내거나, 접근을 차단할 수 있습니다.
app.use((req,res,next) => {
if(req.headers.token) {
req.isLoggedIn = true;
next()
} else {
res.status(400).send('invalid user')
}
}
위 세 가지 예시에서 알 수 있는 부분은, 미들웨어를 사용하는 데, 그 안에 next()
라는 다음 미들웨어를 실행하는 구문이 없다면, 그 미들웨어 함수 내에 next()
가 내장되어있다는 것을 알 수 있습니다.
어떤 데이터를 GET으로 읽거나, POST로 추가하거나 등 사용자가 어떤 작업을 수행하는 지 실시간으로 판단하고자 할 때, 로그를 찍어 확인할 수 있습니다.
const myLog = function (req, res, next) {
let logText = 'http request method is ' + req.method + ', url is ' + req.url;
console.log(logText);
next()
}
app.use(myLogger);
위와 같이 미들웨어 함수를 작성하고, app.use()
로 실행하면 사용자의 요청 메소드와 요청 URL을 콘솔 창에서 확인할 수 있습니다.
위 로거(Logger)는 가장 단순한 미들웨어이며, 간단한 디버깅에 사용할 수 있습니다.
라우팅(Routing)은 메소드와 URL로 분기점을 만드는 것입니다.
즉, 클라이언트는 특정한 HTTP 요청 메소드(GET, POST 등)나 서버의 특정 URI 또는 경로로 HTTP 요청을 보냅니다.
라우팅은 클라이언트 요청에 해당하는 엔드포인트와 메소드에 따라 서버가 응답하는 방법을 결정하는 것입니다.
기존에 우리가 Node.JS로 HTTP 모듈을 사용할 땐
const requestHandler = (req, res) => {
if(req.url === '/lower') {
if (req.method === 'GET') {
res.end(data)
} else if (req.method === 'POST') {
req.on('data', (req, res) => {
// do something...
})
}
}
}
위처럼 if
조건문을 사용해 구현할 수 있었습니다.
Express 에서 제공하는 라우터 기능을 사용하여 더 직관적인 코드를 작성할 수 있습니다.
const router = express.Router()
router.get('/lower', (req, res) =>{
res.send(data)
})
router.post('/lower', (req, res) =>{
// do something
})
위 예시처럼 Route의 메소드 별, 경로 별로 핸들러 함수를 가질 수 있습니다.
app.METHOD(PATH, HANDLER func);
핸들러 함수는 Route가 일치할 때 실행됩니다.
const express = require('express')
const router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
또한, 위처럼 express.Router()
클래스를 사용해 경로 처리 모듈을 만들 수 있습니다.
express.Router()
로 라우터 모듈을 생성하고, 그 안에 미들웨어 기능을 로드합니다.
위 예시로 보면, 현재 시간을 console 창에 출력하는 미들웨어가 있고, GET 요청이 들어왔을 때, 그 경로에 따른 함수를 실행하는 미들웨어가 담겨져 있습니다.
이 라우터 모듈을 메인 앱에 마운트하여 재사용성이 증가된 라우터 모듈을 구현할 수 있습니다. 메인 앱의 목적은 각각 다를 것이고, 그 메인 앱의 루트 경로도 각각 다르기 때문입니다.