본문 바로가기
Tutorial/port2023

33. 포트폴리오 사이트 만들기 : Next-Site : 헤더 영역

by @webstoryboy 2023. 8. 1.
Tutorial/portfolio

포트폴리오 사이트 만들기 - Next

by @webs 2023. 08. 01.
03
포트폴리오 사이트 만들기 : 헤더 영역
난이도 중간

소개

안녕하세요! 웹스토리보이입니다. next.js를 작업하기 전에 기본 셋팅을 완성하겠습니다. 기존 프레이워크랑 차이점이 무엇인지 알아보고, 좋은 점이 무엇이 있는지 알아보겠습니다. 그럼 바로 시작해보겠습니다.

포트폴리오 사이트 만들기

VITE SITE

  • 1. 셋팅하기
    • 1_1. vite 설치하기
    • 1_2. vite 폴더 정리하기
    • 1_3. gsap/lenis 설치하기
    • 1_4. git 연동하기
  • 2. 레이아웃
    • 2.1 레이아웃 구조 만들기
    • 2.2 메인 레이아웃 구조 만들기
    • 2.3 CSS 셋팅하기
    • 2.4 JavaScript 셋팅하기
  • 3. 헤더 영역
    • 3.1 헤더 구조 잡기
    • 3.2 헤더 디자인 설정
    • 3.3 반응형 작업하기
    • 3.4 메뉴 자바스크립트 설정
  • 4. 인트로 영역
    • 4.1 인트로 구조 잡기
    • 4.2 인트로 디자인 설정
    • 4.3 반응형 작업하기
  • 5. 스킬 영역
    • 5.1 스킬 구조 잡기
    • 5.2 스킬 디자인 설정
    • 5.3 반응형 작업하기
  • 6. 사이트 영역
    • 6.1 사이트 구조 잡기
    • 6.2 사이트 디자인 설정
    • 6.3 반응형 작업하기
  • 7. 포트폴리오 영역
    • 7.1 사이트 구조 잡기
    • 7.2 사이트 디자인 설정
    • 7.3 반응형 작업하기
    • 7.4 스크립트 작업하기
  • 8. 연락처 영역
    • 8.1 연락처 구조 잡기
    • 8.2 연락처 디자인 설정
    • 8.3 반응형 작업하기
  • 9. 푸터 영역
    • 9.1 푸터 구조 잡기
    • 9.2 푸터 디자인 설정
    • 9.3 반응형 작업하기
  • 10. 마무리
    • 10.1 스무스 효과주기
    • 10.2 링크 연결하기
    • 10.3 netlify에 배포하기

REACT SITE

  • 1. 셋팅하기
    • 1_1. React 설치하기
    • 1_2. React 폴더 정리하기
    • 1_3. 라이브러리 설치하기
    • 1_4. git 연동하기
  • 2. 라우팅 및 컴퍼넌트
    • 2_1. 라우팅 설정하기
    • 2_2. 컴퍼넌트 설정하기
    • 2_3. SCSS 설정하기
  • 3. 헤더 영역
    • 3_1. 헤더 구조잡기
    • 3_2. 헤더 디자인 설정
    • 3_3. 헤더 데이터 작업
    • 3_4. 헤더 토글 메뉴 작업하기
  • 4. 인트로 영역
    • 4_1. 인트로 구조잡기
    • 4_2. 인트로 디자인 설정
    • 4_3. 인트로 데이터 작업
  • 5. 스킬 영역
    • 5_1. 스킬 구조잡기
    • 5_2. 스킬 디자인 설정
    • 5_3. 스킬 데이터 작업
  • 6. 사이트 영역
    • 6_1. 사이트 구조잡기
    • 6_2. 사이트 디자인 설정
    • 6_3. 사이트 데이터 작업
  • 7. 포트폴리오 영역
    • 7_1. 포트폴리오 구조잡기
    • 7_2. 포트폴리오 디자인 설정
    • 7_3. 포트폴리오 데이터 작업
  • 8. 연락처 영역
    • 8_1. 연락처 구조잡기
    • 8_2. 연락처 디자인 설정
    • 8_3. 연락처 데이터 작업
  • 9. 푸터 영역
    • 9_1. 푸터 구조잡기
    • 9_2. 푸터 디자인 설정
    • 9_3. 푸터 데이터 작업
  • 10. 마무리
    • 10_1. 데이터 통합하기
    • 10_2. 스무스 효과 넣어주기
    • 10_3. 가로모드 구현하기
    • 10_4. netlify에 배포하기

