JUINTINATION

Node.js와 Express.js 가볍게 입문해보기 - 2 본문

StudyNote

Node.js와 Express.js 가볍게 입문해보기 - 2

DEOKJAE KWON 2024. 1. 14. 20:14
반응형

지난 도커(Docker) 가볍게 입문해보기 글에서 도커의 가벼운 입문 말고 Express.js 기본적인 CURD API 만들어보기, Middleware에 대한 개념 이해하기 등의 과제를 받았다고 언급했었다. 이 글에서는 Express.js에 관련된 내용을 적을 것이다.

지난 Node.js와 Express.js 가볍게 입문해보기 - 1에서 이어지는 내용이며 해당 글은 이 링크로 들어가면 확인할 수 있다.

 

Node.js와 Express.js 가볍게 입문해보기 - 1

지난 도커(Docker) 가볍게 입문해보기 글에서 도커의 가벼운 입문 말고 Express.js 기본적인 CURD API 만들어보기, Middleware에 대한 개념 이해하기 등의 과제를 받았다고 언급했었다. 이 글에서는 Node.js

juintination.tistory.com

이전 글과 마찬가지로 일단은 Express.js 작동 원리와 같은 개념적인 내용은 최대한 패스하고 바로 코드적인 내용을 다룰 것이다.

Express.js란?

Node.js루비자바에 비해 상대적으로 역사가 짧은 플랫폼이다. Express.js는 웹 애플리케이션을 생성하는 데 가장 많이 사용되는 Node.js의 프레임워크로 Node.js의 핵심 모듈인 httpConnect 컴포넌트를 기반으로 하는 웹 프레임워크이다.

그러한 컴포넌트를 미들웨어(middleware)라고 하며, 설정보다는 관례(convention over configuration)와 같은 프레임워크의 철학을 지탱하는 주춧돌에 해당한다. 즉, 개발자들은 특정 프로젝트에 필요한 라이브러리를 어떤 것이든 자유롭게 선택할 수 있으며, 이는 개발자들에게 유연함과 수준 높은 맞춤식 구성을 보장한다.

오직 Node.js의 핵심 모듈만을 이용한 애플리케이션을 작성할 경우 다음과 같은 유사한 작업들을 위해 동일한 코드를 반복해서 작성하게 될 가능성이 크다.

  • HTTP 요청 바디 피싱
  • 쿠키 파싱
  • 세션 관리
  • URL 경로와 HTTP 요청 메소드를 기반으로 한 복잡한 if 조건문을 이용한 라우트(route) 구성
  • 데이터 타입을 기반으로 적절한 응답 헤더 결정

Express.js는 이러한 문제를 비롯해 다른 여러 문제를 해결함과 동시에 웹 앱에 MVC 형태의 구조를 제공한다. 이 같은 앱은 백엔드만 갖춘 REST API에서부터 온갖 기능을 제공하는 고도로 확장 가능한 풀스택(jade-browserSocket.IO를 포함하는) 실시간 웹 앱에 이르기까지 다양하다.

다음의 라우터가 두 개인 REST 예제를 살펴봤을 때 (REST) API 서버, 즉 엔드 포인트(end point) 두 개만이 존재하며 이들을 라우트(route)라고 한다. 지속성을 위해 "userland"/external에 위치한 MongoDB 네이티브 드라이버 모듈을 사용한다.

var http = require('http');
var util = require('util');
var querystring = require('querystring');
var mongo = require('mongodb');

var host = process.env.MONGOHQ_URL ||
    'mongodb://@127.0.0.1:27017';
mongo.Db.connect(host, function(error, client) {
    if (error) throw error;
    var collection = new mongo.Collection(
        client,
        'test_collection'
    );
    var app = http.createServer(
        function (request, response) {
            if (
                request.method === 'GET' &&
                request.url === '/messages/list.json'
            ) {
                collection.find().toArray(function(error, results) {
                    response.writeHead(
                        200,
                        {'Content-Type': 'text/plain'}
                    );
                    console.dir(results);
                response.end(JSON.stringify(results));
            });
        };
        if (
            request.method === 'POST' &&
            request.url === "/messages/create.json"
        ) {
            request.on('data', function(data) {
                collection.insert(
                    querystring.parse(data.toString('utf-8')),
                    {safe: true},
                    function(error, obj) {
                        if (error) throw error;
                        response.end(JSON>stringify(obj));
                    }
                };
            });
        };
    });
    var port = process.env.PORT || 5000;
    app.listen(port);
})

위의 코드에서 볼 수 있듯이 개발자들은 HTTP 메서드와 URL을 라우트로 해석하기, 입력 데이터와 출력 데이터를 파싱하기와 같은 많은 일들을 직접 해야 한다.

