본문 바로가기
Tutorial/port2023

20. 포트폴리오 사이트 만들기 : React-Site : 마무리 영역

by @webstoryboy 2023. 7. 31.
Tutorial/portfolio

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

by @webs 2023. 08. 01.
09
포트폴리오 사이트 만들기 : 마무리
난이도 중간

소개

안녕하세요! 웹스토리보이입니다. 드뎌 마무리 단계에 왔네요! 조금 할 것이 많지만 잘 따라해주세요! 🤣

인덱스

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에 배포하기

10. 마무리

10.1 데이터 통합하기

각 컴퍼넌트에 있는 데이터를 한곳에서 볼 수 있도록 통합하겠습니다. 모든 데이터는 constants 폴더 index.js 파일에 넣었습니다.

import port01 from "../assets/img/port01.jpg";
import port02 from "../assets/img/port02.jpg";
import port03 from "../assets/img/port03.jpg";
import port04 from "../assets/img/port04.jpg";
import port05 from "../assets/img/port05.jpg";
import port06 from "../assets/img/port06.jpg";
import port07 from "../assets/img/port07.jpg";
import port08 from "../assets/img/port08.jpg";
import port09 from "../assets/img/port09.jpg";
import port10 from "../assets/img/port10.jpg";

export const headerNav = [
    {
        title: "intro",
        url: "#intro"
    },
    {
        title: "skill",
        url: "#skill"
    },
    {
        title: "site",
        url: "#site"
    },
    {
        title: "portfolio",
        url: "#port"
    },
    {
        title: "contact",
        url: "#contact"
    }
];

export const introText = {
    title: "port developer",
    desc: ["talent is", "found at the end of the", "effort"]
}

export const skillText = [
    {
        title: "꿈을 설계하고 디자인하다.",
        desc: "나는 공간을 만드는 것을 좋아한다. 어려을 때부터 나만의 공간을 만드는 것을 좋아했고 나만의 다락방을 좋아했다. 단 한 사람이라도 내가 만든 공간 속에서 영감을 받거나 마음이 움직였으면 좋겠다. 나만의 공간을 마음것 만들 수 있다는 건 코딩에 엄청난 매력인거 같다. 그 한구석에 나만의 꿈을 설계하고, 개발을 하며 앞으로도 살고 싶다."
    },
    {
        title: "열심히 할수록 기회는 따른다.",
        desc: "운이 좋은 사람은 없다. 단지 운을 만들 뿐이다. 운을 만들기 위해서는 내가 좋아하는 일이나 내가 하고 싶은 일에 몰두하면 된다. 몰두 하다보면 길이 보이고 방향이 보이게 된다. 운이란 고된 노동과 노력을 통해 스스로 만들어 내는 것이다."
    },
    {
        title: "나에게 정직하다.",
        desc: "정직은 다른 사람보다 나에게 큰 의미를 부여해야 한다. 자신이 정직하지 않으면 진정으로 원하는 일을 열정적으로 밀고 나갈 수 없다. 마음에서 우러나오는 일을 해야 정직해지며 삶을 더 즐길 줄 알게 된다."
    }
]

export const siteText = [
    {
        text: ["make", "site compliant with", "webstandard"],
        title: "비트를 이용한 사이트 제작",
        code: "https://github.com/webstoryboy/port2023-vite",
        view: "https://port2023-vite.netlify.app",
        info: [
            "site coding",
            "production period : two days",
            "use stack : HTML5/CSS3, CSS Variable, Vite",
        ],
    },
    {
        text: ["make", "site compliant with", "react.js"],
        title: "리액트를 이용한 사이트 제작",
        code: "https://github.com/webstoryboy/port2023-react",
        view: "https://port2023-react.netlify.app",
        info: [
            "site coding",
            "production period : two days",
            "use stack : HTML5/CSS3, CSS Variable, react",
        ],
    },
    {
        text: ["make", "site compliant with", "vue.js"],
        title: "뷰를 이용한 사이트 제작",
        code: "https://github.com/webstoryboy/port2023-vue",
        view: "https://port2023-vue.netlify.app",
        info: [
            "site coding",
            "production period : two days",
            "use stack : HTML5/CSS3, Scss Variable, vue",
        ],
    },
    {
        text: ["make", "site compliant with", "next.js"],
        title: "넥스트를 이용한 사이트 제작",
        code: "https://github.com/webstoryboy/port2023-next",
        view: "https://port2023-next.netlify.app",
        info: [
            "site coding",
            "production period : two days",
            "use stack : HTML5/CSS3, Scss Variable, next.js",
        ],
    },
];

