input테그나 textarea테그처럼 입력하는 정보가 있을 경우, 해당 테그 전체를 form테그로 감싸준다.
form 테그의 action속성은 form data를 서버로 보낼때 해당 데이터가 도착할 URL을 받는다.
<form action="http://localhost:3000/process_create" method="post">
<p><input type="text" name="title" placeholder="title"></p>
<p><textarea name="description" placeholder="description"></textarea></p>
<p><input type="submit"></p>
</form>
하이퍼텍스트 전송 프로토콜(HTTP)은 HTML과 같은 하이퍼미디어 문서를 전송하기위한 애플리케이션 레이어 프로토콜입니다. HTTP는 클라이언트가 요청을 생성하기 위한 연결을 연다음 응답을 받을때 까지 대기하는 전통적인 클라이언트-서버 모델을 따릅니다.
HTTP통신할때 보내는 데이터는 HTTP 패킷이라 하는데 해당 패킷의 구조는 크게 Header 영역과 Body 영역으로 나누어 집니다. 또한 어떠한 메소드 방식을 사용하였는지에 따라 Body 영역의 사용 유무 및 사용 방법이 달라지게 됩니다.
Get, Post 메소드는 HTTP 프로토콜에서 데이터 전송을 위해 지원하는 7가지 메소드 중 일부입니다.
일반적으로 CRUD중 CUD가 포함된 경우엔 Post 방식이, 데이터를 select만 할 경우에는 Get방식이 사용됩니다.
<Get 방식>
<Post 방식>
//요청이 http://localhost:3000/?id=inha 일경우
var url = require('url');
var http = require('http');
var app = http.createServer(function(request,response){
var _url = request.url; // /?id=ohayo
var queryData = url.parse(_url,true).query; // [Object: null prototype] {id: 'ohayo'}
var title = queryData.id; // inha
});
app.listen(3000, function(){console.log('connected!')});
var qs = require('querystring');
var http = require('http');
var app = http.createServer(function(request,response){
var body = '';
request.on('data',function (data){ //request의 데이터를 data로
body += data;
if (body.length > ie6){ //ie 6 은 숫자
request.connection.destroy(); // 접속끊는 함수
}
});
request.on('end',function(){ // data가 끝낫을경우 실행
var post= qs.parse(body); // body 를 쿼리스트링으로 형식바꿈
console.log(post); // queryString 출력
})
response.writeHead(302, {Location: `/?id=${}`}); //요청한 주소로 리다이렉션
response.end('sucess');
});
Post 방식의 데이터를 읽어오기 위해선 약속된 이벤트들이 필요합니다.
request.on('data',function(data){} 는
request로 들어온 데이터를 일정 기준 조각내어
콜백함수의 data로 넣어준다는 뜻입니다.
조각을 내서 넣어주기때문에 전부 다 받으려고 body += data 를 넣어줍니다.
request.connection.destroy(); 는
접속을 끊는 함수입니다. if문을 사용하여
데이터가 일정 크기 이상 들어올경우 접속을 끊는 함수를 사용한겁니다.
request.on('end',function(){} 은
request 데이터를 다 받고나서 작동합니다. qs.parse()메서드를 이용해
request에서 body로 받은 모든 데이터를 querystring 형식으로 바꿔줍니다.
사용자가 입력한 데이터를 전달한 뒤, 잘 전달되었는지 확인시키기 위해
사용자를 다른 페이지로 이동시키는 것을 리다이렉션이라고 합니다.
writeHead(302,{Location: })
포멧을 이용합니다.var body ='';
request.on('data',function(data){
body = body + data;
});
request.on('end',function(){
var post = qs.parse(body);
var title = post.title;
var description = post.description;
fs.writeFile(`data/${title}`,description, 'utf8', function(err){
// response.writeHead(302, {Location: `/?id=${title}`}); -> 한글이면 오류 발생
response.writeHead(302, {Location: `/?id=${qs.escape(title)}`});
response.end();
});
});
입력과 동시에 파일이 생기고, 목록이 수정되고 주소가 바뀌는 과정은 다음과 같습니다.
update 기능을 구현하기 위해선
1) 수정하고자 하는 파일 기존 값 읽어오기 (fs.readdir , fs.readFile)
2) form 형식으로 기존 내용 표시 (input 테그의 value 속성)
3) 제출된 데이터 전달받기 (request.on)
4) 파일 이름 및 내용 수정 (fs.rename , fs.writeFile)
5) id 페이지로 리다이렉션 (response.writeHead(302,{Location:}) )
이 필요합니다.
form을 이용해 데이터를 전송할때 (Post 방식) 기존 정보를 함께 보내야
어떤 파일을 수정할 것인지 알 수 있습니다.
하지만 기존의 정보를 사용자에게 보일 필요는 없습니다.
이때 type = "hidden" 속성을 사용하면, 사용자의 눈에 보이지 않게 form데이터를 보낼 수 있습니다.
하지만 hidden form을 사용하면 정보를 입력할 수 없으므로
value 테그를 이용해 기본값을 입력해 줍니다.
<form action="/update_process" method="post">
<input type="hidden" name="id" value ="${title}">
<p><input type="text" name="title" placeholder="title" value ="${title}"></p>
<p><textarea name="description" placeholder="description">${description}</textarea></p>
<p><input type="submit"></p>
</form>
request.on로 폼 데이터를 받고 fs.rename으로 파일이름을, fs.writeFile 으로 파일 내용을 수정합니다.
fs.writeFile은 내용생성, 수정에 모두 사용될 수 있습니다.
var body ='';
request.on('data',function(data){
body = body + data;
});
request.on('end',function(){
var post = qs.parse(body);
var id = post.id;
var title = post.title;
var description = post.description;
console.log(post.id); // inha
console.log(post.title); // INHA UNIV
console.log(post.description); // hi inha~
fs.rename(`data/${id}`,`data/${title}`, function(error){ // 파일 이름 변경
// 첫번째 인자에 기존 이름, 두번째 인자에 변경하려는 이름
fs.writeFile(`data/${title}`,description, 'utf8', function(err){ // 파일 내용 변경
// writeFile은 파일 내용 생성, 수정 모두에 쓰임
response.writeHead(302, {Location: `/?id=${qs.escape(title)}`});
response.end();
});
});
});
삭제처럼 중요한 기능은 다른사람이 함부로 정보를 빼갈 수 없도록 post 방식을 써야 합니다.
때문에 post 방식의 form으로 삭제버튼을 감싸줍니다.
이때 파일이름을 보이지 않게 전달하기 위해 hidden form 을 사용합니다.
버튼의 이름은 value 속성으로 바꿔줄 수 있습니다.
<form action="delete_process" method = "post">
<input type="hidden" name="id" value="${title}">
<input type="submit" value="delete">
// submit에서는 value가 버튼 이름
</form>
form 으로부터 값(파일명)을 받기 위해 request.on 을 사용합니다.
이렇게 받은 파일명을 fs.unlink 함수에 넘겨주어 해당 이름을 가진 파일을 삭제합니다.
else if(pathname === '/delete_process'){
var body ='';
request.on('data',function(data){
body = body + data;
});
// form data를 받기 위해 request.on('data',function(data){body+=data;}) 사용
request.on('end',function(){
var post = qs.parse(body);
var id = post.id;
fs.unlink(`data/${id}`,function(error){
response.writeHead(302, {Location: `/`}); // 홈으로 리디렉션
response.end();
})
});
}
// 모듈 require
var http = require('http');
var fs = require('fs');
var url = require('url');
var qs = require('querystring');
// template literal을 html로 return 하는 함수
function templateHTML(title, list, body, control){
return `
<!doctype html>
<html>
<head>
<title>WEB - ${title}</title>
<meta charset="utf-8">
</head>
<body>
<h1><a href="/">WEB</a></h1>
${list}
${control}
${body}
</body>
</html>
`;
}
// 전달받은 배열을 리스트로 return 하는 함수
function templateList(filelist){
var list = '<ul>';
var i = 0;
while(i < filelist.length){
list = list + `<li><a href="/?id=${filelist[i]}">${filelist[i]}</a></li>`;
i = i + 1;
}
list = list+'</ul>';
return list;
}
// nodeis의 서버 생성 방법 : http객체의 createServer, listen
var app = http.createServer(function(request,response){
var _url = request.url;
var queryData = url.parse(_url, true).query;
var pathname = url.parse(_url, true).pathname;
if(pathname === '/'){ // pathname /
if(queryData.id === undefined){ // id 존재 x (Home)
fs.readdir('./data', function(error, filelist){
var title = 'Welcome';
var description = 'Welcome to Node js';
var list = templateList(filelist);
var template = templateHTML(title, list,
`<h2>${title}</h2>${description}`,
`<a href="/create">create</a>`);
response.writeHead(200);
response.end(template);
})
} else { // id 존재 o
fs.readdir('./data', function(error, filelist){
fs.readFile(`data/${queryData.id}`, 'utf8', function(err, description){
var title = queryData.id;
var list = templateList(filelist);
var template = templateHTML(title, list,
`<h2>${title}</h2>${description}`
, `<a href="/create">create</a>
<a href="/update?id=${title}">update</a>
<form action="delete_process" method = "post">
<input type="hidden" name="id" value="${title}">
<input type="submit" value="delete">
</form>`);// form을 쓴다고 다 post방식이 아님 폼데이터로 받으려면 method 속성이 반듯있음
response.writeHead(200);
response.end(template);
});
});
}}
else if(pathname == '/create') { // create페이지
fs.readdir('./data', function(error, filelist){
var title = 'WEB - create';
var list = templateList(filelist);
var template = templateHTML(title, list, `
<form action="/create_process" method="post">
<p><input type="text" name="title" placeholder="title"></p>
<p>
<textarea name="description" placeholder="description"></textarea>
</p>
<p>
<input type="submit">
</p>
</form>
`,'');
// <form> 태그의 action 속성은 폼 데이터(form data)를 서버로 보낼 때 해당 데이터가 도착할 URL
response.writeHead(200);
response.end(template);
})}
else if (pathname === '/create_process'){ // 데이터가 전송되는 create_process페이지
var body ='';
request.on('data',function(data){
body = body + data;
});
request.on('end',function(){
var post = qs.parse(body);
var title = post.title;
var description = post.description;
fs.writeFile(`data/${title}`,description, 'utf8', function(err){
response.writeHead(302, {Location: `/?id=${qs.escape(title)}`});
response.end();
});
});
}
else if(pathname === '/update'){
fs.readdir('./data', function(error, filelist){
fs.readFile(`data/${queryData.id}`, 'utf8', function(err, description){
var title = queryData.id;
var list = templateList(filelist);
var template = templateHTML(title, list,
`<form action="/update_process" method="post">
<input type="hidden" name="id" value ="${title}">
<p><input type="text" name="title" placeholder="title" value ="${title}"></p>
<p>
<textarea name="description" placeholder="description">${description}</textarea>
</p>
<p>
<input type="submit">
</p>
</form>
`
, `<a href="/create">create</a> <a href="/update?id=${title}">update</a>`);
response.writeHead(200);
response.end(template);
});
});
}
else if(pathname === '/update_process'){
var body ='';
request.on('data',function(data){
body = body + data;
});
request.on('end',function(){
var post = qs.parse(body);
var id = post.id; // form hidden으로 추가된 id값도 받을 수 있게!
var title = post.title;
var description = post.description;
console.log(post.id); // inha
console.log(post.title); // INHA UNIV
console.log(post.description); // inha
fs.rename(`data/${id}`,`data/${title}`, function(error){ // 파일이름 변경하는 fs.rename 함수 사용
fs.writeFile(`data/${title}`,description, 'utf8', function(err){ // writeFile은 수정하는 기능도 있나봄!
response.writeHead(302, {Location: `/?id=${qs.escape(title)}`});
response.end();
})
});
});
}
else if(pathname === '/delete_process'){
var body ='';
request.on('data',function(data){
body = body + data;
});
request.on('end',function(){
var post = qs.parse(body);
var id = post.id;
fs.unlink(`data/${id}`,function(error){
response.writeHead(302, {Location: `/`});
response.end();
})
});
}
else {
response.writeHead(404);
response.end('Not found');
}
});
app.listen(3000, function(){
console.log('연결되었습니다!');
});
👉