[JavaScript] 구글 스프레드시트 API를 DB로 사용해서 해시태그를 만들어주는 사이트 만들기

사이트 보러 가기 : https://taghaebwa.netlify.app/

깃허브 레퍼지토리 : https://github.com/Hwangsee/tag-project-test

서론

공부를 하고 싶을 때 ‘해야 하는데...’ 라는 생각이 들면 항상 실패하는 것 같다. 아무리 좋아하는 일이라도 확실한 목표가 없으면 막연하다는 생각만 들어 쉽사리 몸이 움직여지지 않는다. 그래서 나는 개발 공부를 해야겠다고 마음 먹을 때, 개인 프로젝트를 진행하는 편이다. 만들고 싶은 것을 정하고 그것을 만들기 위해선 어떻게 하면 좋을까 공부하고 코드로 삽질도 좀 해보는 과정이 다분히 즐겁다. 완성했을 때 성취감도 느낄 수 있어서 나에게 더욱 잘 맞는 공부 방법이다.

이번에 준비한 개인 프로젝트는 플랫폼에서 근무 중인 친구의 아이디어로부터 시작 되었다. 친구는 해시태그를 할 때 매번 키워드를 찾아서 하나하나 입력하는 업무가 반복적이고 힘들다고 했고 마침 자바스크립트를 공부하고 있었기 때문에 반복 업무를 줄여 주겠다는 당찬 포부를 갖고 사이트를 만들기로 했다.


목표

  • 사용자의 니즈에 맞춰서 웹 사이트를 제작한다.
  • 사용자의 접근성을 쉽게 하기 위해 구글 스프레드 시트를 DB로 하여, 사용자가 데이터를 추가/수정 할 수 있도록 한다.

유저 시나리오

  1. 사용자가 웹 사이트에 접속하면 분류 별로 정리된 태그를 볼 수 있다.
  2. 태그를 클릭하면 ‘#태그’ 텍스트가 생성된다.
  3. 입력 창에서 태그를 직접 입력할 수 있다.
  4. 복사 버튼을 누르면 입력한 태그를 복사할 수 있다.

사용한 기술

  • HTML
  • CSS
  • JavaScript
  • JQuery

주요 로직

구글 스프레드 시트 API를 활용하여 데이터 가져오기

var query = function (sql, callback) {
    var url = 'https://spreadsheets.google.com/a/google.com/tq?',
        params = {
            key: '스프레드 시트 키 값',
            tq: encodeURIComponent(sql),
            tqx: 'responseHandler:' + callback
        },
        qs = [];
    for (var key in params) {
        qs.push(key + '=' + params[key]);
    }
    url += qs.join('&');
    return jsonp(url); // JSONP 도우미 호출
}

