상세 컨텐츠

본문 제목

React로 레이아웃 (헤더, 메뉴바, 푸터) 컴포넌트 만들기

개발

by 호박너구리의 블로그 2021. 5. 10. 18:30

본문

 

대부분의 페이지에는 Header, 메뉴바, Footer 등이 들어갑니다.

하지만 모든 페이지에 일일이 헤더, 메뉴바, 푸터를 작성하기에는 번거롭습니다.

컴포넌트로 만들더라도 매번 세 개의 컴포넌트를 넣는 것은 깔끔하지 않죠.

 

그래서 보통 필요한 컴포넌트를 묶어서 Layout으로 만들어 사용합니다!

오늘은 한 번 React로 Layout 컴포넌트를 만들어보겠습니다.

 

 

 

1. Layout 컴포넌트 생성하기

우선, 원하는 디렉토리에 Layout 컴포넌트를 생성합니다.

저는 components 폴더 안에 Layout이라는 폴더와 파일을 생성했습니다.

(개인적으로 타입스크립트를  선호해서 tsx파일로 만들었는데, jsx를 사용해도 무방합니다)

// components/Layout/Layout.tsx
const Layout = () => {
  return (
    <div>

    </div>
  )
}

export default Layout

 

 

저는 레이아웃을 크게 헤더(메뉴바를 헤더 내부에 생성할 예정입니다), 콘텐츠, 푸터 영역으로 나누었습니다.

한 번 구성한대로 컴포넌트를 만들어 볼까요?

// components/Layout/Header/Header.tsx
const Header = () => {
  return (
    <header>
      <h2>This is Header</h2>
    </header>
  )
}

export default Header

 

// components/Layout/Footer/Footer.tsx
const Footer = () => {
  return (
    <footer>
      <h2>This is Footer</h2>
    </footer>
  )
}

export default Footer

 

 

그리고 만든 헤더와 푸터를 레이아웃 컴포넌트에 적용했습니다.

// components/Layout/Layout.tsx
import Footer from "./Footer/Footer"
import Header from "./Header/Header"

const Layout = () => {
  return (
    <div>
      <Header />

      <Footer />
    </div>
  )
}

export default Layout

 

 

이후 레이아웃 내부에 페이지에 해당하는 컴포넌트를 넣을 수 있도록

property로 컴포넌트를 받도록 반영했습니다.

(자바스크립트의 경우 props안의 필드와 타입 설정은 필요 없겠죠)

// components/Layout/Layout.tsx
import Footer from "./Footer/Footer"
import Header from "./Header/Header"

const Layout = (props: {
  children: React.ReactNode
}) => {
  return (
    <div>
      <Header />
      
      <main>
        {props.children}
      </main>
      
      <Footer />
    </div>
  )
}

export default Layout

 

 

 

2. Layout 컴포넌트 적용하기

헤더와 푸터를 구성하고 스타일링하기 전에,

한 번 만들어둔 Layout 컴포넌트를 적용해보려고 합니다.

 

저는 기존에 만들어둔 페이지 컴포넌트에서 사용했는데요,

기존 pages/index.tsx는 HomeContainer라는 컴포넌트를 사용하여

다음과 같은 화면을 보여주고 있었습니다.

// pages/index.tsx
// NextJS의 폴더구조이며, 다른 React 프로젝트에서 페이지에 해당하는 컴포넌트입니다.
import Layout from "../components/Layout/Layout"
import HomeContainer from "../containers/Home/HomeContainer"

const HomePage = () => {
  return (
    <HomeContainer />
  )
}

export default HomePage

 

 

 

그리고 Layout 컴포넌트를 적용했습니다.

// pages/index.tsx
import Layout from "../components/Layout/Layout"
import HomeContainer from "../containers/Home/HomeContainer"

const HomePage = () => {
  return (
    <Layout>
      <HomeContainer />
    </Layout>
  )
}

export default HomePage

 

그러면 이제 다음과 같이 헤더와 푸터가 적용된 것을 확인할 수 있습니다.

Layout 컴포넌트 내부의 내용이 children으로 props가 되어 넘어간 것이죠!

 

 

 

 

 

3. Layout 컴포넌트 헤더/메뉴바 만들기

이제 Layout 컴포넌트에서 Header를 구체적으로 구성해보겠습니다.

우선 헤더에는 보통 로고와 메뉴가 들어갑니다.

그래서 Header.tsx 파일을 조금 수정했습니다.

// components/Layout/Header/Header.tsx
const Header = () => {
  return (
    <header>
      <div>
        <div>
          로고 자리
        </div>

        <nav>
          <ul>
            <li>
              메뉴 1
            </li>
            <li>
              메뉴 2
            </li>
          </ul>
        </nav>
      </div>
    </header>
  )
}

