본문 바로가기

아키텍처

[Web Server] Express

 

 

  • 학습 내용

express는 웹 모바일 앱을 위한 Node.js 웹 애플리케이션 프레임워크이다. express는 HTTP 유틸리티 메소드 및 미들웨어를 통해 쉽고 빠르게 API를 작성할 수 있다. 즉, express를 활용하면 서버 구축의 측면에서 훨씬 단순하고 직관적으로 할 수 있는 것이다.

 

  • 설치

npm init 명령을 사용하여 먼저 package.json 파일을 작성한 뒤, 이 것을 실행하면 앱 이름 및 버전과 같은 몇 가지 정보에 대해 프롬프트 한다. 이후 npm install express를 통해 해당 디렉토리에 express를 설치할 수 있다. --save 옵션을 사용하면 종속 항목 목록에 저장할 수도 있다.

 

  • Hello world 예제

해당 js파일에 다음과 같은 코드를 작성한다.

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

이것을 node app.js를 통해 실행하면 3000번 포트에서 연결한다. 여기서 루트URL(/) 또는 라우트에 대한 요청으로 'Hello Wolrd!'로 응답할 것이다. 다른 모든 경로는 404 Not Found로 응답한다.

 

  • 기본 라우팅

라우팅은 URI(또는 경로) 및 특정한 HTTP 요청 메소드(GET, POST 등)인 특정 엔드포인트에 대한 클라이언트 요청에 앱이 응답하는 방법을 결정하는 것을 말한다. 각 라우트는 하나 이상의 핸들러 함수를 가질 수 있고, 이러한 함수는 라우트가 일치할 때 실행된다. 라우트는 이러한 구조로 작성한다.

app.METHOD(PATH, HANDLER)

app은 express의 인스턴스이고, METHOD는 HTTP 요청 메소드이다. PATH는 서버의 경로이고, HANDLER는 라우트가 일치할 때 실행되는 함수이다. 

app.get('/', function (req, res) {
  res.send('Hello World!');
});

 

  • Express에서 정적 파일 제공

이미지나 CSS파일 및 JavaScript파일과 같은 정적 파일을 제공할 때는 Express의 기본 제공 미들웨어 함수인 express.static을 사용하면 된다. 정적 파일이 포함된 디렉토리의 이름을 express.static 미들웨어 함수에 전달하면 파일의 직접적인 제공을 할 수 있다. 예를 들면 이런 식이다.

app.use(express.static('public'));

이렇게 하면 public 디렉토리 안에 있는 파일을 불러올 수 있다. 아래와 같이 호출하면 public 디렉토리에 있는 파일을 응답할 것이다.

http://localhost:3000/images/kitten.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js

여러 개의 정적 자산 디렉토리를 이용할 때는 express.static 미들웨어 함수를 여러 번 호출하면 된다. 

 또 express.static 함수를 통해 제공되는 파일에 가상 경로를 작성할 수도 있다. 예를 들면,

app.use('/static', express.static('public'))

이렇게 쓰면 아래의 가상 경로로부터 파일을 불러올 수 있다.

http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js

그러나 함수에서 이렇게 작성하여 만드는 경로는 node 프로세스가 실행하는 원래 디렉토리에 대해 상대적이기 때문에 이왕이면 절대 경로를 사용하는 것이 안전하다.

app.use('/static', express.static(_dirname + '/pubilc'));

 

  • 라우팅

라우팅이란 앱 엔드 포인트(URI)를 정의하고 그 URI가 클라이언트 요청에 응답하는 방식을 의미한다. 기본적으로 라우트 메소드는 HTTP 메소드를 따온다. 

// GET 메소드 라우트
app.get('/', function(req, res) {
  res.send('GET request to the homepage');
});

// POST 메소드 라우트
app.post('/', function(req, res) {
  res.send('POST request to the homepage');
});

여기에 있어 사용 가능한 메소드는 아래와 같다.

get post put head
delete options trace copy
lock mkcol move purge
propfind proppatch unlock report
mkactivity checkout merge m-search
notify subscribe unsubscribe patch
search connect    

단, all의 경우 HTTP 메소드를 불러오지 않는다. 단지 모든 요청 메소드에 대해 한 경로에서 미들 웨어 함수를 불러오는 데 사용된다.

app.all('/secret', function (req, res, next) {
  console.log('Accessing the secret section ...');
  next();
});

 

  • 라우트 핸들러

라우트는 콜백 함수를 제공하여 요청을 처리할 수 있다. 이것은 미들웨어와 유사하다. 유일한 차이점은 next('route')를 호출하여 나머지 라우트 콜백을 우회할 수 있다는 것이다.

app.get('/example/a', function(req, res) {
  res.send('Hello from A!');
});

2개 이상의 콜백 함수를 하나의 라우트에서 처리할 수 있다. 그러나 반드시 next오브젝트를 지정해줘야 한다.

app.get('/example/b', function(req, res, next) {
  console.log('the response will be sent by the next function ...');
  next()
}, function (req, res) {
  res.send('Hello from B!');
});

배열을 사용해서 여러 콜백 함수를 하나의 라우트로 처리할 수도 있다.

let cb0 = function(req, res, next) {
  console.log('CB0');
  next()
}
let cb1 = function (req, res, next) {
  console.log('CB1')
  next()
}
let cb2 = function (req, res, next) {
  console.log('Hello from C!')
}
app.get('/example/c', [cb0,cb1,cb2]);

마찬가지로 배열과 함수를 같이 사용해서 콜백 함수를 라우트로 처리할 수도 있다.

let cb0 = function(req, res, next) {
  console.log('CB0');
  next();
}
let cb1 = function(req, res, next) {
  console.log('CB1');
  next();
}
app.get('/example/d', [cb0, cb1], function(req, res, next) {
  console.log('the response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from D!');
});

 

  • app.route()

app.route()를 이용하면 라우트 경로에 대해서 체인이 가능하다. 경로를 한 곳으로 지정하고 모듈식으로 라우트를 작성하면 된다. 

app.route('/book')
  .get(function(req,res) {
    res.send('Get a random book');
  })
  .post(function(req,res) {
    res.send('Add a book');
  })
  .put(function(req, res) {
    res.send('Update the book');
  });

 

  • express.Router

express.Router 클래스를 사용하면 모듈식으로 사용 가능한 핸들러를 만들 수 있다. Router 인스턴스는 미들웨어이자 라우팅 시스템이다. 그래서 미니 앱(mini-app)으로 불리기도 한다. 예를 들면 birds.js라는 이름의 라우터 파일을 앱 디렉토리에 작성한다.

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

router.use(function timeLog(req, res, next) {
  console.log('Time: ', Data.now());
  next();
});
router.get('/', function(req,res) {
  res.send('Birds home page');
});
router.get('/about', function(req, res) {
  res.send('About birds');
});

module.exports = router;
// 앱 내에서 라우터 모듈을 불러올 수 있다.
let birds = require('./birds')
app.use('/birds', birds);

 

  • 미들웨어

미들웨어 함수란 요청 오브젝트(req), 응답 오브젝트(res), 그리고 앱의 요청-응답을 주고받는 동안 그다음의 미들웨어 함수에 대한 액세스 권한을 갖는 함수를 말한다. 쉽게 말하면 요청-응답을 하는 동안 작동하는 함수라고 할 수 있다.

일단 미들웨어 함수를 불러오려면 미들웨어 함수를 지정하여 use를 사용하여 호출한다. 이렇게 사용하면 미들웨어 함수를 불러올 수 있다.

let express = require('express');
let app = express();

let myLogger = function (req, res, next) {
  console.log('LOGGED');
  next();
};

app.use(myLogger);

app.get('/', function(req, res) {
  res.send('Hello World!');
});

app.listen(3000);

이렇게 쓰면 앱이 요청을 수신할 때마다 미들웨어 함수를 사용한다. 위의 경우 불러올 때마다 LOGGED를 불러올 것이다. 또한 미들웨어는 순서에 따라 불러져온다.

 

  • express의 요청 객체(Resquest) 메소드
