Skip to content

Comments

Feat(landing): landing page#104

Merged
jjangminii merged 2 commits intodevelopfrom
feat/#102/landing-page
Sep 14, 2025
Merged

Feat(landing): landing page#104
jjangminii merged 2 commits intodevelopfrom
feat/#102/landing-page

Conversation

@jjangminii
Copy link
Collaborator

@jjangminii jjangminii commented Sep 14, 2025

📌 Related Issues

관련된 Issue를 태그해주세요. (e.g. - close #25)

📄 Tasks

  • 랜딩페이지

⭐ PR Point (To Reviewer)

📷 Screenshot

Summary by CodeRabbit

  • 신규 기능

    • 랜딩 페이지를 섹션 기반으로 재구성(히어로, 북마크, 리마인더, 리워드, 최종 CTA).
    • 세로 스냅 스크롤 및 부드러운 스크롤 도입으로 탐색 경험 개선.
    • 각 섹션에 Lottie 애니메이션 적용(자동재생/반복).
    • 히어로 영역에서 섹션 이동 스크롤 버튼 추가.
    • 최종 CTA 버튼으로 사이트 이동 가능.
  • 리팩터

    • 기존 인라인 UI를 섹션 컴포넌트로 분리해 구조화.
  • Chores

    • 애니메이션 및 디자인 시스템 관련 의존성 정리 및 추가.

@jjangminii jjangminii self-assigned this Sep 14, 2025
@jjangminii jjangminii linked an issue Sep 14, 2025 that may be closed by this pull request
@vercel
Copy link

vercel bot commented Sep 14, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
pinback-client-client Ready Ready Preview Comment Sep 14, 2025 6:13am

@coderabbitai
Copy link

coderabbitai bot commented Sep 14, 2025

Walkthrough

랜딩 앱에 Lottie 기반 섹션 컴포넌트(히어로/북마크/리마인더/리워드/최종 CTA)를 추가하고, App을 스냅 스크롤 레이아웃으로 구성했습니다. lottie-react를 의존성에 추가했으며, 3개의 Lottie 애셋 JSON 파일을 새로 추가했습니다. 디자인 시스템 버튼을 일부 섹션에서 사용합니다.

Changes

Cohort / File(s) Summary of changes
Dependencies
apps/landing/package.json
lottie-react 추가. 워크스페이스 기반 devDependencies 추가/정렬. @pivanov/vite-plugin-svg-sprite 위치 변경(최종 포함).
App Shell & Layout
apps/landing/src/App.tsx
기존 로컬 상태/임시 UI 제거. 스냅 스크롤 컨테이너로 변경하고 각 기능 섹션 컴포넌트 마운트.
Components
apps/landing/src/components/HeroSection.tsx, .../FeatureBookmarkSection.tsx, .../FeatureReminderSection.tsx, .../FeatureRewardSection.tsx, .../FinalCTASection.tsx
5개 섹션 컴포넌트 추가. Lottie 애니메이션 렌더링, 텍스트/버튼 배치, 스크롤 트리거/외부 이동(CTA) 처리.
Assets (Lottie)
apps/landing/src/assets/2_bookmark.json, .../3_bell.json, .../4_up.json
새 Lottie 애셋 3종 추가(북마크, 벨, 업). 베이스64 이미지/프리컴포지션/키프레임 포함.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant App as App (Landing)
  participant HS as HeroSection
  participant FB as FeatureBookmarkSection
  participant FR as FeatureReminderSection
  participant RW as FeatureRewardSection
  participant FC as FinalCTASection
  participant L as Lottie

  U->>App: 페이지 접속
  App->>HS: 섹션 렌더
  HS->>L: 애니메이션 로드/재생
  App->>FB: 섹션 렌더
  FB->>L: 애니메이션 로드/재생
  App->>FR: 섹션 렌더
  FR->>L: 애니메이션 로드/재생
  App->>RW: 섹션 렌더
  RW->>L: 애니메이션 로드/재생
  App->>FC: 섹션 렌더
  FC->>L: 애니메이션 로드/재생
  note over App,L: 각 섹션은 독립적으로 loop/autoplay
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant HS as HeroSection
  participant FB as FeatureBookmarkSection
  participant FC as FinalCTASection
  participant Win as window.location

  U->>HS: "스크롤하기" 클릭
  HS->>HS: bookmark-section로 스무스 스크롤
  U->>FC: CTA 버튼 클릭
  FC->>Win: href = https://pinback.today
  Win-->>U: 새 위치로 이동
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

🛠️ Feature, feat

Suggested reviewers

  • constantly-dev
  • jllee000

Poem

(\_/) 새 장면 척!
( •ㅅ•) 로띠가 춤추고 버튼은 톡!
/︶\ 스냅-스크롤 따라 둥실둥실
북마크, 알림, 업! 기쁨이 굴실굴실
이제 한 번에 핀백으로—슝!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning 이 PR은 #102의 주된 목표인 랜딩 페이지 구조화 및 Lottie 애니메이션 통합을 컴포넌트와 에셋 추가를 통해 충실히 구현했으므로 해당 이슈는 대체로 충족됩니다, 그러나 [#25](design-system: Progress 컴포넌트 구현)에서 요구한 Progress 컴포넌트 구현(값 클램핑, variant 지원, 접근성 속성, Storybook 스토리/테스트) 및 관련 런타임 의존성(@radix-ui/react-progress) 추가는 본 PR에 포함되어 있지 않습니다, 따라서 연결된 모든 이슈의 요구사항을 완전히 충족하지는 못합니다. 해결 방안으로는 이 PR에 [#25] 요구사항을 포함해 Progress 컴포넌트 구현과 관련 의존성·스토리북 요소를 추가하거나 #25를 별도 PR로 분리한 뒤 이 PR의 관련 이슈 목록에서 제거하거나 분리 의도를 명확히 표기해 주세요; 분리할 경우 PR 본문에 분리 사유와 예정 작업(또는 새 PR 번호)을 명시하면 리뷰어가 변경 범위와 우선순위를 정확히 판단할 수 있습니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed 제목 "Feat(landing): landing page"는 변경사항의 핵심인 랜딩 페이지 추가를 명확히 요약하고 있으며 리포지토리의 컨벤션(Feat(...): ...)에 부합합니다, 불필요한 이모지나 파일 목록 등 잡음이 없어서 히스토리 스캔 시 주된 변경을 빠르게 파악할 수 있습니다.
Out of Scope Changes Check ✅ Passed 변경사항은 apps/landing 내부의 컴포넌트 추가, Lottie 에셋 및 해당 앱의 package.json(런타임 lottie-react 추가 및 워크스페이스 기반 devDependencies 등록) 변경에 국한되어 있어 랜딩 페이지 구현 목적과 일치합니다, 워크스페이스 의존성 추가는 디자인 시스템의 Button 사용 및 타입스크립트 설정 연동을 위한 합리적 변경으로 보이며 다른 패키지 소스나 불필요한 전역 변경은 포함되어 있지 않습니다, 따라서 명시된 목적 외의 범위 확장은 발견되지 않습니다.
Description Check ✅ Passed PR 설명은 제공된 템플릿 구조를 따르며 관련 이슈( close #102 )와 작업 항목 "랜딩페이지"가 포함되어 있어 기본 정보는 충족됩니다, 다만 "PR Point"가 비어 있고 스크린샷이 첨부되지 않아 리뷰어가 중점 검토 포인트 및 시각적 결과를 즉시 파악하기 어렵습니다, 전반적으로 대부분의 필수 섹션은 채워져 있어 'mostly complete'로 판단합니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#102/landing-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the feat 기능 개발하라 개발 달려라 달려 label Sep 14, 2025
@github-actions
Copy link

✅ Storybook chromatic 배포 확인:
🐿️ storybook

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (12)
apps/landing/src/assets/3_bell.json (1)

1-1: Lottie 내 텍스트(문자 쉐이프) 사용: 접근성 대체 텍스트 필요

애니메이션에 “4시간 30분 이후에 사라져요” 텍스트가 포함돼도 스크린리더는 인식하지 못할 수 있습니다. 해당 섹션 컴포넌트에서 Lottie에 role="img"와 의미 있는 aria-label을 부여하거나, 시각 숨김(sr-only) 텍스트를 추가하세요.

apps/landing/src/assets/2_bookmark.json (1)

1-1: 에셋 최적화 및 지연 로딩 권장

여기도 base64 이미지가 다수 포함되어 있습니다. 초기 페인트 전부를 한 번에 로딩하지 않도록 섹션 진입 시점에 동적 import하여 로드하거나, Lottie 최적화(불필요 키프레임/이미지 품질 조정)를 적용해 주세요.

apps/landing/src/components/FeatureBookmarkSection.tsx (2)

19-20: 애니메이션 접근성 속성 추가

스크린리더 친화성을 위해 Lottie에 역할과 라벨을 추가하세요.

-          <Lottie animationData={Bookmark} loop={true} autoplay={true} />
+          <Lottie
+            animationData={Bookmark}
+            loop
+            autoplay
+            role="img"
+            aria-label="북마크 및 메모 애니메이션"
+          />

6-8: 반응형 스택 정리(모바일 세로 스택, 패딩 보강)

작은 화면에서 좌우 2열이 비좁을 수 있습니다. 세로 스택 전환과 내부 패딩을 추가하세요.

-    <section className="flex h-dvh items-center justify-center bg-white">
-      <div className="flex items-center gap-[8rem]">
+    <section className="flex h-dvh items-center justify-center bg-white px-6">
+      <div className="flex flex-col items-center gap-[4rem] md:flex-row md:gap-[8rem]">
apps/landing/src/components/FeatureRewardSection.tsx (2)

20-21: 애니메이션 접근성 속성 추가

이미지 역할/라벨을 명시해 주세요.

-          <Lottie animationData={Progress} loop autoplay />
+          <Lottie animationData={Progress} loop autoplay role="img" aria-label="리워드 상승 애니메이션" />

2-2: 네이밍 니트: 데이터 의미가 드러나도록 변수명 조정

Progress는 컴포넌트/디자인 토큰과 혼동 여지가 있습니다. 에셋 의미에 맞게 변경 권장.

-import Progress from '../assets/4_up.json';
+import UpAnimation from '../assets/4_up.json';
@@
-          <Lottie animationData={Progress} loop autoplay role="img" aria-label="리워드 상승 애니메이션" />
+          <Lottie animationData={UpAnimation} loop autoplay role="img" aria-label="리워드 상승 애니메이션" />

Also applies to: 20-20

apps/landing/src/components/FinalCTASection.tsx (3)

7-7: 배경 색상 토큰 일관성 확인

다른 섹션은 bg-white를 사용하지만 여기만 bg-white-bg를 사용합니다. 토큰 의도라면 유지, 아니라면 일관성 있게 통일하세요.

-    <section className="bg-white-bg flex h-dvh w-full flex-col items-center justify-center gap-[6.4rem] overflow-hidden px-[17.2rem] text-center">
+    <section className="bg-white flex h-dvh w-full flex-col items-center justify-center gap-[6.4rem] overflow-hidden px-[17.2rem] text-center">

8-13: 애니메이션 접근성 속성 추가

역할/라벨 지정 권장.

-      <Lottie
+      <Lottie
         animationData={Chippiface}
         loop
         autoplay
+        role="img"
+        aria-label="치삐 캐릭터 애니메이션"
         className="h-[32rem] w-[32rem]"
       />

22-29: 네비게이션은 onClick보다 a 링크 사용 권장

키보드/마우스 중간 클릭/접근성/SEO 측면에서 a 태그가 바람직합니다. 새 탭 이동 시 rel 보안 속성도 추가하세요.

-          <Button
-            variant="primary"
-            onClick={() => {
-              window.location.href = 'https://pinback.today';
-            }}
-          >
-            지금 시작하기
-          </Button>
+          <a href="https://pinback.today" target="_blank" rel="noopener noreferrer" aria-label="핀백 시작하기 새 탭에서 열기">
+            <Button variant="primary">지금 시작하기</Button>
+          </a>
apps/landing/src/components/FeatureReminderSection.tsx (2)

10-11: 애니메이션 접근성 속성 추가

역할/라벨 지정 권장.

-          <Lottie animationData={lightbulb_chippi_final} loop autoplay />
+          <Lottie
+            animationData={lightbulb_chippi_final}
+            loop
+            autoplay
+            role="img"
+            aria-label="리마인드 벨 애니메이션"
+          />

2-2: 네이밍 니트: 변수명 간결화

에셋 의미에 맞게 짧고 일관된 이름 사용 권장.

-import lightbulb_chippi_final from '../assets/3_bell.json';
+import BellAnimation from '../assets/3_bell.json';
@@
-          <Lottie
-            animationData={lightbulb_chippi_final}
+          <Lottie
+            animationData={BellAnimation}

Also applies to: 10-10

apps/landing/src/components/HeroSection.tsx (1)

29-29: 과도한 고정 패딩의 반응형 처리

px-[17.2rem]는 작은 뷰포트에서 좌우 오버플로우를 유발할 수 있습니다. 반응형 패딩으로 단계적 확대를 권장합니다.

-    <section className="bg-white-bg flex h-dvh w-full flex-col items-center justify-center px-[17.2rem] text-center">
+    <section className="bg-white-bg flex h-dvh w-full flex-col items-center justify-center px-6 sm:px-10 md:px-16 xl:px-[17.2rem] text-center">
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bc3afc and 88b2a1b.

⛔ Files ignored due to path filters (4)
  • apps/landing/src/assets/landing_bell.svg is excluded by !**/*.svg
  • apps/landing/src/assets/landing_icon.svg is excluded by !**/*.svg
  • apps/landing/src/assets/react.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • apps/landing/package.json (1 hunks)
  • apps/landing/src/App.tsx (1 hunks)
  • apps/landing/src/assets/2_bookmark.json (1 hunks)
  • apps/landing/src/assets/3_bell.json (1 hunks)
  • apps/landing/src/assets/4_up.json (1 hunks)
  • apps/landing/src/components/FeatureBookmarkSection.tsx (1 hunks)
  • apps/landing/src/components/FeatureReminderSection.tsx (1 hunks)
  • apps/landing/src/components/FeatureRewardSection.tsx (1 hunks)
  • apps/landing/src/components/FinalCTASection.tsx (1 hunks)
  • apps/landing/src/components/HeroSection.tsx (1 hunks)
🔇 Additional comments (2)
apps/landing/package.json (1)

13-15: 확인 완료 — lottie-react@2.4.1은 React 19과 호환됩니다
lottie-react@2.4.1의 peerDependencies에 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 및 동일한 "react-dom" 범위가 명시되어 있어 React 19과 호환됩니다.

apps/landing/src/App.tsx (1)

15-33: 섹션의 접근 가능한 이름(헤딩) 확보 확인

내부에 시각적으로 노출되는 헤딩(h1–h6)이 필요합니다. Hero는 h1이면 충분합니다.
자동 검사(rg) 결과 아래 컴포넌트 파일들에서 h1–h6 태그가 발견되지 않았습니다(파일이 없거나 태그 부재 가능):

  • apps/landing/src/components/FeatureBookmarkSection.tsx
  • apps/landing/src/components/FeatureReminderSection.tsx
  • apps/landing/src/components/FeatureRewardSection.tsx
  • apps/landing/src/components/FinalCTASection.tsx

각 파일에 시각적 헤딩 또는 aria-label/aria-labelledby가 있는지 확인하고, 경로가 다르거나 헤딩이 다른 파일에 있다면 실제 경로 또는 rg 출력 결과를 붙여 제출하세요.

"devDependencies": {
"@pivanov/vite-plugin-svg-sprite": "^3.1.3",
"@eslint/js": "^9.33.0",
"@pinback/design-system": "workspace:*",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

런타임 의존성 분류 오류: @pinback/design-system 은 dependencies로 이동 필요

FinalCTASection에서 Button을 런타임에 사용하므로 devDependencies가 아닌 dependencies에 선언되어야 합니다. 현재 상태는 프로덕션 빌드/배포 시 의존성 누락 위험이 있습니다.

권장 패치:

@@
   "dependencies": {
+    "@pinback/design-system": "workspace:*",
     "lottie-react": "^2.4.1",
     "react": "^19.1.1",
     "react-dom": "^19.1.1"
   },
@@
   "devDependencies": {
-    "@pinback/design-system": "workspace:*",

Also applies to: 12-16

🤖 Prompt for AI Agents
In apps/landing/package.json around lines 12-19, @pinback/design-system is
declared under devDependencies but is used at runtime (FinalCTASection Button),
so move the @pinback/design-system entry from devDependencies into dependencies
(remove from devDependencies and add the same version string under dependencies)
to ensure it is installed in production; apply the same change for any other
runtime packages noted in lines 12-16.

Click on the Vite and React logos to learn more
</p>
</>
<div className="h-dvh snap-y snap-mandatory overflow-y-scroll scroll-smooth">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

메인 영역 시맨틱(

→ ) + 스크롤/스냅 개선

  • 최상위 스크롤 컨테이너를 <main>으로 변경하고 role/aria-label을 부여하면 내비게이션 및 접근성이 향상됩니다.
  • iOS 바운스/이중 스크롤 방지를 위해 overscroll-y-contain 추가 권장.
  • 모션 선호도에 따라 부드러운 스크롤을 비활성화하려면 Tailwind의 motion-safe: 변형 사용.
-    <div className="h-dvh snap-y snap-mandatory overflow-y-scroll scroll-smooth">
+    <main role="main" aria-label="Pinback 랜딩 페이지" className="h-dvh snap-y snap-mandatory overflow-y-scroll overscroll-y-contain motion-safe:scroll-smooth">
...
-    </div>
+    </main>

Also applies to: 34-34

🤖 Prompt for AI Agents
In apps/landing/src/App.tsx around lines 11 (and also at line 34), replace the
top-level <div> scroll container with a semantic <main> element, add an
appropriate role and aria-label for accessibility (e.g., role="main" and a
descriptive aria-label), add the Tailwind class overscroll-y-contain to prevent
iOS bounce/double-scroll, and change scroll-smooth to motion-safe:scroll-smooth
so smooth scrolling respects user motion preferences.

@@ -0,0 +1 @@
{"nm":"up 2","ddd":0,"h":340,"w":340,"meta":{"g":"@lottiefiles/toolkit-js 0.66.4"},"layers":[{"ty":0,"nm":"up","sr":1,"st":1680,"op":3480,"ip":1680,"ln":"70","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":1},{"ty":0,"nm":"up","sr":1,"st":1560,"op":3360,"ip":1560,"ln":"69","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":2},{"ty":0,"nm":"up","sr":1,"st":1440,"op":3240,"ip":1440,"ln":"68","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":3},{"ty":0,"nm":"up","sr":1,"st":1320,"op":3120,"ip":1320,"ln":"67","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":4},{"ty":0,"nm":"up","sr":1,"st":1200,"op":3000,"ip":1200,"ln":"66","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":5},{"ty":0,"nm":"up","sr":1,"st":1080,"op":2880,"ip":1080,"ln":"65","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":6},{"ty":0,"nm":"up","sr":1,"st":960,"op":2760,"ip":960,"ln":"64","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":7},{"ty":0,"nm":"up","sr":1,"st":840,"op":2640,"ip":840,"ln":"63","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":8},{"ty":0,"nm":"up","sr":1,"st":720,"op":2520,"ip":720,"ln":"62","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":9},{"ty":0,"nm":"up","sr":1,"st":600,"op":2400,"ip":600,"ln":"61","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":10},{"ty":0,"nm":"up","sr":1,"st":480,"op":2280,"ip":480,"ln":"60","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":11},{"ty":0,"nm":"up","sr":1,"st":360,"op":2160,"ip":360,"ln":"59","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":12},{"ty":0,"nm":"up","sr":1,"st":240,"op":2040,"ip":240,"ln":"58","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":13},{"ty":0,"nm":"up","sr":1,"st":120,"op":1920,"ip":120,"ln":"43","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":14},{"ty":0,"nm":"up","sr":1,"st":0,"op":1800,"ip":0,"ln":"42","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170,170]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":340,"h":340,"refId":"1","ind":15}],"v":"5.7.0","fr":30,"op":900,"ip":0,"assets":[{"nm":"up","id":"1","fr":30,"layers":[{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":120,"ip":0,"ln":"29","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Shape 1","it":[{"ty":"sh","nm":"Path 1","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[34.75,-14.25],[126.25,-25.25],[114,62.75]]}}},{"ty":"st","nm":"Stroke 1","lc":2,"lj":2,"ml":4,"o":{"a":0,"k":100},"w":{"a":0,"k":25},"c":{"a":0,"k":[0.4588,0.8471,0.6863]}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":120,"ip":0,"ln":"28","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Shape 1","it":[{"ty":"sh","nm":"Path 1","d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-134.5,128],[-35.75,39.25],[8.5,88.5],[121.5,-20]]}}},{"ty":"st","nm":"Stroke 1","lc":2,"lj":2,"ml":4,"o":{"a":0,"k":100},"w":{"a":0,"k":25},"c":{"a":0,"k":[0.4588,0.8471,0.6863]}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"tm","nm":"Trim Paths 1","e":{"a":1,"k":[{"o":{"x":0.494,"y":0},"i":{"x":0.573,"y":1},"s":[0],"t":87.999},{"s":[100],"t":117.999}]},"o":{"a":0,"k":0},"s":{"a":1,"k":[{"o":{"x":0.494,"y":0},"i":{"x":0.573,"y":1},"s":[0],"t":0},{"s":[100],"t":30}]},"m":1}],"ind":2},{"ty":2,"nm":"1","sr":1,"st":0,"op":120,"ip":0,"ln":"23","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[6.396,17.856]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[98.477,28.84,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":27.272},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":30},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":109.999},{"s":[0],"t":112.727}]}},"refId":"2","ind":3},{"ty":2,"nm":"3","sr":1,"st":0,"op":120,"ip":0,"ln":"27","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[15.776,12.125]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[74.809,49.195,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":13.637},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":16.363},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":96.999},{"s":[0],"t":99.726}]}},"refId":"3","ind":4},{"ty":2,"nm":"2","sr":1,"st":0,"op":120,"ip":0,"ln":"25","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[17.892,6]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[66.878,80.073,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":2.728},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":84.999},{"s":[0],"t":87.727}]}},"refId":"4","ind":5},{"ty":2,"nm":"d2","sr":1,"st":0,"op":120,"ip":0,"ln":"19","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[54.78,54.78]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[263.26,64.26,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"refId":"5","ind":6},{"ty":2,"nm":"d1","sr":1,"st":0,"op":120,"ip":0,"ln":"17","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[76,76]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[166.543,99.543,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"refId":"6","ind":7},{"ty":2,"nm":"bg","sr":1,"st":0,"op":120,"ip":0,"ln":"15","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[170.159,170.159]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[170,170]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"refId":"7","ind":8}]},{"id":"2","e":1,"w":13,"h":36,"p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAkCAYAAABSSLCCAAAACXBIWXMAAAABAAAAAQBPJcTWAAAAJHpUWHRDcmVhdG9yAAAImXNMyU9KVXBMK0ktUnBNS0tNLikGAEF6Bs5qehXFAAABRklEQVQ4jZWVwW3CQBBF3ywRZzqItwK4R0J2JZgLKLe0kA5yQ+ESpwM6AJEGSAU2HZhbDsGbQzbrxUrI+J9Wu/M0M39GtuC1Lve5gwdg7K/eBZ4WdlrQkQA8l/sCmHUfvV6XdprHF2Zd7vMrAMDMx7SQL+mqujGGtodruogxCgCAl3I7iqGjBvrgZhJDlTZbgARqVSBNGs4Od+idSRsoSNtTg9lpIAcX7mmVRNBnpYRuA3RvMy0U9FPeSRO8Kt/SGOplex8jMLhJBLmdDnOj3plAkggSbU8t1CCqpcVvhc+kHvA4QH0HHBuhHnAMqQfc0/LvAQfIqW13owAJjcp2h4khqTSQxOWdlRBERgww2q1ooYW9O6D72m66lv/3BzkNORcX0NJON+Ae/wCOwiCd26yW315X5TYxmBwkFagd7Iaci7nNaoAv7Tpamu61sNgAAAAASUVORK5CYII=","u":""},{"id":"3","e":1,"w":32,"h":25,"p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAZCAYAAABQDyyRAAAACXBIWXMAAAABAAAAAQBPJcTWAAAAJHpUWHRDcmVhdG9yAAAImXNMyU9KVXBMK0ktUnBNS0tNLikGAEF6Bs5qehXFAAABdklEQVRIibXVQU7CQBhA4TdtwlZvYOcE4NqkyA24gWVD465HwBPYnSmregM8ATKJa+EEozeArbHUhSVOTBHbmb5VmcykP19oEVTNtYpKSIB+tbQRkE5lmNNhAiDTKgdujuxZ7SmiWzl662IAb65V9MfNAYYe/jrTKuliAJFpteaH/VTONbwGN4cONESmVdnyrBMND9i0POtEwxOQWpw/A+4zrZ4f9DJoNUD1nD9aDAEWGuJwUR2e8f2tbGr02xDmhwe9DDz8HBhaDrGDMo3lcNZogEMONTYCP5rKq/WxDV7dYizDdE8xAFaWA/RLitdMr2bHNtQKmFV/UikdadQKmE1lmPcoAgFPlgPUapwUMMu0GgM5DjR6FNcTOdqeFDCLZbhwpfGBv4CGAmYuNAT+ZSMBMxcaJZ/j1gJm7TXKu9YCZu01xNaJgFkDjV2PInAiYPZfDQHJRI7cC5hVGilwYSy/A0ksQ7vHsElz/TIo2J/7eNvfr+IvpBOfWRRUBB0AAAAASUVORK5CYII=","u":""},{"id":"4","e":1,"w":36,"h":12,"p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAMCAYAAAAK/x/DAAAACXBIWXMAAAABAAAAAQBPJcTWAAAAJHpUWHRDcmVhdG9yAAAImXNMyU9KVXBMK0ktUnBNS0tNLikGAEF6Bs5qehXFAAAA5klEQVQ4jc2UMW7CMBRA33cukJExPkFzAUA5CXRBytYb9ApZyZQrcAMUiT2cIDkCHSuR/A52IUKgSm1U8yZ/+fv7Wfa3MCFle0h7hhjAoClo7GYkARKfFgMv32sEdj1S5Ha+9/HPxSM0UTQBUEwsaDpKXU5xGIHXjV1UF6GyrdcKb2P7/2agtxHAtq0r4B2YhZJxRBKVbb32MsER+DT+mp4GQ8A3c4sijQktMUY5FwY4hhYB1/a5zTojUAQW2Q1ItrGLyseXtl9NUP8DaK6h7kdbNwNycuNzl9useyDouPMx/rn4b/gCiK9Msyz7oEgAAAAASUVORK5CYII=","u":""},{"id":"5","e":1,"w":110,"h":110,"p":"data:image/png;base64,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","u":""},{"id":"6","e":1,"w":152,"h":152,"p":"data:image/png;base64,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","u":""},{"id":"7","e":1,"w":341,"h":341,"p":"data:image/png;base64,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","u":""}]} No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

