NodeJS - 간단한 홈페이지 구현

KimJH_94·2023년 1월 10일
0

NodeJS

목록 보기
1/4
post-thumbnail

1.기본 모습 구현

// 최초에 해야 하는 것은 필요한 라이브러리를 로드하는 것 (require 함수를 사용)
var http = require('http');     // 'http'라는 객체를 로드
var fs = require('fs');         // 파일을 로드하는 'fs'객체
var url = require('url');       // url이라는 모듈을 사용할 것이다.

// http.createServer(); 시작 (서버객체 생성)
// function(req, resp){} : 요청 처리를 위한 함수 (요청을 받았을 때의 처리) 
// request = 클라이언트의 요청 / response = 서버에서 클라이언트로 리턴
var app = http.createServer(function(request,response){     
    var _url = request.url;                             // 요청받은 url 주소를 변수로 담음

    // ----- node.js에서 url을 파싱하여 메타데이터 (쿼리스트링)을 읽어오는 방법!!------
    // Node.js의 url모듈의 parse 기능 사용 : _url을 분석해 (사용자가 요청한 URL주소) 쿼리 스트링 문자열만 추출하는 코드
    // url.parse(_url, true)는 URL에서 쿼리 스트링을 추출해 객체로 변환해주는 코드
    // ex) 'http://site.com/?id=jh&job=dev'
    // queryData = {id : 'jh', job : 'dev'}
    // console.log(queryData.id); -> jh
    var queryData = url.parse(_url, true).query;        
    var pathname = url.parse(_url, true).pathname;      
    
    if(pathname === '/'){       // pathname이 /을 가르킬 때

        if(queryData.id === undefined){ // homepage로 가는 경우 (localhost:3000/ 이후가 정의되지 않은 경우)
            
                var title = 'Welcome';
                var description = 'Hello, Node.js';
                var template = 
                `
                <!doctype html>
                <html>
                <head>
                <title>WEB1 - ${title}</title>
                <meta charset="utf-8">
                </head>
                <body>
                <h1><a href="/">WEB</a></h1>
                <ul>
                    <li><a href="/?id=HTML">HTML</a></li>
                    <li><a href="/?id=CSS">CSS</a></li>
                    <li><a href="/?id=JavaScript">JavaScript</a></li>
                </ul>
                <h2>${title}</h2>
                <p>${description}</p>
                </body>
                </html>
                `;
                
                // writeHead는 response 객체의 메소드에서 헤더 정보를 응답에 작성해서 내보내는 것
                // resp.writeHeaed(20, {'Content-Type' : 'text/plain'});
                // 첫 번째 인자는 상태 코드를 지정하고, 두번째 인수에 해더 정보를 연관 배열로 정리
                response.writeHead(200);    
                // resp.write(); : 헤더 정보의 다음에 바디 부분이 되는 콘텐츠를 write로 작성 가능. 여러번 호출 가능

                // resp.end(); : 내용 내보내기가 완료되면 end로 콘텐츠 출력 완료. 
                // 인수로 내보낼 내용의 값을 지정할 수 있다. (template를 넣은 것 처럼) -> 그렇게 하면 인수의 값을 쓴 후에 내용을 완료한다.
                response.end(template);

                // 위 3개로 클라이언트에 반환 내용은 모두 쓸 수 있다.
            
        } else{         // homepage에서 다른 페이지로 이동하는 경우 (localhost:3000/?id=HTML, CSS, JavaScript)
            // 파일로드는 fs모듈로 사용. require 함수로 fs 읽고 그 안에있는 메소드를 호출
            // fs.readFile(파일경로, 인코딩, 콜백함수){} 
          	// -> (콜백함수 | 정의 : 함수에 파라미터로 들어가는 함수 / 용도 : 순차적으로 실행하고 싶을 때 씀)
            // readFile은 반환값이 없다. 비동기적으로 실행되는 처리이기 때문. (동기적 실행하려면 readFileSync)
            // 읽기 작업은 백그라운드에서 이뤄지고, 읽기 시작하는 순간 바로 다음 작업으로 진행하도록 설계. 
          	// 로드가 완료되면 미리 설정해둔 처리를 호출하고 읽기 후의 처리를 진행시킴
            // 이러한 "작업이 끝나면 나중에 호출되는 함수"를 콜백함수라고 함. 
          	// (가져올 파일의 경로에서 파일 로드 -> 인코딩 -> 로드 완료 후 콜백함수에서 처리)
            fs.readFile(`data/${queryData.id}`, 'utf-8', function(err, description){ // 비동기 처리이기 때문에, 비동기 작업이 완료된 이후에 응답처리를 해준다.
                var title = queryData.id;
                var template = `
                <!doctype html>
                <html>
                <head>
                <title>WEB1 - ${title}</title>
                <meta charset="utf-8">
                </head>
                <body>
                <h1><a href="/">WEB</a></h1>
                <ul>
                    <li><a href="/?id=HTML">HTML</a></li>
                    <li><a href="/?id=CSS">CSS</a></li>
                    <li><a href="/?id=JavaScript">JavaScript</a></li>
                </ul>
                <h2>${title}</h2>
                <p>${description}</p>
                </body>
                </html>
                `;
                response.writeHead(200);
                response.end(template);
            });
        }
        
    }else{      // 쌩뚱맞은 pathname을 가질 때 -> 404페이지
        response.writeHead(404);
        response.end('Not Found');
    }
}); // http.createServer 종료

