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