[Refactor] WTH-243 : 랜딩 페이지 수정사항 적용#35
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthrough프리런칭 미들웨어 리다이렉트, PostHog 및 GTM/Clarity 스크립트 추가, 랜딩용 BlockedToast·InquiryDialog 컴포넌트 및 API 추가, 서비스 섹션에 비디오·강조 키워드 도입과 랜딩 자산/상수 업데이트가 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant B as Browser
participant M as Middleware (proxy.ts)
participant S as Server/App
participant C as Client (BlockedToast)
B->>M: HTTP request (any pathname)
alt PRE_LAUNCH = true and pathname != /landing and not excluded
M->>B: 302 Redirect to /landing?blocked=true
B->>S: Request /landing?blocked=true
S->>B: Serve landing page (includes scripts + BlockedToast)
B->>C: Client loads BlockedToast
C->>C: detect blocked=true -> show toast, router.replace('/landing')
else normal flow
M->>S: proxy/request forwarded
S->>B: normal response
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 |
|
구현한 기능 Preview: https://weeth-kjy0pgvrx-weethsite-4975s-projects.vercel.app |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-hxxwew8zm-weethsite-4975s-projects.vercel.app |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/landing/ServiceSection.tsx (1)
59-81:⚠️ Potential issue | 🟠 Major첫 번째/마지막 비디오가 섹션 밖에서도 계속 재생됩니다.
autoPlay={i === 0}때문에 이 섹션이 화면 밖에 있어도 첫 비디오가 바로 재생되고,ScrollTrigger에는onLeave/onLeaveBackpause가 없어서 섹션을 벗어난 뒤에는 마지막 active 비디오가 계속 돕니다. 지금 구현은 “active 카드만 재생” 목표와 다르게 offscreen 재생이 남아 있어서, 진입 시에만 활성 카드 재생 + 이탈 시 전체 pause로 맞춰주는 게 좋겠습니다.Also applies to: 174-186
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/landing/ServiceSection.tsx` around lines 59 - 81, The first/last videos are autoplaying offscreen because of the autoPlay={i === 0} prop and the ScrollTrigger only handles onUpdate; remove the JSX autoPlay control (stop unconditional autoplay) and instead control playback via the ScrollTrigger lifecycle: in the ScrollTrigger.create block (the one calling ScrollTrigger.create / onUpdate / setActiveIndex / videoRefs), add onEnter and onEnterBack handlers that play only the currently active video (use setActiveIndex/index from onUpdate or compute index from self.progress) and add onLeave and onLeaveBack handlers that pause all videos via videoRefs.current.forEach(...); keep the existing onUpdate logic that plays the new active index and pauses others, but ensure initial page load/play is only triggered via onEnter so offscreen cards don’t auto-play.
🧹 Nitpick comments (5)
.gitignore (1)
37-48: 중복 ignore 규칙은 정리하는 편이 좋습니다.Line 37의
.env*가 이미 Line 48의.env.local을 포함하므로, Line 48은 제거해도 동작은 동일합니다.정리 예시
- .env.local🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore around lines 37 - 48, Remove the redundant .env.local entry from .gitignore because the existing broader rule `.env*` already covers it; locate the `.env*` and `.env.local` lines and delete the `.env.local` line so the ignore rules are deduplicated while preserving existing behavior.src/components/landing/HeroSection.tsx (1)
4-7: 주석 처리된 CTA 관련 코드 정리를 권장합니다.현재는 기능이 제거된 상태라 주석 import/JSX를 유지하기보다 제거하고, 필요 시 이슈 링크나 feature flag로 복구 경로를 남기는 편이 관리에 더 안전합니다.
Also applies to: 196-205
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/landing/HeroSection.tsx` around lines 4 - 7, Remove the dead/commented CTA imports and JSX in HeroSection.tsx: delete the commented imports (Link, Image, buttonVariants) at the top and remove the commented CTA JSX block around lines where the CTA was rendered (the commented section noted at ~196-205); if you want to keep a recovery path, replace the removed comments with a single-line TODO referencing an issue ID or add a feature-flag reminder (e.g., TODO: restore CTA when FEATURE_CTA enabled or link to issue) so the codebase stays clean and reversible.src/app/(public)/landing/page.tsx (1)
24-25:PublicHeader의도를 코드에 명시해 두는 편이 좋습니다.현재 TODO만 남아 있어 정책이 불명확해 보입니다. 숨김이 의도라면
showAuthButtons={false}를 명시하거나 TODO를 정리해 주세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(public)/landing/page.tsx around lines 24 - 25, The intent for PublicHeader is unclear — either explicitly hide the auth buttons by passing the prop (e.g., set showAuthButtons={false} on the PublicHeader component) or remove the TODO and make the intended behavior explicit (e.g., document/show in a comment or set showAuthButtons={true} if they should be visible); update the JSX where PublicHeader is rendered to include the explicit prop so the policy is clear.src/components/landing/ServiceSection.tsx (2)
199-200: 새로 추가한 강조 색상은 토큰 클래스로 맞추는 편이 좋습니다.
text-[#000]는 이 PR에서 새로 들어온 하드코딩 색상이라 테마 변경 시 추적이 어렵습니다. 이미text-brand-primary,text-text-*토큰을 쓰고 있으니 같은 방식으로 통일해 주세요. As per coding guidelinessrc/**/*.{ts,tsx}: Always use design token classes first (text-, bg-, typo-, p-, gap-*) — no hardcoded values; ask before adding new tokens.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/landing/ServiceSection.tsx` around lines 199 - 200, The span in ServiceSection rendering (around f.highlightKeyword) uses a hardcoded color class text-[`#000`]; replace it with the appropriate design token class (e.g., text-brand-primary or one of the text-text-* tokens used elsewhere) so it follows the project's token-first rule; update the JSX in ServiceSection (where f.highlightKeyword is rendered) to use the chosen token class and ensure it matches existing theme semantics rather than introducing a new hardcoded value.
14-21:cardTitle가 더 이상 렌더링되지 않아 타입과 실제 UI가 어긋납니다.
Feature에서는 아직 필수 필드인데 JSX는description만 사용합니다. 현재처럼 둘 다 constants에서 계속 관리하면 어떤 문구가 실제 노출되는지 헷갈리기 쉬우니, 의도적으로 없앤 거면 타입/데이터에서 제거하고 아니라면 렌더링을 복원해 주세요.Also applies to: 191-206
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/landing/ServiceSection.tsx` around lines 14 - 21, The Feature interface declares cardTitle as required but the ServiceSection JSX only renders description (so types and UI diverge); either restore rendering of cardTitle in the component or remove cardTitle from the Feature type and from the feature constants. Concretely: in ServiceSection (and the feature data constants referenced around the other occurrence at 191-206) either add the cardTitle element back into the mapped JSX where features are rendered (use the Feature.cardTitle value with the same styling as before), or update the Feature interface to make cardTitle optional and remove/adjust any cardTitle values in the constants so types match rendered output; ensure you update both the Feature type declaration and the feature constants used by the ServiceSection component.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/settings.json:
- Line 8: The OPENAPI_SPEC_PATH setting currently points to the Swagger UI HTML
page which causes MCP parsing to fail; update the OPENAPI_SPEC_PATH value (the
JSON key OPENAPI_SPEC_PATH in .claude/settings.json) to the actual OpenAPI JSON
endpoint (replace the current
"https://api-dev.v4.weeth.kr/swagger-ui/index.html#/" with
"https://api-dev.v4.weeth.kr/v3/api-docs") so openapi-mcp-server can fetch the
spec.
In @.claude/settings.local.json:
- Line 19: The path string "Bash(grep -r \"POSTHOG\\|PostHog\"
D:projectweeth-client/.env*)" is missing the path separator after the Windows
drive letter and will fail to find files; update the path segment to include the
separator (e.g., add a slash after "D:" like "D:/projectweeth-client/.env*" or
"D:\\projectweeth-client\\.env*") and ensure any backslashes are properly
escaped for JSON; replace the original string in .claude/settings.local.json
with the corrected, properly escaped path variant so the grep command can locate
the .env files.
In `@instrumentation-client.ts`:
- Around line 3-6: The code uses a non-null assertion on
process.env.NEXT_PUBLIC_POSTHOG_KEY when calling posthog.init which can crash at
runtime if the env var is missing; update the initialization in
instrumentation-client.ts to first check that NEXT_PUBLIC_POSTHOG_KEY (and
optionally NEXT_PUBLIC_POSTHOG_HOST) is defined and only call posthog.init when
present, otherwise skip initialization or log/throw a clear error; reference the
posthog.init call and the environment variables NEXT_PUBLIC_POSTHOG_KEY and
NEXT_PUBLIC_POSTHOG_HOST when making the guard so the fix is easy to locate.
In `@src/components/landing/CTASection.tsx`:
- Line 9: CTASection imports InquiryDialog via the barrel export
'@/components/landing', causing a circular dependency because CTASection itself
is exported from that barrel; change the import in CTASection.tsx to reference
InquiryDialog's direct file path (the component file that defines InquiryDialog)
instead of the barrel, e.g., replace import { InquiryDialog } from
'@/components/landing' with a relative import to the InquiryDialog module so the
module initialization order no longer depends on barrel export ordering.
In `@src/components/landing/InquiryDialog.tsx`:
- Around line 29-30: The current validation uses isValid = email.trim().length >
0 && message.trim().length > 0 which allows invalid emails and the labels aren’t
associated with inputs; change the component to use a proper <form> with a
submit handler (instead of manual button enable/disable), set the email input to
type="email" with required and an id, link its <label htmlFor="..."> to that id,
do the same for the message textarea (required + id + label htmlFor), and rely
on the browser’s built-in validation in the submit handler (preventDefault only
when needed and check form.checkValidity() or let the browser show errors);
update the isValid logic/remove it or use it only for non-blocking UI state, and
apply the same pattern to the other similar blocks mentioned (lines 37-43,
71-88, 99-105) so accessibility and email format validation are enforced.
- Around line 31-35: The dialog allows closing via multiple paths that bypass
the submission state, causing stale request results to surface; centralize all
close logic into a single handler (e.g., replace raw onOpenChange prop and any
individual close callbacks with a unified handleClose/handleRequestClose used by
Dialog, cancel/X buttons, and overlay/ESC) and inside that handler either
prevent closing when isSubmitting is true or cancel the in-flight request before
closing; update references to onOpenChange, the cancel/X button click handlers,
and any direct setEmail/setMessage resets to use this unified handler so all
close paths respect isSubmitting and properly reset state or abort the
submission.
In `@src/lib/apis/inquiry.ts`:
- Around line 10-11: create in src/lib/apis/inquiry.ts calls axios.post with
BASE_URL without validating it; add a guard at the start of the exported create
function (the create: (body: InquiryBody) => ...) to check that BASE_URL is a
non-empty string and throw a clear Error or return a rejected Promise if
missing, so callers fail fast instead of sending a malformed request; include
BASE_URL and a short context in the error message to aid debugging.
In `@src/proxy.ts`:
- Line 8: PRE_LAUNCH 상수가 true로 하드코딩되어 있어 운영 리스크가 큽니다; PRE_LAUNCH를 환경변수 기반으로 변경해
배포 없이 토글할 수 있도록 수정하세요: proxy.ts의 PRE_LAUNCH 선언을 제거하고 process.env 기반(예:
process.env.PRE_LAUNCH 또는 PRE_LAUNCH_MODE)으로 읽되 기본값을 안전한 값(false)으로 설정하고 문자열을
불리언으로 파싱하여 사용하세요; 또한 PRE_LAUNCH를 사용하는 함수/조건들이 새 env 기반 플래그를 참조하는지 확인하고 관련 문서나
.env 샘플을 업데이트하세요.
- Around line 13-19: The PRE_LAUNCH early-return currently redirects all
non-/landing requests before the whitelist check, preventing /kakao/* routes
from ever being reached; update the PRE_LAUNCH condition in the proxy middleware
to exclude the kakao path (e.g., add && !pathname.startsWith('/kakao/')) or move
the pathname.startsWith('/kakao/') check ahead of the PRE_LAUNCH block so that
the OAuth callback handlers (routes matched by pathname.startsWith('/kakao/'))
are allowed through before NextResponse.redirect is returned.
---
Outside diff comments:
In `@src/components/landing/ServiceSection.tsx`:
- Around line 59-81: The first/last videos are autoplaying offscreen because of
the autoPlay={i === 0} prop and the ScrollTrigger only handles onUpdate; remove
the JSX autoPlay control (stop unconditional autoplay) and instead control
playback via the ScrollTrigger lifecycle: in the ScrollTrigger.create block (the
one calling ScrollTrigger.create / onUpdate / setActiveIndex / videoRefs), add
onEnter and onEnterBack handlers that play only the currently active video (use
setActiveIndex/index from onUpdate or compute index from self.progress) and add
onLeave and onLeaveBack handlers that pause all videos via
videoRefs.current.forEach(...); keep the existing onUpdate logic that plays the
new active index and pauses others, but ensure initial page load/play is only
triggered via onEnter so offscreen cards don’t auto-play.
---
Nitpick comments:
In @.gitignore:
- Around line 37-48: Remove the redundant .env.local entry from .gitignore
because the existing broader rule `.env*` already covers it; locate the `.env*`
and `.env.local` lines and delete the `.env.local` line so the ignore rules are
deduplicated while preserving existing behavior.
In `@src/app/`(public)/landing/page.tsx:
- Around line 24-25: The intent for PublicHeader is unclear — either explicitly
hide the auth buttons by passing the prop (e.g., set showAuthButtons={false} on
the PublicHeader component) or remove the TODO and make the intended behavior
explicit (e.g., document/show in a comment or set showAuthButtons={true} if they
should be visible); update the JSX where PublicHeader is rendered to include the
explicit prop so the policy is clear.
In `@src/components/landing/HeroSection.tsx`:
- Around line 4-7: Remove the dead/commented CTA imports and JSX in
HeroSection.tsx: delete the commented imports (Link, Image, buttonVariants) at
the top and remove the commented CTA JSX block around lines where the CTA was
rendered (the commented section noted at ~196-205); if you want to keep a
recovery path, replace the removed comments with a single-line TODO referencing
an issue ID or add a feature-flag reminder (e.g., TODO: restore CTA when
FEATURE_CTA enabled or link to issue) so the codebase stays clean and
reversible.
In `@src/components/landing/ServiceSection.tsx`:
- Around line 199-200: The span in ServiceSection rendering (around
f.highlightKeyword) uses a hardcoded color class text-[`#000`]; replace it with
the appropriate design token class (e.g., text-brand-primary or one of the
text-text-* tokens used elsewhere) so it follows the project's token-first rule;
update the JSX in ServiceSection (where f.highlightKeyword is rendered) to use
the chosen token class and ensure it matches existing theme semantics rather
than introducing a new hardcoded value.
- Around line 14-21: The Feature interface declares cardTitle as required but
the ServiceSection JSX only renders description (so types and UI diverge);
either restore rendering of cardTitle in the component or remove cardTitle from
the Feature type and from the feature constants. Concretely: in ServiceSection
(and the feature data constants referenced around the other occurrence at
191-206) either add the cardTitle element back into the mapped JSX where
features are rendered (use the Feature.cardTitle value with the same styling as
before), or update the Feature interface to make cardTitle optional and
remove/adjust any cardTitle values in the constants so types match rendered
output; ensure you update both the Feature type declaration and the feature
constants used by the ServiceSection component.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 125914c6-bbae-4c37-ab3e-ba34af7b3028
⛔ Files ignored due to path filters (16)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlpublic/videos/landing/admin_attendance.mp4is excluded by!**/*.mp4public/videos/landing/admin_board.mp4is excluded by!**/*.mp4public/videos/landing/admin_member.mp4is excluded by!**/*.mp4public/videos/landing/admin_schedule.mp4is excluded by!**/*.mp4public/videos/landing/user_board.mp4is excluded by!**/*.mp4public/videos/landing/user_home.mp4is excluded by!**/*.mp4src/assets/image/landing/card/landing_card_1.svgis excluded by!**/*.svgsrc/assets/image/landing/card/landing_card_2.svgis excluded by!**/*.svgsrc/assets/image/landing/card/landing_card_3.svgis excluded by!**/*.svgsrc/assets/image/landing/card/landing_card_4.svgis excluded by!**/*.svgsrc/assets/image/landing/card/landing_card_5.svgis excluded by!**/*.svgsrc/assets/image/landing/card/landing_card_6.svgis excluded by!**/*.svgsrc/assets/image/landing/card/landing_card_7.svgis excluded by!**/*.svgsrc/assets/image/landing/card/landing_card_8.svgis excluded by!**/*.svgsrc/assets/image/landing/card/landing_hero_card.svgis excluded by!**/*.svg
📒 Files selected for processing (20)
.claude/settings.json.claude/settings.local.json.gitignoreinstrumentation-client.tspackage.jsonsrc/app/(public)/landing/page.tsxsrc/app/layout.tsxsrc/assets/icons/index.tssrc/assets/image/landing/card/index.tssrc/components/landing/BlockedToast.tsxsrc/components/landing/CTASection.tsxsrc/components/landing/HeroSection.tsxsrc/components/landing/InquiryDialog.tsxsrc/components/landing/PhilosophySection.tsxsrc/components/landing/ServiceSection.tsxsrc/components/landing/index.tssrc/constants/landing/landing.tssrc/lib/apis/index.tssrc/lib/apis/inquiry.tssrc/proxy.ts
| "API_BASE_URL": "https://13.124.170.169.nip.io", | ||
| "OPENAPI_SPEC_PATH": "https://13.124.170.169.nip.io/v3/api-docs" | ||
| "API_BASE_URL": "https://api-dev.v4.weeth.kr", | ||
| "OPENAPI_SPEC_PATH": "https://api-dev.v4.weeth.kr/swagger-ui/index.html#/" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
UI_URL='https://api-dev.v4.weeth.kr/swagger-ui/index.html#/'
SPEC_URL='https://api-dev.v4.weeth.kr/v3/api-docs'
echo '[1] 현재 설정된 URL의 헤더(HTML 여부 확인)'
curl -sSI "$UI_URL" | sed -n '1,10p'
echo '[2] 현재 설정된 URL 바디 앞부분'
curl -sS "$UI_URL" | head -n 5
echo '[3] OpenAPI JSON 후보 URL 헤더'
curl -sSI "$SPEC_URL" | sed -n '1,10p'
echo '[4] OpenAPI JSON 핵심 키 확인(openapi/info/paths)'
curl -sS "$SPEC_URL" | python - <<'PY'
import json, sys
obj = json.load(sys.stdin)
print("has_openapi:", "openapi" in obj)
print("has_info:", "info" in obj)
print("has_paths:", "paths" in obj)
PYRepository: Team-Weeth/weeth-client
Length of output: 1733
OPENAPI_SPEC_PATH가 Swagger UI HTML 페이지를 가리키고 있어 MCP 파싱이 실패합니다.
현재 설정(https://api-dev.v4.weeth.kr/swagger-ui/index.html#/)은 HTML 페이지를 반환하지만, openapi-mcp-server는 OpenAPI 스펙 문서(JSON)를 필요로 합니다.
올바른 스펙 엔드포인트인 https://api-dev.v4.weeth.kr/v3/api-docs로 변경하세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.claude/settings.json at line 8, The OPENAPI_SPEC_PATH setting currently
points to the Swagger UI HTML page which causes MCP parsing to fail; update the
OPENAPI_SPEC_PATH value (the JSON key OPENAPI_SPEC_PATH in
.claude/settings.json) to the actual OpenAPI JSON endpoint (replace the current
"https://api-dev.v4.weeth.kr/swagger-ui/index.html#/" with
"https://api-dev.v4.weeth.kr/v3/api-docs") so openapi-mcp-server can fetch the
spec.
| "Bash(find D:projectweeth-clientsrccomponentsui -type f \\\\\\(-name *.tsx -o -name *.ts \\\\\\))", | ||
| "Bash(find D:/project/weeth-client/src/constants -name *.ts)" | ||
| "Bash(find D:/project/weeth-client/src/constants -name *.ts)", | ||
| "Bash(grep -r \"POSTHOG\\\\|PostHog\" D:projectweeth-client/.env*)" |
There was a problem hiding this comment.
경로 문자열 형식이 깨져 명령이 실패할 수 있습니다.
Line 19의 D:projectweeth-client/.env*는 드라이브 경로 구분자가 빠져 있어 대상 파일을 찾지 못할 가능성이 큽니다.
수정 예시
- "Bash(grep -r \"POSTHOG\\\\|PostHog\" D:projectweeth-client/.env*)"
+ "Bash(grep -r \"POSTHOG\\\\|PostHog\" D:/project/weeth-client/.env*)"📝 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.
| "Bash(grep -r \"POSTHOG\\\\|PostHog\" D:projectweeth-client/.env*)" | |
| "Bash(grep -r \"POSTHOG\\\\|PostHog\" D:/project/weeth-client/.env*)" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.claude/settings.local.json at line 19, The path string "Bash(grep -r
\"POSTHOG\\|PostHog\" D:projectweeth-client/.env*)" is missing the path
separator after the Windows drive letter and will fail to find files; update the
path segment to include the separator (e.g., add a slash after "D:" like
"D:/projectweeth-client/.env*" or "D:\\projectweeth-client\\.env*") and ensure
any backslashes are properly escaped for JSON; replace the original string in
.claude/settings.local.json with the corrected, properly escaped path variant so
the grep command can locate the .env files.
src/lib/apis/inquiry.ts
Outdated
| create: (body: InquiryBody) => | ||
| axios.post(`${BASE_URL}/api/v4/users/inquiries`, body), |
There was a problem hiding this comment.
BASE_URL 미설정 케이스를 방어해야 합니다.
Line 11은 BASE_URL이 비어 있을 때 잘못된 URL로 요청이 나가므로, 호출 전에 명시적으로 검증해 실패를 빠르게 드러내는 것이 안전합니다.
방어 코드 예시
export const inquiryApi = {
- create: (body: InquiryBody) =>
- axios.post(`${BASE_URL}/api/v4/users/inquiries`, body),
+ create: (body: InquiryBody) => {
+ if (!BASE_URL) {
+ throw new Error('NEXT_PUBLIC_API_URL is not set');
+ }
+ return axios.post(new URL('/api/v4/users/inquiries', BASE_URL).toString(), body);
+ },
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/apis/inquiry.ts` around lines 10 - 11, create in
src/lib/apis/inquiry.ts calls axios.post with BASE_URL without validating it;
add a guard at the start of the exported create function (the create: (body:
InquiryBody) => ...) to check that BASE_URL is a non-empty string and throw a
clear Error or return a rejected Promise if missing, so callers fail fast
instead of sending a malformed request; include BASE_URL and a short context in
the error message to aid debugging.
| const PUBLIC_PATHS = ['/', '/login', '/terms', '/hub', '/landing']; | ||
|
|
||
| // TODO: 런칭 후 PRE_LAUNCH 플래그 및 관련 분기 제거 | ||
| const PRE_LAUNCH = true; |
There was a problem hiding this comment.
PRE_LAUNCH 하드코딩은 운영 차단 리스크가 큽니다.
현재 값이 true로 고정되어 있어 런칭 이후에도 전체 경로가 계속 막힐 수 있습니다. 환경변수 기반으로 전환해 배포 없이 제어 가능하게 두는 게 안전합니다.
🔧 제안 수정안
-const PRE_LAUNCH = true;
+const PRE_LAUNCH = process.env.PRE_LAUNCH === 'true';📝 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.
| const PRE_LAUNCH = true; | |
| const PRE_LAUNCH = process.env.PRE_LAUNCH === 'true'; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/proxy.ts` at line 8, PRE_LAUNCH 상수가 true로 하드코딩되어 있어 운영 리스크가 큽니다;
PRE_LAUNCH를 환경변수 기반으로 변경해 배포 없이 토글할 수 있도록 수정하세요: proxy.ts의 PRE_LAUNCH 선언을 제거하고
process.env 기반(예: process.env.PRE_LAUNCH 또는 PRE_LAUNCH_MODE)으로 읽되 기본값을 안전한
값(false)으로 설정하고 문자열을 불리언으로 파싱하여 사용하세요; 또한 PRE_LAUNCH를 사용하는 함수/조건들이 새 env 기반 플래그를
참조하는지 확인하고 관련 문서나 .env 샘플을 업데이트하세요.
| // 런칭 전: /landing 외 모든 경로 차단 | ||
| if (PRE_LAUNCH && pathname !== '/landing') { | ||
| const url = request.nextUrl.clone(); | ||
| url.pathname = '/landing'; | ||
| url.searchParams.set('blocked', 'true'); | ||
| return NextResponse.redirect(url); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# pre-launch 차단 분기와 kakao allow 분기 위치 확인
rg -n "PRE_LAUNCH|pathname.startsWith\\('/kakao/'\\)" src/proxy.ts
# 실제 kakao oauth 라우트 존재 확인
fd -i "route.ts" src/app | rg -n "kakao/oauth/route.ts"Repository: Team-Weeth/weeth-client
Length of output: 263
🏁 Script executed:
wc -l src/proxy.tsRepository: Team-Weeth/weeth-client
Length of output: 82
🏁 Script executed:
cat -n src/proxy.tsRepository: Team-Weeth/weeth-client
Length of output: 2704
PRE_LAUNCH 블록의 조기 반환으로 인해 /kakao/ 경로 허용-목록이 도달 불가능합니다.
Lines 14-19에서 PRE_LAUNCH && pathname !== '/landing' 조건이 참이면 즉시 반환되므로, Lines 50-53의 pathname.startsWith('/kakao/') 조건은 절대 실행되지 않습니다.
OAuth 콜백(예: /kakao/oauth)을 pre-launch 중에 허용하려면 다음 중 하나로 수정이 필요합니다:
- Line 14 조건을
if (PRE_LAUNCH && pathname !== '/landing' && !pathname.startsWith('/kakao/'))로 변경하거나 /kakao/체크를 PRE_LAUNCH 블록 이전으로 이동
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/proxy.ts` around lines 13 - 19, The PRE_LAUNCH early-return currently
redirects all non-/landing requests before the whitelist check, preventing
/kakao/* routes from ever being reached; update the PRE_LAUNCH condition in the
proxy middleware to exclude the kakao path (e.g., add &&
!pathname.startsWith('/kakao/')) or move the pathname.startsWith('/kakao/')
check ahead of the PRE_LAUNCH block so that the OAuth callback handlers (routes
matched by pathname.startsWith('/kakao/')) are allowed through before
NextResponse.redirect is returned.
There was a problem hiding this comment.
이게 이렇게 기울어진 채로 추가돼서.. 제가 일단.. 구현을 햇어요... 예진님한테 이거 다시 받아서 교체하는 게 나을까요...!?
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/landing/ServiceSection.tsx (1)
59-80:⚠️ Potential issue | 🟠 Major섹션 밖에서도 영상이 재생될 수 있으며, 섹션 이탈 시 계속 재생됩니다.
autoPlay={i === 0}로 인해 이 섹션이 viewport 아래에 있어도 첫 번째 영상이 DOM 렌더링되자마자 자동재생됩니다. 더 심각한 문제는 비디오 제어가onUpdate콜백에만 의존한다는 점입니다. 스크롤을 멈추면 콜백이 호출되지 않아 마지막 활성 영상이 계속 재생되며, 같은 인덱스에서도play()가 반복 호출됩니다. 성능에 직접 영향을 미치는 부분이므로:
autoPlay={i === 0}제거- ScrollTrigger의
onEnter/onLeave콜백 추가 또는 Intersection Observer 활용하여 섹션 진입/이탈 시에만 비디오 상태를 동기화관련 코드 구간
ScrollTrigger.create({ // ... onUpdate: (self) => { const index = Math.round(self.progress * (features.length - 1)); setActiveIndex(index); videoRefs.current.forEach((video, idx) => { if (!video) return; if (idx === index) { video.play(); } else { video.pause(); } }); }, });<video ref={(el) => { videoRefs.current[i] = el; }} src={f.video} autoPlay={i === 0} loop muted playsInline preload="metadata" className="h-full w-full object-cover" />Also applies to: 174-185
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/landing/ServiceSection.tsx` around lines 59 - 80, Remove the initial autoPlay prop from the video elements (remove autoPlay={i === 0}) and stop relying solely on ScrollTrigger.create's onUpdate to control play/pause; instead add ScrollTrigger callbacks (onEnter, onEnterBack, onLeave, onLeaveBack) or an IntersectionObserver to synchronize video state only when the section/slide actually enters or leaves the viewport: use the same videoRefs.current array and setActiveIndex logic but invoke video.play()/video.pause() in onEnter/onEnterBack (play the target index) and onLeave/onLeaveBack (pause the leaving index) so videos won't auto-play when off-screen and won't keep playing after scrolling stops; update any existing onUpdate usage to only setActiveIndex (not repeatedly call play()) or remove redundant play() calls there.
🧹 Nitpick comments (2)
src/components/landing/ServiceSection.tsx (2)
116-116: 새로 추가한 임의값 클래스는 토큰 클래스로 치환해 주세요.이번 변경에서
min-h-[1227px],pt-[86px],pl-[306px],mb-[86px],w-[1123px],rounded-[30px],px-[93px],pt-[91px],text-[#909599],text-[#000]같은 arbitrary value가 다시 들어왔습니다. 이 경로는 토큰 우선 규칙 대상이라 기존 spacing/text/bg 토큰으로 치환하거나, 없으면 토큰을 먼저 추가한 뒤 사용하는 쪽이 맞습니다. As per coding guidelines "Always use design token classes first (text-, bg-, typo-, p-, gap-*) — no hardcoded values; ask before adding new tokens".Also applies to: 136-136, 170-171, 191-199
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/landing/ServiceSection.tsx` at line 116, The class string in ServiceSection.tsx currently uses arbitrary value classes like min-h-[1227px], pt-[86px], pl-[306px], mb-[86px], w-[1123px], rounded-[30px], px-[93px], pt-[91px], text-[`#909599`], and text-[`#000`]; replace each arbitrary token in the className (the sticky top-0 h-screen ... string and similar occurrences at the referenced locations) with the appropriate design token utility (spacing/text/bg/typo tokens) according to the token-first rule, and if a matching token does not exist, add a new design token entry and then use that token class instead of the hardcoded value (apply this fix to the className in the component and the other occurrences mentioned).
14-21:Feature타입이 현재 렌더링 규칙을 보장하지 못합니다.지금 정의로는
image와video가 둘 다 optional이라 잘못된 데이터가 들어와도 Line 187-189처럼 빈 카드가 조용히 렌더링됩니다. 반대로 비디오 카드에서 쓰이지 않는bgColor와 더 이상 렌더링하지 않는cardTitle은 계속 필수라서 data contract도 어긋나 있습니다. export 중인 타입인 만큼image | video를 강제하는 union으로 좁히고, 사용하지 않는 필드는 같이 정리해 두는 편이 안전합니다.Also applies to: 171-189
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/landing/ServiceSection.tsx` around lines 14 - 21, The Feature interface in ServiceSection.tsx should be replaced with a discriminated union to guarantee either an image or a video and remove fields not used by the other variant: define export type Feature = ImageFeature | VideoFeature where ImageFeature = { kind: 'image'; image: StaticImageData; chipLabel: string; cardTitle: string; description: string; bgColor: string; highlightKeyword?: string } and VideoFeature = { kind: 'video'; video: string; chipLabel: string; description: string; highlightKeyword?: string } (omit bgColor/cardTitle from the video variant), then update the ServiceSection render/map logic that consumes Feature (the part that previously rendered empty cards) to switch on feature.kind and render the image card for kind==='image' and the video card for kind==='video' so no empty cards can appear and the exported type contract matches actual usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/components/landing/ServiceSection.tsx`:
- Around line 59-80: Remove the initial autoPlay prop from the video elements
(remove autoPlay={i === 0}) and stop relying solely on ScrollTrigger.create's
onUpdate to control play/pause; instead add ScrollTrigger callbacks (onEnter,
onEnterBack, onLeave, onLeaveBack) or an IntersectionObserver to synchronize
video state only when the section/slide actually enters or leaves the viewport:
use the same videoRefs.current array and setActiveIndex logic but invoke
video.play()/video.pause() in onEnter/onEnterBack (play the target index) and
onLeave/onLeaveBack (pause the leaving index) so videos won't auto-play when
off-screen and won't keep playing after scrolling stops; update any existing
onUpdate usage to only setActiveIndex (not repeatedly call play()) or remove
redundant play() calls there.
---
Nitpick comments:
In `@src/components/landing/ServiceSection.tsx`:
- Line 116: The class string in ServiceSection.tsx currently uses arbitrary
value classes like min-h-[1227px], pt-[86px], pl-[306px], mb-[86px], w-[1123px],
rounded-[30px], px-[93px], pt-[91px], text-[`#909599`], and text-[`#000`]; replace
each arbitrary token in the className (the sticky top-0 h-screen ... string and
similar occurrences at the referenced locations) with the appropriate design
token utility (spacing/text/bg/typo tokens) according to the token-first rule,
and if a matching token does not exist, add a new design token entry and then
use that token class instead of the hardcoded value (apply this fix to the
className in the component and the other occurrences mentioned).
- Around line 14-21: The Feature interface in ServiceSection.tsx should be
replaced with a discriminated union to guarantee either an image or a video and
remove fields not used by the other variant: define export type Feature =
ImageFeature | VideoFeature where ImageFeature = { kind: 'image'; image:
StaticImageData; chipLabel: string; cardTitle: string; description: string;
bgColor: string; highlightKeyword?: string } and VideoFeature = { kind: 'video';
video: string; chipLabel: string; description: string; highlightKeyword?: string
} (omit bgColor/cardTitle from the video variant), then update the
ServiceSection render/map logic that consumes Feature (the part that previously
rendered empty cards) to switch on feature.kind and render the image card for
kind==='image' and the video card for kind==='video' so no empty cards can
appear and the exported type contract matches actual usage.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 60690d9a-6444-478c-bb02-088bf83aaddd
📒 Files selected for processing (2)
src/components/landing/ServiceSection.tsxsrc/lib/apis/inquiry.ts
✅ Files skipped from review due to trivial changes (1)
- src/lib/apis/inquiry.ts
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 |
|
구현한 기능 Preview: https://weeth-65azwb7ha-weethsite-4975s-projects.vercel.app |
dalzzy
left a comment
There was a problem hiding this comment.
수고하셨습니다!!!!! 당신은 최고의 프론트,,
스크롤 시 축소되는 건,, 저도 확인해봣는데 어쩔 때는 잘되고 어쩔때는 안되고 그러는 것 같아요.. 왜징




✅ PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
📸 스크린샷 or 실행영상
/landing 페이지에서 확인 가능합니다!
🎸 기타 사항 or 추가 코멘트
현재 서비스 섹션에서 마지막 카드를 제외하고 드래그 시
description영역이 화면에 노출되지 않는 문제가 있습니당...수정하려고 하니 스크롤/애니메이션 쪽 충돌이 많이 나서 예상보다 시간이 오래 걸릴 것 같아... 우선은 현 상태로 pr 올려두엇습니다... ㅜ__ㅜ
Summary by CodeRabbit
새로운 기능
개선사항
기타