export const portText = [
    {
        num: "01",
        title: "어워드에도 올라간 포트폴리오",
        desc: "라마 디자인을 통해 자신의 스킬을 가장 멋지게 표현한 포트폴리오입니다. 가로 모드와 세로 모드는 매우 인상적이며 특히 리액트와 비트를 이용하여 제작한 것은 더욱 놀랍습니다. 이 사이트는 awwwards.com에도 인정받아 올라간 포트폴리오입니다. 확실히 그의 뛰어난 디자인 스킬과 기술력이 빛을 발휘한 결과물인 것 같습니다.",
        img: port01,
        code: "https://github.com/kimsangjunv1/-React-Portfolio",
        view: "https://portfoliosj-react.netlify.app",
        name: "김상* 포트폴리오",
    },
    {
        num: "02",
        title: "빛나는 밤에 포트폴리오",
        desc: "이 사이트는 정말 인상적인 포트폴리오입니다. 특히 스무스한 효과와 가로 모드드 높은 퀄리티를 자랑합니다. 디테일과 꼼꼼함이 넘치는 포트폴리오는 개발자의 뛰어난 능력을 엿볼 수 있습니다. GSAP와 React.js를 이용하여 사이트를 표현한 것은 기술적인 능력과 창의성을 강조하는데, 부족함이 없으며, 세심한 코딩과 디테일한 작업으로 그의 개발 감각과 능력이 빛을 발휘한 것 같습니다. ",
        img: port02,
        code: "https://github.com/seolhee313/PORTFOLIO-REACT",
        view: "https://portfolio-313.web.app/",
        name: "천설* 포트폴리오",
    },
    {
        num: "03",
        title: "열정이 넘치는 포트폴리오",
        desc: "이 사이트는 정말 인상적인 포트폴리오입니다. 특히 스무스한 효과와 가로 모드드 높은 퀄리티를 자랑합니다. 디테일과 꼼꼼함이 넘치는 포트폴리오는 개발자의 뛰어난 능력을 엿볼 수 있습니다. GSAP와 React.js를 이용하여 사이트를 표현한 것은 기술적인 능력과 창의성을 강조하는데, 부족함이 없으며, 세심한 코딩과 디테일한 작업으로 그의 개발 감각과 능력이 빛을 발휘한 것 같습니다. ",
        img: port03,
        code: "https://github.com/seolhee313/PORTFOLIO-REACT",
        view: "https://portfolio-313.web.app/",
        name: "천설* 포트폴리오",
    },
    {
        num: "04",
        title: "모던한 포트폴리오",
        desc: "블랙 컨셉과 애니메이션이 돋보이는 포트폴리오 사이트입니다. GSAP를 통한 애니메이션과 NEXT.js를 통해 제작된 포트폴리오입니다. pin 애니메이션을 통한 포폴 작업물의 표현 능력이 돋보이는 사이트입니다.",
        img: port04,
        code: "https://github.com/dlgnsrb227/portfolio-next",
        view: "https://hoongportfolio-next.netlify.app/",
        name: "이훈* 포트폴리오",
    },
    {
        num: "05",
        title: "가로모드의 정석 포트폴리오",
        desc: "이 포트폴리오는 가로모드를 통해 눈에 띄는 애니메이션 효과를 가진 멋진 작품들이 펼쳐집니다. 세션 간의 부드러운 전환과 흥미로운 움직임이 사용자들에게 색다른 경험을 선사합니다. 사이트에는 탁월한 디자인과 창의적인 애니메이션들이 어우러져, 사용자들에게 인상적인 시각적인 효과를 줍니다. 애니메이션은 적절히 사용되어 사이트를 더욱 생동감 있게 만들어주는 포트폴리오입니다.",
        img: port05,
        code: "/",
        view: "https://junseungpark.github.io/portfolio/index3.html",
        name: "박준* 포트폴리오",
    },
    {
        num: "06",
        title: "화려함의 정석 포트폴리오",
        desc: "화려하고 세련된 디자인과 다채로운 색상이 사용된 포트폴리오는 사용자를 홀릴 듯한 시각적인 매력을 지니고 있습니다. 포트폴리오 내의 각 작품들은 디테일한 디자인과 탁월한 시각적 표현력을 갖추고 있어, 주인공의 뛰어난 예술적 감각을 느낄 수 있습니다. 화려한 애니메이션 효과와 부드러운 전환은 사이트를 더욱 생동감 있게 만들어주며, 사용자들에게 색다른 경험을 선사합니다.",
        img: port06,
        code: "/",
        view: "https://webstoryboy.github.io/port2023/portfolio-student/DavidYang/index.html",
        name: "포트폴리오",
    },
    {
        num: "07",
        title: "패럴랙스 정석 포트폴리오",
        desc: "마치 예술작품을 감상하는 듯한 환상적인 경험을 선사하는 포트폴리오입니다. 패럴랙스 스크롤링을 활용하여 구성된 사이트는 사용자들에게 독특하고 멋진 시각적 효과를 제공합니다. 배경과 움직이는 요소들이 조화롭게 어우러져, 사이트 전반에 걸쳐 깊이와 입체감을 느낄 수 있습니다. 스크롤에 따라 움직이는 요소들은 마치 세계를 탐험하는 듯한 느낌을 주며, 사용자들을 끌어들이는 매력적인 요소로 작용합니다.",
        img: port07,
        code: "/",
        view: "https://webstoryboy.github.io/port2023/portfolio-student/TaeyongLee/index.html",
        name: "포트폴리오",
    },
    {
        num: "08",
        title: "트랜지션 포트폴리오",
        desc: "화면 전환과 요소들의 흐름이 순조롭고 매끄러운 작품들로 가득한 포트폴리오 사이트입니다. 페이지 간의 트랜지션은 마치 이야기를 풀어내는 듯한 흥미진진한 경험을 선사합니다. 트랜지션 효과의 적절한 활용은 작품들을 보다 동적이고 생동감 있게 만들어줍니다. 각 페이지의 이동이 자연스럽고 사용자들이 원활하게 사이트를 탐색할 수 있도록 배려된 구성은 개발자의 디자인 능력을 잘 보여주는 특징입니다.",
        img: port08,
        code: "/",
        view: "https://webstoryboy.github.io/port2023/portfolio-student/HyunroKim/index.html",
        name: "포트폴리오",
    },
    {
        num: "09",
        title: "스크롤의 정석 포트폴리오",
        desc: "스크롤링을 활용하여 훌륭한 사용자 경험을 선사하는 포트폴리오 사이트입니다. 스크롤을 내리면서 작품들이 순차적으로 나타나고 효과적으로 전환되는 것은 마치 예술적인 이야기를 읽어나가는 듯한 느낌을 주며 사용자를 매료시킵니다. 스크롤의 움직임을 통해 작품들이 서서히 드러나고, 각 페이지 간의 전환은 자연스럽고 부드럽습니다. 이러한 트랜지션과 애니메이션들이 작품들의 내용과 테마를 더욱 강조해줍니다.",
        img: port09,
        code: "/",
        view: "https://webstoryboy.github.io/port2023/portfolio-student/ChoJaeHyung/index.html",
        name: "포트폴리오",
    },
    {
        num: "10",
        title: "모던함의 정석 포트폴리오",
        desc: "현대적이고 세련된 디자인으로 가득한 포트폴리오 사이트입니다. 모던한 느낌과 세심한 디테일이 조화를 이루며, 사용자에게 신선하고 품격 있는 경험을 제공합니다. 사이트의 디자인은 깔끔하고 단정하면서도 특유의 감성과 풍부한 표현력이 느껴집니다. 간결한 레이아웃과 모던한 색상 선택은 주인공의 디자인 감각을 잘 보여주는 특징입니다.",
        img: port10,
        code: "/",
        view: "https://webstoryboy.github.io/port2023/portfolio-student/JooHyeji/index.html",
        name: "포트폴리오",
    },
];