대용량 Lottie(JSON + base64 이미지)로 초기 번들 팽창 위험

여러 개의 내장 PNG(base64)와 긴 타임라인이 포함되어 초기 JS 번들 크기가 커질 수 있습니다. 섹션 진입 시 동적 import로 애니메이션 데이터를 지연 로드하거나, 에셋을 public에 두고 fetch로 불러오는 방식을 고려하세요. 또한 LottieFiles Optimizer로 최적화(불필요한 메타/레이어 제거, 이미지 압축) 권장.

리포 내 Lottie 파일 크기 점검 스크립트:


🏁 Script executed:

#!/bin/bash
# Lottie JSON 원본 및 gzip 크기 확인
set -e
for f in apps/landing/src/assets/*.json; do
  raw=$(wc -c < "$f")
  gz=$(gzip -c "$f" | wc -c)
  printf "%-50s raw=%'d bytes  gzip=%'d bytes\n" "$f" "$raw" "$gz"
done

Length of output: 428


초기 번들 팽창 우려 — 대용량 Lottie 자산 지연 로드 필요

검증 결과, 번들에 포함될 경우 초기 로드에 큰 영향을 줄 수 있는 대용량 Lottie JSON이 확인되었습니다:

  • apps/landing/src/assets/1_landingmain.json — raw=4,750,587 bytes (~4.75 MB), gzip=3,373,418 bytes (~3.37 MB)
  • apps/landing/src/assets/5_chippiface.json — raw=501,769 bytes (~0.50 MB), gzip=364,958 bytes (~0.36 MB)
  • apps/landing/src/assets/3_bell.json — raw=31,453 bytes, gzip=7,403 bytes
  • apps/landing/src/assets/4_up.json — raw=24,238 bytes, gzip=11,990 bytes
  • apps/landing/src/assets/2_bookmark.json — raw=11,254 bytes, gzip=6,748 bytes

권장 조치:

  • 초기 번들에서 제거 — 섹션 진입 시 dynamic import 또는 해당 섹션 진입(IntersectionObserver 등) 시 fetch로 지연 로드.
  • 또는 Lottie JSON/이미지를 public으로 옮겨 URL로 불러오기(번들 경량화 + 캐시 이점).
  • LottieFiles Optimizer로 애니메이션 경량화(불필요 메타/레이어 제거, 이미지 압축 및 base64 분리).

우선 처리 대상: apps/landing/src/assets/1_landingmain.json → 즉시 지연 로드 또는 최적화 적용.

🤖 Prompt for AI Agents
In apps/landing/src/assets/4_up.json (lines 1-1): this Lottie JSON is bundled
raw and contributes to initial bundle bloat; remove it from static imports and
instead load it lazily — either move the JSON (and large peers) into the public/
directory and fetch by URL when the animation section becomes visible
(IntersectionObserver) or keep it in src but replace static import with a
dynamic import() when needed; additionally run the asset through LottieFiles
Optimizer (or split out base64 images) to reduce size and ensure the loader
handles errors/timeouts and caches results.

Comment on lines +1 to +3
import Lottie from 'lottie-react';
import Bookmark from '../assets/2_bookmark.json';

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

애니메이션 데이터 동적 import로 초기 번들 축소

JSON 에셋을 동적으로 가져오면 초기 번들을 줄일 수 있습니다. TS 설정에 resolveJsonModule가 필요합니다.

-import Lottie from 'lottie-react';
-import Bookmark from '../assets/2_bookmark.json';
+import Lottie from 'lottie-react';
+import { useEffect, useState } from 'react';
@@
-          <Lottie animationData={Bookmark} loop={true} autoplay={true} />
+          {/*
+            NOTE: 섹션 진입 시 초기 렌더 후 로드.
+            Vite에서는 import('../assets/2_bookmark.json').then(m => m.default)
+          */}
+          <Lottie animationData={anim} loop autoplay role="img" aria-label="북마크 및 메모 애니메이션" />

