티스토리 뷰

    console.log(__dirname + url);
    response.end(fs.readFileSync(__dirname + url));

node로 웹서버를 구동하는 코드 중, __dirname으로 해당 파일의 절대경로, url은 해당 파일의 이름으로 추정

이렇게 붙인 문자열로 파일의 절대경로를 생성하여 이를 읽고, response하는 구조로 보인다.

이렇게 해당 페이지에 있는 이미지도 가져온다.

 

* 문자열 " " or ' ' 이나 통일시켜 사용할 것

* 변수는 var로 선언 

 

var name = 'k8805';
var letter = 'Dear '+name+'\n\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. '+name+' Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa egoing qui officia deserunt mollit anim id est laborum. '+name;
console.log(letter);

var letter = `Dear ${name}

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ${name} Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa egoing qui officia deserunt mollit anim id est laborum. ${name}`;
console.log(letter);

문자열을 붙이거나 할때 +와 ' ' 를 반복적으로 써야하고 \n의 사용 등 코드 작성에 있어 직관적이지 않다.

이를 Template Literal을 이용해 직관적 표기를 할 수 있다. 아래 코드가 Template Literal을 활용한 예.

변수는 ${} 로 호출할 수 있음

 

URL

예시로 글쓰는 지금 이 링크를 보면

https://getcomponent.tistory.com/manage/newpost/?type=post&returnURL=%2Fmanage%2Fposts%2F 

 

https 는 통신규약

getcomponent.tistory.com은 host(domain) 이며

여기에는 웹이라? 생략되었지만 port가 :3000 처럼 추가된다.

/manage/newpost 는 이 페이지의 path

그리고 ?type=post&returnURL=%2Fmanage%2Fposts%2F 가 바로 Query string이다.

이 Query string을 통해 사용자마다 다른 화면을 보여주게 되는것이다. 

/?로 시작하며, &로 구분된다. 값은 =로 표현된다.

? type은 post 이고, returnURL은 %2Fmanage%2Fposts%2F 이다. 라고 해석이 된다.

이 Query string은 request.url에서 불러올 수 있다. 예를들어, 이 페이지에 console.log(request.url);을 한다면 저게 출력되지 않을까...? 했으나 참조에러가 난다. 아직 서버 띄우는 코드를 이해못해서 그런지 request가 내부에서 선언된 변수인듯

아무튼!!

..

오늘공부 적어둔거 다 날아갔네 ㅠㅠ

 

함수도 똑같다. function 함수이름(파라미터1, 파라미터2, ...) { ~~~~ }

다만 파라미터 형태를 안적어줘도됨

 

var fs = require('fs');

//readFileSync
console.log('A');
var result = fs.readFileSync('syntax/sample.txt', 'utf8');
console.log(result);
console.log('C');
//출력 : A B C


//readFile Async
console.log('A');
fs.readFile('syntax/sample.txt', 'utf8', function(err, result) {
  //file 읽음 -> function 실행(비동기)
  console.log(result);
});
console.log(result);
console.log('C');
//출력 : A C B

같은 파일 읽기도 Sync냐 Async냐에 따라 실행결과가 달라짐을 보여주는 코드

파일의 입출력은 시간이 오래걸리기 때문에, 비동기적 실행에 의해 C가 먼저 처리된 것임

저 readFile내 function이 callback 기능임. 파일다읽었으면 콜백해~ st.

성능면에서 Async가 좋음

var a = function() {
  console.log('A');
}
a();

함수의 또다른 방식. 이렇게 함수에 이름을 붙이지 않는 방식을 '익명 함수' 라고 부른다.

변수 a에 저장한이후, a를 함수 형식으로 호출하면 해당함수가 실행되는 걸 확인할 수 있다.

이게 callback의 실행방식과 유사하다

function slowfunc(a){
  a();
}
slowfunc(a)

저렇게 선언된 함수를 이렇게 선언하면 callback 방식이라나?

<form action="http://localhost:3000/process_create">
  <p><input type="text" name="title"></p>
  <p><textarea name="description" rows="8" cols="80"></textarea></p>
  <p><input type="submit" name="submit" value="submit"></p>
</form>

form은 웹 이용자가 입력한 값을 url 쿼리 형태로 받아올 수 있게 한다.

위와같이 작성하고 submit을 누르게 되면,

form으로 지정해준 process_create 경로 뒤에 쿼리 형태로 쓰여지는 걸 확인할 수 있다. 여기서 id는 name으로 설정된 값

그렇지만, data 생성 삭제와 관련 정보는 쿼리 스트링으로 받아오는 것은 부적합하다. 누구나 해당 url로 삭제와 수정 등의 접근이 가능해지기 때문. 

 

정리하자면, 서버에서 데이터를 가져올 때는 쿼리스트링 적합! 서버의 데이터를 수정할 때는 쿼리스트링 부적합!

