본문 바로가기
Projects/개인 프로필 사이트

[개인 프로필 사이트] 8. 피드백 페이지 작성 및 기능 구현

by DevJaewoo 2021. 6. 16.
반응형

Intro

하위 3개 페이지 중 마지막인 피드백 페이지를 작성할 차례다.

마감일이 이틀밖에 남지 않아 기본적인 게시판 기능밖에 구현하지 못했다.

 

게시판 기능은 Web SQL을 사용하여 구현했다.

Web SQL 특성상 로컬에 저장되는 DB이기 때문에, 한 PC에서 작업한 결과를 다른 PC에서 볼 수 없다.

하지만 DB 사용과 게시판 기능 구현 그 자체가 목적이기 때문에, 감안하고 넘어가기로 했다.

 

코드는 아래의 링크를 참조했다.

https://github.com/liokingim/html5canvas/blob/master/sqliteboard.html

 

liokingim/html5canvas

Contribute to liokingim/html5canvas development by creating an account on GitHub.

github.com

 

모든 코드는 github에 업로드해두었습니다.

아래의 링크를 클릭하여 소스코드를 보실 수 있습니다.

https://github.com/DevJaewoo/DevJaewoo.github.io

 

DevJaewoo/DevJaewoo.github.io

Contribute to DevJaewoo/DevJaewoo.github.io development by creating an account on GitHub.

github.com


피드백 페이지 작성

UI는 매우 간단하게 구성했다.

피드백 페이지 UI

 

상단에 메인 이미지, 좌측 하단에 메뉴, 그리고 우측 하단에 사용자 작업 영역을 배치했다.

사용자 작업 영역은 글 목록, 글 작성, 글 조회 3가지로 구성되며, 사용자의 선택에 따라 다른 영역을 보여준다.

 

3가지 영역이 한 곳에 겹쳐져 있고 현재 활성화된 하나의 영역만 active 시켜서 보여주는 방식으로 구현했다.

예시
예시


Web SQL 초기화

Web SQL을 사용하기 위해서 DB를 Open 해주는 코드이다.

var systemDB = null;

function initDatabase() {
    if (!window.openDatabase) {
        alert("현재 브라우저는 Web SQL Database를 지원하지 않습니다");
    }
    else {
        var shortName = "Feedback";
        var version = "1.0";
        var displayName = "Feedback";
        var maxSize = 1024 * 128; // in bytes
        systemDB = openDatabase(shortName, version, displayName, maxSize);
    }
}

이렇게 DB를 초기화하고 변수에 결과값을 저장하면 변수를 통해 DB를 제어할 수 있다.

 

DB를 제어하다 에러가 발생했을 때 해당 에러를 띄워줄 callback 함수도 필요하다.

function errorHandler(transaction, error) {
    console.error(error + 'Error: ' + error.message + ' (Code ' + error.code + ')');
}

테이블 생성/삭제 코드 구현

코드 구현에 앞서 테이블 구조를 정했다.

테이블은 글, 댓글 총 2개이며, 구조는 다음과 같다.

 

1. 글 테이블 (tb_post)

idx username title content regdate
INTEGER TEXT TEXT TEXT TIMESTAMP

 

2. 댓글 테이블 (tb_comment)

idx username content postid regdate
INTEGER TEXT TEXT INTEGER TIMESTAMP

 

글 작성 시 자동으로 id가 설정되며, 해당 글에 댓글을 남길 경우 글의 idx를 postid값으로 하는 댓글이 추가된다.

따라서 댓글을 조회하고 싶을 때 postid를 통해 한 글에 달린 댓글들을 조회할 수 있다.

 

완성된 코드는 다음과 같다.

//테이블 생성
function createTable() {

    //게시글 테이블
    var strCreatePost = "CREATE TABLE IF NOT EXISTS tb_post"
        + " (idx INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
        + " username TEXT NOT NULL,"
        + " title TEXT NOT NULL,"
        + " content TEXT NOT NULL,"
        + " regdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)";

    //댓글 테이블
    var strCreateComment = "CREATE TABLE IF NOT EXISTS tb_comment"
        + " (idx INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
        + " username TEXT NOT NULL,"
        + " content TEXT NOT NULL,"
        + " postid INTEGER NOT NULL,"
        + " regdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)";

    systemDB.transaction(function (tx) {
        tx.executeSql(strCreatePost, [], null, errorHandler);
        tx.executeSql(strCreateComment, [], null, errorHandler);
    });
}