파일 상단에 추가:

const [anim, setAnim] = useState<any>();
useEffect(() => {
  let mounted = true;
  import('../assets/2_bookmark.json').then(m => { if (mounted) setAnim(m.default); });
  return () => { mounted = false; };
}, []);

TS 설정 확인 스크립트:

Also applies to: 17-21


🏁 Script executed:

#!/bin/bash
# apps/landing tsconfig에서 resolveJsonModule 사용 여부 확인
fd -t f 'tsconfig*.json' apps/landing | xargs -I {} sh -c 'echo "== {} =="; cat {} | jq ".compilerOptions.resolveJsonModule"'

Length of output: 37


resolveJsonModule 활성화 필요 — 애니메이션 JSON을 동적 import로 로드

확인: apps/landing/tsconfig.json의 compilerOptions.resolveJsonModule 값이 null입니다. 동적 import를 사용하려면 해당 tsconfig(또는 상위 tsconfig)에 "resolveJsonModule": true를 추가하세요 (파일: apps/landing/tsconfig.json).

-import Lottie from 'lottie-react';
-import Bookmark from '../assets/2_bookmark.json';
+import Lottie from 'lottie-react';
+import { useEffect, useState } from 'react';
@@
-          <Lottie animationData={Bookmark} loop={true} autoplay={true} />
+          {/*
+            NOTE: 섹션 진입 시 초기 렌더 후 로드.
+            Vite에서는 import('../assets/2_bookmark.json').then(m => m.default)
+          */}
+          <Lottie animationData={anim} loop autoplay role="img" aria-label="북마크 및 메모 애니메이션" />

