글생성 UI 만들기

nayonsoso·2021년 4월 26일
0

form 복습

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란?

하이퍼텍스트 전송 프로토콜(HTTP)은 HTML과 같은 하이퍼미디어 문서를 전송하기위한 애플리케이션 레이어 프로토콜입니다. HTTP는 클라이언트가 요청을 생성하기 위한 연결을 연다음 응답을 받을때 까지 대기하는 전통적인 클라이언트-서버 모델을 따릅니다.

HTTP통신할때 보내는 데이터는 HTTP 패킷이라 하는데 해당 패킷의 구조는 크게 Header 영역Body 영역으로 나누어 집니다. 또한 어떠한 메소드 방식을 사용하였는지에 따라 Body 영역의 사용 유무 및 사용 방법이 달라지게 됩니다.

Get 방식과 Post 방식

Get, Post 메소드는 HTTP 프로토콜에서 데이터 전송을 위해 지원하는 7가지 메소드 중 일부입니다.

일반적으로 CRUD중 CUD가 포함된 경우엔 Post 방식이, 데이터를 select만 할 경우에는 Get방식이 사용됩니다.

<Get 방식>

  • URL에 Parameter를 붙여 데이터 전송
    ⇒ Body 영역 사용하지 않음
  • URL에 데이터를 실어 보내므로 전송에 제한이 있음
    Ex.한번 요청시 URL포함 255자 까지 전송 가능 (256byte)
  • 진행속도가 Post방식보다 빠름

<Post 방식>

  • 데이터를 Body영역에 실어 전송
    ⇒ Body 영역 사용
  • 테이터 길이에 제한 없음
    ⇒ 복잡한 형태의 데이터 전송에 유용

Get 방식으로 URL 데이터 받기

  • url모듈 필요
  • request 객체에서 url에 접근
  • url.parse( request.url, true ) 메서드로 필요한 값 추출
//요청이 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!')});

Post 방식으로 FORM 데이터 받기

  • querystring 모듈 필요
  • request.on('data', function(data){ }) 메서드로 수신된 데이터 받기
  • request.connection.destroy() 메서드로 일정 길이 초과시 접속 해제
  • request.on('end',function(){}) 메서드로 수신된 데이터 → 쿼리스트링 형식으로 변환
  • 리다이렉션
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 형식으로 바꿔줍니다.

redirrection이란?

사용자가 입력한 데이터를 전달한 뒤, 잘 전달되었는지 확인시키기 위해
사용자를 다른 페이지로 이동시키는 것을 리다이렉션이라고 합니다.

  • 헤더에 리다이렉션한다는 것을 알려주기 위해 302 를 전달합니다.
  • 헤더에 {Location: } 으로 이동하고자하는 주소를 알려줍니다.
  • 위 내용을 헤더에 전달하기 위해 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(); 
	});
});

입력과 동시에 파일이 생기고, 목록이 수정되고 주소가 바뀌는 과정은 다음과 같습니다.

  • 새로운 파일 생성 -> 파일목록에서 변동 생김 -> a태그 하나 더 늘어남
    -> 해당되는 주소 완성 -> 그 페이지로 이동

Update 기능 구현하기

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();
      })
  });
}

전체 정리

  • 주소의 의미
    href = "" or action = "" 으로 이동 주소를 지정하고
    if문으로 pathname이나 qyerydata.id로 조사하여(Get) 해당 페이지를 꾸민다.
  • 데이터 전송
    form 테그의 action으로 데이터를 보낼 주소 정하고
    해당 주소에서는 form으로 전송된 데이터를 받아
    작업을 수행하고, 리다이렉션으로 다른 페이지로 이동하게 한다.
    ⇒ 데이터를 받는 주소의 페이지는 사용자가 볼 일이 없다.
// 모듈 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('연결되었습니다!');
});

👉

profile
공부한 것들을 정리하기 위한 용도입니다.

0개의 댓글