Skip to content

[feat] 약속 생성 UI 및 기능 구현#44

Merged
haruyam15 merged 7 commits intomainfrom
feat/meetings-create-33
Feb 3, 2026
Merged

[feat] 약속 생성 UI 및 기능 구현#44
haruyam15 merged 7 commits intomainfrom
feat/meetings-create-33

Conversation

@haruyam15
Copy link
Contributor

@haruyam15 haruyam15 commented Feb 1, 2026

🚀 풀 리퀘스트 제안

📋 작업 내용

약속 생성 UI 및 기능 구현
카카오 맵 & 키워드로 장소 검색하기 api 연동

📄 기타

책 조회 모달 & 모임 조회(모임인원 체크용) API 개발되면 붙여서 완성시킬 예정입니다.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 모임 생성 페이지 추가 및 완전한 모임 생성 워크플로우 구현
    • 지도 기반 장소 검색 기능 추가
    • 날짜 및 시간 선택 기능 개선
  • UI/스타일 개선

    • 폼 컴포넌트에 필수 항목 표시 및 에러 메시지 기능 추가
    • DatePicker, Input 컴포넌트 Ref 지원
    • 숫자 입력 필드 스핀 버튼 제거

카카오맵 API 연동 장소 검색 및 약속 생성 폼 구현.
- PlaceSearchModal: 카카오맵 API 기반 장소 검색 모달
- PlaceList: 검색 결과 목록 및 지도 마커 연동
- useKakaoMap, useKakaoPlaceSearch: 지도 및 장소 검색 훅
- useMeetingForm, useCreateMeeting: 약속 생성 폼 상태 관리 및 API 연동
- timeUtils, validation: 시간 유틸리티 및 폼 검증 로직
- MeetingCreatePage: 약속 설정 및 장소 검색 UI 통합
@haruyam15 haruyam15 self-assigned this Feb 1, 2026
@haruyam15 haruyam15 added the feat 새로운 기능 추가 label Feb 1, 2026
@haruyam15 haruyam15 linked an issue Feb 1, 2026 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Feb 1, 2026

Walkthrough

카카오맵 API를 통합한 약속 생성 기능을 추가합니다. 지도 검색, 장소 선택, 시간 설정, 참가자 수 관리 등의 기능을 제공하는 새로운 훅, 컴포넌트, API 엔드포인트 및 페이지를 구현합니다.

Changes

Cohort / File(s) Summary
Kakao Maps SDK 통합
index.html, src/vite-env.d.ts
카카오 지도 SDK 스크립트 태그 추가 및 환경 변수 타입 정의
Kakao Maps 타입 및 유틸
src/features/meetings/kakaoMap.types.ts, src/features/meetings/lib/timeUtils.ts, src/features/meetings/lib/validation.ts, src/features/meetings/lib/index.ts
장소 검색 API 응답 타입, 시간 생성·포맷팅·검증 유틸 함수 추가
Kakao Maps 훅
src/features/meetings/hooks/useKakaoMap.ts, src/features/meetings/hooks/useKakaoPlaceSearch.ts
지도 초기화·마커 렌더링 및 장소 검색 기능을 캡슐화한 훅
약속 생성 API 및 타입
src/features/meetings/meetings.api.ts, src/features/meetings/meetings.types.ts, src/features/meetings/index.ts
CreateMeeting API 엔드포인트 및 요청/응답 타입 추가
약속 생성 훅
src/features/meetings/hooks/useCreateMeeting.ts, src/features/meetings/hooks/useMeetingForm.ts, src/features/meetings/hooks/index.ts
약속 생성 뮤테이션 훅과 폼 상태 관리 훅 추가
장소 검색 컴포넌트
src/features/meetings/components/PlaceList.tsx, src/features/meetings/components/PlaceSearchModal.tsx, src/features/meetings/components/index.ts
장소 목록 렌더링 및 모달 기반 장소 검색 UI 컴포넌트
약속 생성 페이지
src/pages/Meetings/MeetingCreatePage.tsx, src/pages/Meetings/index.ts
약속명, 도서, 장소, 날짜·시간, 참가 인원을 입력받는 완전한 폼 페이지
라우팅 설정
src/routes/index.tsx
약속 생성 페이지 라우트 추가 및 설정 경로 수정
공유 UI 컴포넌트 개선
src/shared/ui/Container.tsx, src/shared/ui/Datepicker.tsx, src/shared/ui/Input.tsx, src/shared/ui/Select.tsx
Ref 포워딩, 비활성화 상태, 필수·오류 메시지 표시 기능 추가
스타일 및 유틸
src/shared/styles/base.css, src/shared/lib/utils.ts
숫자 입력 스핀 버튼 제거 CSS, Tailwind 테마 토큰 구체화

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Page as MeetingCreatePage
    participant PlaceModal as PlaceSearchModal
    participant Map as Kakao Map API
    participant PlaceAPI as Kakao Place Search
    participant Server as Server

    User->>Page: 약속 생성 폼 접근
    User->>Page: 장소 검색 버튼 클릭
    Page->>PlaceModal: 모달 오픈
    PlaceModal->>Map: 지도 초기화
    Map-->>PlaceModal: 지도 렌더링 완료
    User->>PlaceModal: 검색어 입력 + Enter
    PlaceModal->>PlaceAPI: 장소 검색 요청
    PlaceAPI-->>PlaceModal: 검색 결과 (KakaoPlace[])
    PlaceModal->>Map: 마커 렌더링
    User->>PlaceModal: 장소 선택
    PlaceModal->>Page: onSelectPlace(name, address, lat, lng)
    Page->>Page: 폼 상태 업데이트 (location, 좌표)
    PlaceModal->>PlaceModal: 모달 종료
    User->>Page: 폼 입력 완료 (도서, 날짜·시간, 참가자 수)
    User->>Page: 만들기 버튼 클릭
    Page->>Page: validateForm() - 검증
    alt 검증 실패
        Page->>User: 오류 메시지 표시 + 스크롤
    else 검증 성공
        Page->>Server: POST /api/meetings (CreateMeetingRequest)
        Server-->>Page: CreateMeetingResponse
        Page->>User: 성공 알림 + 홈 이동
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • PR #17: DatePicker, Input, Container 등 공유 UI 컴포넌트 수정이 겹침
  • PR #20: routes/index.tsx 라우팅 설정 변경이 연관됨
  • PR #32: 약속 기능 훅 및 API 확장이 중복됨