파일 상단에 추가:

const [anim, setAnim] = useState<any>();
useEffect(() => {
  let mounted = true;
  import('../assets/2_bookmark.json').then(m => { if (mounted) setAnim(m.default); });
  return () => { mounted = false; };
}, []);

예시 tsconfig 변경:

// apps/landing/tsconfig.json
{
  "compilerOptions": {
    "resolveJsonModule": true
  }
}
🤖 Prompt for AI Agents
In apps/landing/src/components/FeatureBookmarkSection.tsx lines 1-3, the static
import of the animation JSON requires resolveJsonModule to be enabled and blocks
using dynamic import; update apps/landing/tsconfig.json (or its nearest parent
tsconfig) to set compilerOptions.resolveJsonModule to true, remove the top-level
static import of '../assets/2_bookmark.json', and change the component to load
the JSON dynamically by using useState and useEffect: in useEffect call
import('../assets/2_bookmark.json').then(m => mounted && setAnim(m.default))
with a mounted flag to avoid state updates after unmount, then pass the loaded
anim to the Lottie component; ensure types use any (or a proper type) for anim
state.

Comment on lines +8 to +16
const floatAnimationStyle = `
@keyframes floatY {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.float-animate {
animation: floatY 2s ease-in-out infinite;
}
`;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

