Skip to content

Feature/detail upgrade#9

Merged
kangdy25 merged 7 commits into
mainfrom
feature/detail-upgrade
Jan 28, 2026
Merged

Feature/detail upgrade#9
kangdy25 merged 7 commits into
mainfrom
feature/detail-upgrade

Conversation

@kangdy25

@kangdy25 kangdy25 commented Jan 27, 2026

Copy link
Copy Markdown
Collaborator

🚀 feat: 투표 현황 페이지 기능 추가 및 폴더 구조 수정

📝 변경사항

  • 폴더 구조 정리 (icon, create/page.tsx)
  • 투표 현황 페이지 기능 추가 (마커 디자인 수정, 줌인/아웃, 출발지 입력 기능)

✅ 체크리스트

  • 코드 리뷰를 받았습니다
  • 테스트를 완료했습니다
  • 린터 에러가 없습니다
  • 타입 에러가 없습니다
  • 브라우저에서 테스트를 완료했습니다
  • 모바일에서 테스트를 완료했습니다 (해당되는 경우)

📸 스크린샷

UI 변경 사항이 있다면 이미지를 드래그해서 넣어주세요!

💬 리뷰어 전달사항

  • 이 브랜치가 메인에 머지되고 나면 메인을 기점으로 마지막 랜딩페이지 작업 진행주시면 될 거 같아요~

Summary by CodeRabbit

  • 새로운 기능

    • 미팅 생성 폼 추가: 이름·유형·장소·참가자 수·기한 설정 및 제출 흐름
    • 결과 페이지 추가
  • 개선사항

    • 지도: 다중 참가자 표시, 오버레이 우선순위 및 확대/축소 컨트롤 추가
    • 지하철 노선 시각화 추가: 폴리라인·중간역 표시·출발 레이블
    • 역 검색 개선: 역 객체 기반 검색 및 한글 초성 지원
    • 참가자 색상 자동 부여 및 모의 데이터 추가
  • 기타

    • 홈 페이지 구성 단순화

✏️ Tip: You can customize this high-level summary in your review settings.

- Mock 데이터 분리
- 지도 관련 편의성 기능 추가
- 출발지 입력 시, 화면 동적 렌더링
- 마커 색 랜덤 구현
- 지도 줌인/줌아웃 버튼 디자인 수정
- 결과보기 버튼 추가
- 결과보기 버튼 추가 시, 최종 결과 페이지로 이동 구현
- create/page.tsx의 isValidForm 조건 수정
- app/page.tsx를 app/create/page.tsx로 수정
- public 하위의 아이콘들을 public/icon 하위로 이동
- 페이지 컴포넌트 함수명을 모두 Page로 수정
@coderabbitai

coderabbitai Bot commented Jan 27, 2026

Copy link
Copy Markdown

Walkthrough

회의 생성/참가/회의 보기/공유 페이지 추가 및 이름 표준화, Kakao 지도 컴포넌트 다중 참가자 마커·라인 렌더링 추가, 색상 유틸·모의 데이터 도입, 역 검색에 Station 객체 사용으로 변경됨.

Changes

