Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough랜딩 앱에 Lottie 기반 섹션 컴포넌트(히어로/북마크/리마인더/리워드/최종 CTA)를 추가하고, App을 스냅 스크롤 레이아웃으로 구성했습니다. lottie-react를 의존성에 추가했으며, 3개의 Lottie 애셋 JSON 파일을 새로 추가했습니다. 디자인 시스템 버튼을 일부 섹션에서 사용합니다. Changes
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
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: 새 위치로 이동
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
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. Comment |
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (4)
apps/landing/src/assets/landing_bell.svgis excluded by!**/*.svgapps/landing/src/assets/landing_icon.svgis excluded by!**/*.svgapps/landing/src/assets/react.svgis excluded by!**/*.svgpnpm-lock.yamlis 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:*", |
There was a problem hiding this comment.
런타임 의존성 분류 오류: @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"> |
There was a problem hiding this comment.
🛠️ 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 | |||
There was a problem hiding this comment.
💡 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"
doneLength 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.
| import Lottie from 'lottie-react'; | ||
| import Bookmark from '../assets/2_bookmark.json'; | ||
|
|
There was a problem hiding this comment.
💡 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.
| const floatAnimationStyle = ` | ||
| @keyframes floatY { | ||
| 0%, 100% { transform: translateY(0); } | ||
| 50% { transform: translateY(-10px); } | ||
| } | ||
| .float-animate { | ||
| animation: floatY 2s ease-in-out infinite; | ||
| } | ||
| `; |
There was a problem hiding this comment.
🛠️ 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.
| <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> |
There was a problem hiding this comment.
🛠️ 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.
| <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.
| <div className="mb-[3.2rem] mt-[5.5rem] leading-snug"> | ||
| <p className="head1 text-gray900">차곡차곡 모아둔 북마크</p> | ||
| <p className="head1 text-gray900"> | ||
| 저장만 하지 말고 재미있게 활용 해 볼까요? | ||
| </p> |
There was a problem hiding this comment.
🛠️ 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.
| <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.
| <div className="float-animate"> | ||
| <Button | ||
| variant="secondary" | ||
| onClick={() => { | ||
| document | ||
| .querySelector('#bookmark-section') | ||
| ?.scrollIntoView({ behavior: 'smooth' }); | ||
| }} | ||
| > | ||
| 스트롤하기 | ||
| </Button> | ||
| </div> |
There was a problem hiding this comment.
🛠️ 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.
| <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.
📌 Related Issues
📄 Tasks
⭐ PR Point (To Reviewer)
📷 Screenshot
Summary by CodeRabbit
신규 기능
리팩터
Chores