From 3675419dadad90ded4f6e8ec8f2bac6adccbcdc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=80=EB=AF=BC?= <108014449+stopmin@users.noreply.github.com> Date: Sun, 21 Jul 2024 05:41:15 +0900 Subject: [PATCH] =?UTF-8?q?=EC=82=AC=EB=9D=BC=EC=A7=80=EB=8A=94=20?= =?UTF-8?q?=EB=A7=88=EC=9D=84=20UI=20=EC=9E=91=EC=97=85=20(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 페이지 구성 #98 * feat: KakaoMapComponent.tsx 구성 #98 * build: kakao-sdk 의존성 추가 #98 * feat: 이미지 허용 도메인 추가 #98 * feat: 마을 페이지 UI 구현 #98 * feat: 마을 이야기 신청 버튼 추가 #98 * feat: 좌표 누락 추가 #98 * feat: 린트 적용 #98 * feat: 린트 적용 #98 * feat: 린트 적용 - 하.. 린트 너무 싫어 #98 * feat: 린트 적용 - 하.. 린트 너무 싫어 #98 * feat: 린트 적용 - 하.. 린트 너무 싫어 #98 * feat: 마을 이미지 수정 #98 * feat: 날짜 추가 #98 * feat: publishedAt 수정 #98 * refactor: 주석 제거 #98 * refactor: KakaoMapComponent.tsx 네이밍 수정 - Props 수정 - React.FC 삭제 #98 * refactor: lint 적용 - lint 이자식 #98 * feat: 랜더링 수정 #98 --- next.config.mjs | 4 + package.json | 1 + src/app/village/page.tsx | 167 ++++++++++++++++++++++++++++++++++++ src/components/KakaoMap.tsx | 96 +++++++++++++++++++++ src/constants/category.ts | 2 +- src/mocks/villages.ts | 59 +++++++++++++ src/types.ts | 10 +++ yarn.lock | 35 ++++---- 8 files changed, 354 insertions(+), 20 deletions(-) create mode 100644 src/app/village/page.tsx create mode 100644 src/components/KakaoMap.tsx create mode 100644 src/mocks/villages.ts diff --git a/next.config.mjs b/next.config.mjs index ff83652..d20245f 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -15,6 +15,10 @@ const nextConfig = { protocol: 'https', hostname: 'img.hankyung.com', }, + { + protocol: 'https', + hostname: 'search.pstatic.net', + }, ], }, }; diff --git a/package.json b/package.json index 0921d9f..d5cd283 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "react": "^18", "react-dom": "^18", "react-intersection-observer": "^9.13.0", + "react-kakao-maps-sdk": "^1.1.27", "react-plotly.js": "^2.6.0" }, "devDependencies": { diff --git a/src/app/village/page.tsx b/src/app/village/page.tsx new file mode 100644 index 0000000..696ebc2 --- /dev/null +++ b/src/app/village/page.tsx @@ -0,0 +1,167 @@ +'use client'; + +import { useEffect, useState } from 'react'; + +import { Box, CardMedia, Divider, Stack, Typography } from '@mui/material'; + +import CommentCard from '@/components/CommentCard'; +import GradientBox from '@/components/GradientBox'; +import KakaoMap from '@/components/KakaoMap'; +import NewsCardHorizontal from '@/components/NewsCardHorizontal'; +import NewsCardVertical from '@/components/NewsCardVertical'; +import color from '@/constants/color'; +import hiddenGems from '@/mocks/villages'; +import { Village } from '@/types'; + +const Page = () => { + const [villages] = useState(hiddenGems); + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + }, []); + + if (!isClient) { + return null; + } + + return ( + + + + + + 오늘의 마을 + + + {villages.slice(0, 3).map((village) => ( + + + + ))} + + + + 지도에서 보는 마을 + + + + + {villages.slice(3).map((filteredItem) => ( + + ))} + + + + + + + + + + 잊혀져 가는 마을,{'\n'}함께 지켜요! 🌿 + + + } + sx={{ textAlign: 'center', fontSize: 14 }} + /> + + + + 우리 마을 이야기, 경제를 새롭게 보다 + + + 우리는 경제 플랫폼으로서 새로운 시각으로 경제를 소개하고자 합니다. + + + + + 최근 ESG 문제와 경제 침체로 인해 사라지는 마을들에 주목하고 있습니다. MZ세대 여러분의 흥미를 끌기 위해 + "1인 가구", "숨겨진 명소", "여행" 등의 주제를 다루며, 이로 인해 발생하는 + 사회적 의미를 함께 살펴보고자 합니다. + + + + + 왜 이런 주제를 다루나요? + + + 경제 침체의 실체를 이해: 경제 침체가 지역 사회에 미치는 영향을 직접적으로 보여주며, 그 심각성을 + 알립니다. ESG 문제 해결: 환경, 사회, 지배구조(ESG) 문제를 이해하고, 그 해결을 위한 관심과 행동을 + 촉구합니다. MZ세대의 관심사 반영: 1인 가구, 숨겨진 명소, 여행 등 여러분이 좋아할 만한 주제를 통해 더욱 + 흥미롭게 접근합니다. + + + + + 기대 효과는 무엇인가요? + + + 경제적 긍정 변화: 사라지는 마을의 경제적 어려움을 알리고, 이를 극복하기 위한 아이디어를 공유합니다. + 사회적 의미 창출: 사라지는 마을의 의미를 기록하고, 후대에 전할 수 있습니다. + + + + + 어떻게 참여할 수 있나요? + + + 간단한 폼을 채우기만 하면, 생성형 AI와 다양한 데이터를 결합해 뉴스처럼 마을을 소개하는 기사를 + 만들어드립니다. 여러분의 손쉽고 간단한 참여가 큰 변화를 만들어낼 수 있습니다. + + + + + 경단의 마을 이야기, 함께 만들어요!{'\n'} + 여러분의 작은 관심이 큰 변화를 만듭니다. + + + + + + + ); +}; + +export default Page; diff --git a/src/components/KakaoMap.tsx b/src/components/KakaoMap.tsx new file mode 100644 index 0000000..92c6532 --- /dev/null +++ b/src/components/KakaoMap.tsx @@ -0,0 +1,96 @@ +import React, { useEffect } from 'react'; + +declare global { + interface Window { + kakao: any; // eslint-disable-line @typescript-eslint/no-explicit-any + } +} + +interface Village { + id: number; + title: string; + content: string; + imageUrl: string; + x: number; + y: number; +} + +interface KakaoMapProps { + villages: Village[]; +} + +const KakaoMap = ({ villages }: KakaoMapProps) => { + useEffect(() => { + const initializeMap = () => { + const container = document.getElementById('map'); + if (!container) { + return; + } + + const options = { + center: new window.kakao.maps.LatLng(36.5, 127.5), + level: 13, + }; + const map = new window.kakao.maps.Map(container, options); + + villages.forEach((village) => { + const markerPosition = new window.kakao.maps.LatLng(village.x, village.y); + const marker = new window.kakao.maps.Marker({ + position: markerPosition, + }); + marker.setMap(map); + + const infoWindow = new window.kakao.maps.InfoWindow({ + content: `
${village.title}
`, + }); + + window.kakao.maps.event.addListener(marker, 'mouseover', () => { + infoWindow.open(map, marker); + }); + + window.kakao.maps.event.addListener(marker, 'mouseout', () => { + infoWindow.close(); + }); + + window.kakao.maps.event.addListener(marker, 'click', () => { + window.location.href = `/village/${village.id}`; + }); + }); + }; + + const loadKakaoMap = () => { + if (window.kakao && window.kakao.maps && window.kakao.maps.load) { + window.kakao.maps.load(() => initializeMap()); + return; + } + + const script = document.createElement('script'); + script.src = `https://dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_APP_JS_KEY}&autoload=false`; + script.async = true; + script.onload = () => { + if (window.kakao && window.kakao.maps && window.kakao.maps.load) { + window.kakao.maps.load(() => { + initializeMap(); + }); + } + }; + script.onerror = () => {}; + + document.head.appendChild(script); + }; + + loadKakaoMap(); + }, [villages]); + + return ( +
+ ); +}; + +export default KakaoMap; diff --git a/src/constants/category.ts b/src/constants/category.ts index 6d7f1d5..184fa1f 100644 --- a/src/constants/category.ts +++ b/src/constants/category.ts @@ -4,7 +4,7 @@ export const mainCategory = [ { id: 3, label: '뉴스레터', path: '/newsletter' }, { id: 4, label: '인사이트', path: '/insight' }, { id: 5, label: '챗봇', path: '/chatbot' }, - // { id: 6, label: '마을', path: '/village' }, + { id: 6, label: '마을', path: '/village' }, ]; export const articleCategory = [ diff --git a/src/mocks/villages.ts b/src/mocks/villages.ts new file mode 100644 index 0000000..95e13df --- /dev/null +++ b/src/mocks/villages.ts @@ -0,0 +1,59 @@ +const hiddenGems = [ + { + id: 1, + title: '정선 아우라지마을', + content: + '정선 아우라지마을은 예쁜 계곡과 맑은 물로 유명한 곳이에요. ⛲\n이곳은 과거부터 민속촌 같은 분위기로 많은 이들이 찾아오는 명소였어요. 맑은 물과 푸른 자연 속에서 즐기는 여유로운 시간은 정말 특별해요. 특히, 여름에는 계곡에서 물놀이를 즐기기 좋고, 가을에는 단풍이 아름다워 산책하기 좋은 곳이에요. 🍁\n하지만 최근 몇 년 동안 관광객이 줄어들면서 마을 경제가 많이 어려워졌어요. 마을 주민들은 생계를 위해 힘든 시간을 보내고 있으며, 전통 문화를 유지하는데 어려움을 겪고 있어요. 😢\n우리가 이 아름다운 마을을 지키기 위해 더 많은 관심을 가져야 해요. 주말에 친구들과 함께 정선 아우라지마을을 방문해보세요. 맑은 공기와 아름다운 자연 속에서 힐링할 수 있을 거예요. 🌿', + imageUrl: + 'https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMDA4MjhfMjk0%2FMDAxNTk4NTg0NjU1NTQ2.Qu9sBGDkKOZ2KB7zm-cjwpKvA38o_ESEQ_Mnr-vyorsg.FMa1HkUp8kWxipC36WXSC6OlQpDF1YdKFp7B9jW3LfAg.PNG.lightboxlab%2FThumbnail.png&type=sc960_832', + x: 37.3793, + y: 128.6616, + publishedAt: '2024-07-20', + }, + { + id: 2, + title: '청송 주왕산마을', + content: + '청송 주왕산마을은 주왕산 국립공원 근처에 위치해 있어 멋진 자연 경관을 자랑해요. 🌄\n이곳은 사계절 내내 아름다운 풍경을 자랑하며, 특히 가을에는 단풍이 절경을 이루어 많은 사람들이 찾았던 곳이에요. 🍂\n그러나 최근 몇 년 동안 관광객이 급감하면서 마을 상권이 침체되어 많은 가게들이 문을 닫았어요. 주왕산의 아름다움을 즐기고 마을 주민들을 도와주기 위해 많은 이들이 방문해주었으면 해요. 😔\n주왕산마을에서 하이킹을 즐기고, 맑은 공기를 마시며 자연 속에서 힐링하는 시간을 가져보세요. 그리고 마을에서 운영하는 작은 상점과 카페들을 방문해 지역 경제에도 도움을 주었으면 좋겠어요. 🍃', + imageUrl: + 'https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2F20130124_272%2Fsung1735_1359006236881C5jrX_JPEG%2FKSS_8232.jpg&type=sc960_832', + x: 36.3446, + y: 129.0571, + publishedAt: '2024-07-20', + }, + { + id: 3, + title: '양구 두타연마을', + content: + '양구 두타연마을은 아름다운 계곡과 울창한 숲으로 유명한 곳이에요. 🌳\n이곳은 자연의 아름다움을 그대로 간직하고 있어, 도시의 번잡함을 벗어나고 싶은 사람들에게 안성맞춤인 곳이에요. 🌲\n그러나 관광객 감소와 함께 농산물 판매가 줄어들어 마을 주민들의 생계가 어려워졌어요. 과거에는 많은 사람들이 찾았던 관광 명소였지만, 이제는 관심이 줄어들어 위기에 처해있답니다. 😢\n두타연마을에서 자연과 하나 되는 시간을 보내보세요. 계곡에서 피크닉을 즐기고, 숲 속을 산책하며 힐링하는 시간을 가질 수 있어요. 그리고 마을 주민들이 정성껏 키운 농산물도 구매해서 맛보세요. 우리의 작은 관심이 마을을 지키는 큰 힘이 된답니다. 🍃', + imageUrl: + 'https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMzA4MTZfMjA5%2FMDAxNjkyMTk2OTYxNjIy.vtgqtLu7296AUHuvKEEB6iAvkm-DTX8AOhNjqSYsqZwg.VM6kA-GRj4mljMW_V33BTkkI_yiicJYztqoglt5D9owg.JPEG.dewdoll%2F20221001_143251.jpg&type=sc960_832', + x: 38.1016, + y: 127.9897, + publishedAt: '2024-07-20', + }, + { + id: 4, + title: '강진 청자골마을', + content: + '강진 청자골마을은 전통 청자 도자기로 유명한 곳이에요. 🏺\n이곳에서는 아름다운 청자 도자기를 직접 만들어보는 체험을 할 수 있어요. 도자기를 빚으며 느끼는 마음의 평온함은 정말 특별해요. 🎨\n하지만 최근 도자기 시장의 침체와 함께 관광객이 줄어들면서 마을 경제가 어려워졌어요. 😔\n청자골마을의 전통을 지키기 위해 많은 사람들이 방문해주어야 해요. 도자기 체험도 하고, 아름다운 자연도 즐길 수 있어요. 우리의 관심과 방문이 마을에 큰 도움이 될 거예요. 🌿', + imageUrl: + 'https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyMzA5MTJfMTYx%2FMDAxNjk0NTAyMjUzMjA1.V6PItlGjqs-AdEjmwpDcUCQ9tc-ywWW9tLoJVvrS7y8g.mv2g_5bk7XkvgASzAV9ceDwOKkTCziQord1AiC-UBIwg.JPEG.fsohx10%2FIMG_4409.jpg&type=sc960_832', + x: 34.6421, + y: 126.7658, + publishedAt: '2024-07-20', + }, + { + id: 5, + title: '함평 나비마을', + content: + '함평 나비마을은 나비 축제로 유명한 곳이에요. 🦋\n봄이 되면 나비와 꽃이 가득한 이곳에서 아름다운 풍경을 감상할 수 있어요. 🌷\n하지만 축제 시즌 외에는 관광객이 거의 없어 마을 경제가 어려움을 겪고 있어요. 😢\n나비마을의 아름다움을 유지하고 마을 주민들을 돕기 위해서는 더 많은 관심과 방문이 필요해요. 나비와 꽃이 가득한 이곳에서 아름다운 추억을 만들어보세요. 🌼', + imageUrl: + 'https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAyNDA0MzBfMTIz%2FMDAxNzE0NDc0Mjc4NTc4.943crSOmERlWxQ-6cJRuqZubCIeG6e8ASLWPpSjEpi0g.b-N_ymLKV74Y8bAWWJCbNyjrkbcdLmsRgJbtpxeYvaUg.JPEG%2F_NZ74880.jpg-0.jpg&type=sc960_832', + x: 35.0633, + y: 126.5167, + publishedAt: '2024-07-20', + }, +]; + +export default hiddenGems; diff --git a/src/types.ts b/src/types.ts index 8825278..b6bb077 100644 --- a/src/types.ts +++ b/src/types.ts @@ -27,3 +27,13 @@ export interface Question { option2: { option: string }; option3: { option: string }; } + +export interface Village { + id: number; + title: string; + content: string; + imageUrl: string; + x: number; + y: number; + publishedAt: string; +} diff --git a/yarn.lock b/yarn.lock index 33179dd..becc51f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -90,7 +90,7 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.22.15", "@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.8.tgz#5d958c3827b13cc6d05e038c07fb2e5e3420d82e" integrity sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA== @@ -3260,6 +3260,11 @@ json5@^1.0.2: object.assign "^4.1.4" object.values "^1.1.6" +kakao.maps.d.ts@^0.1.39: + version "0.1.40" + resolved "https://registry.yarnpkg.com/kakao.maps.d.ts/-/kakao.maps.d.ts-0.1.40.tgz#60a922e6fa1ab90c57e062e84978eb2d0e506412" + integrity sha512-nX69MB1ok04epe3OqS+/tEeWBbU31GSQbvDPJmQRRltzzqn6t4jBsO5v1nzalUjCKzwcH2CptOc767NZ7Hbu3g== + kdbush@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0" @@ -3902,6 +3907,14 @@ react-is@^18.3.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +react-kakao-maps-sdk@^1.1.27: + version "1.1.27" + resolved "https://registry.yarnpkg.com/react-kakao-maps-sdk/-/react-kakao-maps-sdk-1.1.27.tgz#577c5b53b76459a85fd3cc486ae0e799ec073b85" + integrity sha512-1EwYkYsjTDRFqysKStDasFMrFTXcLx2AyRlqMoWD7ONWhRqpjx9M874hkhEEHrnypP2eSIhhDLe0EiSKp3bd2Q== + dependencies: + "@babel/runtime" "^7.22.15" + kakao.maps.d.ts "^0.1.39" + react-plotly.js@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/react-plotly.js/-/react-plotly.js-2.6.0.tgz#ad6b68ee64f1b5cfa142ee92c59687f9c2c09209" @@ -4304,16 +4317,7 @@ string-split-by@^1.0.0: dependencies: parenthesis "^3.1.5" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4405,14 +4409,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==