<form action="http://localhost:3000/process_create" method="post">

이는 method 기입 하나만으로도 해결이 된다. 쿼리스트링으로 적히지 않게되며, 커다란 data도 전송이 가능해진다.

따라서 form을 구상할때는 method 설정이 중요하겠군아

 

pm2 간단한 사용

pm2 start 파일이름.확장자

--watch : 파일 수정시 알아서 재시작

pm2 stop 파일이름.확장자

pm2 log : 로그출력

 

받아온 쿼리를 데이터화하기!

앞서서 /creat_process로 이동시키는 post type의 form을 생성하였는데, 이렇게 post형식의 data를 가져오는 건 어떻게 할까?

검색한다

        var body = '';
        request.on('data', function(data){  //data를 비동기식으로 하나씩 받아오고 callback
          body += data;
        });
        request.on('end', function() {  //정보가 더 이상 들어오지 않으면 callback
          var post = qs.parse(body);  //지금까지 받아와 저장한 body를 parsing 하여 post에 저장

post data는 앞서 http의 createServer의 callback의 parameter 중 하나인 request에게서 가져올 수 있다.

이렇게 받아온 값은 어떤 형식으로 저장되는지 log로 확인해보면

이렇게 저장된다. post.title과 post.descrption으로 값을 받아올 수 있음

이제 이 값을 이용해 파일 생성을 해야한다. 

          fs.writeFile(`data/${title}`, description, 'utf8', function(err) {
            response.writeHead(200);
            response.end('success');
          });

검색해보면 나온다. 전에 이해했으면 파일 쓰는것도 쉽다. 이제 success만 달랑 적힌 화면으로 갈 게 아니라, 다시 메인 화면으로 돌아가서 내가 제출한 파일이 리스트에 반영이 되었는지 확인할 수 있어야 한다.

이를 리다이렉팅(Redirecting)이라고 한다.

            response.writeHead(302, {Location : `/?id=${title}`});

찾아보면 아주 간단하다. 

 

잠시 여기서 미싱토큰 문제로 골머리를 앓았다;; 

 

이제 update기능을 추가할건데, 글 제목과 내용을 불러와서 사용자가 이를 수정하고 제출하면 이를 기반으로 파일을 수정한다.

이를 위해서는 일단 else if 로 /update가 pathname일때의 조건문이 추가되어야 할 것이다.

그리고 선택된 파일 목차의 id를 가져와야 파일을 끌어와 열테니,

      fs.readdir('data', function(err, filelist){
        var list = templateList(filelist);
        fs.readFile(`data/${queryData.id}`, 'utf8', function(err, description){
        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" rows="8" cols="80" placeholder="description">${description}</textarea></p>
            <p><input type="submit"></p>
          </form>
          `,
        `<a href="/create">create</a> <a href="/update/?id=${title}">update</a>`);

이제 여기서 폼에 hidden값으로 id에 title을 부여하고(기존이름이 변경될 경우 파일 수정이 안되기 때문에 저장해두는 것)

value로 파일의 title과 descrption을 배치한다.

이렇게 텍스트가 적혀 나오게 되고, 사용자가 이를 수정하여 제출하면

이런 형태로 서버에 전송되는 것이다. 그러면 이제 할 일은 update_process pathname일때 이 post를 extract하고 파일처리를 해야한다. 

    else if (pathname === '/update_process') {
      var body = '';
      request.on('data', function(data){  //data를 비동기식으로 하나씩 받아오고 callback
        body += data;
      });
      request.on('end', function() {  //정보가 더 이상 들어오지 않으면 callback
        var post = qs.parse(body);  //지금까지 받아와 저장한 body를 parsing 하여 post에 저장
        var id = post.id;
        var title = post.title;
        var description = post.description;
        console.log(post);
      });
    }

앞의 /create_process와 같은 코드이되, 이제는 id값도 추가되었으니 잊지않고 변수 추가도 해준다. 이렇게 한 후 로그를 보면

값이 무사히 보내지고 있다. 

이제 정말 남은 것은 파일 변경 방법이다.

검색하자

구조는 간단히 fs.rename(~~~~function (err) { fs.writeFile(~~~~function(err){redirecting...}} 이다.

그러니까.. 이름 바꾸고 콜백함수에서 내용바꾸고 콜백펑션에서 리다이렉팅으로 바뀐 파일 주소로 연결하는 것임

        fs.rename(`data/${id}`, `data/${title}`, function(err){ //파일 이름 수정
          fs.writeFile(`data/${title}`, description, 'utf8', function(error) {  //파일 내용 수정
            //redirecting...
            response.writeHead(302, {Location: `/?id=${title}`});
            response.end();
          })
        });

그리고 진짜마지막으로 글 삭제 기능. 앞서 말했지만 이런 류의 처리는 쿼리스트링을 이용하면 안되고!!

폼을 이용해 post형태, 그리고 hidden 값으로 삭제하고자 하는 파일 이름을 넘겨주고 처리한다.

이를 위해선 id가 선택된 상황에서의 파일리스트 템플릿 하단에 delete 폼을 추가해야한다!

      } else {  //id값이 정의된 경우
        fs.readdir('data', function(err, filelist){
          var list = templateList(filelist);
          fs.readFile(`data/${queryData.id}`, 'utf8', function(err, description){
          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>
          `);
          response.writeHead(200);
          response.end(template);
        });
      });
      }

