[React] 인터랙티브 웹 개발 찍먹해보기

sjoleee·2023년 1월 26일
3

https://www.bemyidol.xyz/

이전에 만들었던 아이돌만들기 서비스의 메인페이지가 너무 마음에 안들어서 수정해보았다.
연휴동안 시간도 남고, 이왕 하는 거 스크롤 애니메이션을 적용해보았다.

본 글은 정보성 글이 아니고 개인적인 생각의 기록일 뿐입니다.

인터랙티브 웹

사용자에게 있어 서비스란 기능도 물론 중요하지만, 비주얼, 심미성이 굉장히!!! 중요한 요소라고 생각한다.

하지만 실제로 지금까지 해온 공부는 이론적인 부분이나 기능을 구현하기 위한 것이 대부분이었고,
속으로는 '인터랙티브 웹? 대충... 뭐, 할 수 있을 것 같은데?' 라는 오만하고 안일한 생각을 해왔다.

막상 해보니 간단할 것이라고 생각했던 인터랙션을 구현하는 데도 굉장히 머리가 아팠다.
(아무런 디자인이 없는 상태에서 떠오르는 대로 구현하다보니 막막했을 수도 있겠다.)
더욱 복잡한 인터랙션을 구현하려면 많은 고민이 필요할 것 같았다.

간단하게 스크롤의 변화에 따라 함께 변화하는 스크롤 애니메이션을 작성했는데, 두 가지 방법을 물망에 올렸다.

  • scroll event
  • intersection observer

scroll event

스크롤의 변화를 계속 주시하다가 원하는 지점에서 애니메이션을 작동시키는 방법이다.
일하면서 일분코딩님의 강의를 들은 적이 있어서 이미 사용해봤었는데,
직관적이라는 생각도 들었지만 한편으로는 모든 스크롤 이벤트를 감지하는게 성능적으로 좋을 리가 없다는 생각도 들었다.
쓰로틀링을 적용하면 좀 나을까 싶었는데, 그럼 내가 원하는 정확한 타이밍에 애니메이션을 작동시킬 수 없다고 판단했다.

따라서 이 방법 대신 intersection observer를 사용했다.
intersection observer가 scroll event 방식의 상위 호환이라고 생각하지는 않는다.
동일한 방식이 아니니까 구현하고자 하는 인터랙션이 어떤 방식을 사용해야 할지 잘 판단하면 될 것이다.
나는 intersection observer로 충분히 커버할 수 있는 인터랙션을 구현하였다.

intersection observer

무한스크롤 구현할때 사용했었는데, 뷰포트(혹은 다른 요소)에 특정 요소가 들어오는지를 감지해주는 api다.
사용법은 아래 글에 작성해두었다.
[React] 무한 스크롤 적용하기

이번에 구현한 인터랙션은 세 개의 섹션으로 나눌 수 있다.

section 1

가장 먼저 보이는 최상단 화면.
로고 이미지가 뷰포트에 들어오면 스크롤다운 lottie의 opacity를 1로, 뷰포트에서 사라지면 opacity를 0으로 만들어주고 있다.
로고 이미지 요소를 타겟 요소로 관측하였다.

다만, intersection observer의 isIntersecting을 상태에 담아서 사용하고 있기 때문에...
로고가 보였다 안보였다 할 때마다 리렌더링이 발생한다.

이 부분이 조금 신경쓰이긴 한다.

스크롤 이벤트 + className 추가삭제를 통한 방법은 좀 더 나을라나 싶기도 하고...
'리액트스럽게' 구현하자면 스크롤 이벤트를 감지해서 또 상태를 업데이트 해줘야할텐데, 그럼 IO랑 뭐가 다른가 싶기도 하고...
IO의 구체적인 동작원리를 파보지 않아서 observer로 관찰하는게 성능에 문제를 발생시킬까 하는 우려도 있었다.

지금은 그냥 구현하는 방법을 체득하는 수준으로만 진행해보기로 했다.

section 2

아이돌 이름 및 사진을 변경하며 sticky로 보여주는 구간이다.

이 부분을 어떻게 구현할지 고민을 좀 했는데, 결국 선택한 방법은 타이머를 따로 만드는 방법이었다.

그러니까, 눈에 보이지 않는 div를 여러개 만들고, 각 div를 관찰하다가 화면에 들어오면 상태를 변경하여 해당하는 아이돌의 이름과 사진을 보여주는 방식이었다.

예를 들면,

export const INTRODUCE_TEXT_ARR = [
  {
    id: 0,
    content: "아이돌",
  },
  {
    id: 1,
    content: "트와이스 나연",
  },
  {
    id: 2,
    content: "아이브 유진",
  },
  {
    id: 3,
    content: "르세라핌 카즈하",
  },
];

이렇게 어떤 아이돌을 보여줄지 미리 작성해두고,

<div>
  {INTRODUCE_TEXT_ARR.map((item, index) => 
    <Timer key={item.id} index={index} setCurrentIndex={setCurrentIndex} />)
  }
</div>

