본문 바로가기

Web Development/Back-end

[생활코딩-Nodejs-16] '사용자 게시판 - 글쓰기기능' 만들기

오늘부터는 지난번에 HTML-FORM편에서 언급했던,

파일의 생성,수정,삭제를 사용자가 할 수 있게 하는 것을 해보도록 하겠습니다.

 

우선 수정전의 지금까지 했던 main.js파일입니다.

 

var http = require('http');
var fs = require('fs');
var url = require('url'); // URL 모듈

//template 내용 HTML 뼈대를 함수로 만들기.
function templateHTML(title, list, body){ 
  return `
  <!doctype html>
  <html>
  <head>
    <title>WEB1 - ${title}</title>
    <meta charset="utf-8">
  </head>
  <body>
    <h1><a href="/">WEB</a></h1>
    ${list}
    ${body}
  </body>
  </html>
  `;
}

//리스트를 자동으로 만들어주는 코드를 함수로 만들기.
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+=1;
  }
  list = list +'</ul>';
  return list;
}

var app = http.createServer(function(request,response){
    var _url = request.url; //URL 요청
    var queryData = url.parse(_url, true).query; //URL을 분석하는 모듈 중, querystring 값
    var pathname = url.parse(_url, true).pathname; // URL을 분석하는 모듈 중, pathname 값

    //요청한 pathname이 루트값이라면 , if문이 실행
    if(pathname === '/'){
      if(queryData.id === undefined){
          fs.readdir('./data', function(err, filelist){
            var title = 'Welcome';
            var description = 'Hello, Node.js';
            var list = templateList(filelist);
            var template = templateHTML(title, list, `<h2>${title}</h2>${description}`);
            response.writeHead(200); // 페이지가 정상적으로 출력되면 200, 오류페이지면 404.
            response.end(template); //화면출력하는부분
          });

      //요청한 pathname이 루트값이 아니라면,대신 queryData.id가 있다면, else문 실행.
      } else {
        fs.readdir('./data', function(err, filelist){
        //파일을 읽는 함수. description변수가 파일.
          fs.readFile(`data/${queryData.id}`, 'utf-8', function(err, description){
            var title = queryData.id
            var list = templateList(filelist);
            var template = templateHTML(title, list, `<h2>${title}</h2>${description}`);
            response.writeHead(200); // 페이지가 정상적으로 출력되면 200, 오류페이지면 404.
            response.end(template); //화면출력하는부분
          });
        });
      }
    //요청한 pathname이 루트값이 아니고, qeuryData.id가 없다면 실행.
    } else {
      response.writeHead(404); // 페이지가 정상적으로 출력되면 200, 오류페이지면 404.
      response.end('NOT FOUND'); //화면출력하는부분
    }

});
app.listen(3000);

일단 UI(User Interface) 화면에 출력되는 부분을 만들어야겠죠~?

UI부터 만들어봅시다.

 

 

1. 글쓰기 User Interface(UI) 만들기

 

우선 사용자가 글을 쓰려면 글쓰기 버튼이 있어야겠죠?

Create라는 이름으로 버튼부터 생성해보겠습니다.

 

어디를 수정하면될까요?

뼈대를 만들었던 HTML template부분을 수정해 줘야겠죠?

 

이렇게 a태그 링크로 버튼을 만들어 주었습니다.

목록이 출력되는 부분, 그리고 본문이 출력되는 부분 사이에 저는 create버튼을 만들어 주었습니다.

 

 

 

큰 그림을 한번 살펴볼까요?

우리는 pathname을 통해 홈으로 들어왔을 때,

홈이 아닌 곳이지만 QueryData.id값이 존재하는 각 페이지에 들어왔을 때,

그리고 둘다 없는 존재하지 않는 경로에 입장했을 때,

세가지 로 크게 만들었습니다.

 

그런데 방금 Create라는 또다른 새로운 페이지를 만들어냈어요.

a태그를 통해서 말이죠.

그럼 이제 create페이지로 들어왔을 때를 처리하는 코드가 필요합니다.

 

그래서 저는

queryData.id값이 존재하는 각 페이지에 들어왔을때, 다음부분에 글쓰기 페이지 UI 생성할 것입니다.

 

이렇게요. 위의 홈디렉터리에 있던 것을 그대로 가져와서,

빨간 네모친 부분을 설명대로 바꾸었습니다.

 

그리고 웹페이지로 가보시면,

다음과 같이 적용된 것을 확인 할 수 있습니다.

 

 

이제 저 곳에 데이터를 입력하고 한번 보내볼까요?

 

아직 설정해주지 않아 페이지는 정상적으로 동작하지 않지만 !