var my_callback = function (data) {
    data = parse(data); // 데이터 parse
    //불러온 데이터 조작
    for (var i = 0; i < datas.length; i++) {
        if (JSON.stringify(datas[i]) == JSON.stringify(data)) {
            return false;
        }
    }
    datas.push(data);

    // HTML 헤더의 값을 추출
    var col = [];
    for (var i = 0; i < data.length; i++) {
        for (var key in data[i]) {
            if (col.indexOf(key) === -1) {
                col.push(key);
            }
        }
    }
    // 받아 온 데이터 커스텀 
    var table = document.querySelector("#tagTable table");
    if (table === null || table == undefined) {
        // 동적 테이블 생성
        table = document.createElement("table");

        // HTML 테이블 헤더 생성
        var tr = table.insertRow(-1);
        for (var i = 0; i < col.length; i++) {
            var th = document.createElement("th");
            th.innerHTML = col[i];
            tr.appendChild(th);
        }
        // HTML 테이블 ROW 생성
        for (var i = 1; i < data.length; i++) {
            tr = table.insertRow(-1);
            for (var j = 0; j < col.length; j++) {
                var text = data[i][col[j]];
                var tabCell = tr.insertCell(-1);
                tabCell.innerHTML = data[i][col[j]];
                var text = data[i][col[j]];
                //tabCell.click
                tabCell.innerHTML = '<span class="select-data input-tag" data-tag="' + text + '">' + text + '</span>';
                tabCell.setAttribute("onclick","setDataToTag('" + text + "');");
                //tabCell.innerHTML = '<span class="select-data input-tag" data-tag="' + text + '" onclick="setDataToTag(\'' + text +'\')">' + text + '</span>';
            }
        }
        // 마지막으로 JSON 데이터로 새로 만든 테이블을 컨테이너에 추가
        var divContainer = document.getElementById("tagTable");
        divContainer.innerHTML = "";
        divContainer.appendChild(table);
        $("#tagTable tr td").each(function(i, elem) {
            if($(this).find(".select-data").text() != "") {
                $(this).addClass("select-tag");
            }
        });
    } else {
        // 테이블 행 동적 추가
        for (var i = 1; i < data.length; i++) {
            var tr = table.insertRow();
            for (var j = 0; j < col.length; j++) {
                var tabCell = tr.insertCell(-1);
                tabCell.innerHTML = data[i][col[j]];
            }
        }
    }

}
query('select *', 'my_callback');

구글 스프레드 시트 API를 호출하여 받아온 데이터를 JSON으로 파싱하고 동적으로 테이블을 생성하여 화면에 뿌려주는 코드이다.

해당 프로젝트에서 사용하진 않았지만 쿼리를 사용하여 데이터를 필터링 할 수도 있다.

구글 스프레드 시트에 입력한 데이터

데이터를 받아 동적 테이블을 생성한 페이지

CSS로 height: 380px 속성으로 높이를 고정하고 해당 크기를 넘어가면 스크롤이 생기도록 overflow-y: auto 속성을 추가했다.

해시태그 추가 / 삭제 로직

				
		var // 태그 변수
        wrapperTags = $('.tags .wrapper-tags'),
        viewTags = $('.tags .wrapper-tags .view-tags'),
        inputTag = $('.tags .wrapper-tags .input-tag'),
        btnAddTags = $('.tags .add-tags'),
        alertErrorInAdd = $('.show-all-tags .alert-danger'),
        showTagsWhenResult = $('.show-all-tags .show-tags'),
        showCountCharactersTag = $('.tags .show-count-all .count-character-tag'),
        maxCharactersLengthTag = 20,
				tags = []; //추가한 태그 배열

        clickAddTag = (textTag) => {
            tags.push(textTag);
            var newTag = '<span class="tag" data-tag="' + textTag + '">' + textTag + '<i class="fa fa-close"></i></span>';
            inputTag.before(newTag);
        },
        addTag = (textTag) => {
            var newTag = '<span class="tag" data-tag="' + textTag + '">' + textTag + '<i class="fa fa-close"></i></span>';
            inputTag.before(newTag);
        },
        removeTag = function (textTag, animateRemove = 'hard') {
            var indexTag = tags.indexOf(textTag);
            tags.splice(indexTag, 1);
            if (animateRemove === 'hard') {
                viewTags.find('.tag[data-tag="' + textTag + '"]').remove();
                inputTag.focus();
            } else if ('slideLeft') {
                viewTags.find('.tag[data-tag="' + textTag + '"]').animate({
                    'width': 0,
                    'padding': 0,
                    'margin': 0
                }, 300, function () {
                    $(this).remove();
                    inputTag.focus();
                });
            }
        };

해시태그를 클릭했을 때, 혹은 사용자 입력 화면에서 해시태그를 추가했을 때 tags 배열에 추가된 태그의 value를 넣고 동적으로 span 태그를 생성하여 화면에 뿌려준다.

