3주차(월) - 프리 온보딩 코스 프론트엔드 - 기업 과제 회고

minbr0ther·2022년 2월 16일
0

pre-onboarding-fe

목록 보기
11/15
post-thumbnail

이번 과제는 '브랜드의 intro 페이지'를 만드는 것이였다.

특이 사항으로는 3주차(월) 수업시간에 배운 Next.js와 Typescript 활용을 해야 했다.
(주관적인 의견으로 단일 페이지라 라우팅이 필요 없었고 Next.js의 장점을 느껴볼 수 없었다..👀)

클론코딩이다 보니 마크업이 80프로 이상이었고,
구현할 기능은 scroll 이벤트에 반응하는 애니메이션 정도였다.

👨🏻‍💻 프로젝트 세팅

  • CNA + TS template

  • ESLint, Prettier (+ TypeScript)

  • styled-components, reset-css

  • 절대경로 세팅 src/jsconfig.json

  • components 나누기

  • 정적 파일 public/videos, images 넣기

  • common/constants

    • type, interface 정의
  • git repo 생성

    • slack git subscribe pre-onboarding-course-team-6/{레포이름}

⚙️ 구현한 기능 설명

1. Scroll 반응 Header

스크롤을 내리기 전에는 투명+흰 글씨, 스크롤이 있으면 흰 배경+검정 글씨로 바뀌는 헤더를 구현하였다.

scroll을 감지하는 코드를 찾았고 이를 적용하기로 하였다.

import { useState, useEffect } from "react";