// http.createServer 종료 되어 http.Server 객체의 준비가 되면, listen 메소드 실행.
// 서버는 대기상태가 됨
// 클라이언트의 요청이 있으면 받아서 처리할 수 있음
// app.listen(포트번호, 호스트이름, 백로그, 콜백함수);
app.listen(3000); 

2. HTML의 list 부분 자동화 기능 추가

var http = require('http');
var url = require('url');
var fs = require('fs');

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 === '/'){
        if(queryData.id === undefined){
          	// readdir(파일 경로, 콜백함수(error, data){}) -> 원하는 폴더의 파일을 배열로 리턴해줌
            fs.readdir('./data', function(error, filelist){ 
                var title = 'Welcome';
                var description = 'Welcome NodeJs';
                var list = '<ul>'; // list를 변수로 설정하여, 반복문을 태워 폴더 내 파일 명으로 돌려줌
                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>';
                var template = 
                `
                <!doctype html>
                <html>
                <head>
                <title>WEB1 - ${title}</title>
                <meta charset="utf-8">
                </head>
                <body>
                <h1><a href="/">${title}</a></h1>
                ${list}
                <h2>${title}</h2>
                <p>${description}</p>
                </body>
                </html>
                `;
                response.writeHead(200);
                response.end(template);
            })
        }else{
            fs.readdir('./data', function(error, filelist){
                var title = 'Welcome';
                var description = 'Welcome NodeJs';
                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>';

                fs.readFile(`data/${queryData.id}`, 'utf-8', function(err, description){
                    var title = queryData.id;
                    var template = 
                    `
                    <!doctype html>
                    <html>
                    <head>
                    <title>WEB1 - ${title}</title>
                    <meta charset="utf-8">
                    </head>
                    <body>
                    <h1><a href="/">${title}</a></h1>
                    ${list}
                    <h2>${title}</h2>
                    <p>${description}</p>
                    </body>
                    </html>
                    `;
                    response.writeHead(200);
                    response.end(template);
                });
            });
        }
    }else{
        response.writeHead(404);
        response.end('Not Found');
    }
});
app.listen(3000);

3. 반복되는 부분 함수화

var http = require('http');
var url = require('url');
var fs = require('fs');

function templateHTML(title, list, body){ // HTML 템플릿 공통되는 부분 함수화
    return `
    <!doctype html>
    <html>
    <head>
        <title>WEB - ${title}</title>
        <meta charset="utf-8">
    </head>
    <body>
        <h1><a href="/">WEB2</a></h1>
        ${list}
        ${body}
    </body>
    </html>
    `;
}

