2019. 07. 15 수정


이번 글에서는 express-generator 모듈을 통해 생성된 스켈레톤 폴더에 대해 알아보도록 하겠습니다.

특히 app.js를 중점적으로 살펴보도록 하겠습니다.



1. 폴더, 파일의 주요 역할

express-generator로 애플리케이션을 생성하면, 아래와 같은 폴더/파일들이 자동으로 추가됩니다.



  • bin 폴더
    • 프로그램의 실행과 관련된 파일이 있는 폴더입니다.
    • www 파일을 실행해서 서버가 실행됩니다.
  • pulbic 폴더
    • JS, CSS, img 파일 등 리소스 파일이 있는 폴더
  • routes 폴더
    • 라우터와 관련된 모듈이 있는 폴더
  • views 폴더
    • ejs 파일과 같은 템플릿 파일이 있는 폴더
  • app.js 파일
    • 애플리케이션에서 중심이 되는 파일
    • 서버 설정 / 미들웨어 정의 / 라우트 정의 등 여러가지를 설정하고, 서버 운영을 위한 로직을 작성합니다.
  • package.json
    • 현재 애플리케이션과 관련된 정보와 모듈을 설치하는데 필요한 의존성이 작성된 파일


각 폴더/파일의 큰 역할을 위와 같으며, 이제 중요한 부분들에 대해서 자세히 다루도록 하겠습니다.





2. /bin/www.js

이 파일은 서버를 실행시키는 파일입니다.

커맨더 창에서 npm start 명령어를 실행하면, 이 파일이 실행되는 것입니다.


또한 /bin/www.js 파일에서 Node 서버가 실행되는 포트 설정과 서버의 이벤트 핸들링을 작성할 수 있습니다.

1) 포트 설정

... 
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);
...


2) 이벤트 핸들링

...
/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
...





3. app.js

app.js 는 프로젝트의 중심이 되는 파일로서, express의 환경 설정, 미들웨어 등록 등 서버 운영에 필요한 코드를 작성합니다.

코드를 보면서 구성을 살펴보도록 하겠습니다.


1) 모듈 선언 부분

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
...

미들웨어를 등록하기 위한 모듈들을 호출하여 선언합니다.





2) 서버 설정 부분

...
var app = express();

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

    • 뷰 페이지의 폴더 기본 경로로 __dirname + views 이름의 폴더를 사용하겠다는 의미입니다.

    • 예를 들어, express-generator로 애플리케이션 명을 foo로 작성했을 경우, foo/views 폴더가 뷰 폴더의 기본 경로가 됩니다.

  • app.set('view engine', 'ejs');

    • 뷰 엔진으로 ejs를 사용하겠다는 의미입니다.

    • express-generator로 프로젝트를 생성할 때 express 프로젝트명 --view=ejs 과 같이 ejs 엔진을 선택했다면, view engine으로 ejs가 설정됩니다.





3) 미들웨어 등록 부분

...
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');
});
...

개발에 필요한 기본적인 미들웨어들은 express-generator가 자동으로 등록을 해줍니다.

추가적으로 미들웨어가 필요하다면 이곳에서 작성을 할 수 있습니다.





4) 새로운 미들웨어(라우터) 등록

router 함수도 미들웨어 이므로, 한 번 새로운 미들웨어를 작성해보겠습니다.


처음 express-generator로 애플리케이션을 생성하면 routes 폴더에 index.js , users.js 파일밖에 없는데요,

routes 폴더에 추가적으로 test.js 파일을 생성하도록 하겠습니다.

/routes/test.js

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

router.get("/hahaha", function(req, res, next){
res.send("새로운 라우터가 등록되었습니다.")
})

module.exports = router;

이 모듈의 반환 객체는 router 이어야 함을 꼭 기억해주세요!

그래야 라우터 미들웨어로 등록할 수 있습니다.


다음으로 미들웨어 등록을 위해 app.js에 다음과 같이 작성합니다.

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