동적 <style> 주입 최소화 + 모션 접근성 반영

현재 인라인 키프레임을 mount 때마다 주입/제거합니다. 전역 CSS/Tailwind keyframes로 옮기는 게 바람직합니다. 최소 변경으로는 모션 선호도(reduced) 대응과 안전한 제거(style.remove()) 적용을 권합니다.

적용 예:

 const floatAnimationStyle = `
 @keyframes floatY {
   0%, 100% { transform: translateY(0); }
   50% { transform: translateY(-10px); }
 }
 .float-animate {
   animation: floatY 2s ease-in-out infinite;
 }
+@media (prefers-reduced-motion: reduce) {
+  .float-animate { animation: none !important; }
+}
 `;
 ...
   useEffect(() => {
     const style = document.createElement('style');
     style.innerHTML = floatAnimationStyle;
     document.head.appendChild(style);
     return () => {
-      document.head.removeChild(style);
+      style.remove();
     };
   }, []);

Also applies to: 19-26

🤖 Prompt for AI Agents
In apps/landing/src/components/HeroSection.tsx around lines 8-16 (and similarly
lines 19-26), the component injects keyframe CSS into the DOM on mount and
removes it unsafely on unmount; update this to (1) avoid dynamic injection by
moving the keyframes to global CSS/Tailwind if possible, otherwise create the
style element once and on unmount call style.remove() to safely detach it, and
(2) respect prefers-reduced-motion by wrapping the animation rules in a @media
(prefers-reduced-motion: no-preference) query (or disabling animation when
reduced-motion is preferred). Ensure the implementation minimally changes the
component: prefer global/static styles, otherwise create the style element with
the media-aware rules and remove it with style.remove() on cleanup.

