AndChill 개발일지(4) - 홈 화면(nav bar)

dali·2024년 8월 2일
0

And Chill 개발 기록

목록 보기
4/9
post-thumbnail

네비게이션 바를 만들 때 로고, 메뉴리스트, 검색 바 말고 넣고싶었던 것은 지역(국가)을 선택할 수 있는 버튼이었습니다.

just watch 사이트에서는 초기 접속 시 랜딩 페이지에서 국가를 선택할 수 있는데(이것도 footer에서), 그 후로는 조건에 따른 리다이렉트를 시키는 건지 정상적인 방법으로는 랜딩페이지로 돌아갈 방법이 없어 국가를 변경할 수 없습니다.

국가마다 개봉하는 영화들이 조금씩 다르기 때문에, 국가를 변경하면서 어떤 영화들이 개봉하는지 살펴보고 싶었는데 이를 할 수가 없어 조금 아쉬웠었습니다. 그래서 이 기능을 넣기로 하였습니다!

기본 틀은 다음과 같이 잡았습니다. 아마 이렇게 쭉 가다가 나중에 필요 시 수정하지 않을 까 싶습니다.


국가 선택 버튼


우측의 국가 선택 버튼 관련해서는 다음 구현 목록을 작성했습니다.

  1. 어떤 국가인지 명확히 알아볼 수 있도록 ui를 디자인(국기 이모지 등..)
  2. 국가 검색 기능
  3. 선택한 국가 상태를 Zustand persist로 스토리지에 저장해서 전역 상태로 관리

국가 목록 data를 받아오는 api는 있었지만 문제는 data에 해당 국가를 ui상에서 나타낼만한게 국가 코드나 이름밖에 없었습니다. 그래서 전세계 국기 아이콘을 받아서 해당 국가 코드와 매핑해서 사용하였습니다...

결과는 다음과 같이 구현되었습니다. 국기 아이콘이 네트워크 환경이 좋지 않으면 느리게 로드되는 문제점이 있었는데 스켈레톤과 무한 스크롤 중 무엇을 사용해서 UI를 개선할지 고민이 되었었는데 개인적으로 성능차이가 많이 나지 않는 한 스켈레톤 ui를 더 좋아해서 이번에도 적용해 보았습니다! (극단적인 예시를 위해 느린 4G 사용)

Code

간단한 팝업 메뉴 형식의 모달이라 boolean형 state를 사용하여 Modal의 열림/닫힘 상태를 관리하였습니다.
국가 정보 목록 데이터들은 react-query 커스텀 훅을 사용하여 가져왔고, 선택된 국가를 zustand persist로 전역 상태로 저장하기 모달 컴포넌트에서 store의 setter함수를 불러와 업데이트 하였습니다. 현재 어떤 국가가 선택되어 스토리지에 저장되어 있는지 사용자가 알 수 있도록 button의 children에 국가 store 상태값을 불러와 넣어주었습니다!

const RegionSelectButton = () => {
  const [isOpen, setIsOpen] = useState(false);
  const region = useRegionStore((state) => state.region);
  const { data: regionData, isLoading, isError } = useRegionConfigQuery();

  const selectedRegionIcon = `/region-flags/${region.toLowerCase()}.svg`;

  const regionsWithFlags = regionData ? addFlagIcons(regionData) : []; //국기 아이콘 매핑 함수 적용

  const handleSetIsOpen = () => {
    setIsOpen(!isOpen);
  };

  return (
    <S.Container>
      <S.RegionSelectButton onClick={handleSetIsOpen}>
        <S.SelectedRegionIcon src={selectedRegionIcon} />
      </S.RegionSelectButton>
      {isOpen && <RegionSelectModal regionData={regionsWithFlags} handleSetIsOpen={handleSetIsOpen} />}
    </S.Container>
  );
};
const RegionSelectModal = ({ regionData, handleSetIsOpen }: TRegionSelectModalProps) => {
  const [searchQuery, setSearchQuery] = useState('');
  const { setRegion } = useRegionStore((state) => ({
    region: state.region,
    setRegion: state.setRegion,
  }));

  const handleRegionItemClick = (regionCode: string) => {
    setRegion(regionCode);
    location.reload();
  };

  const handleRegionSearchChange = (e: { currentTarget: { value: SetStateAction<string> } }) => {
    setSearchQuery(e.currentTarget.value);
  };

  const filteredRegionList = filteredRegionsBySearchQuery(regionData, searchQuery);

  return (
    <S.Container>
      <S.SearchBar type="text" placeholder="어떤 국가의 영화 정보를 알고 싶나요?" onChange={handleRegionSearchChange} />
      <S.RegionInfoContainer>
        {filteredRegionList
          .filter((region) => !NOT_SUPPORTED_REGIONS.includes(region.iso_3166_1))
          .map((region) => {
            return (
              <S.RegionItem
                onClick={() => {
                  handleRegionItemClick(region.iso_3166_1);
                  handleSetIsOpen();
                }}
                key={region.iso_3166_1}
              >
                <S.FlagIconSkeleton></S.FlagIconSkeleton>
                <S.FlagIcon src={region.flag_icon} />
                <S.RegionName>{region.native_name}</S.RegionName>
              </S.RegionItem>
            );
          })}
      </S.RegionInfoContainer>
    </S.Container>
  );
};

0개의 댓글

관련 채용 정보