Skip to content

refactor: 버스 정류장 아이콘 정상화#27

Merged
KwonDeaGeun merged 3 commits intomainfrom
refactor/busstop-redesign
Nov 5, 2025
Merged

refactor: 버스 정류장 아이콘 정상화#27
KwonDeaGeun merged 3 commits intomainfrom
refactor/busstop-redesign

Conversation

@KwonDeaGeun
Copy link
Owner

@KwonDeaGeun KwonDeaGeun commented Nov 5, 2025

Summary by CodeRabbit

  • 새로운 기능

    • 선택된 정류소 강조(지도 아이콘에 빨간 원)
    • 정류소별 실시간 버스 수 표시 및 이를 기반한 동적 버튼 렌더링
  • UI 변경

    • 정류소 목록 레이아웃 3열 → 2열
    • 지도 아이콘 크기/스타일 변경 및 버스 아이콘 선 굵기 조정
  • 데이터 업데이트

    • 정류소 2건 삭제(종합실험동, 상경관)

@KwonDeaGeun KwonDeaGeun self-assigned this Nov 5, 2025
@vercel
Copy link

vercel bot commented Nov 5, 2025

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

Project Deployment Preview Comments Updated (UTC)
what-the-bus-web Ready Ready Preview Comment Nov 5, 2025 8:54am

@coderabbitai
Copy link

coderabbitai bot commented Nov 5, 2025

Walkthrough

선택된 정류소 이름(selectedStopName)과 버스 수(busCount)를 앱 전반에 전파하고, 지도 오버레이 SVG에 선택 상태(반투명 빨간 원 및 색상 변경)를 추가하며 정류소 목록과 버튼 렌더링을 동적화하고 일부 정류소 항목을 삭제했습니다.

Changes

Cohort / File(s) 변경 요약
앱 엔트리 및 props 전파
src/App.tsx
selectedStopName={bubbleStop?.name}busCount={buses.length} 전달 추가, 일부 포맷팅 정리
지도 오버레이 및 훅
src/components/MapContainer.tsx, src/hooks/useMapOverlays.ts, src/utils/mapOverlays.ts
MapContainerselectedStopName?: string 추가; useMapOverlays 시그니처/의존성에 selectedStopName 포함; createBusStopOverlayscreateIconSVG 시그니처 확장; 선택된 정류소에 대해 반투명 빨간 원과 핀 색상 변경 렌더링 추가; 아이콘 캔버스 크기/뷰박스 조정
정류소 패널 및 리스트
src/components/BusStopsPanel.tsx, src/components/BusStops.tsx
BusStopsPanelbusCount: number prop 추가 및 하위 BusStops로 전달; BusStopsbusCount?: number 도입(기본 0) 및 busCount 기반으로 번호 버튼 동적 생성; 헤더 아이콘 SVG 교체 및 레이아웃 조정; 정류소 목록을 busStops로 매핑
데이터 변경
src/data/busStops.ts
busStops 배열에서 "종합실험동""상경관" 항목 제거
로직/포맷 정리
src/hooks/useBusSelection.ts, src/lib/api.ts
방향 라벨 구성 로직 재포매팅(동작 동일) 및 API 헤더 스프레드 포맷 정리(동작 동일)

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant MapContainer
    participant BusStopsPanel
    participant BusStops
    participant useMapOverlays
    participant mapOverlays
    participant Map

    App->>MapContainer: selectedStopName
    App->>BusStopsPanel: busCount, selectedStopName
    BusStopsPanel->>BusStops: busCount
    MapContainer->>useMapOverlays: map, busStops, buses, selectedStopName
    useMapOverlays->>mapOverlays: createBusStopOverlays(map, busStops, selectedStopName)
    mapOverlays->>mapOverlays: for each stop => isSelected = stop.name === selectedStopName
    alt isSelected
        mapOverlays->>mapOverlays: createIconSVG("mapPin", true)  -- render red circle + red pin
    else not selected
        mapOverlays->>mapOverlays: createIconSVG("mapPin", false) -- render blue pin
    end
    mapOverlays->>Map: addOverlay(icon)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • 주의 필요 영역:
    • src/utils/mapOverlays.ts의 아이콘 크기/뷰박스 변경이 오버레이 위치 및 정렬에 미치는 영향 확인
    • useMapOverlaysselectedStopName을 의존성으로 추가하면서 useEffect 재실행과 오버레이 정리(cleanup)가 올바른지 검증
    • BusStopsbusCount 기반 동적 버튼 렌더링과 레이아웃(열 구성) 확인

