Repository navigation
Refactor/#139 에러 메시지 라이팅 수정 및 SEO 초기세팅 - #142
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
Walkthrough사이트의 robots, sitemap, 메타데이터와 GraphQL 스키마를 업데이트했습니다. 공통 Changes사이트 노출 설정과 공개 계약
공통 오류 폴백 적용
사용자 안내 메시지 정비
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Page
participant ErrorBoundary
participant ErrorFallback
Page->>ErrorBoundary: 콘텐츠 렌더링
ErrorBoundary->>ErrorFallback: 오류 제목과 설명 전달
ErrorFallback-->>Page: 공통 오류 화면 렌더링
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
src/views/resume_edit/ui/edit/AiFeedbackDialog.tsx (1)
253-275: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
FieldSkeleton의 라벨 마크업이Input/Textarea의 라벨 스타일과 중복됩니다.
Text variant={'label1'} weight={'semibold'} className={'truncate'}조합은Input.tsx,Textarea.tsx가 이미 자체적으로 렌더링하는 라벨과 동일한 스타일입니다. 필요하다면 공통 라벨 프리미티브를 추출해 재사용하는 방안을 고려하세요. 현재로서는 동작에 영향을 주지 않습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/views/resume_edit/ui/edit/AiFeedbackDialog.tsx` around lines 253 - 275, Update FieldSkeleton to reuse the existing shared label primitive or label-rendering pattern used by Input and Textarea instead of duplicating the Text variant, weight, and className combination. Preserve the current label text and skeleton layout behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/sitemap.ts`:
- Around line 8-15: Remove the lastModified: new Date() fields from the sitemap
entries, including the entries for the site root and /login, unless a reliable
actual content modification timestamp is available; keep the existing
changeFrequency, priority, and URL values unchanged.
In `@src/features/pdf_upload/ui/PdfUpload.tsx`:
- Line 43: Update the warning message in the PdfUpload component to replace the
incorrect Korean particle in “용량를” with “용량을”, leaving the rest of the message
and toast behavior unchanged.
In `@src/views/resume_edit/ui/edit/AiFeedbackDialog.tsx`:
- Around line 212-220: Update the title Input in AiFeedbackDialog’s title
rendering branch to enforce the API limit by adding a maximum length of 150
characters. Leave the existing value, change handler, and pending-state behavior
unchanged.
- Around line 106-123: The handleGenerate function must catch failures from
polish({ request, workspaceId }) and display a user-facing error with
toast.error. Keep the existing successful response handling and drafts update
unchanged, while ensuring the rejection is handled after the generation request.
In `@src/views/resume_edit/ui/ResumeEditPage.tsx`:
- Around line 144-160: ResumeEditPage의 공유 isPending을 ResumeToolbar의 isSaving으로
직접 전달하지 말고, 자동저장 saveDraft 실행 상태와 수동 저장·완료 작업 상태를 분리하세요. useIntervalAutosave에서
발생한 저장이 진행 중이어도 툴바의 임시저장·완료 버튼은 활성화하고, 수동 저장 또는 완료 작업 중일 때만 해당 버튼들을 비활성화하도록
handleDraftSave와 기존 handleSave 상태를 조정하세요.
---
Nitpick comments:
In `@src/views/resume_edit/ui/edit/AiFeedbackDialog.tsx`:
- Around line 253-275: Update FieldSkeleton to reuse the existing shared label
primitive or label-rendering pattern used by Input and Textarea instead of
duplicating the Text variant, weight, and className combination. Preserve the
current label text and skeleton layout behavior.
🪄 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 Plus
Run ID: d2fdde1b-b9f1-447d-9c6d-315439600158
📒 Files selected for processing (43)
app/global-error.tsxapp/layout.tsxapp/robots.tsapp/sitemap.tsmiddleware.tsschema.graphqlsrc/app/layouts/RootLayout.tsxsrc/app/layouts/metadata.tssrc/entities/notion/model/notion.mutations.tssrc/entities/profile/api/profile.api.tssrc/entities/user/ui/UserProfile.tsxsrc/features/authenticate/ui/WithdrawButton.tsxsrc/features/notion_connect/hooks/useNotionReturn.tssrc/features/pdf_upload/ui/PdfUpload.tsxsrc/features/resume_pdf_download/ui/ResumeDownloadButton.tsxsrc/shared/lib/auth-error.tssrc/shared/lib/gql/gql.tssrc/shared/lib/gql/graphql.tssrc/shared/ui/error_fallback/ErrorFallback.stories.tsxsrc/shared/ui/error_fallback/ErrorFallback.tsxsrc/shared/ui/error_fallback/index.tssrc/shared/ui/index.tssrc/shared/ui/toast/Toast.tsxsrc/views/experiences/ui/AddProjectManualView.tsxsrc/views/experiences/ui/ExperiencesPage.tsxsrc/views/experiences_detail/ui/ExperienceDetailPage.tsxsrc/views/experiences_detail/ui/ExperienceDetailPanel.tsxsrc/views/experiences_detail/ui/ExperienceList.tsxsrc/views/home/ui/HomePage.tsxsrc/views/mypage/hooks/useProfileSectionForm.tssrc/views/mypage/ui/MyAccountPage.tsxsrc/views/mypage/ui/MyProfilePage.tsxsrc/views/onboarding/ui/NotionProcessingStep.tsxsrc/views/onboarding/ui/ResumeProcessingStep.tsxsrc/views/resume_create/ui/ExperiencePickerDialog.tsxsrc/views/resume_create/ui/ResumeCreatePage.tsxsrc/views/resume_detail/ui/ResumeDetailPage.tsxsrc/views/resume_edit/ui/ResumeEditPage.tsxsrc/views/resume_edit/ui/edit/AiFeedbackDialog.tsxsrc/views/resume_edit/ui/edit/CarrerSection.tsxsrc/views/resume_edit/ui/edit/CoreSkillSection.tsxsrc/views/resume_edit/ui/edit/ExperienceSection.tsxsrc/views/resumes/ui/ResumesPage.tsx
| lastModified: new Date(), | ||
| changeFrequency: 'yearly', | ||
| priority: 1 | ||
| }, | ||
| { | ||
| url: `${SITE_URL}/login`, | ||
| lastModified: new Date(), | ||
| changeFrequency: 'yearly', |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
실제 콘텐츠 수정 시각이 없으면 lastModified를 제거하세요.
new Date()는 sitemap 생성 시각을 각 페이지의 수정 시각으로 게시합니다. 배포 또는 재생성만으로 모든 URL이 변경된 것처럼 표시됩니다.
실제 수정 시각을 제공할 수 있을 때만 lastModified를 설정하세요.
수정 예시
{
url: SITE_URL,
- lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1
},
{
url: `${SITE_URL}/login`,
- lastModified: new Date(),
changeFrequency: 'yearly',
priority: 0.8
}📝 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.
| lastModified: new Date(), | |
| changeFrequency: 'yearly', | |
| priority: 1 | |
| }, | |
| { | |
| url: `${SITE_URL}/login`, | |
| lastModified: new Date(), | |
| changeFrequency: 'yearly', | |
| changeFrequency: 'yearly', | |
| priority: 1 | |
| }, | |
| { | |
| url: `${SITE_URL}/login`, | |
| changeFrequency: 'yearly', |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/sitemap.ts` around lines 8 - 15, Remove the lastModified: new Date()
fields from the sitemap entries, including the entries for the site root and
/login, unless a reliable actual content modification timestamp is available;
keep the existing changeFrequency, priority, and URL values unchanged.
| const handleGenerate = async () => { | ||
| // 경험 세부내용을 다듬을 때 맥락으로 넘길 경험명 값(있으면). | ||
| amplitude.track(AMPLITUDE_EVENTS.AI_EDIT_STARTED, { edit_mode: EDIT_MODE_BY_STRUCTURE[structure] }) | ||
| const titleTarget = targets.find((target) => target.kind === 'EXPERIENCE_TITLE') | ||
| const title = titleTarget ? drafts[titleTarget.name] : undefined | ||
|
|
||
| setIsGenerating(true) | ||
| try { | ||
| const entries = await Promise.all( | ||
| targets.map(async (target) => { | ||
| const request: PolishProfileTextRequest = { | ||
| kind: target.kind, | ||
| text: drafts[target.name] ?? '', | ||
| structure, | ||
| instruction: instruction || null, | ||
| jdId: jdId || null, | ||
| title: target.kind === 'EXPERIENCE_DESCRIPTION' ? (title ?? null) : null | ||
| } | ||
| const polished = await polish({ request, workspaceId }) | ||
| return [target.name, polished] as const | ||
| }) | ||
| ) | ||
| setDrafts((prev) => ({ ...prev, ...Object.fromEntries(entries) })) | ||
| } finally { | ||
| setIsGenerating(false) | ||
|
|
||
| const request: PolishProfileTextRequest = { | ||
| kind, | ||
| description: drafts[description.name] ?? '', | ||
| structure, | ||
| instruction: instruction || null, | ||
| jdId: jdId || null, | ||
| title: title ? (drafts[title.name] ?? '') : null | ||
| } | ||
| const polished = await polish({ request, workspaceId }) | ||
| setDrafts((prev) => ({ | ||
| ...prev, | ||
| [description.name]: polished.description, | ||
| ...(title && polished.title !== null ? { [title.name]: polished.title } : {}) | ||
| })) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# usePolishProfileText 정의와 공용 mutation 에러 처리 패턴 확인
fd -e ts -e tsx . src/entities/profile | xargs rg -n -C6 'usePolishProfileText|onError'Repository: YAPP-Github/28th-Web-Team-1-FE
Length of output: 2322
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate files =="
fd -e ts -e tsx 'AiFeedbackDialog|ResumeEditPage|profile\.mutat|profile\.queries|polish' src || true
echo
echo "== AiFeedbackDialog relevant sections =="
fd -e tsx AiFeedbackDialog.tsx src | while read -r f; do
echo "--- $f"
wc -l "$f"
sed -n '1,240p' "$f"
done
echo
echo "== profile mutations =="
fd -e ts profile.mutations.ts src | while read -r f; do
echo "--- $f"
wc -l "$f"
sed -n '1,180p' "$f"
done
echo
echo "== related resume page handlers =="
fd -e tsx ResumeEditPage.tsx src | while read -r f; do
echo "--- $f"
wc -l "$f"
rg -n -C 5 'generateCoreCompetency|toast\.error|polish|handleGenerate|handleApply|handleDraftSave' "$f" || true
done
echo
echo "== global/error toast patterns around usePolishProfileText usages =="
rg -n -C 4 'usePolishProfileText|from ["'\''].*profile["'\'']|onError|toast\.error|toast\.success' src/views src/entities src/shared src/lib 2>/dev/null || trueRepository: YAPP-Github/28th-Web-Team-1-FE
Length of output: 50388
handleGenerate 실패 시 사용자에게 에러 토스트를 표시하세요.
usePolishProfileText는 기본 onError 처리 없이 polishProfileText 호출 결과뿐 반환하고, polish({ request, workspaceId })는 성공한 결과만 drafts에 저장합니다. 요청이 실패하면 isPending만 다시 false가 되고 사용자에게는 아무 안내가 표시되지 않습니다. 생성 버튼에서 polish(...) 실패를 catch해 toast.error(...)로 안내하도록 수정하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/views/resume_edit/ui/edit/AiFeedbackDialog.tsx` around lines 106 - 123,
The handleGenerate function must catch failures from polish({ request,
workspaceId }) and display a user-facing error with toast.error. Keep the
existing successful response handling and drafts update unchanged, while
ensuring the rejection is handled after the generation request.
| {title && ( | ||
| <Flex direction={'column'} gap={'2'}> | ||
| {isPending ? ( | ||
| <FieldSkeleton label={title.label} /> | ||
| ) : ( | ||
| <Input label={target.label} clearable={false} value={drafts[target.name] ?? ''} onChange={(event) => setDrafts((prev) => ({ ...prev, [target.name]: event.target.value }))} /> | ||
| <Input label={title.label} clearable={false} value={drafts[title.name] ?? ''} onChange={(event) => setDrafts((prev) => ({ ...prev, [title.name]: event.target.value }))} /> | ||
| )} | ||
| </Flex> | ||
| ))} | ||
| )} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
제목 입력 필드에 글자 수 제한이 없습니다.
description Textarea는 maxLength={500}으로 API 문서의 글자 수 제한을 강제합니다. 반면 title Input(Line 217)에는 maxLength가 없습니다. graphql.ts의 PolishProfileTextRequest.title 주석은 EXPERIENCE일 때 최대 150자로 명시합니다.
title Input에도 maxLength={150}을 추가해 일관성을 맞추세요.
🛠️ 제안 수정
- <Input label={title.label} clearable={false} value={drafts[title.name] ?? ''} onChange={(event) => setDrafts((prev) => ({ ...prev, [title.name]: event.target.value }))} />
+ <Input
+ label={title.label}
+ clearable={false}
+ maxLength={150}
+ value={drafts[title.name] ?? ''}
+ onChange={(event) => setDrafts((prev) => ({ ...prev, [title.name]: event.target.value }))}
+ />📝 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.
| {title && ( | |
| <Flex direction={'column'} gap={'2'}> | |
| {isPending ? ( | |
| <FieldSkeleton label={title.label} /> | |
| ) : ( | |
| <Input label={target.label} clearable={false} value={drafts[target.name] ?? ''} onChange={(event) => setDrafts((prev) => ({ ...prev, [target.name]: event.target.value }))} /> | |
| <Input label={title.label} clearable={false} value={drafts[title.name] ?? ''} onChange={(event) => setDrafts((prev) => ({ ...prev, [title.name]: event.target.value }))} /> | |
| )} | |
| </Flex> | |
| ))} | |
| )} | |
| {title && ( | |
| <Flex direction={'column'} gap={'2'}> | |
| {isPending ? ( | |
| <FieldSkeleton label={title.label} /> | |
| ) : ( | |
| <Input | |
| label={title.label} | |
| clearable={false} | |
| maxLength={150} | |
| value={drafts[title.name] ?? ''} | |
| onChange={(event) => setDrafts((prev) => ({ ...prev, [title.name]: event.target.value }))} | |
| /> | |
| )} | |
| </Flex> | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/views/resume_edit/ui/edit/AiFeedbackDialog.tsx` around lines 212 - 220,
Update the title Input in AiFeedbackDialog’s title rendering branch to enforce
the API limit by adding a maximum length of 150 characters. Leave the existing
value, change handler, and pending-state behavior unchanged.
leeleeleeleejun
left a comment
There was a problem hiding this comment.
넵 작업 고생하셨습니다!
코드래빗이 오타 짚어줬네영
현재 에러 메시지들을 라이팅 수정했는데,
toast.success()처럼 성공 시 라이팅도 디자이너와 싱크를 맞춰야 하겠네여!
| /** | ||
| * 로그인 이후에만 의미 있는 페이지가 대부분이라 전체를 비공개로 막되, | ||
| * 랜딩(/) · 로그인(/login)만 예외로 연다. | ||
| * '/$'는 '/'로 시작하는 모든 경로가 아니라 루트 경로 자체만 가리킨다(Disallow: /와의 충돌 방지). | ||
| */ | ||
| const robots = (): MetadataRoute.Robots => ({ | ||
| rules: { | ||
| userAgent: '*', | ||
| allow: ['/$', '/login'], | ||
| disallow: '/' | ||
| }, | ||
| sitemap: `${SITE_URL}/sitemap.xml` | ||
| }) |
| return ( | ||
| <html lang="ko" className={`${pretendard.className} ${pretendard.variable} ${elmsSans.variable} h-full antialiased`}> | ||
| <body className="flex h-full flex-col"> | ||
| <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationJsonLd).replace(/</g, '\\u003c') }} /> |
There was a problem hiding this comment.
해당 부분은 josn-ld인데,
JSON 형식을 기반으로 데이터를 정형화하여, 검색엔진과 AI가 콘텐츠의 의미를 명확히 파악할 수 있도록 돕습니다.
이를 통해 SEO 및 AEO 향상에 기여합니다.
시맨틱태그 등으로 하는 것과 별개로 검색엔진이 사이트를 이해하는데 도움이 됩니다.
성공도 같이 작성해서 피드백 요청했는데, 성공 메시지는 별도로 수정 반영 사항이 없어서 현행 유지하기로 했습니다! |
#️⃣연관된 이슈
📝작업 내용
에러메시지 라이팅 수정
토스트에서 줄바꿈이 되도록 토스트 컴포넌트를 수정했습니다.SEO 초기세팅
/sitemap.xml과//login을 허용해두었습니다.스크린샷 (선택)
💬리뷰 요구사항(선택)
Summary by CodeRabbit
새로운 기능
개선 사항