Gatsby에서 Layout을 다루는 방법

개츠비에서는 모든 페이지에서 공통적으로 나타나는 레이아웃 컴포넌트를 만들고, props.children으로 자식페이지를 연결시켜준다.

Layout 컴포넌트 만들기

모든 페이지에서 반복적으로 보여주는 컴포넌트를 포함해서 index.js 파일을 만들어주면 된다.

여기 블로그의 경우엔, navigation을 포함하고 있는 Header 컴포넌트와 저작권 표시를하는 Footer 컴포넌트를 공통적으로 보여줄것이다. Header와 Footer사이에 자식 페이지들이 계속 변화하는 구조이다.

src/layouts/index.js
import React from 'react';

import Header from './Header';
import Footer from './Footer';

const Layout = ({ children }) => {
  return (
    <>
      <Header />
      <main>{children}</main>
      <Footer />
    </>
  );
};

export default Layout;

고정되는 Header 만들기(Sticky Header)

Header는 항상 상단에 고정되어있도록 한다.
react Hook의 useStatewindow.scrollY를 이용하여 구현할 것이다.

  1. useState를 이용하여 scrolled state(fix 여부)를 boolean으로 관리하고,
  2. window.scrollY의 조건에 맞춰 setScrolled를 이용하여 scrolled 값을 변경하는 함수를 만든다.
  3. 스크롤 이벤트시 함수 동작.
  4. 결정된 boolean 값은 header전체의 class값을 변화시키면서, css(scss)로 Header를 고정시킨다.

주의: Debugging HTML Builds 참고
gatsby에서 빌드할때 "browser globals"를 참조하면 "window is not defined"에러가 난다. 해결방법은 오류가 나는 코드를 찾아 a) 빌드시에 작동이 되지 않게 하거나, b) 렌더함수에 있는 경우는 componentDidMount 혹은 useEffect안에 그 코드를 넣어주어야 한다.

src/layouts/Header.js
import React, { useState } from 'react';

const Header = (props) => {
  // 1. useState로 scrolled 상태 관리.
  // 처음은 scrollY값이 0일테니, false를 기본값으로 한다.
  const [scrolled, setScrolled] = useState(false);

  // 주의: 빌드할 때 window is not defined 오류를 해결하려면 useEffect
  useEffect(() => {

    // 2. 현재 state 값과 scrollY > 30 을 기준으로 state값 변경하는 함수
    const handleScroll = () => {
      if (!scrolled && window.scrollY > 30) {
        setScrolled(true);
      } else if (scrolled && window.scrollY <= 30) {
        setScrolled(false);
      }
    };

    // 3. 스크롤 이벤트시 handleScroll 동작
    window.addEventListener('scroll', handleScroll);
    // useEffect cleanup 함수
    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, [scrolled]);

  return (
    // 4. scrolled의 값에 따라 class를 변경
    <header className={scrolled ? 'fix-container scrolled' : 'fix-container'}>
      <div className="header">
         <nav>
           <ul className="nav-list">
            // nav items
           </ul>
         </nav>
      </div>
    </header>
  );
};

export default Header;
  1. scss 속성은 다음과 같다.

    • position: fixed;로 스크롤시에도 고정한다.
    • 가장 상단에 고정할 것이므로, top: 0;.
    • z-index 스크롤하여 페이지가 올라가면서 페이지가 Header위로 올라가는 것을 방지한다. 값이 클수록 위로 올라온다.
    • class에 scrolled가 추가되면, Header 높이가 변경되고 그림자가 생기도록 하였다. 특히 높이의 경우 transition 효과를 주면서 변화하도록했다.
layout.scss
.fix-container {
  position: fixed;
  top: 0;
  z-index: 10;
  // ...생략

  .header {
    height: 150px;
  }

  &.scrolled {
    box-shadow: // ...그림자 속성;

    .header {
      height: 80px;
      transition: height 0.3s ease;
    }
  }
}

마무리

localhost에서 돌리는 개발환경에서는 문제 없이 작동되다가, 빌드 생황에서는 제대로 되지 않는 경우가 있다. 여기선, window 항목 접근시 window is not defined에러가 났고, useEffect를 사용하여 해결하였다. docs에 정리가 되어있으니 참고...

scroll이벤트를 사용하게되면, 스크롤 픽셀마다 이벤트가 적용되기 때문에 주의해야한다. 이 블로그에선 스크롤마다 큰 이벤트가 적용된 것이 아니고, state 변경수준인데다, 이것도 조건을 주어 이벤트가 자주 발생되는 것이 아니므로 크게 무리가 없다.

만약 스크롤에 따라 api를 요청해야하는 무한 스크롤처럼 서버에 부담을 주는 이벤트가 적용되어있다면, throttling이나 debouncing와 같은 기능으로 이벤트 발생을 줄여주어야 한다.

Reference & Learn More

Hooks API Reference - useState
MDN Window.scrollY
Gatsby Layout Components
Gatsby Debugging HTML Builds

Comments