export const contactText = [
    {
        link: "https://open.kakao.com/o/gM7YLzwf",
        title: "KAKAO : webstupids",
    },
    {
        link: "mailto:webstoryboy@naver.com",
        title: "mail : webstoryboy@naver.com",
    },
];

export const footerText = [
    {
        title: "youtube",
        desc: "유튜브에 오시면 더 많은 강의를 볼 수 있습니다.",
        link: "https://www.youtube.com/@Webstoryboy",
    },
    {
        title: "github",
        desc: "깃헙에 오시면 더 많은 소스를 볼 수 있습니다.",
        link: "https://github.com/webstoryboy",
    },
    {
        title: "blog",
        desc: "블러그에 오시면 더 많은 정보를 볼 수 있습니다.",
        link: "https://webstoryboy.co.kr",
    },
    {
        title: "gsap",
        desc: "GSAP에 오시면 더 많은 강의를 볼 수 있습니다.",
        link: "https://www.youtube.com/playlist?list=PL4UVBBIc6giL8-6jvrClimg0cFL-Muqiq",
    },
    {
        title: "react",
        desc: "리액트로 만든 사이트를 같이 만들어 봅니다.",
        link: "https://github.com/webstoryboy/port2023-react",
    },
    {
        title: "vue",
        desc: "뷰로 만든 사이트를 같이 만들어 봅니다.",
        link: "https://github.com/webstoryboy/port2023-vue",
    },
    {
        title: "next",
        desc: "넥스트로 만든 사이트를 같이 만들어 봅니다.",
        link: "https://github.com/webstoryboy/port2023-next",
    },
];

