[클론코딩]인스타그램

Jiyon Lee·2021년 4월 24일
0
post-custom-banner

목차

1. aside 부분 돔 구현
2. 댓글 추가, 삭제 및 좋아요 기능
3. 게시글에 좋아요 누르기
4. nav에 아이디 검색 기능
5. 후기

4월 12일부터 위코드 부트캠프 생활을 시작했다 !
바닐라자바스크립트로 인스타그램을 클론코딩 해봤다.😀
다음주 3주차에는 인스타그램을 리액트를 이용해서 다시 구현해볼 것이다.

본 게시물은 위코드 2주차 과제인 인스타그램 클론코딩-로그인페이지와 메인페이지 구현 내용이다. 기억하고 싶은 코드가 있는 기능을 위주로 작성해보겠다 !

구현한 기능

1. 댓글 추가 삭제 및 각각 댓글의 좋아요 누르기
2. 아이디 검색 시 팝업창을 띄워서 현재 페이지에 보이는 아이디만 보이기

신경 쓴 부분

- 오른쪽 aside부분 프로필 리스트 추가 시 객체들의 배열을 만들고 배열을 반복하면서 돔을 그려나갔다. API를 받아와서 처리할 때 객체와 배열 사용에 익숙해져야 해서 하드코딩을 지양하고 유지보수, 확장성이 용이하게 만들려고 노력했다 !

1. aside 부분 돔 구현

const users = [
    { id: "jiyon", img: "./images/hao.jpg" },
    { id: "Shaman_king", img: "./images/hao2.jpg" },
    { id: "iu", img: "./images/iu.jpg" },
    { id: "Jeon_dongseok", img: "./images/dongseok.jpg" },
    { id: "kakashi", img: "./images/kakashi.png" },
    { id: "gaara", img: "./images/gaara.jpg" },
    { id: "hyoshin", img: "./images/hyoshin.jpeg" },
    { id: "lock-li", img: "./images/lock-li.jpg" },
    { id: "naruto", img: "./images/naruto.jpg" },
];
const userIds = users.reduce((pre, cur) => {
    return [...pre, cur.id];
}, []);
const userImgs = users.reduce((pre, cur) => {
    return [...pre, cur.img];
}, []);
  • 각각 id와 img를 받아올 users객체를 선언했다.
  • id만의 배열과 img만의 배열을 만들기 위해 고민하다가 머릿속 어딘가에서 reduce가 갑자기 떠올라서 reduce메서드를 사용해봤다. 아이디 검색기능 구현 시 사용했다.
const createProfileLists = (dom, imgsrc, profileName, postedTime) => {
    const asideMain = document.querySelector(`${dom} .asideMain`);
    const imgAndId = document.createElement("li");
    const asideProfileImg = document.createElement("img");
    const idAndTime = document.createElement("div");
    const profileId = document.createElement("div");
    const postTime = document.createElement("div");

    imgAndId.classList.add("imgAndId");
    asideProfileImg.classList.add("asideProfileImg");
    idAndTime.classList.add("idAndTime");
    profileId.classList.add("profileId");
    postTime.classList.add("postTime");
    postTime.classList.add("grey");

    idAndTime.append(profileId);
    idAndTime.append(postTime);
    imgAndId.append(asideProfileImg);
    imgAndId.append(idAndTime);
    asideMain.append(imgAndId);

    profileId.textContent = profileName;
    postTime.textContent = postedTime;
    asideProfileImg.setAttribute("src", imgsrc);
};

users.forEach((user) => {
    createProfileLists(".story", user.img, user.id, "20분 전");
});
users.forEach((user) => {
    createProfileLists(
        ".recommend",
        user.img,
        user.id,
        "000님이 팔로우합니다."
    );
});
  • forEach 메소드로 users 객체를 돌면서 createProfileLists함수를 실행시켜서 돔을 그려냈다.


2. 댓글 추가, 삭제 및 좋아요 기능


postCommentBtn.addEventListener("click", () => {
    clickCommentBtn();
});

postCommentInput.addEventListener("keyup", (e) => {
    postCommentInput.value.length > 0
        ? postCommentBtn.classList.add("blue")
        : postCommentBtn.classList.remove("blue");
    if (e.keyCode !== 13) {
        return;
    } else {
        clickCommentBtn();
    }
});
  • 댓글을 입력하고 게시 버튼을 누르거나 엔터버튼을 누르면 clickCommentBtn 함수를 실행시켰다.
