[Chore/#112] kakao key 추가#115
Conversation
|
Thanks for the contribution! Please review the labels and make any necessary changes. |
There was a problem hiding this comment.
Code Review
This pull request implements Kakao Maps integration using react-native-webview and expo-location, featuring a custom map component and a location-tracking hook. The reviewer identified a race condition in the location hook's cleanup and recommended moving business logic from the widget layer to higher-level layers to improve architectural separation. Other feedback includes optimizing performance by preventing unnecessary WebView reloads when coordinates change, adding defensive checks for environment variables, and improving error logging for communication between the WebView and the native application.
| useCallback(() => { | ||
| let positionSub: Location.LocationSubscription | undefined; | ||
| let headingSub: Location.LocationSubscription | undefined; | ||
|
|
||
| (async () => { | ||
| const { status } = await Location.requestForegroundPermissionsAsync(); | ||
| if (status !== "granted") { | ||
| setCenter(SOONGSIL); | ||
| return; | ||
| } | ||
|
|
||
| const loc = await Location.getCurrentPositionAsync({}); | ||
| const userLoc = { lat: loc.coords.latitude, lng: loc.coords.longitude }; | ||
| setCenter(userLoc); | ||
| setMyLocation(userLoc); | ||
|
|
||
| positionSub = await Location.watchPositionAsync( | ||
| { | ||
| accuracy: Location.Accuracy.High, | ||
| timeInterval: 2000, | ||
| distanceInterval: 3, | ||
| }, | ||
| (l) => | ||
| setMyLocation({ lat: l.coords.latitude, lng: l.coords.longitude }), | ||
| ); | ||
|
|
||
| headingSub = await Location.watchHeadingAsync((hdg) => { | ||
| const rounded = Math.round(hdg.magHeading); | ||
| if ( | ||
| lastHeadingRef.current === null || | ||
| Math.abs(rounded - lastHeadingRef.current) >= HEADING_THRESHOLD_DEG | ||
| ) { | ||
| lastHeadingRef.current = rounded; | ||
| setHeading(rounded); | ||
| } | ||
| }); | ||
| })(); | ||
|
|
||
| return () => { | ||
| positionSub?.remove(); | ||
| headingSub?.remove(); | ||
| }; | ||
| }, []), |
There was a problem hiding this comment.
useFocusEffect 내의 비동기 로직에서 positionSub와 headingSub가 할당되기 전에 cleanup 함수가 실행될 수 있는 레이스 컨디션이 존재합니다. isActive 플래그를 도입하여 비동기 작업 완료 시점에 컴포넌트가 여전히 활성 상태인지 확인하고, 이미 cleanup이 호출되었다면 즉시 구독을 해제하도록 로직을 보완해야 합니다. 또한, Rule 7에 따라 widget은 presentational component로 설계되어야 하므로, 해당 비즈니스 로직(hook)의 위치를 features나 pages 레이어로 이동하는 것을 검토해 주세요.
References
- When creating reusable UI blocks (widgets), design them as presentational components that receive data via props. This separates UI from business logic, which should be handled in higher-level layers like features or pages.
| <WebView | ||
| ref={webViewRef} | ||
| source={{ | ||
| html: buildMapHtml(appKey, initialCenter), |
|
|
||
| export const KakaoMap = forwardRef<KakaoMapHandle, KakaoMapProps>( | ||
| function KakaoMap({ initialCenter = SOONGSIL, myLocation, heading }, ref) { | ||
| const appKey = process.env.EXPO_PUBLIC_KAKAO_JS_KEY ?? ""; |
There was a problem hiding this comment.
EXPO_PUBLIC_KAKAO_JS_KEY 환경 변수가 설정되지 않은 경우 appKey가 빈 문자열이 되어 지도가 정상적으로 동작하지 않습니다. Rule 13에 따라 mock data 단계에서는 TODO 주석으로 대체를 고려할 수 있으나, 핵심 설정값인 만큼 명시적인 처리가 권장됩니다.
References
- Defensive logic for handling missing data (e.g., undefined results from .find()) may be deferred during the mock data phase of development, provided a TODO comment is added to ensure it is implemented during the actual API integration.
| try { | ||
| const data = JSON.parse(event.nativeEvent.data) as { type: string }; | ||
| if (data.type === "MAP_READY") setIsMapReady(true); | ||
| } catch {} |
📝 요약
⚙️ 작업 내용
🔗 관련 이슈
✅ 체크리스트
💬 리뷰어에게