Instagram Clone Coding: Westagram! (2)

dosilv·2021년 4월 24일
2

Instagram Clone Coding

목록 보기
2/2
post-thumbnail

▶ 목차

🪐 1. id & password 타이핑 시 기능들
🪐 2. id, password Validation
🪐 3. 검색창 선택 시 아이콘 & 글자 이동
🪐 4. 댓글 추가, 좋아요, 삭제 (🌟로그인 시 입력한 id로 댓글 달기!🌟)
🪐 5. 게시물 좋아요 (클릭 시 애니메이션)
🪐 6. id 검색 기능
🪐 7. 인스타 스토리 바
🪐 8. 스크린 이미지 전환



🪐 5. 게시물 좋아요 (클릭 시 애니메이션)

🌟 클릭했을 때 애니메이션 주기

우선 CSSkeyframes로 표현하고 싶은 애니메이션을 설정했다. 인스타그램 좋아요는 누르면 빈 하트가 커지면서 빨간하트로 바뀌고 다시 작아지는... 그런 효과니까 일단 커지게 하고 작아지게 하는 pop&shrink(내맘대로 지음) 애니메이션을 아래처럼 만들었다.

@keyframes pop {
    0% {transform: scale(1);}
    100% {transform: scale(1.2);}
}

@keyframes shrink {
    0% {transform: scale(1.2);}
    100% {transform: scale(1);}
}

그 다음에 각각의 하트에 저 키프레임들로 애니메이션 속성을 줘야 하는데... 속성값들이랑 순서를 까먹어서 테이블로 다시 정리!


💓 CSS animation

animation: name time func delay iteration dir fill play
디폴트 - 0s ease 0s 1 normal none running
  • time(animation-duration): 회당 진행 시간
  • func(animation-timing-function): 애니메이션의 가속도
    • linear: 일정한 속도
    • ease: 느리게 시작했다가 빨라졌다가 다시 느려짐
    • ease-in: 서서히 시작함
    • ease-out: 서서히 끝남
    • steps(number): number만큼 애니메이션 진행을 쪼개서 단절적으로 보여줌
  • delay(animation-delay): 시작 지연 시간
  • iterateion(animation-iteration-count): 반복 횟수 (숫자 or infinite)
  • dir(animation-direction): 진행 방향
    • normal: 정방향으로 진행
    • reverse: 역방향으로 진행
    • alternate: 정방향으로 진행 후 역방향으로 진행
  • fill(animation-fill-mode): 완료 후 상태
    • none: 아무 스타일도 적용하지 않음
    • forward: 마지막 키프레임 상태 유지
    • backward: 시작 키프레임 상태 유지
  • play(animation-play-state): 애니메이션 진행 여부
    • running: 진행
    • pause: 정지 (hover 등과 함께 사용할 수 있을 듯!)

저렇게 애니메이션을 생성하고 또 고민에 빠졌다,,, hover로 커서 올릴 때 효과를 주는 건 알겠는데, 클릭했을 때 바꾸려면 어떻게 해야 되지...? 하다가 클래스에 애니메이션 스타일 속성을 주고, 자바스크립트로 클릭할 때 클래스네임을 추가하면 되지 않을까? 근데 그러면 클릭할 때 애니메이션이 정상적으로 시작되나? (궁금증 폭발🤔) 머.. 잘 모르겠지만 일단 시도해봄~~~

.tab img:first-child.pop {
    animation: pop 0.2s linear 0s 1 alternate;
}		

&

function addLike() {
    likeBtn.classList.toggle("pop");
}

likeBtn.addEventListener("click", addLike);

그랬더니 클릭할 때 애니메이션이 정상적으로 실행됐다! 🥳🥳🥳
근데 이제 그걸 빨간 하트로 바꿔야 하는데,, 저 addLike 함수 안에 빨간 하트 요소를 선택해 redHeart.style.display = "inline"으로 바꿨더니(원래 display: none) 클릭하자마자 빨간하트가 나오면서 빈 하트 애니메이션이 묻혀서 안 보였다 🙁
그래서 setTimeout을 써서 애니메이션 재생시간(0.2s = 200ms) 만큼 지난 후 inline으로 바뀌도록 만들었다. 그리고 좋아요 숫자를 표시하는 텍스트도 바뀌도록 하는 코드도 추가했다. 그래서 최종적인 코드!