Express.js는 이러한 문제들 뿐만 아니라 추상화 및 코드 구성과 같은 별도의 문제들까지 해결한다. 프레임워크는 뷰, 라우트, 모델을 분명하게 분리시켜 모델-뷰-컨트롤러(Model-Veiw-Controller, 이하 MVC)와 같은 구조를 제공한다.

Express.js 동작 방식

보통 Express.js는 main 파일이라는 진입점이 있다. 대부분의 경우가 node 커맨드를 사용해서 시작할 때 사용하는 파일이ㅏㄷ. 일부 경우에는 모듈로 내보내기도 한다. 이 파일에서는 다음 작업을 실시한다.

  1. 컨트롤러, 유틸리티, 헬퍼, 모델과 같은 자체 모듈뿐만 아니라 서드파티 의존 모듈을 포함시킨다.
  2. 템플릿 엔진과 파일 확장명과 같은 Express.js 애플리케이션 설정을 정한다.
  3. MongoDB, Redis, MySQL과 같은 데이터베이스에 연결한다(선택).
  4. 에러 핸들러, 정적 파일 폴더, 쿠키, 그 외 파스와 같은 미들웨어를 정의한다.
  5. 라우트를 정의한다.
  6. 애플리케이션 구동을 시작한다.
  7. 애플리케이션을 모듈로 내보닌다(선택).

Express.js 애플리케이션이 실행되고 있을 때 수신된 각 요청들은 미들웨어와 라우트들이 정의한 순서대로 처리된다. 이 부분은 실행 흐름을 제어하는 데 굉장히 중요하다. 파일에서 위쪽에 위치한 라우트나 미들웨어는 아래쪽에 위치한 것들보다 우선순위가 높다.

또한 각각의 HTTP 요청을 여러 개의 미들웨어 함수가 처리할 수 있다. 그래서 일부 함수들은 중간에 위치한다.

(그래서 미들웨어라고 부른다.)

다음은 미들웨어 용도의 예다.

  1. 쿠키 정보를 파싱해서 req 객체에 저장
  2. URL로부터 파라미터를 파싱해서 req 객체에 저장
  3. 사용자가 인증되면(쿠키 또는 세션) 파라미터 값을 기반으로 데이터베이스의 정보를 가져와 req 객체에 저장
  4. 사용자 또는 요청 인증(하지 않을 수도 있음)
  5. 데이터를 출력하고 응답을 마침

Express.js 설치

먼저 전역 패키지의 Express.js 제너레이터를 설치한다. 나는 $ npm install -g express-generator 커맨드를 실행하여 설치했다. 설치가 완료되면 $ express --version 커맨드를 통해 다음과 같이 설치된 버전을 확인할 수 있다. 물론 $ npm install -g express-generator@[원하는 버전] 커맨드로 원하는 버전의 express-generator를 설치할 수도 있다.

현재 express의 버전은 4.16.1이다.

이후에 원하는 위치에 $ mkdir express-cli 커맨드를 실행하여 사용할 프로젝트 폴더를 생성한다. 이후에 $ cd express-cli 커맨드를 통해 해당 폴더로 이동한 후에 프로젝트 폴더 내부에서 $ npm init 커맨드를 실행하여 package.json을 생성한다.

나는 위와 같이 작성하였고 다음과 같이 정상적으로 package.json 파일이 생성된 것을 확인할 수 있다.

마지막으로 NPM을 이용하여 모듈을 설치해야 한다. 나는 $ npm install -g express 커맨드를 실행하여 설치했다. 설치가 완료되면 $ express 커맨드로 나중에 새로운 앱을 만들 때 CLI에 접근할 수 있다. 물론 아까와 같이 $ npm install -g express@[원하는 버전] --save 로 원하는 버전의 express를 설치할 수도 있다.

Express.js 스캐폴딩

프로토타이핑의 경우 탄탄한 애플리케이션의 뼈대로 빠르게 시작하는 것이 굉장히 중요하다. 때문에 많은 현대 프레임워크가 일종의 스캐폴딩(Scaffolding)을 제공한다. Express.js는 개발 프로세스를 위해 CLI와 함께 제공되는데 CLI는 대부분의 흔히 있는 경우들을 위한 기본 토대를 생성한다.

Express.js 뼈대 애플리케이션을 생성하려면 터미널 커맨드 $ express [options] [dir|appname] 을 실행해야 한다. 사용할 수 있는 옵션은 다음과 같다.

  • -e, --ejs: EJS 엔진 지원 추가
  • -H, --hogan: Hogan.js 엔진 지원 추가
  • -c [engine], --css [engine]: LESS, Stylus, Compass와 같은 스타일시트 지원 추가
  • -f, --force: 비어있지 않은 디렉터리에 강제적으로 애플리케이션을 생성

Express.js는 dir/appname 옵션이 생략되었을 경우 현재 폴더를 프로젝트의 베이스로 삼아 파일들을 생성한다.

