TIL 39 | JS 개선하기 (Array to HTML)

meow·2020년 8월 29일
0

JavaScript

목록 보기
26/46

인스타그램 클론 코딩에 관한 포스팅 이후 계속해서 더 나은 코드로 리팩토링하는 과정을 거치고 있다. 지난번에 댓글에 좋아요, 삭제 기능을 구현하면서 새로운 댓글을 추가하는 함수가 엄청나게 복잡하게 길어졌다. 기능은 잘 동작했지만, 다소 비효율적인 코드라는 것을 누구나 알 수 있었고, 이 부분을 좀 더 나은 방법을 찾아 보완하고자 했다. 해당 코드는 여기에서 볼 수 있다.

새로 생성된 댓글에 붙는 버튼에도 기존에 존재하던 댓글의 버튼이 가지고 있는 기능을 동일하게 작동하게 만들어야 했기 때문에 새로 댓글을 생성하는 함수 안에 버튼의 클래스에 함수를 선언하는 형태가 되었고, 때문에 댓글을 입력하고 추가되는 과정에서도 댓글의 내용(value), 버튼 두개 등 따로 변수 선언까지 하게 되어 코드가 엄청나게 길고 복잡해졌다.

어마무시한 addComment() ...

위 코드를 단순화하기 위한 방법이 없을지 멘토님께 조언을 구했다. 당연히 정답을 주시지는 않고 다르게 생각하기 위한 힌트를 주셨다.

  1. 현재 함수 하나에 전부 묶여있는데, 기능별로 단순하게 분리를 먼저 해보라.
  2. 댓글 배열을 만들어주고 map을 돌려서 렌더링하는 방법을 적용해보는건 어떨까? 댓글 하나에는 내용, 좋아요 버튼, 삭제 버튼이 포함되어 있는데 이것들이 배열의 객체요소가 된다면?

댓글 Array 만들기

기존 코드에서는 디폴트로 설정되어있는 댓글이 HTML에 한 개가 존재하고, 댓글을 새로 입력할때마다 그때그때 innerHTML로 동적 데이터가 추가되는 형태를 띄고 있다.

이렇게 인풋값을 바로 HTML에 추가하는 것이 아니라 먼저 JS에서 댓글 array에 객체로 추가한 뒤, HTML에서는 array를 매번 동기화하는 방식으로 바꿔보기로 했다.

댓글 데이터가 담긴 Array를 HTML로

위와 같이 디폴트 댓글의 객체 정보가 담긴 array를 선언해주었다. 즉, HTML에 하드코딩으로 넣었던 댓글을 동적 데이터로 바꾼 셈이다.

JavaScript가 실행되는 순간 위 array를 forEach로 돌며 각각의 객체를 showComments() 함수로 넣어준다. showComments() 함수는 JS의 array를 HTML document의 list로 넣어주는 역할을 한다. 이전의 코드에서 일일히 버튼마다 변수를 주고 innerHTML에 넣었던 방법보다 훨씬 간단해졌다.

addComment() 안의 함수 정리

addComment() 함수에서는 새로 인풋된 댓글을 commentArray에 들어갈 수 있는 객체 형태로 변환 후, Array에 Push하는 것으로 시작한다 (line 80). 그리고나서 추가된 Array를 위 showComments() 함수로 HTML에 List로 추가한다.

line 76-79 이미 만들어진 HTML List 뒤에 다시 새로 commentArray가 추가되면서 중복된 리스트가 생기는 이슈가 있었다. 때문에 addComment() 의 본 기능이 시작되기 전에, 항상 HTML의 List <ul> 의 Child <li> 를 모두 지우는 것으로 함수를 시작하도록 수정했다.

HTML에서 모든 자식 노드를 삭제해야하는 경우

var list = document.getElementById("list");
while (list.hasChildNodes()) {
	list.removeChild(list.firstChild);
    }

likeComment() & deleteComment()