코호트 / 파일(들) 변경 요약
새 페이지 추가
app/create/page.tsx, app/result/page.tsx
app/create에 클라이언트 회의 생성 폼 페이지 추가(회의명, 유형, 장소 선택, 참가수, 마감일 등); app/result에 정적 Result 페이지 추가.
페이지 컴포넌트명 표준화
app/join/page.tsx, app/meeting/page.tsx, app/share/page.tsx
기본 내보내기 컴포넌트명을 Page로 통일(기존 JoinMeetingPage, MeetingPage, ShareLinkPage → Page).
홈페이지 단순화
app/page.tsx
홈 페이지를 대형 인터랙티브 컴포넌트에서 단순 정적 div로 축소(상태·핸들러 제거).
회의 페이지 데이터·네비게이션 변경
app/meeting/page.tsx
MOCK_PARTICIPANTS, MOCK_SEARCH_STATIONS 사용으로 참가자·역 데이터 주입; 선택된 역 기준 myParticipant 병합; KakaoMap에 참가자 배열 전달; /result로 라우팅 핸들러 추가.
지도 컴포넌트 확장(마커)
components/map/kakaoMap.tsx
Participant 타입 도입, participants prop 추가; 다중 참가자 CustomOverlay 마커 생성·zIndex 제어, bounds 계산, 줌 컨트롤 추가.
지도 컴포넌트 추가(노선 라인)
components/map/kakaoMapLine.tsx
지하철 노선 폴리라인·중간역 마커·시작역 오버레이 렌더링하는 새 KakaoMapLine 컴포넌트 추가.
역 검색 컴포넌트 개선
components/meeting/stationSearch.tsx
Station 인터페이스(name, lat, lng) 추가; stations prop을 string[]Station[]으로 변경; 필터·선택 로직을 Station 객체 기반으로 수정.
색상 유틸 추가
lib/color.ts
hsvToHex 및 시드 기반 getRandomHexColor(seed) 함수 추가(헥스 컬러 생성).
모의 데이터 추가
mock/mockData.ts
MOCK_PARTICIPANTS(hexColor 포함) 및 MOCK_SEARCH_STATIONS 추가; getRandomHexColor로 참가자 색상 생성.

Sequence Diagram(s)

(생성 조건 미충족 — 생략)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • kim3360
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive PR 제목이 "Feature/detail upgrade"는 매우 일반적이고 변경사항의 구체적인 내용을 전달하지 못합니다. 투표 현황 페이지 추가, 폴더 구조 정리 등 주요 변경사항을 반영한 더 구체적인 제목으로 변경해주세요.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed PR 설명에서 체크리스트는 모두 완료되었으나, 폴더 구조 정리와 투표 현황 페이지 기능이 구체적으로 설명되어 있고 스크린샷은 제공되지 않았습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@kangdy25 kangdy25 requested a review from kim3360 January 27, 2026 14:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 또는 별도의 데이터 파일로 분리하는 것을 고려해 주세요.

Comment thread app/create/page.tsx
Comment on lines +228 to +241
<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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

체크박스 아이콘이 상태와 무관하게 항상 보입니다.
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.

Comment on lines 25 to +75
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]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment thread lib/color.ts Outdated
Comment on lines +66 to +68
// 해시값에 137(겹치지 않게 하는 숫자)을 곱해서 각도를 크게 벌립니다.
const h = Math.abs((hash * 50) % 360);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

주석과 코드 상수가 불일치합니다.
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.

Suggested change
// 해시값에 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.

Comment thread mock/mockData.ts Outdated
- whitecheck는 항상 렌더링되게 변경
- 주석 수정, 좌표 수정
@kangdy25

Copy link
Copy Markdown
Collaborator Author

kakaoMap 관련코드는 ui 작업하면서 아직 더 수정될 필요가 있어서 지금 당장 수정하지는 않았습니다

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Comment thread app/create/page.tsx
<span
className={`text-[12px] leading-[1.334] tracking-[0.3024px] ${isParticipantUndecided ? 'text-blue-5' : 'text-gray-6'}`}
>
아직 안정해졌어요.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

오타: 띄어쓰기 수정 필요

"안정해졌어요"는 "안 정해졌어요" 또는 "정해지지 않았어요"로 수정이 필요합니다.

🐛 수정 제안
-                아직 안정해졌어요.
+                아직 안 정해졌어요.
📝 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.

Suggested change
아직 안정해졌어요.
아직 정해졌어요.
🤖 Prompt for AI Agents
In `@app/create/page.tsx` at line 240, 문장 오타 수정: 텍스트 "안정해졌어요."를 찾아 사용자 표시 문구를
자연스러운 표현으로 바꿔주세요 — 예: "안 정해졌어요." 또는 더 자연스러운 "정해지지 않았어요."로 교체합니다 (대상 문자열이 있는
컴포넌트/파일: page.tsx의 해당 렌더 문자열).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants