[Node.js] form을 통해 body 데이터 받기

zxcev·2023년 1월 18일
0

Node.js

목록 보기
4/17
post-thumbnail

회원가입, 로그인 등을 구현할 때, <form> 태그 내부에 위치한 <input /> 내부에 각 ID, 비밀번호 등의 사용자 정보를 입력하게 한 뒤, 마지막에 Submit 버튼을 누르면 모든 정보가 서버로 전송되고, 그 정보를 토대로 데이터베이스에 사용자의 정보가 저장/조회되고, 가입, 로그인이 처리된다.

간단한 로직이지만 매우 자주 사용되기 때문에 상당히 중요한 부분이다.
완벽한 이해를 위해 <form>을 통해 클라이언트에서 서버로 사용자가 입력한 정보를 전송하고 받는 과정을 간단하게 구현해보도록 하자.

코드는 다음과 같다.

const http = require('http');

http.createServer((req, res) => {
   
    if (req.method === 'GET') {
        res.writeHead(200, {
            'Content-Type': 'text/html',
        })
        res.end(`<form method="POST">
            ID: <input type="text" name="id" />
            <br />
            Password: <input type="text" name="password" />
            <button>Submit</button>
        </form>`);
        return;
    }

    let buf = [];
    req.on('data', chunk => {
        console.log(chunk);
        buf.push(chunk);
    });

    req.on('end', () => {
        console.log(Buffer.concat(buf).toString());
        res.end('ok');
    });
}).listen(3000);

간단한 http 서버를 만들고, GET 요청일 경우, ID, Password를 입력하여 서버로 전송할 수 있는 html 문서를 렌더링해준다.

POST로 요청이 들어오는 경우가 사용자가 form에 입력한 데이터를 전송 받아 처리하는 부분이 된다.

Node.js는 이벤트 기반이므로, 'data''end' 라는 이벤트가 발생했을 때 로직을 처리하도록 각 이벤트에 해당하는 핸들러를 등록해준다.

'data'는 스트림 방식(대용량의 데이터를 한 번에 보내는 것이 아닌, 작은 단위로 쪼개서 순차적으로 전송하는 것)으로 들어오는 데이터를 수신할 때마다 발생하며, 데이터를 콜백 함수의 인자로 전달 받아서 처리할 수 있다.

'end'는 모든 데이터가 전송 완료 되었을 때 발생하는 이벤트다.

ID나 비밀번호 등은 길어야 수십~수백 자이기 때문에 데이터가 매우 작아서 한 번에 받아오게 되지만, 나중에 다루게 될 이미지나 동영상 등은 용량 단위가 KB를 넘어서 MB 이상으로 갈 수도 있고, 그렇게 되면 수십~수백 개로 쪼개져서 전송하게 된다.
그 때 스트림이 제대로 힘을 발휘할 것이다.

이를 구분하기 위해 같은 form이라도 전송 방식이 x-www-form-urlencoded, multipart-form-data 등으로 나뉘게 되는데, 용량이 커서 여러 개의 부분으로 쪼개서 보내야 하는 이미지, 영상 등이 multipart-form-data (이름도 멀티 파트라 기억하기 쉽다)
우리가 사용할 ID, 비밀번호 등의 단순 데이터는 x-www-form-urlencoded 방식이다.

이제 http://localhost:3000 혹은 http://127.0.0.1:3000으로 접속해서 ID와 비밀번호를 서버로 전송해보자.

ID는 MyID, 비밀번호는 1122를 입력하고 Submit 버튼을 누르면 아래처럼 출력된다.

<Buffer 69 64 3d 4d 79 49 44 26 70 61 73 73 77 6f 72 64 3d 31 31 32 32>
id=MyID&password=1122

첫 줄은 데이터가 들어올 때, req.on('data')의 이벤트 핸들러에서 발생한 로그이며, console.log(chunk);chunk를 그대로 출력했기 때문에 버퍼에 저장된 바이너리 데이터 형태로 출력되는 것을 알 수 있다.

아래 부분이다.

let buf = [];
req.on('data', chunk => {
	console.log(chunk);
	buf.push(chunk);
});

buf라는 배열을 만들어 데이터가 들어올 때마다 그 안에 각 chunk들을 push해줬다.
아까 말했듯 용량이 작은 단순 데이터기 때문에 buf 안에 모든 데이터가 한 번에 들어갈 것이고 데이터를 다 수신해도 length가 1인 배열일 것이다.

그리고 다음 코드가 사람이 알아볼 수 있게 버퍼의 바이너리 데이터를 문자열로 변환하여 출력하는 코드다.

req.on('end', () => {
	console.log(Buffer.concat(buf).toString());
	res.end('ok');
});

데이터 수신이 완료되어 end 이벤트가 발생했을 때 실행되며, Buffer.concat(buf)는 배열에 들어 있는 각 Buffer들을 하나로 합쳐준다.

아래와 같이 처리된다고 생각하면 된다.
[Buffer1, Buffer2, Buffer3] -> Buffer

그리고 바이너리 데이터인 Buffer에서 .toString()을 호출하면 문자열로 변환되기 때문에, 원래 우리가 form으로 전송했던 ID와 비밀번호가 문자열 형태로 출력되는 것이다.

재미있는 것은 QueryString과 똑같은 형태를 하고 있다는 것이다.
id=MyID&password=1122

포맷이 같으니 파싱도 지난 장에서 했던 것처럼 하면 될 것이다.

0개의 댓글