Get / Post
- 지난 포스팅에 node.js에서 filestream을 사용하는 방법을 공부하였고 예제로 파일 목록을 확인하고 내용을 읽는 게시판을 만들어보았다.
지난 포스팅에 이어 파일 생성을 구현하기에 앞서 HTTP 프로토콜에서 서버에 요청을 위한 두 방식을 비교하고 공부한다.
-
Get 방식과 Post 방식
- 소포의 의미를 갖는 패킷을 따서 네트워크 상의 데이터를 패킷, HTTP 프로토콜에서의 패킷을 HTTP 패킷이라고 한다. HTTP 패킷은 크게 header와 body로 구성되어 있다.header는 요청 방식, 클라이언트 정보, 브라우저 정보, 접속할 url 등의 설정 정보를 담고, body에는 서버에 실제 데이터를 담는다. -
Get
- 지금까지 우리가 써왔던 요청 방식이 바로 get 방식이다. url에 ?key=value와 같은 키-값 형태의 데이터를 붙여 데이터를 전송하는 것이다. 물론 &를 붙여 데이터를 다중으로 첨부할 수 있고, 이렇게 주소 뒤에 붙은 문자열을 Querystring이라고 한다.이 쿼리스트링의 값은 패킷의 header에 포함되어 전송된다. 지난 포스팅에서 query 객체를 이용하여 편하게 파일의 콘텐츠별로 페이지를 구별한 것처럼 get 방식은 사용자로 하여금 요청 내용을 한눈에 확인 할수 있다는 장점을 갖는다.하지만 주소에서 전송 내용이 들어난다는 것은 반대로 데이터가 길어지면 주소가 길어져 보기 불편하고 많은 양의 데이터를 전송하기에 무리가 있을 수 있다는 말과 같다. 또 보안적인 측면에서 위험하다.가장 중요한 것은 get 방식은 서버의 데이터에 접근을 위한 요청이기 때문에 서버의 데이터에 변화를 주지 않는다. 다시 말해 같은 요청을 여러번 반복하여도 같은 응답을 기대하고, 때문에 이전 포스팅처럼 데이터의 내용에 따른 페이지 호출과 같은 접근에 가장 많이 사용되고, 한번 요청한 정보에 대한 응답이 변하지 않으므로 이를(주로 css/html을) 캐시로 저장하여 다음번 응답부터는 더 빠르게 처리할 수 있다.쿠키,세션과 캐시를 구분한다. -
Post
- 반면 post 방식은 데이터를 body에 담아 전송한다. 그렇기 때문에 가시적이지 않고 더 많은 데이터를 전송 할 수 있다. 또, get 방식보다는 안전하나 역시 접근이 가능하므로 암호화를 필수적으로 요구한다.get 방식이 접근에 중심을 뒀다면 post는 데이터 전송에 중심을 둔다. post 방식의 요청은 서버 데이터나 상태에 변화를 주고, 이는 아래 '파일 생성을 위한 post 요청 사용'이 가장 좋은 예시라고 생각한다. -
File create
- 노드에서 파일 생성은 fs모듈의 writeFile 메소드로 가능하다."writeFile.js" const data = new Uint8Array(Buffer.from('Hello Node.js')); fs.writeFile('message.txt', data, (err) => { if (err) throw err; console.log('The file has been saved!'); }); writeFile 메소드는 현재 폴더에 파일을 생성하고 write 메소드는 지정된 파일에 옵션으로 정해진 시작 위치부터 내용을 적는다. writeFile은 지정한 파일명이 없다면 새로 파일을 생성하고 있다면 기존 내용을 모두 삭제하고 처음부터 데이터를 적는다. appendFile 메소드를 이용하면 파일이 없으면 새로 생성, 있다면 이어서 내용을 추가하는 것이 가능하다.> 모두 비동기이기 때문에 Docs에서는 콜백을 기다리지 않고 한 파일을 여러번 접근할 경우, 매우 불안정하다고 누차 경고하고 있다. 이럴 때는 동기식 메소드를 이용하거나 fs.createWriteStream 메소드를 이용해야한다.- board에서 id 값이 create인 값을 get으로 요청하면 board_create 템플릿 파일을 렌더링한다. 이 템플릿은 post 방식으로 데이터를 전송하고 post로 /create를 요청한다."board_create.pug" body h5 create form(action='/create', method='post') div label(for='title') title: input(name='title') div label(for='content') content: textarea(name='content') div input(type='submit' value = "submit") // 위의 form에서 input 속성으로 name이 의미하는 것이 무엇인지 모르겠다. 만일 name 속성이 존재하지 않으면 body에 데이터가 저장이 안되고 body는 기본 값인 undefined를 갖는다. input에 type만 지정하여 계속 body가 {}으로 전송되었고, 이를 name으로 해결했지만 name이 어떤 역할을 하는지 다시 공부할 생각이다.form에서 전송한 body의 데이터는 form에서 method를 지정한 것을 읽어 req.method="post"을 이용하여 이벤트로 처리할 수 있으나 body-parser 모듈을 이용하면 쉽게 body의 데이터를 처리할 수 있다.터미널에서 다음을 통해 모듈을 설치할 수 있다.npm install body-parser --save 설치 후 다음을 js에 추가하여 모듈을 사용한다.var bodyParser = require('body-parser'); app.use(bodyParser.urlencoded({ extended: false })) post 요청을 처리하기 위해 board.js에 다음 코드를 추가하였다."board.js" app.post('/create',function(req, res){ var New_title = req.body.title; var New_content = req.body.content; fs.writeFile('./files/'+New_title+'.txt',New_content,function(err){ res.redirect('/board?id='+New_title); }); }) form에서의 post 요청을 처리하기 위해 app.post('/create')를 정의해준다. req의 body안의 title,content 값을 각각의 변수에 저장하고 파일을 생성한다.파일을 files라는 폴더안의 New_title.txt로써 저장하고 그 data 내용은 New_content로 한다. 이후 fs.writeFile을 마침과 동시에 콜백으로 redirect(id=파일 명)을 호출하여 생성 이후 바로 그 파일을 읽어 내용을 표시하도록 하였다. -
Conclusion..
- FileStream을 공부하기 위하여 get/ post 방식, queryString, pug- extends, fs 모듈을 공부하고 예제로 간단한 게시판을 만들어 보았다.이번 예제는 다른 강좌나 책의 설명을 보지 않고 docs와 구글링으로 직접 필요한 정보를 찾아가고, 문제를 해결하는 것을 목표로 했고, 다른 예제들보다 시간은 오래걸렸지만 진짜 아는 것과 이해했던 것을 구별할 수 있는 좋은 기회가 되었다. 다음 포스팅 역시 소켓 통신에 대한 개념 공부와 함께 직접 채팅 어플리케이션을 만드는 것을 예제로 쓰려고 한다. -
Code
board.js"board.js" var express = require('express'); var app =express(); var fs= require('fs'); app.engine('pug', require('pug').__express) app.set('views', './template'); app.set('view engine','pug'); app.locals.pretty=true; app.use(express.static('public')); var bodyParser = require('body-parser'); app.use(bodyParser.urlencoded({ extended: false })) app.get('/board',function(req,res){ var id = req.query.id; var file={ title : '', content:'', }; fs.readdir('./files',function(err, FileList) { if(id ==null){ res.render('board',{'files':FileList}); } else if (id=='create'){ res.render('board_create',{'files':FileList}); } else{ fs.readFile('./files/'+id,'utf8', function(err, data){ file.title=id; file.content= data; res.render('board_content',{'files':FileList,'file': file}); }) } }) }) app.post('/create',function(req, res){ var New_title = req.body.title; var New_content = req.body.content; fs.writeFile('./files/'+New_title+'.txt',New_content,function(err){ res.redirect('/board?id='+New_title); }); }) app.listen(3000, function(){ console.log('listen port:3000'); }) board.pug"board.pug" head title Board body div h3 Board! div(style="padding-left:20px") ul each file in files li -   a(href="/board?id="+file)= file li -   a(href="/board?id=create") create block contents style(type="text/css"). a { color:black; text-decoration:none; } ul{ padding-left:20px;list-style:none; line-height:30px; } h5{ font-size:17px; } #content{ font-size:15px; width:800px; } board_content.pug"board_content.pug" extends board.pug block contents h5 #{file.title} div(id='content') #{file.content} board_create.pug"board_create.pug" body h5 New data form(action='/board' method='post' style="padding-left : 30px") p input(type = 'text' name='title' placeholder =' Title' style = "width :800px") p textarea(name='content' placeholder=' Content' style ="width : 800px; height : 300px;") p input(type='submit' value = "submit") -