Node.js | 여러 페이지의 라우팅 및 폼 POST 전송 | 입력폼 POST 전송


이어서 폼을 POST 전송했을 때의 처리에 대해 생각해 본다. 이미 주소로 페이지 처리하는 방법은 알았기에 입력폼을 가진 페이지를 준비하고, 그 전달 대상의 주소 처리하는 작업은 알고 있다.

GET과 POST

가장 큰 문제는 “GET인지, POST인지"를 어떻게 알 것인가하는 점이다. 그리고 또 하나는 “전송된 입력폼의 정보를 어떻게 얻을 것인가"라는 점이다.

우선, GET과 POST 분리하는 방법이다. 이는 사실 간단하다. request 이벤트 핸들러에 인수로 전달된 request 객체의 ‘method’를 조사하는 것만으로 끝이다. 핸들러 안에 이런 식으로 처리를 준비하면 된다.

if (request.method == "GET"){
    ...... GET 처리 ......
}
if (request.method == "POST"){
    ...... POST 처리 ......
}

다음으로 “POST 전송된 입력폼 데이터를 가져오기"는 실제 코드를 보는 편이 알기 쉬울 것이다. 그럼, 이에 대해서도 샘플을 만들어 설명한다. 우선은 표시하는 내용의 템플릿이다. 이번에는 content1.ejs에 입력폼을 넣고, 새롭게 준비하는 content3.ejs에서 전송된 입력폼의 표시를 하도록 한다.

아래애 예제를 올려 두었다. 우선 이를 작성한다.

content1.ejs

<h2>예제로 작성한 내용입니다.</h2>
<p><%= message %></p>
<hr>
<form method="post" action="./post">
<table>
    <tr><td>ID:</td><td><input type="text" name="idname"></td></tr>
    <tr><td>PASS:</td><td><input type="password" name="pass"></td></tr>
    <tr><td></td><td><input type="submit"></td></tr>
</table>
</form>

content3.ejs

<p>POST으로 액세스된 내용입니다.</p>
<p>ID: <%= idname %></p>
<p>PASS: <%= pass %></p>
<p><a href="/">돌아가기</a></p>

POST로 전송된 데이터 처리

이어서 Node.js 스크립트를 작성한다. 이번에는 입력폼의 대상으로 content3.ejs을 표시하기 위해 “/ post"라는 경로로 정보를 추가하고 있다.

var http = require('http');
var fs = require('fs');
var ejs = require('ejs');
var url = require('url');
var qs = require('querystring');
 
var template = fs.readFileSync('./template.ejs', 'utf8');
var content1 = fs.readFileSync('./content1.ejs', 'utf8');
var content2 = fs.readFileSync('./content2.ejs', 'utf8');
var content3 = fs.readFileSync('./content3.ejs', 'utf8');
 
var routes = {
    "/":{
        "title":"Main Page",
        "message":"이것은 예제 페이지입니다.",
        "content":content1},
    "/index":{
        "title":"Main Page",
        "message":"이것은 예제 페이지입니다.",
        "content":content1},
    "/other":{
        "title":"Other Page",
        "message":"다른 페이지를 표시하고 있습니다.",
        "content":content2},
    "/post":{
        "title":"Post Page",
        "content":content3}
};
 
var server = http.createServer();
server.on('request', doRequest);
server.listen(1234);
console.log('Server running!');
 
// 요청 처리
function doRequest(request, response) {
    var url_parts = url.parse(request.url);
    // route check
    if (routes[url_parts.pathname] == null){
        response.writeHead(200, { 'Content-Type': 'text/html' });
        response.end("<html><body><h1>NOT FOUND PAGE:" + 
            request.url + "</h1></body></html>");
        return;
    }
    // get
    if (request.method == "GET"){
        var content = ejs.render( template,
            {
                title: routes[url_parts.pathname].title,
                content: ejs.render(
                    routes[url_parts.pathname].content,
                    {
                        message: routes[url_parts.pathname].message
                    }
                )
            }
        );
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write(content);
        response.end();
        return;
    }
    // post
    if (request.method == "POST"){
        if (url_parts.pathname == "/post"){
            var body='';
            request.on('data', function (data) {
                body +=data;
            });
            request.on('end',function(){
                var post =  qs.parse(body);
                var content = ejs.render( template,
                    {
                        title: routes[url_parts.pathname].title,
                        content: ejs.render(
                            routes[url_parts.pathname].content,
                            {
                                idname: post.idname,
                                pass: post.pass
                            }
                        )
                    }
                );
                response.writeHead(200, {'Content-Type': 'text/html'});
                response.write(content);
                response.end();
            });
        } else {
            response.writeHead(200, {'Content-Type': 'text/plain'});
            response.write("NO-POST!!");
            response.end();
        }
    }
}

스크립트를 작성한 후에 Node.js를 기동시켜서 액세스해 본다. http://127.0.0.1:1234/ 에 액세스하면 준비된 양식이 표시된다. 여기에 ID와 PASS를 적당히 값을 기입하고 전송하면, 전송된 내용이 표시된다.

이번 스크립트에는 “querystring"라는 객체를 로드하여 사용하고 있다. 시작 부분에 있는 다음 문장 이다.

var qs = require('querystring');

이 querystring은 쿼리 문자열을 처리하는 기능을 제공한다. 이를 이용하여 쿼리 문자열에서 필요한 값을 지정하여 꺼낼 올 수 있게 되다. 그럼, 스크립트의 내용을 살펴 보자.

여기에서는 request.method으로 GET시와 POST에서 처리를 나눈다. GET인 경우에 처리는 앞전과 동일하다. 문제는 POST에서의 처리이다. 여기에서는 먼저 “data"라는 이벤트 핸드링을 하고 있다.

var body='';
request.on('data', function (data) {
    body +=data;
});

이 data 이벤트는 POST로 전송된 데이터를 수신했을 때 발생한다. 이벤트 핸들러에는 보내져 온 데이터가 인수로 전달된다. 이렇게 얻어진 데이터를 변수 body에 하나씩 추가하여 수신된 데이터가 완성되어 간다.

그리고 모든 수신 처리가 완료된 후에 POST 데이터의 처리와 페이지의 렌더링을 실시한다. 이것은 “end"라는 이벤트 핸들러를 준비하고 구현한다.

request.on('end',function(){
    var post =  qs.parse(body);
    ...... 중략 ......

end 이벤트 핸들러으로 최초에 수행하고 있는 것은 앞전에 data 이벤트에서 받은 데이터를 정리한 변수 body를 파싱하는 과정이다. 이는 querystring 객체의 “parse"라는 메소드로 실행하고 있다. 이 메소드는 인수로 전달된 쿼리 문자열을 파싱하여 객체에 정리한다. 예를 들면, 아래와 같은 식이다.

a=abc&x=xyz

{ a: "abc", x:"xyz" }

이 때, URL 인코딩된 값도 자동으로 원래의 문자열로 디코딩된다. 이렇게 얻어진 변수 post에서 필요한 값을 꺼내면 된다. 이번에는 content3를 렌더링할 때 다음과 같이하고 있다.

content: ejs.render(
    routes[url_parts.pathname].content,
    {
        idname: post.idname,
        pass: post.pass
    }
)

전송된 값은 post.idname, post.pass에서 꺼낼 수 있다. 이 후에는 이들을 정리해서 렌더링할 뿐이다.

POST 전송은 데이터의 수신이 조금 복잡하지만, 그래도 알게 되면 쉽다. 이것으로 대부분의 일반 Web 페이지는 만들 수 있을 것이다.