Comment on lines +33 to +41
<img src={landing_icon} alt="Landing Icon" />
<Lottie
animationData={landingmain}
loop
autoplay
className="h-[413px] w-[480px]"
/>
<img src={landing_bell} alt="Landing Icon" />
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

이미지/Lottie 접근성 및 LCP 최적화

  • 두 이미지 alt가 동일("Landing Icon")이며 의미 전달이 불명확합니다. 장식용이면 alt=""와 aria-hidden으로 스크린리더에서 제외하세요.
  • 히어로 위상 이미지는 LCP 영향이 크므로 loading="eager", decoding="async", fetchPriority="high" 권장.
  • Lottie는 장식용이면 aria-hidden 지정.
-          <img src={landing_icon} alt="Landing Icon" />
+          <img src={landing_icon} alt="" aria-hidden="true" loading="eager" decoding="async" fetchPriority="high" />
           <Lottie
             animationData={landingmain}
             loop
             autoplay
             className="h-[413px] w-[480px]"
+            aria-hidden="true"
           />
-          <img src={landing_bell} alt="Landing Icon" />
+          <img src={landing_bell} alt="" aria-hidden="true" loading="eager" decoding="async" fetchPriority="high" />

추가로, 이미지의 고정 width/height 지정으로 레이아웃 시프트를 더 줄일 수 있습니다.

