[TIL]240205_컴포넌트 분리 및 코드개선해보기

ㅇㅖㅈㅣ·2024년 2월 5일
1

Today I Learned

목록 보기
71/93
post-thumbnail

👩🏻‍💻 Today Learn

  • 최종프로젝트 시연영상 제작
  • 프로젝트의 핵심기술 및 기술적 강점 정리

⌨️ 컴포넌트 분리해보기

Next.js의 app Router를 사용중인데 반려동물 동반시설 페이지를 구현할때 서버컴포넌트 안에 'use client'를 사용했었다.

따라서 최적화와 코드개선 기간인 이때 컴포넌트로 분리해서 'use client'를 없애버리는 것이 1차 목표!

근데 분리한 컴포넌트에서 'use client'를 써버리면 이게 의미있는건가? 하는 의문도 들었다.
이 부분에 대해서는 더 알아봐야할 것 같다.

그리고 길게 나열된 코드에서 오버레이를 사용하는 부분과 현재위치로 이동하는 버튼부분을 분리해보았다.

컴포넌트 분리

// FacilitiesMapComponent.tsx
const FacilitiesMapComponent = () => {
  const [currentLocation, setCurrentLocation] = useState<{ latitude: number; longitude: number }>({
    latitude: 33.450701,
    longitude: 126.570667
  });

  const [coordinate, setCoordinate] = useState<{ sw: number[]; ne: number[] }>({
    sw: [33.44653220300056, 126.56202403813722],
    ne: [33.45501290255946, 126.57927700861282]
  });
  const [activeMarkerId, setActiveMarkerId] = useState<number | null>(null);
  const [currentLocationMarker, setCurrentLocationMarker] = useState<boolean>(false);
  const [showCurrentInfo, setShowCurrentInfo] = useState<boolean>(true);
  const { facilitiesData } = useFacilitiesQuery();
  const { warnTopCenter } = useToast();

  // 현재위치로 가는 버튼
  const currentButtonHandler = () => {
   // ...
  };

  // 장소이름 클릭 시 해당 마커로 이동
  const markerFocusHandler = ({ latitude, longitude }: { latitude: number; longitude: number }) => {
    setCurrentLocation({
      latitude,
      longitude
    });
    setShowCurrentInfo(false);
  };

  // 현재위치를 시작점으로 만들기
  useEffect(() => {
    if ('geolocation' in navigator) {
     // ...
    }
  }, []);

  // onBoundsChanged시 화면 이동 할때마다 데이터를 계속 받아와서 느려짐 -> 디바운싱 이용
  const debouncedSetCoordinate = useDebounce((map) => {
   // ...
  }, 1000);

  return (
    <div className={styles.mapContainer}>
				// ...
      // 지도에 오버레이를 생성해주는 컴포넌트
      <FacilitiesOverlay place={place} activeMarkerId={activeMarkerId} />
            	// ...
      <NearFacilities markerFocusHandler={markerFocusHandler} coordinate={coordinate} />
      // 현재위치로 가는 버튼 컴포넌트
      <CurrentLocationButton currentButtonHandler={currentButtonHandler} />
    </div>
  );
};

export default FacilitiesMapComponent;

이런식으로 조금 세분화해서 컴포넌트를 분리해보았다.


hook 생성하기

그리고 다음으로는 이 역시 튜터님의 피드백을 통해 진행하게된 작업인데 이러한 말씀을 해주셨다.
"커스텀훅은 비즈니스 로직이기 때문에 복잡해도된다. 하지만 뷰어로직인 컴포넌트는 깔끔하고 우아하게 관리해주어야 한다. 컴포넌트에 useState 여러개 넣지말고 최대한 커스텀 훅으로 빼서 정리해라"

그래서 hook을 만들어서 조금이라도 코드를 개선해보고자 하였다.

useCurrentLocation.ts

import { useEffect, useState } from 'react';
import { useToast } from '@/hooks/useToast';

const useCurrentLocation = () => {
  const { warnTopCenter } = useToast();

  const [currentLocation, setCurrentLocation] = useState<{ latitude: number; longitude: number }>({
    latitude: 33.450701,
    longitude: 126.570667
  });
  const [currentLocationMarker, setCurrentLocationMarker] = useState<boolean>(false);
  const [showCurrentInfo, setShowCurrentInfo] = useState<boolean>(true);

  // 현재위치로 가는 버튼
  const currentButtonHandler = () => {
    if ('geolocation' in navigator) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          setCurrentLocation({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude
          });
          setCurrentLocationMarker(true);
          setShowCurrentInfo(true);
        },
        () => {
          warnTopCenter({ message: '현재 위치를 찾지 못하였습니다 🥲' });
          setCurrentLocationMarker(false);
        }
      );
    }
  };

  // 현재위치를 시작점으로 만들기
  useEffect(() => {
    if ('geolocation' in navigator) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          setCurrentLocation({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude
          });
          setCurrentLocationMarker(true);
        },
        () => {
          setCurrentLocation({ latitude: 33.450701, longitude: 126.570667 });
        }
      );
    }
  }, []);

  return {
    currentLocation,
    setCurrentLocation,
    currentLocationMarker,
    showCurrentInfo,
    setShowCurrentInfo,
    currentButtonHandler
  };
};

export default useCurrentLocation;

useMapBounds.ts

import { useState } from 'react';
import useDebounce from '@/hooks/useDebounce';

const useMapBounds = () => {
  const [coordinate, setCoordinate] = useState<{ sw: number[]; ne: number[] }>({
    sw: [33.44653220300056, 126.56202403813722],
    ne: [33.45501290255946, 126.57927700861282]
  });

  const debouncedSetCoordinate = useDebounce((map) => {
    setCoordinate({
      sw: map.getBounds().getSouthWest().toString().replace(/\(|\)/g, '').split(',').map(Number),
      ne: map.getBounds().getNorthEast().toString().replace(/\(|\)/g, '').split(',').map(Number)
    });
  }, 1000);

  return { debouncedSetCoordinate, coordinate };
};

export default useMapBounds;

이렇게 훅으로 분리해주었더니 기존 컴포넌트의 코드가 반으로 싹둑 잘려나갔다!

FacilitiesMapComponent

  const { debouncedSetCoordinate, coordinate } = useMapBounds();
  const {
    showCurrentInfo,
    setCurrentLocation,
    setShowCurrentInfo,
    currentLocation,
    currentLocationMarker
  } = useCurrentLocation();

기존 컴포넌트에서는 이렇게 불러와서 사용하면 된다!


✍🏻 회고

코드개선 쉽지않네... 컴포넌트를 분리하다보니 고장이 계속 발생하여 고치고 바로잡는 시간도 꽤 걸렸다.
지금은 기능구현에 초점을 맞추다보니 그냥 한페이지에 주르륵 짜기에 급급했는데 뒤늦게라도 분리해보고 오류를 잡아보면서 익숙해지도록 해야할 것 같다.
그러다보면 나중에는 코드를 짜면서 어떻게 분리하고 관리할지 그려지지않을까? 하는 바램을 가져본다.

profile
웰씽킹_나는 경쟁력을 갖춘 FE개발자로 성장할 것이다.

5개의 댓글

comment-user-thumbnail
2024년 2월 6일

TIL에 정성이 가득하군요 ..

1개의 답글
comment-user-thumbnail
2024년 2월 6일

서버컴포넌트에서는 훅 못쓴다구요

1개의 답글