Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[민우] - 발자취 페이지 : 내 발자취 탭 구현 #495

Open
wants to merge 19 commits into
base: dev
Choose a base branch
from

Conversation

MinwooP
Copy link
Contributor

@MinwooP MinwooP commented Jun 3, 2024

📌 관련 이슈

close #492

🚀 구현 내용

발자취 페이지 - 내 발자취 탭만 우선적으로 구현했습니다.

⚠️ 현재 발자취 부분 서버 API가 없어서 내 발자취 조회 API 대신 전체 계획 조회 API 를 이용해 테스트를 진행하고 연결해놓은 상태입니다.

default.mp4

💡 발자취 페이지 설명

1. 컴포넌트 구조

/footprint의 url을 가지는 발자취 페이지의 컴포넌트 구조는 다음과 같습니다.

  • page.tsx
    • FootPrintsTab
    • MyFootPrints (or AllFootPrints)
      • FootPrintFilter
      • FootPrintList

2. 주요 로직 설명

  • 발자취 페이지를 렌더링하는 page.tsx 파일에서 현재 무슨 탭에 있는 지를 나타내는 state를 관리합니다. 이를 통해 내 발자취 탭과 발자취 둘러보기 탭을 구분합니다.
     // page.tsx
     const [isMyFootPrintsTab, setIsMyFootPrintsTab] = useState(true);
     
     return (
        <div className={classNames('footprint-page')}>
          <FootPrintTab
            isMyFootPrintsTab={isMyFootPrintsTab}
            setMyFootPrintsTab={setMyFootPrintsTab}
            setAllFootPrintsTab={setAllFootPrintsTab}
          />
          {isMyFootPrintsTab ? <MyFootPrints /> : <AllFootPrints />}
        </div>
      );
  • FootPrintsTab 컴포넌트에서 탭을 변경할 수 있습니다.
  • MyFootPrints 컴포넌트는 내 발자취 탭과 관련된 내용을 관리합니다. 선택된 년도, 선택된 계획 이렇게 2개 데이터에 대한 state를 관리합니다.
    • FootPrintFilter 컴포넌트에서 위 state들을 변경합니다.
    • FootPrintList 컴포넌트에서 위 state들을 prop으로 받아 이를 바탕으로 내 발자취들을 불라옵니다.
    • default로는 2024년, 모든 계획 에 대한 발자취들을 불러오도록 했습니다.
    • 계획 data들은 기존에 있던 getMyPlans API 함수를 사용해 서버로부터 불러와 현재 년도에 해당하는 계획들만 filter 해주었습니다.

💡 내 발자취 탭 무한 스크롤 구현

  • TanStack-Query의 useInfiniteQuery hook을 이용해 무한 스크롤에 필요한 data 및 함수들을 받아왔습니다.

  • react-intersection-observer의 useInView hook을 사용해 스크롤이 끝나는 순간(마지막 발자취 Item 밑에 있는 div요소가 viewPort에 나타나는 순간)을 감지해 데이터를 추가적으로 불러왔습니다

    const { tempFootPrintList, fetchNextPage, isFetchingNextPage } =
         useMyFootPrintsQuery({
           sort: 'latest',
           current: true,
          });
    // fetchNextPage: 다음 데이터를 불러오는 함수 
    	
    const { ref, inView } = useInView();
    // ref로 참조하고 있는 요소가 
    // viewPort에 없으면 inView값은 false, viewPort에 나타나면 inView 값 true
    // ref는 마지막 발자취 Item 밑의 div 요소에 등록해줄 것
    
     useEffect(() => {
       // ref로 참조하고 있는 div 요소가 viewPort에 보여진다면 다음 데이터 fetch
       if (inView) {
         fetchNextPage();
       }
     }, [inView]);
  • 마지막 item까지 스크롤이 되어 데이터를 더 불러올 때, 데이터를 불러오는 동안 발자취 list 아래 부분에 로딩스피너를 보여줍니다.

    <div className="footprint-list__loading-wrapper">
    	{isFetchingNextPage ? (
    	  <FadeLoader color={COLOR.PRIMARY} speedMultiplier={1.3} />
      ) : (
        <div className="footprint-list__end" ref={ref} />
      )}
    </div>

    useInfiniteQuery 훅을 통해 받아온 isFetchingNexPage의 값을 기준으로 새로 데이터를 받아오고 있는 중인지 판단해, 로딩 스피너를 조건부렌더링 해주었습니다.

@MinwooP MinwooP added the 🎶낮음 낮은단계 label Jun 3, 2024
@MinwooP MinwooP requested review from qkdl60 and suehdn June 3, 2024 09:30
@MinwooP MinwooP self-assigned this Jun 3, 2024
Copy link
Contributor

@qkdl60 qkdl60 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다. 👍👍

Copy link
Contributor

@qkdl60 qkdl60 Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IconName 타입 리팩토링이 필요한 것 같습니다.

const ICON_NAME_MAP={
  PLUS: 'add',
  CREATE_NEW_PLAN: 'stylus',
  FEEDBACK: 'edit_note',
  OTHER_PLAN: 'explore',
  HOME: 'home',
}

type IconName= keyof typeof ICON_NAME_MAP

이런 식으로 변경하면 IconName은 따로 수정 안해도 될 거 같습니다.
관련 블로그 ✨ 객체 key를 상수 타입으로

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 너무 좋은데요??? 대박 👍🏻👍🏻

current: true,
});

const { ref, inView } = useInView();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

둘러보기 페이지와 다르게 useInView 쓴 이유가 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아직 적용하지는 않았지만, useInView를 사용해 특정 element가 viewPort에 들어오는지 나가는지를 파악해, 스크롤이 많이 된다면 현재 viewPort에 보이지 않고 상단에 쌓여있는 element들은 dom에서 제거해주는 기능을 추가해주기 위해 사용했습니다!!

혜수님은 둘러보기 페이지에서 react-infinite-scroller 라이브러리를 사용하신 것 같은데 이 라이브러리로는 각 element의 viewPort 진입 여부를 각각 판단해주는 기능은 없는 것 같아서요!!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

페이지도 영역 별로 컴포넌트화 하니 깔끔한 것 같습니다 .👍👍

Comment on lines +1 to +7
@mixin ellipsis($line) {
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: $line;
-webkit-box-orient: vertical;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 줄별로 쓸 수 있는게 유용하네요

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋㅋ이거 혜수님이 작성해주신 코드 그대로 재사용한 겁니다!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🎶낮음 낮은단계
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[민우] - 발자취 페이지 : 내 발자취 탭 구현
2 participants