form에서 보내려고하는 localhost:3000/create_process 로 데이터가 잘 전송되었고,

F12키를 통해 '검사'창에서 '네트워크' 탭에서 create_process 파일을 눌러보시면,

Form Data 부분에 포스트방식으로 은밀하게 데이터가 잘 전송된 것을 확인 하실 수 있습니다.

 

 

 

그럼 이제 뭘 해야겠어요?

Form Data에 post방식으로 잘 전송된 데이터를 받아줘서

Data 디렉토리에 추가하고 화면에 출력하는 것까지 되야겠죠?

이제부터는 POST방식으로 받아온 데이터를 어떻게 받을 수 있는지 알아보겠습니다.

 

 

 

 

 

 

 

2. POST 데이터 받기

우선 위에서 form을 통해 데이터를 /create_process라는 pathname으로 보내주었기에,

/create_process를 처리해줄 조건문을 또 만들어 주었습니다.

 

그리고, body라는 내용을 담을 빈 변수를 선언해주구요.

request.on('data', function(data){ 를 통해서 서버에서 수신한 정보를 data 매개변수에 담을 수 있도록 해주었습니다.

그리고, 빈 변수로 선언한 data에 받은 데이터를 추가시켜주는 것이죠.

 

그리고 나서, 데이터를 다 받았어요.

그럼

request.on('end', function(){ 이부분이 실행되는데요.

위에서 저장된 data가 querystring의 body부분에 저장됩니다.

그것을 qs.parse(body)라는 모듈과 함수를 이용해 post에 저장해줍니다.

 

그럼 포스트에 뭐가들었는지 궁금하지않나요?

도대체가 뭔소린지 모르겠어서 찍어봤습니다.

 

 

이렇게 제가 작성한 데이터들이 담겨있는 것을 확인 할 수 있습니다.

첫번째 부분은 post(qs.parse(body))만 출력하면 나오구요.

구체적으로 post.title, post.description이라고 하면 저렇게 분할해서도 나오게됩니다.

 

 

그래서 저것을 var title과 var description이라는 변수로 감싸 정보를 변수로 만들어주었습니다.

 

이렇게 POST의 데이터를 읽는 방법을 살펴보았습니다.

 

 

 

자 그럼 다시 돌아가 웹페이지를 완성시켜볼까요?

 

 

3. 웹페이지에 적용

 

위에서 데이터를 받아왔다면, 이제 그 데이터를 data디렉토리에 추가시키는 방법이 필요하겠죠?

그러기위해서 fs.writeFile()이라는 Nodejs의 기능을 살펴보겠습니다.

fs.writeFile( ) 은 저렇게 생겼어요.

데이터를 받아 파일로 저장시켜주는 역할을 합니다.

이제 이걸 제 코드에 적용시켜보겠습니다.

이렇게 말이죠.

 

그럼 data디렉토리에 title변수에 들어온 제목을 이름으로 파일이 저장될 것이고,

description은 위에 post.description을 통해 받은 본문내용이 파일의 내용으로 저장될 것입니다.

뒤에 UTF8은 표기방식이며,

function(error)은 콜백함수로, error가 났을때 동작하는 부분인데,

처리하지 않았습니다.

 

 

이렇게 완성시켰는데

문제가 발생했어요. 글작성이 끝나면 'Success'라고 나오고..

뒤로 버튼을 통해서 번거롭게 페이지를 이동해야 한단말이죠?

 

 

그래서 이 문제를 해결해보겠습니다.

 

4. 리디렉션 (Re-direction)

리디렉션, 리다이렉션이란

페이지에 사용자를 그 페이지가 아닌 다른 페이지로 보내는 것을 말합니다.

그럼 어딜 수정해야할까요?

 

 

response.writeHead부분을 수정해주었는데요.

원래 200이라고 성공페이지라는 표시를 해주었 던 것을

302라고 고쳐줬어요. (리다이렉션하는 약속된 규칙이에요)

 

그리고 {} 객체를 선언하고,

Location이라는 키값을 준뒤에, 보내줄 페이지를 value값으로 주었습니다.

그러면, 내가 정한 페이지로 사용자는 글이 완성된 뒤에 보내지게 됩니다.

저는 /id=$title이라고 해서 본인이 쓴 글을 바로 볼 수 있도록 보내주었습니다.

 

 

마지막으로 완성된 코드보면서 마무리 하겠습니다.

var http = require('http'); // http 모듈
var fs = require('fs'); // filesystem 모듈
var url = require('url'); // URL 모듈
var qs = require('querystring'); //querystring 모듈

//template 내용 HTML 뼈대를 함수로 만들기.
function templateHTML(title, list, body){ 
  return `
  <!doctype html>
  <html>
  <head>
    <title>WEB1 - ${title}</title>
    <meta charset="utf-8">
  </head>
  <body>
    <h1><a href="/">WEB</a></h1>
    ${list}
    <a href="/create">Create</a>
    ${body}
  </body>
  </html>
  `;
}

//리스트를 자동으로 만들어주는 코드를 함수로 만들기.
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+=1;
  }
  list = list +'</ul>';
  return list;
}

var app = http.createServer(function(request,response){
    var _url = request.url; //URL 요청
    var queryData = url.parse(_url, true).query; //URL을 분석하는 모듈 중, querystring 값
    var pathname = url.parse(_url, true).pathname; // URL을 분석하는 모듈 중, pathname 값
    //요청한 pathname이 루트값이라면 , if문이 실행
    if(pathname === '/'){ //Pathname이 루트 값일 경우 실행되는 코드
      if(queryData.id === undefined){
          //디렉토리를 읽는 함수 filelist변수가 파일목록. 배열로 반환.
          fs.readdir('./data', function(err, filelist){
            var title = 'Welcome';
            var description = 'Hello, Node.js';
            var list = templateList(filelist); //파일목록배열을 list변수에 할당.
            //HTML 뼈대 함수를 호출해 template 변수에 할당.
            var template = templateHTML(title, list, `<h2>${title}</h2>${description}`);
            response.writeHead(200); // 페이지가 정상적으로 출력되면 200, 오류페이지면 404.
            response.end(template); //화면출력하는부분에 template변수를 넣어줘서 출력.
          });

      //요청한 pathname이 루트값이 아니라면,대신 queryData.id가 있다면, else문 실행.
      } else {
        //디렉토리를 읽는 함수 filelist변수가 파일목록. 배열로 반환.
        fs.readdir('./data', function(err, filelist){
        //파일을 읽는 함수. description변수가 파일.
          fs.readFile(`data/${queryData.id}`, 'utf-8', function(err, description){
            var title = queryData.id
            var list = templateList(filelist);
            var template = templateHTML(title, list, `<h2>${title}</h2>${description}`);
            response.writeHead(200); // 페이지가 정상적으로 출력되면 200, 오류페이지면 404.
            response.end(template); //화면출력하는부분
          });
        });
      };

    //pathName이 Create라면 실행할 조건문선언.
    } else if(pathname ==='/create'){
          //디렉토리를 읽는 함수 filelist변수가 파일목록. 배열로 반환.
          fs.readdir('./data', function(err, filelist){
            var title = 'WEB - Create';
            var list = templateList(filelist); //파일목록배열을 list변수에 할당.
            //HTML 뼈대 함수를 호출해 template 변수에 할당.
            var template = templateHTML(title, list, `
            <!--form 태그-->
            <form action="http://localhost:3000/create_process" method="POST">
              <p><input type="text" name="title" placeholder="제목입력"></p>
              <p><textarea name="description" placeholder="본문입력"></textarea></p>
              <p><input type="submit"></p>
            </form> 
            `);
            response.writeHead(200); // 페이지가 정상적으로 출력되면 200, 오류페이지면 404.
            response.end(template); //화면출력하는부분에 template변수를 넣어줘서 출력.
          });

    //글쓰기 전송버튼을 실행시켰을 때, 작동할 페이지.
    } else if(pathname === '/create_process'){
      var body=''; // body에 빈 변수 선언.
      request.on('data', function(data){ //콜백함수. 서버에서 수신한 정보를 data 매개변수에 담음.
        body = body + data; //데이터가 전송될 때마다, 빈 변수 body에다가 전송된 데이터를 추가해줌.
      });
      request.on('end', function(){ // 데이터를 호출하다가, 데이터를 다 받은 뒤 할 실행될 함수.
        var post = qs.parse(body); //포스트에 바디에 저장된, 데이터를 querystring body부분을 통해 전달받음.
        var title = post.title;
        var description = post.description;
        fs.writeFile(`data/${title}`, description, 'utf8', function(error){
          //200 성공,  404 실패, 302 리다이렉션
          response.writeHead(302, {Location: `/?id=${title}`}); 
          response.end(); //화면출력하는부분
        })
      });

    
    //요청한 pathname이 루트값이 아니고, qeuryData.id가 없다면 즉, 오류페이지 실행.    
    } else {
      response.writeHead(404); // 페이지가 정상적으로 출력되면 200, 오류페이지면 404.
      response.end('NOT FOUND'); //화면출력하는부분
    }

});
app.listen(3000); // 3000번 포트를 서버로 사용하겠다. 선언.

 

 

- E N D -