From b1e7ae39ff8274d31a89a5f6079cc0a145b53c68 Mon Sep 17 00:00:00 2001 From: leemirae <416homin@daum.net> Date: Mon, 20 Nov 2023 17:55:13 +0900 Subject: [PATCH 1/6] Feat : Connect naver-map-api --- mern_client/public/index.html | 57 +++++++------------ mern_client/src/components/MapContainer.tsx | 8 +++ mern_client/src/components/common/Block.tsx | 1 - mern_client/src/components/common/Button.tsx | 1 + .../src/components/common/Map/index.tsx | 22 +++++++ mern_client/src/components/common/Span.tsx | 2 +- mern_client/src/index.tsx | 4 +- mern_client/src/pages/Home/index.tsx | 2 + mern_client/src/styles/GlobalStyles.ts | 6 -- 9 files changed, 55 insertions(+), 48 deletions(-) create mode 100644 mern_client/src/components/MapContainer.tsx create mode 100644 mern_client/src/components/common/Map/index.tsx diff --git a/mern_client/public/index.html b/mern_client/public/index.html index aa069f2..4dd70a0 100644 --- a/mern_client/public/index.html +++ b/mern_client/public/index.html @@ -1,43 +1,24 @@ - - - - - - - - - - - React App - - - -
- - - + + + + \ No newline at end of file diff --git a/mern_client/src/components/MapContainer.tsx b/mern_client/src/components/MapContainer.tsx new file mode 100644 index 0000000..4613ea5 --- /dev/null +++ b/mern_client/src/components/MapContainer.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import Map from "./common/Map/index"; + +function MapContainer() { + return ; +} + +export default MapContainer; diff --git a/mern_client/src/components/common/Block.tsx b/mern_client/src/components/common/Block.tsx index 38bc85a..3e684f3 100644 --- a/mern_client/src/components/common/Block.tsx +++ b/mern_client/src/components/common/Block.tsx @@ -11,7 +11,6 @@ const StyledBlock = styled.div` height: ${(props) => props.height}; cursor: ${(props) => props.onClick && "pointer"}; `; - function Block({ height, onClick }: BlockProps) { return ; } diff --git a/mern_client/src/components/common/Button.tsx b/mern_client/src/components/common/Button.tsx index bfdb743..c4a7be9 100644 --- a/mern_client/src/components/common/Button.tsx +++ b/mern_client/src/components/common/Button.tsx @@ -20,6 +20,7 @@ const StyledButton = styled.button` display: flex; align-items: center; justify-content: center; + background: none; padding: 0; cursor: pointer; diff --git a/mern_client/src/components/common/Map/index.tsx b/mern_client/src/components/common/Map/index.tsx new file mode 100644 index 0000000..cdb4d0a --- /dev/null +++ b/mern_client/src/components/common/Map/index.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { useEffect } from "react"; + +interface MapProps { + width: string; + height: string; +} + +function Map({ width, height }: MapProps) { + useEffect(() => { + const mapOptions = { + center: new naver.maps.LatLng(37.3595704, 127.105399), + zoom: 10, + }; + + const map = new naver.maps.Map("map", mapOptions); + }, []); + + return
; +} + +export default Map; diff --git a/mern_client/src/components/common/Span.tsx b/mern_client/src/components/common/Span.tsx index c4d50a0..a0d5912 100644 --- a/mern_client/src/components/common/Span.tsx +++ b/mern_client/src/components/common/Span.tsx @@ -13,6 +13,7 @@ interface SpanProps { const StyledSpan = styled.span` color: ${(props) => props.color || "black"}; + &.small { font-size: 0.8rem; } @@ -26,7 +27,6 @@ const StyledSpan = styled.span` font-weight: bold; } `; - function Span({ children, size = "normal", color }: SpanProps) { return ( diff --git a/mern_client/src/index.tsx b/mern_client/src/index.tsx index 51bdee4..cac390a 100644 --- a/mern_client/src/index.tsx +++ b/mern_client/src/index.tsx @@ -9,10 +9,10 @@ const root = ReactDOM.createRoot( document.getElementById("root") as HTMLElement ); root.render( - + <> - + ); reportWebVitals(); diff --git a/mern_client/src/pages/Home/index.tsx b/mern_client/src/pages/Home/index.tsx index bebd887..c46201a 100644 --- a/mern_client/src/pages/Home/index.tsx +++ b/mern_client/src/pages/Home/index.tsx @@ -1,10 +1,12 @@ import React from "react"; import Navigation from "../../components/Navigation"; +import MapContainer from "../../components/MapContainer"; function Home() { return (
+
); } diff --git a/mern_client/src/styles/GlobalStyles.ts b/mern_client/src/styles/GlobalStyles.ts index afc92f3..cd99651 100644 --- a/mern_client/src/styles/GlobalStyles.ts +++ b/mern_client/src/styles/GlobalStyles.ts @@ -3,28 +3,22 @@ import reset from "styled-reset"; const GlobalStyles = createGlobalStyle` ${reset} - a { text-decoration: none; color : black; } - * { box-sizing: border-box; } - html,body { height: 100%; } - #root { height: 100%; } - input:focus { outline : none; } - `; export default GlobalStyles; From cd9368d2777b18f41050c9de25e96d55bafd423e Mon Sep 17 00:00:00 2001 From: Lee Mi Rae <132829711+future9061@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:41:48 +0900 Subject: [PATCH 2/6] Revert "Feat : Connect naver-map-api" From 35eed0053a13a5da75fec725174cde19a10224b9 Mon Sep 17 00:00:00 2001 From: leemirae <416homin@daum.net> Date: Fri, 24 Nov 2023 17:44:39 +0900 Subject: [PATCH 3/6] Feat : Add jotai Library --- mern_client/src/atoms/map.ts | 4 ++++ mern_client/src/components/MapContainer.tsx | 13 ++++++++++++- mern_client/src/components/common/Map/index.tsx | 7 ++++++- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 mern_client/src/atoms/map.ts diff --git a/mern_client/src/atoms/map.ts b/mern_client/src/atoms/map.ts new file mode 100644 index 0000000..1aa5173 --- /dev/null +++ b/mern_client/src/atoms/map.ts @@ -0,0 +1,4 @@ +import React from "react"; +import { atom } from "jotai"; + +export const mapAtom = atom(null); diff --git a/mern_client/src/components/MapContainer.tsx b/mern_client/src/components/MapContainer.tsx index 4613ea5..5bde3b9 100644 --- a/mern_client/src/components/MapContainer.tsx +++ b/mern_client/src/components/MapContainer.tsx @@ -1,8 +1,19 @@ import React from "react"; import Map from "./common/Map/index"; +import { useSetAtom } from "jotai"; +import { mapAtom } from "../atoms/map"; function MapContainer() { - return ; + const setMap = useSetAtom(mapAtom); + + const initMap = (map: naver.maps.Map) => { + setMap(map); + naver.maps.Event.addListener(map, "click", () => { + console.log("맵 클릭!"); + }); + }; + + return ; } export default MapContainer; diff --git a/mern_client/src/components/common/Map/index.tsx b/mern_client/src/components/common/Map/index.tsx index cdb4d0a..9e8df67 100644 --- a/mern_client/src/components/common/Map/index.tsx +++ b/mern_client/src/components/common/Map/index.tsx @@ -4,9 +4,10 @@ import { useEffect } from "react"; interface MapProps { width: string; height: string; + initMap?: (map: naver.maps.Map) => void; } -function Map({ width, height }: MapProps) { +function Map({ width, height, initMap }: MapProps) { useEffect(() => { const mapOptions = { center: new naver.maps.LatLng(37.3595704, 127.105399), @@ -14,6 +15,10 @@ function Map({ width, height }: MapProps) { }; const map = new naver.maps.Map("map", mapOptions); + + if (initMap) { + initMap(map); + } }, []); return
; From ef2c0214269503c20ce42c92e99c58558b948e42 Mon Sep 17 00:00:00 2001 From: leemirae <416homin@daum.net> Date: Fri, 24 Nov 2023 20:21:57 +0900 Subject: [PATCH 4/6] Feat : Add marker --- mern_client/src/atoms/info.ts | 7 +++ .../src/components/MarkersContainer.tsx | 43 +++++++++++++++++++ .../src/components/common/Marker/Marker.css | 11 +++++ .../src/components/common/Marker/index.tsx | 39 +++++++++++++++++ mern_client/src/data/infors.ts | 29 +++++++++++++ mern_client/src/pages/Home/index.tsx | 11 +++++ mern_client/src/types/info.ts | 9 ++++ 7 files changed, 149 insertions(+) create mode 100644 mern_client/src/atoms/info.ts create mode 100644 mern_client/src/components/MarkersContainer.tsx create mode 100644 mern_client/src/components/common/Marker/Marker.css create mode 100644 mern_client/src/components/common/Marker/index.tsx create mode 100644 mern_client/src/data/infors.ts create mode 100644 mern_client/src/types/info.ts diff --git a/mern_client/src/atoms/info.ts b/mern_client/src/atoms/info.ts new file mode 100644 index 0000000..b7cb738 --- /dev/null +++ b/mern_client/src/atoms/info.ts @@ -0,0 +1,7 @@ +import { atom } from "jotai"; +import { info } from "../types/info"; + +//위치 데이터 전역으로 쓰기 위해 +export const infosAtom = atom(null); +//선택한 마커의 데이터 가져와서 저장 +export const selectInfoAtom = atom(null); diff --git a/mern_client/src/components/MarkersContainer.tsx b/mern_client/src/components/MarkersContainer.tsx new file mode 100644 index 0000000..4efdb77 --- /dev/null +++ b/mern_client/src/components/MarkersContainer.tsx @@ -0,0 +1,43 @@ +import { useAtom, useAtomValue } from "jotai"; +import React from "react"; +import { mapAtom } from "../atoms/map"; +import { infosAtom, selectInfoAtom } from "../atoms/info"; +import { info } from "../types/info"; +import Marker from "./common/Marker"; + +function MarkersContainer() { + const map = useAtomValue(mapAtom); + const infos = useAtomValue(infosAtom); + const [selectInfo, setSelectInfo] = useAtom(selectInfoAtom); + + if (!map || !infos) return null; + + return ( + <> + {infos.map((info: info) => ( + "} + onClick={() => { + setSelectInfo(info); + }} + /> + ))} + {selectInfo && ( + "} + onClick={() => { + setSelectInfo(null); + }} + /> + )} + + ); +} + +export default MarkersContainer; diff --git a/mern_client/src/components/common/Marker/Marker.css b/mern_client/src/components/common/Marker/Marker.css new file mode 100644 index 0000000..10eb606 --- /dev/null +++ b/mern_client/src/components/common/Marker/Marker.css @@ -0,0 +1,11 @@ +.marker { + width: 20px; + height: 20px; + background: red; + border-radius: 10px; + border: 1px solid rgba(0, 0, 0, 0.4); +} + +.marker.select { + background: blue; +} diff --git a/mern_client/src/components/common/Marker/index.tsx b/mern_client/src/components/common/Marker/index.tsx new file mode 100644 index 0000000..4498757 --- /dev/null +++ b/mern_client/src/components/common/Marker/index.tsx @@ -0,0 +1,39 @@ +import React, { useEffect } from "react"; +import "./Marker.css"; + +interface MarkerProps { + map: naver.maps.Map; + position: { + lat: number; + lng: number; + }; + content: string; + onClick?: () => void; +} + +function Marker({ map, position, content, onClick }: MarkerProps) { + useEffect(() => { + let marker: naver.maps.Marker | null = null; + + if (map) { + marker = new naver.maps.Marker({ + map, + position: new naver.maps.LatLng(position), + icon: { content }, + }); + } + + if (onClick) { + naver.maps.Event.addListener(marker, "click", onClick); + map.panTo(position); + } + + return () => { + marker?.setMap(null); + }; + }, [map]); + + return null; +} + +export default Marker; diff --git a/mern_client/src/data/infors.ts b/mern_client/src/data/infors.ts new file mode 100644 index 0000000..b003b37 --- /dev/null +++ b/mern_client/src/data/infors.ts @@ -0,0 +1,29 @@ +export const infos = [ + { + id: 1, + addressName: "서울 용산구 동자동 43-205", + placeName: "서울역", + position: { + lat: 37.5546788388674, + lng: 126.970606917394, + }, + }, + { + id: 2, + addressName: "서울 강남구 역삼동 858", + placeName: "강남역 2호선", + position: { + lat: 37.49808633653005, + lng: 127.02800140627488, + }, + }, + { + id: 3, + addressName: "서울 관악구 봉천동 979-2", + placeName: "서울대입구역 2호선", + position: { + lat: 37.4812845080678, + lng: 126.952713197762, + }, + }, +]; diff --git a/mern_client/src/pages/Home/index.tsx b/mern_client/src/pages/Home/index.tsx index c46201a..9fd182f 100644 --- a/mern_client/src/pages/Home/index.tsx +++ b/mern_client/src/pages/Home/index.tsx @@ -1,12 +1,23 @@ import React from "react"; import Navigation from "../../components/Navigation"; import MapContainer from "../../components/MapContainer"; +import { infosAtom } from "../../atoms/info"; +import { useSetAtom } from "jotai"; +import { infos } from "../../data/infors"; +import MarkersContainer from "../../components/MarkersContainer"; function Home() { + const setInfos = useSetAtom(infosAtom); + + if (infos) { + setInfos(infos); + } + return (
+
); } diff --git a/mern_client/src/types/info.ts b/mern_client/src/types/info.ts new file mode 100644 index 0000000..c5da703 --- /dev/null +++ b/mern_client/src/types/info.ts @@ -0,0 +1,9 @@ +export type info = { + id: number; + addressName: string; + placeName: string; + position: { + lat: number; + lng: number; + }; +}; From b9f01463628df34318397819609d8318bfcf0809 Mon Sep 17 00:00:00 2001 From: leemirae <416homin@daum.net> Date: Thu, 30 Nov 2023 17:31:14 +0900 Subject: [PATCH 5/6] Feat : Add infoWindow --- mern_client/src/atoms/info.ts | 6 +- mern_client/src/components/MapContainer.tsx | 5 +- .../src/components/MarkersContainer.tsx | 7 +- .../common/infoWindow/InfoWindow.css | 29 ++++++++ .../components/common/infoWindow/index.tsx | 70 +++++++++++++++++++ mern_client/src/types/info.ts | 2 +- 6 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 mern_client/src/components/common/infoWindow/InfoWindow.css create mode 100644 mern_client/src/components/common/infoWindow/index.tsx diff --git a/mern_client/src/atoms/info.ts b/mern_client/src/atoms/info.ts index b7cb738..3c0ddd8 100644 --- a/mern_client/src/atoms/info.ts +++ b/mern_client/src/atoms/info.ts @@ -1,7 +1,7 @@ import { atom } from "jotai"; -import { info } from "../types/info"; +import { Info } from "../types/info"; //위치 데이터 전역으로 쓰기 위해 -export const infosAtom = atom(null); +export const infosAtom = atom(null); //선택한 마커의 데이터 가져와서 저장 -export const selectInfoAtom = atom(null); +export const selectInfoAtom = atom(null); diff --git a/mern_client/src/components/MapContainer.tsx b/mern_client/src/components/MapContainer.tsx index 5bde3b9..3719d84 100644 --- a/mern_client/src/components/MapContainer.tsx +++ b/mern_client/src/components/MapContainer.tsx @@ -2,14 +2,15 @@ import React from "react"; import Map from "./common/Map/index"; import { useSetAtom } from "jotai"; import { mapAtom } from "../atoms/map"; +import { selectInfoAtom } from "../atoms/info"; function MapContainer() { const setMap = useSetAtom(mapAtom); - + const setSelectInfo = useSetAtom(selectInfoAtom); const initMap = (map: naver.maps.Map) => { setMap(map); naver.maps.Event.addListener(map, "click", () => { - console.log("맵 클릭!"); + setSelectInfo(null); }); }; diff --git a/mern_client/src/components/MarkersContainer.tsx b/mern_client/src/components/MarkersContainer.tsx index 4efdb77..66db859 100644 --- a/mern_client/src/components/MarkersContainer.tsx +++ b/mern_client/src/components/MarkersContainer.tsx @@ -2,8 +2,10 @@ import { useAtom, useAtomValue } from "jotai"; import React from "react"; import { mapAtom } from "../atoms/map"; import { infosAtom, selectInfoAtom } from "../atoms/info"; -import { info } from "../types/info"; + import Marker from "./common/Marker"; +import { Info } from "../types/info"; +import InfoWindow from "./common/infoWindow"; function MarkersContainer() { const map = useAtomValue(mapAtom); @@ -14,7 +16,7 @@ function MarkersContainer() { return ( <> - {infos.map((info: info) => ( + {infos.map((info: Info) => ( )} + ); } diff --git a/mern_client/src/components/common/infoWindow/InfoWindow.css b/mern_client/src/components/common/infoWindow/InfoWindow.css new file mode 100644 index 0000000..f840218 --- /dev/null +++ b/mern_client/src/components/common/infoWindow/InfoWindow.css @@ -0,0 +1,29 @@ +.infoBox { + padding: 20px; + border: 1px solid rgba(0, 0, 0, 0.4); + background: #ffffff; + margin: 0; + box-sizing: border-box; + border-radius: 10px; +} + +.infoPlaceName { + font-size: 14px; + font-weight: bolder; +} + +.infoAddressName { + font-size: 12px; + font-weight: normal; +} + +.infoSubmit { + font-size: 14px; + text-align: center; + font-weight: bold; + cursor: pointer; + color: #ffffff; + background: #0f2cc5; + margin-top: 6px; + padding: 6px 0; +} diff --git a/mern_client/src/components/common/infoWindow/index.tsx b/mern_client/src/components/common/infoWindow/index.tsx new file mode 100644 index 0000000..df5d0aa --- /dev/null +++ b/mern_client/src/components/common/infoWindow/index.tsx @@ -0,0 +1,70 @@ +import { useEffect, useState } from "react"; +import { Info } from "../../../types/info"; +import "./InfoWindow.css"; + +interface InfoWindowProps { + map: naver.maps.Map; + selectInfo: Info | null; + onSubmit?: () => void; +} + +function InfoWindow({ map, selectInfo, onSubmit }: InfoWindowProps) { + const [infoWindow, setInfoWindow] = useState( + null + ); + + useEffect(() => { + const _infoWindow = new naver.maps.InfoWindow({ + content: "", + backgroundColor: "transparent", + borderWidth: 0, + disableAnchor: true, + pixelOffset: new naver.maps.Point(10, -20), + }); + + setInfoWindow(_infoWindow); + + return () => { + _infoWindow?.setMap(null); + }; + }, []); + + useEffect(() => { + if (!infoWindow || !map) return; + if (selectInfo) { + infoWindow.setContent(InfoWindowMaker(selectInfo, onSubmit)); + infoWindow.open(map, selectInfo.position); + } else { + infoWindow.close(); + } + }, [selectInfo]); + + return null; +} + +function InfoWindowMaker(selectInfo: Info, onSubmit?: () => void) { + const infoWindowBox = document.createElement("div"); + infoWindowBox.className = "infoBox"; + + const infoWindowPlace = document.createElement("div"); + infoWindowPlace.className = "infoPlaceName"; + infoWindowPlace.innerHTML = `${selectInfo.placeName}`; + infoWindowBox.appendChild(infoWindowPlace); + + const infoWindowAddress = document.createElement("div"); + infoWindowAddress.className = "infoAddressName"; + infoWindowAddress.innerHTML = `${selectInfo.addressName}`; + infoWindowBox.appendChild(infoWindowAddress); + + if (onSubmit) { + const infoWindowButton = document.createElement("div"); + infoWindowButton.className = "infoSubmit"; + infoWindowButton.innerHTML = "등록"; + infoWindowButton.onclick = onSubmit; + infoWindowBox.appendChild(infoWindowButton); + } + + return infoWindowBox; +} + +export default InfoWindow; diff --git a/mern_client/src/types/info.ts b/mern_client/src/types/info.ts index c5da703..e8db084 100644 --- a/mern_client/src/types/info.ts +++ b/mern_client/src/types/info.ts @@ -1,4 +1,4 @@ -export type info = { +export type Info = { id: number; addressName: string; placeName: string; From f87171148fdb9923ed19e6c817d37731edd51a04 Mon Sep 17 00:00:00 2001 From: leemirae <416homin@daum.net> Date: Fri, 1 Dec 2023 14:09:23 +0900 Subject: [PATCH 6/6] Feat : Add search --- mern_client/src/atoms/search.ts | 3 ++ mern_client/src/components/Navigation.tsx | 59 +++++++++++++++++---- mern_client/src/components/common/Input.tsx | 6 --- mern_client/src/hooks/useInput.ts | 13 +++++ mern_client/src/pages/Upload/index.tsx | 11 +++- 5 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 mern_client/src/atoms/search.ts create mode 100644 mern_client/src/hooks/useInput.ts diff --git a/mern_client/src/atoms/search.ts b/mern_client/src/atoms/search.ts new file mode 100644 index 0000000..50d1dad --- /dev/null +++ b/mern_client/src/atoms/search.ts @@ -0,0 +1,3 @@ +import { atom } from "jotai"; + +export const selectAtom = atom(false); diff --git a/mern_client/src/components/Navigation.tsx b/mern_client/src/components/Navigation.tsx index 5f20a36..eda3a62 100644 --- a/mern_client/src/components/Navigation.tsx +++ b/mern_client/src/components/Navigation.tsx @@ -1,22 +1,63 @@ -import React from "react"; +import React, { useCallback } from "react"; import ShadowBox from "./common/ShadowBox"; import Button from "./common/Button"; import Span from "./common/Span"; import Divider from "./common/Divider"; import Block from "./common/Block"; import { GoPlus } from "react-icons/go"; +import { useAtom } from "jotai"; +import { selectAtom } from "../atoms/search"; +import { FaArrowLeft, FaSearch } from "react-icons/fa"; +import Input from "./common/Input"; +import useInput from "../hooks/useInput"; + +interface NavigationProps { + type?: "home" | "upload"; +} + +function Navigation({ type = "home" }) { + const [select, setSelect] = useAtom(selectAtom); + const { value, onChange } = useInput(""); + + const onChangeSelect = useCallback(() => { + setSelect(!select); + }, [select, setSelect]); + + const onSubmit = useCallback(() => { + console.log(value); + }, [value]); -function Navigation() { return ( - + {type === "upload" && select ? ( + + ) : ( + + )} + - - + {select ? ( + + ) : ( + + )} + + {type === "upload" ? ( + + ) : ( + + )} ); } diff --git a/mern_client/src/components/common/Input.tsx b/mern_client/src/components/common/Input.tsx index e156080..7cc49a4 100644 --- a/mern_client/src/components/common/Input.tsx +++ b/mern_client/src/components/common/Input.tsx @@ -1,12 +1,6 @@ import React from "react"; import styled from "styled-components"; -//children -//name -//value -//onChange -//onSubmit - interface InputProps { children?: React.ReactNode; name?: string; diff --git a/mern_client/src/hooks/useInput.ts b/mern_client/src/hooks/useInput.ts new file mode 100644 index 0000000..6dea464 --- /dev/null +++ b/mern_client/src/hooks/useInput.ts @@ -0,0 +1,13 @@ +import { useCallback, useState } from "react"; + +function useInput(initalForm: string) { + const [value, setValue] = useState(initalForm); + const onChange = useCallback((e: React.ChangeEvent) => { + const { value } = e.target; + setValue(value); + }, []); + + return { value, onChange }; +} + +export default useInput; diff --git a/mern_client/src/pages/Upload/index.tsx b/mern_client/src/pages/Upload/index.tsx index f42a907..8fdcee8 100644 --- a/mern_client/src/pages/Upload/index.tsx +++ b/mern_client/src/pages/Upload/index.tsx @@ -1,7 +1,16 @@ import React from "react"; +import Navigation from "../../components/Navigation"; +import MapContainer from "../../components/MapContainer"; +import MarkersContainer from "../../components/MarkersContainer"; function Upload() { - return
Upload
; + return ( + <> + + + + + ); } export default Upload;