Suggested reviewers

  • choiyoungae
  • mgYang53
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 변경 사항의 핵심인 약속 생성 기능 UI 및 로직 구현을 명확하게 설명합니다.

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

✨ 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 feat/meetings-create-33

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

🤖 Fix all issues with AI agents
In `@src/features/meetings/hooks/useKakaoMap.ts`:
- Around line 31-33: The forEach callback in clearMarkers is using a concise
arrow that returns marker.setMap(null), which triggers the Biome lint rule;
update the callback used on markersRef.current.forEach inside the clearMarkers
function to use a block-bodied arrow so it calls marker.setMap(null) without
returning a value (i.e., change the callback to a block that invokes setMap and
no return), leaving the subsequent markersRef.current = [] assignment unchanged.
- Around line 21-26: Replace the three refs currently typed as any in
useKakaoMap.ts (mapRef, markersRef, infowindowRef) with concrete Kakao Maps
types so compile-time checks catch API misuse: install the community types (npm
install -D `@types/kakaomaps`) and change mapRef to useRef<kakao.maps.Map |
null>(null), markersRef to useRef<kakao.maps.Marker[]>([]), and infowindowRef to
useRef<kakao.maps.InfoWindow | null>(null); alternatively, if you maintain local
types, extend your kakaoMap.types.ts with equivalent kakao.maps.Map /
kakao.maps.Marker / kakao.maps.InfoWindow declarations and import those types
for the three refs.

In `@src/features/meetings/meetings.types.ts`:
- Around line 80-125: CreateMeetingResponse currently defines location:
MeetingLocation while CreateMeetingRequest allows location: MeetingLocation |
null; update the response type to match backend behavior by making
CreateMeetingResponse.location nullable (location: MeetingLocation | null) if
the backend can return no location, or verify backend always sends a location
and leave as-is; check and adjust the CreateMeetingResponse type (and any
consumers) to use MeetingLocation | null when backend can omit location.

In `@src/pages/Meetings/MeetingCreatePage.tsx`:
- Around line 101-105: The success handler for createMutation.mutate still uses
a placeholder navigate('/') with a TODO comment; replace this with the correct
post-create route using your app's route constants or route builder (e.g., call
the route constant or a function that builds the meeting detail/list path)
inside the onSuccess callback after awaiting openAlert; update the reference in
createMutation.mutate's onSuccess (and remove the comment) so navigation uses
the canonical route (e.g., Routes.MEETING_DETAIL or
buildMeetingRoute(meetingId)) instead of '/'.
- Around line 21-24: The component currently hardcodes gatheringId and
gatheringMaxCount, causing incorrect meeting creations; update MeetingCreatePage
to source these from a real data path (route params or API) instead of the
temporary constants: remove the fixed consts (gatheringId, gatheringMaxCount),
add state (e.g., useState) and a data fetch in the MeetingCreatePage component
(or accept them via props/route params) to populate gatheringId and
gatheringMaxCount, track a loading/ready flag, and keep the submit handler and
submit button disabled until the values are loaded/validated so submissions
cannot proceed with placeholder values.
- Around line 169-188: The place-validation error set by validateForm
(errors.location) isn't displayed; update MeetingCreatePage's place section
(inside the Container.Content that renders locationAddress, locationName and the
place-search Button) to conditionally render the errors.location message when
present (e.g., beneath the Button), using the same styling pattern as other
field errors so users see why submission is blocked; ensure you reference the
existing errors object and do not remove/alter locationAddress, locationName or
the setIsPlaceSearchOpen handler.

