[TIL] 230721

이세령·2023년 7월 21일
0

TIL

목록 보기
61/118

Map 리팩토링

주소 -> 좌표 변환 함수

kakao map api를 이용한 함수들을 한 코드로 사용할 수 있도록 분리하는데 return문을 사용해서 callback함수를 내보내려고 했지만 문법 이해의 부족인지 아이디어가 떠오르지 않았다.

setState로 지정하는 것을 시도해봤지만, 실패했다.
Promise를 사용하여 result 데이터를 넘겨보았다.

const Test = (map, title, address) => {
  return new Promise((resolve) => {
    const geocoder = new kakao.maps.services.Geocoder();

    const callback = (result, status) => {
      if (status === kakao.maps.services.Status.OK) {
        const coords = new kakao.maps.LatLng(result[0].y, result[0].x);
        // ... Rest of your marker creation code ...

        // Resolve the promise with the result value
        resolve(result);
      }
    };
    geocoder.addressSearch(address, callback);
  });
};

console.log(
      Test(map, filteredData[0]?.title, filteredData[0]?.address).then((data) => console.log('pormise', data))
    );


성공적으로 주소 변환 데이터를 받을 수 있었다!

사용하는 것은 좌표 뿐이니, coords를 내보내도록 resolve(coords);로 수정해주었다.

LatLng에 담지않고 내보낼까 했지만, 해당 api는 객체로 만들기 때문에 사용하고 싶을 때 참조하여 사용하면 될 것 같다.

마커 생성 함수 코드 변경!

export const makeNewMarker = (map, title, address) => {
  addressToCoords(map, title, address).then((coords) => {
    const imageSrc = 'https://ifh.cc/g/KPoAgp.png', // 마커이미지의 주소입니다
      imageSize = new kakao.maps.Size(40, 40), // 마커이미지의 크기입니다
      imageOption = { offset: new kakao.maps.Point(20, 40) }; // 마커이미지의 옵션입니다. 마커의 좌표와 일치시킬 이미지 안에서의 좌표를 설정합니다.

    // 마커의 이미지정보를 가지고 있는 마커이미지를 생성합니다
    const markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imageOption);

    const marker = new kakao.maps.Marker({
      position: coords,
      image: markerImage
    });
    marker.setMap(map);

    kakao.maps.event.addListener(marker, 'click', function () {
      // 레벨 설정 및 좌표 중심으로 이동
      map.setLevel(3);
      map.setCenter(coords);
    });
    map.setLevel(3);
    map.setCenter(coords);
    const content =
      `<div class="customoverlay" style="color:orange; border: 1px solid orange; background-color:white; padding: 8px; border-radius: 30px; margin-top:-75px; 
      ">` +
      title +
      ` </div>`;

    // 커스텀 오버레이가 표시될 위치입니다

    const customOverlay = new kakao.maps.CustomOverlay({
      position: coords,
      content: content
    });

    customOverlay.setMap(map);
  });
};

생각해보니 marker에 모달이 열리는 이벤트를 빠트렸다.
함수를 넣어서 실행하는 시도를 했는데, 마커 생성 함수가 다른곳에서도 사용되다 보니 실패했다..

마커 생성 후 해당 마커를 return하여 받는 것으로 해당 마커에 이벤트를 따로 추가하는 방식으로 코딩했다.
MainMap.jsx

export const makeNewMarker = async (map, title, address) => {
  ..
  return marker; // 해당 부분만 추가!
};

// 메인 컴포넌트에서의 useEffect
useEffect(() => {
    const map = makeNewMap();
    setMap(map);

    if (area === '전체' && category === '전체') {
    } else {
      const bounds = new kakao.maps.LatLngBounds(); 

      filteredData.forEach((position) => {

        const marker = makeNewMarker(map, position.title, position.address);
        marker.then((item) => { // 해당 마커마다 이벤트 추가
          kakao.maps.event.addListener(item, 'click', function () {
            listOnclickHandler(position);
          });
        });

        addressToCoords(position.address).then((coords) => {
          bounds.extend(coords); 
          map.setBounds(bounds);
        });
      });
    }
  }, [filteredData]);

다른 컴포넌트에서 사용하는 경우 모달 이벤트 함수 형식이 다르기에 따로 이벤트를 추가해줬다.
PlaceList

const openModal = (item) => {
    dispatch(setDetailModalData(item));
    dispatch(setDetailModalOn(true));
  };

  const listOnclickHandler = (item) => {
    openModal(item);

    const map = makeNewMap();
    const marker = makeNewMarker(map, item.title, item.address);
    marker.then((mark) => {
      kakao.maps.event.addListener(mark, 'click', function () {
        openModal(item);
      });
    });
  };

로딩 이벤트

먼저 다른 컴포넌트에서 사용할 컴포넌트를 생성해준다.
Loading