Possibly related PRs

Poem

🐰 작은 토끼가 전하네,
빨간 원이 반짝이면 길이 빛나네,
버튼 수가 늘어나 춤을 추고,
정류소는 깔끔히 정리되고,
지도가 웃으며 안내하네 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경사항과 직접적으로 관련되어 있습니다. 변경사항은 버스 정류장 아이콘 렌더링 개선(선택된 정류장 강조 표시, SVG 아이콘 변경, 원형 하이라이트 추가)과 관련된 리팩토링이며, 제목이 이를 명확하게 요약합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/busstop-redesign

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

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

🧹 Nitpick comments (1)
src/components/BusStops.tsx (1)

10-11: selectedStopName prop이 사용되지 않습니다.

selectedStopName prop이 정의되었지만 컴포넌트 내에서 사용되지 않습니다. 향후 사용 예정이거나 필요 없다면 제거를 고려하세요.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2faa06 and e110acd.

📒 Files selected for processing (9)
  • src/App.tsx (4 hunks)
  • src/components/BusStops.tsx (7 hunks)
  • src/components/BusStopsPanel.tsx (2 hunks)
  • src/components/MapContainer.tsx (1 hunks)
  • src/data/busStops.ts (0 hunks)
  • src/hooks/useBusSelection.ts (1 hunks)
  • src/hooks/useMapOverlays.ts (2 hunks)
  • src/lib/api.ts (1 hunks)
  • src/utils/mapOverlays.ts (4 hunks)
💤 Files with no reviewable changes (1)
  • src/data/busStops.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: KwonDeaGeun
Repo: KwonDeaGeun/WhatTheBus-Web PR: 21
File: src/App.tsx:16-23
Timestamp: 2025-09-22T04:45:54.455Z
Learning: KwonDeaGeun은 TypeScript에서 any 타입 사용을 피하고 더 타입 안전한 코드를 선호한다.
🧬 Code graph analysis (4)
src/components/MapContainer.tsx (3)
src/api/bus.ts (1)
  • useBusLocations (6-16)
src/hooks/useMapOverlays.ts (1)
  • useMapOverlays (10-34)
src/data/busStops.ts (1)
  • busStops (7-12)
src/hooks/useMapOverlays.ts (1)
src/utils/mapOverlays.ts (2)
  • OverlayHandle (4-6)
  • createBusStopOverlays (129-161)
src/utils/mapOverlays.ts (1)
src/data/busStops.ts (2)
  • busStops (7-12)
  • BusStop (1-5)
src/App.tsx (1)
src/components/MapContainer.tsx (1)
  • MapContainer (15-32)
🔇 Additional comments (21)
src/lib/api.ts (1)

8-10: LGTM!

조건부 헤더 스프레드를 여러 줄로 분리하여 가독성이 향상되었습니다. 로직 변경은 없으며 타입 안전합니다.

src/hooks/useBusSelection.ts (1)

18-26: LGTM!

중첩된 삼항 연산자를 명확한 다중 조건문으로 리팩토링하여 가독성이 크게 향상되었습니다. 의미론적으로 동일하며 타입 안전합니다.

src/App.tsx (3)

76-78: LGTM!

ReactQueryDevtools 조건부 렌더링이 명확하게 구성되었습니다.


91-95: LGTM!