태그를 삭제할 때 x 버튼을 눌러 태그를 지울 경우 매개변수에 slideLeft를 전달하여 jquery의 animated 함수를 호출하고 태그가 0.3초 뒤에 삭제되는 것처럼 보이도록 한다.

viewTags.on('click', '.tag i', function (e) {
        var textTag = $(this).parent().attr('data-tag');
        removeTag(textTag, 'slideLeft');
    });

백스페이스로 태그를 지울 때는 매개변수를 주지 않는다.

			else if (key === 8 || key === 46) {
            if (numRepaierRemoveTag === 0) {
                var lastTag = tags[tags.length - 1];
                removeTag(lastTag);
                inputTag.val(lastTag);
            }
        }

해시태그 생성 버튼 클릭

각 버튼에 data-layer 속성을 추가하여 버튼을 구분할 수 있도록 했다.

btnAddTags.on('click', function () {
        //선택된 태그가 없을 때
        if (!tags.length) {
            // 에러 메세지 보여줌
            alertErrorInAdd.slideDown(200);
            showTagsWhenResult.fadeOut(10);
        } else {
            var AllTagsInArray = '';
            tags.forEach((val, i) => {
                var dataLayer = $(this).data('layer');
                AllTagsInArray += showAllTags(val, i, dataLayer);
            });
            //마지막 쉼표를 제거하는 정규식 사용
            showTagsWhenResult.find('.tags').html("<span class='value'>" + AllTagsInArray.replace(/,\s*$/, "") + "</span>");
            showTagsWhenResult.fadeIn(500);
        }
    });
// 태그 생성
var showAllTags = (val, i, dataLayer) => {
    if(dataLayer == 'layer1') {
        return "#" + val + " ";
    }
    else if (dataLayer == 'layer2') {
        return  val + ", " ;
    }
    else if (dataLayer == 'layer3') 
    {
        return "#" + val + ", ";
    }
};

선택된 태그가 없으면 slideDown 메소드를 이용하여 에러 메세지가 표시되도록 한다.

태그들이 정상적으로 추가 된 상태라면 배열에 담긴 태그에 해시태그나 쉼표를 추가하여 하나의 텍스트로 만들어 준다.

/* 클립보드 복사 이벤트 */
$(document).ready(function() {
    $("#tagTitle").click(function() {
        var copyTxt = document.getElementById('tagsBox').innerText;
        //input 태그에서만 작동하는 select 메소드를 이용하기 위해 teaxarea 엘리먼트 생성
        const textarea = document.createElement('textarea');
        textarea.textContent = copyTxt;
        document.body.append(textarea);
        textarea.select();
        document.execCommand('copy');
        textarea.remove();
        alert('클립보드에 복사 되었습니다.');
    });
});

Javascript에서 클립보드로 복사하기 위해서는 전체 텍스트를 선택해야 하는데 이 때 select() 함수가 사용된다.

select() 함수를 사용하기 위해서는 복사할 텍스트가 textbox나 Input 태그에 있어야 하기 때문에 임시로 textbox 엘리먼트를 생성하였다.

마무리

배포 후 친구에게서 잘 사용하고 있다는 연락을 받아 매우 뿌듯했습니다.
태그가 많아지면 스크롤이 그만큼 길어지는 문제나 중복 태그를 입력할 수 있는 부분은 개발 당시 생각하지 못했던 부분인데 배포 후 피드백을 받아 수정할 수 있었습니다.
수정하는 과정 또한 즐거웠습니다.
이번 프로젝트를 통해 자바스크립트와 제이쿼리에 대해 더 잘 알 수 있는 기회가 된 것 같습니다.
프로젝트를 진행하며 바닐라 자바스크립트로 개인 프로젝트를 진행해보고 싶다는 새로운 목표가 생기기도 했습니다.
당분간은 자바스크립트 공부에 빠져 살 것 같네요.
또 재미있는 도전을 할 수 있기를! 😆

참고한 사이트


좋은 웹페이지 즐겨찾기