[Ez to Play _ Function] 까먹기 전에 적는 캐러셀 반응형으로 리팩토링하기

 

 

내가 열심히 만든 홈 화면과 캐러셀. 반응형을 시도하기로 했기 때문에 화면의 크기에 따라 컨텐츠 크기도 바뀌도록 만들어야 한다.

 

반응형은 @media로 시작하는 media query를 사용하면 된다. 

 @media screen and (min-width: 820px) {
      width: calc(170px * ${sizeChange.tablet});
      height: calc(${props => props.height}px * ${sizeChange.tablet});
    }

우리는 미디어 쿼리를 공통 컴포넌트로 만들어 사용했는데 이 글을 쓰는 이유는 캐러셀 로직에 관련된 부분이므로 css 부분은 넘어가도록 하겠다. 

 

앞선 캐러셀 만들기 글에서도 언급했지만 캐러셀은 가로로 길게 늘어선 이미지들을 화면 길이만큼씩 순서대로 보여주는 식이다. 문제는 캐러셀이 동작과 관련된 코드에 가로 길이가 들어가고, 반응형으로 만들기 위해서는 css에 미디어 쿼리를 추가하는것과 별개로 화면에 맞게 가로 길이가 바뀌도록 동작 로직을 수정해야 한다는 것이다. 조금 머리를 써야했기에 오늘은 그 과정을 기록하려고 한다. 

 

완성하면 이렇게 된다.

 


 

 

앞선 코드의 일부를 살펴보면 390이 잔뜩 들어가 있는 것이 보인다. 원래 캐러셀의 사이즈다. -390씩 위치를 이동시켜가며 다음 슬라이드를 보여주도록 코드가 작성된 것을 알 수 있다. 

 

저 390, 화면 크기에 따라 390이었다가, 585였다가 하며 화면 크기에 따라 알아서 바뀌어야 한다.

 

일단 사용할 변수를 만든다. 사실 실제 순서는 이게 마지막이었지만 설명의 용이를 위해..

const mobileSize = 390; // 캐러셀 모바일 사이즈
const tabletSize = 390 * 1.5; // 캐러셀 타블렛 사이즈
const tabletbifurcationPoint = 820; // 타블렛분기점

변수로 만들어 놓아야 나중에 사이즈를 변경하게 되더라도 코드를 한 줄 한 줄 뒤질 필요 없이 변수 값만 수정하면 되니 유지보수가 편리해진다. 타블렛 분기점이라 되어있는 화면 크기 820px을 기준으로 캐러셀 사이즈가 바뀐다.

 

그럼 javascript에서 화면 크기는 어떻게 알 수 있을까?

window.innerWidth, window.innerHeight를 사용하면 가능하다. 이 친구들은 화면의 크기를 px단위로 알려준다. 

 

let [carouselWidth, setCarouselWidth] = useState(
    window.innerWidth >= tabletbifurcationPoint ? tabletSize : mobileSize
  ); // 첫 로딩시 캐러셀 크기 초기값 세팅
  
/** 화면 크기 받아오는 함수 */
  const handleResize = () => {
    if (
      window.innerWidth >= tabletbifurcationPoint &&
      carouselWidth !== tabletSize
    ) {
      setCarouselWidth(tabletSize);
      location.reload();
    } else if (
      window.innerWidth < tabletbifurcationPoint &&
      carouselWidth !== mobileSize
    ) {
      setCarouselWidth(mobileSize);
      location.reload();
    }
  };

  /** handleResize 함수를 실행시키는 이벤트리스너 */
  useEffect(() => {
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  });

 

이렇게 작성했다. 화면 크기를 받아오는 handleResize 함수를 만들고, 이벤트리스너에 연결해 useEffect에서 실행시켰다. handleResize 함수 안에는 if문을 통해 만약 window.innerWidth가 tabletbifurcationPoint(820px)보다 크면 carouselWidth에 타블렛 사이즈, 작으면 모바일 사이즈를 지정해줬다. carouselWidth는 앞에 썼던 390 을 대신하기 위한 변수이다. 계속 바뀌어야 하기 때문에 useState로 지정해준다. 

 

여기서 설명해야 할 것이 세 가지 있다. 사실 이 두 가지가 이 글을 쓰는 주된 목적이다.

 

첫 번째로 carouselWidth의 초기값.

놀랍게도 useState의 초기값 지정에 삼항연산자를 이용할 수 있다. 캐러셀의 초기값은 처음 접속시 사용자 화면의 사이즈에 맞게 줘야 한다. 모바일 사이즈나 타블렛 사이즈 둘 중에 하나를 줘버리면 처음 접속시 그 값으로 캐러셀이 움직이기 때문에, 만약 초기값이 390인데 화면이 820이 넘어 캐러셀 사이즈가 390*1.5 의 값으로 움직여야 한다면, 캐러셀 슬라이드가 하나씩 움직이지 않고 중간 연결부분이 보이게된다. 

 

두 번째로 화면 크기를 받아오는 handleResize함수 안의 location.reload().

location.reload()는 화면을 새로고침 시킨다. 캐러셀 좌표가 계속 변화하기 때문에 캐러셀 좌표가 바뀌는 시점에 화면 사이즈를 바꾸면 애매한 좌표에서부터 새로운 값으로 캐러셀 슬라이드가 돌게 되므로 이 또한 캐러셀 슬라이드가 하나씩 움직이지 않고 중간 연결부분이 보이게된다. 이를 방지하기 위해 carouselWidth가 바뀔 때 화면을 새로고침시켜 슬라이드가 처음부터 시작하도록 했다.

 

세 번째로 화면 크기를 받아오는 handleResize함수 안의 carouselWidth !== tabletSize 부분.

현재 화면 크기가 820px이 넘는지 확인(넘으면 carouselWidth 390*1.5의 값으로 바꿔줘야함)하면서 현재 carouselWidth가 390*1.5 인지 확인하는 식이다. 이 부분은 너무 잦은 새로고침을 방지하기 위해 적었다. 화면크기가 조금이라도 바뀌면 바뀔 때 마다 함수가 실행되기때문에 유저가 화면 크기를 바꾸는 동안 1px마다 새로고침이 되게 된다. 이 문제를 해결하기 위해 현재 carouselWidth와 바꿔야할 값이 일치하지 않을때만 값을 바꾸고 새로고침하도록 코드를 짜보았다. 

 

이 세 가지가 이번에 캐러셀 리팩토링하면서 신경쓴 부분들인데 잊기 전에 적어보았다. 

화면 크기가 바뀌어도 캐러셀이 잘 작동하는것을 볼 수 있다.

 

새로고침부분은 팀원분이 새로고침 대신 사용할만한 코드에 대한 설명이 담긴 영상을 공유해주셔서 한 번 보고 다른 방식으로 다시 수정할 수도 있을 것 같다.

 

 


참고자료

https://db2dev.tistory.com/entry/React-resize-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%8B%A4%EB%A3%A8%EA%B8%B0