Skip to content

feat: 검색 최적화 기반 정비#538

Merged
manNomi merged 6 commits into
mainfrom
feat/seo-geo-foundation
Jun 2, 2026
Merged

feat: 검색 최적화 기반 정비#538
manNomi merged 6 commits into
mainfrom
feat/seo-geo-foundation

Conversation

@manNomi
Copy link
Copy Markdown
Contributor

@manNomi manNomi commented Jun 1, 2026

관련 이슈

  • resolves: 없음

작업 내용

  • 사이트 기준 URL을 공통 SEO 유틸로 통일하고 canonical, robots, sitemap의 기본 도메인을 https://www.solid-connection.com으로 맞췄습니다.
  • sitemap을 공개 검색 표면 중심으로 재구성해 홈, 대학 선택, 약관, 홈대학별 대학 목록, 대학 상세 URL을 노출합니다.
  • 로그인, 회원가입, 마이페이지, 멘토, 커뮤니티, 지원/성적 입력 등 인증 또는 클라이언트 의존 페이지는 noindex로 정리했습니다.
  • 홈 JSON-LD의 실제 라우트와 맞지 않는 SearchAction을 제거하고, 대학 상세 404는 notFound()로 처리하도록 정리했습니다.
  • Next 타입체크를 막던 revalidateTag 호출 시그니처를 현재 타입에 맞게 수정했습니다.

특이 사항

  • 독립 리뷰어 피드백을 반영해 멘토/커뮤니티 상세는 현재 서버 렌더 공개 콘텐츠와 실제 클라이언트 콘텐츠가 일치하지 않아 sitemap에서 제외하고 noindex로 두었습니다.
  • pnpm --filter @solid-connect/web run build는 컴파일 성공 후 static prerender 단계에서 Unsupported Server Component type: {...} 오류로 실패합니다. 오류는 루트, 로그인, 마이페이지, 대학 상세 등 다수 기존 라우트에 걸쳐 발생하며, 빌드 중 Next가 tsconfig.jsonjsx를 자동 변경하려 해 해당 변경은 원복했습니다.
  • 로컬 Node는 v23.10.0이고 패키지 요구 버전은 Node 22.x라 검증 시 경고가 출력됩니다.

리뷰 요구사항 (선택)

  • sitemap에 포함한 공개 URL 범위가 현재 서비스 정책과 맞는지 확인 부탁드립니다.
  • 멘토/커뮤니티를 향후 검색 노출하려면 서버 렌더 공개 상세, 개인정보 동의, UGC moderation, 실제 404/noindex 정책을 먼저 정리해야 합니다.

검증

  • pnpm --filter @solid-connect/web run lint:check 통과
  • pnpm --filter @solid-connect/web run typecheck:ci 통과
  • 커밋 훅의 ci:check 통과
  • push 훅의 ci:check 통과 후 next build prerender 오류로 실패하여, 위 특이 사항을 남기고 훅을 우회해 브랜치를 푸시했습니다.

@manNomi manNomi requested review from enunsnv and wibaek as code owners June 1, 2026 07:15
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

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

Project Deployment Actions Updated (UTC)
solid-connection-web Ready Ready Preview, Comment Jun 2, 2026 6:40am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
solid-connect-web-admin Skipped Skipped Jun 2, 2026 6:40am

@github-actions github-actions Bot added the web label Jun 1, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Warning

Review limit reached

@manNomi, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes and 7 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 343d7bf2-4f68-4057-8381-8b5e8970d195

📥 Commits

Reviewing files that changed from the base of the PR and between 797be74 and 8b4b12c.

📒 Files selected for processing (2)
  • apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/index.tsx
  • apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/index.tsx