const clickCommentBtn = () => {
    if (postCommentInput.value.length == 0) {
        return;
    }

    const commentWrap = document.createElement("div");
    const commenter = document.createElement("span");
    const comment = document.createElement("span");
    const commentDeleteBtn = document.createElement("button");
    const commentLikesBtn = document.createElement("button");

    commentWrap.classList.add("commentWrap");
    commenter.classList.add("commenter");
    comment.classList.add("comment");
    commentDeleteBtn.classList.add("commentBtn");
    commentDeleteBtn.classList.add("commentDeleteBtn");
    commentLikesBtn.classList.add("commentBtn");
    commentLikesBtn.classList.add("commentLikesBtn");

    commentWrap.append(commenter);
    commentWrap.append(comment);
    commentWrap.append(commentDeleteBtn);
    commentWrap.append(commentLikesBtn);
    articleComment.append(commentWrap);

    commentDeleteBtn.innerHTML = `<i class="far fa-trash-alt"></i>`;
    commentLikesBtn.innerHTML = `<i class="emptyHeart far fa-heart"></i>
<i class="redHeart fas fa-heart red hide"></i>`;
    commenter.textContent = "Shaman_king";
    comment.textContent = postCommentInput.value;

    postCommentInput.focus();
    commentTime.textContent = "방금";
    postCommentBtn.classList.remove("blue");
    postCommentInput.value = "";

    commentLikesBtn.addEventListener("click", (e) => {
        let emptyHeart = e.currentTarget.querySelector(".emptyHeart");
        let redHeart = e.currentTarget.querySelector(".redHeart");
        emptyHeart.classList.toggle("hide");
        redHeart.classList.toggle("hide");
    });

    const commentDeleteBtns = document.querySelectorAll(".commentDeleteBtn");
    commentDeleteBtns.forEach((commentDeleteBtn) => {
        commentDeleteBtn.addEventListener("click", () => {
            commentDeleteBtn.parentNode.remove();
        });
    });
};
  • fontawsome에서 하트모양을 가져왔다. 속이 빈 하트와 속이 찬 하트를 가져와서 좋아요를 누를 때마다 토글이 되게 하였다.
  • 돔을 만들면서 이벤트리스너를 붙여주었다.

3. 게시글에 좋아요 누르기


pushLike.addEventListener("click", (e) => {
    e.preventDefault();
    if (pushLike.style.backgroundPosition.slice(0, 4) === "-156") {
        pushLike.style.backgroundPosition = `-130px -478px`;
        likeNum.textContent = Number(likeNum.textContent) + 1;
    } else {
        pushLike.style.backgroundPosition = `-156px -478px`;
        likeNum.textContent = Number(likeNum.textContent) - 1;
    }
});
  • 이 하트는 스프라이트 이미지로 구현했기 때문에 좋아요를 누를 때마다 backgroundPosition이 변경되도록 하였다.

4. nav에 아이디 검색 기능


const createSearchLists = (searchInputValue) => {
    if (searchPopup.childNodes.length > 0) {
        return;
    }
    const searchedList = document.createElement("li");
    const profileImg = document.createElement("img");
    const idAndNickname = document.createElement("div");
    const userid = document.createElement("div");
    const nickname = document.createElement("div");

    userid.textContent = searchInputValue;
    let userIdIndex = userIds.indexOf(searchInputValue);
    profileImg.setAttribute("src", users[userIdIndex].img);
  // ----------------------------------------
  // ex) iu의 위치를 받아서 그 위치의 이미지를 넣어줬다.
  // setAttribute를 처음 사용해보았다.
  // ----------------------------------------

    idAndNickname.append(userid);
    idAndNickname.append(nickname);
    searchedList.append(profileImg);
    searchedList.append(idAndNickname);
    searchPopup.append(searchedList);
};

navSearch.addEventListener("input", (e) => {
    if (e.target.value.length === 0) {
        searchPopup.innerHTML = "";
    } else {
        for (i = 0; i < userIds.length; i++) {
            if (userIds[i].startsWith(e.target.value)) {
                createSearchLists(userIds[i]);
                break;
            } else {
                searchPopup.innerHTML = "";
            }
        }
      // ------------------------------------------------
      // forEach 사용시 break 가 되지 않아서 for loop을 돌렸다.
      // ------------------------------------------------
    }
});

navSearch.addEventListener("blur", () => {
    searchPopup.innerHTML = "";
});
  • String.startsWith 메소드로 iu의 i만 쳐도 나오게끔 했다.

5. 후기

처음엔 딱 과제를 받고 '2~3일이면 하려나?!'라는 생각을 했었다. 오만한 생각이었다.🥺 착착 진행되는 부분도 있었지만 막히는 부분들도 많았다. 그래도 로직 생각하고 구현하는게 재밌었다. 마지막에는 조금 조급해 지기도 했다. 회사에 도움이 되는 개발자가 되어야 하는데 ..
존경하는 제로초 개발자님이 할 수 있느냐 없느냐도 중요하지만 속도도 중요하다고 했다. 정확히 빠르게 꼼꼼히 버그없이 아자아자 !

로직은 다 구현이 된 상태니 아마 다음주 리액트로 구현하는 것은 금방 할 수 있지 않을까?!! (허튼 생각이 아니길!)

profile
이사했습니다🚚🚛 https://yonyas.github.io/ 📧jiyonlee.d@gmail.com
post-custom-banner

2개의 댓글

comment-user-thumbnail
2021년 4월 24일

와 filter 말고 reduce를 사용할 수도 있네요!!! 지연님 응용력 짱..👍🏻

1개의 답글