function templateList(filelist){    // List 함수화
    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;
}

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 === '/'){
        if(queryData.id === undefined){
            fs.readdir('./data', function(error, filelist){
                var title = 'Welcome';
                var description = 'Welcome Node JS';
                var list = templateList(filelist);  // List 함수 대입
                var template = templateHTML(title, list, `<h2>${title}</h2>${description}`); // HTML template 함수 대입
                response.writeHead(200);
                response.end(template);
            })
        }else{
            fs.readdir('./data', function(error, filelist){
                fs.readFile(`data/${queryData.id}`, 'utf-8', function(err, description){
                    var title = queryData.id;
                    var list = templateList(filelist);  // List 함수 대입
                    var template = templateHTML(title, list, `<h2>${title}</h2>${description}`); // HTML template 함수 대입
                    response.writeHead(200);
                    response.end(template);
                });
            });
        }
    }else{
        response.writeHead(404);
        response.end('Not Found');
    }
});
app.listen(3000);

4. form 태그 추가(제목과 글 넣으면 자동으로 파일 저장되며, 해당 페이지로 리다이렉션 기능)

var http = require('http');
var url = require('url');
var fs = require('fs');
var qs = require('querystring');

function templateHTML(title, list, body){
    return `
    <!doctype html>
    <html>
    <head>
        <title>WEB1 - Welcome</title>
        <meta charset="utf-8">
    </head>
    <body>
        <h1><a href="/">${title}</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++;
    }
    list = list + '</ul>';
    return list;
}

var app = http.createServer(function(req, resp){
    var _url = req.url;
    var queryData = url.parse(_url, true).query;
    var pathname = url.parse(_url, true).pathname;


    if(pathname === '/'){
        if(queryData.id === undefined){
            fs.readdir('./data', function(err, filelist){
                var title = 'Welcome';
                var description = 'Welcome NodeJS';
                var list = templateList(filelist);
                var template = templateHTML(title, list, `<h2>${title}</h2>${description}`);
                resp.writeHead(200);
                resp.end(template);
            })
        }else{
            fs.readdir('./data', function(err, filelist){
                fs.readFile(`./data/${queryData.id}`, function(err, description){
                    var title = queryData.id;
                    var list = templateList(filelist);
                    var template = templateHTML(title, list, `<h2>${title}</h2>${description}`);
                    resp.writeHead(200);
                    resp.end(template);
                });
            });
        }
    }else if(pathname === '/create'){ // create 버튼 눌렀을 때 날라오는 곳
        fs.readdir('./data', function(err, filelist){ // 홈페이지 코드 재활용
            var title = 'Web - create';
            var list = templateList(filelist);
            var template = templateHTML(title, list, `
            <form action="http://localhost:3000/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>
            `);
            resp.writeHead(200);
            resp.end(template);
        });
    }else if(pathname === '/create_process'){   // form에서 데이터를 작성하고 날렸을 때 날라오는 곳
        var body='';
        req.on('data', function(data){
            body = body + data; // 콜백이 실행될 때 마다 데이터를 body에 추가해줌
        }); // 웹브라우저가 포스트로 데이터 전송할 때 데이터가 엄청 많으면 데이터를 한번에 처리하다가는 컴퓨터에 무리가 갈수도?
            // 노드js에서는 데이터가 많은 경우를 대비해서 사용방법을 제공
            // 특정한 양 만큼 서버에서 수신할 때 마다 콜백함수를 호출하도록 약속 (100에서 20만큼)
            // data라는 인자를 통해서 수신한 정보를 넘겨주기로 약속함
        req.on('end', function(){ // 더이상 들어올 정보가 없으면 end에 해당되는 콜백을 실행 (실행되면 정보 수신이 끝났다는 의미)
            var post = qs.parse(body);  // post 데이터에 post 정보를 파싱해서 넣어줌 정보를 객체화!!!!!!!
            var title = post.title;
            var description = post.description;
            fs.writeFile(`data/${title}`, description, 'utf-8', function(err){
                // 200은 성공 
                // 302는 임시적으로 새로운 URL로 이동했다는 뜻 (쇼핑몰에서일시적으로 품절되었음을 알릴 때) 
                // 301은 영구이동 (웹사이트가 도메인을 변경했거나 개편했을 때 사용)
                resp.writeHead(302, {Location: `/?id=${title}`}); 
                resp.end('success');
            })
        })
    } else{
        resp.writeHead(404);
        resp.end('Not Found');
    }
});
app.listen(3000);

5. 글 수정, 삭제 기능 추가

var http = require('http');
var url = require('url');
var fs = require('fs');
var qs = require('querystring');

function templateHTML(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> 
    `;
}

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++;
    }
    list = list + '</ul>';
    return list;
}

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 === '/'){
        if(queryData.id === undefined){
            fs.readdir('./data', function(err, filelist){
                var title = 'Welcome';
                var description = 'Welcome 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{
            fs.readdir('./data', function(err, filelist){
                fs.readFile(`data/${queryData.id}`, 'utf-8' ,function(err, description){
                    var title = queryData.id;
                    var list = templateList(filelist);
                    // delete도 post방식으로 보내야함 (링크로 queryString 태워 보내면 안됨)
                    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" d>
                            <input type="hidden" name="id" value="${title}">
                            <input type="submit" value="delete">
                        </form>`
                    ); 
                        
                    response.writeHead(200);
                    response.end(template);
                });
            });
        }
    }else if(pathname === '/create'){
        fs.readdir('./data', function(err, filelist){
            var title = 'Web - createform';
            var list = templateList(filelist);
            var template = templateHTML(title, list, `<h2>${title}</h2>
            <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>`, ''); // 여기엔 create나 update는 필요없지만, 공백문자라도 줘서 파라미터 맞춰주기
            response.writeHead(200);
            response.end(template);
        });
    }else if(pathname === '/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, 'utf-8', function(err){
                response.writeHead(302, {Location : `/?id=${title}`});
                response.end();
            })
        });
    }else if(pathname === '/update'){
        fs.readdir('./data', function(err, filelist){
            fs.readFile(`data/${queryData.id}`, 'utf-8', 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>`); 
                    // 아이디 값이 있으면 수정 나오도록 (localhost:3000/update?id=HTML 과 같이 queryString으로 누굴 수정할 지 정해줘야함)
                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;
            var title = post.title;
            var description = post.description;
            fs.rename(`data/${id}`, `data/${title}`, function(err){
                fs.writeFile(`data/${title}`, description, 'utf-8', function(err){
                    response.writeHead(302, {Location : `/?id=${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(err){
                response.writeHead(302, {Location : `/`});
                response.end();
            })
        });
    }else{
        response.writeHead(404);
        response.end('Not Found');
    }
});
app.listen(3000);

6. 함수를 객체에 담아 리팩토링

var http = require('http');
var url = require('url');
var fs = require('fs');
var qs = require('querystring');

// refactoring (리팩토링 : 코드 간결화 시키는 작업)
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){
        var list = '<ul>';
        var i = 0;
        while(i < filelist.length){
            list = list + `<li><a href='/?id=${filelist[i]}'>${filelist[i]}</a></li>`
            i++;
        }
        list = list + '</ul>';
        return list;
    }
}

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 === '/'){   // 홈페이지로 갈 때
        if(queryData.id === undefined){    // 홈페이지로 갈 때
            fs.readdir(`data/`, function(err, filelist){
                var title = 'Web';
                var description = 'Welcome Node JS';
                // 파일만 있던 세상에 폴더가 만들어진 느낌!!
                // template이라는 변수에 담아주면 객체 이름과 겹치기 때문에 변수명을 변경해주자!
                var list = template.list(filelist);
                var html = template.HTML(title, list, 
                    `<h2>${title}</h2>${description}`, 
                    `<a href='/create'>create</a>`);
                response.writeHead(200);
                response.end(html);
            });
        }else{  // 리스트에 링크를 눌렀을 때
            fs.readdir(`data/`, function(err, filelist){
                fs.readFile(`data/${queryData.id}`, 'utf-8', function(err, description){
                    var title = queryData.id;
                    var list = template.list(filelist);
                    var html = template.HTML(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(html);
                });
            });
        }
    }else if(pathname === '/create'){
        fs.readdir(`data/`, function(err, filelist){
            var title = 'Web';
            var form = `
            <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>
            `;
            var list = template.list(filelist);
            var html = template.HTML(title, list, 
                `<h2>${title}</h2>${form}`, 
                `<a href='/create'>create</a>`);
            response.writeHead(200);
            response.end(html);
        });
    }else if(pathname === '/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, 'utf-8', function(err){
                response.writeHead(302, {Location: `/?id=${title}`});
                response.end();
            });           
        });
    }else if(pathname === '/update'){
        fs.readdir(`data/`, function(err, filelist){
            fs.readFile(`data/${queryData.id}`, function(err, description){
                var title = queryData.id;
                var form = `
                <form action='/update_process' method='post'>
                    <p><input type='hidden' name='id' value='${title}'></p>
                    <p><input type='text' name='title' value='${title}'></p>
                    <p><textarea name='description'>${description}</textarea></p>
                    <p><input type='submit'></p>
                </form>
                `;
                var list = template.list(filelist);
                var html = template.HTML(title, list, 
                    `<h2>${title}</h2>${form}`, 
                    `<a href='/create'>create</a>
                     <a href='/update'>update</a>`);
                response.writeHead(200);
                response.end(html);
            });
        });
    }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;
            var title = post.title;
            var description = post.description;
            fs.rename(`data/${id}`, `data/${title}`, function(err){
                fs.writeFile(`data/${title}`, description, function(err){
                    response.writeHead(302, {Location: `/?id=${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(err){
                response.writeHead(302, {Location : `/`});
                response.end();
            });
        });
    }else{  // 쌩뚱맞은 페이지로 갈 때
        response.writeHead(404);
        response.end('Not Found');
    }
});
app.listen(3000);

7. 함수의 모듈화

  1. 객체화 시킨 함수를 lib/template.js 에 담아준다. 이 때 모듈화를 시킨다.
module.exports = {
  HTML : function(title, list, body, control){
        return `
        <!doctype html>
        <html>
        <head>
            <title>WEB2 - ${title}</title>
            <meta charset="utf-8">
        </head>
        <body>
            <h1><a href="/">Web</a></h1>
            ${list}
            ${control}
            ${body}
        </body>
        </html>     
        `;
    },
    list : function(filelist){
        var list = '<ul>';
        var i = 0;
        while(i < filelist.length){
            list = list + `<li><a href='/?id=${filelist[i]}'>${filelist[i]}</a></li>`
            i++;
        }
        list = list + '</ul>';
        return list;
    }
}
  1. main.js에서 함수를 모듈에서 꺼내 사용한다.
var http = require('http');
var url = require('url');
var fs = require('fs');
var qs = require('querystring');

// lib/template.js 에 있는 모듈을 꺼내 template에 담아 사용 (객체형태로 저장되어 있음)
// template라는 폴더를 새롭게 만들어줬다고 생각하면 됨
var template = require('./lib/template.js');

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 === '/'){   // 홈페이지로 갈 때
        if(queryData.id === undefined){    // 홈페이지로 갈 때
            fs.readdir(`data/`, function(err, filelist){
                var title = 'Web';
                var description = 'Welcome Node JS';
                // 파일만 있던 세상에 폴더가 만들어진 느낌!!
                // template이라는 변수에 담아주면 객체 이름과 겹치기 때문에 변수명을 변경해주자!
                var list = template.list(filelist);
                var html = template.HTML(title, list, 
                    `<h2>${title}</h2>${description}`, 
                    `<a href='/create'>create</a>`);
                response.writeHead(200);
                response.end(html);
            });
        }else{  // 리스트에 링크를 눌렀을 때
            fs.readdir(`data/`, function(err, filelist){
                fs.readFile(`data/${queryData.id}`, 'utf-8', function(err, description){
                    var title = queryData.id;
                    var list = template.list(filelist);
                    var html = template.HTML(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(html);
                });
            });
        }
    }else if(pathname === '/create'){
        fs.readdir(`data/`, function(err, filelist){
            var title = 'Web';
            var form = `
            <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>
            `;
            var list = template.list(filelist);
            var html = template.HTML(title, list, 
                `<h2>${title}</h2>${form}`, 
                `<a href='/create'>create</a>`);
            response.writeHead(200);
            response.end(html);
        });
    }else if(pathname === '/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, 'utf-8', function(err){
                response.writeHead(302, {Location: `/?id=${title}`});
                response.end();
            });           
        });
    }else if(pathname === '/update'){
        fs.readdir(`data/`, function(err, filelist){
            fs.readFile(`data/${queryData.id}`, function(err, description){
                var title = queryData.id;
                var form = `
                <form action='/update_process' method='post'>
                    <p><input type='hidden' name='id' value='${title}'></p>
                    <p><input type='text' name='title' value='${title}'></p>
                    <p><textarea name='description'>${description}</textarea></p>
                    <p><input type='submit'></p>
                </form>
                `;
                var list = template.list(filelist);
                var html = template.HTML(title, list, 
                    `<h2>${title}</h2>${form}`, 
                    `<a href='/create'>create</a>
                     <a href='/update'>update</a>`);
                response.writeHead(200);
                response.end(html);
            });
        });
    }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;
            var title = post.title;
            var description = post.description;
            fs.rename(`data/${id}`, `data/${title}`, function(err){
                fs.writeFile(`data/${title}`, description, function(err){
                    response.writeHead(302, {Location: `/?id=${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/${queryData.id}`, function(err){
                response.writeHead(302, {Location : `/`});
                response.end();
            });
        });
    }else{  // 쌩뚱맞은 페이지로 갈 때
        response.writeHead(404);
        response.end('Not Found');
    }
});
app.listen(3000);

8. 정보보안

var http = require('http');
var url = require('url');
var fs = require('fs');
var qs = require('querystring');
var template = require('./lib/template.js');
// 사용자가 입력한 정보(외부에서 들어온 정보)는 다 path로 바꿔줘야함 -> 오염된 정보 쳐내야 함
var path = require('path'); 
// 웹브라우저가 script 태그를 만나면 자바스크립트를 실행시킴 (공격자가 우리 사이트에 자바스크립트 코드를 심어넣을 수 있음)
// 정보를 필터링 해야함 (1. 스크립트 태그를 지워버리기 2. 스크립트 태그의 꺽쇠를 지워버리기 (&lt;, &gt; 이용해서) 3. 다른 사람이 만든 것 활용하기)
// npm sanitize package 사용하기 (npm 에 등록되어있는 수 많은 모듈들 중 sanitize 사용)
// 1. npm init (우리가 사용한 app을 npm으로 관리하기 위한 절차의 시작) -> npm init -y
// 2. package.json 생성 (우리 프로젝트에 대한 정보 담겨있음 - 다른사람이랑 공유할 거 아니면 그렇게 상관 없음)
// 3. npm install -S sanitize-html(-g는 전역적 -S는 지역적 다운로드) : sanitize-html npm에서 다운로드 -> package.json의 dependencies에 sanitize-html 추가된 것 확인
// 4. var dirty = require('sanitize-html'); 로 모듈 불러오기
// 5. var clean = sanitizeHTML(dirty);  로 세탁해주기
// ** 오류 -> npm을 다운받고 서버 내렸다가 다시 켜주기. pm2가 npm에서 내려받은 sanitize-HTML을 인식 하지 못하는 상황 발생 **
var sanitizeHtml = require('sanitize-html');

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 === '/'){   
        if(queryData.id === undefined){  
            fs.readdir(`data/`, function(err, filelist){
                var title = 'Web';
                var description = 'Welcome Node JS';
                var list = template.list(filelist);
                var html = template.HTML(title, list, 
                    `<h2>${title}</h2>${description}`, 
                    `<a href='/create'>create</a>`);
                response.writeHead(200);
                response.end(html);
            });
        }else{  // 리스트에 링크를 눌렀을 때
            fs.readdir(`data/`, function(err, filelist){
                 // 주소창에서 ../password.js로 검색해도 base만 돌려주기때문에 주소를 세탁할 수 있음 (사용자가 의도적으로 디렉토리 접근하는것을 막기)
                 // 외부에서 들어오는 정보의 오염 방지 (readFile(주소) << 이 부분 필터링 해주기)
                var filteredId = path.parse(queryData.id).base;
                fs.readFile(`data/${filteredId}`, 'utf-8', function(err, description){
                    var title = queryData.id;
                    // title은 살균 전 (더러운) 데이터 sanitizeHtml(); 이라는 살균 과정을 통해 sanitizedTitle로 살균된 데이터로 만들기
                    var sanitizedTitle = sanitizeHtml(title);
                    // description은 살균 전 (더러운) 데이터 sanitizeHtml(); 이라는 살균 과정을 통해 sanitizedDescription로 살균된 데이터로 만들기
                    // sanitize-HTMl은 script태그와 같이 예민한 태그가 있으면 아예 통채로 날려버림 , 제목태그와 같은것은 태그는 날리지만 내용은 남겨둠
                    // 허용하고 싶은 태그가 있다면 allowedTags:[]
                    var sanitizedDescription = sanitizeHtml(description, {
                        allowedTags:['h1']
                    });
                    var list = template.list(filelist);
                    var html = template.HTML(sanitizedTitle, list, 
                        `<h2>${sanitizedTitle}</h2>${sanitizedDescription}`, 
                        `<a href='/create'>create</a>
                         <a href=/update?id=${sanitizedTitle}>update</a>
                         <form action='delete_process' method='post'>
                            <input type='hidden' name='id' value='${sanitizedTitle}'>
                            <input type='submit' value='delete'>
                         </form>`);
                    response.writeHead(200);
                    response.end(html);
                });
            });
        }
    }else if(pathname === '/create'){
        fs.readdir(`data/`, function(err, filelist){
            var title = 'Web';
            var form = `
            <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>
            `;
            var list = template.list(filelist);
            var html = template.HTML(title, list, 
                `<h2>${title}</h2>${form}`, 
                `<a href='/create'>create</a>`);
            response.writeHead(200);
            response.end(html);
        });
    }else if(pathname === '/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, 'utf-8', function(err){
                response.writeHead(302, {Location: `/?id=${title}`});
                response.end();
            });           
        });
    }else if(pathname === '/update'){
        fs.readdir(`data/`, function(err, filelist){
            var filteredId = path.parse(queryData.id).base;
            fs.readFile(`data/${filteredId}`, function(err, description){
                var title = queryData.id;
                var form = `
                <form action='/update_process' method='post'>
                    <p><input type='hidden' name='id' value='${title}'></p>
                    <p><input type='text' name='title' value='${title}'></p>
                    <p><textarea name='description'>${description}</textarea></p>
                    <p><input type='submit'></p>
                </form>
                `;
                var list = template.list(filelist);
                var html = template.HTML(title, list, 
                    `<h2>${title}</h2>${form}`, 
                    `<a href='/create'>create</a>
                     <a href='/update'>update</a>`);
                response.writeHead(200);
                response.end(html);
            });
        });
    }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;
            var title = post.title;
            var description = post.description;
            fs.rename(`data/${id}`, `data/${title}`, function(err){
                fs.writeFile(`data/${title}`, description, function(err){
                    response.writeHead(302, {Location: `/?id=${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;
            // 삭제할 때의 id값도 오염될 수 있기 때문에 주소 세탁해주기
            var filteredId = path.parse(id).base;
            fs.unlink(`data/${filteredId}`, function(err){
                response.writeHead(302, {Location : `/`});
                response.end();
            });
        });
    }else{ 
        response.writeHead(404);
        response.end('Not Found');
    }
});
app.listen(3000);
profile
안녕하세요.

0개의 댓글