[TIL] - 프로젝트 튜토리얼 만들기(1)

장광진·2024년 3월 14일

MyList 베타서비스 배포 전 QA 및 피드백을 받아본 결과 공통적으로 나온 결론은 "신규 유저가 사용하기 어렵다!"였다. 분명 내가 만질 때는 쉬웠는데... 이러한 점이 배포된 서비스와 개인 포트폴리오와의 큰 차이점이 아닌가한다. 내 눈에 예쁘고, 편한데... 이게 어려우면 다른건 어떻게 쓰지..?라는게 내 주관적인 생각이었지만, 피드백을 받은 이상 고쳐야지머..

1. 이미지로 튜토리얼 진행하기

처음엔 디자이너분께 이미지 또는 GIF형식의 파일을 달라했다. 단순히 이미지를 넣고 State값을 관리하면서 이미지만 넘기면서 보여주면 되지않을까하는 안일한 실수였다. 이미지도 당연히 width, height만 조절하면될줄 알았는데 모바일에서 URL Navbar때문에 잘리고, width height비율이 맞지않아 이상하게 보인다거나 문제가 많았다. 가장 큰 문제는 이미지를 넘기는 버튼을 화면의 빈칸인 맨 아래에 absoulte로 넣었다는 것인데, 이가 스마트폰 기종에 따라 Navbar가 하단에 위치하는 경우가 있어 가차없이 짤려버렸다. 그렇다고 flex-column으로 넣어버리면 배경이 붕뜨는 이상한 UI가 완성되었다. 구글링을 한 결과에서도 보통은 Native로 개발한 분들뿐이고 참고할만한 데이터가 많지 않았다. 그래서 결과적으로 선택한 방법은 화면 전체적으로 투명도를 낮추고 강조할 부분만 보여주며 말풍선을 달자이다.

2. 튜토리얼 상태를 만들어 튜토리얼 모드를 관리하자

총 튜토리얼은 7개의 화면을 보여줘야했고, 같은 페이지가 아닌 여러 페이지에서 튜토리얼을 진행해야했다. 떄문에 튜토리얼 상태라는 것을 알려주기 위해 전역적으로 관리가 필요했다. 간단한 상태값 관리기 떄문에 Recoil을 사용하여

TutorialStep.ts

import { atom } from "recoil";

export type TutorialStep =
  | "header"
  | "playlist"
  | "env"
  | "list1"
  | "list2"
  | "add1"
  | "add2"
  | null;

export const tutorialStepState = atom<TutorialStep>({
  key: "tutorialStepState",
  default: null, 
});

총 7개의 튜토리얼 상태를 만들어줬다. 또한 이를 편하게 사용하기 위해 hooks에 함수로 만들어 줬다.

export function useTutorial() {
  const [tutorialStep, setTutorialStep] =
    useRecoilState<TutorialStep>(tutorialStepState);

  const toggleTutorialMode = () => {
    const stepOrder: TutorialStep[] = [
      "header",
      "playlist",
      "env",
      "list1",
      "list2",
      "add1",
      "add2",
      null,
    ];
    const currentStepIndex = stepOrder.indexOf(tutorialStep);
    const nextStep = stepOrder[(currentStepIndex + 1) % stepOrder.length];
    setTutorialStep(nextStep);
  };

  return { tutorialStep, toggleTutorialMode, setTutorialStep };
}

이로써 튜토리얼을 진행할 1차 관문을 넘었따. 세팅이 끝났으니 본격적으로 컴포넌트에 적용을 시켜보자!!

...
const { tutorialStep, toggleTutorialMode, setTutorialStep } = useTutorial();

  // 튜토리얼 모드를 표시하는 조건부 렌더링
  const isTutorialMode = tutorialStep !== null;
  const handlePageClick = (e: React.MouseEvent) => {
    if (isTutorialMode) {
      if (tutorialStep === "playlist") {
        e.stopPropagation(); // 이벤트 전파 중단
       
        return;
      }
      toggleTutorialMode();
    }
  };

...
return (
    <div
      className={`h-full scrollbar-hide overflow-scroll relative ${
        isTutorialMode ? "bg-black bg-opacity-50" : ""
      }`}
      onClick={handlePageClick}
    >
      {isTutorialMode && (
        <div className="fixed inset-0 bg-black bg-opacity-70 z-10"></div>
      )}
      <Tutorial
        username={userData?.username}
        setTutorialMode={setTutorialStep}
        length={playlistData?.length}
      />
    

간단히 코드를 살펴보자. 우선 화면을 클릭하여 튜토리얼이 넘어가도록 최상단 div에 onClick 이벤트를 넣어주었다. 여기서 문제가 무엇이냐. 두번째 튜토리얼인 "playlist"이후에는 새로운 플레이리스트를 보내는 로직(post)이 동작하여 페이지를 이동한 후 튜토리얼을 진행해야했다. 즉, 그대로 넘어가버린다면 아직 데이터가 생성되지도않은 페이지로 이동할 수 없기에 튜토리얼이 진행이 안된다는 것이다 ㅠㅠㅠ... 그래서 playlist일때는 페이지에서 튜토리얼을 넘어가는 방식을 멈추고

const handleAddPlaylistClick = async (e: React.MouseEvent) => {
    e.stopPropagation();

    if (tutorialStep === "playlist") {
      handleAddPlaylist(title, titleImage, token); 
    }
  };

추가하는 버튼을 눌러야만 튜토리얼도 같이 진행되며 페이지가 이동되도록 만들었다.

또한 위 모달에서 튜토리얼을 시작해야하므로 시작 지점을 "보러가기"버튼으로 옮겨주었다. 또한 플레이리스트가 0개일떄, token이 있을떄라는 조건을 달아 첫 로그인한 유저가 아니라면 튜토리얼 진행을 할 수 없도록 구현하였다. 이제 추가적인 디테일부분은 2탄에서 작성하도록 하겠다.

profile
점진적 과부하

0개의 댓글