2019. 07. 15 수정


1. Epxress란?

Node는 Express, Koa, Hapi와 같은 프레임워크를 사용하여 개발할 수 있습니다.

위의 프레임워크들이 개발에 필요한 다양한 기능들을 지원하기 때문에, 개발 생산성이 높아지기 때문이죠.


시중( 2017년 11월 기준 )의 Node.js 책을 보시면 Express에 대한 내용은 있는데, 저는 이것이 왜 프레임워크인지 이해를 하지 못했습니다.

그냥 쌩 Node를 사용하는 것과 별반 차이가 없었습니다.

프레임워크라면 개발에 필요한 폴더와 파일들을 생성해주고(스켈레톤), 기본적인 설정도 되어있는 것이라고 생각을 했었으니까요.


그래서 검색을 해보니, 제가 원하는 대로 뼈대가 잡혀있는 프로젝트를 생성하려면 express-generator 모듈을 사용해야 한다고 합니다.

이 글에서 express-generator 모듈을 통해 프로젝트를 생성하는 방법에 대해 알아보겠습니다.





2. Express 프레임워크 생성하기

express-generator 모듈로 프로젝트를 생성하면 프로젝트 뼈대를 만들어 줍니다.


1) express-generator 모듈 설치

우선 exporess-generator 모듈은 내장 모듈이 아니므로, npm을 통해 모듈을 설치해야 합니다.

# npm install –g express-generator


-g 옵션은 global을 의미합니다.

global로 설치한 모듈은 커맨더 창 어디에서든 명령어를 실행할 수 있으므로, express-generator 모듈을 설치하면 커맨더 창에서 express라는 키워드를 사용할 수 있습니다.




2) 프로젝트 생성

이어서 express-generator로 HelloExpress라는 이름의 스켈레톤 애플리케이션을 생성합니다.

# express HelloExpress --view=ejs


-e 옵션은 ejs라는 View 엔진을 사용하겠다고 선언한 것입니다.

저는 ejs 방식이 익숙해서 ejs 뷰 엔진을 사용했습니다.

( 기본 값으로 jade를 View 엔진으로 사용합니다. )

  • ejs
    • 태그를 통하여 html문서를 작성합니다.
    • <% %> 와 <%= %>를 이용하여 서버가 전달해준 값을 사용할 수 있습니다.
  • jade
    • 들여쓰기 방식으로 html 문서를 작성합니다.


위의 명령어를 실행하면 HelloExpress 폴더가 생성되며, 그 아래에 또 여러가지 폴더 및 파일들이 생성됩니다. 

각 폴더에 대한 설명은 뒤에서 다루도록 하며, 우선은 박스친 부분의 명령어를 실행해보도록 하겠습니다.



*** error ***

"'express'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다."

이와 같은 에러가 발생하는 이유는, -g 옵션으로 설치한 npm에 대한 경로가 환경변수로 잡혀 있어야 합니다. ( 참고 )

  • C:\Users\{사용자이름}\AppData\Roaming\npm 경로를 시스템 환경 변수의 Path로 설정합니다.
  • Webstorm 터미널에서 실행하신다면, 환경 변수 설정 후 Webstorm을 재시작하시길 바랍니다.




3) npm install

# cd HelloExpress # npm install


npm install은 모듈을 설치할 때 사용하는 명령어입니다.

그런데 지금까지 한 것은 HelloExpress 프로젝트를 만들었을 뿐인데, 어디에 모듈이 정의되어 있는걸까요?



바로 package.json 파일에 dependencies 프로퍼티에 모듈들이 정의되어 있습니다.

이 모듈들은 express-generator가 생성해준 것입니다.


npm install명령어를 실행하면, package.json 파일의 dependencies 프로퍼티를 보고 모듈을 설치합니다.

즉, dependencies 에는 모듈을 사용하겠다고 선언만 한 것이고, 진짜로 설치를 하려면 npm install을 해주어야 합니다.





3. Node 서버 실행

다음으로 Node.js 서버를 실행해보록 하겠습니다.

