[feature] 동아리 상세페이지에 동아리방 위치를 지도에 표시한다#1367
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
NaverMap 컴포넌트 및 스타일 frontend/src/components/map/NaverMap.tsx, frontend/src/components/map/NaverMap.styles.ts |
NaverMap React 컴포넌트와 styled-components 기반 MapContainer 추가; mapRef를 통해 렌더 영역 제공. |
NaverMap 초기화 및 스크립트 로드 frontend/src/components/map/useNaverMap.ts, frontend/src/components/map/loadNaverMapScript.ts |
Naver 지도 스크립트 동적 로드 기능 추가(중복 로드 검사 포함) 및 useNaverMap 훅에서 지도/마커 초기화 로직 구현. |
클럽 위치 데이터 frontend/src/constants/clubLocation.ts |
ClubLocation 타입 정의 및 다수 클럽 좌표/위치 정보를 담은 상수 배열 추가 (읽기전용). |
ClubDetailPage 통합 변경 frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx, frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts |
좌측 섹션 및 맵 카드 스타일 추가, clubLocations 조회 후 조건부로 NaverMap 렌더링하도록 페이지 구조 변경. |
프로필 카드 스타일 조정 frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts |
태블릿 브레이크포인트에서 background-color에 더해 padding 조정. |
전역 타입 및 인덱스 변경 frontend/src/types/window.d.ts, frontend/src/index.tsx |
Window 인터페이스에 naver 및 navermap_authFailure 추가; 개발 환경에서 인증 실패 핸들러 정의(콘솔 출력). |
Sequence Diagram
sequenceDiagram
participant User as User
participant ClubDetail as ClubDetailPage
participant NaverMap as NaverMap Component
participant Hook as useNaverMap Hook
participant Loader as loadNaverMapScript
participant API as Naver Maps API
User->>ClubDetail: 클럽 상세 페이지 방문
ClubDetail->>ClubDetail: clubLocations에서 위치 조회
ClubDetail->>NaverMap: 위치 props로 렌더
NaverMap->>Hook: mapRef, lat, lng 전달
Hook->>Loader: 스크립트 로드 요청
alt 스크립트 이미 존재
Loader-->>Hook: 즉시 반환
else 스크립트 미존재
Loader->>API: 외부 스크립트 요청
API-->>Loader: 스크립트 로드 완료
Loader-->>Hook: Promise 해결
end
Hook->>API: 지도 인스턴스 및 마커 생성
API-->>NaverMap: 지도/마커 렌더링 완료
NaverMap->>User: 지도 표시
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~35 minutes
Possibly related PRs
- [HotFix] 동아리 상세페이지 모집 버튼 푸터 삭제 복구 #978: ClubDetailPage와 관련된 변경을 포함하며, 본 PR의 ClubDetailPage 구조/지도 통합과 직접적으로 겹칠 가능성이 있음.
Suggested reviewers
- seongwon030
- oesnuj
- lepitaaar
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | 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. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | PR 제목이 변경 사항의 주요 목표를 명확하게 설명하고 있습니다. 네이버 지도 API를 동아리 상세페이지에 통합하여 동아리방 위치를 지도에 표시하는 기능 추가가 정확히 반영되어 있습니다. |
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
📝 Generate docstrings
- Create stacked PR
- Commit on current branch
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
feature/#1357-club-detail-map-location-MOA-767
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 @coderabbitai help to get the list of available commands and usage tips.
🎨 UI 변경사항을 확인해주세요
2개 스토리 변경 · 전체 56개 스토리 · 22개 컴포넌트 |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts (1)
171-173:⚠️ Potential issue | 🔴 Critical구문 오류:
IntroSection의 닫는 중괄호가 누락되었습니다.
IntroSectionstyled-component의${media.tablet}블록에 닫는}가 없어 빌드 오류가 발생합니다.🐛 수정 제안
export const IntroSection = styled.section` padding: 16px; border-radius: 14px; background-color: ${colors.base.white}; ${media.tablet} { background-color: ${colors.gray[100]}; + } `;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts` around lines 171 - 173, The IntroSection styled-component has a missing closing brace causing a syntax/build error; locate the IntroSection definition (the block containing ${media.tablet} and colors.gray[100]) and add the missing closing curly brace(s) to properly terminate the ${media.tablet} media query and the IntroSection styled-component so the CSS block parses correctly.
🧹 Nitpick comments (6)
frontend/src/constants/clubLocation.ts (1)
9-9: 상수 이름에 UPPER_SNAKE_CASE 사용을 고려해 주세요.코딩 가이드라인에 따르면 상수는
UPPER_SNAKE_CASE를 사용해야 합니다.clubLocations→CLUB_LOCATIONS로 변경하면 일관성이 향상됩니다.♻️ 상수 이름 변경 예시
-export const clubLocations = [ +export const CLUB_LOCATIONS = [ // ... ] as const;사용처도 함께 업데이트:
-import { clubLocations } from '@/constants/clubLocation'; +import { CLUB_LOCATIONS } from '@/constants/clubLocation'; -const location = clubLocations.find( +const location = CLUB_LOCATIONS.find(As per coding guidelines:
Use UPPER_SNAKE_CASE for constant names and centralize them in src/constants/🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/constants/clubLocation.ts` at line 9, Rename the exported constant clubLocations to CLUB_LOCATIONS and update its export accordingly; then update all usages/imports of clubLocations across the codebase to reference CLUB_LOCATIONS (search for the symbol "clubLocations" and replace with "CLUB_LOCATIONS") and ensure it remains exported from src/constants/clubLocation.ts so other modules import the new name.frontend/src/types/window.d.ts (1)
4-5:naver에any대신 최소한의 타입 정의를 고려해 주세요.코딩 가이드라인에서
any타입 사용을 지양하도록 권장하고 있습니다. Naver Maps SDK에 공식 TypeScript 타입이 없으므로, 프로젝트에서 사용하는 API에 대해 최소한의 인터페이스를 정의하면 타입 안전성이 향상됩니다.♻️ 최소 타입 정의 예시
interface NaverMaps { Map: new (element: HTMLElement, options: object) => object; LatLng: new (lat: number, lng: number) => object; Marker: new (options: object) => object; // 필요한 추가 API... } interface Naver { maps: NaverMaps; } declare global { interface Window { Kakao: any; naver: Naver | undefined; navermap_authFailure: () => void; } }As per coding guidelines:
frontend/**/*.{ts,tsx}: Do not use 'any' type; use explicit type definitions instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/types/window.d.ts` around lines 4 - 5, Replace the broad any on window.naver with a minimal typed interface: define NaverMaps and Naver interfaces that cover the SDK pieces you use (e.g., Map, LatLng, Marker constructors and any methods/options you call) and change the Window declaration so naver is typed as Naver | undefined; keep navermap_authFailure as a function type. Update the types in frontend/src/types/window.d.ts by adding the NaverMaps and Naver interface names and using them in the global Window declaration instead of any to satisfy the lint rule against any.frontend/src/index.tsx (1)
8-12: SDK 초기화 로직을initSDK.ts로 이동하는 것을 고려해 주세요.학습된 패턴에 따르면 SDK 초기화는
src/utils/initSDK.ts에서 관리됩니다.navermap_authFailure핸들러도 해당 파일로 이동하면 일관성이 향상됩니다.♻️ initSDK.ts로 이동 예시
frontend/src/utils/initSDK.ts에 추가:export function initializeNaverMapAuthHandler() { if (import.meta.env.DEV) { window.navermap_authFailure = function () { console.error('Naver Map Error 인증 실패'); }; } }
frontend/src/index.tsx에서 호출:-if (import.meta.env.DEV) { - window.navermap_authFailure = function () { - console.error('Naver Map Error 인증 실패'); - }; -} +import { initializeNaverMapAuthHandler } from './utils/initSDK'; +initializeNaverMapAuthHandler();Based on learnings:
Manage all SDK initialization (Mixpanel, Sentry, Channel.io, Kakao) in src/utils/initSDK.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/index.tsx` around lines 8 - 12, Move the dev-only Naver Map auth failure handler out of frontend/src/index.tsx into the centralized SDK init module: create or use src/utils/initSDK.ts and add a function (e.g., initializeNaverMapAuthHandler) that registers window.navermap_authFailure in the same DEV-only conditional, then import and call that function from index.tsx as part of the SDK initialization sequence (alongside other init functions like Mixpanel/Sentry/Channel.io/Kakao) so the handler and SDK bootstrapping are managed in one place.frontend/src/components/map/NaverMap.tsx (1)
5-13: 사용되지 않는 props를 제거하거나 활용해 주세요.
NaverMapProps인터페이스에clubName,building,detailLocation이 정의되어 있지만 컴포넌트에서 사용되지 않습니다. 현재 필요하지 않다면 제거하고, 향후 마커 레이블이나 InfoWindow에 사용할 예정이라면 해당 기능을 구현하거나 TODO 주석을 추가해 주세요.♻️ 사용하지 않는 props 제거
interface NaverMapProps { lat: number; lng: number; - clubName: string; - building: string; - detailLocation: string; } -const NaverMap = ({ lat, lng }: NaverMapProps) => { +const NaverMap = ({ lat, lng }: NaverMapProps) => { const mapRef = useRef<HTMLDivElement | null>(null); useNaverMap(mapRef, lat, lng); return <Styled.MapContainer ref={mapRef} />; };또는
ClubDetailPage.tsx에서 호출 시에도 해당 props를 제거해야 합니다:<NaverMap - clubName={location.clubName} lat={location.lat} lng={location.lng} - building={location.building} - detailLocation={location.detailLocation} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/map/NaverMap.tsx` around lines 5 - 13, The NaverMapProps interface declares clubName, building, and detailLocation but the NaverMap component signature and body only use lat and lng; either remove those unused props from NaverMapProps and from the NaverMap call sites (e.g., where ClubDetailPage calls NaverMap) or update NaverMap to accept and use clubName/building/detailLocation (for example to render a marker label or InfoWindow) and add a TODO comment if you prefer to defer UI work; update the interface, the NaverMap function parameters, and all places that construct NaverMap props (notably ClubDetailPage) to keep them in sync.frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts (1)
57-66: 하드코딩된 색상 대신 테마 색상 사용을 권장합니다.
background-color:#f2f2f2``가 하드코딩되어 있습니다. 일관성을 위해 테마 시스템의 색상을 사용하는 것이 좋습니다.border속성에서는 이미 `colors.gray[400]`을 사용하고 있습니다.♻️ 수정 제안
export const MapCard = styled.div` width: 100%; height: 189px; border-radius: 20px; border: 1px solid ${colors.gray[400]}; overflow: hidden; - background-color: `#f2f2f2`; + background-color: ${colors.gray[100]}; `;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts` around lines 57 - 66, Replace the hardcoded background-color in the MapCard styled component with a themed color; instead of `background-color: `#f2f2f2``, use the project's color token (e.g., `colors.gray[100]`) to match the existing `colors.gray[400]` usage so the component follows the theme system and remains consistent with other styles.frontend/src/components/map/useNaverMap.ts (1)
3-3: 내부 모듈 import는@/alias로 통일해 주세요.Line 3의 상대경로 import를 alias 경로로 맞추면 규칙 일관성과 리팩터링 안정성이 좋아집니다.
수정 제안
-import { loadNaverMapScript } from './loadNaverMapScript'; +import { loadNaverMapScript } from '@/components/map/loadNaverMapScript';As per coding guidelines,
Use path alias@/* to reference src/* for cleaner imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/map/useNaverMap.ts` at line 3, Replace the relative import of loadNaverMapScript in useNaverMap.ts with the project path-alias form (use "@/..." to reference the same module) so the import for loadNaverMapScript uses the `@/` alias consistently with the repo convention; update the import statement that currently references './loadNaverMapScript' to the equivalent '@/...' alias path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/map/loadNaverMapScript.ts`:
- Around line 2-3: Remove the debug console.log statements in
loadNaverMapScript.ts (the two lines logging import.meta.env and
VITE_NAVER_CLIENT_ID) to eliminate CI `Unexpected console statement` warnings
and noise; also correct the environment variable usage to the actual runtime key
(replace references to VITE_NAVER_CLIENT_ID with VITE_NAVER_MAP_CLIENT_ID where
the script reads the Naver map client id) so the loader uses the correct value.
- Around line 5-24: The loader currently resolves too early when an existing
script tag is present and never rejects on load failure; update
loadNaverMapScript so it never resolves simply because document.querySelector
found a script — instead attach load and error listeners to the existingScript
(listen for 'load' to resolve and 'error' to reject or fallback) and add both
onload and onerror handlers to the newly created script before appending it
(ensure you append the script to document.head/document.body); keep using
window.naver?.maps to short-circuit only if the SDK is already available, and
ensure the Promise always settles (resolve on load, reject or resolve with
failure handling on error) so callers won’t hang.
In `@frontend/src/constants/clubLocation.ts`:
- Around line 118-132: There are duplicate entries for clubName '모비딕'
(detailLocation '106호' and '214호') so only the first will be returned by any
Array.find lookup; either deduplicate the data by removing the unintended
duplicate or merge them into a single record (e.g., change detailLocation to an
array like detailLocations: ['106호','214호'] and adjust consumers), or if
multiple locations are intended keep both objects but update the lookup code to
use Array.filter (or a dedicated getLocationsByClubName function) instead of
Array.find; locate the objects with clubName '모비딕' in clubLocation.ts and apply
the chosen fix consistently across consumers that reference clubName.
---
Outside diff comments:
In
`@frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts`:
- Around line 171-173: The IntroSection styled-component has a missing closing
brace causing a syntax/build error; locate the IntroSection definition (the
block containing ${media.tablet} and colors.gray[100]) and add the missing
closing curly brace(s) to properly terminate the ${media.tablet} media query and
the IntroSection styled-component so the CSS block parses correctly.
---
Nitpick comments:
In `@frontend/src/components/map/NaverMap.tsx`:
- Around line 5-13: The NaverMapProps interface declares clubName, building, and
detailLocation but the NaverMap component signature and body only use lat and
lng; either remove those unused props from NaverMapProps and from the NaverMap
call sites (e.g., where ClubDetailPage calls NaverMap) or update NaverMap to
accept and use clubName/building/detailLocation (for example to render a marker
label or InfoWindow) and add a TODO comment if you prefer to defer UI work;
update the interface, the NaverMap function parameters, and all places that
construct NaverMap props (notably ClubDetailPage) to keep them in sync.
In `@frontend/src/components/map/useNaverMap.ts`:
- Line 3: Replace the relative import of loadNaverMapScript in useNaverMap.ts
with the project path-alias form (use "@/..." to reference the same module) so
the import for loadNaverMapScript uses the `@/` alias consistently with the repo
convention; update the import statement that currently references
'./loadNaverMapScript' to the equivalent '@/...' alias path.
In `@frontend/src/constants/clubLocation.ts`:
- Line 9: Rename the exported constant clubLocations to CLUB_LOCATIONS and
update its export accordingly; then update all usages/imports of clubLocations
across the codebase to reference CLUB_LOCATIONS (search for the symbol
"clubLocations" and replace with "CLUB_LOCATIONS") and ensure it remains
exported from src/constants/clubLocation.ts so other modules import the new
name.
In `@frontend/src/index.tsx`:
- Around line 8-12: Move the dev-only Naver Map auth failure handler out of
frontend/src/index.tsx into the centralized SDK init module: create or use
src/utils/initSDK.ts and add a function (e.g., initializeNaverMapAuthHandler)
that registers window.navermap_authFailure in the same DEV-only conditional,
then import and call that function from index.tsx as part of the SDK
initialization sequence (alongside other init functions like
Mixpanel/Sentry/Channel.io/Kakao) so the handler and SDK bootstrapping are
managed in one place.
In `@frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts`:
- Around line 57-66: Replace the hardcoded background-color in the MapCard
styled component with a themed color; instead of `background-color: `#f2f2f2``,
use the project's color token (e.g., `colors.gray[100]`) to match the existing
`colors.gray[400]` usage so the component follows the theme system and remains
consistent with other styles.
In `@frontend/src/types/window.d.ts`:
- Around line 4-5: Replace the broad any on window.naver with a minimal typed
interface: define NaverMaps and Naver interfaces that cover the SDK pieces you
use (e.g., Map, LatLng, Marker constructors and any methods/options you call)
and change the Window declaration so naver is typed as Naver | undefined; keep
navermap_authFailure as a function type. Update the types in
frontend/src/types/window.d.ts by adding the NaverMaps and Naver interface names
and using them in the global Window declaration instead of any to satisfy the
lint rule against any.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 09b15eb6-e842-4223-bbea-bae4fee24d6e
⛔ Files ignored due to path filters (2)
frontend/src/assets/images/icons/location_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/marker.svgis excluded by!**/*.svg
📒 Files selected for processing (10)
frontend/src/components/map/NaverMap.styles.tsfrontend/src/components/map/NaverMap.tsxfrontend/src/components/map/loadNaverMapScript.tsfrontend/src/components/map/useNaverMap.tsfrontend/src/constants/clubLocation.tsfrontend/src/index.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.styles.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.tsfrontend/src/types/window.d.ts
| console.log(import.meta.env); | ||
| console.log('NAVER CLIENT ID: ', import.meta.env.VITE_NAVER_CLIENT_ID); |
There was a problem hiding this comment.
디버그 console.log는 머지 전에 제거해 주세요.
Line 2-3은 CI 경고(Unexpected console statement)와 동일하며, 운영 로그 노이즈를 유발합니다. 또한 VITE_NAVER_CLIENT_ID와 실제 사용 키(VITE_NAVER_MAP_CLIENT_ID)가 달라 디버깅 혼선을 줍니다.
🧰 Tools
🪛 GitHub Check: ci
[warning] 3-3:
Unexpected console statement
[warning] 2-2:
Unexpected console statement
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/map/loadNaverMapScript.ts` around lines 2 - 3, Remove
the debug console.log statements in loadNaverMapScript.ts (the two lines logging
import.meta.env and VITE_NAVER_CLIENT_ID) to eliminate CI `Unexpected console
statement` warnings and noise; also correct the environment variable usage to
the actual runtime key (replace references to VITE_NAVER_CLIENT_ID with
VITE_NAVER_MAP_CLIENT_ID where the script reads the Naver map client id) so the
loader uses the correct value.
| return new Promise<void>((resolve) => { | ||
| if (window.naver?.maps) { | ||
| resolve(); | ||
| return; | ||
| } | ||
|
|
||
| const existingScript = document.querySelector( | ||
| 'script[src*="oapi.map.naver.com"]', | ||
| ); | ||
| if (existingScript) { | ||
| resolve(); | ||
| return; | ||
| } | ||
|
|
||
| const script = document.createElement('script'); | ||
| script.src = `https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=${import.meta.env.VITE_NAVER_MAP_CLIENT_ID}`; | ||
| script.async = true; | ||
|
|
||
| script.onload = () => resolve(); | ||
|
|
There was a problem hiding this comment.
SDK 로더가 조기 resolve/무한 대기 상태를 만들 수 있습니다.
Line 14-16에서 script 태그가 “존재”하기만 해도 resolve되어 SDK 준비 전에 다음 로직이 실행될 수 있습니다. 또한 Line 23은 onload만 있어 로드 실패 시 Promise가 정착되지 않습니다. 로더 Promise를 단일화하고, 기존 script 분기에서도 load/error를 기다리도록 바꿔주세요.
수정 제안
+let naverMapScriptPromise: Promise<void> | null = null;
+
export const loadNaverMapScript = () => {
- console.log(import.meta.env);
- console.log('NAVER CLIENT ID: ', import.meta.env.VITE_NAVER_CLIENT_ID);
-
- return new Promise<void>((resolve) => {
- if (window.naver?.maps) {
- resolve();
- return;
- }
+ if (window.naver?.maps) return Promise.resolve();
+ if (naverMapScriptPromise) return naverMapScriptPromise;
+
+ naverMapScriptPromise = new Promise<void>((resolve, reject) => {
+ const finish = () => resolve();
+ const fail = () => reject(new Error('Failed to load Naver Map SDK'));
const existingScript = document.querySelector(
'script[src*="oapi.map.naver.com"]',
- );
+ ) as HTMLScriptElement | null;
+
if (existingScript) {
- resolve();
+ existingScript.addEventListener('load', finish, { once: true });
+ existingScript.addEventListener('error', fail, { once: true });
return;
}
const script = document.createElement('script');
script.src = `https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=${import.meta.env.VITE_NAVER_MAP_CLIENT_ID}`;
script.async = true;
-
- script.onload = () => resolve();
+ script.onload = finish;
+ script.onerror = fail;
document.head.appendChild(script);
});
+
+ return naverMapScriptPromise;
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/map/loadNaverMapScript.ts` around lines 5 - 24, The
loader currently resolves too early when an existing script tag is present and
never rejects on load failure; update loadNaverMapScript so it never resolves
simply because document.querySelector found a script — instead attach load and
error listeners to the existingScript (listen for 'load' to resolve and 'error'
to reject or fallback) and add both onload and onerror handlers to the newly
created script before appending it (ensure you append the script to
document.head/document.body); keep using window.naver?.maps to short-circuit
only if the SDK is already available, and ensure the Promise always settles
(resolve on load, reject or resolve with failure handling on error) so callers
won’t hang.
| { | ||
| clubName: '모비딕', | ||
| lat: 35.131654, | ||
| lng: 129.104659, | ||
| building: '한솔관(E16)', | ||
| detailLocation: '106호', | ||
| }, | ||
| // 바구니-A동 | ||
| { | ||
| clubName: '모비딕', | ||
| lat: 35.131654, | ||
| lng: 129.104659, | ||
| building: '한솔관(E16)', | ||
| detailLocation: '214호', | ||
| }, |
There was a problem hiding this comment.
중복된 clubName: '모비딕'이 있습니다.
'모비딕'이 두 번 등록되어 있습니다 (Line 119, 127). Array.find()는 첫 번째 항목만 반환하므로 두 번째 위치 정보(214호)는 사용되지 않습니다. 의도된 동작인지 확인이 필요합니다.
#!/bin/bash
# Description: 중복된 clubName 확인
echo "=== 중복된 clubName 목록 ==="
rg -o "clubName: '[^']+'" frontend/src/constants/clubLocation.ts | sort | uniq -d🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/constants/clubLocation.ts` around lines 118 - 132, There are
duplicate entries for clubName '모비딕' (detailLocation '106호' and '214호') so only
the first will be returned by any Array.find lookup; either deduplicate the data
by removing the unintended duplicate or merge them into a single record (e.g.,
change detailLocation to an array like detailLocations: ['106호','214호'] and
adjust consumers), or if multiple locations are intended keep both objects but
update the lookup code to use Array.filter (or a dedicated
getLocationsByClubName function) instead of Array.find; locate the objects with
clubName '모비딕' in clubLocation.ts and apply the chosen fix consistently across
consumers that reference clubName.
| ); | ||
| if (existingScript) { | ||
| resolve(); | ||
| return; |
There was a problem hiding this comment.
현재 로더는 existingScript 존재만 확인하고 즉시 resolve해서, 실제 로드 완료 전에 훅이 실행되면 window.naver가 없어 초기화가 누락될 수 있습니다. 기존 스크립트가 있더라도 load/error 리스너를 연결해 로드 완료 시점에 resolve/reject 하도록 보강해 주세요.
|
|
||
| const location = clubLocations.find( | ||
| (location) => location.clubName === clubDetail?.name, | ||
| ); |
There was a problem hiding this comment.
clubName 기준 조회(find)와 데이터 중복이 결합되어 두 번째 동일 이름 항목이 사실상 도달 불가합니다. 위치 매핑은 clubId 같은 고유 키로 전환하거나, 최소한 중복 이름 데이터를 정리해 매핑 오차를 막아주세요.
There was a problem hiding this comment.
현재는 동아리명이 고유하게 관리되고 있어 name 기반 매핑으로 구현했습니다. 또한 위치 데이터는 프론트에서 직접 관리하고 있어, 작성 편의성 측면에서 clubId보다 clubName 기반이 더 간단한 장점이 있었습니다.
다만 확장 시 안정성을 위해 clubId 기반 매핑으로 전환하는 방향도 고려하겠습니다.
| @@ -0,0 +1,27 @@ | |||
| export const loadNaverMapScript = () => { | |||
| console.log(import.meta.env); | |||
| console.log('NAVER CLIENT ID: ', import.meta.env.VITE_NAVER_CLIENT_ID); | |||
There was a problem hiding this comment.
운영 콘솔 노이즈와 불필요한 환경 정보 노출을 줄이기 위해 디버그 로그(import.meta.env, client id)는 제거하거나 DEV 조건으로 제한하는 것을 권장합니다.
lepitaaar
left a comment
There was a problem hiding this comment.
Always Approve 정책에 따라 승인합니다.
중복 제거 및 근거 보강된 핵심 사항은 인라인 코멘트로 남겼습니다.
거절 사유(비차단 아님)
-
Severity: Medium
-
항목: Naver script loader의 existingScript 즉시 resolve로 인한 race condition
-
영향: 스크립트 태그가 존재하지만 아직 로드 전인 타이밍에서 지도 초기화가 누락되어 빈 지도 상태가 발생할 수 있음
-
수정방안: existingScript 분기에서도 load/error 리스너로 로드 완료를 보장한 뒤 resolve/reject 처리
-
Severity: Medium
-
항목: clubName 기반 위치 매핑 + 중복 데이터 결합
-
영향: 동명이칭 또는 중복 이름 항목에서 잘못된 좌표가 선택되거나 일부 데이터가 사실상 도달 불가
-
수정방안: clubId 등 고유 키 기반 매핑으로 전환하고 중복 이름 데이터 정리
-
Severity: Low
-
항목: map loader 내 운영 콘솔 디버그 로그 노출
-
영향: 콘솔 노이즈 증가 및 불필요한 환경 정보 노출
-
수정방안: 로그 제거 또는 DEV 조건으로 제한
#️⃣연관된 이슈
📝작업 내용
웹

모바일

❓작업 개요
상세페이지에 동아리방 위치를 확인할 수 있도록 네이버 지도 API를 연동하고, 커스텀 마커 및 UI를 적용했습니다.
신입 부원 기준으로 동아리실 위치를 쉽게 찾을 수 있도록 UX 개선을 목표로 구현했습니다.
🤔지도 API 선정 이유
1. 네이버 지도
2. 카카오 지도
3. 구글 지도
=> 위 이유로 네이버 지도를 채택했습니다.
🤗도입 목적
🛠️구현 내용
🔑환경 변수 설정
네이버 지도는 API 키가 없으면 동작하지 않기 때문에
.env파일에 추가해야합니다. FE 노션 자료실에 NaverMap API 키 글에 작성해두었으니 참고해주세요.네이버 지도 API 특성상 preview URL이 매번 변경되기 때문에
모든 preview 도메인을 사전에 등록하기 어려운 한계가 있습니다.
현재 등록된 preview 주소에서는 정상 동작하지만,
그 외 새로 생성되는 preview 환경에서는 도메인 미등록으로 인해
지도 API 요청이 정상적으로 동작하지 않을 수 있습니다.
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit