본문 바로가기
project/AVANTE CLONE

[javascript] avante 클론코딩 회고록

by dev_kong 2022. 1. 16.
728x90
728x90

0. 목차

1. 개요

2. 결과물

3. 진행 중 이슈 및 해결

4. 후기

1. 개요

1월 10일 부터 1월 14일 까지 진행한 아반떼 클론코딩.

처음엔 언제 다하나 싶었는데 하나하나 하다보니 제법 만족스러운 결과물이 나왔다.

하루하루 진행할때마다 있었던 이슈 같은걸 정리하려고 했는데,

막상 시작하고 나니 만드는데 급급해서 그때그때 정리를 못했다.

한번에 정리를 하려니까 이것도 막막하네...

 

2. 결과물

 

최애애애대한 눈에 보이는 기능들은 다 구현해보려고 노력을 했다.

그래서 이것저것 해보다 보니 여기저기서 이슈들이 생기더라구...

3. 이슈 및 해결

진짜 뒤지게 많은 이슈가 있었다.

짜잘짜잘한 오타이슈 까지 다 합치면 아마 3천개는 됐을듯..?

그래도 짜잘한거 말고 큼직하게 좀 기억에 남는 것들만 정리해보려고 한다.

 

3-1. 스크롤 시 슬라이드 두개씩 넘어오는 이슈

아반떼 사이트를 보면

스크롤한번에 많이 땡기면 한번에 두개의 슬라이드가 넘어온다.

그리고 내가 짠 코드도 이런식으로 동작했다.

근데 나는 이상하게 이게 싫더라.

휠을한번에 아무리 많이 돌려도 하나의 슬라이드만 올라오게 만들고 싶었다.

이부분을 해결하기위해 진짜 많은 검색을 해봤는데,

결국 해결하지 못했다.

그런데 다른 부분의 이슈를 해결하기위해 구글링을 하던도중,

stack overflow에서  maximum time of mouse wheel (정확하지 않음..)이란 글을 봤는데,

대략적인 내용은

브라우저마다 휠이 한번에 최대로 동작할 수 있는 시간이 정해져 있다라는 내용이었다.

으음..? 하면서 읽어내려가던 도중 깨달음을 얻었다.

 

let isWheelActive = false;

function wheelEventHadler(event) {
  event.preventDefault();
  if (isWheelActive) return;

  isWheelActive = true;

  if (event.wheelDelta < 0) {
    moveNext();
  }

  if (event.wheelDelta > 0) {
    movePrev();
  }

  checkCount();

  setTimeout(() => {
    isWheelActive = false;
  }, 1500);
}

내가 짠 코드중 휠 액티브에 관한 부분인데,

isWheelActive가 false일 때만 동작하게 만들었다.

휠이 움직인 방향에 따라 moveNext와 movePrev가 동작한다.

그리고 마지막 줄에 setTimeout을 통해 1.5초 뒤에 isWheelActive를 false로 변경해준다.

저 setTimeout의 delay_time이 문제였다.

stackoverflow에서 본 글에 따르면 크롬에서는 한번에 최대 1.3초가 휠의 최대 동작시간이라고 나와있었기에,

1.3초 보다 긴 1.5초로 지정해주었더니, 진짜 문제가 해결 되었다.

물론 1.4초로 지정해도 한장만 넘어가는걸 확인할 수 있는데, 숫자가 1.4보단 1.5가 이뻐서 1.5로 했다.

근데 사실 이 부분은 취향의 차이가 아닐까.. 

나 같은 경우는 휠을 많이 땡겨도 한장만 넘어갔으면 좋겠다 라고 생각하지만

어떤 사람은 많이 땡기면 두장도 세장도 넘어가야지! 라고 생각할 수도 있을듯..

 

이 이슈를 해결하면서 가장 마음이 아팠던건,

저 stack oveflow에서 봤던 글의 링크를 저장해두지 않았다는것 ㅠㅠㅠㅠ

솔직한 얘기로 신빙성이 있는 얘기인지도 확실하지도 않아서,

링크를 저장해뒀어야 했는데,

신나서 테스트 해보고 나니까 ㅎㅎㅎ 링크가 사라져있었다.. ㅎ

 

3-2. querySelectorAll Array Method 사용 불가 이슈

일단 코드를 보자.

 

function ContentSlide(imgView, labelClass, img) {
  this.designImgView = document.querySelector(imgView);
  this.labelList = document.querySelectorAll(labelClass);
  this.designImg = document.querySelector(img);

  this.labelIndex = null;
  this.designSlideWidth = this.designImg.offsetWidth;

  this.contentSlideInit = () => {

    this.labelList.forEach((v) => {
      v.addEventListener('click', this.ImgSlide);
    });
  };

  this.ImgSlide = (event) => {
    // 문제의 부분
    this.labelIndex = this.labelList.indexOf(event.target);
    this.designImgView.style.transform = `translateX(-${
      this.designSlideWidth * this.labelIndex
    }px)`;
  };
}

슬라이드를 사용하는 페이지가 여러개 있다보니 생성자함수로 만들었다.

전체 이미지를 감싸고 있는 div와 클릭에 반응할 label 의 노드리스트와

이미지를 땡겨오고,

클릭된 label의 인덱스와 이미지의 넓이를 곱해진 값만큼 이동하는 함수이다.

 

이론상 완벽하다.

그런데

indexOf가 사용이 안된다.

엥... indexof 였던가.. 해서 바꿔 봤지만 당연히안되지;;;

querySelectorAll의 선택자를 잘못 지정했나 싶어서

console.log를 이용해 labeList를 찍어봤다.

뭐지 잘 찍히는데.. 뭐가 문젤까 

하던 도중 눈에들어온 프로토타입...

노드리스트 라고 되어있다.

뭐지 querySelectorAll 로 땡겨온 애는 Array의 형태를 프로토 타입을 상속 받지 않네....?

 

책에서 봤던 내용이 떠오른다..!

노드리스트는 배열처럼 index와 length가 있는 유사배열객체(aka 배열조무사..!)이며

이터러블 속성을 갖고있어 for문으로 순회가 가능하다.

 

아아아아아. 확인차 프로토타입을 눌러보니

역시나 프로토타입 체인상에 Array는 존재 하지 않는다.

그러니까 당연히 Array의 메서드를 사용할 수 없지..

 

그럼 어떻게 해야 될까 고민하다가.

아 저 객체를 그냥 배열로 바꾸면 Array 메서드 쓸수 있겠네..?

어떻게 바꾸지..?

하다가 떠오른 spread연산자.

이때 살짝 소름 돋았음.

 

spread연산자를 이용해 수정한 코드

function ContentSlide(imgView, labelClass, img) {
  this.designImgView = document.querySelector(imgView);
  this.labelList = document.querySelectorAll(labelClass);
  this.designImg = document.querySelector(img);

  this.labelIndex = null;
  this.designSlideWidth = this.designImg.offsetWidth;

  this.contentSlideInit = () => {

    this.labelList.forEach((v) => {
      v.addEventListener('click', this.ImgSlide);
    });
  };

  this.ImgSlide = (event) => {
    // 문제의 부분
    this.labelIndex = [...this.labelList].indexOf(event.target);
    console.log(this.labelIndex);
    this.designImgView.style.transform = `translateX(-${
      this.designSlideWidth * this.labelIndex
    }px)`;
  };
}

확인을 위해 console.log로 인덱스를 한번 찍어봤다.

 

아주 잘된다 ㅎㅎㅎ

 

3-3. 브라우저 사이즈 변경시 이미지 슬라이드 이슈

오 제법 고통 받았던 부분이다.

사실 처음에는 반응형을 염두에 두고 클론을 시작했었다.

그런데.... 기능을 하나하나 구현 할 수록 반응형으로 만드는건 좀 힘들지 않을까.. .란 생각이 들었고,

게다가 반응형 => CSS노가다 원모어타임.. 이란걸 깨닫게 된 이후로

결국 반응형은 포기했다..ㅎ;;

 

무튼 이번 이슈는 반응형웹을 제작하는 도중에 또 생길 수 있는 이슈라고 생각해 정리하기로 결심!

 

화면을 줄였을때의 이슈
화면을 늘였을 때의 이슈

 

function ContentSlide(imgView, labelClass, img) {
  this.designImgView = document.querySelector(imgView);
  this.labelList = document.querySelectorAll(labelClass);
  this.designImg = document.querySelector(img);

  this.labelIndex = null;
  this.designSlideWidth = this.designImg.offsetWidth;

  this.contentSlideInit = () => {
  
    this.labelList.forEach((v) => {
      v.addEventListener('click', this.ImgSlide);
    });
  };

  this.ImgSlide = (event) => {
    this.labelIndex = [...this.labelList].indexOf(event.target);
    this.designImgView.style.transform = `translateX(-${
      this.designSlideWidth * this.labelIndex
    }px)`;
  };
}

 

이게 문제의 코드였다. 

맞다 3-2. 와 같은 코드다.

3-2를 해결하니까 

3-3이 생긴 이슈체이닝^^

 

무튼 이 코드는 

클릭된 라벨의 인덱스를 라벨리스트에서 검색해서

라벨의 인덱스와 이미지의 넓이를 곱한만큼 

이동하는 방식으로 동작하는데,

문제는 윈도우가 리사이즈 됨에 따라 이미지의 크기도 변하는데,

이미지의 넓이를 처음에 랜더 될때만 받아오기 때문에,

발생하는 이슈였다.

즉, 이미지 크기가 변하면 변한 크기에 따라 이동하는 값도 변경되어야 되는데

그러지 못했다는거임.

 

예전에 MDN에서 addEventListener의 type들을 쭈욱 한번 읽어본적이 있는데

그때 봤던 resize type이 생각났다.

아 윈도우에addEventListener를 걸고 리사이즈 될때마따 이미지크기를 새로 따오면 되겠구나!

싶어서 수정한 코드가

 

function ContentSlide(imgView, labelClass, img) {
  this.designImgView = document.querySelector(imgView);
  this.labelList = document.querySelectorAll(labelClass);
  this.designImg = document.querySelector(img);

  this.labelIndex = null;
  this.designSlideWidth = this.designImg.offsetWidth;

  this.contentSlideInit = () => {
    window.addEventListener('resize', () => {
      this.designSlideWidth = this.designImg.offsetWidth;
      this.designImgView.style.transform = 'translateX(0)';
    });

    this.labelList.forEach((v) => {
      v.addEventListener('click', this.ImgSlide);
    });
  };

  this.ImgSlide = (event) => {
    this.labelIndex = [...this.labelList].indexOf(event.target);
    this.designImgView.style.transform = `translateX(-${
      this.designSlideWidth * this.labelIndex
    }px)`;
  };
}

 

이거였고,

 

결과역시 생각한대로 잘 작동해 주었다.

 

3-4. keypress 방향키 이슈

90%의 사용자는 신경도 안쓰겠지만,

저 사이트 키보드에도 반응한다.

뭐 keypress로 화살표 눌러지면 동작하게 하면 되겠네

했는데

이게 왠걸 화살표를 눌러도 반응이 없다.

알고보니 keypress는 입력이 되는 키에만 반응한다..!

입력이 되는 키란, 영어, 한글, 숫자, 특수만자 같은 키들을 말하며

방향키나, 탭키, 딜리트키, 백스페이스 키 같은 입력이 안되는 키들에는 반응하지 않는다(엔터와, 스페이스키는 예외)

 

그렇다면 어떤 이벤트를 사용해야 할까.

답은 keydown 이었다.

keydown을 이용하니 내가 원하는 대로 동작하였다.

 

window.addEventListener('keydown', keyEventHandler);
function keyEventHandler(e) {
  if (
    e.keyCode === 9 ||
    e.keyCode === 38 ||
    e.keyCode === 40 ||
    e.keyCode === 37 ||
    e.keyCode === 39
  ) {
    e.preventDefault();
  }
  if (isKeyActive) return;

  isKeyActive = !isKeyActive;

  if (e.keyCode === 40) {
    moveNext();
  }

  if (e.keyCode === 38) {
    movePrev();
  }

  checkCount();

  setTimeout(() => {
    isKeyActive = false;
  }, 1300);
}

 

다른 이유때문에 탭키도 동작하지 않게 막아버렸다.

화살표가 눌러졌을때 스크롤이 움직이지 않게끔 막아버렸고,

아래화살표가 눌러지면 moveNext함수가

윗방향화살표가 눌러지면 movePrev함수가 동작하게끔 만들었다.

 

keypress와 keydown의 차이를 알수 있었다.

 

 

4. 후기

 

솔직히 말하면 진짜 재밌었다.

내가 무언가를 입력하면 바로바로 눈에 보이는 변화가 생기는게 신기하고 재밌었다.

처음 페이지가 넘어가는 로직을 짤때는 조금 힘들었지만,

그부분을 넘기고 나니 생각보다 수월하게 만들었다.

다만 아직도 CSS는 나를 화나게 만든다...

대체 왜..?! 라는 의문이 머릿속에 남는데,

계속하다보니 약간 득도의 경지에 오른듯

잘한 다는게 아니라 포기함 ㅎㅎ

아..? 그래 그럼 이렇게 하면 대충 되겠네? 하면서 해버린다.

약간 논리를 포기했다.

갬성코딩이랄까.

 

지옥같던CSS에서 js 로 넘어오면 ㅎㅎ.. 행복하다.

이런저런 이슈들이 여전히 괴롭히지만,

js는 논리적이다ㅎ 조금 고민해보고 검색해보면

아아아 맞네 맞네 또는 아아 이런거였어? 하면서 깨달음을 얻게 된다.

 

하는 내내 항상 좀 피곤하긴했어도

진짜진짜 재밌었고

열심히 한만큼의 결과물도 나온것 같아서 뿌듯하다.

728x90
728x90

댓글