VUE SITE

  • 1. 셋팅하기
    • 1_1. Vue 설치하기
    • 1_2. Vue 폴더 정리하기
    • 1_3. 라이브러리 설치하기
    • 1_4. git 연동하기
  • 2. 라우팅 및 컴퍼넌트
    • 2_1. 라우팅 설정하기
    • 2_2. 컴퍼넌트 설정하기
    • 2_3. SCSS 설정하기
  • 3. 헤더 영역
    • 3_1. 헤더 구조잡기
    • 3_2. 헤더 디자인 설정
    • 3_3. 헤더 데이터 작업
    • 3_4. 헤더 토글 메뉴 작업하기
  • 4. 인트로 영역
    • 4_1. 인트로 구조잡기
    • 4_2. 인트로 디자인 설정
    • 4_3. 인트로 데이터 작업
  • 5. 스킬 영역
    • 5_1. 스킬 구조잡기
    • 5_2. 스킬 디자인 설정
    • 5_3. 스킬 데이터 작업
  • 6. 사이트 영역
    • 6_1. 사이트 구조잡기
    • 6_2. 사이트 디자인 설정
    • 6_3. 사이트 데이터 작업
  • 7. 포트폴리오 영역
    • 7_1. 포트폴리오 구조잡기
    • 7_2. 포트폴리오 디자인 설정
    • 7_3. 포트폴리오 데이터 작업
  • 8. 연락처 영역
    • 8_1. 연락처 구조잡기
    • 8_2. 연락처 디자인 설정
    • 8_3. 연락처 데이터 작업
  • 9. 푸터 영역
    • 9_1. 푸터 구조잡기
    • 9_2. 푸터 디자인 설정
    • 9_3. 푸터 데이터 작업
  • 10. 마무리
    • 10_1. 스무스 효과 넣어주기
    • 10_2. 가로모드 구현하기
    • 10_3. 링크 클릭 구현하기
    • 10_4. netlify에 배포하기

NEXT SITE

  • 1. 셋팅하기
    • 1_1. Next 설치하기
    • 1_2. Next 폴더 정리하기
    • 1_3. 라이브러리 설치하기
    • 1_4. git 연동하기
  • 2. 라우팅 및 컴퍼넌트
    • 2_1. 라우팅 설정하기
    • 2_2. 컴퍼넌트 설정하기
    • 2_3. SCSS 설정하기
    • 2_4. Script 설정하기
  • 3. 헤더 영역
    • 3_1. 헤더 구조잡기
    • 3_2. 헤더 디자인 설정
    • 3_3. 헤더 데이터 작업
    • 3_4. 헤더 토글 메뉴 작업하기

3. 헤더 영역

3.1 헤더 구조잡기

vite에서 작업했던 소스를 토대로 리액트화 하겠습니다. <a href="#">은 <a href="/">로 변경해야 경고가 없어집니다. classclassName으로 변경하고, tabindextabIndex으로 변경합니다. 에러 또는 경고로 틀린 부분을 알려주니, 모르는게 있으면 검색을 통해서 확인해도 됩니다.

import React from "react";

const Header = () => {
    return (
        <header id="header" role="banner">
            <div className="header__inner">
                <div className="header__logo">
                    <a href="/">portfolio<em>vite</em></a>
                </div>
                <nav className="header__nav" role="navigation" aria-label="메인 메뉴">
                    <ul>
                        <li><a href="#intro">intro</a></li>
                        <li><a href="#skill">skill</a></li>
                        <li><a href="#site">site</a></li>
                        <li><a href="#port">portfolio</a></li>
                        <li><a href="#contact">contact</a></li>
                    </ul>
                </nav>
                <div className="header__nav__mobile" id="headerToggle" aria-controls="primary-menu" aria-expanded="false" role="button" tabIndex="0">
                    <span></span>
                </div>
            </div>
        </header>
    )
}

export default Header;

3.2 헤더 디자인 설정

SCSS 파일도 기존에 리액트에서 했던 파일을 그대로 가져오겠습니다.

#header {
    @include position-fixed;
    z-index: 10000;
}