불필요하게 addComment() 함수 안에서 선언되어있던 함수 두개를 밖으로 빼냈다. 호옥시 궁금해하시는 분들이 있을까 싶어서 코드만 올려본다!

새 댓글이 등록될때마다 위 두가지 함수가 적용되어 deleteBtncommentLike 변수가 계속 업데이트 될 수 있다. 업데이트 될 변수일지라도 함수 안에서는 새로 선언할 일이 없기 때문에 const로 변수를 선언해주었다!

JS 코드

// 댓글 데이터
const commentArray = [
    {id: "postmalone",
    content: "내가 입으면 더 잘 어울릴 것 같아",
    deletebutton: `<img alt="more" class="comment-more" src="https://s3.ap-northeast-2.amazonaws.com/cdn.wecode.co.kr/bearu/more.png">`,
    likebutton: `<div class="comment-like">
                    <img alt="하트" class="comment-heart" src="https://s3.ap-northeast-2.amazonaws.com/cdn.wecode.co.kr/bearu/heart.png">
                    <img alt="좋아요된하트" class="comment-heart-liked" src="img/liked.png">
                </div>`}
    ]
    
// 댓글 데이터 노출
commentArray.forEach(function(e) {
    showComments(e)
});

function showComments(comment) {
    const newComment = document.createElement('li');
    newComment.innerHTML = `
        <span>
            <span class="point-span userID">${comment.id}</span>${comment.content}
        </span>
            <div>
                ${comment.deletebutton}
                ${comment.likebutton}
            </div>`;
    commentList.appendChild(newComment);
}

// 댓글 추가
function addComment() {
    const commentList = document.getElementsByClassName('comments')[0];
    while (commentList.hasChildNodes()) {
        commentList.removeChild(commentList.firstChild);
    }
    const newComment = {
        id: "thisisyourhyung",
        content: `${commentInput.value}`
        }
    commentArray.push(newComment);
    commentInput.value = "";
    commentBtn.disabled = true;
    commentArray.forEach(function(e) {
        showComments(e)
    });
    likeComment();
    deleteComment();
}

commentBtn.addEventListener('click', function(){
    if (commentInput.value) {
        addComment();
    }
})

commentInput.addEventListener('keyup', function(e){
    if (commentInput.value) {
        commentBtn.disabled = false;
        if (e.which === 13) {
            addComment();
        }
    }
    else {
    commentBtn.disabled = true;
    }
})

// 삭제, 좋아요 기능
function deleteComment() {
    const deleteBtn = document.querySelectorAll('.comment-more');
    deleteBtn.forEach(function(event) {
        event.addEventListener('click', function() {
            commentArray.pop(event.parentNode.parentNode);
            this.parentNode.parentNode.remove();
        });
    })
}

function likeComment() {
    const commentLike = document.querySelectorAll('.comment-like');
    commentLike.forEach(function(event) {
        event.addEventListener('click', function() {
            const likeBtn = this.querySelector('.comment-heart');
            const likedBtn = this.querySelector('.comment-heart-liked');

            if (likeBtn.style.display === 'none') {
                likeBtn.style.display = 'inline-block';
                likedBtn.style.display = 'none';
            } else {
                likeBtn.style.display = 'none';
                likedBtn.style.display = 'inline-block';
            }
        })
    })
}

likeComment();
deleteComment();
profile
🌙`、、`ヽ`ヽ`、、ヽヽ、`、ヽ`ヽ`ヽヽ` ヽ`、`ヽ`、ヽ``、ヽ`ヽ`、ヽヽ`ヽ、ヽ `ヽ、ヽヽ`ヽ`、``ヽ`ヽ、ヽ、ヽ`ヽ`ヽ 、ヽ`ヽ`ヽ、ヽ、ヽ`ヽ`ヽ 、ヽ、ヽ、ヽ``、ヽ`、ヽヽ 🚶‍♀ ヽ``ヽ``、ヽ`、

1개의 댓글

comment-user-thumbnail
2022년 1월 4일

정말 잘 보고 갑니다. 감사합니다

답글 달기