setBubbleStop 타입 주석이 명확하고 타입 안전합니다.


135-148: LGTM!

MapContainerBusStopsPanel에 새로운 props(selectedStopName, busCount)가 올바르게 연결되었습니다. bubbleStop?.namebuses.length의 전파가 적절합니다.

src/components/MapContainer.tsx (2)

12-12: LGTM!

selectedStopName optional prop이 올바르게 정의되었습니다.


15-24: LGTM!

selectedStopName이 컴포넌트 시그니처에 추가되고 useMapOverlays로 올바르게 전달되었습니다.

src/hooks/useMapOverlays.ts (2)

10-24: LGTM!

selectedStopName 파라미터가 올바르게 추가되고 createBusStopOverlays로 전달되었습니다. 타입 안전한 구현입니다.


33-33: LGTM!

의존성 배열에 selectedStopName이 올바르게 추가되어 선택된 정류장이 변경될 때 effect가 재실행됩니다.

src/components/BusStopsPanel.tsx (2)

9-17: LGTM!

busCountselectedStopName props가 올바르게 추가되고 destructure되었습니다. 타입 정의가 명확합니다.


35-36: LGTM!

새로운 props가 BusStops 컴포넌트로 올바르게 전달되었습니다.

src/utils/mapOverlays.ts (4)

9-19: LGTM!

createIconSVGshowCircle 파라미터가 추가되어 선택된 정류장의 시각적 표현이 가능해졌습니다. SVG 크기와 viewBox가 원을 수용하도록 증가되었고, 선택 상태에 따라 색상이 조건부로 적용됩니다.


23-35: LGTM!

선택된 정류장에 대한 반투명 빨간 배경 원이 올바르게 구현되었습니다. showCircle이 true일 때만 렌더링됩니다.


37-57: LGTM!

MapPin 아이콘의 fill 색상이 선택 상태에 따라 올바르게 조건부 처리되었습니다 (선택됨: 빨강, 기본: 파랑).


129-160: LGTM!

createBusStopOverlaysselectedStopName을 받아 각 정류장의 선택 상태를 계산하고 아이콘에 반영합니다. 문자열 비교가 명확하고 타입 안전합니다.

src/components/BusStops.tsx (6)

19-19: LGTM!

busCount의 기본값 0이 안전하게 설정되었습니다.


79-97: LGTM!

정류장 아이콘 SVG가 명시적인 strokeWidth로 업데이트되어 일관성이 향상되었습니다.


117-117: LGTM!

그리드 레이아웃이 3열에서 2열로 변경되어 UI 디자인이 개선되었습니다.


171-174: LGTM!

버스 아이콘에 명시적인 strokeWidth={2.5}가 설정되어 일관성이 향상되었습니다.


200-214: LGTM!

버스 번호 버튼이 busCount 기반으로 동적 렌더링되도록 올바르게 리팩토링되었습니다. Array.from 사용이 적절합니다.


123-149: 데이터 일관성 검증 완료 - 모든 정류장 일치

검증 결과, busStops 데이터와 BusStops.tsx의 하드코딩된 목록이 완전히 일치합니다:

  • busStops 데이터: 평화의광장, 치과병원, 정문, 죽전역 (4개)
  • 하드코딩된 목록: 죽전역, 치과병원, 정문, 평화의광장 (4개)

모든 정류장이 양쪽에 존재하며 누락되거나 초과된 항목이 없습니다. 코드가 busStops.find((s) => s.name === stop.name)을 사용하여 이름 기반으로 조회하므로 순서 차이는 문제가 되지 않습니다.

Copy link

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/BusStops.tsx (1)

122-148: 하드코딩된 정류장 이름이 busStops 프롭과 불일치할 수 있습니다.

정류장 이름을 하드코딩하고 있지만, 실제 데이터는 busStops 프롭에서 find로 찾고 있습니다(134-136줄). 만약 busStops에 해당 이름의 정류장이 없으면 버튼 클릭 시 아무 동작도 하지 않습니다. 이는 데이터 불일치 시 조용히 실패하는 문제를 일으킬 수 있습니다.

