Conversation
- OG Image 설정을 위한 컴포넌트 분리 작업 및 이미지 다운로드
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. Walkthrough서버 컴포넌트로 join/share 페이지를 전환하고 메타데이터 생성 함수를 추가했으며, 클라이언트 폼 로직을 Changes
Sequence DiagramsequenceDiagram
actor User
participant Browser
participant Server
participant JoinForm as JoinForm (Client)
participant API as API Server
participant Storage as LocalStorage
User->>Browser: 요청 /join/{id}
Browser->>Server: 페이지 요청
Server->>Server: generateMetadata(params, searchParams)
Server->>Server: render Page -> includes JoinForm mount point
Server-->>Browser: HTML + metadata
Browser->>JoinForm: mount (client)
JoinForm->>API: (optional) check login / initial hooks
User->>JoinForm: 입력(이름, 비밀번호) 및 제출
JoinForm->>JoinForm: 입력 검증
JoinForm->>API: useEnterParticipant mutation
API-->>JoinForm: 성공 / 실패
alt 성공
JoinForm->>Storage: setMeetingUserId(...)
JoinForm->>Browser: navigate to /meeting/{id}
else 실패
JoinForm->>Browser: show toast error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/map/kakaoMapLine.tsx (1)
89-98:⚠️ Potential issue | 🟡 Minor빈 상태와 정상 렌더링 경로 간 스타일 불일치
Line 89의 빈 상태 컨테이너에는
w-full max-w-full overflow-hidden이 추가되었지만, Line 98의 실제 지도 컨테이너에는 여전히relative ${className}만 사용하고 있습니다. 모바일 가로 스크롤 방지가 목적이라면 정상 렌더링 경로에도 동일한 클래스를 적용해야 합니다.♻️ 수정 제안
- <div className={`relative ${className}`}> + <div className={`relative w-full max-w-full overflow-hidden ${className}`}>
🤖 Fix all issues with AI agents
In `@components/join/joinForm.tsx`:
- Around line 114-148: The custom checkbox built around
isRemembered/setIsRemembered (the outer clickable div and inner visual div)
lacks keyboard accessibility; update the outer div to include role="checkbox",
tabIndex={0}, aria-checked={isRemembered}, and an onKeyDown handler that toggles
setIsRemembered when Space or Enter is pressed (mirroring the onClick behavior),
and ensure the visible label span remains associated (e.g., keep it adjacent) so
screen readers announce the control correctly; also preserve existing visual
state logic using isFormValid/isRemembered when applying classes.
In `@components/modal/nudgeModal.tsx`:
- Around line 49-56: Update the Image usage in NudgeModal by changing the alt
text from "모임 링크를 공유해주세요" to a string that reflects the nudge intent (e.g., "아직
입력하지 않은 친구를 재촉해 보세요") in the Image component instance, and rename the referenced
image file from "nudge_modals.jpg" to the consistent singular "nudge_modal.jpg"
(and update any import/path usages) so the <Image ... src="nudge_modal.jpg" ...
alt="아직 입력하지 않은 친구를 재촉해 보세요" /> accurately describes the modal and matches
naming conventions.
🧹 Nitpick comments (8)
app/meeting/[id]/page.tsx (1)
315-320: 모바일에서 제출 버튼 하단 여백 부재 확인 필요
md:absolute조건부 포지셔닝은 좋은 접근이지만, 모바일(비absolute)에서 버튼 하단에 여백(mb)이 없어 화면 하단에 버튼이 붙을 수 있습니다. Toast 컴포넌트(Line 321)와의 간격도 확인해보세요.hooks/api/query/useShareMeeting.ts (1)
46-49: 클립보드 복사 실패 시alert()대신 Toast 사용 고려성공 시에는
show()로 Toast를 표시하지만, 실패 시에는alert()를 사용하고 있습니다. 일관된 UX를 위해 실패 시에도 Toast 또는 별도의 에러 피드백을 사용하는 것이 좋습니다.components/share/shareContent.tsx (2)
17-18: 로딩 중 빈 화면 표시 문제
isLoading시null을 반환하면 사용자에게 빈 화면이 잠시 보일 수 있습니다. 간단한 스켈레톤이나 스피너를 표시하면 UX가 개선됩니다.
28-35: 이미지에 적절한 크기 제약 확인 필요컨테이너가
h-70 w-80로 고정되어 있지만,Image컴포넌트의width={360} height={257}은 이와 다릅니다. 컨테이너에overflow-hidden이 없으므로 특정 상황에서 이미지가 컨테이너를 벗어날 수 있습니다. PR 목적에서 언급된 "모달에서 이미지가 영역을 벗어나는 문제"와 유사한 패턴입니다.♻️ 수정 제안
- <section className="mb-9 flex h-70 w-80 max-w-sm items-center justify-center rounded-2xl md:w-90"> + <section className="mb-9 flex h-70 w-80 max-w-sm items-center justify-center overflow-hidden rounded-2xl md:w-90">components/join/joinForm.tsx (1)
111-112: 패스워드 입력 필드의 Tailwind 클래스 충돌 가능성외부에
pl-3 text-center가 있고, 조건부로pl-0 text-center또는pl-3 text-left를 추가하고 있습니다. Tailwind v4에서는 동일 속성의 중복 유틸리티 클래스 간 우선순위가 소스 순서가 아닌 CSS 레이어 순서로 결정되므로, 의도대로 오버라이드가 안 될 수 있습니다.clsx/cn등으로 조건부 클래스를 깔끔하게 분리하는 것을 권장합니다.♻️ 조건부 클래스 분리 제안
- className={`border-gray-2 placeholder:text-gray-3 text-gray-10 focus:border-blue-5 w-full rounded-sm border py-2 pl-3 text-center text-[15px] focus:bg-white focus:outline-none ${password ? 'pl-0 text-center' : 'pl-3 text-left'}`} + className={`border-gray-2 placeholder:text-gray-3 text-gray-10 focus:border-blue-5 w-full rounded-sm border py-2 text-[15px] focus:bg-white focus:outline-none ${password ? 'pl-0 text-center' : 'pl-3 text-left'}`}app/join/[id]/page.tsx (1)
30-42: OG URL 하드코딩 및description누락
Line 34의
url이 하드코딩되어 있습니다.app/layout.tsx에metadataBase가 설정되어 있다면, 상대 경로(/join/${id})만으로도 충분합니다. 환경(스테이징/프로덕션)에 따라 달라질 수 있으므로metadataBase를 활용하는 것이 더 유지보수에 유리합니다.
app/share/[id]/page.tsx는description을 포함하고 있는데, 이 파일에는 누락되어 있습니다. SNS 미리보기 시 설명 텍스트가 표시되지 않을 수 있습니다.♻️ 개선 제안
return { title, + description: isNudge + ? '모임원들이 기다리고 있어요. 출발지를 입력해주세요!' + : '링크를 통해 모임에 참여하고 출발지를 등록하세요.', openGraph: { title, - url: `https://www.mingling.kr/join/${id}`, // 실제 도메인으로 변경 권장 + description: isNudge + ? '모임원들이 기다리고 있어요. 출발지를 입력해주세요!' + : '링크를 통해 모임에 참여하고 출발지를 등록하세요.', + url: `/join/${id}`, images: [imageUrl, ...previousImages], }, twitter: { card: 'summary_large_image', title, + description: isNudge + ? '모임원들이 기다리고 있어요. 출발지를 입력해주세요!' + : '링크를 통해 모임에 참여하고 출발지를 등록하세요.', images: [imageUrl], }, };app/share/[id]/page.tsx (1)
8-27: 메타데이터 구조가 깔끔합니다. URL 하드코딩만 일관성 있게 수정하면 좋겠습니다.
join/[id]/page.tsx와 동일하게url이 하드코딩되어 있습니다.metadataBase가 설정되어 있다면 상대 경로(/share/${id})로 충분합니다. 또한join페이지에서는parent의openGraph.images를 병합하는데, 이 파일에서는 하지 않고 있습니다. 의도적이라면 괜찮지만, 일관성을 위해 확인해 주세요.components/modal/shareModal.tsx (1)
21-84:ShareModal과NudgeModal의 중복이 큽니다.두 모달 컴포넌트가 mode, 이미지 경로, 타이틀 텍스트만 다르고 구조가 거의 동일합니다. 공유 모달 컴포넌트로 통합하면 향후 UI 변경 시 한 곳만 수정하면 됩니다.
♻️ 통합 컴포넌트 예시
// components/modal/linkShareModal.tsx interface LinkShareModalProps { isOpen: boolean; onClose: () => void; meetingId: string; mode: 'share' | 'nudge'; } const MODAL_CONFIG = { share: { title: '모임 링크를 공유해주세요', image: '/images/share_modal.jpg', inputName: 'shareLink', }, nudge: { title: ( <>아직 입력하지 않은 친구를 <br /> 재촉해 보세요!</> ), image: '/images/nudge_modals.jpg', inputName: 'NudgeLink', }, } as const;
| <div | ||
| onClick={() => setIsRemembered(!isRemembered)} | ||
| className="flex cursor-pointer items-center gap-2" | ||
| > | ||
| <div | ||
| className={`flex h-5 w-5 items-center justify-center rounded border transition-colors ${ | ||
| !isRemembered | ||
| ? 'border-gray-300 bg-white' | ||
| : isFormValid | ||
| ? 'border-blue-500 bg-blue-500' | ||
| : 'border-gray-300 bg-gray-300' | ||
| }`} | ||
| > | ||
| {isRemembered && ( | ||
| <svg | ||
| width="8" | ||
| height="6" | ||
| viewBox="0 0 14 10" | ||
| fill="none" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| > | ||
| <path | ||
| d="M1 5L4.5 8.5L13 1" | ||
| stroke="white" | ||
| strokeWidth="2" | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| /> | ||
| </svg> | ||
| )} | ||
| </div> | ||
| <span className={`text-xs font-medium ${isFormValid ? 'text-blue-5' : 'text-gray-5'}`}> | ||
| 내 정보 기억하기 | ||
| </span> | ||
| </div> |
There was a problem hiding this comment.
커스텀 체크박스에 키보드 접근성이 누락되어 있습니다.
div 기반의 체크박스 토글에 role="checkbox", tabIndex={0}, onKeyDown (Space/Enter 처리), aria-checked 속성이 없어 키보드 사용자가 이 컨트롤을 조작할 수 없습니다.
♿ 접근성 개선 제안
<div
onClick={() => setIsRemembered(!isRemembered)}
className="flex cursor-pointer items-center gap-2"
+ role="checkbox"
+ aria-checked={isRemembered}
+ tabIndex={0}
+ onKeyDown={(e) => {
+ if (e.key === ' ' || e.key === 'Enter') {
+ e.preventDefault();
+ setIsRemembered(!isRemembered);
+ }
+ }}
>📝 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 | |
| onClick={() => setIsRemembered(!isRemembered)} | |
| className="flex cursor-pointer items-center gap-2" | |
| > | |
| <div | |
| className={`flex h-5 w-5 items-center justify-center rounded border transition-colors ${ | |
| !isRemembered | |
| ? 'border-gray-300 bg-white' | |
| : isFormValid | |
| ? 'border-blue-500 bg-blue-500' | |
| : 'border-gray-300 bg-gray-300' | |
| }`} | |
| > | |
| {isRemembered && ( | |
| <svg | |
| width="8" | |
| height="6" | |
| viewBox="0 0 14 10" | |
| fill="none" | |
| xmlns="http://www.w3.org/2000/svg" | |
| > | |
| <path | |
| d="M1 5L4.5 8.5L13 1" | |
| stroke="white" | |
| strokeWidth="2" | |
| strokeLinecap="round" | |
| strokeLinejoin="round" | |
| /> | |
| </svg> | |
| )} | |
| </div> | |
| <span className={`text-xs font-medium ${isFormValid ? 'text-blue-5' : 'text-gray-5'}`}> | |
| 내 정보 기억하기 | |
| </span> | |
| </div> | |
| <div | |
| onClick={() => setIsRemembered(!isRemembered)} | |
| className="flex cursor-pointer items-center gap-2" | |
| role="checkbox" | |
| aria-checked={isRemembered} | |
| tabIndex={0} | |
| onKeyDown={(e) => { | |
| if (e.key === ' ' || e.key === 'Enter') { | |
| e.preventDefault(); | |
| setIsRemembered(!isRemembered); | |
| } | |
| }} | |
| > | |
| <div | |
| className={`flex h-5 w-5 items-center justify-center rounded border transition-colors ${ | |
| !isRemembered | |
| ? 'border-gray-300 bg-white' | |
| : isFormValid | |
| ? 'border-blue-500 bg-blue-500' | |
| : 'border-gray-300 bg-gray-300' | |
| }`} | |
| > | |
| {isRemembered && ( | |
| <svg | |
| width="8" | |
| height="6" | |
| viewBox="0 0 14 10" | |
| fill="none" | |
| xmlns="http://www.w3.org/2000/svg" | |
| > | |
| <path | |
| d="M1 5L4.5 8.5L13 1" | |
| stroke="white" | |
| strokeWidth="2" | |
| strokeLinecap="round" | |
| strokeLinejoin="round" | |
| /> | |
| </svg> | |
| )} | |
| </div> | |
| <span className={`text-xs font-medium ${isFormValid ? 'text-blue-5' : 'text-gray-5'}`}> | |
| 내 정보 기억하기 | |
| </span> | |
| </div> |
🤖 Prompt for AI Agents
In `@components/join/joinForm.tsx` around lines 114 - 148, The custom checkbox
built around isRemembered/setIsRemembered (the outer clickable div and inner
visual div) lacks keyboard accessibility; update the outer div to include
role="checkbox", tabIndex={0}, aria-checked={isRemembered}, and an onKeyDown
handler that toggles setIsRemembered when Space or Enter is pressed (mirroring
the onClick behavior), and ensure the visible label span remains associated
(e.g., keep it adjacent) so screen readers announce the control correctly; also
preserve existing visual state logic using isFormValid/isRemembered when
applying classes.
- 피드백 남기기, 인스타/쓰레드 아이콘 제거
🚀 feat: OG Image 설정 및 모바일 UI/반응형 개선
📝 변경사항
OG (Open Graph) Image 설정
링크 공유 로직 개선
모바일 모달 UI 수정 (NudgeModal, ShareModal)
메인 페이지 타이포그래피 개선
데스크탑, 태블릿, 모바일 해상도별로 자연스러운 줄바꿈(
)이 적용되도록 수정했습니다.
지도 컴포넌트 사이즈 조절
✅ 체크리스트
📸 스크린샷
💬 리뷰어 전달사항
Summary by CodeRabbit
릴리스 노트
새로운 기능
UI/UX 개선
기타