Skip to content

Feat#49 daum address api#50

Merged
Dobbymin merged 30 commits intomainfrom
feat#49-daum-address-api
Aug 22, 2025
Merged

Feat#49 daum address api#50
Dobbymin merged 30 commits intomainfrom
feat#49-daum-address-api

Conversation

@Dobbymin
Copy link
Copy Markdown
Contributor

📝 요약 (Summary)

카카오맵 API를 활용한 주소 검색 기능을 구현하고, Zustand를 사용한 전역 상태 관리 시스템을 구축했습니다. 사용자가 주소를 입력하고 검색하면 카카오맵 API를 통해 좌표를 얻어 지도에 표시하는 기능을 추가했습니다.

✅ 주요 변경 사항 (Key Changes)

  • 카카오맵 API를 활용한 주소 검색 유틸리티 함수 구현
  • Zustand를 사용한 지도 좌표 전역 상태 관리 스토어 추가
  • 주소 검색 훅(useKakaoAddressSearch) 구현
  • 지도 컴포넌트에서 동적 좌표 설정 기능 추가
  • MainPage 레이아웃 컴포넌트 순서 최적화

💻 상세 구현 내용 (Implementation Details)

1. 카카오맵 API 유틸리티 (src/shared/utils/kakao-map-utils.ts)

  • searchAddressToCoordinates: 주소를 좌표로 변환하는 함수
  • TypeScript 타입 정의 (KakaoGeocoderResult, AddressSearchResult)
  • Promise 기반 비동기 처리 및 에러 핸들링

2. 전역 상태 관리 (src/features/main/store/useMapAddress.ts)

  • Zustand를 사용한 지도 좌표 상태 관리
  • setMapAddress, clearMapAddress, updateCoordinates, updateAddress 액션 제공
  • 메모리 기반 상태 관리로 빠른 성능 확보

3. 주소 검색 훅 (src/features/main/hooks/useKakaoAddressSearch.ts)

  • React Hook 형태로 구현된 주소 검색 기능
  • 폼 컨텍스트와 연동하여 주소 입력값 자동 감지
  • 검색 결과를 Zustand 스토어에 자동 저장

4. 지도 컴포넌트 업데이트 (src/features/main/ui/MapSection.tsx)

  • Zustand 스토어에서 좌표 정보 구독
  • 저장된 좌표가 있으면 사용, 없으면 기본값 사용
  • 실시간 지도 업데이트 기능

5. 주소 입력 필드 개선 (src/features/main/components/common/AddressField.tsx)

  • 카카오맵 API 검색 기능 통합
  • 지도 아이콘 버튼 클릭 시 주소 검색 실행
  • 검색 결과를 전역 상태로 관리

🚀 트러블 슈팅 (Trouble Shooting)

1. Props Drilling 문제 해결

  • 문제: 주소 데이터를 여러 컴포넌트에 props로 전달하는 복잡성
  • 해결: Zustand 전역 상태 관리로 props drilling 제거

2. 카카오맵 API 타입 정의

  • 문제: 카카오맵 API의 TypeScript 타입 정의 부재
  • 해결: declare global을 사용하여 Window 객체에 카카오맵 타입 확장

3. 비동기 처리 최적화

  • 문제: 카카오맵 API 콜백 기반 비동기 처리
  • 해결: Promise로 래핑하여 async/await 패턴 적용

⚠️ 알려진 이슈 및 참고 사항 (Known Issues & Notes)

  • 카카오맵 API 키 설정이 필요합니다 (현재는 타입 정의만 완료)
  • 페이지 새로고침 시 상태가 초기화됩니다 (persist 미들웨어 미사용)
  • 카카오맵 스크립트 로드 로직은 별도 구현이 필요합니다

📸 스크린샷 (Screenshots)

주소 검색 기능 구현으로 인한 UI 변경사항:

  • 주소 입력 필드에 지도 아이콘 버튼 추가
  • 검색 후 지도 좌표 자동 업데이트

#️⃣ 관련 이슈 (Related Issues)

@Dobbymin Dobbymin linked an issue Aug 22, 2025 that may be closed by this pull request
@Dobbymin Dobbymin self-assigned this Aug 22, 2025
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @Dobbymin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 카카오맵 API를 활용한 주소 검색 기능을 구현하고, Zustand를 사용하여 지도 좌표를 전역적으로 관리하는 시스템을 구축합니다. 사용자가 주소를 입력하고 검색하면 카카오맵 API를 통해 좌표를 얻어 지도에 표시하며, 기존 UI 컴포넌트들을 개선하여 사용자 경험을 향상시킵니다.

Highlights

  • 카카오맵 API 주소 검색 기능 구현: 주소를 좌표로 변환하는 유틸리티 함수와 이를 활용한 주소 검색 훅(useKakaoAddressSearch)을 추가했습니다.
  • Zustand를 이용한 지도 좌표 전역 상태 관리: 지도 좌표를 전역적으로 관리하는 Zustand 스토어(useMapAddress)를 도입하여 props drilling 문제를 해결하고 성능을 최적화했습니다.
  • 주소 입력 필드 개선 및 통합: react-daum-postcode를 사용하여 주소 검색 UI를 통합하고, 검색된 주소를 자동으로 입력 필드에 채우며, 지도 아이콘 버튼 클릭 시 카카오맵 API 검색을 트리거하도록 했습니다.
  • 지도 컴포넌트 동적 좌표 설정: MapSection 컴포넌트가 Zustand 스토어에서 지도 좌표를 구독하여 실시간으로 지도 위치를 업데이트할 수 있도록 변경했습니다.
  • 코드 구조 및 타입 정의 개선: 카카오맵 API 타입 정의를 전역으로 선언하고, 관련 유틸리티 및 UI 컴포넌트를 entities/map 디렉토리로 재구성했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