📝 Committable suggestion

‼️ 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.

Suggested change
<img src={landing_icon} alt="Landing Icon" />
<Lottie
animationData={landingmain}
loop
autoplay
className="h-[413px] w-[480px]"
/>
<img src={landing_bell} alt="Landing Icon" />
</div>
<img src={landing_icon} alt="" aria-hidden="true" loading="eager" decoding="async" fetchPriority="high" />
<Lottie
animationData={landingmain}
loop
autoplay
className="h-[413px] w-[480px]"
aria-hidden="true"
/>
<img src={landing_bell} alt="" aria-hidden="true" loading="eager" decoding="async" fetchPriority="high" />
</div>
🤖 Prompt for AI Agents
In apps/landing/src/components/HeroSection.tsx around lines 33 to 41, update the
two <img> elements and the Lottie component for accessibility and LCP: give each
image a meaningful, distinct alt text (or if purely decorative set alt="" and
add aria-hidden="true"), add loading="eager", decoding="async", and
fetchPriority="high" to the hero LCP image, and provide explicit width and
height attributes to both images to avoid layout shift; if the Lottie animation
is decorative, add aria-hidden="true" (and remove any alt) or otherwise provide
an accessible title/aria-label. Ensure attributes are added consistently in JSX
without changing layout semantics.

