Repository: StarSync-FE/StarSync
Pull request #194: 💄Style: 랜딩 페이지 디자인 작업
Author: login: HarrySeop
Labels: name: 💄Style
description: 스타일링
Created: 2025-04-25T21:34:59Z | Merged: 2025-04-26T05:03:14Z

## 🧩 관련 이슈 번호

- #168 

## ✨ 요약

<!-- 구현 및 수정한 내용을 간단하게 적어주세요. -->
- 랜딩페이지 디자인 적용

## 📌 주요 변경 사항

<!-- 해당 PR의 변경 사항을 자세하게 적어주세요. -->
- 시은님이 만드신 404페이지, 펜딩UI에서 배경과 스타일 속성을 가져와서 배경과 폰트 적용
- 풀페이지 스크롤 방식으로 랜딩페이지를 구현했습니다.

## ✅ 체크리스트

<!-- 체크리스트 내용을 수정하고 싶으면 회의 때 얘기부탁드려요. -->

- [x] PR 하기 전에 이슈에서 빼먹은건 없는지 확인했습니다.
  - [x] 구현 기간에 맞는 이슈에서 서브이슈로 등록했습니다.
  - [x] 라벨 및 마일스톤을 사이드 탭에서 등록했습니다.
  - [x] PR 사이드 탭에서는 Projects을 등록 하지않았습니다.
- [x] PR을 보내는 브랜치가 올바른지 확인했습니다.
- [x] 팀원들이 리뷰하기 쉽도록 설명을 자세하게 작성했습니다.
- [x] 변경사항을 충분히 테스트 했습니다.
- [x] (함수를 구현 했을 때) JSDoc을 양식에 맞춰서 작성했습니다.
- [x] 컨벤션에 맞게 구현했습니다.

## 📷 UI 변경 사항 (선택)

<!-- UI 관련 구현 및 수정 사항이 있다면 이미지 or 동영상을 첨부해주세요.  -->

https://github.com/user-attachments/assets/a17cd210-8708-40f2-bd25-ddbbd3b61909


## ❓무슨 문제가 발생했나요 ?

전 디자인을 못하는 것 같습니다. 아니 못합니다.
타 팀에 비해 너무 빈약한 랜딩 페이지 죄송합니다..

각각의 섹션에는 저희 서비스의 목업 디자인이 완선되면 리팩토링으로 추가하려고 합니다!

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
	- Introduced a visually enhanced landing page with multiple animated sections, smooth scrolling, and interactive dot navigation.
	- Added a starry animated background and glowing text effects for an immersive user experience.
	- Included a scroll guide button to assist with navigation between sections.

- **Style**
	- Implemented responsive design, complex animations, and improved layout for the landing page.

- **Chores**
	- Added a new animation library dependency to support enhanced visual effects.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/package-lock.json b/package-lock.json
index 567292c..909dc67 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
         "@emotion/styled": "^11.14.0",
         "axios": "^1.8.4",
         "facepaint": "^1.2.1",
+        "motion": "^12.9.0",
         "react": "^19.0.0",
         "react-dom": "^19.0.0",
         "react-router": "^7.5.0",
@@ -2800,6 +2801,33 @@
         "node": ">= 6"
       }
     },