카카오맵 API와 Daum 우편번호 서비스를 연동한 주소 검색 기능 구현을 확인했습니다. Zustand를 도입하여 전역 상태를 관리하고 Props Drilling 문제를 해결한 점이 인상적입니다. 코드 구조 또한 기능별로 잘 분리되어 유지보수성이 높아 보입니다. 몇 가지 React 모범 사례 및 사용자 경험 개선을 위한 제안 사항을 리뷰에 남겼으니 확인 부탁드립니다.

if (error) return <div>Error: {error.message}</div>;
if (loading) return <Spinner />;

if (error) return toast.error(error.message);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

렌더링 함수 내에서 toast.error()와 같은 사이드 이펙트를 유발하는 함수를 직접 호출하는 것은 React의 설계 원칙에 어긋납니다. 렌더링 함수는 순수해야 하며, UI를 반환하는 역할에 집중해야 합니다. toast.error는 토스트 ID를 반환하며, React는 이를 텍스트로 렌더링하려고 시도하여 의도치 않은 UI가 표시될 수 있습니다.

사이드 이펙트는 useEffect 훅을 사용하여 처리하는 것이 올바른 방법입니다. 이렇게 수정하면 컴포넌트의 예측 가능성이 높아지고, React의 동시성 렌더링(Concurrent Rendering)과 같은 최적화 기능을 사용할 때 발생할 수 있는 잠재적인 문제를 방지할 수 있습니다.

아래와 같이 수정하는 것을 권장합니다.

import { useEffect } from 'react';
// ...

export const KakaoMap = ({ lat, lng }: Props) => {
  const [loading, error] = useKakaoLoader({
    // ...
  });

  useEffect(() => {
    if (error) {
      toast.error(error.message);
    }
  }, [error]);

  // ...

  if (error) return <div>지도를 불러오는데 실패했습니다.</div>;

  return (
    // ...
  );
};

Comment on lines +12 to +26
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen) {
setIsOpen(false);
}
};

if (isOpen) {
document.addEventListener('keydown', handleKeyDown);
}

return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

주소 검색 모달이 열렸을 때 배경 콘텐츠가 스크롤되는 문제가 있습니다. 이는 사용자 경험을 저해할 수 있습니다.

모달이 활성화될 때 document.bodyoverflow 스타일을 hidden으로 설정하고, 모달이 닫힐 때 원래 값으로 복원하여 배경 스크롤을 방지하는 것이 좋습니다.

  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape' && isOpen) {
        setIsOpen(false);
      }
    };

    if (isOpen) {
      document.body.style.overflow = 'hidden';
      document.addEventListener('keydown', handleKeyDown);
    }

    return () => {
      document.body.style.overflow = 'unset';
      document.removeEventListener('keydown', handleKeyDown);
    };
  }, [isOpen]);

Comment on lines +41 to +45
} catch {
setSearchResult({
success: false,
error: '주소 검색 중 오류가 발생했습니다.',
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

try...catch 구문에서 catch 블록이 비어 있어, searchAddressToCoordinates 함수에서 예기치 않은 오류가 발생했을 때 해당 정보가 무시될 수 있습니다. 이로 인해 디버깅이 어려워질 수 있습니다.

오류를 console.error 등으로 기록하여 개발 중에 문제를 쉽게 파악하고 해결할 수 있도록 하는 것이 좋습니다.

Suggested change
} catch {
setSearchResult({
success: false,
error: '주소 검색 중 오류가 발생했습니다.',
});
} catch (error) {
console.error('주소 검색 중 예기치 않은 오류 발생:', error);
setSearchResult({
success: false,
error: '주소 검색 중 오류가 발생했습니다.',
});

Comment on lines +48 to +69
{isOpen && (
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/50'>
<div className='mx-4 w-full max-w-md rounded-lg bg-white'>
<div className='scrollbar-hide flex items-center justify-between border-b py-3 pr-2 pl-5'>
<h3 className='text-lg font-semibold'>주소 검색</h3>
<Button
type='button'
variant='ghost'
onClick={closeSearch}
className='text-gray-500 hover:text-gray-700'
>
<X className='size-5' />
</Button>
</div>
<DaumPostcode
onComplete={completeAddress}
onClose={closeSearch}
style={{ width: '100%', height: '400px' }}
/>
</div>
</div>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

현재 주소 검색 모달이 div로 구현되어 있어 웹 접근성 측면에서 개선의 여지가 있습니다. 스크린 리더 사용자가 모달을 인지하기 어렵고, 키보드 포커스가 모달 내부에 갇히지 않아(focus trap) 키보드 사용성이 떨어질 수 있습니다.

Radix UI와 같은 UI 라이브러리에서 제공하는 Dialog 컴포넌트를 사용하면 접근성 관련 기능(ARIA 속성, 포커스 관리, 포털 렌더링 등)을 쉽게 구현할 수 있습니다. 이미 프로젝트에서 Radix UI를 사용하고 있으니 Dialog 컴포넌트를 활용하여 개선하는 것을 고려해보세요.

@Dobbymin Dobbymin merged commit d540949 into main Aug 22, 2025
2 checks passed
@Dobbymin Dobbymin deleted the feat#49-daum-address-api branch August 22, 2025 11:07
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.

[기능 구현] 다음 주소 검색 API 적용

1 participant