// 테이블 삭제
function dropTable() {

    systemDB.transaction(function (tx) {
        tx.executeSql("DROP TABLE tb_post", [], refreshPost, errorHandler);
        tx.executeSql("DROP TABLE tb_comment", [], refreshPost, errorHandler);
    });
}

테이블 생성 코드는 웹 페이지에 접속할 때마다 실행하므로 테이블이 존재하지 않을 때만 테이블을 생성하도록 구현했다.

 

코드를 적용하면 크롬에서 테이블이 생성된 것을 확인할 수 있다.

[Chrome 실행 - F12 - Application - Storage - Web SQL]

localstorage 메뉴


insert, update, delete, select문 구현

insert: 테이블에 데이터를 삽입한다. 글 쓰기 창의 또는 댓글 창의 확인 버튼을 누를 시 실행된다.

//게시글 작성
function insertPost(username, title, content) {
    var query = "INSERT INTO tb_post (username, title, content) VALUES (?, ?, ?)";

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [username, title, content], refreshPost, errorHandler);
    });
}

//댓글 작성
function insertComment(username, content, postid) {
    var query = "INSERT INTO tb_comment (username, content, postid) VALUES (?, ?, ?)";

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [username, content, postid], refreshComment, errorHandler);
    });
}

 

update: 테이블의 데이터를 수정한다. 글 수정 또는 댓글 수정을 선택한 후 작성 확인 버튼을 누를 시 실행된다.

//게시글 수정
function updatePost(username, title, content, idx) {
    var query = "UPDATE tb_post SET username = ?, title = ?, content = ? WHERE idx = ?";

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [username, title, content, idx], refreshPost, errorHandler);
    });
}

//댓글 수정
function updateComment(username, content, idx) {
    var query = "UPDATE tb_comment SET username = ?, content = ? WHERE idx = ?";

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [username, content, idx], refreshComment, errorHandler);
    });
}

 

delete: 테이블의 데이터를 삭제한다. 글 또는 댓글의 삭제 버튼을 선택할 시 실행된다.

//게시글 삭제
function deletePost(idx) {

    var query = "DELETE FROM tb_comment WHERE postid=?";
    systemDB.transaction(function (tx) {
        tx.executeSql(query, [Number(idx)], refreshPost, errorHandler);
    });

    var query = "DELETE FROM tb_post WHERE idx=?";
    systemDB.transaction(function (tx) {
        tx.executeSql(query, [Number(idx)], refreshPost, errorHandler);
    });
}

//댓글 삭제
function deleteComment(idx) {
    var query = "DELETE FROM tb_comment WHERE idx=?";
    systemDB.transaction(function (tx) {
        tx.executeSql(query, [Number(idx)], refreshComment, errorHandler);
    });
}

 

select: 테이블의 데이터를 조회하는 코드로, 글 목록 또는 댓글 목록이 사용자에게 보여져야 할 때 실행된다.

목록이 표시되는 곳의 div 태그에 내부 HTML을 추가하여 표시하도록 구현했다.

//게시글 조회
function selectPostList() {
    var query = "SELECT * FROM tb_post order by idx desc";
    $(".post-read").html('');

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [], function (tx, result) {
            dataset = result.rows;
            var str = '';

            if (dataset.length > 0) {
                for (var i = 0, item = null; i < dataset.length; i++) {
                    item = dataset.item(i);
                    str += '<div class="flex row post" data-no="' + i + '" data-idx="' + item['idx'] + '">\n'
                        + '<span class="num">' + (i + 1) + '</span>\n'
                        + '<span class="title">' + item['title'] + '</span>\n'
                        + '<span class="user">' + item['username'] + '</span>\n'
                        + '<span class="date">' + item['regdate'] + '</span>\n'
                        + '<div class="control">\n'
                        + '<span class="button edit">수정</span>\n'
                        + '<span class="button delete">삭제</span>\n'
                        + '</div>\n'
                        + '</div>';
                }
            }
            else {
                str += "작성된 글이 없습니다.";
            }

            $(".post-read").html(str);
        });
    });
}