function addLike() {
    likeBtn.classList.toggle("pop");
    setTimeout(() => {redHeart.style.display = "inline"}, 200);
    likes.innerHTML = "좋아요 20개";
}


🪐 6. id 검색 기능

🌟 계정 오브젝트를 담은 배열 생성하기

오브젝트를 실제로 써 보니까 얼마나 유용한지 알게 되었다! 저 많은 사람들의 엘리먼트를 html로 하나하나 만드는 건 너무 비효율적이기 때문에... 배열을 만들고, 그걸 forEach나 filter, map으로 돌려서 원하는 코드를 딱 한 번만 짜면 모든 계정에 적용할 수 있는 것!
근데 계정 정보가 id 스트링 하나만 있는 게 아니라 프로필 사진도 있고, 설명도 있어서 아래처럼 오브젝트를 담은 배열을 만들었다.

const USERS = [{
    id: 'dlwlrma',
    profileImg: 'img/dlwlrma.jpg',
    description: '이지금 IU'
}, {
    id: 'songkang_b',
    profileImg: 'img/songkang_b.jpg',
    description: '송강'
}, {
    id: 'cafeknotted',
    profileImg: 'img/cafeknotted.jpg',
    description: '노티드 Cafe Knotted'
},
.
.
.
];

인스타에서 연예인들의 실제 계정과 각종 베이커리카페 계정을 따왔다😗 처음엔 검색 기능 구현하려고 만든 거였는데, 나중에 스토리랑 추천친구에 뜨는 요소들 만들 때도 매우 유용했음!



🌟 startsWith으로 자동완성 기능 적용하기

이제 저 오브젝트들 id와 사용자가 입력한 값을 비교해서! 앞부분이 일치하는 오브젝트들만 따로 배열을 만들기 위해 filterstartsWith 메서드를 이용했다. 원래는 빈 배열을 선언해 놓고 이것도 forEach문을 돌면서 조건에 맞는 오브젝트들을 빈 배열에 추가하게 했는데, 리팩토링 과정에서 멘토님의 코멘트를 보고 filter 함수로 수정했다.

const matchedUsers = USERS.filter(users => users.id.startsWith(typing.value));

그리고나서 matchedUsers라는 배열을 이용해 각 검색리스트 안에 들어갈 엘리먼트들을 생성하고, 검색리스트에 append했다. forEach템플릿 리터럴(``)을 사용했다.

matchedUsers.forEach(i => {
    const matchedId = document.createElement('div');
    matchedId.innerHTML = `<div class="searchedUser">
    <img alt="user's profile image" src=${i.profileImg}>
    <div class="userId">
      <p class="id">${i.id}</p>
      <p class="gray twelve" id="description">${i.description}</p>
    </div>
    </div>`
    searchList.appendChild(matchedId);
  })

그리고 매치되는 오브젝트가 없을 경우 '검색결과가 없습니다'라는 메세지를 추가하도록 조건문을 추가해서 아래처럼 showMatchedId라는 함수를 완성했다! 함수 짜놓고 확인해 볼 때 잘 작동하면 넘 뿌듯해>.<

function showMatchedId() {
        searchList.innerHTML="";
        const matchedUsers = USERS.filter(users => users.id.startsWith(typing.value));
        if(matchedUsers.length === 0) {
            searchList.innerHTML='<p>검색결과가 없습니다.</p>';
        }
        matchedUsers.forEach(i => {
            const matchedId = document.createElement('div');
            matchedId.innerHTML = `<div class="searchedUser">
            <img alt="user's profile image" src=${i.profileImg}>
            <div class="userId">
              <p class="id">${i.id}</p>
              <p class="gray twelve" id="description">${i.description}</p>
            </div>
            </div>`
            searchList.appendChild(matchedId);
        })
    }


🪐 7. 인스타 스토리 바