In `@src/shared/lib/utils.ts`:
- Around line 7-21: The spacing array contains a duplicate 'small' entry in the
spacing list (the spacing constant/array), remove one of the 'small' values or
rename the second occurrence to the intended token name (e.g., 'xbase' or
another unique token) so all entries are unique; update any references that
expect the renamed token accordingly (search for usages of spacing tokens if you
rename).
🧹 Nitpick comments (4)
src/shared/styles/base.css (1)

14-20: Firefox 지원 누락

WebKit 브라우저만 타겟팅되어 있음. Firefox에서 일관된 스타일을 원하면 추가 규칙 필요.

♻️ Firefox 지원 추가 제안
  input {
+   -moz-appearance: textfield;
    &::-webkit-outer-spin-button,
    &::-webkit-inner-spin-button {
      -webkit-appearance: none;
      margin: 0;
    }
  }
src/shared/ui/Datepicker.tsx (2)

33-46: 내부 state와 prop 동기화 패턴 재검토 권장

문제: value prop과 date state를 useEffect로 동기화하는 패턴은 불필요한 re-render와 state 불일치 가능성이 있습니다.

영향: Controlled component에서 부모가 value를 변경할 때 한 프레임 지연 발생 가능.

대안: 내부 state 없이 value를 직접 사용하거나, 현재 패턴이 필요한 이유(예: Popover 닫기 전 임시 선택)가 있다면 주석으로 명시해 주세요.

♻️ 내부 state 제거 제안
 const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(function DatePicker(
   { value, onChange, placeholder = '날짜 선택', className, disabled, isDisabled = false },
   ref
 ) {
-  const [date, setDate] = React.useState<Date | null>(value)
-
-  React.useEffect(() => {
-    setDate(value)
-  }, [value])
-
   const handleSelect = (selectedDate: Date) => {
-    setDate(selectedDate)
     onChange?.(selectedDate)
   }

   return (
     <Popover>
       <PopoverTrigger asChild>
         <button
           ref={ref}
           type="button"
-          data-empty={!date}
+          data-empty={!value}
           disabled={isDisabled}
           ...
         >
           <CalendarDays size={20} />
-          {date ? (
-            format(date, 'yyyy.MM.dd')
+          {value ? (
+            format(value, 'yyyy.MM.dd')
           ) : (
             <span className="typo-subtitle5 text-grey-600">{placeholder}</span>
           )}
         </button>
       </PopoverTrigger>
       <PopoverContent className="p-0 border-none w-fit" align="start">
         <Calendar
           mode="single"
-          selected={date ?? undefined}
+          selected={value ?? undefined}
           onSelect={handleSelect}
           required
           disabled={disabled}
         />
       </PopoverContent>
     </Popover>
   )
 })

15-16: disabled vs isDisabled 네이밍 명확화 고려

문제: 두 prop 모두 "disabled" 관련이지만 목적이 다릅니다.

  • disabled: Calendar 날짜 비활성화 (Matcher)
  • isDisabled: 컴포넌트 전체 비활성화 (boolean)

영향: 사용 시 혼란 가능.

대안: disabledDates 또는 disabledMatcher로 명확히 구분하면 의도 파악이 쉬워집니다.

src/features/meetings/components/PlaceSearchModal.tsx (1)

25-29: window.kakao 타입을 정의된 타입 파일에 추가하거나 @types/kakaomaps 패키지 설치

문제: window.kakao: any로 선언되어 타입 안전성이 낮고, 공식 타입 정의 패키지가 설치되지 않았습니다.

영향: Kakao Maps API 호출 시 타입 오류를 컴파일 단계에서 검출할 수 없어 런타임 버그 위험이 있습니다.

대안:

  • 옵션 1: npm install -D @types/kakaomaps``를 설치하고 declare global 제거
  • 옵션 2: src/features/meetings/kakaoMap.types.tswindow.kakao 타입을 추가한 후 이 파일에서 제거

옵션 2 추천 (기존 kakaoMap.types.ts와 일관성 유지).

Copy link
Contributor

@mgYang53 mgYang53 left a comment

Choose a reason for hiding this comment

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

카카오맵 sdk 사용 시 타입이 정의되지 않아 불편할 수 있습니다.
저는 아래 라이브러리 사용해서 괜찮게 해결했었던 것 같아요. 참고하면 좋을 것 같습니다.

https://github.com/JaeSeoKim/react-kakao-maps-sdk
https://github.com/JaeSeoKim/kakao.maps.d.ts

Copy link
Contributor

@mgYang53 mgYang53 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다! 👍

@haruyam15 haruyam15 merged commit 7d00002 into main Feb 3, 2026
2 checks passed
@haruyam15 haruyam15 deleted the feat/meetings-create-33 branch February 3, 2026 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 약속 생성 UI 및 기능 구현

2 participants