export default Header

 

 

로고에 해당하는 자리와 메뉴들이 잘 뜨는 것을 확인할 수 있습니다!

이제 헤더에 css를 추가해볼까요?

(예시에 적은 css 방식은 NextJS에서 지원하는 CSSModule방식으로 이전 글에서 확인할 수 있습니다. 물론 자유롭게 스타일링 하셔도 무방합니다!)

/* components/Layout/Header/Header.module.scss */
.header {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 80px;
  background-color: #dde0ea;
}

.contents {
  display: flex;
  width: 96%;
  max-width: 1100px;
  height: 100%;
  margin: 0 auto;
  align-items: center;
  justify-content: space-between;
}

.navigation {
  ul {
    display: flex;
    list-style: none;

    li + li {
      margin-left: 30px;
    }
  }
}

 

// components/Layout/Header/Header.tsx
import styles from './Header.module.scss'

const Header = () => {
  return (
    <header className={styles.header}>
      <div className={styles.contents}>
        <div>
          로고 자리
        </div>

        <nav className={styles.navigation}>
          <ul>
            <li>
              메뉴 1
            </li>
            <li>
              메뉴 2
            </li>
          </ul>
        </nav>
      </div>
    </header>
  )
}

export default Header

 

우선 스크롤이 길어져도 상단에 고정시키기 위해 header class에 position: fixed; 속성을 주었습니다. 그리고 로고부터 메뉴바까지의 길이를 조정하고 좌우로 붙을 수 있도록 flex 속성을 활용했죠. 그리고 네비게이션 메뉴들을 ul, li 태그로 만들어서 기본 스타일(점 표시)을 비활성화 시켰습니다. 그렇게 되면 화면은 아래와 같이 보입니다.

 

 

position: fixed; 속성으로 위쪽의 공간이 가려진 것인데요, 그래서 Layout 컴포넌트의 상단에 헤더와 같은 높이의 패딩을 줘서 해결했습니다. 

/* components/Layout/Layout.module.scss */
.layout {
  padding-top: 80px;
}

 

// components/Layout/Layout.tsx
import Footer from "./Footer/Footer"
import Header from "./Header/Header"
import styles from './Layout.module.scss'

const Layout = (props: {
  children: React.ReactNode
}) => {
  return (
    <div className={styles.layout}>
      <Header />

      <main>
        {props.children}
      </main>
      
      <Footer />
    </div>
  )
}

export default Layout

 

 

이제 원하던대로 헤더가 생겼습니다!

 

 

 

4. Layout 컴포넌트 푸터 만들기

푸터는 화면에 고정시키지 않아도 되기에 헤더에 비해 비교적 간단합니다.

그리고 푸터는 디자인이 매우 다양한 편인데요,

지금은 연습인 만큼 가운데에 회사 이름만 적어두는 디자인으로 만들어보겠습니다.

 

우선 Footer의 컴포넌트와 스타일시트를 수정/추가 했습니다.

// components/Layout/Footer/Footer.tsx
import styles from './Footer.module.scss'

const Footer = () => {
  return (
    <footer className={styles.footer}>
      <div className={styles.contents}>
        <h2 className={styles.title}>
          Do what you love
        </h2>
      </div>
    </footer>
  )
}

export default Footer

 

/* components/Layout/Footer/Footer.modules.scss */
.footer {
  height: 100px;
  margin-top: auto;
  background-color: #dde0ea;
}

.contents {
  width: 96%;
  max-width: 1100px;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
}

.title {
  font-weight: 600;
  font-size: 20px;
}

 

그리고 헤더와 푸터 사이의 메인 페이지 컴포넌트 부분이

높이가 짧아도 푸터가 최하단에 올 수 있도록 main의 스타일을 추가했습니다.

// components/Layout/Layout.tsx
import Footer from "./Footer/Footer"
import Header from "./Header/Header"
import styles from './Layout.module.scss'

const Layout = (props: {
  children: React.ReactNode
}) => {
  return (
    <div className={styles.layout}>
      <Header />

      <main className={styles.main}>
        {props.children}
      </main>
      
      <Footer />
    </div>
  )
}

export default Layout

 

/* components/Layout/Layout.module.scss */
.layout {
  padding-top: 80px;
}

.main {
  min-height: calc(100vh - 180px);
}

 

그렇게 최종적으로 헤더와 푸터가 완성되었습니다!

 

 

이제 어떤 페이지를 만들든, 매번 헤더와 푸터를 만들지 않고도

새로운 페이지를 생성할 수 있게 되었습니다!

앞으로 레이아웃을 활용해서 더 많은 페이지를 한 번 꾸며볼게요!

 

 

728x90

관련글 더보기

댓글 영역