커맨드와 옵션들을 확인했으니 이번에는 스캐폴딩을 이용해서 애플리케이션을 생성해보자.

  1. 애플리케이션 생성 코드는 변경되기가 쉬우므로 Express.js 버전을 확인한다.
  2. 옵션과 함께 스캐폴딩 커맨드를 실행한다.
  3. 로컬에서 애플리케이션을 실행시킨다.
  4. 라우트, 미들웨어, 설정과 같은 다른 절들을 이해한다.
  5. Jade 템플릿을 살펴본다.

Express.js 커맨드라인 인터페이스

이제 새로운 Express.js 애플리케이션을 만들기 위해 CLI를 사용할 수 있게 되었다. 예를 들면 Stylus를 사용하는 애플리케이션을 생성하려면 $ express -c styl express-styl 커맨드를 입력한 후에 다음 커맨드를 실행한다.

$ cd express-styl && npm install
$ DEBUG=my-application ./bin/www

Express.js 제너레이터 v4.0.0을 이용한 express-styl/app.js의 전체 코드는 생략한다.

Express.js의 라우트

express-style/app.js를 보면 중간에 두 개의 라우트를 볼 수 있을 것이다.

app.use('/', routes);
app.use('/users', users);

첫 번쨰 라우트는 기본적으로 http://localhost:3000/과 같은 홈페이지에 대한 모든 요청을 처리한다.

두 번째 라우트는 http://localhost:3000/users와 같은 /users에 대한 요청들을 처리한다.

두 개의 라우트 모두가 URL을 처리할 때 대소문자를 구분하지 않으며 맨 끝에 위치한 슬래시(/)가 존재한다고 여긴다.

Express.js는 기본적으로 다음과 같이 쿼리 문자열을 포함한 라우팅을 개발자들이 허용하지 않는다.

GET: www.webapplog.com/?id=10233
GET: www.webapplog.com/about/?author=10239
GET: www.webapplog.com/books/?id=10&ref=201

하지만 다음과 같이 자체 미들웨어를 작성하는 것은 필수다.

app.use(function (req, res, next) {
    if (req.query.id && req.query.ref) {
        // id와 ref가 존재하는 경우에만 처리한 다음 next()를 호출
    } else if (req.query.author) {
        // id와 동일하게 처리
    } else if (req.query.id) {
        // id를 처리
    } else {
        next();
    }
});

app.get('/about', function (req, res, next) {
    // 이 코드는 쿼리 문자열 미들웨어 다음에 실행
}

요청 핸들러 자체(예: index.js)는 복잡하지 않다. HTTP 요청의 모든 내용은 req에 있으며 응답 결과를 res에 작성한다.

exports.list = function(req, res) {
    res.send('respond with a resource');
};

Express.js의 중추인 미들웨어

app.js 라우트 앞의 각각의 라인 또는 문장은 미들웨어다.

var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
// ...
app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

미들웨어는 유용한 동작을 하거나 요청이 실행되는 데 도움이 되는 무언가를 추가하는 패스스루(pass-through) 함수가 있다. 예를 들면 bodyParser()와 cookieParser()는 각각 HTTP 요청 페이로드(req.body)와 쿠키 데이터(req.cookie)를 추가한다. 그리고 app.js에서 app.use(logger('dev'));는 터미널에 각각의 요청에 대한 로그를 끊임없이 출력한다. 또한 generator를 선언하고 포함시켰으며 static-favicon, morgan, cookie-parser, body-parser와 같은 모듈들을 추가로 설치했다.

Express.js 애플리케이션 설정

일반적인 Express.js 애플리케이션(app.js 파일)에서 설정을 추가하는 방법은 다음과 같다.

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

이후에 bin/www 파일을 다음과 같이 작성한다.

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

일반적으로 설정할 때는 views와 같은 이름과 ㅅemplates 또는 views가 위치한 폴더 경로인 path.join(__dirname, 'views')와 같은 값을 설정한다.

Express.js와 Node.js를 위한 Haml, Jade

Jade 템플릿 엔진은 layout.jade와 같이 공백과 들여쓰기를 사용한다는 점에서 루비 온 레일스의 Haml과 유사하다.

doctype html
html
    head
        title=title
        link(rel='stylesheet', href='/stylesheets/style.css')
    body
        block content

그 외에는 Jade 템플릿 안에 접두사 -을 사용하여 완전한 자바스크립트 코드를 사용하는 것이 가능하다.


결론

앞서 보았듯이 Express.js를 이용해서 웹 애플리케이션을 생성하는 것은 어렵지 않으며 프레임워크는 REST API 구현도 훌륭히 해낸다는 것을 알 수 있다. 하지만 생각보다 정리할 양이 많아 여기서 글 작성을 끝내야겠다. 다음 글은 간단한 예시 프로젝트 구현 또는 Express.js 기본적인 CURD API 만들어보기가 될 것 같다.

728x90
Comments