데이터가 빠진 컴퍼넌트에는 다음과 같이 연동 소스를 넣어주어야 합니다.

import { contactText } from "../constants";
import { footerText } from "../constants";
import { headerNav } from "../constants";
import { introText } from "../constants";
import { portText } from "../constants";
import { siteText } from "../constants";
import { skillText } from "../constants";

                    

10.2 스무스 효과 넣어주기

스무스 효과는 전체 소스에 넣어줘야 하기 때문에 App.js에 넣어주겠습니다. 링크 효과도 전체에 들어가야 하기 때문에 같이 작업해주겠습니다. 이때 useEffect를 사용하게 됩니다.

import React, { useEffect } from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";

import HomeView from "./views/HomeView";
import lenis from "./utils/lenis";
import link from "./utils/link";

const App = () => {
    useEffect(() => {
        lenis();
        link();
    }, []);

    return (
    <BrowserRouter>
        <Routes>
            <Route path="/" element={<HomeView />} />
        </Routes>
    </BrowserRouter>
    );
};

export default App;

useEffect() 함수는 컴퍼넌트가 렌더링 될 때마다 특정 작업(Sied effect)을 실행할 수 있도록 하는 리액트 훅(Hook)입니다. useEffect를 사용하여 특정 작업을 처리하면, 컴포넌트의 생명주기에 따라 특정 작업을 수행할 수 있습니다. useEffect는 컴포넌트가 렌더링되고 난 이후에 실행되며, 매 렌더링 시마다 실행됩니다. 그러나 useEffect의 동작을 조절하기 위해 의존성 배열(dependency array)을 사용할 수 있습니다.

