[React] 스크롤에 따라 navagation fixed

HongDuHyeon·2022년 4월 3일
1
post-thumbnail
post-custom-banner
백발백중하는 명사수...

부딪혔다. 프로젝트 중에. fix

죄송합니다. 다시 하겠습니다.

프로젝트를 진행하던 중 화면의 스크롤 값에 따라 고정 너비를 가진 header가 상단에 position:fixed로 붙는 인터랙션을 구현해야 했다. 대충 감은 오는데 jQuery에서나 오던 감으로는 React에서 적용하기에 힘들 것 같아서 또 참지 못하고 먼저 구글링으로 검색을 해봤다.
상대를 알고 나를 알면 백전백ㅅ...

오케이. 일단 지금까지 들여왔던 습관 중 하나를 꺼냈다.

해야하는 일 리스트로 적어보기

  1. 스크롤 값 가져오기
  2. 원하는 스크롤 값에 나타날 css 만들어 놓기

크게 2가지로 생각을 하고 실행에 옮겼다.

1. 스크롤 값 얻어와서 저장하기

const [scrollY, setScrollY] = useState(0);

const scrollFixed = () => {
  if (scrollY > 100) {
    setScrollY(window.pageYOffset);
    setScrollActive(true);
  } else {
    setScrollY(window.pageYOffset);
    setScrollActive(false);
  }
};

먼저 스크롤을 useState를 사용해서 값을 저장해야 한다고 생각해서 state를 하나 만들고
window.pageYOffset을 사용해서 스크롤 Y의 값을 가져왔다. console.log()에 찍어본 결과 당연히 최상단에 있는 값이니 0이 나왔고 그걸 setScrollY를 사용해서 스크롤이 될때마다 scrollY를 바꿔줘야한다. 그럼 일단 scroll 값이 저장 되는 걸 확인 했으니 다음으로 넘어가보자.

2. 스크롤 감시(?) 하기

이제서야 좀 반가운 단어가 나왔다.

addEventListener

const [navActive, setNavActive] = useState(false);
const [scrollY, setScrollY] = useState(0);
const [scrollActive, setScrollActive] = useState(false);

 const scrollFixed = () => {
    if (scrollY > 100) {
      setScrollY(window.pageYOffset);
      setScrollActive(true);
    } else {
      setScrollY(window.pageYOffset);
      setScrollActive(false);
    }
  };

  useEffect(() => {
    const scrollListener = () => {
      window.addEventListener('scroll', scrollFixed);
    };
    scrollListener();
    return () => {
      window.removeEventListener('scroll', scrollFixed);
    };
  });

useEffect를 사용해서 안에 윈도우에 스크롤을 감시 ? 계속 확인해주는 로직을 작성해준다.
하지만 useEffect의 특성상 이 구문을 끝내는 로직을 작성해주지 않으면 그야말로 난장판이 되어버린다.

다시 한번 정리해보자면 scrollFixed라는 구문에 if문 true값으로 본인이 원하는 윈도우의 높이값 이후에 본인이 작성 클래스를 setState로 true 값으로 전달할지 false값으로 전달하지 상황에 맞게 전달한다.

true / false값 전달하기

const Nav = () => {
  const [navActive, setNavActive] = useState(false);
  const [scrollY, setScrollY] = useState(0);
  const [scrollActive, setScrollActive] = useState(false);

  const scrollFixed = () => {
    if (scrollY > 100) {
      setScrollY(window.pageYOffset);
      setScrollActive(true);
    } else {
      setScrollY(window.pageYOffset);
      setScrollActive(false);
    }
  };

  useEffect(() => {
    const scrollListener = () => {
      window.addEventListener('scroll', scrollFixed);
    };
    scrollListener();
    return () => {
      window.removeEventListener('scroll', scrollFixed);
    };
  });

  return (
    <div className={`nav ${scrollActive ? 'fixed' : ''}`}>
      <ul className="navUtil inner">
        <li>로그인</li>
        <li>회원가입</li>
        <li>
          <span className="instagram">인스타</span>
          <span className="facebook">페이스북</span>
        </li>
      </ul>
    </div>

최상위 DOM에 접근해서 기존에 갖고 있던 nav라는 클래스에 fixed가 들어오면 상단에 고정이 되고 else값으로 false값이 도출된다면 원래 기존에 갖고 있던 스타일이 나오게 된다.

여기서 살짝 짚고 넘어가자면 기능구에 급급해서 충분히 하지 않을 수 있었던 코드의 가독성을 해치는 실수를 했다.

<div className={`nav ${scrollActive ? 'fixed' : ''}`}>

이 부분인데 지금은 Templete Literal로 작성이 되어 있지만 원래는 이렇게 썼다.

<div className={'nav + (scrollActive ? ' fixed' : '')}'}>

이렇게 Templete Literal로 작성해도 충분했던 구문을 놓쳤던게 좀 아쉬웠다.

후기

스크롤 이벤트가 될때마다 잘 적용이 되는 화면을 보면 뿌듯하다. 하지만 만들기전까지의 과정을 생각하면 내가 왜 이렇게 안썼지? 아 이런 방법도 있구나 라는 포인트를 많이 알게 되는 것 같다. 그런 리팩토링의 과정을 최소화해서 속도를 내고 싶은 욕심은 있지만 너무 급하게 생각하면 오늘 나온 Templete Literal처럼 하지 않아도 될 실수를 또 반복하고 있을 수도 있으니 천천히 확실하게 작업하는 방식을 좀 더 연습해봐야겠다. 끝 !

profile
마음이 시키는 프론트엔드.. RN과 IOS를 곁들인..
post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 4월 3일

너무 유용합니다!!!!

1개의 답글