글 작성자: 개발자 올라프

지난주에 HTML+CSS, JavaScript를 각 2.5일 동안 공부하는 시간을 가졌다. JavaScript로 어려웠던 문제를 풀면서 성공적인 일주일을 마쳤다고 생각함과 동시에 인스타그램 클론 코딩을 하게 되었다(너무 짧은 거 아닌가🤷‍♂️). 어느 정도 틀은 잡힌 상태로 시작해서 여러 이벤트를 구현할 줄 알았지만, 백지상태에서 아무런 도움 없이 스스로 HTML+CSS를 작성하고 여러 이벤트를 구현해야 했다.

 

인스타그램 기능을 모두 구현, 클론하는 프로젝트는 아니고 'HTML+CSS+JavaScript를 잘 적용할 수 있는가?'를 위한 시간이 아니었을까 싶다. 다소 짧은 시간인 1주일 동안 내가 학습한 내용으로 웹 페이지 모양을 갖출 수 있다는 것이 신기했다. 특히 인스타그램 형태를 따라 하기 위해서 HTML을 어떤 식으로 작성하면 좋을지 머릿속에 그려지는 부분이 놀라웠다. 이제 본론으로 들어가 어떤 내용을 구현하고, 어떤 시간을 보냈는지에 대해서 이야기해보자.

 


 

인스타그램 클론 코딩 (`22.05.02 ~ `22.05.06) 

 

Skills 🔨

  • HTML
  • CSS
  • JavaScript

 

Mission

  • 필수 구현 : 로그인 페이지 레이아웃, ID/PW 입력 시 로그인 활성화, 메인 페이지 레이아웃, 댓글 입력 후 게시 클릭 시 댓글 추가
  • 추가 구현 : ID/PW 유효성 검사, 댓글 좋아요/삭제 기능 추가, 아이디 검색 기능(미구현), 프로필 사진 클릭시 메뉴 박스 생성(미구현)
  • 여유 있을 때 구현에 실패한 내용 진행하기

 

구현 사항

  • 로그인 페이지 레이아웃
  • ID, PW 유효성 검사
  • 메인 페이지 레이아웃
  • 댓글 추가 기능
  • 댓글 좋아요 / 삭제 기능

 


로그인 페이지

const id = document.getElementById('id');
const password = document.getElementById('password');
const loginBtn = document.getElementsByClassName('loginBtn')[0];

const verifyId = () => {
    const idValue = id.value;
    return idValue.includes('@') ? true : false;
}

const verifyPw = () => {
    const pwValue = password.value;
    return pwValue.length>=5 ? true : false;
}

// ID, PW 길이에 따른 로그인버튼
const activateLogin = () => {
    if(verifyId() && verifyPw()) {
        loginBtn.disabled = false;
        loginBtn.style.backgroundColor = '#0095F6';
        loginBtn.style.cursor = 'pointer';
    } else {
        loginBtn.disabled = true;
        loginBtn.style.backgroundColor = '#C0DFFD';
        loginBtn.style.cursor = 'default';
    }
}

const successLogin = () => {
    alert('로그인 성공!');
    location.href = './main.html'
}

id.addEventListener('keyup', activateLogin);
password.addEventListener('keyup', activateLogin);
loginBtn.addEventListener('click', successLogin);

 

삼항 조건 연산자

조건문으로 들어갈 표현식 ? 참일 때 실행할 식 : 거짓일 때 실행할 식으로 if 명령문의 단축 형태이다. 삼항 조건 연산자를 사용하면 코드의 길이가 줄어들기도 하면서 가독성이 좋아지는 장점이 있다.

 

keyup event ⌨️

keyup, keydown, keypress에 대한 차이점에 대해서 이해할 필요가 있다.

  • The keydown event is triggered first when user presses a key
  • The keyup event is triggered last when user releases a key
  • In between, the keypress event is triggered

 

keydown, keyup은 각자 키를 누르고 있는 동안, 키를 놓았을 때 반응하는 것이고 keypress는 키를 눌렀을 때 반응한다.

 


 

 

메인 페이지

let inputComment = document.getElementsByClassName("inputComment")[0];
let submitCommentBtn = document.getElementsByClassName("submitCommentBtn")[0];
let heart = document.querySelector('.commentHeart');

// 댓글게시 작동 조건
const checkInput = () => {
    if(!inputComment.value.length) {
        return;
    }
    submitComment();
}

// 댓글구현
function submitComment() {
    const newUserComment = document.getElementsByClassName("commentBox")[0];

    const commentDiv = document.createElement("div");
    const userName = document.createElement("p");
    const comment = document.createElement("div");
    const heart = document.createElement("p");
    const deleteComment = document.createElement("p");

    commentDiv.classList.add("userCommentBox");
    userName.classList.add("userName");
    comment.classList.add("userComment");
    heart.classList.add("userHeart");
    deleteComment.classList.add("delete");

    userName.innerHTML = "happyhappy"; // *** 임시 닉네임 ***
    comment.innerText = inputComment.value;
    heart.innerHTML = '<i class="fa-solid fa-heart commentIcon commentHeart"></i>';
    
    // 좋아요 기능 구현
    heart.addEventListener('click', (e) => {
        e.target.classList.toggle('fa-heart-circle-check');
    })

    deleteComment.innerHTML = '<i class="fa-solid fa-trash commentIcon"></i>';
    // 삭제 기능 구현
    deleteComment.addEventListener('click', () => {
        newUserComment.removeChild(commentDiv);
    })

    console.log(commentDiv);

    commentDiv.appendChild(userName);
    commentDiv.appendChild(comment);
    commentDiv.appendChild(heart);
    commentDiv.appendChild(deleteComment);

    newUserComment.appendChild(commentDiv);
}

