드래그 동작을 막아놓은 피드의 지도부분을 클릭했을 때 큰 모달창이 뜨면서 드래그, 확대축소가 가능한 지도가 출력되도록 하고 싶었다.
■ 모달 기본 틀 만들기
이전에 자전거 경로를 선으로 표시한 지도와 동일한 지도를 불러오게 되므로 동일한 코드를 부모 컴포넌트에 옮겨서 함수로 감싼 뒤 피드의 지도, 모달창의 지도 각 컴포넌트에 props로 내려주었다.
지도 생성함수인 showMap을 useEffect 로 감싸서 컴포넌트가 렌더링 되고 실행되도록 하였고 모달 기본 틀을 만들어 주었다.
// Posting.jsx
const Posting = () => {
const { kakao } = window;
const [modalOpen, setModalOpen] = useState(false);
// 지도 생성 함수
const showMap = (id, roadLine) => {
const container = document.getElementById(id);
const options = {
center: new kakao.maps.LatLng(
roadLine[Math.floor(roadLine.length / 2)].LINE_XP,
roadLine[Math.floor(roadLine.length / 2)].LINE_YP
),
level:
(roadLine.length <= 100 && 8) ||
(roadLine.length <= 500 && 9) ||
(roadLine.length >= 1000 && 11) ||
(roadLine.length >= 3000 && 12),
draggable: false
};
const map = new kakao.maps.Map(container, options);
const linePath = [];
roadLine.forEach((el) => linePath.push(new kakao.maps.LatLng(el.LINE_XP, el.LINE_YP)));
const polyline = new kakao.maps.Polyline({
path: linePath,
strokeWeight: 5,
strokeColor: "#FF54F1",
strokeOpacity: 0.7,
strokeStyle: "solid"
});
polyline.setMap(map);
};
return (
<div>
{feeds?.length > 0 ? (
feeds.map((feed) => {
return (
<FeedContainder key={feed.id}>
<RidingMap id={feed.id} roadLine={feed.roadLine} />
<RidingMap setModalOpen={setModalOpen} showMap={showMap} id={feed.id} roadLine={feed.roadLine} />
{modalOpen && (
<ModalMap setModalOpen={setModalOpen} showMap={showMap} id={feed.id} roadLine={feed.roadLine} />
)}
</FeedContainder>
);
})
) : (
<p>피드가 없습니다.</p>
)}
</div>
)}
// ModalMap.jsx
import { useEffect, useRef } from "react";
import styled from "styled-components";
const ModalMap = ({ showMap, setModalOpen, id, roadLine }) => {
// showMap : 지도를 생성하는 함수
useEffect(() => {
showMap(id, roadLine);
}, []);
// 모달창 외부화면 클릭 시 모달 닫기
const outSection = useRef();
const closeOutSection = (e) => {
if (outSection.current === e.target) {
closeModal();
}
};
const closeModal = () => {
setModalOpen(false);
};
return (
<Layer ref={outSection} onClick={closeOutSection}>
<Map id={id}>
<ClostBtn onClick={closeModal}>
X
</ClostBtn>
</Map>
</Layer>
);
};
export default ModalMap;
const Layer = styled.div`
z-index: 500;
display: block;
background: rgba(0, 0, 0, 0.3);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
`;
const ClostBtn = styled.p`
position: absolute;
font-size: 30px;
right: 15px;
top: 10px;
color: white;
cursor: pointer;
`;
const Map = styled.div`
width: 1200px;
height: 1000px;
z-index: 100;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 2px solid white;
`;
🚨 발생한 문제
지도생성함수를 한 페이지의 RidingMap, ModalMap 두 컴포넌트에서 호출했으나 지도가 한 컴포넌트에서만 출력되는 문제가 발생했다.
❗️원인
함수 내부에서 getElementById로 id값을 사용하는데, 두 컴포넌트가 같은 id를 지니고 있어 앞쪽 요소만 지도가 담겼다.
✅ 해결방법
id생성방식을 변경하여 다른 id값을 지니게 해 문제를 해결하였다.
// ModalMap 컴포넌트의 지도 호출
const ModalMap = ({ showMap, id, roadLine }) => {
useEffect(() => {
showMap(id + "_map", roadLine);
}, []);
return <Map id={id + "_map"}></Мар>
};
// RidingMap 컴포넌트의 지도 호출
const RidingMap = ({ showMap, id, roadLine }) => {
useEffect(() => {
showMap(id, roadLine);
}, []);
return <Map id={id}></Map>;
};
🚨 발생한 문제
두 컴포넌트에 모두 지도를 불러오지만 피드의 모달을 클릭했을 때 피드 5개씩 동일한 지도를 불러와지는 문제가 발생했다.
❗️원인
feeds의 id, roadLine 값을 모달 컴포넌트에 props로 내려주려고 ModalMap 컴포넌트도 같이 map에 넣어서 렌더링했더니 동일한 지도의 모달이 5개가 생겼다.
✅ 해결방법
ModalMap 컴포넌트를 map으로 렌더링하는 중괄호 바깥에다가 옮긴 뒤, modalOpen state 를 없애고 초기값이 빈값인 selectedPost state를 만들어서 props를 내려주었다.
■ 최종 코드
// Posting.jsx
const Posting = () => {
const { kakao } = window;
const [selectedPost, setSelectedPost] = useState();
// 지도 생성 함수
const showMap = (id, roadLine) => {
const container = document.getElementById(id);
const options = {
center: new kakao.maps.LatLng(
roadLine[Math.floor(roadLine.length / 2)].LINE_XP,
roadLine[Math.floor(roadLine.length / 2)].LINE_YP
),
level:
(roadLine.length <= 100 && 8) ||
(roadLine.length <= 500 && 9) ||
(roadLine.length >= 1000 && 11) ||
(roadLine.length >= 3000 && 12),
// 드래그가능 여부를 selectedPost 값 존재여부로 판단
// 피드지도에서는 드래그가 불가하고 모달창에서는 가능하다.
draggable: selectedPost ? true : false
};
const map = new kakao.maps.Map(container, options);
const linePath = [];
roadLine.forEach((el) => linePath.push(new kakao.maps.LatLng(el.LINE_XP, el.LINE_YP)));
const polyline = new kakao.maps.Polyline({
path: linePath,
strokeWeight: 5,
strokeColor: "#FF54F1",
strokeOpacity: 0.7,
strokeStyle: "solid"
});
polyline.setMap(map);
};
return (
<div>
{feeds?.length > 0 ? (
feeds.map((feed) => {
return (
<FeedContainder key={feed.id}>
<ContentsContainer
style={{
backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.65), rgba(0,0,0,0.65)), url(${feed.profile_img})`
}}
>
<Profile src={feed.profile_img} alt="profile_img" />
<RiderNameContainer>
<RiderName>{feed.nickname}</RiderName>라이더 님
</RiderNameContainer>
<RidingRoad>종주점 : {feed.BICYCLE_PATH}</RidingRoad>
<DetailContainer>
<p>종주 일자 : {feed.created_time}</p>
<Thumb currentFeedId={feed.id} currentThumb={feed.thumb} thumbUser={feed.userId} />
</DetailContainer>
</ContentsContainer>
<RidingMap setSelectedPost={setSelectedPost} showMap={showMap} id={feed.id} roadLine={feed.roadLine} />
</FeedContainder>
);
})
) : (
<p>피드가 없습니다.</p>
)}
{selectedPost && (
<ModalMap
setSelectedPost={setSelectedPost}
showMap={showMap}
id={selectedPost.id}
roadLine={selectedPost.roadLine}
/>
)}
<div ref={ref} />
</div>
);
};
export default Posting;
// RidingMap.jsx
const RidingMap = ({ setSelectedPost, showMap, id, roadLine }) => {
useEffect(() => {
showMap(id, roadLine);
}, []);
const OpenModal = () => {
// feeds에서 받은 id, roadLine 값을 부모컴포넌트(Positng)에서 해당값을 사용할 수 있도록 setSelectedPost 해줌
setSelectedPost({ id, roadLine });
};
return <Map id={id} onClick={OpenModal}></Map>;
};
// ModalMap.jsx
const ModalMap = ({ showMap, setSelectedPost, id, roadLine }) => {
useEffect(() => {
showMap(id + "_map", roadLine);
}, []);
const outSection = useRef();
const closeOutSection = (e) => {
if (outSection.current === e.target) {
closeModal();
}
};
const closeModal = () => {
// setSelectedPost가 빈값일 때 모달이 닫히도록 수정
setSelectedPost(null);
};
return (
<Layer ref={outSection} onClick={closeOutSection}>
<Map id={id + "_map"}>
<ClostBtn onClick={closeModal}>
X
</ClostBtn>
</Map>
</Layer>
);
};
■ 완성 모습
😢 느낀점
모달 화면에 띄워지지 않는 문제를 구글에 검색했을 때 kakao developers 질문 탭에서 map.relayout(); 을 추가해야한다는 답변을 많이보았다. 해당 메서드를 어떻게 적용해야할 지 고민에 빠져서 지도api 를 여러군데에서 생성할 때 id 값을 다르게 넣어주어야 한다는걸 빨리 알아차리지 못해서 많이 헤맸었다. 그래도 잘 해결해서 기능이 잘 동작하는 모습을 볼 수 있어서 좋았고 외부 api 사용에 대해 많이 배우게 된 시간이었다.
'TIL' 카테고리의 다른 글
Typescript 개념 공부 (0) | 2024.09.24 |
---|---|
아웃소싱 팀 프로젝트 ⑥ - 마무리, 회고 (0) | 2024.09.23 |
아웃소싱 팀 프로젝트 ④ - Infinite Scroll (0) | 2024.09.20 |
아웃소싱 팀 프로젝트 ③ - 카카오맵 api 로 자전거 경로 지도 출력 (0) | 2024.09.19 |
아웃소싱 팀 프로젝트 ② - 기능구현(따봉) (0) | 2024.09.15 |