function selectCommentList() {
    var query = "SELECT * FROM tb_comment WHERE postid=? order by idx asc";
    var idx = $(".post-view").data("idx");

    $(".comment-read").html('');

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [Number(idx)], function (tx, result) {
            dataset = result.rows;
            var str = '';

            if (dataset.length > 0) {
                for (var i = 0, item = null; i < dataset.length; i++) {
                    item = dataset.item(i);
                    str += '<div class="flex row comment" data-no="' + i + '" data-idx="' + item['idx'] + '">\n'
                    + '<span class="user">' + item['username'] + '</span>\n'
                    + '<span class="content">' + item['content'] + '</span>\n'
                    + '<span class="date">' + item['regdate'] + '</span>\n'
                    + '<div class="control">\n'
                    + '<span class="button edit">수정</span>\n'
                    + '<span class="button delete">삭제</span>\n'
                    + '</div>\n'
                    + '</div>\n';
                }
            }
            else {
                str += "작성된 댓글이 없습니다.";
            }

            $(".comment-read").html(str);
        });
    });
}

기능 함수 구현

같은 코드를 여러번 호출하지 않기 위해 특정한 동작을 하기 위한 코드들을 하나의 함수로 묶었다.

 

selectForeground: 현재 활성화된 영역을 바꾼다.

function selectForeground(section) {
    $(".mainpage .active").removeClass("active");
    $(section).addClass("active");
}

resetPost / resetComment: 글 / 댓글 작성 칸을 비운다.

function resetPost() {
    $("#post-new-title").val("");
    $("#post-new-name").val("");
    $("#post-new-content").val("");
}

function resetComment() {
    $("#post-comment-name").val("");
    $("#post-comment-content").val("");
    $(".comment-write").data("idx", "-1");
}

 

refreshPost / refreshComment: 글 / 댓글 목록을 새로고침한다. insert/update/delete 명령의 callback으로 들어가있다.

function refreshPost() {
    resetPost();
    selectPostList();
}

function refreshComment() {
    resetComment();
    selectCommentList();
}

 

loadPostNew: 글 작성 환경을 구성한다.

function loadPostNew() {
    resetPost();
    $(".post-new").data("idx", "-1");
    selectForeground(".post-new");
}

 

loadPostEdit: 글 수정 환경을 구성한다.

function loadPostEdit() {
    var query = "SELECT * FROM tb_post WHERE idx=?";
    var idx = $(".post-new").data("idx");

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [Number(idx)], function (tx, result) {
            var item = result.rows.item(0);
            $("#post-new-title").val((item['title']).toString());
            $("#post-new-name").val((item['username']).toString());
            $("#post-new-content").val((item['content']).toString());

            selectForeground(".post-new");
        });
    })
}

 

loadCommentEdit: 댓글 수정 환경을 구성한다.

function loadCommentEdit() {
    var query = "SELECT * FROM tb_comment WHERE idx=?";
    var idx = $(".comment-write").data("idx");

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [Number(idx)], function (tx, result) {
            var item = result.rows.item(0);
            $("#post-comment-name").val((item['username']).toString());
            $("#post-comment-content").val((item['content']).toString());
        });
    });
}

 

loadPostView: 글 선택 시 글의 정보와 댓글들을 불러온다.

function loadPostView() {
    var query = "SELECT * FROM tb_post WHERE idx=?";
    var idx = $(".post-view").data("idx");

    console.log("idx: " + idx);

    systemDB.transaction(function (tx) {
        tx.executeSql(query, [Number(idx)], function (tx, result) {
            var item = result.rows.item(0);

            $(".post-view-main .title").html(item['title']);
            $(".post-view-main .user").html("작성자:" + item['username']);
            $(".post-view-main .date").html(item['regdate']);
            $(".post-view-main .content").html(item['content']);
        });
    });

    resetComment();
    selectCommentList();
    selectForeground(".post-view");
}

jQuery 이벤트 연동

마지막으로 jQuery에 각 버튼 클릭 이벤트 발생 시 실행 할 동작을 지정했다.

