728x90
반응형
목차
저번 맵 불러오는 것에 이어서 마커들을 버튼을 누를때 마다 종류별로 보이게끔 해보았다.
그 과정에서 리액트로 컴포넌트화하고 리팩토링한 것을 정리해 보려고 한다.
타입을 지정해주는 과정에서 많은 것을 배운 것 같다.
완성 모습
완성되어 버튼을 누를때 마다 바뀌는 모습이다..
data.ts
마커들을 종류별로 저장하기 위해 만든 파일이다.
import { Position, MarkerProps } from './Marker';
// 인터페이스를 임포트 받아 상속을 통해 확장한 모습이다.
export interface DataMarkerProps extends MarkerProps {
name: string;
}
// 이름, 위치, origin을 속성으로 가진다.
const markers: DataMarkerProps[] = [
{
name: 'coffee',
Positions: [
{ lat: 37.499590490909185, lng: 127.0263723554437 },
{ lat: 37.499427948430814, lng: 127.02794423197847 },
{ lat: 37.498553760499505, lng: 127.02882598822454 },
{ lat: 37.497625593121384, lng: 127.02935713582038 },
{ lat: 37.49646391248451, lng: 127.02675574250912 },
{ lat: 37.49629291770947, lng: 127.02587362608637 },
{ lat: 37.49754540521486, lng: 127.02546694890695 },
],
Origin: { x: 10, y: 0 },
},
{
name: 'store',
Positions: [
{ lat: 37.497535461505684, lng: 127.02948149502778 },
{ lat: 37.49671536281186, lng: 127.03020491448352 },
{ lat: 37.496201943633714, lng: 127.02959405469642 },
{ lat: 37.49640072567703, lng: 127.02726459882308 },
{ lat: 37.49640098874988, lng: 127.02609983175294 },
{ lat: 37.49932849491523, lng: 127.02935780247945 },
{ lat: 37.49996818951873, lng: 127.02943721562295 },
],
Origin: { x: 10, y: 36 },
},
{
name: 'carpark',
Positions: [
{ lat: 37.49966168796031, lng: 127.03007039430118 },
{ lat: 37.499463762912974, lng: 127.0288828824399 },
{ lat: 37.49896834100913, lng: 127.02833986892401 },
{ lat: 37.49893267508434, lng: 127.02673400572665 },
{ lat: 37.49872543597439, lng: 127.02676785815386 },
{ lat: 37.49813096097184, lng: 127.02591949495914 },
{ lat: 37.497680616783086, lng: 127.02518427952202 },
],
Origin: { x: 10, y: 72 },
},
];
export default markers;
Marker.tsx
각각의 마커 스타일을 위한 파일이다.
import { Map, MapMarker } from 'react-kakao-maps-sdk';
// postion 타입 지정
export interface Position {
lat: number;
lng: number;
}
// 포지션 배열과 origin을 추가한 타입 지정
export interface MarkerProps {
Positions: Position[];
Origin: { x: number; y: number };
}
const markerImageSrc =
'https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/category.png';
const imageSize = { width: 22, height: 26 };
const spriteSize = { width: 36, height: 98 };
// 컴포넌트를 담은 함수
// 컴포넌트는 항상 JSX를 return하기 때문에 JSX.Element로 타입을 지정하면 된다.
function Marker({ Positions, Origin }: MarkerProps): JSX.Element {
return (
<>
{Positions.map((position) => (
<MapMarker
key={`${position.lat},${position.lng}`}
position={position}
image={{
src: markerImageSrc,
size: imageSize,
options: {
spriteSize,
spriteOrigin: Origin,
},
}}
/>
))}
</>
);
}
export default Marker;
KaKaoMap.tsx
맵과 마커가 보일 파일이다.
import React, { useEffect, useRef, useState } from 'react';
import getGeolocation from 'utils/getGeolocation';
import { Map, MapMarker } from 'react-kakao-maps-sdk';
import Marker, { MarkerProps } from './Marker';
import markers, { DataMarkerProps } from './data';
const { kakao } = window;
export default function KaKaoMap() {
const { longitude, latitude } = getGeolocation();
// 초기 배열 기본값
const FirstMarker: DataMarkerProps[] = markers.filter(
(category) => category.name === 'coffee',
);
const [selectedCategory, setSelectedCategory] =
useState<DataMarkerProps[]>(FirstMarker);
// 누른 마커만 표시하는 함수
function DeleteMarks(name: string) {
// 원본 배열 가져옴
setSelectedCategory(markers);
const newMarkers: DataMarkerProps[] = markers.filter(
(category) => category.name === name, // 일치하는 배열만 추출
);
// 배열 재설정
setSelectedCategory(newMarkers);
}
return (
<div>
<Map
center={{
lat: latitude,
lng: longitude,
}} // 지도의 중심 좌표
style={{ width: '100vw', height: '100vh' }} // 지도 크기
level={3} // 지도 확대 레벨
>
{/* 맵 중첩이가능함 */}
{selectedCategory.map((mark: DataMarkerProps, index: number) => {
return (
<Marker
key={`${mark.Positions[index].lat},${mark.Positions[index].lng}`}
Positions={mark.Positions}
Origin={mark.Origin}
/>
);
})}
</Map>
<div className="category">
{markers.map((mark, index: number) => {
return (
<button
key={`${mark.Positions[index].lat},${mark.Positions[index].lng}`}
type="button"
onClick={() => DeleteMarks(mark.name)}
>
{mark.name}
</button>
);
})}
</div>
</div>
);
}
MapPage.tsx
맵과 마커를 불러오는 페이지이다.
import React, { useEffect } from 'react';
import KaKaoMap from 'components/map/KaKaoMap';
export default function MapPage() {
return (
<div>
<KaKaoMap />
</div>
);
}
배운 점
1. type error
Uncaught TypeError: Cannot read properties of undefined (reading 'classList')
발견 시 이런식으로 타입을 지정해준다.
const [selectedCategory, setSelectedCategory] =
useState<DataMarkerProps[]>(FirstMarker);
2. 타입의 확장 및 타입의 임포트
// postion 타입 지정
export interface Position {
lat: number;
lng: number;
}
// 포지션 배열과 origin을 추가한 타입 지정
export interface MarkerProps {
Positions: Position[];
Origin: { x: number; y: number };
}
export interface DataMarkerProps extends MarkerProps {
name: string;
}
3. 배열의 변경 및 불변성 유지(filter 사용)
function DeleteMarks(name: string) {
// 원본 배열 가져옴
setSelectedCategory(markers);
const newMarkers: DataMarkerProps[] = markers.filter(
(category) => category.name === name, // 일치하는 배열만 추출
);
// 배열 재설정
setSelectedCategory(newMarkers);
}
참고
https://velog.io/@nemo/react-error-cannot-read-property
https://www.howdy-mj.me/react/react-node-and-jsx-element
반응형
'프로젝트 > 재활용프로젝트' 카테고리의 다른 글
[리액트][재활용 프로젝트] 댓글 crud 구현 (0) | 2024.06.07 |
---|---|
[재활용 프로젝트][리액트] 메인 화면 재구상하기 (0) | 2024.05.23 |
[재활용 프로젝트][리액트] 카카오 api 카테고리 마커 세부화 (0) | 2024.05.13 |
[재활용프로젝트][리액트] main화면 구성하기 (0) | 2024.05.08 |
[프로젝트][리액트] 사이드 바 구현하기 (0) | 2024.05.02 |