Walkthrough

  1. SEO 유틸리티 추가
    • getSiteUrl, isNonIndexSiteUrl, createUrl/createAbsoluteUrl, stripHtml, truncateDescription 및 NO_INDEX_ROBOTS를 도입했습니다.
  2. 인프라 리팩토링
    • RootLayout의 metadataBase를 getSiteUrl() 기반으로 변경하고 robots·sitemap 생성 로직을 SEO 유틸로 통합했습니다.
  3. 페이지별 noindex 적용 및 동적 메타데이터 전환
    • 로그인/가입/마이페이지/커뮤니티/멘토/대학 관련 다수 페이지에 NO_INDEX_ROBOTS를 적용하고 일부는 generateMetadata로 전환했습니다.
  4. Canonical·OpenGraph 통일
    • 홈·약관·대학 홈 등에서 createUrl 기반 canonical·openGraph·Structured Data를 일관되게 생성합니다.
  5. API 정리
    • revalidateTag 호출을 revalidateTagNow 래퍼로 통일하여 expire 옵션을 중앙화했습니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • wibaek
  • enunsnv
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 '검색 최적화 기반 정비'로, 핵심 변경사항인 SEO 통합 및 최적화를 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 관련 이슈, 작업 내용, 특이 사항, 리뷰 요구사항을 포함하여 템플릿을 잘 따르고 있으며, 변경사항이 상세하게 기술되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/seo-geo-foundation

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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9a527d7693

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (boardCode) {
revalidatePath(`/community/${boardCode}`);
revalidateTag(`posts-${boardCode}`, { expire: 0 });
revalidateTag(`posts-${boardCode}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore the required revalidateTag profile

With this repo on Next 16.2.6, the official API docs define revalidateTag(tag: string, profile: string | { expire?: number }) and note that the single-argument form is deprecated and a TypeScript error (docs); because the root workflow asks for pnpm typecheck, this route will fail type checking for both this board invalidation and the generic tag branch unless the second argument is kept, e.g. { expire: 0 } or an appropriate cache profile.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (2)
apps/web/src/app/sitemap.ts (1)

29-49: ⚡ Quick win

1. 대학 데이터 패치 실패 시에도 정적 경로는 살려두면 어떨까요?

getAllUniversities()가 거절(reject)되면 sitemap 생성 전체가 실패해서, 안전한 정적 경로(/, /university, /terms, 홈 대학 목록)까지 함께 사라지게 됩니다. PR 설명에도 prerender 단계 불안정이 언급되어 있으니, 동적 경로만 실패하도록 감싸두면 검색 노출의 핵심 경로를 지킬 수 있어요.

♻️ 동적 경로 실패를 흡수하는 제안
 const getUniversityDetailRoutes = async (): Promise<MetadataRoute.Sitemap> => {
-  const universities = await getAllUniversities();
+  let universities;
+  try {
+    universities = await getAllUniversities();
+  } catch {
+    return [];
+  }
   const seenUrls = new Set<string>();
🤖 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 `@apps/web/src/app/sitemap.ts` around lines 29 - 49, The
getUniversityDetailRoutes function currently lets getAllUniversities rejection
bubble up and break sitemap generation; wrap the await getAllUniversities() call
in a try/catch inside getUniversityDetailRoutes (or handle its Promise
rejection) so that on error you log the failure and return an empty array (i.e.,
no dynamic university entries) instead of throwing, preserving the static
routes; reference getUniversityDetailRoutes, getAllUniversities, seenUrls, and
toSitemapEntry when making the change.
apps/web/src/app/university/[homeUniversity]/[id]/page.tsx (1)

126-144: 💤 Low value

1. 404 분기 이후 남은 삼항 조건은 이제 죽은 코드예요.

isNotFoundErrortrue면 위에서 notFound()가 즉시 throw하기 때문에, 아래 fallback 렌더는 항상 404가 아닌 경우(준비 중)에만 도달합니다. 그래서 title/descriptionisNotFoundError ? ... : undefined 삼항은 늘 undefined로만 평가되는 죽은 분기가 되었어요. 의도는 정확하니 가독성 차원에서 정리해두면 다음 사람이 헷갈리지 않습니다.

♻️ 불필요해진 조건 정리 제안
-    const isNotFoundError = universityDetailResult.status === 404;
-
-    if (isNotFoundError) {
+    if (universityDetailResult.status === 404) {
       notFound();
     }

     return (
       <>
         <TopDetailNavigation title="파견 학교 상세" backHref={`/university/${homeUniversity}`} />
-        <UniversityDetailPreparingFallback
-          backHref={`/university/${homeUniversity}`}
-          title={isNotFoundError ? "해당 대학 정보를 찾을 수 없어요." : undefined}
-          description={
-            isNotFoundError ? "요청하신 파견학교를 찾지 못했습니다. 목록에서 다른 학교를 선택해 주세요." : undefined
-          }
-        />
+        <UniversityDetailPreparingFallback backHref={`/university/${homeUniversity}`} />
       </>
     );
🤖 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 `@apps/web/src/app/university/`[homeUniversity]/[id]/page.tsx around lines 126
- 144, The ternary checks using isNotFoundError are dead code because notFound()
short-circuits when isNotFoundError is true; remove the redundant conditionals
in the UniversityDetailPreparingFallback props (title and description) and
simplify them to their preparing-state values or omit them (i.e., pass undefined
directly or remove the props) so the component only reflects the “preparing”
state; update references to isNotFoundError, notFound(), and
UniversityDetailPreparingFallback to ensure clarity.
🤖 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 `@apps/web/src/app/api/revalidate/route.ts`:
- Line 60: The call to revalidateTag(`posts-${boardCode}`) is missing the
required second argument causing TS2554; restore the profile argument (either
"max" or the original object like { expire: 0 }) to match the intended cache
behavior. Update both calls of revalidateTag (the one at
revalidateTag(`posts-${boardCode}`) and the other similar call at line 81) to
pass the appropriate profile as the second parameter so the function signature
for revalidateTag(tag, profile) is satisfied and the desired revalidation policy
(e.g., "max" or { expire: 0 }) is preserved.

In `@apps/web/src/app/my/match/page.tsx`:
- Line 9: The page metadata title ("metadata.title") currently reads "프로필 수정"
but the rendered header uses TopDetailNavigation with the label "매칭 멘토"; update
the metadata to match the visible header by changing metadata.title to "매칭 멘토"
(or alternatively make TopDetailNavigation read from metadata.title) in the
page.tsx so the browser tab and page header show the same title; locate the
export/const named metadata and the TopDetailNavigation usage in this file and
ensure they use the identical string.

---

Nitpick comments:
In `@apps/web/src/app/sitemap.ts`:
- Around line 29-49: The getUniversityDetailRoutes function currently lets
getAllUniversities rejection bubble up and break sitemap generation; wrap the
await getAllUniversities() call in a try/catch inside getUniversityDetailRoutes
(or handle its Promise rejection) so that on error you log the failure and
return an empty array (i.e., no dynamic university entries) instead of throwing,
preserving the static routes; reference getUniversityDetailRoutes,
getAllUniversities, seenUrls, and toSitemapEntry when making the change.

In `@apps/web/src/app/university/`[homeUniversity]/[id]/page.tsx:
- Around line 126-144: The ternary checks using isNotFoundError are dead code
because notFound() short-circuits when isNotFoundError is true; remove the
redundant conditionals in the UniversityDetailPreparingFallback props (title and
description) and simplify them to their preparing-state values or omit them
(i.e., pass undefined directly or remove the props) so the component only
reflects the “preparing” state; update references to isNotFoundError,
notFound(), and UniversityDetailPreparingFallback to ensure clarity.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 20373075-2365-4913-8ada-a5ebcdf992e6

📥 Commits

Reviewing files that changed from the base of the PR and between 857e527 and 9a527d7.

📒 Files selected for processing (41)
  • apps/web/src/app/(home)/page.tsx
  • apps/web/src/app/api/revalidate/route.ts
  • apps/web/src/app/community/[boardCode]/[postId]/modify/page.tsx
  • apps/web/src/app/community/[boardCode]/[postId]/page.tsx
  • apps/web/src/app/community/[boardCode]/create/page.tsx
  • apps/web/src/app/community/[boardCode]/page.tsx
  • apps/web/src/app/community/page.tsx
  • apps/web/src/app/layout.tsx
  • apps/web/src/app/login/apple/callback/page.tsx
  • apps/web/src/app/login/kakao/callback/page.tsx
  • apps/web/src/app/login/page.tsx
  • apps/web/src/app/mentor/[id]/page.tsx
  • apps/web/src/app/mentor/chat/[chatId]/page.tsx
  • apps/web/src/app/mentor/chat/page.tsx
  • apps/web/src/app/mentor/modify/page.tsx
  • apps/web/src/app/mentor/page.tsx
  • apps/web/src/app/mentor/waiting/page.tsx
  • apps/web/src/app/my/apply-mentor/layout.tsx
  • apps/web/src/app/my/favorite/page.tsx
  • apps/web/src/app/my/match/page.tsx
  • apps/web/src/app/my/modify/page.tsx
  • apps/web/src/app/my/page.tsx
  • apps/web/src/app/my/password/page.tsx
  • apps/web/src/app/robots.ts
  • apps/web/src/app/sign-up/email/page.tsx
  • apps/web/src/app/sign-up/page.tsx
  • apps/web/src/app/sitemap.ts
  • apps/web/src/app/terms/page.tsx
  • apps/web/src/app/university/(home)/page.tsx
  • apps/web/src/app/university/[homeUniversity]/[id]/page.tsx
  • apps/web/src/app/university/[homeUniversity]/page.tsx
  • apps/web/src/app/university/[homeUniversity]/search/page.tsx
  • apps/web/src/app/university/application/apply/page.tsx
  • apps/web/src/app/university/application/page.tsx
  • apps/web/src/app/university/list/[homeUniversityName]/page.tsx
  • apps/web/src/app/university/score/example/layout.tsx
  • apps/web/src/app/university/score/page.tsx
  • apps/web/src/app/university/score/submit/gpa/page.tsx
  • apps/web/src/app/university/score/submit/language-test/page.tsx
  • apps/web/src/app/university/search/page.tsx
  • apps/web/src/utils/seo.ts

Comment thread apps/web/src/app/api/revalidate/route.ts Outdated
Comment thread apps/web/src/app/my/match/page.tsx Outdated
@manNomi
Copy link
Copy Markdown
Contributor Author

manNomi commented Jun 2, 2026

@codex review 한국어로 리뷰해줘. 코드 식별자는 영어 원문을 유지하고, 실제 버그/회귀/테스트 누락 위주로 봐줘.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@vercel vercel Bot temporarily deployed to Preview – solid-connect-web-admin June 2, 2026 06:38 Inactive
@manNomi manNomi merged commit b2d33f4 into main Jun 2, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant