본문 바로가기

Node, Express Request, Response 객체

by pagehit 2021. 7. 13.
반응형

Express를 이용해 서버를 구축할 대, request object로 시작해 response object로 끝난다. 이 두 객체는 Node에서 비롯됐으며, Express에서 확장되어 사용되고 있다. 

먼저 클라이언트가 어떻게 서버에 페이지를 요청하는지 살펴보자.

URL은 protocol(http, https, file, or ftp), hostname(ex, google.com), port(:3000), path(/about), querystring(?test=1&first=9), fragment(#q=express)로 구성된다.

port: 80은 http, 443은 https로 가정한다. 80이나 443을 사용하지 않으면 1023보다 큰 수를 사용해야 한다. 하나의 서버는 주어진 하나의 포트 넘버와 연관지어질 수 있다.

fragment: server에 전달되지 않으며, 브라우저가 사용한다. 몇몇 single-page application은 fragment를 이용해 naviagtion을 제어한다. 원래 fragment의 용도는 anchor tag(ex, <a id="below">)로 표시되는 문서의 한 부분을 표시하기 위해 사용되었다.

 

HTTP Request methods

HTTP 프로토콜은 request method를 정의하고 클라이언트는 이를 이용해 서버와 이야기한다.

 

Request Headers

페이지를 navigate할 때 URL만 넘겨주는 것이 아니라, 보이지 않는 많은 정보를 매번 넘겨준다. 예를 들면, 어떤 언어로 되어있는 페이지를 넘길지? user agent(브라우저, 운영체제, 하드웨어 정보) 이러한 정보들은 request header로 보내진다. 이는 request object의 headers property로 이용가능하다. 

app.get('/headers', (req, res) => {
    res.type('text/plain')
    const headers = Object.entries(req.headers)
        .map(([key, value]) => `${key}: ${value}`)
    res.send(headers.join('\n'))
})

 

response headers

서버도 마찬가지로 렌더링할 필요가 없는 정보를 포함해 보낸다. 전달되는 정보는 보통 metadata, 서버 정보이다. Content-Type header를 통해 어떤 컨텐트(HTML, 이미지, CSS, JavaScript)를 전송하는지 알려준다. 이외에 response가 압축되어있는지, 어떤 인코딩을 사용하는지 표시한다. 또 얼마나 자원을 cache할지도 알려준다. 이는 웹사이트를 최적화할때 필요하다.

response header는 어떤 종류의 서버인지, 운영체제에 대한 자세한 정보 등을 포함한다. 보안을 고려하는 서버들은 이러한 정보를 제거하거나 잘못된 정보를 주기도 한다. 

// Express의 default X-Powered-By header를 아래의 코드를 disable
app.disable('x-powered-by')

 

Content-Type의 형식은 internet media type이다. 이는 type, subtype, optional parameter로 구성된다. 예를 들어, text/html; charset=UTF-8은 text type, html subtype, UTF-8 문자열 인코딩을 나타낸다. content type, Internet media type, MIME type(Multipurpose Internet Mail Extensions)이 거의 같은 의미로 사용된다.

 

Request Body

response body는 실제 content를 포함한다. 마찬가지로 request body도 body를 가질 수 있다. GET request는 body가 없고, POST request는 있음. POST에서 많이 사용되는 media type은 application/x-www-form-urlencoded이다. 이는 ampersnad로 구분되는 name/value 쌍이다. POST가 파일을 업로드 하려면, media type은 multipart/form-data이며, 이는 더 복잡한 형식이다. Ajax request는 application/json을 body로 사용할 수 있다.

 

Request Object

request handler의 첫 번째 파라미터로 전달되는 request object는 핵심 노드 객체인 http.IncomingMessage 인스턴스로 생명주기를 시작한다. 주로 req이나 request를 이름 짓는다. Express는 더 많은 기능을 추가한다.

 

아래는 Express에서 추가된 request object의 property와 method

req.params - 라우트 파라미터를 포함하는 배열

req.query - querystring 파라미터를 포함하는 name/value쌍 객체

req.body - POST 파라미터를 포함하는 객체, POST 파라미터는 request의 body에서 전달되며, URL에서 querystring 파라미터로 전달되지 않음. req.body를 사용하려면 body content type을 파싱할 수 있는 middleware가 필요.

req.route - 현재 매칭된 라우트에 대한 정보, 라우트 디버깅에 사용한다

req.cookies/req.signedCookies - 클라이언트에서 전달된 쿠키 값을 포함하는 객체

req.headers - 클라이언트에서 받은 request header, 키가 header name이고, value가 header value인 객체, 이는 http.IncomingMessage 객체에서 나온 것이며, Node에서 비롯된 것. Express document에서 찾을 수 없다

req.accpts(types) - 클라이언트가 주어진 type을 받는지 결정하는 메소드(types은 application/json이나 comma-delimited list나 array와 같은 하나의 MIME type이다), public API를 작성하는 사람들에게 필요하며, 모든 브라우저가 기본값으로 HTML을 받는다고 가정한다.

req.ip - 클라이언트의 IP

req.path - protocol, host, port, querystring이 없는 request path

req.hostname - 클라이언트가 제공하는 hostname을 반환하는 메소드, 이 정보는 spoof될 수 있으며 보안때문에 사용하면 안됨

req.xhr - request가 Ajax call이면 true를 반환하는 property

req.protocol - request를 만드는데 사용하는 protocol, 여기서는 http나 https임

req.secure - connection이 안전하면 true를 반환, 이는 req.protocol === 'https'와 동등함

req.url/req.originalUrl - path와 querystring을 반환(protocol, host, port는 포함하지 않음), req.url은 내부 라우팅 목적으로 재작성될 수 있지만 req.originalUrl은 원래의 request와 querystring으로 남아있는다

 

The Response object

request handler의 두 번째 파라미터로 전달되는 response object, 주로 res, resp, response라고 명명하며, 핵심 Node object인 http.ServerResponse의 인스턴스로 생명주기를 시작한다. 

아래는 Express에서 추가된 response object의 property와 method

res.status(code) - HTTP status code를 설정한다. Express의 default는 200 (OK)이다. 404 (Not found), 500 (Server Error) 등과 같은 상태코드를 반환할 때 이 메소드를 사용한다. 상태코드 301, 302, 303, 307과 같은 redirect를 사용할 때는 redirect 메소드를 사용하는 편이다. res.status는 response object를 반환하므로, chain call을 사용할 수 있다: res.status(404).send('Not found')

res.set(name, value) - response header를 설정한다. key가 header name이고, value가 header value인 하나의 객체 인자를 넘겨주어 여러가지 header를 한 번에 설정할 수 있다.

res.cookie(name, value, [options]), res.claerCookie(name, [options]) - 클라이언트에서 저장된 쿠키를 설정하거나 지운다. 이는 middleware를 이용한다.

res.redirect([status], url) - 브라우저를 redirect한다. default redirect code는 302 (Found)이다. 일반적으로 페이지를 영구적으로 이동시키는 것이 아니면 redirection 사용을 최소화해야 한다. 영구적으로 페이지를 이동시키는 코드는 301 (Moved Permanently)이다.

res.send(body) - response를 client에게 보낸다. Express의 content type default는 text/html이다. 예를 들어 text/plain으로 바꾸고 싶으면 res.send를 하기 전에 res.type('text/plain')을 호출해야한다. body가 object이거나 배열이면 response는 JSON으로 보내진다. 하지만 JSON을 보내고 싶으면 res.json을 사용하는 것을 권장하는 편.

res.json(json) - JSON을 client에게 보낸다

res.jsonp(json) - JSONP를 client에게 보낸다

res.end() - response를 보내지 않고 connection을 끝낸다. 

res.type(type) - Content-Type header를 설정하는 매소드이다. 이는 문자열을 슬래쉬 없이 사용하면 file extension을 internet media type에 매핑한다는 점을 제외하곤, res.set(\'Content-Type', type)과 동일하다. 예를 들어, res.type(\'txt ')는 Content-Type이 text/plain이 된다. 서로 다른 multimedia file을 자동으로 serving하는 경우에 유용하게 사용될 수 있지만, 일반적으로 internet media type을 명시적으로 사용하는 것이 좋다.

res.format(object) - Accept request header에 따라 다른 content를 보낼 수 있도록 만드는 메소드이다. API에서 주로 사용한다. 예를 들어, res.format({'text/plain': 'hi there', 'text/html': '<b>hi there</b>'})

res.attachment([filename]), res.download(path, [filename], [callback]) - Content-Disposition이라는 response header를 attachment로 설정한다. 브라우저가 content를 표시하지 않고 다운로드 하도록 만든다. res.download는 다운로드할 파일을 명시할 수 있지만, res.attachment는 헤더를 설정할 뿐이며 content를 client한테 보내야 한다.

res.sendFile(path, [options], [callback]) - path에 명시된 파일을 읽으면 읽은 content를 client에게 보낸다. static middleware를 사용하는게 더 쉬우며, 이용하고자 하는 파일을 public directory를 통해 client에게 주는 편이다. 하지만 어떤 조건에 따라 같은 URL에서 다른 resource를 보내고 싶다면 이 메소드가 유용함.

res.links(links) - Links response header를 설정한다. 대부분의 애플리케이션에서 거의 사용하지 않는 헤더이다.

res.locals, res.reander(view, [locals], callback) - res.locals는 랜더링할 default context를 가지고 있는 object이다. res.render는 configured templating engine을 이용해 view를 랜더링한다. res.render의 locals parameter는 res.locals의 context를 override하지만 override되지 않은 context는 그대로 사용할 수 있다. res.render의 default response code는 200이다. res.status를 사용해 다른 response를 사용할 수 있다.

http://expressjs.com/en/4x/api.html express API 문서를 잘 활용하자. 문서에서도 찾을 수 없다면 https://github.com/expressjs/express 소스코드를 살펴봐야 한다.

 

Express 소스의 구조를 간단히 보면 아래와 같다.

lib/application.js - main Express interface이다. 어떻게 middleware가 link되는지 view는 어떻게 랜더링되는지 볼 수 있다.

lib/express.js - createApplication 함수를 제공하는 짧은 파일. 이는 Express allication 인스턴스를 생성한다.

lib/request.js - Node의 http.IncomingMessage 객체를 확장해 robust한 request object를 제공한다. request obejct의 property와 method에 대해 볼 수 있다.

lib/response.js - Node의 http.ServerResponse 객체를 확장해 response object를 제공한다.

lib/router/route.js - 기본적인 routing을 지원한다. routing은 app에서 중요한 역할은 코드는 상대적으로 230줄로 짧은 편이며 간단하게 작성되어 있다.

Express의 코드를 보다 보면 https://nodejs.org/en/docs/ Node 문서를 봐야한다. 특히 http 부분.

 

대부분 Express의 작은 부분만, 즉 결국 쓰는 것만 사용한다.

 

반응형

댓글