From df7cc790b9c6e992107889b1d2be5fe69ca64ee7 Mon Sep 17 00:00:00 2001 From: effozen Date: Sat, 23 Nov 2024 08:22:22 +0900 Subject: [PATCH] =?UTF-8?q?[FE][Refactor]=20#224=20:=20=EC=A7=80=EB=8F=84?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 코드 가독성 개선 - 에러 케이스 추가 - 타입 분리 --- frontend/src/component/maps/Map.tsx | 41 +++++++---------- frontend/src/component/maps/Map.types.ts | 42 +++++++++++++++++ frontend/src/component/maps/NaverMap.tsx | 25 +++++++---- frontend/src/component/maps/mapUtils.ts | 32 +++++++++++++ frontend/src/component/maps/naverMapUtils.ts | 47 +------------------- 5 files changed, 107 insertions(+), 80 deletions(-) create mode 100644 frontend/src/component/maps/Map.types.ts create mode 100644 frontend/src/component/maps/mapUtils.ts diff --git a/frontend/src/component/maps/Map.tsx b/frontend/src/component/maps/Map.tsx index 6fee30b8..b042d038 100644 --- a/frontend/src/component/maps/Map.tsx +++ b/frontend/src/component/maps/Map.tsx @@ -1,14 +1,9 @@ -import { NaverMap } from '@/component/maps/NaverMap.tsx'; import { ReactNode, useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react'; +import { NaverMap } from '@/component/maps/NaverMap.tsx'; +import { IMapObject, IMapOptions, IMapRefMethods } from '@/component/maps/Map.types.ts'; import classNames from 'classnames'; -type IMapObject = naver.maps.Map | null; - -export interface IMapOptions { - lat: number; - lng: number; - zoom?: number; -} +const validateKindOfMap = (type: string) => ['naver'].includes(type); interface IMapProps extends IMapOptions { className?: string; @@ -16,21 +11,14 @@ interface IMapProps extends IMapOptions { initMap: (mapObject: IMapObject) => void; } -// 부모 컴포넌트가 접근할 수 있는 메서드들을 정의한 인터페이스 -export interface IMapRefMethods { - getMapObject: () => naver.maps.Map | null; - getMapContainer: () => HTMLElement | null; - onMouseClickHandler: (event?: React.MouseEvent) => void; -} - -const validateKindOfMap = (type: string) => ['naver'].includes(type); - export const Map = forwardRef((props, ref) => { - if (!validateKindOfMap(props.type)) throw new Error('Invalid map type'); + if (!validateKindOfMap(props.type)) + throw new Error('🚀 지도 로딩 오류 : 알 수 없는 지도 타입이 인자로 들어 왔습니다.'); - const mapRef = useRef(null); + const mapRefMethods = useRef(null); const mapContainer = useRef(null); - const [mapObject, setMapObject] = useState(null); + + const [mapObject, setMapObject] = useState(); const [MapComponent, setMapComponent] = useState(); const onMapInit = (mapObj: IMapObject) => { @@ -44,21 +32,24 @@ export const Map = forwardRef((props, ref) => { lat={props.lat} lng={props.lng} zoom={props.zoom} - ref={mapRef} + ref={mapRefMethods} onMapInit={onMapInit} /> ); setMapComponent(mapComponent); } - }, [props.lat, props.lng, props.zoom, props.type]); + }, []); useEffect(() => { - mapContainer.current = mapRef.current?.getMapContainer() ?? null; - props.initMap(mapObject); + mapContainer.current = mapRefMethods.current?.getMapContainer() ?? null; + if (mapObject) props.initMap(mapObject); }, [mapObject]); useImperativeHandle(ref, () => ({ - getMapObject: () => mapObject, + getMapObject: () => { + if (mapObject) return mapObject; + throw new Error('🚀 지도 로딩 오류 : 지도 객체가 존재하지 않습니다.'); + }, getMapContainer: () => mapContainer.current, onMouseClickHandler: () => {}, })); diff --git a/frontend/src/component/maps/Map.types.ts b/frontend/src/component/maps/Map.types.ts new file mode 100644 index 00000000..83089b24 --- /dev/null +++ b/frontend/src/component/maps/Map.types.ts @@ -0,0 +1,42 @@ +export type IMapObject = naver.maps.Map; + +export type IMapLatLngBound = naver.maps.LatLngBounds; + +export interface IMapOptions { + lat: number; + lng: number; + zoom?: number; +} + +/** + * Forward Ref 를 통해서, 부모에서 자식 컴포넌트의 Ref에 접근할 때 쓰이는 인터페이스. + * 다음과 같은 목적으로 쓰인다. + * 1. 지도 컨테이너 요소와, 지도 객체를 가져온다. + * 2. 지도 객체의 이벤트 위임을 위해 자신을 컨트롤 할 수 있는 Handler를 부모에게 전달하는 역할을 한다. + * 이렇게 전달받은 핸들러로 부모 컴포넌트에서 자식에 있는 지도 객체를 컨트롤 할 수 있다. + * */ +export interface IMapRefMethods { + getMapObject: () => naver.maps.Map | null; + getMapContainer: () => HTMLElement | null; + onMouseClickHandler: (event?: React.MouseEvent) => void; +} + +// lat: 위도(y), lng: 경도(x) INaverMapVertexPosition +export interface IMapVertexCoordinate { + ne: { + lng: number; + lat: number; + }; + nw: { + lng: number; + lat: number; + }; + se: { + lng: number; + lat: number; + }; + sw: { + lng: number; + lat: number; + }; +} diff --git a/frontend/src/component/maps/NaverMap.tsx b/frontend/src/component/maps/NaverMap.tsx index 72cdfa03..0f9049d0 100644 --- a/frontend/src/component/maps/NaverMap.tsx +++ b/frontend/src/component/maps/NaverMap.tsx @@ -1,14 +1,15 @@ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { setNaverMapSync } from '@/component/maps/naverMapUtils.ts'; -import { IMapOptions, IMapRefMethods } from '@/component/maps/Map.tsx'; +import { IMapOptions, IMapRefMethods } from '@/component/maps/Map.types.ts'; interface INaverMapProps extends IMapOptions { onMapInit: (map: naver.maps.Map) => void; // 콜백 프로퍼티 추가 } export const NaverMap = forwardRef((props, ref) => { - const naverMapObject = useRef(null); - const naverMapContainer = useRef(null); + const mapObject = useRef(null); + const mapContainer = useRef(null); + const [mapOptions, setMapOptions] = useState({ lat: props.lat, lng: props.lng, @@ -24,17 +25,23 @@ export const NaverMap = forwardRef((props, ref) }, [props.lat, props.lng, props.zoom]); useEffect(() => { - if (naverMapContainer.current && mapOptions !== null) { - naverMapObject.current = setNaverMapSync(naverMapContainer.current, mapOptions); - if (naverMapObject.current !== null) props.onMapInit(naverMapObject.current); // 콜백 호출 + if (mapContainer.current && mapOptions) { + mapObject.current = setNaverMapSync(mapContainer.current, mapOptions); + if (mapObject.current) props.onMapInit(mapObject.current); // 콜백 호출 } }, [mapOptions]); useImperativeHandle(ref, () => ({ - getMapObject: () => naverMapObject.current, - getMapContainer: () => naverMapContainer.current, + getMapObject: () => { + if (mapObject) return mapObject.current; + throw new Error('🚀 지도 로딩 오류 : 지도 객체가 존재하지 않습니다.'); + }, + getMapContainer: () => { + if (mapContainer) return mapContainer.current; + throw new Error('🚀 지도 로딩 오류 : 지도 컨테이너가 존재하지 않습니다.'); + }, onMouseClickHandler: () => {}, })); - return
; + return
; }); diff --git a/frontend/src/component/maps/mapUtils.ts b/frontend/src/component/maps/mapUtils.ts new file mode 100644 index 00000000..821341ac --- /dev/null +++ b/frontend/src/component/maps/mapUtils.ts @@ -0,0 +1,32 @@ +import { IMapVertexCoordinate, IMapObject, IMapLatLngBound } from '@/component/maps/Map.types.ts'; + +export const getMapVertexCoordinate = (map: IMapObject): IMapVertexCoordinate => { + let bounds: IMapLatLngBound; + + if (map instanceof naver.maps.Map) { + bounds = map.getBounds() as naver.maps.LatLngBounds; + } else { + throw new Error('🚀 꼭지점 좌표 가져오기 오류 : 지도 객체가 없습니다.'); + } + + const sw = bounds.getSW(); + const ne = bounds.getNE(); + return { + se: { + lng: ne.lng(), + lat: sw.lat(), + }, + sw: { + lng: sw.lng(), + lat: sw.lat(), + }, + ne: { + lng: ne.lng(), + lat: ne.lat(), + }, + nw: { + lng: sw.lng(), + lat: ne.lat(), + }, + }; +}; diff --git a/frontend/src/component/maps/naverMapUtils.ts b/frontend/src/component/maps/naverMapUtils.ts index 4a6788e9..8de7b4e1 100644 --- a/frontend/src/component/maps/naverMapUtils.ts +++ b/frontend/src/component/maps/naverMapUtils.ts @@ -1,48 +1,4 @@ -import { IMapOptions } from '@/component/maps/Map.tsx'; - -// lat: 위도(y), lng: 경도(x) -export interface INaverMapVertexPosition { - ne: { - lng: number; - lat: number; - }; - nw: { - lng: number; - lat: number; - }; - se: { - lng: number; - lat: number; - }; - sw: { - lng: number; - lat: number; - }; -} - -export const getNaverMapVertexPosition = (map: naver.maps.Map): INaverMapVertexPosition => { - const bounds = map.getBounds() as naver.maps.LatLngBounds; - const sw = bounds.getSW(); - const ne = bounds.getNE(); - return { - se: { - lng: ne.lng(), - lat: sw.lat(), - }, - sw: { - lng: sw.lng(), - lat: sw.lat(), - }, - ne: { - lng: ne.lng(), - lat: ne.lat(), - }, - nw: { - lng: sw.lng(), - lat: ne.lat(), - }, - }; -}; +import { IMapOptions } from '@/component/maps/Map.types.ts'; export const setNaverMapOption = (mapOptions: IMapOptions): IMapOptions => { return { @@ -53,7 +9,6 @@ export const setNaverMapOption = (mapOptions: IMapOptions): IMapOptions => { }; }; -// utils에 있는 일반 함수로 사용 export const setNaverMap = ( htmlElement: HTMLElement, mapOptions: IMapOptions,