$(document).ready(function () {
    initDatabase();

    // 홈으로 이동
    $(".menu .home").on("click", function() {
        resetPost();
        selectForeground(".post-list");
    });

    // 새 글 작성
    $(".menu .new").on("click", function() {
        loadPostNew()
    });

    // 테이블 삭제
    $(".menu .drop").on("click", function () {

        if (!confirm("테이블을 삭제하겠습니까?")) {
            return;
        }

        dropTable();
    });

    // 글 이동
    $("body").on("click", ".post .title", function() {
        var no = $(this).parent().data("no");
        var idx = $(this).parent().data("idx");

        $(".post-view .num").html("번호: " + (Number(no) + 1));
        $(".post-view").data("idx", idx);
        loadPostView();
    });

    // 글 작성 확인
    $(".post-new-submit").on("click", function() {
        
        var postIdx = $(".post-new").data("idx");
        var username = $('#post-new-name').val(); 
        var title = $('#post-new-title').val();
        var content = $('#post-new-content').val();

        if (username == "" || title == "" || content == "") {
            alert("글을 적어주세요.");
            return;
        }

        if(Number(postIdx) == -1) {
            insertPost(username, title, content);
        }
        else {
            updatePost(username, title, content, postIdx);
        }

        resetPost();
        selectForeground(".post-list");
    });

    // 댓글 작성 확인
    $(".comment-write .submit").on("click", function() {
        var postIdx = $(".post-view").data("idx");
        var commentIdx = $(".comment-write").data("idx");
        var username = $('#post-comment-name').val(); 
        var content = $('#post-comment-content').val();

        if (username == "" || content == "") {
            alert("댓글을 적어주세요.");
            return;
        }

        if(Number(commentIdx) == -1) {
            insertComment(username, content, postIdx);
        }
        else {
            updateComment(username, content, commentIdx);
        }

        resetComment();
    });

    // 글 작성 취소
    $(".post-new-cancel").on("click", function() {
        resetPost();
        selectForeground(".post-list");
    });

    // 댓글 작성 취소
    $(".comment-write .cancel").on("click", function() {
        resetComment();
    });
    
    // 글 수정
    $("body").on("click", ".post .edit", function() {
        var idx = $(this).parent().parent().data("idx");

        $(".post-new").data("idx", idx);
        loadPostEdit();
    });

    // 댓글 수정
    $("body").on("click", ".comment .edit", function() {
        var idx = $(this).parent().parent().data("idx");

        $(".comment-write").data("idx", idx);
        loadCommentEdit();
    });

    // 글 삭제
    $("body").on("click", ".post .delete", function() {
        
        var idx = $(this).parent().parent().data("idx");

        if (Number(idx) <= 0) {
            alert("삭제할 글을 선택해주세요.");
            return;
        }    

        if (!confirm("삭제하겠습니까?")) {
            return;    
        }

        deletePost(idx);
    });

    // 댓글 삭제
    $("body").on("click", ".comment .delete", function() {
        
        var idx = $(this).parent().parent().data("idx");

        if (Number(idx) <= 0) {
            alert("삭제할 글을 선택해주세요.");
            return;
        }    

        if (!confirm("삭제하겠습니까?")) {
            return;    
        }

        deleteComment(idx);
    });
});

 

이렇게 마지막 피드백 페이지까지 완성했다.

파일 용량 제한으로 인해 결과 동영상을 넣을 수 없어 아래의 링크로 대체한다.

https://DevJaewoo.github.io/subpages/feedback 

 

DevJaewoo

 

devjaewoo.github.io

 

 

2주라는 짧은 기간동안 나만의 웹 사이트를 만드는 일이 쉽지는 않았다.

하지만 여러 기능을 구현해보고 에러를 고치느라 삽질하는 과정에서 많은 걸 배운것 같다.

 

웹 사이트는 github.io에 등록 하여 누구나 접속하여 만든 페이지를 볼 수 있다.

만약 나중에 링크가 삭제되더라도, github에 올려두었으니 다운로드 하고 압축을 해제하면 열어볼 수 있다.

 

https://DevJaewoo.github.io 

 

DevJaewoo

 

DevJaewoo.github.io

https://github.com/DevJaewoo/DevJaewoo.github.io 

 

DevJaewoo/DevJaewoo.github.io

Contribute to DevJaewoo/DevJaewoo.github.io development by creating an account on GitHub.

github.com

 

이 프로젝트는 이쯤에서 마무리하려고 한다.

다음에 웹 사이트를 만들게 되면 이번 프로젝트를 진행하며 얻은 경험을 통해 더 잘 만들 수 있을 것 같다.

반응형