export const useScroll = () => {
  // 초기상태 설정
  const [state, setState] = useState({
    x: 0,
    y: 0,
  });
  
  // 현재의 좌표를 State에 저장한다
  const onScroll = () => {
    setState({
      x: window.scrollX,
      y: window.scrollY,
    });
  };
  
  // 스크롤이 감지되면 onScroll을 호출한다
  useEffect(() => {
    window.addEventListener("scroll", onScroll);
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  
  // 좌표가 저장되어 있는 state 반환
  return state;
};
const Header: React.FC = () => {
  const { y } = useScroll(); // 스크롤 좌표중 y축의 좌표만 불러온다

  return (
    <S.Header yScroll={y}>
      <S.Container yScroll={y}>
        <a href="#">
          {/* y축이 0이상이면 검정색 로고, 0이면 흰색 로고(default) */}
          <img src={y > 0 ? BLACK_LOGO_URL : WHITE_LOGO_URL} alt="logo" />
        </a>
        <nav>
          <ul>
            <li>도서구매</li>
            <li>장바구니</li>
            <li>|</li>
            <li>이용권 관리</li>
            <li>로그인/회원가입</li>
          </ul>
        </nav>
      </S.Container>
    </S.Header>
  );
};

2. useScrollFadeIn

컴포넌트의 일부분이 화면에 노출 되기 시작하면 떠오르는 애니메이션을 구현했다.

라이브러리를 쓸 수 있었지만 스스로 구현을 해보고 싶었다.

하지만 한정된 시간 속에서 아쉽게도 구현은 하지 못하고 학습을 위해서 hook으로 사용할 수 있는 코드를 찾았다.

아래의 코드를 읽기 전에 Intersection Observer API를 모른다면 여기를 먼저 참조하면 좋다.

  • intersection observer 는 Target Element 가 화면에 노출되었는 지 여부를 간단하게 구독할 수 있는 API 이다.

  • js -> ts로 변환 하면서 여러 이슈가 있었다 (많이 배울 수 있었다😮)

import { useRef, useEffect, useCallback, MutableRefObject } from "react";

interface ScrollFadeIn { // 반환 타입 정의
  ref?: MutableRefObject<HTMLDivElement>;
  style: {
    opacity: number;
    transform: string;
  };
}

const useScrollFadeIn = (
  direction = "up", // default parameter
  duration = 1,
  delay = 0
): ScrollFadeIn => {
  
  // Ref는 포커스, 미디어 재생 또는 애니메이션을 직접적으로 실행 시키기 위해 
  // 외부에서 DOM(또는 React Component)을 제어 할 수 있게 도와줍니다.
  const dom = useRef<HTMLDivElement>(null);

  // name이 up이면 아래서 위로 올라오는 모션
  // stay는 움직임 없이 투명도에 변화만 주고싶을때 사용
  const handleDirection = (name: string) => {
    switch (name) {
      case "up":
        return "translate3d(0, 50%, 0)";
      case "down":
        return "translate3d(0, -50%, 0)";
      case "left":
        return "translate3d(50%, 0, 0)";
      case "right":
        return "translate3d(-50%, 0, 0)";
      case "stay":
        return "translate3d(0, 0, 0)";
      default:
        return;
    }
  };

  // 관찰할 대상이 등록되거나 가시성(보이는지 안보이는지)에 변화가 생기면 관찰자는 콜백(Callback)을 실행한다.
  const handleScroll = useCallback(
    ([entry]) => {
      const {
        current: { style },
      } = dom;
      
      // 만약 원하는 threshold만큼 노출되었다면,
      if (entry.isIntersecting) {
        style.transitionProperty = "all";
        
        // 애니메이션 동작 시간
        style.transitionDuration = `${duration}s`;
        style.transitionTimingFunction = "cubic-bezier(0, 0, 0.2, 1)";
        
        // 지연시간 => 여러 컴포넌트를 각각 다른 시간에 보여주고 싶으면 사용할 수 있다.
        style.transitionDelay = `${delay}s`;
        
        // transition과 함께 투명도와 위치를 원래대로 되돌린다.
        style.opacity = "1";
        style.transform = "translate3d(0, 0, 0)";
      }
    },
    [delay, duration]
  );

  useEffect(() => {
    let observer;
    const { current } = dom;

    if (current) {
      // IntersectionObserver에 동작 하게 할 함수와 Observer 세팅 값들을 넘겨 줍니다.
      // - 'threshold'는 TargetElement의 노출 비율을 말하는 것이며, 
      // -  0.5는 50% 정도 노출 되었을 때 해당 이벤트가 실행되게 됩니다.
      observer = new IntersectionObserver(handleScroll, { threshold: 0.5 });
      observer.observe(current);
    }

    return () => observer && observer.disconnect();
  }, [handleScroll]);

  // useScrollFadeIn를 사용할 DOM에 지정할 CSS 속성들 
  return { 
    ref: dom,
    style: {
      opacity: 0,
      transform: handleDirection(direction),
    },
  };
};

export default useScrollFadeIn;
const Section01: React.FC = () => {
  // 애니메이션의 delay 시간을 각각 다르게 주어서
  // first -> fourth 순으로 동작하게 한다
  const firstAnimated = useScrollFadeIn("up", 1, 0);
  const secondAnimated = useScrollFadeIn("up", 1, 0.1);
  const thirdAnimated = useScrollFadeIn("up", 1, 0.1);
  const fourthAnimated = useScrollFadeIn("up", 1, 0.3);

  return (
    <S.Section>
      <S.TextWrapper>
        {/* spread operator로 원하는 부분에 적용시켜준다*/}
        <S.StarLine {...fourthAnimated}>
          <img className="star" src="/images/star2.png" alt="star" />
          <img className="line" src="/images/line.png" alt="line" />
        </S.StarLine>
        <S.Span {...firstAnimated}>책 읽는 재미,</S.Span>
        <S.Span {...secondAnimated}>땅콩스쿨이</S.Span>
        <S.Span {...thirdAnimated}>만들어줄게요!</S.Span>
      </S.TextWrapper>
      <S.MouseAnimation>
    </S.Section>
  );
};

🤔 후기

  1. 요구사항에서 '3분 이내로 작동하는 E2E 테스트를 구현해 주세요.' 라는 항목이 있었지만 시간 부족으로 할 수 없었다..

  2. 지난번 과제에서 CSS를 자주 하지 않으면 실력이 퇴화되어 간다는 느낌을 받았다. 이번에는 과제 두 개중에 선택할 수 있었고, 이 과제를 선택했는데 연습을 열심히 할 수 있었다.

  3. Custom hooks를 (직접 구현은 하지 못하고 🥲) 사용해볼 수 있었다. 리액트를 처음 배울때 Nomad Coders가 만든 Custom hooks 10개 만들기 강의가 있었는데, 초보자 시절에는 20%도 흡수 못했던 것 같다. 다시 수강하면서 학습을 해야할 필요성을 느꼈다.

  4. Next.js를 처음 사용해봤는데 아직 진가를 못느끼고 있다. 공부할 시간이 필요한데 과제를 진행하면서 학습한다는 것은 정말 힘든 것 같다. POB가 끝나면 좀 더 깊숙하게 공부하고 제대로 사용해보고 싶다.

profile
느리지만 꾸준하게 💪🏻

0개의 댓글