다음 중 하나의 방식으로 개선하는 것을 권장합니다:

방법 1: busStops 프롭을 직접 렌더링 (권장)

-{[
-    { name: "죽전역" },
-    { name: "치과병원" },
-    { name: "정문" },
-    { name: "평화의광장" },
-].map((stop) => (
+{busStops.map((stop) => (
    <button
        key={stop.name}
        type="button"
        disabled={disabled}
        onClick={() =>
            handleClick(() => {
-                const realStop = busStops.find(
-                    (s) => s.name === stop.name
-                );
-                if (realStop) {
-                    onSelect(realStop);
-                    if (onToggleBubble)
-                        onToggleBubble(realStop);
-                }
+                onSelect(stop);
+                if (onToggleBubble) onToggleBubble(stop);
            })
        }
        className="..."
    >
        {stop.name}
    </button>
))}

방법 2: 하드코딩 유지가 필요하다면 찾지 못한 경우 처리

 onClick={() =>
     handleClick(() => {
         const realStop = busStops.find(
             (s) => s.name === stop.name
         );
         if (realStop) {
             onSelect(realStop);
             if (onToggleBubble)
                 onToggleBubble(realStop);
+        } else {
+            console.warn(`정류장 "${stop.name}"을 찾을 수 없습니다.`);
         }
     })
 }
🧹 Nitpick comments (1)
src/components/BusStops.tsx (1)

78-96: 정류장 아이콘이 인라인 SVG로 교체되었습니다.

접근성 속성들(role, aria-label, title)이 잘 적용되어 있고, 아이콘 정상화 목적에 맞게 커스텀 SVG를 사용하고 있습니다.

향후 재사용성과 유지보수를 위해 별도 컴포넌트로 추출하는 것을 고려해보세요:

// 별도 파일 또는 같은 파일 내
const BusStopIcon = () => (
  <svg
    width="32"
    height="32"
    viewBox="0 0 24 24"
    fill="none"
    stroke="#2563eb"
    strokeWidth="2.5"
    strokeLinecap="round"
    strokeLinejoin="round"
    role="img"
    aria-label="정류장 아이콘"
  >
    <title>정류장 아이콘</title>
    <path
      d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"
      fill="#2563eb"
    />
    <circle cx="12" cy="10" r="3" fill="white" />
  </svg>
);

그리고 사용처에서:

-<svg
-  width="32"
-  ...
-</svg>
+<BusStopIcon />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e110acd and af83442.

📒 Files selected for processing (3)
  • src/App.tsx (4 hunks)
  • src/components/BusStops.tsx (7 hunks)
  • src/components/BusStopsPanel.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/BusStopsPanel.tsx
  • src/App.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: KwonDeaGeun
Repo: KwonDeaGeun/WhatTheBus-Web PR: 21
File: src/App.tsx:16-23
Timestamp: 2025-09-22T04:45:54.455Z
Learning: KwonDeaGeun은 TypeScript에서 any 타입 사용을 피하고 더 타입 안전한 코드를 선호한다.
🔇 Additional comments (4)
src/components/BusStops.tsx (4)

10-10: busCount 프롭 추가가 타입 안전하게 구현되었습니다.

타입이 명확하고 기본값 처리가 적절합니다. 다만 busCount가 0일 때 버스 번호 선택 영역이 비어있게 되는데, 이 경우 사용자에게 안내 메시지를 보여주는 것이 좋을지 확인해보세요.

Also applies to: 18-18


116-116: 그리드 레이아웃이 3열에서 2열로 변경되었습니다.

4개의 정류장을 2x2로 배치하는 것이 더 균형잡힌 레이아웃입니다. 다만 모바일 화면에서도 2열 레이아웃이 적절한지 확인해보세요.


170-173: Bus 아이콘에 명시적인 strokeWidth가 추가되었습니다.

위의 정류장 SVG 아이콘과 동일한 strokeWidth={2.5}를 사용하여 시각적 일관성을 유지하고 있습니다.


199-213: 버스 번호 렌더링이 동적으로 개선되었습니다.

하드코딩된 [1, 2, 3, 4, 5] 대신 busCount 프롭을 기반으로 동적으로 버튼을 생성하도록 변경되어, 더 유연하고 확장 가능한 구조가 되었습니다.

Copy link

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

🧹 Nitpick comments (1)
src/components/BusStops.tsx (1)

78-96: 커스텀 SVG 아이콘 구현이 훌륭합니다!

정류장 아이콘을 커스텀 인라인 SVG로 교체한 것은 좋은 선택입니다. 접근성 속성(role, aria-label, title)이 적절하게 구현되어 있고, 시각적 일관성도 잘 유지되고 있습니다.

참고: SVG의 색상(#2563eb)이 하드코딩되어 있는데, 향후 다크모드를 지원하거나 테마를 변경할 경우 Tailwind의 currentColor나 CSS 변수를 사용하는 것을 고려해볼 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af83442 and 06fa80b.

📒 Files selected for processing (1)
  • src/components/BusStops.tsx (6 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: KwonDeaGeun
Repo: KwonDeaGeun/WhatTheBus-Web PR: 21
File: src/App.tsx:16-23
Timestamp: 2025-09-22T04:45:54.455Z
Learning: KwonDeaGeun은 TypeScript에서 any 타입 사용을 피하고 더 타입 안전한 코드를 선호한다.
🧬 Code graph analysis (1)
src/components/BusStops.tsx (1)
src/data/busStops.ts (1)
  • busStops (7-12)
🔇 Additional comments (5)
src/components/BusStops.tsx (5)

116-116: LGTM!

정류장이 4개인 경우 grid-cols-2로 변경하여 2x2의 균형잡힌 레이아웃을 만든 것은 좋은 개선입니다.


122-137: 동적 렌더링으로의 전환이 우수합니다!

하드코딩된 정류장 목록을 busStops.map()을 사용한 동적 렌더링으로 변경한 것은 확장성과 유지보수성을 크게 향상시킵니다. 각 버튼에 올바른 key prop이 설정되어 있고, 이벤트 핸들러와 접근성 속성도 잘 유지되고 있습니다.


159-162: 시각적 일관성 개선!

Bus 아이콘에 strokeWidth={2.5}를 명시적으로 추가하여 커스텀 정류장 SVG 아이콘과 시각적 일관성을 맞춘 것은 좋은 디테일입니다.


188-202: 동적 버스 번호 렌더링 구현이 깔끔합니다!

Array.from({ length: busCount })을 사용한 동적 버튼 생성은 깔끔하고 유연한 접근 방식입니다. 코드가 타입 안전하고 React 모범 사례를 잘 따르고 있습니다.

참고: busCount가 0일 때의 UX는 Lines 10, 18에 대한 별도 코멘트를 참조해주세요.


10-10: 리뷰 코멘트가 부분적으로 부정확합니다.

busCountApp.tsxbuses.length로부터 항상 BusStopsPanel을 통해 전달되므로, 기본값 0에 의한 빈 버스 선택 섹션 문제는 현재 코드에서 발생하지 않습니다.

  • BusStopsPanel에서 busCount는 필수 prop이며 항상 값을 전달
  • BusStops에서 기본값 0은 컴포넌트를 독립적으로 사용할 때를 위한 방어적 기본값으로 적절함
  • 현재 코드 구조에서는 busCount = 0이 실제로 적용되는 상황이 없음

@KwonDeaGeun KwonDeaGeun merged commit f3036dc into main Nov 5, 2025
4 checks passed
@KwonDeaGeun KwonDeaGeun deleted the refactor/busstop-redesign branch November 5, 2025 09:12
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.

1 participant