뭔가 예뻐 보여서 빨리 만들고 싶었던(ㅎㅎ) 스토리 목록~~~

🌟 오브젝트 배열 뒤집어서 엘리먼트 만들기

위에서 만든 USERS를 그냥 쓸 수 도 있었지만, 다양한 계정들을 보여주고 싶어서 USERS의 순서를 뒤집어서 적용하고 싶었다.
처음엔 그냥 USERS.reverse()를 해서 forEach를 돌렸더니 USERS의 원본 배열까지 수정되어서 검색창에도 뒤집힌 순서대로 계정들이 나와버렸다🤨
그래서 아래처럼 새 변수에 USERS를 담아서 그 새 변수에 revers()를 하면 되지 않을까?! 했는데

const usersForStory = USERS;
usersForStory.reverse();

똑같이 USERS까지 reverse가 적용됐다. usersToSearch가 단순히 USERS를 복사하는 게 아니라 아예 USERS를 참조하는 걸루 되나 보다....
방법이 없을까 하고 찾아보다가 옛날에 얼핏 본 전개연산자('...')로 원본 배열 수정 없이! 배열을 뒤집는 방법을 알아냈다.

const usersForStory = [...USERS].reverse();

그래서 검색리스트 요소 순서랑은 반대로 스토리 요소들을 찍어낼 수 있었다!

추가적으로 저 스토리 요소가 hover 상태일 때 스토리링에 transform:rotate와 transition값을 줘서 커서를 올리면 링이 빙글빙글 돌도록 만들었다.

.story:hover #storyring {
    transform: rotate(3turn);
    transition: 4s;
}


🌟 긴 아이디 말줄임표 처리


근데 가로로 정렬을 하다 보니까 id가 짧으면 괜찮은데, 긴 경우엔 사진처럼 양옆 요소들이랑 겹쳐서 보기가 싫었다. 그래서 id가 10자 이상인 경우엔 8자만 보여주고 말줄임표로 처리하도록 아래 코드를 추가했다.

const ids = document.querySelectorAll(".story p");
ids.forEach(id => {
    if(id.innerHTML.length > 10) {
        const longId = id.innerHTML;
        id.innerHTML = longId.slice(0, 8)+'...';
    }
})


근데 여기서 의문점! innerHTML을 innerTEXT로 바꾸면 오류가 생긴다. 콘솔창에 innerHTML이랑 innerTEXT를 찍어 보면 똑같이 오브젝트의 id값이 나오는데.... 멀까? 나중에 더 고민해 봐야겠당. (이러고 안할듯)
👉4.26 추가: innerText인데 innerTEXT라고 써서 그런 거였다ㅜ!!! 참나 완전 바보아님?



🌟 슬라이드 버튼 추가

우선 overflow: hidden 스타일을 준 <div class="storyBox"> 안에, 전체 스토리 리스트를 포함하는 <div class="storySpan">을 하나 만들었다(박스는 유지한 채 스토리들만 양 옆으로 움직여야 하니까). 그리고 storyBox 안, storySpan 밖에 (버튼은 움직이면 안 되니까!) 버튼도 하나씩 추가했다.

<div class="storyBox">
  <i class="fas fa-chevron-circle-left"></i>
  <i class="fas fa-chevron-circle-right"></i>
  <div class="storySpan">
  </div>
</div>

그리고 자바스크립트에서 storySpan을 선택해서 버튼을 누를 때마다 right를 조정해 주면 되겠지! 하고 let right를 선언해 storySpan.style.right를 담았다. 근데 +- 연산을 하려면 스트링이 아닌 숫자 형태여야 하는데 style.right는 "0px", "100px" 이런 식으로 나오니까 이걸 숫자로 변환하기 위해 parseInt를 사용했다.

const prevBtn = document.querySelector(".fa-chevron-circle-left");
const nextBtn = document.querySelector(".fa-chevron-circle-right");
let right = parseInt(storySpan.style.right);

const F00 = 265;

function next() {
    if(right < 795) {
        right += F00;
        storySpan.style.right = `${right}px`;
    }
}