+    "node_modules/framer-motion": {
+      "version": "12.9.0",
+      "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.9.0.tgz",
+      "integrity": "sha512-N8seoPlZomM/8vpXk2X2kyd/CZ5/1WWKn7mdzzaP7rOCQQAh+WoAGIJg4zNU+TMNmspA79bf4mkVfoZxCJPSJQ==",
+      "license": "MIT",
+      "dependencies": {
+        "motion-dom": "^12.9.0",
+        "motion-utils": "^12.8.3",
+        "tslib": "^2.4.0"
+      },
+      "peerDependencies": {
+        "@emotion/is-prop-valid": "*",
+        "react": "^18.0.0 || ^19.0.0",
+        "react-dom": "^18.0.0 || ^19.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/is-prop-valid": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        },
+        "react-dom": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/fsevents": {
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -3747,6 +3775,47 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/motion": {
+      "version": "12.9.0",
+      "resolved": "https://registry.npmjs.org/motion/-/motion-12.9.0.tgz",
+      "integrity": "sha512-Oll9Jxx5Ea9o5ViZDpm3P3hslA3KU4iwOfvQ2MK6si2Dri2wC//2RrWG1ZtUp5iSqtXx9QHSi9lMBp1NO2IlZg==",
+      "license": "MIT",
+      "dependencies": {
+        "framer-motion": "^12.9.0",
+        "tslib": "^2.4.0"
+      },
+      "peerDependencies": {
+        "@emotion/is-prop-valid": "*",
+        "react": "^18.0.0 || ^19.0.0",
+        "react-dom": "^18.0.0 || ^19.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/is-prop-valid": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        },
+        "react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/motion-dom": {
+      "version": "12.9.0",
+      "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.9.0.tgz",
+      "integrity": "sha512-LDRyb86zk9Fz5Wee7zTiB/tilmAAv71Ex85UIrqMmQ51H08qSz18iXJCvOkimS3PntuD5CraKX35Fcu8lTPF+g==",
+      "license": "MIT",
+      "dependencies": {
+        "motion-utils": "^12.8.3"
+      }
+    },
+    "node_modules/motion-utils": {
+      "version": "12.8.3",
+      "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.8.3.tgz",
+      "integrity": "sha512-GYVauZEbca8/zOhEiYOY9/uJeedYQld6co/GJFKOy//0c/4lDqk0zB549sBYqqV2iMuX+uHrY1E5zd8A2L+1Lw==",
+      "license": "MIT"
+    },
     "node_modules/ms": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -4053,9 +4122,9 @@
       "license": "MIT"
     },
     "node_modules/react-router": {
-      "version": "7.5.1",
-      "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.1.tgz",
-      "integrity": "sha512-/jjU3fcYNd2bwz9Q0xt5TwyiyoO8XjSEFXJY4O/lMAlkGTHWuHRAbR9Etik+lSDqMC7A7mz3UlXzgYT6Vl58sA==",
+      "version": "7.5.2",
+      "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.2.tgz",
+      "integrity": "sha512-9Rw8r199klMnlGZ8VAsV/I8WrIF6IyJ90JQUdboupx1cdkgYqwnrYjH+I/nY/7cA1X5zia4mDJqH36npP7sxGQ==",
       "license": "MIT",
       "dependencies": {
         "cookie": "^1.0.1",
@@ -4076,12 +4145,12 @@
       }
     },
     "node_modules/react-router-dom": {
-      "version": "7.5.1",
-      "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.1.tgz",
-      "integrity": "sha512-5DPSPc7ENrt2tlKPq0FtpG80ZbqA9aIKEyqX6hSNJDlol/tr6iqCK4crqdsusmOSSotq6zDsn0y3urX9TuTNmA==",
+      "version": "7.5.2",
+      "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.2.tgz",
+      "integrity": "sha512-yk1XW8Fj7gK7flpYBXF3yzd2NbX6P7Kxjvs2b5nu1M04rb5pg/Zc4fGdBNTeT4eDYL2bvzWNyKaIMJX/RKHTTg==",
       "license": "MIT",
       "dependencies": {
-        "react-router": "7.5.1"
+        "react-router": "7.5.2"
       },
       "engines": {
         "node": ">=20.0.0"
@@ -4618,6 +4687,12 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
     "node_modules/turbo-stream": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
diff --git a/package.json b/package.json
index 1e87738..6985205 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
     "@emotion/styled": "^11.14.0",
     "axios": "^1.8.4",
     "facepaint": "^1.2.1",
+    "motion": "^12.9.0",
     "react": "^19.0.0",
     "react-dom": "^19.0.0",
     "react-router": "^7.5.0",
diff --git a/src/pages/landing/LandingPage.jsx b/src/pages/landing/LandingPage.jsx
index 5efa0a0..6fa665e 100644
--- a/src/pages/landing/LandingPage.jsx
+++ b/src/pages/landing/LandingPage.jsx
@@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
+import { useEffect, useRef, useState } from 'react';
 import { Link } from 'react-router-dom';
 import * as S from './landingPage.styles';
 
+const stars = [
+  { id: 1, top: '10%', left: '15%', width: 30, height: 30 },
+  { id: 2, top: '70%', left: '80%', width: 40, height: 40 },
+  { id: 3, top: '50%', left: '10%', width: 20, height: 20 },
+  { id: 4, top: '20%', left: '30%', width: 50, height: 50 },
+  { id: 5, top: '10%', left: '80%', width: 30, height: 30 },
+  { id: 6, top: '80%', left: '20%', width: 30, height: 30 },
+  { id: 7, top: '85%', left: '85%', width: 25, height: 25 },
+  { id: 8, top: '5%', left: '50%', width: 35, height: 35 },
+  { id: 9, top: '60%', left: '60%', width: 20, height: 20 },
+  { id: 10, top: '40%', left: '70%', width: 30, height: 30 },
+  { id: 11, top: '25%', left: '5%', width: 20, height: 20 },
+  { id: 12, top: '90%', left: '45%', width: 30, height: 30 },
+  { id: 13, top: '50%', left: '90%', width: 30, height: 30 },
+];
+
+const sections = [
+  {
+    id: 'main',
+    title: 'StarSync',
+    description: '후원과 투표를 통해 아이돌을 응원하세요. 당신의 선택이 그들의 무대가 됩니다.',
+    buttonText: '지금 시작하기',
+    showScrollGuide: true,
+  },
+  {
+    id: 'voting',
+    title: '투명한 투표 시스템',
+    description: '투명하고 신뢰할 수 있는 투표 시스템으로 여러분의 소중한 한 표를 행사하세요.',
+    buttonText: '투표하러 가기',
+  },
+  {
+    id: 'funding',
+    title: '팬덤의 힘을 보여주세요',
+    description: '여러분이 좋아하는 아이돌의 활동을 직접 후원하고 특별한 혜택도 받아가세요.',
+    buttonText: '후원하러 가기',
+  },
+  {
+    id: 'my-idol',
+    title: '나만의 아이돌을 위한 특별한 공간',
+    description:
+      '여러 아이돌 중에서 오직 당신의 최애만 선택하고 팬심을 가장 나다운 방식으로 표현해보세요.',
+    buttonText: '내 최애 선택하기',
+  },
+];
+
 const LandingPage = () => {
+  const sectionRefs = useRef([]);
+  const [activeIndex, setActiveIndex] = useState(0);
+
   const handleClearStorage = () => {
     localStorage.clear();
   };
 
+  useEffect(() => {
+    const observer = new IntersectionObserver(
+      (entries) => {
+        for (const entry of entries) {
+          if (entry.isIntersecting) {
+            const index = Number(entry.target.getAttribute('data-index'));
+            setActiveIndex(index);
+          }
+        }
+      },
+      { threshold: 0.5 },
+    );
+
+    for (const ref of sectionRefs.current) {
+      if (ref) observer.observe(ref);
+    }
+
+    return () => observer.disconnect();
+  }, []);
+
+  const handleSmoothScroll = (index) => {
+    sectionRefs.current[index]?.scrollIntoView({
+      behavior: 'smooth',
+      block: 'start',
+    });
+  };
+
+  const handleScrollGuideClick = () => {
+    const nextIndex = Math.min(activeIndex + 1, sections.length - 1);
+    handleSmoothScroll(nextIndex);
+  };
+
   return (
-    <div css={S.landingWrapper}>
-      <Link to="/list" onClick={handleClearStorage}>
-        <CustomButton type="button" variant="landing">
-          지금 시작하기
-        </CustomButton>
-      </Link>
+    <div css={S.pageContainer}>
+      <div css={S.backgroundStarsWrapper}>
+        {stars.map((star) => (
+          <div key={star.id} css={S.starStyle(star)} />
+        ))}
+      </div>
+
+      {/* Dot Navigation */}
+      <nav css={S.navDots}>
+        {sections.map((section, idx) => (
+          <button
+            key={section.id}
+            type="button"
+            css={[S.dot, activeIndex === idx && S.activeDot]}
+            onClick={() => handleSmoothScroll(idx)}
+            aria-label={`Move to ${section.id} section`}
+          />
+        ))}
+      </nav>
+
+      {/* Sections */}
+      {sections.map((section, idx) => (
+        <section
+          key={section.id}
+          data-index={idx}
+          ref={(el) => {
+            sectionRefs.current[idx] = el;
+          }}
+          css={S.section}
+        >
+          <motion.div
+            css={S.sectionContent}
+            initial={{ opacity: 0, y: 50 }}
+            animate={{ opacity: activeIndex === idx ? 1 : 0.3, y: activeIndex === idx ? 0 : 50 }}
+            transition={{ duration: 0.8 }}
+          >
+            <h1>{section.title}</h1>
+            <p>{section.description}</p>
+            <Link to="/list" onClick={handleClearStorage}>
+              <CustomButton type="button" variant="landing">
+                {section.buttonText}
+              </CustomButton>
+            </Link>
+            {section.showScrollGuide && (
+              <button
+                type="button"
+                css={S.scrollGuide}
+                onClick={handleScrollGuideClick}
+                aria-label="Scroll to next section"
+              >
+                스크롤하여 아래로 이동
+              </button>
+            )}
+          </motion.div>
+        </section>
+      ))}
     </div>
   );
 };
diff --git a/src/pages/landing/landingPage.styles.js b/src/pages/landing/landingPage.styles.js
index 05af8d8..f0c5095 100644
--- a/src/pages/landing/landingPage.styles.js
+++ b/src/pages/landing/landingPage.styles.js
@@ -1,8 +1,271 @@
-import { css } from '@emotion/react';
+import starImg from '@/assets/images/star.png';
+import media from '@/styles/responsive';
+import { css, keyframes } from '@emotion/react';
 
-export const landingWrapper = css`
+const auroraMove = keyframes`
+  0% {
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+  }
+  50% {
+    background-position: 50% 50%, 50% 0%, 0% 100%;
+  }
+  100% {
+    background-position: 100% 100%, 0% 50%, 50% 0%;
+  }
+`;
+
+const twinkle = keyframes` 
+  0%, 100% {
+    opacity: 0.3;
+  }
+  50% {
+    opacity: 0.6;
+  }
+`;
+
+const glow = keyframes`
+  0%, 100% {
+    text-shadow: 0 0 8px var(--pink), 0 0 16px var(--blue-sky), 0 0 24px var(--purple);
+  }
+  50% {
+    text-shadow: 0 0 12px var(--pink), 0 0 24px var(--blue-sky), 0 0 36px var(--purple);
+  }
+`;
+
+export const pageContainer = css`
+  overflow: hidden auto;
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  color: var(--white);
+  background: var(--black-deep);
+  scroll-snap-type: y mandatory;
+  scroll-behavior: smooth;
+  -ms-overflow-style: none;
+  scrollbar-width: none;
+
+  &::-webkit-scrollbar {
+    display: none;
+  }
+
+  &::before {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 0;
+    width: 200%;
+    height: 200%;
+    background: radial-gradient(circle, var(--purple), transparent 70%),
+                radial-gradient(circle, var(--blue-dark), transparent 70%),
+                radial-gradient(circle, var(--pink-purple), transparent 70%);
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+    background-size: 50% 50%;
+    animation: ${auroraMove} 10s infinite alternate;
+    content: '';
+    opacity: 0.5;
+  }
+`;
+
+export const backgroundStarsWrapper = css`
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 1;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+`;
+
+export const starStyle = ({ top, left, width, height }) => css`
+  position: absolute;
+  top: ${top};
+  left: ${left};
+  width: ${width}px;
+  height: ${height}px;
+  background: url(${starImg}) no-repeat center center;
+  background-size: contain;
+  opacity: 0.3;
+  animation: ${twinkle} 3s infinite;
+`;
+
+export const section = css`
   display: flex;
+  position: relative;
+  z-index: 2;
   justify-content: center;
   align-items: center;
+  width: 100%;
   height: 100vh;
+  padding: 0 2rem;
+  scroll-snap-align: start;
+  scroll-snap-stop: always;
+`;
+
+export const sectionContent = css`
+  display: flex;
+  position: relative;
+  z-index: 1;
+  flex-direction: column;
+  gap: 1rem;
+  width: 100%;
+  max-width: 144rem;
+  margin: 0 auto;
+  padding: 2rem;
+  padding-bottom: 6rem;
+  text-align: center;
+
+  ${media({
+    gap: ['2.4rem', '2.8rem', '2rem', '2rem'],
+    padding: ['2.4rem', '2.8rem', '3.2rem', '3.6rem'],
+    paddingBottom: ['8rem', '8rem', '8rem', '10rem'],
+  })}
+
+  h1 {
+    font-size: 3.2rem;
+    font-weight: 900;
+    line-height: 1.3;
+    letter-spacing: 0.2rem;
+    word-break: keep-all;
+    color: var(--white);
+    text-shadow: 2px 2px 4px rgb(0 0 0 / 70%);
+    cursor: pointer;
+    transition: opacity 0.3s ease;
+    animation: ${glow} 2s infinite ease-in-out;
+    
+    &:hover {
+      opacity: 0.8;
+    }
+    
+    ${media({
+      fontSize: ['3.2rem', '4rem', '5rem', '6rem'],
+      letterSpacing: ['0.2rem', '0.3rem', '0.4rem', '0.5rem'],
+      lineHeight: ['1.3', '1.2', '1.2', '1.2'],
+    })}
+  }
+
+  p {
+    max-width: 80rem;
+    margin: 0 auto;
+    padding: 0 1.6rem;
+    font-size: 1.4rem;
+    font-weight: 500;
+    line-height: 1.6;
+    white-space: pre-line;
+    word-break: keep-all;
+    color: var(--white);
+    text-shadow: 2px 2px 4px rgb(0 0 0 / 70%);
+    cursor: pointer;
+    transition: opacity 0.3s ease;
+
+    &:hover {
+      opacity: 0.8;
+    }
+    
+    ${media({
+      fontSize: ['1.6rem', '1.8rem', '2rem', '2.4rem'],
+      maxWidth: ['30rem', '50rem', '60rem', '80rem'],
+      lineHeight: ['1.7', '1.7', '1.7', '1.7'],
+      padding: ['1.6rem', '1.6rem', '2rem', '2.4rem'],
+    })}
+  }
+
+  a {
+    margin-top: 1.6rem;
+    
+    ${media({
+      marginTop: ['1.6rem', '2rem', '2.4rem', '2.8rem'],
+    })}
+  }
+`;
+
+export const navDots = css`
+  display: flex;
+  position: fixed;
+  top: 50%;
+  right: 1rem;
+  z-index: 10;
+  flex-direction: column;
+  gap: 1rem;
+  transform: translateY(-50%);
+  
+  ${media({
+    right: ['1rem', '1.5rem', '2rem', '3rem'],
+    gap: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+  })}
+`;
+
+export const dot = css`
+  width: 0.8rem;
+  height: 0.8rem;
+  padding: 0;
+  border: none;
+  border-radius: 50%;
+  background-color: var(--white);
+  opacity: 0.5;
+  cursor: pointer;
+  transition: all 0.4s ease;
+  
+
+  &:hover {
+    opacity: 0.8;
+    transform: scale(1.1);
+  }
+  
+  ${media({
+    width: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+    height: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+  })}
+`;
+
+export const activeDot = css`
+  opacity: 1;
+  transform: scale(1.2);
+`;
+
+export const scrollGuide = css`
+  display: inline-flex;
+  position: absolute;
+  bottom: 1rem;
+  left: 50%;
+  align-items: center;
+  gap: 1.2rem;
+  padding: 1rem;
+  border: none;
+  font-size: 1.4rem;
+  white-space: nowrap;
+  color: var(--white);
+  background: none;
+  cursor: pointer;
+  transition: opacity 0.3s ease;
+  animation: bounce 2s infinite;
+  transform: translateX(-50%);
+  
+  &::before {
+    content: '↓';
+    font-size: 2rem;
+    order: -1;
+  }
+
+  &:hover {
+    opacity: 0.8;
+  }
+  
+  ${media({
+    fontSize: ['1.2rem', '1.4rem', '1.6rem', '1.8rem'],
+    bottom: ['1rem', '1.4rem', '1.8rem', '2.2rem'],
+  })}
+
+  @keyframes bounce {
+    0%, 20%, 50%, 80%, 100% {
+      transform: translateX(-50%) translateY(0);
+    }
+
+    40% {
+      transform: translateX(-50%) translateY(-0.5rem);
+    }
+
+    60% {
+      transform: translateX(-50%) translateY(-0.3rem);
+    }
+  }
 `;


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>src/pages/landing/landingPage.styles.js (2)</summary><blockquote>

`5-33`: **Respect `prefers-reduced-motion` to improve accessibility**

Continuous animated gradients, twinkle, glow and bounce run indefinitely. Users who enable reduced-motion settings may experience discomfort.

```diff
+@media (prefers-reduced-motion: reduce) {
+  animation: none !important;
+}
```

Add the block above to each keyframe consumer (aurora `::before`, `starStyle`, heading `h1`, and `scrollGuide`) so animations are disabled when the user requests it.

---

`197-218`: **Add visible focus style to navigation dots for keyboard users**

Buttons are keyboard-focusable but have no focus indicator, making navigation invisible to non-mouse users.

```diff
   transition: all 0.4s ease;

+  &:focus-visible {
+    outline: 2px solid var(--pink);
+    outline-offset: 2px;
+  }
```

This small tweak meets WCAG 2.2 focus-visible requirements without affecting design.

</blockquote></details>
<details>
<summary>src/pages/landing/LandingPage.jsx (2)</summary><blockquote>

`56-78`: **Clean up IntersectionObserver when sections change**

`sectionRefs.current` is populated after every render, but the observer is created only once.  
If the list of sections ever changes (e.g., A/B tests), stale elements remain observed.

```diff
-  useEffect(() => {
+  useEffect(() => {
     const observer = new IntersectionObserver(…);
-    for (const ref of sectionRefs.current) {
-      if (ref) observer.observe(ref);
-    }
+    sectionRefs.current.forEach((ref) => ref && observer.observe(ref));
     return () => observer.disconnect();
-  }, []);
+  }, [sections.length]);
```

This redeploys the observer when the section count changes.

---

`56-58`: **Avoid clearing all localStorage – target only StarSync keys**

`localStorage.clear()` removes *everything*, including data unrelated to StarSync (e.g., auth tokens from other sub-domains during local development).  
Prefer a scoped key or prefix:

```diff
-const handleClearStorage = () => {
-  localStorage.clear();
-};
+const handleClearStorage = () => {
+  Object.keys(localStorage)
+    .filter((k) => k.startsWith('starsync_'))
+    .forEach((k) => localStorage.removeItem(k));
+};
```

This preserves other applications’ data while keeping your intent.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c6ab4948f1d74d553f37b040d17346716f4e5018 and 71829c360b06f7dd8199c5a5ce91d7b9cb3ca545.

</details>

<details>
<summary>⛔ Files ignored due to path filters (8)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`
* `src/assets/images/test/bgimg1.png` is excluded by `!**/*.png`
* `src/assets/images/test/bgimg2.png` is excluded by `!**/*.png`
* `src/assets/images/test/bgimg3.png` is excluded by `!**/*.png`
* `src/assets/images/test/bgimg4.png` is excluded by `!**/*.png`
* `src/assets/images/test/screen1.png` is excluded by `!**/*.png`
* `src/assets/images/test/screen2.png` is excluded by `!**/*.png`
* `src/assets/images/test/screen3.png` is excluded by `!**/*.png`

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `package.json` (1 hunks)
* `src/pages/landing/LandingPage.jsx` (1 hunks)
* `src/pages/landing/landingPage.styles.js` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>src/pages/landing/landingPage.styles.js (1)</summary><blockquote>

<details>
<summary>src/styles/responsive.js (1)</summary>

* `media` (14-14)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>src/pages/landing/LandingPage.jsx (1)</summary>

`1-4`: **Import path inconsistent with declared dependency**

If you switch `package.json` to `framer-motion` (see previous comment), this line is correct.  
If you keep `"motion"`, change the import to `'motion'`.

Synchronise both sides to prevent module-not-found errors.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-25T21:36:47Z
user: login: pappaya109
body: 고생하셨습니다~
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:11:05Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (7)</summary><blockquote>

<details>
<summary>src/pages/landing/LandingPage.jsx (7)</summary><blockquote>

`7-21`: **Consider separating star data into a separate file**

The stars array contains data for positioning and sizing star elements on the page. For better code organization and maintainability, consider moving this data to a separate file like `src/pages/landing/data/stars.js`.

---

`23-50`: **Consider separating section content into a separate file**

The sections array contains all the content for the landing page sections. For better maintainability and potential future localization, consider moving this data to a separate file like `src/pages/landing/data/sections.js`.

---

`60-78`: **Good use of IntersectionObserver for scroll detection**

The implementation of IntersectionObserver to track the visible section is well done. However, consider wrapping the observer setup in a custom hook for better reusability and testability.

---

`126-126`: **Consider enhancing the animation state transition**

The current animation only changes opacity and y-position based on whether a section is active. To create a more engaging experience, consider adding staggered animations for child elements.

```diff
animate={{ 
  opacity: activeIndex === idx ? 1 : 0.3, 
  y: activeIndex === idx ? 0 : 50 
}}
```

---

`131-135`: **Consider making each section's button URL configurable**

Currently, all buttons navigate to the "/list" route regardless of the section. For better flexibility, consider adding a `buttonUrl` property to each section object.

```diff
// In the sections array
{
  id: 'voting',
  title: '투명한 투표 시스템',
  description: '투명하고 신뢰할 수 있는 투표 시스템으로 여러분의 소중한 한 표를 행사하세요.',
  buttonText: '투표하러 가기',
+ buttonUrl: '/voting', // or keep as '/list' if that's the intention
},

// In the JSX
-<Link to="/list" onClick={handleClearStorage}>
+<Link to={section.buttonUrl || "/list"} onClick={handleClearStorage}>
```

---

`136-145`: **Enhance scroll guide with animation**

The scroll guide button would benefit from an animated indicator (like an arrow or bouncing dot) to make it more intuitive for users. Consider adding an animation to draw attention to this navigation element.

---

`53-54`: **Use a more descriptive name for the refs array**

`sectionRefs` is fine, but a more descriptive name like `sectionElementRefs` would better indicate that these refs are holding DOM elements for the sections.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 71829c360b06f7dd8199c5a5ce91d7b9cb3ca545 and 8863c7f93bb3fa66d0bcb6dbd601420502865acb.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/pages/landing/LandingPage.jsx` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>src/pages/landing/LandingPage.jsx (1)</summary>

`92-149`: **Well-structured landing page with multiple sections and navigation**

The landing page implementation follows good practices with:
- A starry animated background
- Vertical dot navigation for section selection
- Multiple content sections with smooth transitions
- Accessible navigation elements with aria labels

This aligns well with the PR objective to apply design elements to the landing page using a full-page scroll method.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-26T04:14:05Z
user: login: Insung-Jo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:14:08Z
user: login: Insung-Jo
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:14:12Z
user: login: yuj2n
body: 지섭님 랜딩 페이지 디자인 수고 많으셨고 너무 무리하신 것 같아서 얼른 쉬셨으면 좋겠습니다!!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:25:22Z
user: login: HarrySeop
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:29:46Z
user: login: HarrySeop
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:30:12Z
user: login: HarrySeop
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:30:32Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-26T04:30:35Z
user: login: HarrySeop
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:32:20Z
user: login: HarrySeop
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:32:53Z
user: login: HarrySeop
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T04:33:14Z

Review comments:
diff_hunk: @@ -14,6 +14,7 @@
     "@emotion/styled": "^11.14.0",
     "axios": "^1.8.4",
     "facepaint": "^1.2.1",
+    "motion": "^12.9.0",
     "react": "^19.0.0",
path: package.json
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Dependency name does not match the import – use `framer-motion` instead of `motion`**

The component imports `motion` from `framer-motion`, but `package.json` adds `"motion": "^12.9.0"`. At runtime the bundler will fail to resolve `framer-motion`, breaking the landing page.

```diff
-    "motion": "^12.9.0",
+    "framer-motion": "^12.9.0",
```

If you intended to migrate to the new standalone `motion` package, you also need to:

1. Replace every `import { motion } from 'framer-motion'` with `import { motion } from 'motion'`.
2. Verify the API parity; some props were renamed in the new package.

Please align the dependency and the import in the same PR to avoid build-time errors.  


Run the script below after applying the fix to be sure there are no stale imports:

---

🏁 Script executed:

```shell
#!/bin/bash
# Find all React files still importing framer-motion
rg -n $'from .framer-motion.' --glob '*.{js,jsx,ts,tsx}'
```

Length of output: 133

---



</details>

**Update package.json dependency to match your imports**

We still have one `framer-motion` import in src/pages/landing/LandingPage.jsx:2, so the package must be named accordingly:

• Replace in package.json  
```diff
-    "motion": "^12.9.0",
+    "framer-motion": "^12.9.0",
```

• Run your grep script again after installing to ensure no mismatches remain:  
```bash
rg -n "from 'framer-motion'" --glob '*.{js,jsx,ts,tsx}'
```

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-25T21:36:46Z
author_association: NONE
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
path: src/pages/landing/LandingPage.jsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Incorrect import path for framer-motion**

The import path `motion/react` is incorrect. Based on the official documentation and standard usage, it should be imported from `framer-motion`.

```diff
-import { motion } from 'motion/react';
+import { motion } from 'framer-motion';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
-import { motion } from 'motion/react';
+import { motion } from 'framer-motion';
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-26T04:14:04Z
author_association: NONE
diff_hunk: @@ -1,8 +1,271 @@
-import { css } from '@emotion/react';
+import starImg from '@/assets/images/star.png';
+import media from '@/styles/responsive';
+import { css, keyframes } from '@emotion/react';
 
-export const landingWrapper = css`
+const auroraMove = keyframes`
+  0% {
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+  }
+  50% {
+    background-position: 50% 50%, 50% 0%, 0% 100%;
+  }
+  100% {
+    background-position: 100% 100%, 0% 50%, 50% 0%;
+  }
+`;
+
+const twinkle = keyframes` 
+  0%, 100% {
+    opacity: 0.3;
+  }
+  50% {
+    opacity: 0.6;
+  }
+`;
+
+const glow = keyframes`
+  0%, 100% {
+    text-shadow: 0 0 8px var(--pink), 0 0 16px var(--blue-sky), 0 0 24px var(--purple);
+  }
+  50% {
+    text-shadow: 0 0 12px var(--pink), 0 0 24px var(--blue-sky), 0 0 36px var(--purple);
+  }
+`;
+
+export const pageContainer = css`
+  overflow: hidden auto;
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  color: var(--white);
+  background: var(--black-deep);
+  scroll-snap-type: y mandatory;
+  scroll-behavior: smooth;
+  -ms-overflow-style: none;
+  scrollbar-width: none;
+
+  &::-webkit-scrollbar {
+    display: none;
+  }
+
+  &::before {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 0;
+    width: 200%;
+    height: 200%;
+    background: radial-gradient(circle, var(--purple), transparent 70%),
+                radial-gradient(circle, var(--blue-dark), transparent 70%),
+                radial-gradient(circle, var(--pink-purple), transparent 70%);
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+    background-size: 50% 50%;
+    animation: ${auroraMove} 10s infinite alternate;
+    content: '';
+    opacity: 0.5;
+  }
+`;
+
+export const backgroundStarsWrapper = css`
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 1;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+`;
+
+export const starStyle = ({ top, left, width, height }) => css`
+  position: absolute;
+  top: ${top};
+  left: ${left};
+  width: ${width}px;
+  height: ${height}px;
+  background: url(${starImg}) no-repeat center center;
+  background-size: contain;
+  opacity: 0.3;
+  animation: ${twinkle} 3s infinite;
+`;
+
+export const section = css`
   display: flex;
+  position: relative;
+  z-index: 2;
   justify-content: center;
   align-items: center;
+  width: 100%;
   height: 100vh;
+  padding: 0 2rem;
+  scroll-snap-align: start;
+  scroll-snap-stop: always;
+`;
+
+export const sectionContent = css`
+  display: flex;
+  position: relative;
+  z-index: 1;
+  flex-direction: column;
+  gap: 1rem;
+  width: 100%;
+  max-width: 144rem;
+  margin: 0 auto;
+  padding: 2rem;
+  padding-bottom: 6rem;
+  text-align: center;
+
+  ${media({
+    gap: ['2.4rem', '2.8rem', '2rem', '2rem'],
+    padding: ['2.4rem', '2.8rem', '3.2rem', '3.6rem'],
+    paddingBottom: ['8rem', '8rem', '8rem', '10rem'],
+  })}
+
+  h1 {
+    font-size: 3.2rem;
+    font-weight: 900;
+    line-height: 1.3;
+    letter-spacing: 0.2rem;
+    word-break: keep-all;
+    color: var(--white);
+    text-shadow: 2px 2px 4px rgb(0 0 0 / 70%);
+    cursor: pointer;
+    transition: opacity 0.3s ease;
+    animation: ${glow} 2s infinite ease-in-out;
+    
+    &:hover {
+      opacity: 0.8;
+    }
+    
+    ${media({
+      fontSize: ['3.2rem', '4rem', '5rem', '6rem'],
+      letterSpacing: ['0.2rem', '0.3rem', '0.4rem', '0.5rem'],
+      lineHeight: ['1.3', '1.2', '1.2', '1.2'],
+    })}
+  }
+
+  p {
+    max-width: 80rem;
+    margin: 0 auto;
+    padding: 0 1.6rem;
+    font-size: 1.4rem;
+    font-weight: 500;
+    line-height: 1.6;
+    white-space: pre-line;
+    word-break: keep-all;
+    color: var(--white);
+    text-shadow: 2px 2px 4px rgb(0 0 0 / 70%);
+    cursor: pointer;
+    transition: opacity 0.3s ease;
+
+    &:hover {
+      opacity: 0.8;
+    }
+    
+    ${media({
+      fontSize: ['1.6rem', '1.8rem', '2rem', '2.4rem'],
+      maxWidth: ['30rem', '50rem', '60rem', '80rem'],
+      lineHeight: ['1.7', '1.7', '1.7', '1.7'],
+      padding: ['1.6rem', '1.6rem', '2rem', '2.4rem'],
+    })}
+  }
+
+  a {
+    margin-top: 1.6rem;
+    
+    ${media({
+      marginTop: ['1.6rem', '2rem', '2.4rem', '2.8rem'],
+    })}
+  }
+`;
+
+export const navDots = css`
+  display: flex;
+  position: fixed;
+  top: 50%;
+  right: 1rem;
+  z-index: 10;
+  flex-direction: column;
+  gap: 1rem;
+  transform: translateY(-50%);
+  
+  ${media({
+    right: ['1rem', '1.5rem', '2rem', '3rem'],
+    gap: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+  })}
+`;
+
+export const dot = css`
+  width: 0.8rem;
+  height: 0.8rem;
+  padding: 0;
+  border: none;
+  border-radius: 50%;
+  background-color: var(--white);
+  opacity: 0.5;
+  cursor: pointer;
+  transition: all 0.4s ease;
+  
+
+  &:hover {
+    opacity: 0.8;
+    transform: scale(1.1);
+  }
+  
+  ${media({
+    width: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+    height: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+  })}
+`;
+
+export const activeDot = css`
+  opacity: 1;
+  transform: scale(1.2);
+`;
+
+export const scrollGuide = css`
+  display: inline-flex;
+  position: absolute;
+  bottom: 1rem;
+  left: 50%;
+  align-items: center;
+  gap: 1.2rem;
+  padding: 1rem;
+  border: none;
+  font-size: 1.4rem;
+  white-space: nowrap;
+  color: var(--white);
+  background: none;
+  cursor: pointer;
+  transition: opacity 0.3s ease;
+  animation: bounce 2s infinite;
+  transform: translateX(-50%);
+  
+  &::before {
+    content: '↓';
+    font-size: 2rem;
+    order: -1;
+  }
+
+  &:hover {
+    opacity: 0.8;
+  }
+  
+  ${media({
+    fontSize: ['1.2rem', '1.4rem', '1.6rem', '1.8rem'],
+    bottom: ['1rem', '1.4rem', '1.8rem', '2.2rem'],
+  })}
+
+  @keyframes bounce {
+    0%, 20%, 50%, 80%, 100% {
+      transform: translateX(-50%) translateY(0);
+    }
+
+    40% {
+      transform: translateX(-50%) translateY(-0.5rem);
+    }
+
+    60% {
+      transform: translateX(-50%) translateY(-0.3rem);
+    }
+  }
path: src/pages/landing/landingPage.styles.js
user: login: Insung-Jo
body: 여기는  리팩토링 할 때 분리 해야 할 거 같네요
created_at: 2025-04-26T04:14:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
+import { useEffect, useRef, useState } from 'react';
 import { Link } from 'react-router-dom';
 import * as S from './landingPage.styles';
 
+const stars = [
+  { id: 1, top: '10%', left: '15%', width: 30, height: 30 },
+  { id: 2, top: '70%', left: '80%', width: 40, height: 40 },
+  { id: 3, top: '50%', left: '10%', width: 20, height: 20 },
+  { id: 4, top: '20%', left: '30%', width: 50, height: 50 },
+  { id: 5, top: '10%', left: '80%', width: 30, height: 30 },
+  { id: 6, top: '80%', left: '20%', width: 30, height: 30 },
+  { id: 7, top: '85%', left: '85%', width: 25, height: 25 },
+  { id: 8, top: '5%', left: '50%', width: 35, height: 35 },
+  { id: 9, top: '60%', left: '60%', width: 20, height: 20 },
+  { id: 10, top: '40%', left: '70%', width: 30, height: 30 },
+  { id: 11, top: '25%', left: '5%', width: 20, height: 20 },
+  { id: 12, top: '90%', left: '45%', width: 30, height: 30 },
+  { id: 13, top: '50%', left: '90%', width: 30, height: 30 },
+];
+
+const sections = [
+  {
+    id: 'main',
+    title: 'StarSync',
+    description: '후원과 투표를 통해 아이돌을 응원하세요. 당신의 선택이 그들의 무대가 됩니다.',
+    buttonText: '지금 시작하기',
+    showScrollGuide: true,
+  },
+  {
+    id: 'voting',
+    title: '투명한 투표 시스템',
+    description: '투명하고 신뢰할 수 있는 투표 시스템으로 여러분의 소중한 한 표를 행사하세요.',
+    buttonText: '투표하러 가기',
+  },
+  {
+    id: 'funding',
+    title: '팬덤의 힘을 보여주세요',
+    description: '여러분이 좋아하는 아이돌의 활동을 직접 후원하고 특별한 혜택도 받아가세요.',
+    buttonText: '후원하러 가기',
+  },
+  {
+    id: 'my-idol',
+    title: '나만의 아이돌을 위한 특별한 공간',
+    description:
+      '여러 아이돌 중에서 오직 당신의 최애만 선택하고 팬심을 가장 나다운 방식으로 표현해보세요.',
+    buttonText: '내 최애 선택하기',
+  },
+];
+
path: src/pages/landing/LandingPage.jsx
user: login: yuj2n
body: 이 내용 중에서 랜덤으로 보여지는 걸까요?
created_at: 2025-04-26T04:22:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
+import { useEffect, useRef, useState } from 'react';
 import { Link } from 'react-router-dom';
 import * as S from './landingPage.styles';
 
+const stars = [
+  { id: 1, top: '10%', left: '15%', width: 30, height: 30 },
+  { id: 2, top: '70%', left: '80%', width: 40, height: 40 },
+  { id: 3, top: '50%', left: '10%', width: 20, height: 20 },
+  { id: 4, top: '20%', left: '30%', width: 50, height: 50 },
+  { id: 5, top: '10%', left: '80%', width: 30, height: 30 },
+  { id: 6, top: '80%', left: '20%', width: 30, height: 30 },
+  { id: 7, top: '85%', left: '85%', width: 25, height: 25 },
+  { id: 8, top: '5%', left: '50%', width: 35, height: 35 },
+  { id: 9, top: '60%', left: '60%', width: 20, height: 20 },
+  { id: 10, top: '40%', left: '70%', width: 30, height: 30 },
+  { id: 11, top: '25%', left: '5%', width: 20, height: 20 },
+  { id: 12, top: '90%', left: '45%', width: 30, height: 30 },
+  { id: 13, top: '50%', left: '90%', width: 30, height: 30 },
+];
+
+const sections = [
+  {
+    id: 'main',
+    title: 'StarSync',
+    description: '후원과 투표를 통해 아이돌을 응원하세요. 당신의 선택이 그들의 무대가 됩니다.',
+    buttonText: '지금 시작하기',
+    showScrollGuide: true,
+  },
+  {
+    id: 'voting',
+    title: '투명한 투표 시스템',
+    description: '투명하고 신뢰할 수 있는 투표 시스템으로 여러분의 소중한 한 표를 행사하세요.',
+    buttonText: '투표하러 가기',
+  },
+  {
+    id: 'funding',
+    title: '팬덤의 힘을 보여주세요',
+    description: '여러분이 좋아하는 아이돌의 활동을 직접 후원하고 특별한 혜택도 받아가세요.',
+    buttonText: '후원하러 가기',
+  },
+  {
+    id: 'my-idol',
+    title: '나만의 아이돌을 위한 특별한 공간',
+    description:
+      '여러 아이돌 중에서 오직 당신의 최애만 선택하고 팬심을 가장 나다운 방식으로 표현해보세요.',
+    buttonText: '내 최애 선택하기',
+  },
+];
+
 const LandingPage = () => {
+  const sectionRefs = useRef([]);
+  const [activeIndex, setActiveIndex] = useState(0);
+
   const handleClearStorage = () => {
     localStorage.clear();
   };
 
+  useEffect(() => {
+    const observer = new IntersectionObserver(
path: src/pages/landing/LandingPage.jsx
user: login: yuj2n
body: IntersectionObserver 함수는 어떻게 쓰는걸까요?
created_at: 2025-04-26T04:23:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
+import { useEffect, useRef, useState } from 'react';
 import { Link } from 'react-router-dom';
 import * as S from './landingPage.styles';
 
+const stars = [
+  { id: 1, top: '10%', left: '15%', width: 30, height: 30 },
+  { id: 2, top: '70%', left: '80%', width: 40, height: 40 },
+  { id: 3, top: '50%', left: '10%', width: 20, height: 20 },
+  { id: 4, top: '20%', left: '30%', width: 50, height: 50 },
+  { id: 5, top: '10%', left: '80%', width: 30, height: 30 },
+  { id: 6, top: '80%', left: '20%', width: 30, height: 30 },
+  { id: 7, top: '85%', left: '85%', width: 25, height: 25 },
+  { id: 8, top: '5%', left: '50%', width: 35, height: 35 },
+  { id: 9, top: '60%', left: '60%', width: 20, height: 20 },
+  { id: 10, top: '40%', left: '70%', width: 30, height: 30 },
+  { id: 11, top: '25%', left: '5%', width: 20, height: 20 },
+  { id: 12, top: '90%', left: '45%', width: 30, height: 30 },
+  { id: 13, top: '50%', left: '90%', width: 30, height: 30 },
+];
+
+const sections = [
+  {
+    id: 'main',
+    title: 'StarSync',
+    description: '후원과 투표를 통해 아이돌을 응원하세요. 당신의 선택이 그들의 무대가 됩니다.',
+    buttonText: '지금 시작하기',
+    showScrollGuide: true,
+  },
+  {
+    id: 'voting',
+    title: '투명한 투표 시스템',
+    description: '투명하고 신뢰할 수 있는 투표 시스템으로 여러분의 소중한 한 표를 행사하세요.',
+    buttonText: '투표하러 가기',
+  },
+  {
+    id: 'funding',
+    title: '팬덤의 힘을 보여주세요',
+    description: '여러분이 좋아하는 아이돌의 활동을 직접 후원하고 특별한 혜택도 받아가세요.',
+    buttonText: '후원하러 가기',
+  },
+  {
+    id: 'my-idol',
+    title: '나만의 아이돌을 위한 특별한 공간',
+    description:
+      '여러 아이돌 중에서 오직 당신의 최애만 선택하고 팬심을 가장 나다운 방식으로 표현해보세요.',
+    buttonText: '내 최애 선택하기',
+  },
+];
+
 const LandingPage = () => {
+  const sectionRefs = useRef([]);
+  const [activeIndex, setActiveIndex] = useState(0);
+
   const handleClearStorage = () => {
     localStorage.clear();
   };
 
+  useEffect(() => {
+    const observer = new IntersectionObserver(
+      (entries) => {
+        for (const entry of entries) {
+          if (entry.isIntersecting) {
+            const index = Number(entry.target.getAttribute('data-index'));
+            setActiveIndex(index);
+          }
+        }
+      },
+      { threshold: 0.5 },
+    );
+
+    for (const ref of sectionRefs.current) {
+      if (ref) observer.observe(ref);
+    }
+
+    return () => observer.disconnect();
+  }, []);
+
+  const handleSmoothScroll = (index) => {
+    sectionRefs.current[index]?.scrollIntoView({
+      behavior: 'smooth',
+      block: 'start',
+    });
+  };
path: src/pages/landing/LandingPage.jsx
user: login: yuj2n
body: smooth하게 주셨다는 게 이거군요!
created_at: 2025-04-26T04:24:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,8 +1,271 @@
-import { css } from '@emotion/react';
+import starImg from '@/assets/images/star.png';
+import media from '@/styles/responsive';
+import { css, keyframes } from '@emotion/react';
 
-export const landingWrapper = css`
+const auroraMove = keyframes`
+  0% {
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+  }
+  50% {
+    background-position: 50% 50%, 50% 0%, 0% 100%;
+  }
+  100% {
+    background-position: 100% 100%, 0% 50%, 50% 0%;
+  }
+`;
+
+const twinkle = keyframes` 
+  0%, 100% {
+    opacity: 0.3;
+  }
+  50% {
+    opacity: 0.6;
+  }
+`;
+
+const glow = keyframes`
+  0%, 100% {
+    text-shadow: 0 0 8px var(--pink), 0 0 16px var(--blue-sky), 0 0 24px var(--purple);
+  }
+  50% {
+    text-shadow: 0 0 12px var(--pink), 0 0 24px var(--blue-sky), 0 0 36px var(--purple);
+  }
+`;
+
+export const pageContainer = css`
+  overflow: hidden auto;
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  color: var(--white);
+  background: var(--black-deep);
+  scroll-snap-type: y mandatory;
+  scroll-behavior: smooth;
+  -ms-overflow-style: none;
+  scrollbar-width: none;
+
+  &::-webkit-scrollbar {
+    display: none;
+  }
+
+  &::before {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 0;
+    width: 200%;
+    height: 200%;
+    background: radial-gradient(circle, var(--purple), transparent 70%),
+                radial-gradient(circle, var(--blue-dark), transparent 70%),
+                radial-gradient(circle, var(--pink-purple), transparent 70%);
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+    background-size: 50% 50%;
+    animation: ${auroraMove} 10s infinite alternate;
+    content: '';
+    opacity: 0.5;
+  }
+`;
+
+export const backgroundStarsWrapper = css`
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 1;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+`;
+
+export const starStyle = ({ top, left, width, height }) => css`
+  position: absolute;
+  top: ${top};
+  left: ${left};
+  width: ${width}px;
+  height: ${height}px;
+  background: url(${starImg}) no-repeat center center;
+  background-size: contain;
+  opacity: 0.3;
+  animation: ${twinkle} 3s infinite;
+`;
+
+export const section = css`
   display: flex;
+  position: relative;
+  z-index: 2;
   justify-content: center;
   align-items: center;
+  width: 100%;
   height: 100vh;
+  padding: 0 2rem;
+  scroll-snap-align: start;
+  scroll-snap-stop: always;
+`;
+
+export const sectionContent = css`
+  display: flex;
+  position: relative;
+  z-index: 1;
+  flex-direction: column;
+  gap: 1rem;
+  width: 100%;
+  max-width: 144rem;
+  margin: 0 auto;
+  padding: 2rem;
+  padding-bottom: 6rem;
+  text-align: center;
+
+  ${media({
+    gap: ['2.4rem', '2.8rem', '2rem', '2rem'],
+    padding: ['2.4rem', '2.8rem', '3.2rem', '3.6rem'],
+    paddingBottom: ['8rem', '8rem', '8rem', '10rem'],
+  })}
+
+  h1 {
+    font-size: 3.2rem;
+    font-weight: 900;
+    line-height: 1.3;
+    letter-spacing: 0.2rem;
+    word-break: keep-all;
+    color: var(--white);
+    text-shadow: 2px 2px 4px rgb(0 0 0 / 70%);
+    cursor: pointer;
+    transition: opacity 0.3s ease;
+    animation: ${glow} 2s infinite ease-in-out;
path: src/pages/landing/landingPage.styles.js
user: login: yuj2n
body: glow 효과!!
created_at: 2025-04-26T04:24:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
path: src/pages/landing/LandingPage.jsx
user: login: HarrySeop
body: 이게 공식문서에서의 import 방식이라 수정하지 않을 것 같아
created_at: 2025-04-26T04:29:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,8 +1,271 @@
-import { css } from '@emotion/react';
+import starImg from '@/assets/images/star.png';
+import media from '@/styles/responsive';
+import { css, keyframes } from '@emotion/react';
 
-export const landingWrapper = css`
+const auroraMove = keyframes`
+  0% {
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+  }
+  50% {
+    background-position: 50% 50%, 50% 0%, 0% 100%;
+  }
+  100% {
+    background-position: 100% 100%, 0% 50%, 50% 0%;
+  }
+`;
+
+const twinkle = keyframes` 
+  0%, 100% {
+    opacity: 0.3;
+  }
+  50% {
+    opacity: 0.6;
+  }
+`;
+
+const glow = keyframes`
+  0%, 100% {
+    text-shadow: 0 0 8px var(--pink), 0 0 16px var(--blue-sky), 0 0 24px var(--purple);
+  }
+  50% {
+    text-shadow: 0 0 12px var(--pink), 0 0 24px var(--blue-sky), 0 0 36px var(--purple);
+  }
+`;
+
+export const pageContainer = css`
+  overflow: hidden auto;
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  color: var(--white);
+  background: var(--black-deep);
+  scroll-snap-type: y mandatory;
+  scroll-behavior: smooth;
+  -ms-overflow-style: none;
+  scrollbar-width: none;
+
+  &::-webkit-scrollbar {
+    display: none;
+  }
+
+  &::before {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 0;
+    width: 200%;
+    height: 200%;
+    background: radial-gradient(circle, var(--purple), transparent 70%),
+                radial-gradient(circle, var(--blue-dark), transparent 70%),
+                radial-gradient(circle, var(--pink-purple), transparent 70%);
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+    background-size: 50% 50%;
+    animation: ${auroraMove} 10s infinite alternate;
+    content: '';
+    opacity: 0.5;
+  }
+`;
+
+export const backgroundStarsWrapper = css`
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 1;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+`;
+
+export const starStyle = ({ top, left, width, height }) => css`
+  position: absolute;
+  top: ${top};
+  left: ${left};
+  width: ${width}px;
+  height: ${height}px;
+  background: url(${starImg}) no-repeat center center;
+  background-size: contain;
+  opacity: 0.3;
+  animation: ${twinkle} 3s infinite;
+`;
+
+export const section = css`
   display: flex;
+  position: relative;
+  z-index: 2;
   justify-content: center;
   align-items: center;
+  width: 100%;
   height: 100vh;
+  padding: 0 2rem;
+  scroll-snap-align: start;
+  scroll-snap-stop: always;
+`;
+
+export const sectionContent = css`
+  display: flex;
+  position: relative;
+  z-index: 1;
+  flex-direction: column;
+  gap: 1rem;
+  width: 100%;
+  max-width: 144rem;
+  margin: 0 auto;
+  padding: 2rem;
+  padding-bottom: 6rem;
+  text-align: center;
+
+  ${media({
+    gap: ['2.4rem', '2.8rem', '2rem', '2rem'],
+    padding: ['2.4rem', '2.8rem', '3.2rem', '3.6rem'],
+    paddingBottom: ['8rem', '8rem', '8rem', '10rem'],
+  })}
+
+  h1 {
+    font-size: 3.2rem;
+    font-weight: 900;
+    line-height: 1.3;
+    letter-spacing: 0.2rem;
+    word-break: keep-all;
+    color: var(--white);
+    text-shadow: 2px 2px 4px rgb(0 0 0 / 70%);
+    cursor: pointer;
+    transition: opacity 0.3s ease;
+    animation: ${glow} 2s infinite ease-in-out;
+    
+    &:hover {
+      opacity: 0.8;
+    }
+    
+    ${media({
+      fontSize: ['3.2rem', '4rem', '5rem', '6rem'],
+      letterSpacing: ['0.2rem', '0.3rem', '0.4rem', '0.5rem'],
+      lineHeight: ['1.3', '1.2', '1.2', '1.2'],
+    })}
+  }
+
+  p {
+    max-width: 80rem;
+    margin: 0 auto;
+    padding: 0 1.6rem;
+    font-size: 1.4rem;
+    font-weight: 500;
+    line-height: 1.6;
+    white-space: pre-line;
+    word-break: keep-all;
+    color: var(--white);
+    text-shadow: 2px 2px 4px rgb(0 0 0 / 70%);
+    cursor: pointer;
+    transition: opacity 0.3s ease;
+
+    &:hover {
+      opacity: 0.8;
+    }
+    
+    ${media({
+      fontSize: ['1.6rem', '1.8rem', '2rem', '2.4rem'],
+      maxWidth: ['30rem', '50rem', '60rem', '80rem'],
+      lineHeight: ['1.7', '1.7', '1.7', '1.7'],
+      padding: ['1.6rem', '1.6rem', '2rem', '2.4rem'],
+    })}
+  }
+
+  a {
+    margin-top: 1.6rem;
+    
+    ${media({
+      marginTop: ['1.6rem', '2rem', '2.4rem', '2.8rem'],
+    })}
+  }
+`;
+
+export const navDots = css`
+  display: flex;
+  position: fixed;
+  top: 50%;
+  right: 1rem;
+  z-index: 10;
+  flex-direction: column;
+  gap: 1rem;
+  transform: translateY(-50%);
+  
+  ${media({
+    right: ['1rem', '1.5rem', '2rem', '3rem'],
+    gap: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+  })}
+`;
+
+export const dot = css`
+  width: 0.8rem;
+  height: 0.8rem;
+  padding: 0;
+  border: none;
+  border-radius: 50%;
+  background-color: var(--white);
+  opacity: 0.5;
+  cursor: pointer;
+  transition: all 0.4s ease;
+  
+
+  &:hover {
+    opacity: 0.8;
+    transform: scale(1.1);
+  }
+  
+  ${media({
+    width: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+    height: ['0.8rem', '1rem', '1.2rem', '1.4rem'],
+  })}
+`;
+
+export const activeDot = css`
+  opacity: 1;
+  transform: scale(1.2);
+`;
+
+export const scrollGuide = css`
+  display: inline-flex;
+  position: absolute;
+  bottom: 1rem;
+  left: 50%;
+  align-items: center;
+  gap: 1.2rem;
+  padding: 1rem;
+  border: none;
+  font-size: 1.4rem;
+  white-space: nowrap;
+  color: var(--white);
+  background: none;
+  cursor: pointer;
+  transition: opacity 0.3s ease;
+  animation: bounce 2s infinite;
+  transform: translateX(-50%);
+  
+  &::before {
+    content: '↓';
+    font-size: 2rem;
+    order: -1;
+  }
+
+  &:hover {
+    opacity: 0.8;
+  }
+  
+  ${media({
+    fontSize: ['1.2rem', '1.4rem', '1.6rem', '1.8rem'],
+    bottom: ['1rem', '1.4rem', '1.8rem', '2.2rem'],
+  })}
+
+  @keyframes bounce {
+    0%, 20%, 50%, 80%, 100% {
+      transform: translateX(-50%) translateY(0);
+    }
+
+    40% {
+      transform: translateX(-50%) translateY(-0.5rem);
+    }
+
+    60% {
+      transform: translateX(-50%) translateY(-0.3rem);
+    }
+  }
path: src/pages/landing/landingPage.styles.js
user: login: HarrySeop
body: 넵 일단 전체 테스트 후 리팩토링때 적용할 예정입니다!
created_at: 2025-04-26T04:30:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
+import { useEffect, useRef, useState } from 'react';
 import { Link } from 'react-router-dom';
 import * as S from './landingPage.styles';
 
+const stars = [
+  { id: 1, top: '10%', left: '15%', width: 30, height: 30 },
+  { id: 2, top: '70%', left: '80%', width: 40, height: 40 },
+  { id: 3, top: '50%', left: '10%', width: 20, height: 20 },
+  { id: 4, top: '20%', left: '30%', width: 50, height: 50 },
+  { id: 5, top: '10%', left: '80%', width: 30, height: 30 },
+  { id: 6, top: '80%', left: '20%', width: 30, height: 30 },
+  { id: 7, top: '85%', left: '85%', width: 25, height: 25 },
+  { id: 8, top: '5%', left: '50%', width: 35, height: 35 },
+  { id: 9, top: '60%', left: '60%', width: 20, height: 20 },
+  { id: 10, top: '40%', left: '70%', width: 30, height: 30 },
+  { id: 11, top: '25%', left: '5%', width: 20, height: 20 },
+  { id: 12, top: '90%', left: '45%', width: 30, height: 30 },
+  { id: 13, top: '50%', left: '90%', width: 30, height: 30 },
+];
+
+const sections = [
+  {
+    id: 'main',
+    title: 'StarSync',
+    description: '후원과 투표를 통해 아이돌을 응원하세요. 당신의 선택이 그들의 무대가 됩니다.',
+    buttonText: '지금 시작하기',
+    showScrollGuide: true,
+  },
+  {
+    id: 'voting',
+    title: '투명한 투표 시스템',
+    description: '투명하고 신뢰할 수 있는 투표 시스템으로 여러분의 소중한 한 표를 행사하세요.',
+    buttonText: '투표하러 가기',
+  },
+  {
+    id: 'funding',
+    title: '팬덤의 힘을 보여주세요',
+    description: '여러분이 좋아하는 아이돌의 활동을 직접 후원하고 특별한 혜택도 받아가세요.',
+    buttonText: '후원하러 가기',
+  },
+  {
+    id: 'my-idol',
+    title: '나만의 아이돌을 위한 특별한 공간',
+    description:
+      '여러 아이돌 중에서 오직 당신의 최애만 선택하고 팬심을 가장 나다운 방식으로 표현해보세요.',
+    buttonText: '내 최애 선택하기',
+  },
+];
+
path: src/pages/landing/LandingPage.jsx
user: login: HarrySeop
body: 아뇨 처음부터 순서대로 보여집니다!
created_at: 2025-04-26T04:30:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
path: src/pages/landing/LandingPage.jsx
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@HarrySeop 죄송합니다, 제가 잘못 이해했네요. 확인해보니 패키지 `motion` v12.9.0을 사용하고 계시는 것 같은데, 해당 패키지의 공식 문서에 따르면 `import { motion } from 'motion/react'`가 맞는 import 방식입니다. "framer-motion"과는 다른 라이브러리네요. 정확한 정보 공유해주셔서 감사합니다!



---

🌐 Web query:

```web
Documentation for "motion" package version 12.9.0 npm javascript import syntax
```

💡 Result:

## Import Syntax for the "motion" Package (version 12.9.0)

**For JavaScript (Vanilla/DOM):**

To use the core Motion animation functions (like `animate` and `scroll`) in a non-React JavaScript project, install the package via npm:

```bash
npm install motion
```

Then import the functions you need:

```javascript
import { animate, scroll } from "motion"
```

Example usage:

```javascript
import { animate } from "motion"

animate(".box", { rotate: 360 })
```
This approach directly imports the named exports from the `"motion"` package for use in standard JavaScript environments[4].

---

**For React Projects:**

If you're using Motion in a React project, install the package as above, then import the special `motion` component from the `"motion/react"` subpath:

```javascript
import { motion } from "motion/react"
```

Example usage in a React component:

```javascript
import { motion } from "motion/react"

export function Component({ isVisible }) {
  return <motion.div animate={{ opacity: isVisible ? 1 : 0 }} />
}
```
This syntax enables the use of the `<motion.div>` component for declarative animations in React[1][2][4].

---

**ESM CDN Import (Optional, for HTML projects):**

You can also use Motion directly in the browser via a CDN:

```html
<script type="module">
  import { animate, scroll } from "https://cdn.jsdelivr.net/npm/motion@12.9.0/+esm"
  animate(".box", { x: 100 })
</script>
```
It's best practice to specify the version (e.g., `@12.9.0`) rather than "latest" for stability[4].

---

### Summary Table

| Environment        | Install Command            | Import Syntax                          | Example Use                                    |
|--------------------|---------------------------|----------------------------------------|------------------------------------------------|
| Vanilla JS / DOM   | npm install motion        | import { animate } from "motion"       | animate(".box", { x: 100 })                    |
| React              | npm install motion        | import { motion } from "motion/react"  | <motion.div animate={{ x: 100 }} />            |
| CDN (ESM/HTML)     | n/a                       | import { animate } from "cdn-url"      | animate(".box", { x: 100 })                    |

---

**References:**  
- Official npm documentation and examples for the `motion` package[1][2][4].

Citations:

- 1: https://www.npmjs.com/package/motion
- 2: https://classic.yarnpkg.com/en/package/framer-motion
- 3: https://www.npmjs.com/package/motion-dom
- 4: https://motion.dev/docs/quick-start
- 5: https://docs.gitlab.com/ee/user/packages/npm_registry/
- 6: https://help.webstudio.is/js-libraries-via-npm-WchmTsMNjN3W
- 7: https://cgit.freebsd.org/ports/commit/?id=b1a2d52166abffd763c903ff7a5bf5dfb84c13a2
- 8: https://atlaskit.atlassian.com/packages/core/datetime-picker/changelog
- 9: https://www.npmjs.com/package/framer-motion/v/4.1.18-rc.12
- 10: https://classic.yarnpkg.com/en/package/gestalt

---



</details>

@HarrySeop 네, 확인했습니다. 정말 죄송합니다! 제가 실수했네요. motion 패키지(v12.9.0)의 공식 문서를 확인해보니 React 프로젝트에서는 `import { motion } from "motion/react"` 구문을 사용하는 것이 맞습니다. 이는 framer-motion 라이브러리와는 다른 패키지이고, 다른 import 방식을 사용합니다. 귀하의 import 방식이 정확합니다. 혼동을 드려서 죄송합니다.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: HarrySeop
PR: StarSync-FE/StarSync#194
File: src/pages/landing/LandingPage.jsx:2-2
Timestamp: 2025-04-26T04:30:33.930Z
Learning: The "motion" package (v12.9.0) is a separate library from "framer-motion" and should be imported using `import { motion } from 'motion/react'` syntax as per its official documentation.
```

---

```
Learnt from: HarrySeop
PR: StarSync-FE/StarSync#194
File: src/pages/landing/LandingPage.jsx:2-2
Timestamp: 2025-04-26T04:30:33.930Z
Learning: The "motion" package (v12.9.0) is a separate library from "framer-motion" and should be imported using `import { motion } from "motion/react"` syntax for React projects as per its official documentation.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

@HarrySeop 죄송합니다, 제가 잘못 이해했네요. 확인해보니 패키지 `motion` v12.9.0을 사용하고 계시는 것 같은데, 해당 패키지의 공식 문서에 따르면 `import { motion } from 'motion/react'`가 맞는 import 방식입니다. "framer-motion"과는 다른 라이브러리네요. 정확한 정보 공유해주셔서 감사합니다!



---

🌐 Web query:

```web
Documentation for "motion" package version 12.9.0 npm javascript import syntax
```

💡 Result:

## Import Syntax for the "motion" Package (version 12.9.0)

**For JavaScript (Vanilla/DOM):**

To use the core Motion animation functions (like `animate` and `scroll`) in a non-React JavaScript project, install the package via npm:

```bash
npm install motion
```

Then import the functions you need:

```javascript
import { animate, scroll } from "motion"
```

Example usage:

```javascript
import { animate } from "motion"

animate(".box", { rotate: 360 })
```
This approach directly imports the named exports from the `"motion"` package for use in standard JavaScript environments[4].

---

**For React Projects:**

If you're using Motion in a React project, install the package as above, then import the special `motion` component from the `"motion/react"` subpath:

```javascript
import { motion } from "motion/react"
```

Example usage in a React component:

```javascript
import { motion } from "motion/react"

export function Component({ isVisible }) {
  return <motion.div animate={{ opacity: isVisible ? 1 : 0 }} />
}
```
This syntax enables the use of the `<motion.div>` component for declarative animations in React[1][2][4].

---

**ESM CDN Import (Optional, for HTML projects):**

You can also use Motion directly in the browser via a CDN:

```html
<script type="module">
  import { animate, scroll } from "https://cdn.jsdelivr.net/npm/motion@12.9.0/+esm"
  animate(".box", { x: 100 })
</script>
```
It's best practice to specify the version (e.g., `@12.9.0`) rather than "latest" for stability[4].

---

### Summary Table

| Environment        | Install Command            | Import Syntax                          | Example Use                                    |
|--------------------|---------------------------|----------------------------------------|------------------------------------------------|
| Vanilla JS / DOM   | npm install motion        | import { animate } from "motion"       | animate(".box", { x: 100 })                    |
| React              | npm install motion        | import { motion } from "motion/react"  | <motion.div animate={{ x: 100 }} />            |
| CDN (ESM/HTML)     | n/a                       | import { animate } from "cdn-url"      | animate(".box", { x: 100 })                    |

---

**References:**  
- Official npm documentation and examples for the `motion` package[1][2][4].

Citations:

- 1: https://www.npmjs.com/package/motion
- 2: https://classic.yarnpkg.com/en/package/framer-motion
- 3: https://www.npmjs.com/package/motion-dom
- 4: https://motion.dev/docs/quick-start
- 5: https://docs.gitlab.com/ee/user/packages/npm_registry/
- 6: https://help.webstudio.is/js-libraries-via-npm-WchmTsMNjN3W
- 7: https://cgit.freebsd.org/ports/commit/?id=b1a2d52166abffd763c903ff7a5bf5dfb84c13a2
- 8: https://atlaskit.atlassian.com/packages/core/datetime-picker/changelog
- 9: https://www.npmjs.com/package/framer-motion/v/4.1.18-rc.12
- 10: https://classic.yarnpkg.com/en/package/gestalt

---



</details>

@HarrySeop 네, 확인했습니다. 정말 죄송합니다! 제가 실수했네요. motion 패키지(v12.9.0)의 공식 문서를 확인해보니 React 프로젝트에서는 `import { motion } from "motion/react"` 구문을 사용하는 것이 맞습니다. 이는 framer-motion 라이브러리와는 다른 패키지이고, 다른 import 방식을 사용합니다. 귀하의 import 방식이 정확합니다. 혼동을 드려서 죄송합니다.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: HarrySeop
PR: StarSync-FE/StarSync#194
File: src/pages/landing/LandingPage.jsx:2-2
Timestamp: 2025-04-26T04:30:33.930Z
Learning: The "motion" package (v12.9.0) is a separate library from "framer-motion" and should be imported using `import { motion } from 'motion/react'` syntax as per its official documentation.
```

---

```
Learnt from: HarrySeop
PR: StarSync-FE/StarSync#194
File: src/pages/landing/LandingPage.jsx:2-2
Timestamp: 2025-04-26T04:30:33.930Z
Learning: The "motion" package (v12.9.0) is a separate library from "framer-motion" and should be imported using `import { motion } from "motion/react"` syntax for React projects as per its official documentation.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-26T04:30:35Z
author_association: NONE
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
+import { useEffect, useRef, useState } from 'react';
 import { Link } from 'react-router-dom';
 import * as S from './landingPage.styles';
 
+const stars = [
+  { id: 1, top: '10%', left: '15%', width: 30, height: 30 },
+  { id: 2, top: '70%', left: '80%', width: 40, height: 40 },
+  { id: 3, top: '50%', left: '10%', width: 20, height: 20 },
+  { id: 4, top: '20%', left: '30%', width: 50, height: 50 },
+  { id: 5, top: '10%', left: '80%', width: 30, height: 30 },
+  { id: 6, top: '80%', left: '20%', width: 30, height: 30 },
+  { id: 7, top: '85%', left: '85%', width: 25, height: 25 },
+  { id: 8, top: '5%', left: '50%', width: 35, height: 35 },
+  { id: 9, top: '60%', left: '60%', width: 20, height: 20 },
+  { id: 10, top: '40%', left: '70%', width: 30, height: 30 },
+  { id: 11, top: '25%', left: '5%', width: 20, height: 20 },
+  { id: 12, top: '90%', left: '45%', width: 30, height: 30 },
+  { id: 13, top: '50%', left: '90%', width: 30, height: 30 },
+];
+
+const sections = [
+  {
+    id: 'main',
+    title: 'StarSync',
+    description: '후원과 투표를 통해 아이돌을 응원하세요. 당신의 선택이 그들의 무대가 됩니다.',
+    buttonText: '지금 시작하기',
+    showScrollGuide: true,
+  },
+  {
+    id: 'voting',
+    title: '투명한 투표 시스템',
+    description: '투명하고 신뢰할 수 있는 투표 시스템으로 여러분의 소중한 한 표를 행사하세요.',
+    buttonText: '투표하러 가기',
+  },
+  {
+    id: 'funding',
+    title: '팬덤의 힘을 보여주세요',
+    description: '여러분이 좋아하는 아이돌의 활동을 직접 후원하고 특별한 혜택도 받아가세요.',
+    buttonText: '후원하러 가기',
+  },
+  {
+    id: 'my-idol',
+    title: '나만의 아이돌을 위한 특별한 공간',
+    description:
+      '여러 아이돌 중에서 오직 당신의 최애만 선택하고 팬심을 가장 나다운 방식으로 표현해보세요.',
+    buttonText: '내 최애 선택하기',
+  },
+];
+
 const LandingPage = () => {
+  const sectionRefs = useRef([]);
+  const [activeIndex, setActiveIndex] = useState(0);
+
   const handleClearStorage = () => {
     localStorage.clear();
   };
 
+  useEffect(() => {
+    const observer = new IntersectionObserver(
path: src/pages/landing/LandingPage.jsx
user: login: HarrySeop
body: 제가 라이브러리로 구현한 것이 아니다보니 풀페이지 스크롤을 구현하려면 브라우저 화면이 특정 지점에 들어왔는지를 확인해야하기에 해당 함수인 observer가 특정 영역의 50%이상에 화면이 들어오면 어느 섹션이 보이는지 감지하는 함수입니다
created_at: 2025-04-26T04:32:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,19 +1,151 @@
 import CustomButton from '@/components/customButton';
+import { motion } from 'motion/react';
+import { useEffect, useRef, useState } from 'react';
 import { Link } from 'react-router-dom';
 import * as S from './landingPage.styles';
 
+const stars = [
+  { id: 1, top: '10%', left: '15%', width: 30, height: 30 },
+  { id: 2, top: '70%', left: '80%', width: 40, height: 40 },
+  { id: 3, top: '50%', left: '10%', width: 20, height: 20 },
+  { id: 4, top: '20%', left: '30%', width: 50, height: 50 },
+  { id: 5, top: '10%', left: '80%', width: 30, height: 30 },
+  { id: 6, top: '80%', left: '20%', width: 30, height: 30 },
+  { id: 7, top: '85%', left: '85%', width: 25, height: 25 },
+  { id: 8, top: '5%', left: '50%', width: 35, height: 35 },
+  { id: 9, top: '60%', left: '60%', width: 20, height: 20 },
+  { id: 10, top: '40%', left: '70%', width: 30, height: 30 },
+  { id: 11, top: '25%', left: '5%', width: 20, height: 20 },
+  { id: 12, top: '90%', left: '45%', width: 30, height: 30 },
+  { id: 13, top: '50%', left: '90%', width: 30, height: 30 },
+];
+
+const sections = [
+  {
+    id: 'main',
+    title: 'StarSync',
+    description: '후원과 투표를 통해 아이돌을 응원하세요. 당신의 선택이 그들의 무대가 됩니다.',
+    buttonText: '지금 시작하기',
+    showScrollGuide: true,
+  },
+  {
+    id: 'voting',
+    title: '투명한 투표 시스템',
+    description: '투명하고 신뢰할 수 있는 투표 시스템으로 여러분의 소중한 한 표를 행사하세요.',
+    buttonText: '투표하러 가기',
+  },
+  {
+    id: 'funding',
+    title: '팬덤의 힘을 보여주세요',
+    description: '여러분이 좋아하는 아이돌의 활동을 직접 후원하고 특별한 혜택도 받아가세요.',
+    buttonText: '후원하러 가기',
+  },
+  {
+    id: 'my-idol',
+    title: '나만의 아이돌을 위한 특별한 공간',
+    description:
+      '여러 아이돌 중에서 오직 당신의 최애만 선택하고 팬심을 가장 나다운 방식으로 표현해보세요.',
+    buttonText: '내 최애 선택하기',
+  },
+];
+
 const LandingPage = () => {
+  const sectionRefs = useRef([]);
+  const [activeIndex, setActiveIndex] = useState(0);
+
   const handleClearStorage = () => {
     localStorage.clear();
   };
 
+  useEffect(() => {
+    const observer = new IntersectionObserver(
+      (entries) => {
+        for (const entry of entries) {
+          if (entry.isIntersecting) {
+            const index = Number(entry.target.getAttribute('data-index'));
+            setActiveIndex(index);
+          }
+        }
+      },
+      { threshold: 0.5 },
+    );
+
+    for (const ref of sectionRefs.current) {
+      if (ref) observer.observe(ref);
+    }
+
+    return () => observer.disconnect();
+  }, []);
+
+  const handleSmoothScroll = (index) => {
+    sectionRefs.current[index]?.scrollIntoView({
+      behavior: 'smooth',
+      block: 'start',
+    });
+  };
path: src/pages/landing/LandingPage.jsx
user: login: HarrySeop
body: 맞아요 속도 조절을 더 하고싶었는데 css의 기본 속성으로 있는 것이라서 쉽게 수정하진 못할 것 같아서 우선 이렇게 구현했습니다
created_at: 2025-04-26T04:32:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,8 +1,271 @@
-import { css } from '@emotion/react';
+import starImg from '@/assets/images/star.png';
+import media from '@/styles/responsive';
+import { css, keyframes } from '@emotion/react';
 
-export const landingWrapper = css`
+const auroraMove = keyframes`
+  0% {
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+  }
+  50% {
+    background-position: 50% 50%, 50% 0%, 0% 100%;
+  }
+  100% {
+    background-position: 100% 100%, 0% 50%, 50% 0%;
+  }
+`;
+
+const twinkle = keyframes` 
+  0%, 100% {
+    opacity: 0.3;
+  }
+  50% {
+    opacity: 0.6;
+  }
+`;
+
+const glow = keyframes`
+  0%, 100% {
+    text-shadow: 0 0 8px var(--pink), 0 0 16px var(--blue-sky), 0 0 24px var(--purple);
+  }
+  50% {
+    text-shadow: 0 0 12px var(--pink), 0 0 24px var(--blue-sky), 0 0 36px var(--purple);
+  }
+`;
+
+export const pageContainer = css`
+  overflow: hidden auto;
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  color: var(--white);
+  background: var(--black-deep);
+  scroll-snap-type: y mandatory;
+  scroll-behavior: smooth;
+  -ms-overflow-style: none;
+  scrollbar-width: none;
+
+  &::-webkit-scrollbar {
+    display: none;
+  }
+
+  &::before {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 0;
+    width: 200%;
+    height: 200%;
+    background: radial-gradient(circle, var(--purple), transparent 70%),
+                radial-gradient(circle, var(--blue-dark), transparent 70%),
+                radial-gradient(circle, var(--pink-purple), transparent 70%);
+    background-position: 0% 0%, 100% 0%, 50% 100%;
+    background-size: 50% 50%;
+    animation: ${auroraMove} 10s infinite alternate;
+    content: '';
+    opacity: 0.5;
+  }
+`;
+
+export const backgroundStarsWrapper = css`
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 1;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+`;
+
+export const starStyle = ({ top, left, width, height }) => css`
+  position: absolute;
+  top: ${top};
+  left: ${left};
+  width: ${width}px;
+  height: ${height}px;
+  background: url(${starImg}) no-repeat center center;
+  background-size: contain;
+  opacity: 0.3;
+  animation: ${twinkle} 3s infinite;
+`;
+
+export const section = css`
   display: flex;
+  position: relative;
+  z-index: 2;
   justify-content: center;
   align-items: center;
+  width: 100%;
   height: 100vh;
+  padding: 0 2rem;
+  scroll-snap-align: start;
+  scroll-snap-stop: always;
+`;
+
+export const sectionContent = css`
+  display: flex;
+  position: relative;
+  z-index: 1;
+  flex-direction: column;
+  gap: 1rem;
+  width: 100%;
+  max-width: 144rem;
+  margin: 0 auto;
+  padding: 2rem;
+  padding-bottom: 6rem;
+  text-align: center;
+
+  ${media({
+    gap: ['2.4rem', '2.8rem', '2rem', '2rem'],
+    padding: ['2.4rem', '2.8rem', '3.2rem', '3.6rem'],
+    paddingBottom: ['8rem', '8rem', '8rem', '10rem'],
+  })}
+
+  h1 {
+    font-size: 3.2rem;
+    font-weight: 900;
+    line-height: 1.3;
+    letter-spacing: 0.2rem;
+    word-break: keep-all;
+    color: var(--white);
+    text-shadow: 2px 2px 4px rgb(0 0 0 / 70%);
+    cursor: pointer;
+    transition: opacity 0.3s ease;
+    animation: ${glow} 2s infinite ease-in-out;
path: src/pages/landing/landingPage.styles.js
user: login: HarrySeop
body: 이건 시은님 코드를 가져온 것이라..! 영광을 시은님에게 돌리겠습니다!
created_at: 2025-04-26T04:33:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-25T21:35:06Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces a comprehensive redesign of the landing page. The `LandingPage` component is refactored to feature multiple animated sections with smooth scrolling, intersection tracking, and a starry animated background. Navigation is enhanced with vertical dot navigation for section jumps and a scroll guide. The visual styling is overhauled using Emotion CSS-in-JS with custom keyframe animations for backgrounds, stars, and glowing text. The `framer-motion` library is integrated for section transitions, necessitating the addition of the `motion` dependency in the project’s `package.json`.

## Changes

| File(s)                                              | Change Summary                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
|------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `package.json`                                       | Added `"motion": "^12.9.0"` to dependencies.                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `src/pages/landing/LandingPage.jsx`                  | Refactored the component to implement a multi-section animated landing page with starry background, smooth scrolling, animated section transitions, intersection observer for active section tracking, vertical dot navigation, and scroll guide. Added new state, refs, handlers, and useEffect hooks.                                                                                                                           |
| `src/pages/landing/landingPage.styles.js`            | Completely rewritten to define styled components and keyframe animations for the landing page. Added aurora background, twinkling stars, glowing headings, scroll guide, navigation dots, and responsive section layouts using Emotion CSS-in-JS.                                                                                                                                                                                                                         |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
  participant User
  participant LandingPage
  participant FramerMotion
  participant IntersectionObserver

  User->>LandingPage: Loads landing page
  LandingPage->>LandingPage: Render sections, stars, nav dots
  LandingPage->>IntersectionObserver: Observe section visibility
  IntersectionObserver-->>LandingPage: Update activeIndex on section enter/leave
  User->>LandingPage: Clicks nav dot or scroll guide
  LandingPage->>LandingPage: Smooth scroll to section
  LandingPage->>FramerMotion: Animate section content (opacity/position)
  User->>LandingPage: Clicks "Go to List" button
  LandingPage->>LandingPage: Clear localStorage and navigate to /list
```

## Poem

> In a sky of stars, our page now gleams,  
> With glowing text and animated dreams.  
> Sections scroll with gentle grace,  
> While dots and arrows guide your pace.  
> Aurora hues and twinkling light—  
> A landing page that feels just right!  
> 🐇✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=StarSync-FE/StarSync&utm_content=194):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: sgoldenbird
created_at: 2025-04-26T04:35:44Z
body: 수고했어요~ 아이돌 이미지 랜딩페이지에 활용해도 좋을 것 같아요~
author_association: CONTRIBUTOR