...

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/test', testRouter);
  • testRouter
    • routes 폴더의 test.js 모듈 파일을 호출하여 testRouter 변수에 할당합니다.
  • app.use('/test', testRouter);
    • 라우터 함수인 testRouter 변수의 경로를 /test 라는 가상 경로로 사용하겠다고 선언합니다.


이제 새로운 미들웨어가 잘 동작되는지 테스트를 위해 브라우저에서 localhost:3000/test/hahaha로 접근해보세요.

이렇게 함으로써 기능별로 라우터를 정의할 수 있기 때문에 프로젝트 구조를 쉽게 파악할 수 있고 유지보수가 편해집니다.





5) 프로젝트 폴더를 가상 경로로 설정하기

프로젝트 내의 어떤 폴더를 가상 경로로 설정할 수 있습니다.


Node.js에서는 루트라는 개념이 없고, 상대 경로로써 파일에 접근할 수 있습니다.

그런데 어떤 파일의 경로를 가상 경로로 설정할 수 있기 때문에, 항상 상대 경로로 접근할 필요가 없습니다.

직접 확인해보겠습니다.


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>

<link> 태그 부분에 stylesheets/style.css 파일은 public 폴더 안에 있기 때문에, href 속성의 경로는 public/stylesheets/style.css 가 되어야 할텐데, /stylesheets/style.css가 작성되어 있습니다.

public이 없는데 알아서 잘 css 파일을 찾아가고 있는 상황이죠.


그 이유는 app.js에서 public 폴더에 대한 접근을 가상 경로인 /로 설정했기 때문입니다.

app.js

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


이와 마찬가지로 가상경로를 설정하려면, 다음과 같이 작성할 수 있습니다.

app.use('/upload', express.static('uploads'))

위 코드의 의미는 uploads 폴더에 대한 가상 경로로 /upload를 사용하겠다는 의미입니다.

그러면 개발을 할 때 uploads 폴더에 접근할 일이 있을 때, 상대 경로로 접근하는 것이 아니라 /upload 경로로 접근을 할 수 있습니다.

( 정적 파일 제공에 대한 문서는 여기를 참고해주세요. )





4. router 객체의 렌더링

1) depth가 있는 파일을 응답 페이지로 사용


예를 들어, 위와 같이 views 폴더 아래에 product 폴더가 있는 depth 구조에 edit.ejs 파일이 있다고 가정하겠습니다.

/views/product/edit.ejs

<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>edit.ejs 파일입니다~</h1>
</body>
</html>


그러면 라우터 미들웨어에서는 이 페이지를 렌더링 하기 위해 다음과 같이 작성할 수 있습니다.

/routes/index.js

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

router.get('/', function(req, res, next) {
res.render('product/edit', {
title: "Express"
});
});

module.exports = router;

render() 함수의 파일 경로를 보시면 'product/edit'으로 작성했습니다.

depth 구조라고 해서 특별한 것업이 경로를 그대로 명시해주면 됩니다.


응답이 잘 되는지 브라우저에서 https://localhost:3000로 접근해 확인해보세요.




2) include

어떤 ejs 파일에 다른 ejs 파일을 포함하고 싶을 경우 include 지시어를 사용합니다.


예를 들어, index.ejs 파일에 header.ejs 파일을 포함하고 싶은 경우 다음과 같이 작성할 수 있습니다.

/view/index.ejs

<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<% include header %>

<h1><%= title %></h1>
</body>
</html>

<% include header %>를 사용해서 header.ejs 파일을 include 하는 코드입니다.


/views/header.ejs

<h2> 이건 헤더 파일입니다~ </h2>

이렇게 헤더(header)와 푸터(footer)처럼, 모든 페이지에 동일하게 추가돼야 하는 템플릿인 경우, include를 활용하면 좋습니다.





이상으로 express-generator로 프로젝트를 생성했을 때, 자동으로 추가된 스켈레톤 파일들에 대해 알아보았습니다.