function prev() {
    if(right > 0) {
        right -= F00;
        storySpan.style.right = `${right}px`;
    }
}

nextBtn.addEventListener("click", next);
prevBtn.addEventListener("click", prev);

근데! 버튼을 눌러도 작동이 안됨ㅠ!!! 내 로직은 완벽한데~~~;;; 머지????😒 하고 console.log로 이것저것 찍어보다가 알게된 사실! css 파일에서 지정한 style값은 자바스크립트로 접근이 안 되고 undefined로 뜬다...

자바스크립트에서 style.color 등으로 들어가서 속성을 부여하는 건 가능한데! 그건 html 태그에 inline style로 지정하게 되는 것. 그래서 애초에 storySpan.style.right은 undefined인데 if(right < 795), if(right > 0) 등으로 undefined랑 숫자를 비교하려고 하니까 false가 되고 if문에서 통과가 안 된 거였다.

그래서 생각해낸 두 가지 해결 방법!
1. html에서 storyBox 태그에 인라인으로 style="right: 0px"을 삽입한다.

<div class="storySpan" style="right: 0px;">
  1. javascript에 style.right가 없을 경우(버튼 한 번도 안 누른 초기 상태일 때) right를 0으로 할당하는 코드를 추가한다.
storySpan.style.right? right = parseInt(storySpan.style.right) : right = 0;

두 경우 다 실험해 봤는데 잘 작동했다! 그냥 기능을 만들었다는 사실보다 새로운 걸 알게 되어서 더 뿌듯✨✨



🪐 8. 스크린 이미지 전환


이건 사실 선경님이 javascript의 promise로 구현할 수 있다고 하셨는데 그게 뭔지 몰라서(...) 다음에 추가해 봐야지 했던 효과인데, 저 위에 CSS animation 개념 정리하다가 문득...! 이걸로도 되지 않을까? 싶어서 실험해 봤다. 근데 얼렁뚱땅 되길래 신남~~~

🌟 CSS animation으로 이미지 바꾸기

첨에 keyframes로 진행 퍼센트를 균일하게 쪼개고, 각각 다른 이미지를 넣어서 iteration을 infinite로 주면 되지 않을까! 생각했는데 img src는 스타일값이 아니라 html attribute니까 안 되겠구나 싶었다. 근데 또 문득 background-image는 스타일값이니까 되지 않을까?! 해서 도전해 봄.

우선 html에서 빈 div를 만들어 저 아이폰 화면이랑 딱 맞는 위치&크기로 맞춰 놓고, CSS에서 아래처럼 keyframes를 지정했다.

@keyframes imgchange {
    0% {background-image: url("img/screen1.jpg");}
    25% {background-image: url("img/screen2.jpg");}
    50% {background-image: url("img/screen3.jpg");}
    75% {background-image: url("img/screen4.jpg");}
    100% {background-image: url("img/screen1.jpg");}
}

그리고 저 스크린 CSS 값으로 애니메이션을 한 줄 더 추가했다.

animation: imgchange 6s linear infinite;

그랬더니 keyframes 자체가 %로 진행되는 거라서 부드럽게 넘어감! 아마 자바스크립트에서도 setTimeOut 같은 걸로 하면 이미지 src 자체를 바꾸면서 구현할 수 있을 것 같당.



클론코딩보다 벨로그 쓰는 게 더 힘든 것 같은 기분....ㅎㅎ 그래도 기록해 놓으니까 뿌듯하고 다시 한 번 개념들을 정리할 수 있어서 좋았다~~~🍀🍀

profile
DevelOpErUN 성장일기🌈

4개의 댓글

comment-user-thumbnail
2021년 4월 24일

역시 도은님.. 정리까지 완전 잘하시네요 .... 잘보고갑니다~! 👍👍👍👍

1개의 답글
comment-user-thumbnail
2021년 4월 30일

도은님ㅋㅋㅋㅋㅋㅋ너무 재밌고 신나하시는게 글에서도 막 느껴져욬ㅋㅋㅋㅋ👍👏💯👍👏💯👍👏💯

1개의 답글