Feature/detail upgrade#9
Conversation
- Mock 데이터 분리 - 지도 관련 편의성 기능 추가
- 출발지 입력 시, 화면 동적 렌더링 - 마커 색 랜덤 구현
- 지도 줌인/줌아웃 버튼 디자인 수정 - 결과보기 버튼 추가 - 결과보기 버튼 추가 시, 최종 결과 페이지로 이동 구현
- create/page.tsx의 isValidForm 조건 수정 - app/page.tsx를 app/create/page.tsx로 수정 - public 하위의 아이콘들을 public/icon 하위로 이동 - 페이지 컴포넌트 함수명을 모두 Page로 수정
Walkthrough회의 생성/참가/회의 보기/공유 페이지 추가 및 이름 표준화, Kakao 지도 컴포넌트 다중 참가자 마커·라인 렌더링 추가, 색상 유틸·모의 데이터 도입, 역 검색에 Station 객체 사용으로 변경됨. Changes
Sequence Diagram(s)(생성 조건 미충족 — 생략) 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
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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/meeting/page.tsx (1)
140-142: 하드코딩된 사용자 이름 확인 필요
'안가연'이 모든 참가자에게 동일하게 표시됩니다. 이것이 의도된 것인지, 아니면user.name또는 다른 필드를 사용해야 하는지 확인해 주세요.
🤖 Fix all issues with AI agents
In `@app/create/page.tsx`:
- Around line 228-241: The checkbox button currently always renders the same
check icon because both branches render <Image src="/icon/whitecheck.svg">;
update the conditional rendering around isParticipantUndecided in the button
(and the identical other occurrence) so that the check icon is only rendered
when isParticipantUndecided is true (or render an alternate unselected icon when
false), by modifying the JSX inside the button that uses
handleParticipantCheckbox and isParticipantUndecided to conditionally return the
icon only for the selected state.
In `@components/map/kakaoMap.tsx`:
- Around line 25-75: The useEffect that creates kakao.maps.CustomOverlay
instances for each participant leaks overlays because no cleanup removes them
when participants change; modify the effect to track created overlays (e.g., an
array of overlays created inside the window.kakao.maps.load callback) and return
a cleanup function from useEffect that iterates those overlays and calls
overlay.setMap(null) (or the appropriate removal API) and clears references;
ensure you also clear any bounds or listener state and keep references to
kakaoMap and overlays scoped so the cleanup can access and remove them before
creating new overlays in the next effect run.
In `@lib/color.ts`:
- Around line 66-68: The comment in lib/color.ts says 137 but the code
multiplies hash by 50 for hue calculation (const h = Math.abs((hash * 50) %
360)); make them consistent: either update the comment to mention 50 (e.g.,
"multiply by 50 to spread angles") or change the constant in the expression to
137 if 137 was intended (update const h calculation accordingly). Edit the line
with const h and the preceding comment together so the comment and the constant
match, referencing the h computation in lib/color.ts.
In `@mock/mockData.ts`:
- Line 12: The mock data has inconsistent longitude for 용산역 between
RAW_PARTICIPANTS (entry id: 8, lng: 126.964803) and MOCK_SEARCH_STATIONS (lng:
126.964561); pick the canonical coordinate and update the other to match so both
data sources use the same lng for '용산역' (edit RAW_PARTICIPANTS or
MOCK_SEARCH_STATIONS accordingly), ensuring the station name '용산역' and its
lat/lng pair in both RAW_PARTICIPANTS and MOCK_SEARCH_STATIONS are identical.
🧹 Nitpick comments (5)
app/page.tsx (1)
1-5: 컴포넌트 이름을Page로 맞추는 것을 권장합니다.
다른 라우트들과 동일한 네이밍 컨벤션을 유지하면 추적과 디버깅이 쉬워집니다.♻️ 제안 변경
-const page = () => { +const Page = () => { return <div>page</div>; }; -export default page; +export default Page;components/map/kakaoMap.tsx (1)
23-23:any타입 사용 지양Static analysis에서 지적된 대로
any타입 대신 구체적인 타입을 사용하세요. Kakao Maps SDK의 타입이 없다면 최소한unknown을 사용하거나 간단한 인터페이스를 정의하는 것이 좋습니다.♻️ 타입 개선 제안
+// Kakao Map 인스턴스 타입 (필요한 메서드만 정의) +interface KakaoMapInstance { + setLevel: (level: number, options?: { animate: boolean }) => void; + getLevel: () => number; + setBounds: (bounds: unknown) => void; +} + export default function KakaoMap({ className, participants = [] }: KakaoMapProps) { const mapContainer = useRef<HTMLDivElement>(null); - const [map, setMap] = useState<any>(null); + const [map, setMap] = useState<KakaoMapInstance | null>(null);app/meeting/page.tsx (2)
39-47: 프로덕션 코드에서console.log제거 필요디버깅용
console.log가 남아있습니다. 프로덕션 배포 전에 제거하거나 적절한 로깅 유틸리티로 교체하세요.♻️ console.log 제거
const handleSubmit = () => { if (!selectedStation) { alert('출발지를 선택해주세요!'); return; } - console.log('결과 요청:', allParticipants); router.push('/result'); };
147-152: 버튼에type속성 추가 권장
<button>요소에type="button"을 명시적으로 추가하면 폼 내에서 의도치 않은 submit 동작을 방지할 수 있습니다.♻️ type 속성 추가
<button + type="button" onClick={handleSubmit} className="bg-gray-8 absolute right-5 bottom-0 left-5 h-12 rounded text-lg text-white md:right-0 md:left-0" > 결과보기 </button>components/map/kakaoMapLine.tsx (1)
9-68: 데이터를 별도 파일로 분리 고려
REAL_SUBWAY_PATHS데이터가 컴포넌트 파일 내에 하드코딩되어 있습니다. 유지보수성을 위해mock/mockData.ts또는 별도의 데이터 파일로 분리하는 것을 고려해 주세요.
| <button | ||
| type="button" | ||
| onClick={handleParticipantCheckbox} | ||
| className={`flex h-5 w-5 items-center justify-center rounded-[4px] transition-colors ${ | ||
| isParticipantUndecided ? 'bg-blue-5' : 'bg-gray-3' | ||
| }`} | ||
| > | ||
| {isParticipantUndecided && ( | ||
| <Image src="/icon/whitecheck.svg" alt="check" width={10} height={10} /> | ||
| )} | ||
| {!isParticipantUndecided && ( | ||
| <Image src="/icon/whitecheck.svg" alt="check" width={10} height={10} /> | ||
| )} | ||
| </button> |
There was a problem hiding this comment.
체크박스 아이콘이 상태와 무관하게 항상 보입니다.
Line 235–240과 Line 287–292에서 체크 아이콘이 양쪽 분기에 모두 렌더되어 항상 체크된 것처럼 보여요. 선택 상태에 따라 아이콘을 조건부로 렌더링(또는 미선택 아이콘 제공)해주세요.
🐛 한 가지 수정안 (미선택 시 아이콘 숨김)
- {isParticipantUndecided && (
- <Image src="/icon/whitecheck.svg" alt="check" width={10} height={10} />
- )}
- {!isParticipantUndecided && (
- <Image src="/icon/whitecheck.svg" alt="check" width={10} height={10} />
- )}
+ {isParticipantUndecided && (
+ <Image src="/icon/whitecheck.svg" alt="check" width={10} height={10} />
+ )}- {isDeadlineFlexible && (
- <Image src="/icon/whitecheck.svg" alt="check" width={10} height={10} />
- )}
- {!isDeadlineFlexible && (
- <Image src="/icon/whitecheck.svg" alt="check" width={10} height={10} />
- )}
+ {isDeadlineFlexible && (
+ <Image src="/icon/whitecheck.svg" alt="check" width={10} height={10} />
+ )}Also applies to: 280-292
🤖 Prompt for AI Agents
In `@app/create/page.tsx` around lines 228 - 241, The checkbox button currently
always renders the same check icon because both branches render <Image
src="/icon/whitecheck.svg">; update the conditional rendering around
isParticipantUndecided in the button (and the identical other occurrence) so
that the check icon is only rendered when isParticipantUndecided is true (or
render an alternate unselected icon when false), by modifying the JSX inside the
button that uses handleParticipantCheckbox and isParticipantUndecided to
conditionally return the icon only for the selected state.
| useEffect(() => { | ||
| // 1. 카카오 스크립트가 로드되지 않았으면 중단 | ||
| if (!window.kakao) return; | ||
|
|
||
| // 2. 카카오 맵 로드 (autoload=false라서 명시적으로 load 호출 필요) | ||
| window.kakao.maps.load(() => { | ||
| const container = mapContainer.current; | ||
| if (!container) return; | ||
|
|
||
| const options = { | ||
| center: new window.kakao.maps.LatLng(37.5563, 126.9224), // 홍대입구역 좌표 | ||
| level: 3, // 지도 확대 레벨 | ||
| center: new window.kakao.maps.LatLng(37.5563, 126.9224), | ||
| level: 8, | ||
| }; | ||
|
|
||
| // 지도 생성 | ||
| const map = new window.kakao.maps.Map(container, options); | ||
| const kakaoMap = new window.kakao.maps.Map(container, options); | ||
| setMap(kakaoMap); | ||
|
|
||
| const bounds = new window.kakao.maps.LatLngBounds(); | ||
|
|
||
| if (participants.length > 0) { | ||
| participants.forEach((person) => { | ||
| const position = new window.kakao.maps.LatLng(person.lat, person.lng); | ||
| bounds.extend(position); | ||
|
|
||
| // [2] '나'일 경우 z-index를 높여서 맨 위에 표시 | ||
| const isMe = person.id === 'me'; | ||
| const zIndex = isMe ? 2 : 1; | ||
|
|
||
| // (선택) 마커 추가 예시 | ||
| const markerPosition = new window.kakao.maps.LatLng(37.5563, 126.9224); | ||
| const marker = new window.kakao.maps.Marker({ position: markerPosition }); | ||
| marker.setMap(map); | ||
| const content = ` | ||
| <div class="relative flex flex-col items-center group" style="z-index: ${zIndex};"> | ||
| <div class="absolute bottom-9 mb-1 whitespace-nowrap rounded-md bg-gray-9 px-2 py-1 text-xs text-white opacity-0 transition-opacity group-hover:opacity-100"> | ||
| ${person.station} (${person.name}) | ||
| </div> | ||
| <div class="flex h-8 w-8 items-center justify-center rounded-full border border-white" | ||
| style="background-color: ${person.hexColor};"> | ||
| <span class="text-sm font-semibold text-white">${person.name}</span> | ||
| </div> | ||
| </div> | ||
| `; | ||
|
|
||
| new window.kakao.maps.CustomOverlay({ | ||
| position: position, | ||
| content: content, | ||
| yAnchor: 1, | ||
| zIndex: zIndex, // [3] 카카오 오버레이 옵션에도 zIndex 적용 | ||
| map: kakaoMap, | ||
| }); | ||
| }); | ||
|
|
||
| kakaoMap.setBounds(bounds); | ||
| } | ||
| }); | ||
| }, []); | ||
| }, [participants]); |
There was a problem hiding this comment.
CustomOverlay 정리(cleanup) 누락으로 인한 메모리 누수 가능성
participants가 변경될 때마다 새로운 오버레이가 생성되지만, 기존 오버레이를 제거하는 cleanup 로직이 없습니다. 이로 인해 오버레이가 중복 생성되거나 메모리 누수가 발생할 수 있습니다.
🔧 cleanup 로직 추가 제안
export default function KakaoMap({ className, participants = [] }: KakaoMapProps) {
const mapContainer = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<any>(null);
+ const overlaysRef = useRef<any[]>([]);
useEffect(() => {
if (!window.kakao) return;
window.kakao.maps.load(() => {
const container = mapContainer.current;
if (!container) return;
+ // 기존 오버레이 제거
+ overlaysRef.current.forEach((overlay) => overlay.setMap(null));
+ overlaysRef.current = [];
+
const options = {
center: new window.kakao.maps.LatLng(37.5563, 126.9224),
level: 8,
};
const kakaoMap = new window.kakao.maps.Map(container, options);
setMap(kakaoMap);
const bounds = new window.kakao.maps.LatLngBounds();
if (participants.length > 0) {
participants.forEach((person) => {
// ... position, content 생성 코드 ...
- new window.kakao.maps.CustomOverlay({
+ const overlay = new window.kakao.maps.CustomOverlay({
position: position,
content: content,
yAnchor: 1,
zIndex: zIndex,
map: kakaoMap,
});
+ overlaysRef.current.push(overlay);
});
kakaoMap.setBounds(bounds);
}
});
+
+ return () => {
+ overlaysRef.current.forEach((overlay) => overlay.setMap(null));
+ overlaysRef.current = [];
+ };
}, [participants]);🤖 Prompt for AI Agents
In `@components/map/kakaoMap.tsx` around lines 25 - 75, The useEffect that creates
kakao.maps.CustomOverlay instances for each participant leaks overlays because
no cleanup removes them when participants change; modify the effect to track
created overlays (e.g., an array of overlays created inside the
window.kakao.maps.load callback) and return a cleanup function from useEffect
that iterates those overlays and calls overlay.setMap(null) (or the appropriate
removal API) and clears references; ensure you also clear any bounds or listener
state and keep references to kakaoMap and overlays scoped so the cleanup can
access and remove them before creating new overlays in the next effect run.
| // 해시값에 137(겹치지 않게 하는 숫자)을 곱해서 각도를 크게 벌립니다. | ||
| const h = Math.abs((hash * 50) % 360); | ||
|
|
There was a problem hiding this comment.
주석과 코드 상수가 불일치합니다.
Line 66의 주석은 137을 언급하지만 Line 67은 50을 곱하고 있어요. 의도에 맞게 주석 또는 상수를 정합시켜 주세요.
📝 주석 정합화 예시
- // 해시값에 137(겹치지 않게 하는 숫자)을 곱해서 각도를 크게 벌립니다.
+ // 해시값에 50을 곱해 각도를 크게 벌립니다.📝 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.
| // 해시값에 137(겹치지 않게 하는 숫자)을 곱해서 각도를 크게 벌립니다. | |
| const h = Math.abs((hash * 50) % 360); | |
| // 해시값에 50을 곱해 각도를 크게 벌립니다. | |
| const h = Math.abs((hash * 50) % 360); | |
🤖 Prompt for AI Agents
In `@lib/color.ts` around lines 66 - 68, The comment in lib/color.ts says 137 but
the code multiplies hash by 50 for hue calculation (const h = Math.abs((hash *
50) % 360)); make them consistent: either update the comment to mention 50
(e.g., "multiply by 50 to spread angles") or change the constant in the
expression to 137 if 137 was intended (update const h calculation accordingly).
Edit the line with const h and the preceding comment together so the comment and
the constant match, referencing the h computation in lib/color.ts.
- whitecheck는 항상 렌더링되게 변경 - 주석 수정, 좌표 수정
|
kakaoMap 관련코드는 ui 작업하면서 아직 더 수정될 필요가 있어서 지금 당장 수정하지는 않았습니다 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@app/create/page.tsx`:
- Line 240: 문장 오타 수정: 텍스트 "안정해졌어요."를 찾아 사용자 표시 문구를 자연스러운 표현으로 바꿔주세요 — 예: "안
정해졌어요." 또는 더 자연스러운 "정해지지 않았어요."로 교체합니다 (대상 문자열이 있는 컴포넌트/파일: page.tsx의 해당 렌더
문자열).
🧹 Nitpick comments (1)
app/create/page.tsx (1)
228-236: 접근성 개선: 체크박스에 ARIA 속성 추가 권장체크박스 버튼들이 시각적으로는 체크박스처럼 동작하지만, 스크린 리더 사용자를 위한 접근성 속성이 없습니다.
role="checkbox"와aria-checked속성을 추가하면 접근성이 개선됩니다.♻️ 접근성 개선 예시 (참여 인원 체크박스)
<button type="button" onClick={handleParticipantCheckbox} + role="checkbox" + aria-checked={isParticipantUndecided} className={`flex h-5 w-5 items-center justify-center rounded-[4px] transition-colors ${ isParticipantUndecided ? 'bg-blue-5' : 'bg-gray-3' }`} >Also applies to: 277-285
| <span | ||
| className={`text-[12px] leading-[1.334] tracking-[0.3024px] ${isParticipantUndecided ? 'text-blue-5' : 'text-gray-6'}`} | ||
| > | ||
| 아직 안정해졌어요. |
There was a problem hiding this comment.
오타: 띄어쓰기 수정 필요
"안정해졌어요"는 "안 정해졌어요" 또는 "정해지지 않았어요"로 수정이 필요합니다.
🐛 수정 제안
- 아직 안정해졌어요.
+ 아직 안 정해졌어요.📝 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.
| 아직 안정해졌어요. | |
| 아직 안 정해졌어요. |
🤖 Prompt for AI Agents
In `@app/create/page.tsx` at line 240, 문장 오타 수정: 텍스트 "안정해졌어요."를 찾아 사용자 표시 문구를
자연스러운 표현으로 바꿔주세요 — 예: "안 정해졌어요." 또는 더 자연스러운 "정해지지 않았어요."로 교체합니다 (대상 문자열이 있는
컴포넌트/파일: page.tsx의 해당 렌더 문자열).
🚀 feat: 투표 현황 페이지 기능 추가 및 폴더 구조 수정
📝 변경사항
✅ 체크리스트
📸 스크린샷
💬 리뷰어 전달사항
Summary by CodeRabbit
새로운 기능
개선사항
기타
✏️ Tip: You can customize this high-level summary in your review settings.