From c5b67bd710edd99cb7793c137f7a914fea379214 Mon Sep 17 00:00:00 2001 From: useusername1 Date: Wed, 25 Jan 2023 15:43:02 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20carousel=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=ED=98=84=20-useusername1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project/package-lock.json | 16 ++ project/package.json | 2 + project/src/Main.tsx | 22 +- .../components/Carousel/CarouselContent.tsx | 84 ++++++ project/src/components/Carousel/index.tsx | 172 +++++++++++++ project/src/components/Carousel/style.tsx | 239 ++++++++++++++++++ project/src/components/GoogleLogin.js | 54 ++-- project/src/data/CarouselData.ts | 115 +++++++++ .../{searchBarData.tsx => searchBarData.ts} | 0 9 files changed, 659 insertions(+), 45 deletions(-) create mode 100644 project/src/components/Carousel/CarouselContent.tsx create mode 100644 project/src/components/Carousel/index.tsx create mode 100644 project/src/components/Carousel/style.tsx create mode 100644 project/src/data/CarouselData.ts rename project/src/data/{searchBarData.tsx => searchBarData.ts} (100%) diff --git a/project/package-lock.json b/project/package-lock.json index 7f2a1a90..86350e6b 100644 --- a/project/package-lock.json +++ b/project/package-lock.json @@ -21,9 +21,11 @@ "@types/styled-react-modal": "^1.2.2", "aws-sdk": "^2.1296.0", "axios": "^1.2.2", + "blurhash": "^2.0.4", "hangul-js": "^0.2.6", "http-proxy-middleware": "^2.0.6", "react": "^18.2.0", + "react-blurhash": "^0.3.0", "react-daum-postcode": "^3.1.1", "react-dom": "^18.2.0", "react-geocode": "^0.2.3", @@ -5284,6 +5286,11 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "node_modules/blurhash": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/blurhash/-/blurhash-2.0.4.tgz", + "integrity": "sha512-r/As72u2FbucLoK5NTegM/GucxJc3d8GvHc4ngo13IO/nt2HU4gONxNLq1XPN6EM/V8Y9URIa7PcSz2RZu553A==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -14180,6 +14187,15 @@ "node": ">=14" } }, + "node_modules/react-blurhash": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/react-blurhash/-/react-blurhash-0.3.0.tgz", + "integrity": "sha512-XlKr4Ns1iYFRnk6DkAblNbAwN/bTJvxTVoxMvmTcURdc5oLoXZwqAF9N3LZUh/HT+QFlq5n6IS6VsDGsviYAiQ==", + "peerDependencies": { + "blurhash": "^2.0.3", + "react": ">=15" + } + }, "node_modules/react-daum-postcode": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/react-daum-postcode/-/react-daum-postcode-3.1.1.tgz", diff --git a/project/package.json b/project/package.json index 716a7197..bbcfd749 100644 --- a/project/package.json +++ b/project/package.json @@ -16,9 +16,11 @@ "@types/styled-react-modal": "^1.2.2", "aws-sdk": "^2.1296.0", "axios": "^1.2.2", + "blurhash": "^2.0.4", "hangul-js": "^0.2.6", "http-proxy-middleware": "^2.0.6", "react": "^18.2.0", + "react-blurhash": "^0.3.0", "react-daum-postcode": "^3.1.1", "react-dom": "^18.2.0", "react-geocode": "^0.2.3", diff --git a/project/src/Main.tsx b/project/src/Main.tsx index 1f80cb1e..4961e5e1 100644 --- a/project/src/Main.tsx +++ b/project/src/Main.tsx @@ -6,7 +6,7 @@ import Place from "./pages/Place"; import Post from "./pages/Post"; import { Header } from "./components/Header/index"; import HiddenHeader from "./components/Header/HiddenHeader"; - +import Carousel from "./components/Carousel"; const Body = styled.div` width: 83.5%; @@ -20,10 +20,7 @@ const Footer = styled.div` height: 157px; `; - - function Main() { - return ( <>
@@ -32,9 +29,8 @@ function Main() {
{/* */} {/* */} - - - + +
footer
); @@ -42,9 +38,6 @@ function Main() { export default Main; - - - // import axios from "axios"; // import ButtonForm from "./components/Button" @@ -56,8 +49,6 @@ export default Main; // LoggedUser, // } from "./recoil/state"; - - // const [isLogin, setIslogin] = useRecoilState(LoginState); // const [auth, setAuth] = useRecoilState(AuthToken); // const [rafresh, setRefresh] = useRecoilState(RefreshToken); @@ -79,7 +70,7 @@ export default Main; // } // }) // .then((res) => { -// console.log(res) +// console.log(res) // console.log("로긴성공") // // navigate("/"); @@ -88,5 +79,6 @@ export default Main; // } - -{/* */} +{ + /* */ +} diff --git a/project/src/components/Carousel/CarouselContent.tsx b/project/src/components/Carousel/CarouselContent.tsx new file mode 100644 index 00000000..e06a35ae --- /dev/null +++ b/project/src/components/Carousel/CarouselContent.tsx @@ -0,0 +1,84 @@ +import { memo, useState } from "react"; +import { + CarouselItemContainer, + CarouselTextWrapper, + LeftCarouselTextWrapper, + RightCarouselTextWrapper, + ImageWrapper, +} from "./style"; +import { IoIosArrowForward as RightArrowIcon } from "react-icons/io"; +import { MdLocationOn as PinIcon } from "react-icons/md"; +import { Blurhash } from "react-blurhash"; +interface CarouselContentProps { + data: { + img: string; + id: number; + title: string; + subtitle: string; + link: string; + color: string; + blur_hash: string; + location: string; + }; + isTransitionEnd: boolean; +} +const CarouselContent = memo( + ({ data, isTransitionEnd }: CarouselContentProps) => { + const [imgLoaded, setImgLoaded] = useState(false); + const { + img: imgUrl, + id, + title, + subtitle, + link, + color, + blur_hash, + location, + } = data; + + const handleImgLoad = () => { + setImgLoaded(true); + }; + + return ( + + + + {`carousel${id}`} + + + +

{title}

+

{subtitle}

+ + 더 보러가기 + + +
+ + + + {location} + + +
+
+ ); + } +); + +export default CarouselContent; diff --git a/project/src/components/Carousel/index.tsx b/project/src/components/Carousel/index.tsx new file mode 100644 index 00000000..00b37186 --- /dev/null +++ b/project/src/components/Carousel/index.tsx @@ -0,0 +1,172 @@ +import { useState, useEffect, useRef, useLayoutEffect, Fragment } from "react"; +import CarouselContent from "./CarouselContent"; +import carouselData from "../../data/CarouselData"; +import { + CarouselWrapper, + CarouselContentListContainer, + CarouselControlContainer, + RangeSliderContainer, + SliderDot, +} from "./style"; +import { IoIosArrowForward as NextIcon } from "react-icons/io"; +import { IoIosArrowBack as PrevIcon } from "react-icons/io"; +import { IoPause as PauseIcon } from "react-icons/io5"; +import { IoPlay as PlayIcon } from "react-icons/io5"; + +const newCarouselData = [ + { ...carouselData[carouselData.length - 1], id: 20 }, + ...carouselData, + { ...carouselData[0], id: 21 }, +]; + +const INTERVAL = 6000; +const Carousel = () => { + const [currentPhoto, setCurrentPhoto] = useState(1); //현재 캐러셀 위치 + const [textTransition, setTextTransition] = useState(-1); //textTransition이 일어나야할 번호 + const [isPlaying, setIsPlaying] = useState(true); //재생버튼 + const transitionOnRef = useRef(false); //전체 캐러셀 애니메이션 관리 + const autoCarouselTimerIdRef = useRef(null); //setInterval id + const preUpdatedCurrentPhotoRef = useRef(1); + + useLayoutEffect(() => { + setTimeout(() => { + switch (currentPhoto) { + case 0: + setCurrentPhoto(newCarouselData.length - 2); + setTextTransition(newCarouselData.length - 2); + transitionOnRef.current = false; + break; + case newCarouselData.length - 1: + setCurrentPhoto(1); + setTextTransition(1); + transitionOnRef.current = false; + break; + default: + transitionOnRef.current = false; + setTextTransition(currentPhoto); + } + }, 500); + }, [currentPhoto]); + + useEffect(() => { + setTextTransition(currentPhoto); + + autoCarouselTimerIdRef.current = setInterval(() => { + transitionOnRef.current = true; + setCurrentPhoto((p) => p + 1); + }, INTERVAL); + return () => + clearInterval(autoCarouselTimerIdRef.current as NodeJS.Timeout); + }, []); + + if (currentPhoto === 0) { + preUpdatedCurrentPhotoRef.current = newCarouselData.length - 2; + } else if (currentPhoto === newCarouselData.length - 1) { + preUpdatedCurrentPhotoRef.current = 1; + } else { + preUpdatedCurrentPhotoRef.current = currentPhoto; + } + + //transitionOnRef.current가 false가 되는 경우: 맨 마지막 이미지 또는 맨 처음 이미지에 도달하고 transition이 끝났을 때 + //true가 되는 경우: 다음 이미지 번호로 상태를 업데이트 할 때 + const handlePrevClick = () => { + if (!transitionOnRef.current) { + clearInterval(autoCarouselTimerIdRef.current as NodeJS.Timeout); + transitionOnRef.current = true; + setCurrentPhoto((p) => p - 1); + if (isPlaying) { + autoCarouselTimerIdRef.current = setInterval(() => { + transitionOnRef.current = true; + setCurrentPhoto((p) => p + 1); + }, INTERVAL); + } + } + }; + const handleNextClick = () => { + if (!transitionOnRef.current) { + clearInterval(autoCarouselTimerIdRef.current as NodeJS.Timeout); + transitionOnRef.current = true; + setCurrentPhoto((p) => p + 1); + if (isPlaying) { + autoCarouselTimerIdRef.current = setInterval(() => { + transitionOnRef.current = true; + setCurrentPhoto((p) => p + 1); + }, INTERVAL); + } + } + }; + const handlePauseClick = () => { + if (autoCarouselTimerIdRef.current) { + clearInterval(autoCarouselTimerIdRef.current as NodeJS.Timeout); + setIsPlaying(false); + autoCarouselTimerIdRef.current = null; + } + }; + const handlePlayClick = () => { + if (!autoCarouselTimerIdRef.current) { + autoCarouselTimerIdRef.current = setInterval(() => { + transitionOnRef.current = true; + setCurrentPhoto((p) => p + 1); + }, INTERVAL); + setIsPlaying(true); + } + }; + + const handleDotClick = (i: number) => { + if (!transitionOnRef.current) { + clearInterval(autoCarouselTimerIdRef.current as NodeJS.Timeout); + transitionOnRef.current = true; + + setCurrentPhoto(i); + if (isPlaying) { + autoCarouselTimerIdRef.current = setInterval(() => { + transitionOnRef.current = true; + setCurrentPhoto((p) => p + 1); + }, INTERVAL); + } + } + }; + + return ( + <> + + + {newCarouselData.map((el, i) => ( + + ))} + + + + {isPlaying ? ( + + ) : ( + + )} + + + + + {newCarouselData.map((el, i) => + i === 0 || i === newCarouselData.length - 1 ? ( + + ) : ( + handleDotClick(i)} + /> + ) + )} + + + + ); +}; +export default Carousel; diff --git a/project/src/components/Carousel/style.tsx b/project/src/components/Carousel/style.tsx new file mode 100644 index 00000000..864962ec --- /dev/null +++ b/project/src/components/Carousel/style.tsx @@ -0,0 +1,239 @@ +import styled from "styled-components"; +const CarouselWrapper = styled.div` + overflow: hidden; + position: relative; +`; +const CarouselContentListContainer = styled.ul<{ + currentPhotoNum: number; + transitionOn: boolean; +}>` + transform: ${(props) => `translateX(-${props.currentPhotoNum}00%)`}; + transition: ${(props) => + props.transitionOn ? "transform 0.5s ease" : "none"}; + height: 480px; + list-style: none; + white-space: nowrap; +`; +const CarouselItemContainer = styled.li` + height: 100%; + width: 100%; + position: relative; + display: inline-block; + /* list-style: none; */ +`; +const CarouselTextWrapper = styled.div` + padding: 50px; + position: absolute; + vertical-align: baseline; + top: 0; + height: 100%; + width: 100%; + letter-spacing: 0.15rem; + white-space: pre-wrap; + display: flex; + &:hover { + cursor: default; + } +`; +const LeftCarouselTextWrapper = styled.div<{ + isTransitionEnd: boolean; + textColor: string; +}>` + margin-top: 255px; + max-width: 600px; + + h2, + h3, + a { + color: ${(props) => props.textColor}; + opacity: ${(props) => (props.isTransitionEnd ? "1" : "0")}; + transition-property: transform, opacity; + transition-duration: 1s; + transition-timing-function: ease; + } + h2 { + transition-delay: 0.1s; + margin-bottom: 7px; + margin-left: 30px; + font-size: 28px; + transform: ${(props) => + props.isTransitionEnd ? "translate(-30px, 0)" : "none"}; + } + h3 { + transition-delay: 0.4s; + font-weight: var(--fw-medium); + margin-bottom: 8px; + margin-left: 30px; + font-size: var(--font-sm); + font-weight: var(--font-reg); + word-break: keep-all; + transform: ${(props) => + props.isTransitionEnd ? "translate(-30px, 0)" : "none"}; + } + a { + transition-delay: 0.8s; + transition-timing-function: transform 0.8s cubic-bezier(0.58, 0, 0, 1); + height: 30px; + vertical-align: middle; + margin-top: 30px; + padding: 8px 15px; + display: inline-flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(14px); + border-radius: 50px; + font-size: var(--font-xs); + transform: ${(props) => + props.isTransitionEnd ? "translate(0, -20px)" : "none"}; + &:hover { + transition: all 0.3s ease; + cursor: pointer; + background-color: hsla(0, 1%, 100%, 0.3); + } + } + .right-arrow { + height: 10px; + padding-left: 3px; + color: var(--purple-200); + } +`; +const RightCarouselTextWrapper = styled.div<{ + isTransitionEnd: boolean; + textColor: string; +}>` + margin-top: auto; + margin-left: auto; + color: ${(props) => props.textColor}; + opacity: ${(props) => (props.isTransitionEnd ? "0.8" : "0")}; + transition-property: transform, opacity; + transition-duration: 0.7s; + transition-timing-function: ease; + + span { + width: 100%; + display: flex; + align-items: center; + color: var(--black-600); + font-size: var(--font-xs); + transition: all 1s ease; + &:hover { + color: white; + } + } + + .location-pin { + height: 13px; + width: 50px; + padding-left: 15px; + } +`; +const ImageWrapper = styled.div<{ isLoaded: boolean }>` + display: inline-block; + height: 100%; + width: 100%; + position: relative; + display: inline-block; + canvas { + position: relative; + z-index: var(--zi-m-two); + } + img { + position: absolute; + top: 0; + left: 0; + aspect-ratio: 21/9; + height: 100%; + width: 100%; + object-fit: cover; + opacity: ${(props) => (props.isLoaded ? "1" : "0")}; + transition: opacity 0.7s ease; + } + &:after { + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + content: ""; + background: linear-gradient( + 0deg, + rgba(0, 0, 0, 1) 0%, + rgba(0, 0, 0, 0) 70% + ); + mix-blend-mode: normal; + opacity: 0.9; + } +`; +const CarouselControlContainer = styled.div` + display: flex; + justify-content: space-between; + width: 100px; + position: absolute; + transform: translate(50px, -430px); + + svg { + padding: 3px; + width: 20px; + height: 20px; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(14px); + border-radius: var(--br-s); + transition: opacity 0.3s; + color: var(--black-250); + transition: all 0.2s ease; + &:hover { + cursor: pointer; + background-color: hsla(0, 1%, 100%, 0.3); + } + } +`; +const RangeSliderContainer = styled.div<{ currentPhoto: number }>` + width: 250px; + height: 0.3px; + background-color: rgba(255, 255, 255, 0.1); + position: absolute; + left: calc(50% - 125px); + bottom: 30px; + display: flex; + align-items: center; + justify-content: space-between; + transition: width 0.5s ease; +`; +const SliderDot = styled.span<{ currentDot: boolean }>` + width: ${(props) => (props.currentDot ? "30px" : "7px")}; + height: 7px; + transition: all 0.7s ease; + background-color: ${(props) => + props.currentDot ? "var(--purple-300)" : "rgba(255, 255, 255, 0.3)"}; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(14px); + opacity: 0.9; + border-radius: 50px; + + :hover { + cursor: pointer; + background-color: white; + opacity: 0.7; + } + :after { + content: ""; + position: absolute; + width: 14px; + height: 14px; + } +`; +export { + CarouselWrapper, + CarouselTextWrapper, + LeftCarouselTextWrapper, + RightCarouselTextWrapper, + CarouselContentListContainer, + CarouselItemContainer, + CarouselControlContainer, + ImageWrapper, + RangeSliderContainer, + SliderDot, +}; diff --git a/project/src/components/GoogleLogin.js b/project/src/components/GoogleLogin.js index fe4875e4..5f30e2fa 100644 --- a/project/src/components/GoogleLogin.js +++ b/project/src/components/GoogleLogin.js @@ -1,50 +1,44 @@ -import { GoogleLogin } from '@react-oauth/google'; -import { GoogleOAuthProvider } from '@react-oauth/google'; - +import { GoogleLogin } from "@react-oauth/google"; +import { GoogleOAuthProvider } from "@react-oauth/google"; const GoogleLogIn = () => { - - - - return ( - - { - console.log(credentialResponse); - }} - onError={() => { - console.log('Login Failed'); - }} - /> - - ) - } - - export default GoogleLogIn - + return ( + + { + console.log(credentialResponse); + }} + onError={() => { + console.log("Login Failed"); + }} + /> + + ); +}; +export default GoogleLogIn; // import React, { useEffect } from 'react' // import { GoogleLogin, GoogleLogout } from 'react-google-login' - + // const GoogleLogIn = () => { - + // // 로그인 성공했을 때 처리 함수 // const successGoogle = (response) => { // console.log(response); // } - + // //로그인 실패했을 때 처리 함수 // const failGoogle = (response) => { // console.log(response); // } - + // // 로그아웃 성공했을 때 처리 함수 // const onLogoutSuccess = () => { // console.log('SUCESS LOG OUT'); // }; - + // return ( // // { // // ) // } - -// export default GoogleLogIn \ No newline at end of file + +// export default GoogleLogIn diff --git a/project/src/data/CarouselData.ts b/project/src/data/CarouselData.ts new file mode 100644 index 00000000..5f20cba1 --- /dev/null +++ b/project/src/data/CarouselData.ts @@ -0,0 +1,115 @@ +// import photo1 from "./../data/photo1.jpg"; +// import photo2 from "./../data/photo2.jpg"; +// import photo3 from "./../data/photo3.jpg"; +// import photo4 from "./../data/photo4.jpg"; +// import photo5 from "./../data/photo5.jpg"; +// import photo7 from "./../data/photo7.jpg"; +// import photo8 from "./../data/photo8.jpg"; +// import photo9 from "./../data/photo9.jpg"; +// import photo10 from "./../data/photo10.jpg"; +// import photo11 from "./../data/photo11.jpg"; +//https://drive.google.com/uc?id=1OmsgU1GLU9iUBYe9ruw_Uy1AcrN57n4g +const prefix = "https://drive.google.com/uc?id="; +const carouselData = [ + { + img: prefix + "1YWd1jKmfExPR6i8xi0omGNBRVlxNojcd", + id: 3, + title: "사진찍기 가장 좋은 장소는 어디일까요?", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다. 명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.1?", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "L0264{RgD|-sNDbds?R$Mtt8%PIl", + location: "남산공원", + }, + { + img: prefix + "16asJgbGLraRUT9FZoD3h1Ivzq6Jax97V", + id: 0, + title: "사진찍기 가장 좋은 장소는 어디일까요?", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다. 명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.2", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "LYGlr0R+j]of%MadWVfR_4ofofof", + location: "여의도한강공원", + }, + { + img: prefix + "1A0hK4ijcsEWHTG-IGs_gO8v8QHuiPJ_8", + id: 1, + title: "사진찍기 가장 좋은 장소는 어디일까요?", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다. 명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.3", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "LsHezp_NWWj[xvR+Rjt7off6RjRj", + location: "동대문디자인플라자", + }, + { + img: prefix + "1UDr-bGWp8AqSF_pSuiimNwPvRnLMKbYV", + id: 2, + title: "사진찍기 가장 좋은 장소는 어디일까요?", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다. 명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.4", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "LNKI9VWC11S4}[WWWCoKBDfjocjt", + location: "여의도한강공원", + }, + + { + img: prefix + "1_N4v0M79nc4EkceHvVj1YxL09krXYucr", + id: 4, + title: "사진찍기 가장 좋은 장소는 어디일까요?", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다. 명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.5", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "LEF}_|aI01xc?Fsk9eaJ~DV?Ivax", + location: "청계천", + }, + { + img: prefix + "1bMLTieIC-3W1v99DpWR_frxzW17pDKwR", + id: 5, + title: "사진찍기 가장 좋은 장소는 어디일까요?", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다.명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.6", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "LKA13Ls,RiWq.Tt6adayIVt7oLe.", + location: "광화문광장", + }, + { + img: prefix + "1bgUbgqqS6ZSytEhJnIde6064EkgSWMcb", + id: 6, + title: "사진찍기 가장 좋은 장소는 어디일까요?", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다.명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.7", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "L77A;[E1E0xt.ARiROog16$f$zR+", + location: "N서울타워", + }, + { + img: prefix + "1k_CtXEn_CrHLmu3S8mgmxVCsX89mjFNr", + id: 8, + title: "사진찍기 가장 좋은 장소는 어디일까요?", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다. 명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.8", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "LWHwcx%MIoa#}[I.s;ay^Ps.R*WX", + location: "서울숲공원", + }, + { + img: prefix + "1x5A69O3EE25JFnykZYG_9hgDWzJ_l9A-", + id: 9, + title: "사진찍기 가장 좋은 장소는 어디일까요", + subtitle: + "나만의 사진 명소를 발견할 수 있을지도 모릅니다. 명소부터 포스트까지 다양한 정보를 통해 나만의 사진을 찍어보세요.9", + link: "https://naver.com", + color: "var(--black-250)", + blur_hash: "LPKA+%$f-ps9~U?GnN%2~qg4t7S4", + location: "창덕궁", + }, +]; +export default carouselData; diff --git a/project/src/data/searchBarData.tsx b/project/src/data/searchBarData.ts similarity index 100% rename from project/src/data/searchBarData.tsx rename to project/src/data/searchBarData.ts