이렇게 map으로 타이머들을 생성해준 다음,

onst Timer = ({
  setCurrentIndex,
  index,
}: {
  setCurrentIndex: (index: number) => void;
  index: number;
}) => {
  const onIntersectFirstSection: IntersectionObserverCallback = ([entry]) => {
    entry.isIntersecting ? setCurrentIndex(index) : null;
  };

  const { setTarget } = useObserver({
    onIntersect: onIntersectFirstSection,
    threshold: 1,
  });

  return <div ref={setTarget} className=" bg-transparent w-1 h-[50vh]"></div>;
};

이런식으로 currentIndex라는 상태를 해당하는 아이돌의 index로 업데이트해줌으로써 사용자가 해당 아이돌의 이름과 이미지를 볼 수 있게 되는 구조...!

근데 굉장히 맘에 들지 않는다 ㅋㅋㅋㅋ

이유는, 스크롤 이벤트를 어거지로 IO로 대체하려고 한 듯한 느낌이 들기 때문이다.
이 부분에서 차라리 섹션2 구간만 스크롤 이벤트로 구현하는 것도 괜찮을 것 같다는 생각이 문득 든다.

조금 더 고민하면 덜 억지스러운 방식으로 IO를 활용할 수 있을 것 같은데...
암튼 좀 아쉽다.

아, 타이머 대신 사진이나 사진+문구 컴포넌트 자체를 sticky로 두고 threshold: 1로 사용하면 되지 않나 싶은데, 이게 모바일 사파리에서 scroll up을 하면 주소창이 올라와서 처음 계산한 vh단위와 달라지는 것 같더라.

그래서 threshold: 1 + 100vh를 조합할 경우 모바일 scroll up에 제대로 대응하지 못하는 문제가 군데군데 발생했고, 이를 피하기 위해서 이런 번거로운 방식을 택했다.

threshold: 1대신 수치를 다르게 넣거나,
모바일 사이즈일 경우에만 수치를 낮추는 방식,
혹은 동적으로 vh를 계산해주는 방식을 생각했으나,

다음 프로젝트에서 또 겪게 될 문제인데 그때 더 깊게 파보고 싶어서 건너뛰었다(미안... 아이돌아...)

혹시 이게 원인이 아닐지도...
다음 번에 조금 더 파봐야겠다.

section 3

마지막 섹션은 섹션1과 마찬가지로 버튼이 나타나는게 전부인 섹션이다.
분홍색 영역은 sticky고 흰 글자가 나타나는건... 나타나는게 아니라 원래 존재하는 흰 글씨가 배경색의 변화로 눈에 들어오는 것 뿐이다.

분홍 영역이 뷰포트에 들어오면 버튼이 fade-in 되도록 설정해주었다.

문제가 하나 있는데, 분홍색 영역이 사라지면 버튼 fade-out 효과도 주고 있다.
그런데 이 fade-out효과가 페이지 진입시에도 발생한다는 것이다.

https://www.bemyidol.xyz/

직접 들어가보시라. 잘 보면 진입 시 아래쪽에 버튼이 fade-out되는 것을 확인할 수 있다.

섹션2가 보이는지 안보이는지 상태 : 초기값 false
위 상태가 true이면 fade-in 적용, false이면 fade-out 적용
초기값이 false라서 처음 렌더링 되고 바로 fade-out 애니메이션 동작

이런 방식으로 작성되어 있는게 원인이다.
해결방법을 찾아보자면... 버튼 자체를 display: none으로 DOM에서 없애버리고 true면 나타나게 하면 되지 않을까?
그리고 false가 되면 다시 display: none으로 만드는데, fade-out을 위해 setTimeout으로 애니메이션 종료 이후 display 속성을 변경하는 것으로...

뇌피셜인데 될지 모르겠다.
그리고 해결방법을 생각해놓고 적용을 안한게 좀 아쉽긴 하다 ㅋㅋㅋㅋㅋㅋ
개인적으로 이런저런 일이 많아서 시간이 좀 부족했고, 다음 프로젝트로 생각중인 것도 인터랙티브 웹이라서 두 프로젝트를 동시에 손보게 될 것 같당!

느낀점

인터랙티브 웹은 정말 재밌다!
구현하고자 하는 인터랙션이 조금만 복잡해져도 중첩된 컴포넌트들의 css를 죄다 뜯어고쳐야 하는 경우가 생기는데, 아마 디자인 없이 혼자 즉석에서 바꿔가면서 하다보니 그런 것 같기도 하다 ㅋㅋㅋㅋ

시각적인 부분을 강화한다는게 서비스의 경쟁력에 큰 영향을 준다고 생각하는 사람으로써 더욱 자세하게 공부하고 싶다!

근데 인터랙티브 웹에서도 자주 쓰이는 인터랙션은 정형화된 테크닉, 노하우가 존재할 것 같다는 생각이 들어서 고수들의 테크닉을 엿보기 위해 패캠 강의를 구매했다.

조금 기대해봐도 될런지...?

profile
상조의 개발일지

0개의 댓글