# npm start

그저 express-generator로 뼈대만 생성했는데도 애플리케이션이 실행됩니다.


다음으로 브라우저에서 http://localhost:3000으로 접근하면, 아래와 같은 페이지를 볼 수 있습니다.

이상으로 express가 잘 설치되었고, 서버가 잘 실행 된다는 것을 확인할 수 있습니다.





4. views 폴더와 routes 폴더

그런데 브라우저에 보이는 "Welcome to Express"는 어디에 작성되어 있는 것일까요?


바로 /views/index.ejs 파일에 작성되어 있습니다.

<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
</body>
</html>

여기서 <%= title %>, title은 서버에서 동적으로 처리하여 클라이언트로 응답을 보낸 값입니다.



그렇다면 서버가 보내준 title 변수의 값은 어디에 작성되어 있을까요?

/routes/index.js 파일을 보면, 아래와 같이 작성되어 있을 것입니다.

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});

module.exports = router;

router.get() 함수의 인자는 다음과 같이 정의됩니다.

  • 첫 번째 인자
    • 요청 URL을 정의합니다.
  • 두 번째 입자
    • 콜백 함수에는 요청 객체인 req, 응답 객체인 res가 정의되어 있습니다.
    • 또 응답할 페이지( index )를 렌더링 하기 위해, render() 메서드를 호출합니다.
      • render() 메서드를 호출 할 때, 렌더링 할 페이지 경로와 함께 동적으로 처리할 변수를 프로퍼티로 하여 객체를 전달합니다.
      • 따라서 index.ejs 파일에 있는 <%= title %>에 Express가 값으로 할당된 것입니다.


그런데 render()를 호출할 때, 경로에는 "index"만 작성했는데, 어떻게 views 폴더에 있는 index.ejs 파일인지 알아서 렌더링을 했을까요?

그 이유는 app.js 파일에서 기본 views 폴더를 views폴더로 지정했기 때문입니다.


// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');




routes 폴더를 보시면 index.js 파일 외에 users.js 파일이 있습니다.


var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});

module.exports = router;

routes/index.js 파일 처럼 URL 매핑이 "/"으로 되어 있네요.


먼저 브라우저에서 http://localhost:3000/users에 접근해보겠습니다.

이번에는 페이지에 "respond with a resource"가 출력 되는 것을 확인할 수 있습니다.

즉, users.js 파일의 라우터 함수에 접근하기 위해서는 URL에 /users를 추가하면 됩니다.



이로 미루어보아,

/routes/users.js 파일 안에 새로운 라우터 함수(get)를 작성 할 경우, localhost:3000/users/~~~~ 와 같이 요청을 하면 응답될 수 있다는 것을 유추할 수 있습니다.

예를 들어, /routes/users.js 파일 안에 아래와 같이 라우터 함수가 추가되면,

router.get("/foo", function( req, res, next ){
res.send('foo~~!');
});

http://localhost:3000/users/foo 와 같이 요청할 수 있습니다.



그러면 routes 폴더에 파일만 추가하면 자동으로 라우터가 등록되는 것일까요?

app.js 파일에 보면 아래와 같은 코드가 있는데, 이런 식으로 작성을 해줘야 라우터가 등록이 됩니다.

...


var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

...


app.use('/', indexRouter);
app.use('/users', usersRouter);


...

이 부분은 다음 글에서 자세히 다루도록 하겠습니다.





5. 미들웨어

router 함수의 콜백 함수를 보면 매개변수로 req, res, next가 있는데, 여기에 미들웨어의 개념이 깔려있습니다.

미들웨어란, 자기가 수행 할 부분을 수행하고 다음 과정으로 진행을 넘기는 것을 의미합니다.


app.js 파일을 보면, app.use() 메서드가 쭉 작성되어 있습니다.

...


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

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

// render the error page
res.status(err.status || 500);
res.render('error');
});

위에 보시는 것들이 미들웨어 입니다. 


형태는 조금 다르지만, routes/index.js 파일에서 사용하는 router 함수 역시 미들웨어 입니다.

