[TIL]240117_Zustand찍먹하기

ㅇㅖㅈㅣ·2024년 1월 17일
3

Today I Learned

목록 보기
59/93
post-thumbnail

👩🏻‍💻 Today Learn

  • react-Datepicker로 캘린더 사용하기
  • 지역선택 select박스 만들기

🤦🏻‍♀️ 고민한 부분

KakaoMapMarker 컴포넌트는 카카오맵으로 희망하는 장소를 선택하면 해당 위치의 위도, 경도, 주소정보를 가지고 올 수 있다.

// KakaoMapMarker
'use client';

import style from './kakaoMapMarker.module.scss';
import Script from 'next/script';
import { Map, MapMarker, MapTypeControl, ZoomControl } from 'react-kakao-maps-sdk';
import { useState, useEffect } from 'react';
import { useAddress, usePosition } from '@/hooks/useKakaoMapMarker';

const KAKAO_SDK_URL = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_APP_KEY}&libraries=services&autoload=false`;

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

  const [position, setPosition] = useState<{ lat: number; lng: number }>();
  const [address, setAddress] = useState<string>('');

  const mapClickHandler = (_t: any, mouseEvent: any) => {
    const lat = mouseEvent.latLng.getLat();
    const lng = mouseEvent.latLng.getLng();
    setPosition({ lat, lng });
    getAddress(lat, lng);
  };

  // 위도, 경도로 주소 정보 가져오기
  const getAddress = (lat: number, lng: number) => {
    const geocoder = new kakao.maps.services.Geocoder();

    const coord = new kakao.maps.LatLng(lat, lng);
    const callback = (result: any, status: any) => {
      if (status === kakao.maps.services.Status.OK) {
        setAddress(result[0].address.address_name);
        console.log('결과', result);
      }
    };

    geocoder.coord2Address(coord.getLng(), coord.getLat(), callback);
  };

  // 현재위치를 시작점으로 만들기
  useEffect(() => {
    if ('geolocation' in navigator) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          setCurrentLocation({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude
          });
        },
        () => {
          console.log('위치 받기에 실패하였습니다');
          setCurrentLocation({ latitude: 33.450701, longitude: 126.570667 });
        }
      );
    }
  }, []);

  return (
   // ...
  );
};

export default KakaoMapMarker;

Zustand

여기서 positionaddress값을 원래는 state로 관리하였었는데 이 컴포넌트를 상품을 등록하는 페이지에서 불러와서 사용하면서부터 고민이 시작되었다.

위도, 경도, 주소값을 전달해주는 방법에 대해 고민을 하다가 전역으로 관리해보라는 튜터님의 조언을 듣고 Zustand를 처음 사용하게 되었다.

Redux보다 훨씬 간편한게 장점이라고 했기 때문에 유튜브와 블로그와 문서를보며...

참고자료
유튜브
공식문서

딱 이렇게 사용하였다!

// import { useCallback, useState } from 'react';
import { create } from 'zustand';

export type Position = {
  position: { lat: number; lng: number };
  setPosition: (position: { lat: number; lng: number }) => void;
};

export type Address = {
  address: string;
  setAddress: (address: string) => void;
};

const usePosition = create<Position>((set) => ({
  position: { lat: 0, lng: 0 },
  setPosition: (position) => set(() => ({ position }))
}));

const useAddress = create<Address>((set) => ({
  address: '',
  setAddress: (address) => set(() => ({ address }))
}));

export { usePosition, useAddress };

type을 지정해 주지 않으면 사용하는 부분에서 이러한 오류를 마주할 수 있다.

그리고 업데이트를 해주고 필요한 부분에서 가져올 수 있다.

// KakaoMapMarker 
 const setPosition = usePosition((state) => state.setPosition);
 const setAddress = useAddress((state) => state.setAddress);

// CreateForm 
 const position = usePosition((state) => state.position); 
 const address = useAddress((state) => state.address);

console에는 정보가 찍히지만 등록이 안되는 현상

//CreateForm
 const [inputForm, setInputForm] = useState<TablesInsert<'used_item'>>({
    title: '',
    address: address,
    content: '',
    latitude: position.lat,
    longitude: position.lng,
    main_category_id: 0,
    sub_category_id: 0,
    photo_url: [],
    place_name: '',
    price: 0,
    sold_out: false,
    user_id: user?.id!
  });

console에 초기에는 {lat:0, lng:0} 주소는 빈값이었다가 위치를 클릭하면 해당 정보가 찍히는 것을 볼 수 있다.

그런데 찍은 정보값이 등록하기를 클릭하면 데이터에 빈값으로 들어가는 것이 문제였다.

inputForm에 값을 담아주는 로직이었는데 초기값에만 넣어주고 setInputForm을 해주지 않은 것이다...

위치와 장소값이 초기 렌더링 시점에 초기값으로 설정되어있는 상태에서 위치를 변경하면 자동으로 업데이트 되길 바란것이다 😏

해결방법!

useEffect(() => {
    setInputForm((prev) => ({
      ...prev,
      address: address,
      latitude: position.lat,
      longitude: position.lng
    }));
  }, [address, position]);

usePositionuseAddress에서 상태를 가져오는 것은 비동기적으로 발생할 수 있어 최신상태로 업데이트가 되지 않는다.

그래서 useEffect를 사용하여 addressposition이 바뀔때마다 inputForm을 펼쳐서 그 안에 그 값들을 넣어주는 코드를 추가해주었더니

계속 빈값이었던 데이터가 들어온 것을 확인할 수 있다!!


✍🏻 회고

Zustand는 처음 사용해보았는데 Redux에 비해 정말 간단해서 신기하면서도 프로젝트가 커지면 괜찮을까? 하는 생각도 들었다. 그래도 이 기회에 겉핥기 식으로 사용해보아서 좋았다.
해결에 어려움을 겪었던 데이터 전달하는 부분은 분명히 어려운게 아닌건 느껴졌는데 명확히 콕찝어 찾아내지 못하고 헤맸던게 아쉬웠다 ㅠㅠ

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

0개의 댓글