리액트 훅(Hook)은 함수형 컴포넌트에서 상태(state)와 다양한 기능을 추가적으로 사용할 수 있도록 도와주는 기능입니다. 리액트 훅은 React 버전 16.8부터 도입되었으며, 클래스형 컴포넌트를 사용하지 않고도 상태 관리와 다른 리액트 기능들을 사용할 수 있게 해줍니다.

컴포넌트의 생명주기(Lifecycle)는 컴포넌트가 생성되고, 업데이트되고, 소멸되는 과정을 의미합니다. 리액트 컴포넌트는 특정 시점에 호출되는 생명주기 메서드를 가지고 있으며, 이를 활용하여 컴포넌트의 동작을 제어하고 원하는 작업을 수행할 수 있습니다.

lenis.js 파일에 작업을 하겠습니다.

import Lenis from "@studio-freight/lenis";

const lenis = () => {
    const lenis = new Lenis({
        duration: 1,
        easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
    });

    function raf(time) {
        lenis.raf(time);
        requestAnimationFrame(raf);
    }

    requestAnimationFrame(raf);

    lenis.on("scroll", (e) => {
        console.log(e);
    });
};

export default lenis;

link.js 파일에 작업을 하겠습니다.

const link = () => {
    document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
        anchor.addEventListener("click", function (e) {
            e.preventDefault();

            const targetId = this.getAttribute("href");
            const targetElement = document.querySelector(targetId);

            if (targetElement) {
                targetElement.scrollIntoView({ behavior: "smooth" });
            }
        });
    });
};

export default link;
  • document.querySelectorAll('a[href^="#']'): querySelectorAll 메서드를 사용하여 웹 페이지 내에서 href 속성 값이 #으로 시작하는 모든 앵커 요소들을 선택합니다. 즉, 페이지 내에서 아이디(id)가 있는 앵커들을 선택합니다.
  • forEach((anchor) => { ... }): forEach 메서드를 사용하여 선택된 모든 앵커 요소들에 대해 반복문을 실행합니다.
  • anchor.addEventListener("click", function (e) { ... }): 각 앵커 요소에 클릭 이벤트를 추가합니다. 클릭 이벤트가 발생하면, 해당 함수가 실행됩니다.
  • e.preventDefault(): 클릭 이벤트의 기본 동작을 취소합니다. 기본적으로 앵커를 클릭하면 해당 앵커의 위치로 바로 이동하게 되는데, preventDefault()를 사용하여 이 동작을 막습니다.
  • const targetId = this.getAttribute("href"): 클릭된 앵커의 href 속성 값을 가져와서 targetId 변수에 저장합니다. 이 값은 스크롤하고자 하는 대상 앵커의 아이디를 나타냅니다.
  • const targetElement = document.querySelector(targetId): targetId로 지정된 아이디를 가지는 요소를 querySelector 메서드를 사용하여 선택합니다. 이 요소는 페이지 내에서 스크롤하고자 하는 대상 요소를 의미합니다.
  • if (targetElement) { ... }: targetElement가 유효한 요소인지 체크합니다. 유효한 경우에만 아래 코드를 실행합니다.
  • targetElement.scrollIntoView({ behavior: "smooth" }): targetElement가 가리키는 위치로 페이지를 스크롤합니다. behavior: "smooth" 옵션을 추가하여 부드러운 스크롤을 적용합니다. 이렇게 하면 클릭된 앵커의 위치로 자연스럽게 이동할 수 있습니다.

10.3 가로모드 구현하기