요점은 router 미들웨어를 사용할 때 콜백함수에

  • 요청 객체인 req
  • 응답 객체인 res
  • 다음 미들웨어로 진행하라는 next

객체들을 인자로 넘겨줘야 한다는 것입니다.

이로 인해서, res.render() 메서드를 사용할 수 있었던 것입니다.



미들웨어를 사용하는 이유는 어떤 미들웨어에서 req, res 객체에 속성 또는 메서드를 추가했을 때,

다른 미들웨어서도 이전 미들웨어에서 추가한 req, res 객체의 속성 또는 메서드를 사용할 수 있기 때문입니다.


따라서 npm start로 서버가 실행되면 app.js에서 등록된 미들웨어들이 수행이 되고,

/routes/index.js 파일의 router 미들웨어에서는 app.js에서 req, res 객체에 추가했던 프로퍼티, 메서드들을 사용할 수 있게 됩니다.


다시 말하면, app.js 파일에서 어떤 미들웨어가 요청 객체( req )에 A라는 함수를 사용할 수 있도록 추가했다고 가정하겠습니다.

그러면, /routes/index.js 파일의 router 미들웨어에서도 요청 객체( req )에 A라는 함수를 사용할 수 있게 됩니다.





5. 미들웨어 종류

다음으로 미들웨어에는 어떤 것들이 존재하는지 살펴보겠습니다.

아래의 미들웨어 외에도 무수히 많은 미들웨어가 있으며, 단지 어떤 미들웨이들이 있는지 대충 감을 잡기 위한 용도로 정리해봤습니다.

  • router
    • 라우팅 역할을 하는 미들웨어 입니다.
    • URL 경로와 렌더링할 페이지를 작성합니다.
  • static
    • 지정한 폴더에 있는 내용들을 웹 서버 루트 폴더에 모두 올립니다.
  • cookieParser
    • 요청 쿠키를 추출하는 미들웨어입니다.
    • 이 미들웨어를 사용하면 req와 res 객체에 cookies 속성과 cookie() 메서드가 추가되므로, 쿠키를 다룰 수 있게 됩니다.
  • bodyParser
    • POST 요청 데이터를 추출하는 미들웨어입니다.
    • 이 미들웨어를 사용하면 req 객체에 body 속성이 추가됩니다.
  • connect-multipart
    • 일반적인 form 입력 양식의 인코딩 방식
      • application/x-www-form-urlencoded
    • 파일 전송 form 입력 양식의 인코딩 방식
      • multipart/form-data
    • 때문에 body-parser 미들웨어는 파일 전송 인코딩 방식을 지원하지 않으므로, 파일 전송을 위해서는 connect-multipart 미들웨어를 사용해야 합니다





6. req, res 객체

마지막으로 express-generator로 생성한 미들웨어의 req, res 객체에는 어떤 메서드가 존재하는지 살펴보겠습니다.


1) res객체 메서드

  • send( [body] )
    • 매개변수의 자료형에 따라 적절한 형태로 응답
    • 문자열 – HTML
    • 배열 – JSON
    • 객체 – JSON
  • status( [상태코드] ).send( [body] )
    • 상태 코드와 함께 응답
  • json( [body] )
    • JSON 형태로 응답
  • redirect([ body] )
    • 해당 경로로 redirect


2) req객체 메서드

  • params()
    • 라우팅 매개변수를 추출
  • query()
    • 요청 매개변수를 추출
  • headers()
    • 요청 헤더를 추출
  • header()
    • 요청 헤더의 속성을 지정하여 추출
  • accepts( type )
    • 요청 헤더의 Accept 속성을 확인
  • is( type )
    • 요청 헤더의 Content-Type 속성을 확인





이상으로 express-generator와 미들웨어에 대해서 알아보았습니다.

앞으로의 글에서 계속 express-generator로 스켈레톤을 생성할 것이며, 미들웨어 개념도 계속 나오므로 익숙해지실 것이라 생각됩니다.