diff --git a/.github/workflows/preview.yaml b/.github/workflows/preview.yaml new file mode 100644 index 0000000..e7b1b06 --- /dev/null +++ b/.github/workflows/preview.yaml @@ -0,0 +1,21 @@ +name: GitHub Actions Vercel Preview Deployment +env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} +on: + push: + branches-ignore: + - main +jobs: + Deploy-Preview: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Vercel CLI + run: npm install --global vercel@canary + - name: Pull Vercel Environment Information + run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} + - name: Build Project Artifacts + run: vercel build --token=${{ secrets.VERCEL_TOKEN }} + - name: Deploy Project Artifacts to Vercel + run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a547bf3..fc5ae9f 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ dist-ssr *.njsproj *.sln *.sw? +.vercel diff --git a/index.html b/index.html index a687fa8..e4628af 100644 --- a/index.html +++ b/index.html @@ -10,15 +10,16 @@ + diff --git a/src/components/common/InformationBar/InformationBar.jsx b/src/components/common/InformationBar/InformationBar.jsx index 2af4b0a..06c778c 100644 --- a/src/components/common/InformationBar/InformationBar.jsx +++ b/src/components/common/InformationBar/InformationBar.jsx @@ -17,17 +17,24 @@ function InformationBar({ const [isModalOpen, setIsModalOpen] = useState(false); const modalRef = useRef(null); const [showToast, setShowToast] = useState(false); - const buttonRef = useRef(null); + // ✅ 카카오 SDK 초기화 (한 번만 실행) + useEffect(() => { + if (window.Kakao && !window.Kakao.isInitialized()) { + window.Kakao.init("0e75199aafea8afc76aa6dd724c8f4bd"); // 🔥 여기에 JavaScript 키 입력 + console.log("✅ Kakao SDK Initialized"); + } + }, []); + const shareToKakao = () => { if (window.Kakao) { window.Kakao.Share.sendDefault({ objectType: "feed", content: { - title: "웹사이트 공유 제목", - description: "웹사이트 설명", - imageUrl: "", + title: "롤링페이퍼 8팀", + description: "친구들에게 멋진 롤링페이퍼를 공유해 보세요!🎉", + imageUrl: "https://cdn-icons-png.flaticon.com/512/5220/5220478.png", link: { mobileWebUrl: window.location.href, webUrl: window.location.href, @@ -117,7 +124,7 @@ function InformationBar({ {isModalOpen && ( - + )} diff --git a/src/components/domain/rollingpaper/Card/Card.jsx b/src/components/domain/rollingpaper/Card/Card.jsx index 258df2b..ecf1f87 100644 --- a/src/components/domain/rollingpaper/Card/Card.jsx +++ b/src/components/domain/rollingpaper/Card/Card.jsx @@ -13,8 +13,8 @@ const CardContainer = styled.div` display: flex; align-items: center; justify-content: center; - top: ${(props) => props.top || "0"}; /* top 값을 받아서 사용 */ - left: ${(props) => props.left || "0"}; /* left 값을 받아서 사용 */ + top: ${(props) => props.top || "0"}; + left: ${(props) => props.left || "0"}; `; const CircleButton = styled.button` diff --git a/src/components/domain/rollingpaper/Card/CardWrite.jsx b/src/components/domain/rollingpaper/Card/CardWrite.jsx index 2b1663a..445df8b 100644 --- a/src/components/domain/rollingpaper/Card/CardWrite.jsx +++ b/src/components/domain/rollingpaper/Card/CardWrite.jsx @@ -22,16 +22,16 @@ const EditorWrapper = styled.div.withConfig({ })` .ql-editor { width: 336px; - height: 106px; /* 높이를 고정 */ + height: 106px; font-size: 1rem !important; line-height: 28px; padding: 16px 0; background: #fff; font-family: ${(props) => props.$fontFamily || "Noto Sans KR"}; text-align: ${(props) => props.$textAlign || "left"}; - overflow: hidden; /* 내용이 박스를 넘지 않도록 설정 */ - white-space: pre-wrap; /* 줄 바꿈과 공백을 유지하면서 텍스트를 자동으로 줄 바꿈 */ - word-wrap: break-word; /* 단어가 길 경우 줄 바꿈 */ + overflow: hidden; + white-space: pre-wrap; + word-wrap: break-word; } .ql-picker.ql-font { @@ -73,7 +73,18 @@ const CardContainer = styled.div` flex-direction: column; padding: 28px 24px; box-sizing: border-box; - cursor: pointer; /* 클릭 가능하도록 커서 변경 */ + cursor: pointer; + + /*눌린듯한 느낌*/ + transition: transform 0.1s ease, box-shadow 0.1s ease; + &:hover { + transform: scale(0.97); + box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.1); + } + &:active { + transform: scale(0.97); + box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.1); + } `; const Header = styled.div` diff --git a/src/pages/Message/MessagePage.jsx b/src/pages/Message/MessagePage.jsx index a0ac157..7c70d53 100644 --- a/src/pages/Message/MessagePage.jsx +++ b/src/pages/Message/MessagePage.jsx @@ -15,7 +15,6 @@ const Bone = styled.div` @media (max-width: 360px) { padding: 50px 20px; } - overflow: auto; `; function MessagePage() { diff --git a/src/pages/RollingPaper/RollingPaperPage.jsx b/src/pages/RollingPaper/RollingPaperPage.jsx index 1fac0c4..d672433 100644 --- a/src/pages/RollingPaper/RollingPaperPage.jsx +++ b/src/pages/RollingPaper/RollingPaperPage.jsx @@ -9,26 +9,12 @@ import axios from "axios"; const CardContainer = styled.div` - width: min(100%, 1200px); - margin-inline: auto; - padding: 0 24px; - box-sizing: border-box; + margin: auto; display: flex; justify-content: center; - height: 1140px; - min-height: 50vh; padding: 100px 24px; width: 100%; - - background-image: ${({ backgroundImageURL }) => backgroundImageURL ? `url(${backgroundImageURL})` : "none"}; - background-color: ${({ bgColor }) => bgColor || "#FFE2AD"}; - min-height: calc(100vh - 65px); - height: auto; - background-size: cover; - background-repeat: no-repeat; - background-position: center top; - background-attachment: fixed; - + box-sizing: border-box; @media (max-width: 768px) { background-attachment: scroll; } @@ -37,30 +23,34 @@ const CardContainer = styled.div` const DivWrap = styled.div` display: grid; grid-template-columns: repeat(3, 1fr); - grid-template-rows: repeat(2, auto); gap: 28px; opacity: ${({ isLoaded }) => (isLoaded ? 1 : 0)}; transition: opacity 0.5s ease-in-out; - height: fit-content; - padding-top: 112px; - `; const BackgroundWrap = styled.div` - background-image: ${({ backgroundImageURL }) => backgroundImageURL ? `url(${backgroundImageURL})` : "none"}; - background-color: ${({ bgColor }) => bgColor || "#FFE2AD"}; - min-height: 100vh; - height: auto; - background-size: contain; + background-image: ${({ backgroundImageURL }) => + backgroundImageURL + ? `linear-gradient(180deg, rgba(0, 0, 0, 0.54) 0%, rgba(0, 0, 0, 0.54) 100%), url(${backgroundImageURL})` + : "ffffff"}; + background-color: ${({ bgColor }) => bgColor || "#ffffff"}; + min-height: calc(100vh - 65px); + background-size: cover; background-repeat: no-repeat; background-position: center top; background-attachment: fixed; - @media (max-width: 768px) { background-attachment: scroll; } `; +const colorMap = { + beige: "#FFE2AD", + purple: "#ECD9FF", + blue: "#B1E4FF", + green: "#D0F5C3", +}; + function RollingPaperDetailPage() { const { id } = useParams(); const [loading, setLoading] = useState(true); @@ -68,23 +58,14 @@ function RollingPaperDetailPage() { const [messages, setMessages] = useState([]); const [nextUrl, setNextUrl] = useState(null); const [isLoaded, setIsLoaded] = useState(false); - const observerRef = useRef(null); const lastMessageRef = useRef(null); const isFetchingRef = useRef(false); - const colorMap = { - beige: "#FFE2AD", - purple: "#ECD9FF", - blue: "#B1E4FF", - green: "#D0F5C3", - }; - - useEffect(() => { async function fetchInitialData() { try { const [messagesResponse, recipientResponse] = await Promise.all([ - recipientsService.getRecipientsMessages(id, 8, 0), + recipientsService.getRecipientsMessages(id, 5, 0), recipientsService.getRecipientsId(id), ]); setMessages(messagesResponse.data.results); @@ -99,7 +80,6 @@ function RollingPaperDetailPage() { if (id) fetchInitialData(); }, [id]); - const loadMoreMessages = async () => { if (!nextUrl || isFetchingRef.current) return; isFetchingRef.current = true; @@ -116,13 +96,13 @@ function RollingPaperDetailPage() { useEffect(() => { if (!lastMessageRef.current) return; - observerRef.current = new IntersectionObserver( + const observer = new IntersectionObserver( ([entry]) => entry.isIntersecting && loadMoreMessages(), - { root: null, rootMargin: "-50px" } + { root: null, rootMargin: "100px" } ); - observerRef.current.observe(lastMessageRef.current); - return () => observerRef.current?.disconnect(); - }, [messages.length]); + observer.observe(lastMessageRef.current); + return () => observer.disconnect(); + }, [messages]); useEffect(() => { setTimeout(() => setIsLoaded(true), 100); @@ -130,7 +110,7 @@ function RollingPaperDetailPage() { return ( - + {messages.map((message, index) => ( @@ -155,4 +134,4 @@ function RollingPaperDetailPage() { ); } -export default RollingPaperDetailPage; \ No newline at end of file +export default RollingPaperDetailPage; diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..2cda195 --- /dev/null +++ b/vercel.json @@ -0,0 +1,15 @@ +{ + "rewrites": [ + { "source": "/(.*)", "destination": "/index.html", "statusCode": 200 } + ], + "headers": [ + { + "source": "/assets/(.*).js", + "headers": [{ "key": "Content-Type", "value": "application/javascript" }] + }, + { + "source": "/assets/(.*).css", + "headers": [{ "key": "Content-Type", "value": "text/css" }] + } + ] +} diff --git a/vite.config.js b/vite.config.js index 4b21872..ebfb29a 100644 --- a/vite.config.js +++ b/vite.config.js @@ -2,12 +2,20 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import svgr from "vite-plugin-svgr"; -// https://vite.dev/config/ export default defineConfig({ plugins: [react(), svgr()], + base: '/', // ✅ Vercel 배포 시 '/' 유지 + build: { + outDir: 'dist', + assetsDir: 'assets', + }, resolve: { alias: { "@src": "/src", }, }, + server: { + strictPort: true, + historyApiFallback: true, // ✅ 추가! React Router가 정상 작동하도록 설정 + }, });