포트폴리오를 보여주는 가로모드를 구현하겠습니다. 보기에는 가롤로 움직이는 것처럼 보이지만, 사실 해당 섹션에 정지되어 왼쪽으로 움직이는 효과일 뿐입니다. 어째든 바로 시작해보겠습니다. 이 효과는 해당 컴퍼넌트에 가서 작업하겠습니다. 이 사이트에서 가장 어려운 부분이 아닐가 생각이 드는데 하나식 해봅시다.

import React, { useEffect, useRef } from "react";

import { portText } from "../constants";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

const Port = () => {
    const horizontalRef = useRef(null);
    const sectionsRef = useRef([]);

    useEffect(() => {
        gsap.registerPlugin(ScrollTrigger);
    
        const horizontal = horizontalRef.current;
        const sections = sectionsRef.current;
    
        let scrollTween = gsap.to(sections, {
            xPercent: -120 * (sections.length - 1),
            ease: "none",
            scrollTrigger: {
            trigger: horizontal,
            start: "top 56px",
            end: () => "+=" + horizontal.offsetWidth,
            pin: true,
            scrub: 1,
            invalidateOnRefresh: true,
            anticipatePin: 1,
            },
        });
    
        return () => {
            scrollTween.kill();
        };
    }, []);

    return (
        <section id="port" ref={horizontalRef}>
            <div className="port__inner">
                <div className="port__title">
                    portfolio <em>포폴 작업물</em>
                </div>
                <div className="port__wrap">
                    {portText.map((port, key) => (
                        <article 
                            className={`port__item p${key + 1}`} 
                            key={key} 
                            ref={(el) => (sectionsRef.current[key] = el)
                        }>
                            <span className="num">{port.num}.</span>
                            <a href={port.code} target="_blank" className="img" rel="noreferrer">
                                <img src={port.img} alt={port.name} />
                            </a>
                            <h3 className="title">{port.title}</h3>
                            <p className="desc">{port.desc}</p>
                            <a href={port.view} target="_blank" className="site" rel="noreferrer">사이트 보기</a>
                        </article>
                    ))}
                </div>
            </div>
        </section>
    );
};

export default Port;
  • const horizontalRef = useRef(null);: horizontalRef라는 변수를 생성하고 useRef 훅으로 초기화합니다. 이 변수는 가로 스크롤이 적용될 부모 요소를 참조하는데 사용됩니다.
  • const sectionsRef = useRef([]);: sectionsRef라는 변수를 생성하고 빈 배열로 초기화합니다. 이 배열은 각 포트폴리오 아이템 요소들을 참조하는데 사용됩니다.
  • useEffect: 컴포넌트가 렌더링된 후에 실행되는 부수 효과를 정의하는 코드 블록입니다. useEffect 내부에서는 gsap 라이브러리의 ScrollTrigger를 등록하고, 가로 스크롤 애니메이션을 구성합니다.
  • gsap.registerPlugin(ScrollTrigger);: gsap 라이브러리에서 ScrollTrigger를 사용할 수 있도록 플러그인을 등록합니다.
  • gsap.to(sections, { ... });: gsap의 to 메서드를 사용하여 sections 배열에 있는 요소들에 대한 가로 스크롤 애니메이션을 설정합니다. sections 배열은 sectionsRef를 통해 각 포트폴리오 아이템 요소들을 참조합니다.
  • scrollTrigger: gsap의 ScrollTrigger를 이용하여 스크롤 이벤트를 트리거로 하는 애니메이션을 설정합니다. 해당 설정으로 스크롤 이벤트가 horizontal 요소가 시작되고 끝날 때까지 트리거되며, 스크롤 위치에 따라 가로로 이동하는 애니메이션을 적용합니다.
  • return () => { ... }: 컴포넌트가 언마운트될 때(clean-up) 실행되는 함수를 정의합니다. scrollTween.kill()를 사용하여 스크롤 애니메이션을 제거합니다. 이렇게 하면 컴포넌트가 언마운트될 때 메모리 누수를 방지할 수 있습니다.
  • portText.map((port, key) => ... ): portText 배열을 map 메서드를 사용하여 포트폴리오 아이템들을 렌더링합니다. 각 포트폴리오 아이템은 이미지, 제목, 설명 등의 정보를 가지고 있습니다.
  • ref={(el) => (sectionsRef.current[key] = el)}: 각 포트폴리오 아이템 요소에 대해 ref 속성을 사용하여 sectionsRef에 해당 요소를 저장합니다. 이렇게 하면 gsap의 to 메서드에서 애니메이션을 적용할 때 해당 요소들을 참조할 수 있습니다.