submitCommentBtn.addEventListener("click", (e) => {
    checkInput();
    inputComment.value = "";
});

heart.addEventListener("click", (e) => {
    e.target.classList.toggle('fa-heart-circle-check');
});

 

 

⭐️ Event.target ⭐️

나는 위스타그램을 진행하면서 좋아요 기능 구현에만 2일의 시간을 투자했다. 덕분에 추가 구현 미션 2개를 진행하지 못했고 이는 개인적으로 시간 남을 때 끝까지 구현해볼 생각이다. 좋아요 기능을 구현하면서 하트가 눌리는, 즉 이벤트가 발생하는 대상에 변화를 주고 싶었는데 이것을 지정할 방법을 도저히 못 찾았었다. getElementsByClassName, querySelector을 이용하니 추가되는 몇 개의 하트를 누르더라도 제일 처음에 있는 기존 하트에만 좋아요가 작동했다. 왜 그러는지는 충분히 알고 있었다. 왜냐하면 배열에서 등장하는 첫 번째 요소를 가져오기 때문이다. 이를 통해서 좋아요를 구현하는 함수에는 문제가 없고 이벤트 대상을 지정하는데 문제가 있음을 깨달았지만 도저히 방법이 떠오르지 않아서 이틀 동안 삽질만 수백번 하고 금요일 위스타그램 세션이 종료되고 집으로 우울하게 돌아오는데 이대로 끝내면 너무 아쉬울 것 같아서 집에 돌아오는 내내 구글을 뒤져봤다. 우연히 target을 사용한 사람의 글을 보았고 무슨 기능을 하는지 검색을 해보았다.

 

 

이벤트가 발생한 대상 객체를 가리킵니다.라는 문구를 보자마자 진심 희열을 느꼈다. 내가 원하는 바가 정확하게 적혀있었고 이틀 동안 고생한 게 헛되지 않을 거란 기대감이 가득 찼고 결국엔 원하는 하트에 좋아요를 누를 수가 있게 되었다. 비록 좋아요에 이틀의 시간이 뺏겨서 다른 기능을 구현하지는 못했지만 의미 있는 시간이었다고 생각이 든다. 자바스크립트 기본서를 한 번이라도 봤다면 쉽게 풀었을 문제였을 것 같은데 자바스크립트는 지난 수요일부터 일주일 정도 공부한 게 전부였던 터라 여기까지 오는데 오래 걸렸다. 남은 두 개의 기능을 마저 구현해보고 싶지만 3주차부터 리액트를 배우기 때문에 주말에는 리액트에 시간을 투자해야 하므로 남은 기능은 나중에 개인적으로 구현해볼 생각이다.

 

좋아요 기능

마지막 날 겨우 target의 기능을 알고서 작성했기 때문에 기존에 있던 코드인 하트에 적용할 함수, 추가로 생긴 댓글 하트에 적용할 함수 두 개가 생겼다. 추후에 리팩토링을 통해서 하나의 함수로 만들어서 정리하여 사용할 필요가 있어 보인다.

 


 

아쉬웠던 점과 좋았던 점

위코드에 들어오기 전에 자바스크립트에 대해서 전혀 모르고 들어왔다는 점이다. 어떤 메서드가 있는지를 모르니 검색을 하는 것부터 난관이었다. 예를 들어서 좋아요 기능을 구현할 때 target의 존재를 알았더라면, 이벤트 발생 객체(object onto which the event was dispatched)를 검색하면 됐을 텐데 자식 객체 선택하는 방법을 검색하고 있었다. 자바스크립트 기본의 중요성을 깨닫고 위코드를 진행하는 동안 시간이 그렇게 많이 남지 않는 것을 알지만, 기본서를 하루에 한 시간 정도는 읽을 시간을 내야겠다고 생각이 들었다.

 

그래도 금요일 Westagram wrap-up 시간을 갖고 집으로 돌아올 때 좋아요 기능을 포기한 상태였는데 이대로 끝낼 수는 없다면서 끝까지 해냈던 경험이 앞으로의 개발자 생활에 도움이 될 것이라고 확신한다.

 


 

추가) 2주차에는 무엇을 했는가? 🏃‍♂️

조언 중 하나가 필기를 꼼꼼히 했던 일이 굉장히 도움이 됐다고 했는데 기존에 정리를 열심히 해왔던 나로서는 반가운 말이었다.

 

휴식의 중요성 🧘

5월 4일 수요일에 31기가 33에게라는 시간을 가졌다. 먼저 위코드를 경험하고 기업 협업에 나가 있는 선배기수가 후배 기수에게 조언을 하는 시간이었다. 고민이 하나 있었는데 같은 33기분들 중에는 역시나 뛰어나게 잘하고 과제를 무리 없이 해나가는 사람이 몇 있었다. 그런 사람들을 보며 내가 너무 뒤처지고 있다는 느낌을 받아서 잠을 줄여가며 하루도 안 쉬고 수요일까지 열흘을 달렸다. 그런데 몸이 많이 힘들었는지 하루는 도저히 집중이 안됐기에 조심스럽게 31기에게 휴식은 얼마나 가졌나요? 라고 물어보았다. 예상은 "잠은 죽어서 자라" 였지만 의외로 대답은 "일주일에 한 번은 그냥 마음 놓고 푹 쉬었다."였다. 여기서 조금이나마 위안되어 하루 동안 푹 쉬지는 못하더라도 잠만큼은 내 몸에 맞게 충분히 자야겠다고 생각했다. 열흘 동안 페이스 조절을 하지 않고 무리하게 달렸더니 이대로는 끝까지 달리지도 못하고 지칠 것이라고 생각 들었다. 조금은 느리더라도 내 속도에 맞추어 꾸준히 하는 것이 중요함을 깨닫는 순간이었다.