저 템플릿을 보면 폼으로 액션은 delete_process, method post, hidden값으로 타이틀 값을 id에 부여하고, delete 이름의 submit 버튼을 생성했다.

이제 여기서 delete 파일을 누른다면, 해당 페이지의 id값이 서버로 넘겨지고, 페이지는 /delete_process로 넘어가게 될 것이다.

    else if (pathname = '/delete_process') {
      var body = '';
      request.on('data', function(data){  //data를 비동기식으로 하나씩 받아오고 callback
        body += data;
      });
      request.on('end', function() {  //정보가 더 이상 들어오지 않으면 callback
        var post = qs.parse(body);  //지금까지 받아와 저장한 body를 parsing 하여 post에 저장
        var id = post.id;
      });    }

이전의 process를 가져오되, 불필요한 값들은 모두 지움. 이제 nodejs에서 파일 삭제하는 함수를 검색해보면 된다.

검색하자

        fs.unlink(`data/${id}`, function(error){
          response.writeHead(302, {Location : `/`});
          response.end();

이렇게 구현하고, callback으로 home으로 redirecting했다. 

 

헥헥 오늘은 여기까지 낼은 객체 시작. 


객체의 하나씩 이용하는 방법

var roles = { //객체
  'programmer':'Ash',
  'designer' : 'Park',
  'manager' : 'Hyun'
}
console.log(roles.designer);
for(var name in roles) {
  console.log('object test : ' + name);
  console.log('object name : ' + roles[name]);
}

var ~~ in ___ 을 이용한 for문으로 접근 가능, 그 값은 ___[~~~] 로 가져올수있다.

var f = function() {  //함수를 변수에 저장 - 객체
  console.log(1+1);
  console.log(1+2);
}
var a = [f]; //배열의 원소로서 함수를 저장할 수 있다.
a[0]();

var o = { //o의 프로퍼티로 f를 부여
  func:f
}
o.func();
var o = {
  v1:'v1', v2:'v2',
  f1:function() {
    console.log(this.v1);
  },
  f2:function() {
    console.log(this.v2);
  }
}

o.f1();
o.f2();

그리고 객체면 역시 함수를 담아야겠지?

저런 방식으로 코딩하면 된다


이젠 기존 코드에 객체를 도입시킬 차례!

var template = {
  html : function(title, list, body, control){
    return `
    <!doctype html>
    <html>
    <head>
      <title>WEB1 - ${title}</title>
      <meta charset="utf-8">
    </head>
    <body>
      <h1><a href="/">WEB</a></h1>
      ${list}
      ${control}
      ${body}
    </body>
    </html>
    `;
  },
  list : function(filelist) {
    list = '<ul>';
    for(i = 0; i < filelist.length; i++) {
      list += `<li><a href="/?id=${filelist[i]}">${filelist[i]}</a></li>`
    }
    list += '</ul>';
    return list;
  }
}

이렇게 template라는 객체를 만들어서, 기존의 templateHTML과 templateList를 내부 프로퍼티로 바꿨다.

물론 이뿐만이 아니라 이를 이용하는 코드 수정을 모두 해줘야 한다.

원래 쓰고있던 template 이름도 변경하는걸 잊지 말 것!

이런 방식의 코드 정리를 'refactoring'이라고 한다.


모듈!

언제까지고 한 파일에 모든 코드를 적어넣을텐가?

이를 파일단위로 쪼개는 것이 모듈이다. 

var M = {
  v:'v',
  f:function(){
    console.log(this.v);
  }
}

M.f();

이를 기존 한 파일에 모두 작성된 코드라고 하자.

보통 이 과정에서 수많은 변수와 객체 선언을 하게되고는 한다.

그래서 이 부분만 모듈화하여 다른 파일로 옮겨보자.

var M = {
  v:'v',
  f:function(){
    console.log(this.v);
  }
}

module.exports = M; //M에 담겨있는 객체를 이 모듈 바깥에서 사용할 수 있도록 exports 하겠다.

새로운 파일에 객체만 적어두고, 아래에 해당 선언을 해준다. 이는 약속이다.

그리고 기존 파일은(물론 객체부분은 지우고) 이렇게 해준다.

var part = require('./mparts.js'); //해당 모듈을 loading한 결과를 M에 저장한다

// M.f();
part.f();

이젠 다시 기존 코드에 모듈을 적용시켜본다.

아까 객체화한 template를 모듈화해보는데... 

module.exports = {
  html : function(title, list, body, control){
    return `
    <!doctype html>
    <html>
    <head>
      <title>WEB1 - ${title}</title>
      <meta charset="utf-8">
    </head>
    <body>
      <h1><a href="/">WEB</a></h1>
      ${list}
      ${control}
      ${body}
    </body>
    </html>
    `;
  },
  list : function(filelist) {
    list = '<ul>';
    for(i = 0; i < filelist.length; i++) {
      list += `<li><a href="/?id=${filelist[i]}">${filelist[i]}</a></li>`
    }
    list += '</ul>';
    return list;
  }
}

위처럼 module.exports로 바로 선언을 해버리면 아래에 한 줄을 더 붙일 필요가 없다.

var template = require('./lib/template.js');

그리고 상단부분에 해당 줄 추가해주기

 


보안에 관하여

 

쿼리스트링을 오염시켜 서버에 있는 외적 정보까지 빼돌려질 수 있다.

예를들어, http://localhost:3000/?id=CSS 의 ?id=CSS를 ../password.js 로 바꿔버린다면

          fs.readFile(`data/${queryData.id}`, 'utf8', function(err, description){

해당 부분에 값이 어떻게 들어가겠는가?

          fs.readFile(`data/../password.js`, 'utf8', function(err, description){

이렇게 들어가서, 파일을 읽어오는게 data 상위 디렉토리의 password.js을 읽어와버리는 대참사가 발생한다.

이는, 저러한 경로 처리 방식을 이용해 ../나 .../ 등으로 내가 접근을 허용한 폴더의 파일 이외의 것에 접근이 허용되기때문에 발생한다. 그렇기에 쿼리스트링으로 주어지는 경로를 필터링해본다.

var path = require('path');
path.parse('../password.js').base;	//password.js

이를 위해 path.parse의 base를 이용한다. 쿼리스트링에 ../ 등의 값이 주어져도 해당 파일값만을 추출하게 만든다.

따라서 이 필터를 적용하게되면 위와같이 쿼리 스트링을 ../password.js로 바꿔도, 내부 필터에서 이를 password.js로 바꿔버리기 때문에 data폴더 외부로의 접근이 불가능하게 된다.

이는 또 다른 공격방식이다. 이와같이 제출해버리면

XSS 항목을 누를때마다 위와같이 나온다.

이는 사용자가 입력방식을 이용해 허가받지 못한 스크립트를 규제없이 이용할 수 있다는 점에서 무시무시한 허점이다.

이런 문제는 <script>가 자바스크립트의 문법으로 작동하지 않도록, 그저 문자의 나열이도록 처리를 해주면 된다.

직접 처리한다면야, 모든 꺽쇠, < > 를 entity로 변경해주면 된다. 그렇지만 세상에는 좋은 라이브러리들이 있으니 이를 이용한다.

참고로 이런걸 막는 걸 sanitize(살균) 라고 한다.

npm에서 해당 기능뿐만이 아닌 많은 패키지를 찾아볼 수 있다.

물론 패키지를 쓸 때는 신용할만한 패키지인지를 확인하도록 하자.

cmd 창에 위의 install을 복사해서 쳐보면 해당 폴더에 성공적으로 추가된다.

이를 참고하여 var sanitizeHtml = require('sanitize-html'); 으로 모듈을 삽입한다.

dirty 부분에 기존 데이터, clean이 후처리된 data일것이다.

descrption와 title을 살균시켜서 적용해보면

이렇게 강력하게 살균된 것을 볼 수 있었다.

외에 예외처리할 태그 등 설정이 가능하다. 

 


API?

 

Application Programming Interface. 해당 언어에서 정의하고 안내하는 인터페이스? filestream(맞냐?)의 readdir이나 readFile 등등이 해당한다. 해당 언어에서 제공되는 완성된 모듈?

 

이부분은 중요한데 집중이 전혀 안돼서 내일 이어보고

이렇게 node.js의 기초를 닦았으니, 병렬적으로 할 게 2개가 있다.

 

1. 간단한 웹 구현

2. node.js을 이용한 알고리즘 공부 시작

'■ FE 로드맵 > ◻ WEB 기초' 카테고리의 다른 글

javascript로 html과 상호작용하기  (0) 2022.07.27
MySQL 겅부  (0) 2022.07.21
javascript 백준 기초  (0) 2022.07.20
CSS 겅부  (0) 2022.07.13
WEB javascript 기초  (0) 2022.06.02
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/09   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함