useRef는 리액트의 훅(Hook) 중 하나로, 함수형 컴포넌트에서 DOM 요소에 접근하거나, 특정 값들을 기억하고 관리하는 기능을 제공합니다. useRef를 사용하면 클래스형 컴포넌트에서의 this와 비슷한 역할을 할 수 있습니다.

10.4 netlify에 배포하기

깃헙에 모든 소스를 올리겠습니다.

git add .
git commit -m "🥺 마무리 완료"
git push -u origin main

이제 빌드를 한번해보겠습니다. 지금까지 작업했던 소스는 브라우저가 읽을 수 없습니다. 브라우저가 읽을 수 있도록 컴파일을 해주고 빌드 작업을 해주어야 브라우저에서 볼 수 있습니다. 빌드 과정을 통해 소스는 가벼워지고 효율성이 증가됩니다.

$ npm run build

빌드화 과정을 거치면 소스가 정리된 것을 볼 수 있습니다. CSS파일 JavaScript파일도 여백없이 한줄로 정리된 것을 확인 할 수 있고, 이미지도 이름이 변형되어 정리된 것을 확인할 수 있습니다. 아래와 같은 메세지가 나오면 성공한 것입니다.

webstoryboy@Webstoryboyui-MacBookPro react-site % npm run build

> react-site@0.1.0 build
> react-scripts build
    
Compiled successfully.

File sizes after gzip:

    104.1 kB  build/static/js/main.7e8e3310.js
    3.42 kB   build/static/css/main.9c85898e.css

The project was built assuming it is hosted at /.
You can control this with the homepage field in your package.json.

The build folder is ready to be deployed.
You may serve it with a static server:

    npm install -g serve
    serve -s build

Find out more about deployment here:

    https://cra.link/deployment

마지막으로 다시 한번 업로드 해보겠습니다. 이렇게 올려도 깃헙에는 큰 변화가 없을 것입니다. .gitignore파일에 build 폴더가 포함되어 있기 때문에 파일은 올라가지 않습니다.

git add . 
git commit -m "😇 1차 완성본"
git push -u origin main

이제 netlify 사이트로 이동하겠습니다. 로그인을 하고 add new site 버튼을 누르고 import an existing project를 선택하겠습니다.

netlify

deploy width GitHub 버튼을 클릭하고 port2023-react를 검색 후 선택합니다.

netlify

이번엔 딱히 수정할께 없습니다. 바로 Deploy port2023-react 버튼을 클릭하면 됩니다.

netlify

완료가 되었습니다. 도메인 이름을 저번처럼 변경하시면 됩니다. 기억이 안나면 vite 튜토리얼을 참고해보세요!

netlify

마지막으로 깃헙에 정리하면 되겠습니다. 메모장에도 같이 정리하겠습니다.

netlify

3. 마무리

긴 여정이 드디어 끝났네요! 리액트까지 완성을 하였습니다. 할만 하시죠? 그렇게 어려운 부분까지는 들어가지 않고 기본 흐름만 작업했다고 보시면 됩니다. 리액트라는 사이트는 이런식으로 만들어지는 구나!! 이런 느낌.. 어째든 벌써 반이 끝났습니다. 남은 미션은 지금보다 훨씬 더 빨리 끝날 것입니다. 그럼 지금까지 정말 수고하셨습니다. 조금만 더 힘을 내서 마무리 합시다 🥶😵‍💫😸


예제 목록

댓글