홍보 페이지를 계속 어떻게 구현할까 고민 했는데...
어차피 카카오맵 API를 쓰는 김에 이것도 카카오맵으로 처리하고 싶었는데(카카오맵에는 건물들이 다 나와있으니까), 생각만 하던 중에 마땅한 수가 없어서 뒤로 미루고 있었다
어느날 백을 담당중이던 팀장의 제안+인터넷을 찾아보다가
지도를 만들어서 클릭할 수 있게하는 게 어떻겠냐고 해서...
🤔 마땅한 SVG 파일이 없다...
🧏♀️ 만들면 된다!
누군가 만들고 간 학교 지도를 레이어로 깔아서 피그마에서 노가다로 도형 만들었다(기존에 지도 깃헙 링크도 있었던 걸로 알고 있는데 폭파되었다..)
사실 별로 오래 안걸림
그리고 피그마에서 만든 파일을 SVG로 변환 시켰다->https://www.svgviewer.dev/svg-to-react-jsx 여기서 jsx로 수정도 가능
🤔 도형을 합쳤다고 생각했는데 코드 상에서는 안 합쳐지고 rect가 엄청 많이 나온다. 한 건물에 rect가 여러개 있다...
🧏♀️ Inkspace로 합침
의도한 바는...건물마다 컴포넌트가 분리되어 건물을 클릭하면 해당 건물 전시회 정보를 불러오는 것 이기 때문에, 컨포넌트를 분리하는 게 중요했다
위에서 Inkspace로 여러 rect를 한 컴포넌트로 합쳤기 때문에, 거기에 id를 붙여서 구분할 수 있게 했다
🤔 스타일을 어떻게 입히는가(styled-component를 사용할 것)
🧏♀️ 솔직히 잘 모르겠어서 gpt한테 물어봤다
const Rect = styled.div`
transition: fill 0.2s;
&:hover {
fill: #ff0000;
}
`;
...
const Promotion_map = (props) => (
<svg
width={815}
height={528}
viewBox="0 0 815 528"
fill="none"
id="svg40"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<rect width={815} height={528} fill="white" id="rect1" />
<Rect as="rect"
x={244.17}
y={27.8761}
width={66.1104}
height={38.0104}
transform="rotate(25.1765 244.17 27.8761)"
fill="#D9D9D9"
id="I"
/> ...
라고 알려줌!
path도 있다고 하니까
as 태그를 써서 <Element as="rect" />
<Element as="path" />
이런식으로 하랜다
🤔 rect는 되는데 path는 안 먹는다
이유:<Path id="R" style={{ fill: "#d9d9d9", }} d="M -45.218329 11.261575 L -45.21773 53.805969 L -0.00059213439 53.805947 L -0.00039105698 66.0249 C -17.969428 64.461396 -35.082878 62.464904 -46.015792 60.969479 L -43.38038 95.988445 C -32.176966 98.000779 -3.5785063 102.47849 21.183476 104.28837 C 45.946121 106.09902 103.79958 96.530594 129.63157 91.520329 L 118.59714 61.078854 C 111.0107 62.177403 89.098694 65.052709 62.138132 67.764971 C 55.50417 68.432343 47.078295 68.5607 37.790683 68.331433 L 37.789891 53.80636 L 46.763737 53.805655 L 46.763138 11.261261 L 37.790882 11.260864 L 37.790643 -0.00040853996 L 0.00015990644 -0.00082135131 L 0.00039862067 11.260451 L -45.218329 11.261575 z " transform="matrix(0.825487 0.564421 -0.581206 0.813756 109.599 332.358)" />
이런식으로 style이 원래 적용되어 있기 때문
🧏♀️
const Path = styled.path` fill: #d9d9d9; // 기본 색상 transition: fill 0.2s; // 색상 변화를 부드럽게 만들기 위한 트랜지션 &:hover { fill: #ff0000; // 호버 시 변경하고 싶은 색상 } ... <Path id="L" transform="rotate(24.8205 214.136 128)" d="m 214.136,128 h 36 v 17 h -36 z m 12.14671,-52.657299 21.3951,0.609952 -1.89742,66.555257 -21.3951,-0.60995 z m 3.53024,45.231539 41.99541,0.62086 -0.699,47.28073 -41.99541,-0.62086 z" />
style을 지우면 된다
부모 컴포넌트에서 getPromotionList
를 하면 자식 컴포넌트(map)에서 눌린 건물의 정보가 부모 컴포넌트에parameter
로 전달되고, 그 parameter
에 있는 hallId
로 서버에 쿼리를 보내서 반환되는 전시 정보 리스트를 띄워줘야한다
getPromotionList (부모 컴포넌트)
const getPromotionList= async (hallId)=>{
try{
setHallName(hallId); // 어느 건물인지 위에 띄워주기 위해
const resp=await axios.get('http://localhost:8080/promotion',{
params:{
hall: hallId // hallId가 parameter로 들어감
},
headers: headers }
);
console.log(resp.data);
setPromotionList(resp.data);
}
catch (e) {
console.log(e);
}
}
...
<Promotion_map onClick={getPromotionList}/>
...
}
Promotion_map (자식 컴포넌트)
const Promotion_map = ({onClick}) => {
const [hallId, setHallId]=useState(null);
useEffect(()=>{
onClick(hallId);
},[hallId])
const handleClick = (event) => {
const clickedElementId = event.target.id;
// svg 요소(rect, path..)마다 hall이름을 가진 id가 있기 때문에, click되면 clicekdElementId에 id 저장
setHallId(clickedElementId);
// hallId에 clickedElementId 저장
};
삽질 했던 기존코드
useEffect(()=>{
},[hallId])
const handleClick = (event) => {
const clickedElementId=event.target.id;
setHallId(clickedElementId);
onClick(hallId);
// 비동기적으로 업데이트 되는 문제->useEffect에서 hallId가 바뀔 때마다 onClick 호출로 해결
🤔
setHallId
를 하고onClick
에hallId
를 넣으니 비동기적으로 업데이트됨
🧏♀️useEffect
를 이용해서hallId
가 바뀔 때마다onClick
을 호출하는 것으로 해결
🤔 styled-component를 입혀서 hover을 하면 색이 생기는데 정작 클릭이 됐을 때는 색이 없는 게 어색해서, 클릭 되었을 때도 도형에 색이 있었으면 했다
🧏♀️
const [clicked, setClicked]=useState(null);
를 만들고
handleClick
함수에setClicked(clickedElementId)
를 추가했다
그러면 건물이 눌리면clicked
변수에 그 건물의id
가 들어가게 된다
이제 스타일을 바꿔줘야하는데 styled component에서props
를 처음 써봐서 애를 좀 먹었다
const Rect = styled.rect`
fill: ${(props) => (props.isClicked ? '#013B70' : '#ECECEC')};
transition: fill 0.2s;
&:hover {
fill: #013B70;
}
...
const Promotion_map = ({onClick}) => {
const [hallId, setHallId]=useState(null);
const [clicked, setClicked]=useState(null);
useEffect(()=>{
onClick(hallId);
},[hallId])
const handleClick = (event) => {
const clickedElementId = event.target.id;
setHallId(clickedElementId);
setClicked(clickedElementId);
};
return (
<svg
viewBox="0 0 815 528"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...onClick}
onClick={handleClick}
>
<Rect as="rect"
x={244.17}
y={27.8761}
width={66.1104}
height={38.0104}
transform="rotate(25.1765 244.17 27.8761)"
id="I"
isClicked={clicked === "I"}
/>
isClicked
props
를 만들어주고, clicked
가 id
와 같으면 fill 스타일이 실행되게 했다(isClicked가 있으면(clicked가 id라는 뜻이니까==눌렸다) #013B70, 없으면 #D9D9D9)
svg내의 컴포넌트를 클릭하게 되면 clickedElementId
에 클릭된 컴포넌트의 id
(건물 이름)이 들어가게 된다
hallId
를 clickedElementId
로 바꾼다
hallId
가 변경되었으므로 onClick
을 호출
onClick
를 사용하는 부모컴포넌트의 axios 요청 매개변수 hallId
로 서버에 요청
서버가 hallId
에 등록된 전시 정보 리스트를 불러옴
Clicked
가 현재 눌린 건물의 id
로 변경
컴포넌트의 isClicked
가 현재 눌린 건물의 id
와 일치한다면 true(건물에 색이 생김)