function Loading() {
  return (
    <>
      <S.LoadingBackground>
        <S.LoadingAnimation />
      </S.LoadingBackground>
    </>
  );
}

회전하는이벤트를 만들기 위해 style을 지정해줬다.
LoadingStyled.jsx

const S = {
  LoadingBackground: styled.div`
    width: 100%;
    height: 100vh;
    inset: 0px;
    position: fixed;
    opacity: 0.8;
    z-index: 100;
    background-color: rgb(221, 221, 221);
  `,
  LoadingAnimation: styled.div`
    display: inline-block;
    width: 50px;
    height: 50px;
    border: 4px solid #f3f3f3;
    border-top: 4px solid orange;
    border-radius: 50%;
    animation: spin 2s linear infinite;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

    @keyframes spin {
      0% {
        transform: translate(-50%, -50%) rotate(0deg);
      }
      100% {
        transform: translate(-50%, -50%) rotate(360deg);
      }
    }
  `
};

0% 일때, 요소를 수평 수직 50%으로 컨테이너 중앙에 배치하고 0도(시작 시 요소가 회전하지 않게)로 설정해줍니다.
100%일때, 완전한 원 회전을 적용하여 회전하는 것 처럼 보이게 만들어줬습니다.

날씨 데이터 가공하기

기존에 하드코딩 되어있던 데이터를 사용하기 편하게 하시기 위해 다음과 같이 고쳤다.
기존 데이터

-> 여러 데이터들 중 하나

const firstDayData = [];
  const secondDayData = [];
  const thirdDayData = [];
  const fourthDayData = [];
  const fifthDayData = [];

  const pushData = data?.list.forEach((item) => {
    if (item?.dt_txt.includes(days[1])) {
      firstDayData.push(item.main.temp_max);
      firstDayData.push(item.main.temp_min);
      firstDayData.push(item.weather[0].icon);
    }
    if (item.dt_txt.includes(days[2])) {
      secondDayData.push(item.main.temp_max);
      secondDayData.push(item.main.temp_min);
      secondDayData.push(item.weather[0].icon);
    }
    if (item.dt_txt.includes(days[3])) {
      thirdDayData.push(item.main.temp_max);
      thirdDayData.push(item.main.temp_max);
      thirdDayData.push(item.weather[0].icon);
    }
    if (item.dt_txt.includes(days[4])) {
      fourthDayData.push(item.main.temp_max);
      fourthDayData.push(item.main.temp_min);
      fourthDayData.push(item.weather[0].icon);
    }
    if (item.dt_txt.includes(days[5])) {
      fifthDayData.push(item.main.temp_max);
      fifthDayData.push(item.main.temp_min);
      fifthDayData.push(item.weather[0].icon);
    }
  });

하드 코딩 하신 것들과 비교하여 날짜에 따라 동일한 데이터를 정리해주었다.

데이터 가공

const weatherData = days?.map((day) => {
      const dataForDay = data?.list?.filter((item) => item.dt_txt.includes(day));
      return {
        Max: dataForDay.map((item) => item.main.temp_max),
        Min: dataForDay.map((item) => item.main.temp_min),
        Icon: dataForDay[0]?.weather[0]?.icon ?? 'default-icon'
      };
    });

null 병합 연산자와 optional chainging을 사용하여 빈 배열일 때 오류를 방지해줬다.

모달창 활성화 시 해당 리스트 표시

모달창의 활성여부와 title의 동일여부를 판단하여 props를 내려주어 스타일을 처리하였다.
PlaceList.jsx

<S.ListItem
            key={item.id}
            onClick={() => {
              listOnclickHandler(item);
            }}
            img={item.detail.imageURL}
            isSelected={item.title === modalTitle && modalState}
          >

AsideStyled.jsx

${(props) =>
      props.isSelected &&
      css`
        font-weight: bold;
        transform: scale(1.1);
        color: white;
        box-shadow: 0px 3px 3px 2px rgba(0, 0, 0, 0.1);
        position: relative;
        background-image: url(${(props) => props.img});
        background-position: center;
        background-size: cover;
        background-repeat: no-repeat;

        &::before {
          content: '';
          background-color: rgba(255, 165, 0, 0.9);
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
        }
      `}

해당 props가 존재할 때 해당 css를 적용할 수 있다.

부족한 점
해답은 이미 배웠던 것에 있을 수 있다.
콜백함수를 까먹어서 초심으로 돌아가 다시 공부할 필요가 있다.
팀원들이 사용하기 편하게 데이터를 가공하기 위해 사고력의 필요성을 느꼈다. 프로젝트 때문에 밀린 알고리즘 공부를 꼼꼼히 해야겠다.

profile
https://github.com/Hediar?tab=repositories

0개의 댓글