Comment on lines +43 to +47
<div className="mb-[3.2rem] mt-[5.5rem] leading-snug">
<p className="head1 text-gray900">차곡차곡 모아둔 북마크</p>
<p className="head1 text-gray900">
저장만 하지 말고 재미있게 활용 해 볼까요?
</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

헤딩 시맨틱 정비 (

/

)

히어로의 주 메시지는 문서의 주제 제목으로 노출되는 것이 접근성에 유리합니다.

-          <p className="head1 text-gray900">차곡차곡 모아둔 북마크</p>
-          <p className="head1 text-gray900">
+          <h1 className="head1 text-gray900">차곡차곡 모아둔 북마크</h1>
+          <p className="head1 text-gray900">
             저장만 하지 말고 재미있게 활용 해 볼까요?
-          </p>
+          </p>
📝 Committable suggestion

‼️ 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.

Suggested change
<div className="mb-[3.2rem] mt-[5.5rem] leading-snug">
<p className="head1 text-gray900">차곡차곡 모아둔 북마크</p>
<p className="head1 text-gray900">
저장만 하지 말고 재미있게 활용 볼까요?
</p>
<div className="mb-[3.2rem] mt-[5.5rem] leading-snug">
<h1 className="head1 text-gray900">차곡차곡 모아둔 북마크</h1>
<p className="head1 text-gray900">
저장만 하지 말고 재미있게 활용 볼까요?
</p>
🤖 Prompt for AI Agents
In apps/landing/src/components/HeroSection.tsx around lines 43 to 47, the hero
uses two <p> tags for the main title and subtitle which is not semantically
correct; replace the primary title <p className="head1 text-gray900">차곡차곡 모아둔
북마크</p> with an <h1> preserving the same class names and content, leave the
second line as a paragraph or subtitle element (e.g., <p>) and ensure heading
order remains correct for accessibility.

Comment on lines +50 to +61
<div className="float-animate">
<Button
variant="secondary"
onClick={() => {
document
.querySelector('#bookmark-section')
?.scrollIntoView({ behavior: 'smooth' });
}}
>
스트롤하기
</Button>
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

버튼 라벨 오탈자 및 기본 type 명시

  • 라벨 오탈자: “스트롤하기” → “스크롤하기”
  • form 내 포함 가능성을 대비해 type="button" 명시.
  • 가능하다면 앵커 내비게이션 사용(접근성/기본 동작 유지). 디자인시스템이 asChild를 지원하면 그 방식이 최적.

최소 수정:

-          <Button
+          <Button
             variant="secondary"
+            type="button"
             onClick={() => {
               document
                 .querySelector('#bookmark-section')
                 ?.scrollIntoView({ behavior: 'smooth' });
             }}
           >
-            스트롤하기
+            스크롤하기
           </Button>

asChild 지원 시(선호):

-          <Button variant="secondary" type="button" onClick={...}>스크롤하기</Button>
+          <Button variant="secondary" asChild>
+            <a href="#bookmark-section">스크롤하기</a>
+          </Button>
📝 Committable suggestion

‼️ 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.

Suggested change
<div className="float-animate">
<Button
variant="secondary"
onClick={() => {
document
.querySelector('#bookmark-section')
?.scrollIntoView({ behavior: 'smooth' });
}}
>
스트롤하기
</Button>
</div>
<div className="float-animate">
<Button
variant="secondary"
type="button"
onClick={() => {
document
.querySelector('#bookmark-section')
?.scrollIntoView({ behavior: 'smooth' });
}}
>
스크롤하기
</Button>
</div>
🤖 Prompt for AI Agents
In apps/landing/src/components/HeroSection.tsx around lines 50 to 61, the Button
has a label typo and missing explicit type: change the label from "스트롤하기" to
"스크롤하기" and add type="button" to prevent accidental form submission; if the
design system supports asChild prefer rendering an anchor (or use a plain <a>
with href="#bookmark-section") for native navigation/ accessibility while
keeping smooth scroll behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 개발하라 개발 달려라 달려

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 랜딩페이지

1 participant