.header__inner {
    @include flex-between;
    padding: 1rem;
    background: rgba(116, 99, 99, 0.1);
    backdrop-filter: blur(15px);

    .header__logo {
        font-size: 1.625rem;
        text-align: center;
        text-transform: uppercase;
        line-height: 1;

        em {
            display: block;
            font-size: 10px;
            color: var(--black200);
        }
    }

    .header__nav {
        li {
            display: inline-block;

            a {
                text-transform: uppercase;
                font-size: 14px;
                padding: 14px;
                position: relative;

                &::before {
                    content: "";
                    width: calc(100% - 28px);
                    height: 1px;
                    background-color: var(--black);
                    position: absolute;
                    left: 14px;
                    bottom: 10px;
                    transform: scaleX(0);
                    transition: all 0.3s;
                }

                &:hover::before {
                    transform: scaleX(1);
                }
            }
        }
        @media (max-width: 800px) {
            display: none;

            &.show {
                display: block;

                ul {
                    display: block;
                    position: absolute;
                    right: 0;
                    top: 68px;
                    background: rgba(116, 99, 99, 0.1);
                    backdrop-filter: blur(15px);
                    z-index: 10000;

                    li {
                        display: block;
                        text-align: right;
                        a {
                            display: inline-block;
                            padding: 10px;
                        }
                    }
                }
            }
            &.show + .header__nav__mobile span::before {
                width: 20px;
            }
            &.show + .header__nav__mobile span::after {
                width: 20px;
            }
        }
    }

    .header__nav__mobile {
        width: 40px;
        height: 40px;
        @include flex-center;
        cursor: pointer;
        display: none;

        span {
            display: block;
            width: 40px;
            height: 2px;
            background-color: var(--black);
            position: relative;
            margin-top: 19px;

            &::before {
                content: "";
                width: 40px;
                height: 2px;
                background-color: var(--black);
                position: absolute;
                right: 0;
                top: 6px;
                transition: width 0.3s;
            }

            &::after {
                content: "";
                width: 40px;
                height: 2px;
                background-color: var(--black);
                position: absolute;
                left: 0;
                bottom: 6px;
                transition: width 0.3s;
            }
        }

        @media (max-width: 800px) {
            display: block;
        }
    }
}

3.3 헤더 데이터 작업

데이터를 가져와서 데이터를 뿌려주겠습니다. 리액트와 동일하게 map() 메서드를 사용하였습니다.

import React from "react";

import { headerNav } from "../constants";

const Header = () => {
  return (
    <header id="header" role="heading">
        <div className="header__inner">
            <div className="header__logo">
                <a href="/">portfolio<em>next.js</em></a>
            </div>
            <nav className="header__nav" role="navigation" aria-label="메인 메뉴">
                <ul>
                    {headerNav.map((nav, key) => (
                        <li key={key}>
                            <a href={nav.url}>{nav.title}</a>
                        </li>
                    ))}
                </ul>
            </nav>
            <div className="header__nav__mobile" id="headerToggle" aria-controls="primary-menu" aria-expanded="false" role="button" tabIndex="0">
                <span></span>
            </div>
        </div>
    </header>
  )
}

export default Header

3.4 헤더 토글 메뉴 작업하기

useState를 선언하겠습니다.

import React, { useState } from "react";

import { headerNav } from "../constants";

const Header = () => {
    const [show, setShow] = useState(false);

    const toggleMenu = () => {
        setShow((prevShow) => !prevShow);
    };

    return (
        <header id="header" role="heading">
            <div className="header__inner">
                <div className="header__logo">
                    <a href="/">portfolio<em>next.js</em></a>
                </div>
                <nav className={`header__nav ${show ? "show" : ""}`} role="navigation" aria-label="메인 메뉴">
                    <ul>
                        {headerNav.map((nav) => (
                            <li key={nav.id}>
                                <a href={nav.url}>{nav.title}</a>
                            </li>
                        ))}
                    </ul>
                </nav>
                <div 
                    className="header__nav__mobile" 
                    id="headerToggle" 
                    aria-controls="primary-menu" 
                    aria-expanded={show ? "true" : "false"}
                    role="button" 
                    tabIndex="0"
                    onClick={toggleMenu}
                >
                    <span></span>
                </div>
            </div>
        </header>
    )
}

export default Header

useState는 React 라이브러리에서 제공하는 함수 중 하나로, 함수형 컴포넌트에서 상태(state)를 관리할 수 있게 도와주는 훅(hook)입니다. useState를 사용하면 클래스형 컴포넌트에서 사용되는 this.state와 비슷한 역할을 하는 상태 변수를 함수형 컴포넌트에서 사용할 수 있습니다.

[show, setShow] 배열의 첫 번째 요소는 상태 변수(count와 같은)이며, 두 번째 요소는 해당 상태 변수를 변경하는 함수(setCount와 같은)입니다. 상태 변수는 상태의 현재 값이며, 변경 함수를 호출하면 해당 상태 변수의 값을 업데이트하고 컴포넌트를 다시 렌더링합니다.

여기서 주의할 점은 useState를 사용할 때, 상태는 불변성을 유지해야 한다는 것입니다. 상태를 직접 수정하면 안 되며, 대신 useState가 반환하는 상태 변경 함수를 사용하여 상태를 변경해야 합니다. 이를 통해 함수형 컴포넌트에서도 상태를 쉽게 관리하고, 상태 변경에 따라 렌더링을 자동으로 처리할 수 있습니다.

3. 마무리

리액트와 동일하게 토글 메뉴와 데이터 작업을 끝냈습니다. 리액트와 다를게 없어서 크게 어렵진 않았을 것입니다. 그럼 복습한다는 의미로 생각하면 더 수업이 쉽지 않을까? 생각이 드네요! 오늘도 수고하셨습니다. 🫨


예제 목록

댓글