req.params 이름 붙은 라우트 파라미터를 담음. ex) app.get('/:id', (req,res) => { res.send(req.params.id) }
req.query GET 방식으로 넘어오는 쿼리 스트링 파라미터를 담고 있음
req.body POST 방식으로 넘어오는 파라미터를 담고있음. HTTP의 BODY부분에 담겨져있는데, 이부분을 파싱하기 위해 body-parser와 같은 패키지가 필요함
req.route 현재 라우트에 관한 정보를 보여줌. 디버깅용.
req.cookies
(req.signedCookes)
클라이언트가 전달한 쿠키 값을 가짐.
req.headers HTTP의 Header 정보를 가지고 있음.
req.accepts([types]) 클라이언트가 해당하는 타입을 받을 수 있는지 확인하는 메소드.
req.ip 클라이언트의 ip address.
req.path 클라이언트가 요청한 경로, 프로토콜, 호스트, 포트, 쿼리스트링을 제외한 순수 경로를 불러온다.
req.host 요청 호스트 이름을 반환하는 간단한 메소드. 보안의 위험이 있어 사용에 유의해야한다.
req.xhr 요청이 ajax 호출로 시작되었으면 true를 반환함.
req.protocol 현재 요청의 프로토콜을 출력함.
req.secure 현재 요청이 보안 요청이면 true를 반환함.
req.url
(req.originalUrl)
URL경로와 쿼리 스트링을 반환. 원본 요청을 logging하는 목적으로 많이 쓰임.
req.acceptedLanguages 클라잉너트가 선호하는 자연어 목록을 반환함. header에서 파싱하면 다국어를 지원하는 앱이면 초기 언어 선택에 도움을 줄 수 있다.

 

  • express의 응답 객체(Response) 메소드
res.status(code) HTTP응답 코드를 설정한다. 응답 코드가 redirect(30x)라면, res.redirect를 쓰는게 나음.
res.set(name, value) 응답 헤더를 설정함.
res.cookie(name,
value, [option])
클라이언트에 저장될 쿠키를 설정하거나 제거한다. cookie-parser 패키지가 필요함.
res.redirect([status],url) redirect. 기본 응답 값은 302이다.
res.send(body),
res.send(status, body)
클라이언트에 응답을 보낸다. 상태 코드의 경우 옵션으로 넣을 수 있다. 기본 콘텐츠 타입은 text/html이다. 그래서 text/plain을 보내려면 res.set('Content-Type', 'text/plain')을 먼저 호출해야 한다. JSON을 보낼때는 res.json을 사용하는 것이 좋다.
res.json(json),
res.json(status, json)
클라이언트로 JSON 값을 보냄.
res.jsonp(json),
res.jsonp(status, json)
클라이언트로 JSONP 값을 보냄.
res.type(type) Contents-Type 헤더를 설정할 수 있는 간단한 메소드.
res.format(object) Accept 요청에 따라 다른 콘텐츠를 전송할 수 있는 메소드.
res.attachment([filename]),
res.download(path, [filename],[callbakc])
클라이언트에게 파일을 표시하지 말고 다운로드 받으라고 전송함. filename을 주면 파일 이름이 명시되며, res.attachment는 헤더만 설정하므로 다운로드를 위한 node코드가 따로 필요함.ion
res.sendFile(path, [option], [callback]) path의 파일을 읽고 해당 내용을 클라이언트로 전송한다.
res.links(links) Links 응답 헤더를 설정한다.
res.locals,
res.render(view, [locals], callback)
res.lacals는 뷰를 렌더링하는 기본 콘텍스트를 포함하는 객체다.
res.render는 jade와 같은 템플릿 엔진을 사용하여 뷰를 렌더링한다.

'아키텍처' 카테고리의 다른 글

[배포] Amazon Web Service  (0) 2022.06.29
네트워크 심화  (0) 2022.06.27
클라이언트 빌드와 배포  (0) 2021.12.08
[Web Server] 기초  (0) 2021.12.08
네트워크 기초  (0) 2021.11.03