Skip to content

Commit

Permalink
Merge branch 'frontend' into feature/fe/#249-canvasMap
Browse files Browse the repository at this point in the history
  • Loading branch information
happyhyep authored Nov 25, 2024
2 parents a8f0c16 + 7a0f842 commit 0f28fa5
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 58 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

- name: Run Build
run: pnpm build # pnpm์„ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ ์‹คํ–‰
continue-on-error: false # ๋นŒ๋“œ ์‹คํŒจ ์‹œ ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํŒจ๋กœ ์ฒ˜๋ฆฌ
# continue-on-error: false # ๋นŒ๋“œ ์‹คํŒจ ์‹œ ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํŒจ๋กœ ์ฒ˜๋ฆฌ

- name: Run Tests
run: pnpm test # pnpm์„ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์‹คํ–‰
Expand Down
1 change: 1 addition & 0 deletions frontend/src/component/canvas/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,4 @@
// </div>
// );
// });

2 changes: 1 addition & 1 deletion frontend/src/component/canvas/CanvasWithMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@
// />
// </div>
// );
// };
// };
86 changes: 86 additions & 0 deletions frontend/src/component/canvas/useEventHandlers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useRef } from 'react';
import { ICanvasRefMethods } from '@/component/canvas/Canvas.tsx';
import { IMapObject, IMapRefMethods } from '@/component/maps/Map.types.ts';

// TODO: ๋ฆฌํŒฉํ† ๋ฃ… ์‹œ null์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ
interface IUseEventHandlers {
(
canvasElement: HTMLCanvasElement | null,
canvasRefMethods: ICanvasRefMethods | null,
mapElement: HTMLElement | null,
mapRefMethods: IMapRefMethods | null,
mapObject: IMapObject | null, // ๋น„๋™๊ธฐ ๋กœ๋”ฉ ์‹œ null๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด์„œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ ํ•„์š”
): {
handleClick: (event: React.MouseEvent) => void;
handleMouseDown: (event: React.MouseEvent) => void;
handleMouseMove: (event: React.MouseEvent) => void;
handleMouseUp: (event: React.MouseEvent) => void;
};
}

interface IMouseEventState {
isMouseDown: boolean;
mouseDownPosition: { x: number; y: number };
// mouseMovePosition: { x: number; y: number };
mouseDeltaPosition: { x: number; y: number };
}

const MouseEventStateInitialValue = {
isMouseDown: false,
mouseDownPosition: { x: 0, y: 0 },
// mouseMovePosition: { x: 0, y: 0 },
mouseDeltaPosition: { x: 0, y: 0 },
};

export const useEventHandlers: IUseEventHandlers = (
canvasElement,
canvasRefMethods,
mapElement,
mapRefMethods,
mapObject,
) => {
// if (!canvasElement || !canvasElement || !mapElement || !mapRefMethods || !mapObject)
// throw new Error('๐Ÿš€ useEventHandler error : null ๊ฐ’์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.');

const mouseEventState = useRef<IMouseEventState>({ ...MouseEventStateInitialValue });

const handleClick = (event: React.MouseEvent) => {
mapRefMethods?.onMouseClickHandler(event);
canvasRefMethods?.onMouseClickHandler(event);
};

const handleMouseDown = (event: React.MouseEvent) => {
if (!mapElement || !canvasElement) return;
mouseEventState.current.isMouseDown = true;
mouseEventState.current.mouseDownPosition = { x: event.clientX, y: event.clientY };
canvasRefMethods?.onMouseDownHandler(event);
};

const handleMouseMove = (event: React.MouseEvent) => {
if (!mapElement || !canvasElement || !mouseEventState.current.isMouseDown) return;

// TODO: ์“ฐ๋กœํ‹€๋ง ๊ฑธ๊ธฐ
mouseEventState.current.mouseDeltaPosition = {
x: -(event.clientX - mouseEventState.current.mouseDownPosition.x),
y: -(event.clientY - mouseEventState.current.mouseDownPosition.y),
};

// TODO: ๋ฒ”์šฉ ์ง€๋„์— ๋”ฐ๋ฅธ Refactoring ํ•„์š”, ์šฐ์„ ์€ ๋„ค์ด๋ฒ„ ์ง€๋„์— ํ•œํ•ด์„œ๋งŒ ์ˆ˜ํ–‰
mapObject?.panBy(
new naver.maps.Point(
mouseEventState.current.mouseDeltaPosition.x,
mouseEventState.current.mouseDeltaPosition.y,
),
);

canvasRefMethods?.onMouseMoveHandler(event);
};

const handleMouseUp = (event: React.MouseEvent) => {
if (!mapElement || !canvasElement) return;
mouseEventState.current = { ...MouseEventStateInitialValue };
canvasRefMethods?.onMouseUpHandler(event);
};

return { handleClick, handleMouseDown, handleMouseMove, handleMouseUp };
};
2 changes: 1 addition & 1 deletion frontend/src/component/maps/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,4 @@
// {MapComponent}
// </article>
// );
// });
// });
42 changes: 42 additions & 0 deletions frontend/src/component/maps/Map.types.ts
Original file line number Diff line number Diff line change
@@ -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;
};
}
25 changes: 16 additions & 9 deletions frontend/src/component/maps/NaverMap.tsx
Original file line number Diff line number Diff line change
@@ -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<IMapRefMethods, INaverMapProps>((props, ref) => {
const naverMapObject = useRef<naver.maps.Map | null>(null);
const naverMapContainer = useRef<HTMLElement | null>(null);
const mapObject = useRef<naver.maps.Map | null>(null);
const mapContainer = useRef<HTMLElement | null>(null);

const [mapOptions, setMapOptions] = useState<IMapOptions>({
lat: props.lat,
lng: props.lng,
Expand All @@ -24,17 +25,23 @@ export const NaverMap = forwardRef<IMapRefMethods, INaverMapProps>((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 <section ref={naverMapContainer} className="h-full w-full" />;
return <section ref={mapContainer} className="h-full w-full" />;
});
32 changes: 32 additions & 0 deletions frontend/src/component/maps/mapUtils.ts
Original file line number Diff line number Diff line change
@@ -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(),
},
};
};
47 changes: 1 addition & 46 deletions frontend/src/component/maps/naverMapUtils.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -53,7 +9,6 @@ export const setNaverMapOption = (mapOptions: IMapOptions): IMapOptions => {
};
};

// utils์— ์žˆ๋Š” ์ผ๋ฐ˜ ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉ
export const setNaverMap = (
htmlElement: HTMLElement,
mapOptions: IMapOptions,
Expand Down

0 comments on commit 0f28fa5

Please sign in to comment.