Skip to content

[CHORE] 프로젝트 초기 세팅 - #1

Open
yoonsunmindd wants to merge 161 commits into
mainfrom
develop
Open

[CHORE] 프로젝트 초기 세팅#1
yoonsunmindd wants to merge 161 commits into
mainfrom
develop

Conversation

@yoonsunmindd

@yoonsunmindd yoonsunmindd commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

작업 내용

  • Vite 기반 프로젝트 초기 생성
  • README 작성 및 프로젝트 정보 추가
  • .gitignore 설정
  • Prettier, ESLint, EditorConfig 설정
  • 기본 폴더 구조(src, public) 생성

변경 화면

  • UI 변경 없음

확인 방법

  1. develop 브랜치 변경 사항 확인
  2. Merge 후 프로젝트가 정상적으로 실행되는지 확인

체크리스트

  • develop 최신 반영 완료
  • 관련 없는 파일 변경 없음
  • 로컬 실행 확인
  • 콘솔 에러 없음
  • 반응형 확인
  • 불필요한 console.log 제거

스크린샷

  • 해당 없음

참고 사항

  • 프로젝트 초기 세팅을 main 브랜치에 반영하기 위한 PR입니다.

kimtaehyeokkkk and others added 13 commits July 3, 2026 15:40
Updated project name and added sections for project introduction, team roles, technology stack, and folder structure.
Add coding and Git conventions to README
Update README with project introduction, team roles, folder structure, and commit conventions.
Updated headings for consistency and formatting in README.
Updated team roles, directory structure, coding conventions, and Git conventions in README.
Updated project introduction, team roles, technology stack, and coding conventions in README.md.
Removed the type table from the README.
@yoonsunmindd yoonsunmindd changed the title [CHORE] 프로젝트 초기 세팅을 main에 반영 [CHORE] 프로젝트 초기 세팅 Jul 5, 2026
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 24925905-08ef-489e-8337-53c191c57814

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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.

kimtaehyeokkkk and others added 4 commits July 10, 2026 00:00
* feat: Home ReviewSection 및 ReviewCard 구현

* refactor: improve ReviewCard accessibility
- ContentCard에 className, imageClassName 옵션 추가
- ContentCardSkeleton에 className, imageClassName 옵션 추가
- 기존 기본 스타일을 유지해 Home 화면 카드 UI 영향 없도록 처리

Closes #22
* chore: React Query Provider 설정

* feat: 여기도 추천 코스 화면 구현
kimtaehyeokkkk and others added 24 commits August 5, 2026 21:25
* feat: 이메일 회원가입 API 연동 (check-email, signup)

* feat: 이메일 회원가입 API 연동 (check-email, signup)

* feat: 이메일 회원가입 API 연동 인증번호 추가

---------

Co-authored-by: 윤선민 <yoonsunmin@yunseonmin-ui-MacBookAir.local>
* docs: add local course detail mock data design

* Revert "docs: add local course detail mock data design"

This reverts commit 18ab8dd.

* fix: 방문월 순서 보장

* fix: 방문월 검증 에러 메시지 폰트 크기를 scale 기준으로 통일
* refactor: 코스 enum 라벨 매핑 공용화

여행 기간/이동 수단/동행 라벨이 utils/courseCard.ts와 review/reviewCourse.ts에
따로 정의돼 있어 한곳으로 모은다. 후기 작성자 표기에 쓸 연령대/성별 매핑도
함께 추가한다.

문서와 실제 응답의 enum 표기가 어긋난 사례가 반복돼(ONE_DAY/MORE vs
DAY_TRIP/THREE_PLUS, Swagger에 없는 PUBLIC 등) 알려진 별칭을 함께 받고,
모르는 값이 와도 화면이 비지 않게 원문을 돌려준다. 연령대/성별은 문장으로
조합돼 노출되므로 원문 대신 빈 문자열을 돌려준다.

* feat: 리뷰 API 연동 계층 추가

후기 조회/작성/수정/삭제 6개 엔드포인트와 화면 데이터 매퍼를 붙인다.
사진 업로드는 여행 기록과 동일한 presigned URL 방식을 재사용한다.

enum은 Swagger와 백엔드 명세서가 서로 달라 string으로 받고 표시 시점에
매핑한다. 삭제는 이미 없는 리뷰(REVIEW4041/404)를 성공으로 처리한다.
실패로 두면 목록에 남아 재시도해도 계속 404가 나서 빠져나갈 수 없다.

리뷰 응답에 작성자 식별자가 없어 본인 여부를 알 수 없으므로, 내 게시물
목록에서 리뷰 ID를 모아 대조한다. 닉네임 비교는 동명이인에서 틀린다.
백엔드가 isMine을 내려주면 걷어낼 수 있다.

상세 화면 밖에서 코스를 읽을 수 있도록 useCourseDetail을 공용 훅에 둔다.

* feat: 후기 화면을 실 API로 연결하고 본인 후기 메뉴 추가

홈 최근 후기, 최근 후기 코스, 코스 상세, 후기 전체보기, 후기 작성을 모두
서버 데이터로 바꾼다. 작성 직후 반영을 대신하던 임시 Zustand 상태와 목
데이터 파일을 제거한다.

본인이 쓴 후기 카드에 더보기 메뉴를 붙여 삭제할 수 있게 한다. 메뉴와 확인
다이얼로그는 여행 기록 상세의 생김새를 따르고, 카드가 overflow-hidden이라
둘 다 포털로 띄운다. 후기 코스 카드는 좋아요와 더보기가 같이 쌓이면 하트가
후기에 대한 것으로 오해되므로 본인 후기일 때 더보기만 남긴다.

코스별 후기는 정렬 파라미터가 없어 받아온 목록을 클라이언트에서 정렬한다.
페이징도 없어 전체가 한 번에 오므로 잘린 목록을 정렬할 위험은 없다.

리뷰 목록 API의 코스 정보에 동행/해시태그가 없어 해당 항목은 비워 두고,
카드가 값 없는 항목을 건너뛰도록 한다. 수정은 유지할 사진의 imageKey를
받을 수 없어 아직 메뉴에 노출하지 않는다.

* fix: 리뷰 작성자 프로필 이미지 타입을 nullable로 수정

실제 응답에서 profileImageUrl이 null로 내려오는 것을 확인했다. 매퍼가
이미 빈 문자열로 대체하고 있어 화면 동작에는 문제가 없었고, 타입만
사실과 달랐다.

* feat: 후기 카드 상세 조회 및 홈 최신 후기 개선

* fix: 홈 후기 삭제 반영과 코스 바로가기 보완

홈 후기 목록은 무한 쿼리가 아니라 일반 쿼리로 담겨 있어, 삭제 시 낙관적
제거 updater에 걸리지 않아 재조회 전까지 지운 카드가 남아 있었다. 홈
캐시를 따로 걷어낸다.

쿼리 키를 ['reviews'] 아래로 합치지 않는다. 합치면 무한 쿼리용 updater가
pages 없는 데이터에 걸려 터지는데, 타입이 InfiniteData로 단언돼 있어
컴파일 단계에서 걸러지지 않는다.

홈 조회가 GET /reviews로 바뀌어 코스 정보가 함께 오므로 모달에서 코스로
갈 수 있게 한다. 카드 자체에는 이동을 붙이지 않는다. 홈 후기는 좌우
스와이프로 넘기는 캐러셀이라 탭 판정이 스와이프와 부딪친다.

* fix: 후기 상세 모달이 닫히지 않는 문제 수정

카드를 길게 누르는 중에 포인터 아래에서 모달이 열린다. 그 포인터를 모달
위에서 떼면 pointerdown 대상(카드)과 pointerup 대상(모달)이 달라 브라우저가
click을 만들지 않아, X도 딤도 반응하지 않았다. 딤이 화면 전체를 덮고 있어
페이지 전체가 멈춘 것처럼 보였다.

브라우저의 click 생성에 기대지 않고 같은 포인터로 눌렀다 뗐는지를 직접
판정한다. 모달이 열리기 전에 시작된 포인터는 기록이 없어 자연히 걸러진다.

pointerup으로 닫으면 뒤따르는 click이 모달이 사라진 자리의 요소로 떨어지므로
그 한 번은 capture 단계에서 삼킨다. 키보드는 포인터 이벤트 없이 click만
보내므로 닫기 버튼의 onClick은 남겨 둔다.

* refactor: 리뷰 화면 중복 배선 정리 및 죽은 코드 제거

홈이 GET /reviews를 쓰게 되면서 getRecentReviews와 그 응답 타입, RecentReview가
아무 데서도 쓰이지 않게 됐다. 함께 지우면 toReviewCardProps의 유니온 타입과
course 유무 분기도 사라져 ReviewDetail 하나만 받는 매퍼가 된다.

후기 카드가 놓인 네 화면에 모달과 삭제 다이얼로그 배선이 그대로 복사돼 있었다.
확인 문구까지 네 번 적혀 있었다. 열림 상태는 useReviewDetailModal로, 삭제
다이얼로그는 ReviewDeleteDialog와 useReviewDelete의 dialogProps로 모은다.
모달은 후기 값을 흩어진 여섯 개 prop 대신 review 하나로 받는다.

리뷰 수정 관련 코드는 아직 쓰는 화면이 없지만 지우지 않는다. 조회 응답에
imageKey가 없어 화면을 못 만드는 것이고 백엔드에 요청해 둔 상태라, 이유를
주석으로 남겨 다음 사람이 죽은 코드로 오해하지 않게 한다.

* chore: 백엔드 확인 결과를 enum 매핑과 주석에 반영

ageGroup 실제 값이 TEENS~HUNDRED_PLUS 10단계로 확정됐다. 명세서에만 있던
TEEN, FIFTIES_PLUS는 코드와 맞지 않는 옛 값이라 별칭을 걷어낸다.

gender enum에 NONE이 추가됐다. 성별을 밝히지 않은 사용자라 라벨을 비워
작성자 표기에 연령대만 남게 한다. ReviewAuthor에 gender가 추가되면 그대로
동작한다.

커서는 cursorId만 되돌려 보내면 되고 서버가 그 리뷰에서 정렬 기준값을
가져간다는 답을 받아, 확인 중이라고 적어 둔 주석을 확정 내용으로 바꾼다.

* feat: 리뷰 수정 모달 UI 구현

* feat: 리뷰 수정 연동 및 코스별 후기 커서 페이징

백엔드가 조회 응답에 imageKey를 추가하고 코스별 후기에 정렬·페이징을 열어
줘서 두 가지를 붙인다.

수정은 유지하는 사진의 imageKey를 그대로 되돌려 보내고 새로 고른 사진만
업로드한다. images가 전체 교체 규칙이라 유지분까지 함께 실어야 하고,
imageOrder는 1부터 연속이어야 한다(REVIEW4002).

수정 진입은 홈과 최근 후기 코스에만 붙인다. 코스별 후기 응답은 아직
imageUrls 문자열 배열이라 유지할 사진을 지목할 수 없어, 그 화면들에서
열면 사진이 사라진다.

코스별 후기는 클라이언트 정렬을 걷어내고 서버 정렬로 바꾼다. 코스 상세는
미리보기라 첫 페이지만 size=4로 받고, 전체보기는 무한 스크롤로 잇는다.
cursorValue와 cursorId는 하나만 보내면 400이라 둘 다 있을 때만 다음
페이지를 요청한다.

카드가 들고 있던 수정 모달은 페이지로 옮긴다. 카드마다 모달이 생기고
카드가 저장까지 알아야 해서, 삭제 다이얼로그·상세 모달과 같은 자리에 둔다.

* fix: 사진을 바꾸지 않은 후기 수정은 images를 보내지 않음

명세상 images를 생략하면 서버가 기존 이미지를 그대로 둔다. 안 바뀐 목록을
굳이 실어 보내면 서버가 지웠다 다시 넣는 일을 헛하게 되므로, 사진을 손대지
않았으면 요청에서 빼는 것이 맞다.

지금은 서버가 이미 그 리뷰에 붙어 있는 imageKey를 다시 받으면 500을 내는
상태라, 이 변경으로 별점·내용 수정이 함께 풀린다. 사진을 유지한 채 일부만
교체하는 경우는 여전히 실패하며 백엔드 수정을 기다린다.

* fix: 코드 리뷰 지적 사항 반영

동작하지 않는 메뉴 항목을 감춘다. 코스별 후기 응답에는 imageKey가 없어
코스 상세와 코스 후기 전체보기는 수정을 열지 않는데, 그 화면에서도 '수정'이
보이고 눌러도 아무 일이 없었다.

카드에 role=button과 tabIndex를 붙여 놓고 키보드로는 아무 동작도 하지
않던 문제를 고친다. 길게 누르기만 있는 화면에서는 Enter/Space로 후기
상세를 연다.

포인터 주 버튼만 처리한다. 그러지 않으면 데스크톱에서 우클릭만 해도 카드가
눌린 것으로 처리돼 코스 상세로 이동한다.

잘못된 courseId로 들어오면 쿼리가 비활성이라 isPending이 계속 true로 남아
스피너가 멈추지 않았다. 로딩으로 보지 않고 코스를 찾을 수 없다고 알린다.

후기 작성은 코스가 확인되기 전에는 보낼 수 없게 한다. 코스를 못 찾은 상태로
제출되면 작성한 내용이 그대로 날아간다. 조회에 실패한 경우도 안내한다.

후기 상세 모달의 본문 높이를 고정에서 최대치로 바꾼다. 카드에서 잘려 보이는
본문을 다 보여주려고 만든 모달인데 두 줄만 보이고 있었다.
* feat: 문화콘텐츠 좋아요 API 연동

* feat: 행사 목록 카드 좋아요 API 연동

홈/검색/진행중/최근본 등 행사 카드가 노출되는 모든 목록 화면의 좋아요를
로컬 상태 대신 useContentLikeToggle로 연결

* fix: 콘텐츠/코스 좋아요 동시 토글 시 콜백 유실 수정

같은 useMutation 인스턴스를 여러 항목이 공유할 때, mutate()에 개별 전달한
onSuccess/onError/onSettled는 마지막 호출 것만 유효해 먼저 시작한 요청의
콜백이 실행되지 않던 문제를 useMutation 옵션으로 이동해 해결

* fix: 콘텐츠 좋아요가 페이지 이동 후 풀리는 문제 수정

/contents 목록 응답엔 isLiked 필드가 아예 없어 재방문 시 참고할 서버
진실값이 없다. 코스는 목록 응답에 isLiked가 있어 문제없지만, 콘텐츠는
좋아요 상태를 localStorage에 별도로 기록해두고 재마운트 시 그 값을
기본값으로 사용하도록 수정
OPTIONS 프리플라이트의 Allow 헤더로 실제 지원 메서드가 PUT,DELETE뿐임을
확인 (POST는 지원 안 함). 목록/상세 페이지 모두 이 함수를 공유해서 쓰고
있어 한 곳만 고치면 전체에 반영된다.
* fix: 삭제된 코스의 후기 목록 안내 구분

코스가 없거나 삭제된 경우(COURSE4041)와 후기 조회가 실패한 경우를 같은
문구로 안내하고 있었다. 사용자가 할 수 있는 일이 다르므로 나눈다. courseId가
숫자가 아닌 경우도 같은 안내로 묶는다.

없는 코스에 후기를 쓰러 갈 수는 없으므로 하단 작성 버튼도 함께 감춘다.

* feat: 후기 카드 코스 정보 보강 및 진입 흐름 정리

최근 후기 코스 카드에 해시태그와 동행을 채운다. 후기 목록 응답의 course에는
두 값이 없지만 코스 상세에는 있어서, 코스별 상세를 함께 읽는다. 캐시 키가
카드 클릭 시의 타입 조회와 같아 이동도 즉시 이뤄진다.

홈 후기 카드도 눌러 코스 상세로 갈 수 있게 한다. 스와이프와 부딪칠까 봐
빼 뒀는데, useLongPress가 10px 넘게 움직이면 클릭도 취소하므로 캐러셀을
넘기는 동작에서는 이동이 일어나지 않는다.

후기 작성 화면의 별점을 5에서 시작한다. 0개로 두면 제출 버튼이 왜 비활성인지
알기 어렵다. 수정 모달이 쓰던 기본값과 상수를 합친다.

사진이 한 장인 후기는 카드 폭을 채우고, 후기 전체보기 배경을 카드와 구분되게
바꾼다.

* fix: 코스 좋아요 요청 방식 변경 반영 및 후기 카드 코스 제목 표시

백엔드가 코스 좋아요 등록을 POST에서 PUT으로 바꿨다. 그대로 POST를 보내고
있어 좋아요가 걸리지 않았다. 취소(DELETE)와 응답 형태는 그대로다.

후기 목록의 course.isLiked는 서버가 아직 임시 사용자 기준으로 계산해서,
비로그인에도 남의 좋아요가 켜진 채로 온다. 최근 후기 코스 화면은 태그를
그리려고 이미 코스 상세를 읽고 있으므로, 사용자 기준으로 맞게 오는 그 값을
우선 쓴다. 추가 요청은 없다.

코스별 후기 응답에는 코스 제목이 없어 코스 상세의 후기 카드가 제목 줄을
비운 채 그리고 있었다. 화면이 아는 제목을 넘긴다. 후기 전체보기는 목록에서
넘어올 때만 제목이 실려 오므로, 주소로 바로 들어온 경우에만 코스를 읽어
채운다.

후기 전체보기 카드는 높이가 남아 variant로 따로 잡는다.

* fix: 비로그인 상태에서 후기 코스 카드의 좋아요를 꺼둔다

후기 목록의 course.isLiked가 서버에서 임시 사용자 기준으로 계산돼, 로그인
하지 않아도 하트가 채워진 채로 보였다. 누르면 로그인 모달이 뜨지만, 그
전까지는 자기가 좋아요한 코스로 오해하게 된다.

로그인하지 않았으면 좋아요가 있을 수 없으므로 서버 값과 무관하게 끈다.

* style: 후기 목록 로딩 스켈레톤을 카드 모양에 맞춘다

후기 카드가 화면마다 높이와 구성이 달라졌는데 스켈레톤은 한 모양이라, 로딩
중과 로딩 후의 자리가 어긋났다. 후기 코스 카드용 스켈레톤을 따로 두고,
후기 카드 스켈레톤은 화면별 variant를 받게 한다.

캐러셀과 목록은 첫 화면에 한 장만 보이므로 스켈레톤도 한 장만 그린다.

* fix: 삭제된 코스 판정과 후기 스켈레톤 크기 보정

- 코스 상세 조회의 COURSE4041도 코스 없음 판정에 포함
- 코드 없는 404를 삭제된 코스로 단정하지 않도록 판정 기준을 좁힘
- 후기 코스 카드 스켈레톤에 카드와 같은 스케일 프레임 적용
* feat: 관리자 추천 행사 등록 UI 구현

* fix: 관리자 행사 등록 코드리뷰 피드백 반영

- 행사 기간 종료월별 실제 일수 반영 및 유효하지 않은 일자 자동 초기화
- 장소명 공백만 입력 시 제출 방지, 장소 미선택 상태로 기본정보 진입 시 /admin 리다이렉트
- 상세 미리보기 지도에 위경도 유효성 검사 적용, 홈페이지 미입력 시 '-' 표시
- 사진 교체/삭제 시 이전 blob URL 해제, 스토어 setPhoto가 동일 URL이면 재해제하지 않도록 수정
- 관리자 행사 등록 라우트에 로그인 가드(ProtectedRoute) 적용
- 카테고리 선택 상태를 사진/키워드와 동일하게 제출 시점에만 저장하도록 통일, 접근성 id 중복 방지

* fix: 사진 blob URL 소유권을 store로 중앙화

photo-tag 페이지가 store의 photo를 로컬 state로 복사해 쓰다 보니, 제출 전에 사진을 교체/삭제하면 store가 아직 참조 중인 URL을 먼저 revoke해버리는 문제가 있었음. 로컬 사본을 없애고 변경 즉시 store에 반영하도록 바꿔 URL 생성/해제 책임을 store(setPhoto) 한 곳으로 모음 (local-recommendation/tag-selection의 setPendingImage 패턴과 동일).
* docs: add local course detail mock data design

* Revert "docs: add local course detail mock data design"

This reverts commit 18ab8dd.

* feat: 찜 목록 API 연동 및 거리/기간 표시 개선

- 찜 목록 조회 API(likes.api.ts)와 useLikedItems 훅 추가
- EVENT 카테고리 쿼리 파라미터를 서버 규격(CONTENT)에 맞게 매핑
- 코스 기간을 한글 라벨(당일치기/1박 2일 등)로 변환
- Kakao Map에 Haversine 기반 현위치-장소 거리 계산 및 표시 추가

* feat: 행사 필터링 및 행사 날짜 표시 수정

* feat: 장소 카드에 현 위치 기준 거리 표시

- 좋아요 목록 응답의 distance를 PLACE 카테고리 카드에 "현위치와 N KM"으로 표시
- 디자인 시안에 맞춰 카드 정보 줄 레이아웃 조정
  - 아이콘을 늘리지 않고 14px 슬롯 가운데 원본 크기로 배치
  - 태그 없는 카드는 정보 줄 간격을 넓게(22px) 사용
- 사용하지 않는 목 데이터(mockLikedItems) 제거

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: 매칭 불가능한 좋아요 상세 필터 제거 및 거리 계산 안정화

- 좋아요 목록 응답에 행사/장소 분류 값이 없어 항상 0건이던 2번째 필터 옵션 제거
- 하버사인 중간값을 1로 제한하고 asin 대신 atan2를 써 대척점에서 NaN이 나지 않게 수정
- 두 동작에 대한 테스트 추가

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: 좋아요 목록 코스 이동 수단 및 동행 유형 표시 연동

* refactor: 미사용 하버사인 거리 계산 유틸리티 및 테스트 제거

* feat: add Google Places opening hours

* fix: refine place hours lookup behavior

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat: 프로필 및 프로필 수정 화면 구현

* feat: 프로필 및 소상공인 인증 UI 구현

* style: 사업장 카드 위치 아이콘 제거

* fix: 프로필 인증 입력 및 접근성 보완
* feat: 소상공인 홍보 목록/상세 API 연동 (테스트 데이터 확보 후 최종 검증 필요)

* 상세페이지 place칸 복구

* feat: 소상공인 지역 캐러셀 API 기반 regionId 매칭

* fix:enable 게이팅

* fix: 무한로딩 버그 수정

* feat: 소셜 로그인 연동 작업 (미완성/확인 필요)

* 코드래빗 수정

---------

Co-authored-by: 윤선민 <yoonsunmin@yunseonmin-ui-MacBookAir.local>
* feat: 문화콘텐츠 등록 API 연동

관리자 여기도 추천 행사 등록 마지막 단계(사진/키워드/카테고리)에서 실제 POST /contents API를 호출하도록 연동.

- 대표 사진은 /files/presigned-url 발급 후 S3에 직접 업로드, 반환된 objectKey를 thumbnailImageKey로 전달
- 선택한 키워드는 /hashtags 조회 후 라벨 매칭으로 hashtagIds로 변환 (local-recommendation/tag-selection과 동일 패턴)
- 카테고리(체험/전시/공연/축제)는 서버 enum(EXPERIENCE/EXHIBITION/PERFORMANCE/FESTIVAL)으로 매핑
- 등록 성공 시 실제 생성된 콘텐츠의 상세 페이지(/festival/detail/:contentId)로 이동, 실패 시 서버 에러 메시지 표시 후 재시도 가능
- 더 이상 필요 없어진 mock 미리보기 화면(complete 페이지, mockEventDetail)과 라우트 제거

스키마는 라이브 OpenAPI 문서(https://api.yeogido.kr/v3/api-docs)로 필드명/enum 값을 직접 확인해 검증함.

* fix: 행사 등록 화면 코드리뷰 반영

- 제출 버튼 라벨을 '장소 등록하기' -> '행사 등록하기'로 수정 (콘텐츠 등록 화면이므로)
- 해시태그 조회 실패를 조용히 삼키지 않고 상위 catch로 전파해 등록을 중단하도록 수정 (기존엔 실패 시 hashtagIds를 빈 배열로 등록해버림)
- 키워드/카테고리 선택을 사진과 동일하게 선택 즉시 store에 반영하도록 수정 (기존엔 로컬 state에만 있어서 뒤로 갔다 다시 오면 선택이 사라짐)
* refactor: 후기 응답의 코스 타입으로 상세 이동 경로를 정한다

- ReviewCourse에 추가된 courseType을 매퍼에서 그대로 넘김
- 라우트 판별용 코스 상세 선조회(useNavigateToCourseDetail) 제거
- 이동수단 라벨 prop 이름을 transport로 바로잡아 코스 타입과 구분

* fix: 찜 목록이 쓰는 코스 상세 이동 훅을 되살린다

develop의 찜 목록이 useNavigateToCourseDetail을 쓰기 시작했다.
LikedResponse에는 courseType이 없어 선조회가 필요하다.
후기는 응답에 courseType이 있어 계속 buildCourseDetailPath로 바로 이동한다.

* feat: 코스 후기 이미지 키와 작성자 성별을 반영한다

- 코스별 후기 응답이 imageUrls에서 images(imageKey 포함)로 바뀐 것을 반영
- 코스 상세와 후기 전체보기에서 막아 뒀던 수정 진입을 연다
- 작성자 메타에 성별을 붙여 20대 여 형태로 표시

* docs: 후기 수정 지원 범위를 주석에 맞춘다

코스별 후기 응답에도 imageKey가 오면서 네 화면 모두 수정에 들어갈 수 있게
됐는데, 두 곳에만 있다고 설명하는 주석이 남아 있었다.
* feat: 하위 지역(구/군) 조회 API 연동 및 지역 정보 페이지 전면 개편

- 지역 검색의 시/도 → 구/군 선택을 GET /regions/{regionId}/sub-regions로 대체 (기존 전국 하드코딩 데이터 제거)
- 하위 지역이 더 있으면(3단계) 재귀적으로 드릴다운, 없으면(리프) 바로 검색 결과로 이동
- region-info 페이지가 쓰던 정적 7개 지역 매핑을 제거하고, /regions + /regions/search로 실시간 조회하도록 변경 → 시/도 17개 전체 + 시/군/구 단위까지 정상 표시
- 존재하지 않는 지역은 부산으로 조용히 대체하던 기존 동작 대신 '지역 정보를 찾을 수 없습니다' 상태를 명시적으로 표시
- 홈 지도 클릭 시 지역명을 정규화하는 표를 7개에서 17개 전체로 확장

* fix: 지역 정보 페이지 코드리뷰 반영

- 강원/전북의 새 행정구역명(강원특별자치도/전북특별자치도)을 정규화 표에 추가 (라이브 API가 이미 새 이름으로 fullName을 내려줌)
- 지역 검색에서 드릴다운한 상태로 '전체'를 클릭하면 상위 도시가 아니라 드릴다운한 지역 기준으로 이동하도록 수정
- 지역 검색이 이미 알고 있는 regionId를 region-info로 navigate할 때 state로 함께 넘기고, region-info는 그 값이 있으면 이름 검색(동명 지역이 여럿일 수 있어 부정확) 대신 그대로 신뢰하도록 수정
- visibleDistricts의 정지 조건을 sub-regions 쿼리와 동일하게 통일
* docs: add local course detail mock data design

* Revert "docs: add local course detail mock data design"

This reverts commit 18ab8dd.

* feat: 코스/행사/업체 상세에서 카카오맵 길찾기 연동

- CourseStopItem, 행사/업체 상세의 장소 카드 클릭 시 카카오맵 길찾기(map.kakao.com/link/to) 오픈
- 좌표가 없는 장소는 클릭 비활성화, 좋아요 버튼은 이벤트 버블링 분리
- openKakaoMapRoute 유틸로 세 곳에서 공통 로직 재사용

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: 좋아요 버튼 키보드 이벤트가 길찾기 핸들러로 전파되는 문제 수정

좋아요 버튼 포커스 상태에서 Enter/Space 입력 시 keydown이 상위 article의
길찾기 onKeyDown까지 버블링되어 좋아요 토글과 동시에 카카오맵이 열리는
문제를 stopPropagation으로 차단

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* refactor: 후기 응답의 코스 타입으로 상세 이동 경로를 정한다

- ReviewCourse에 추가된 courseType을 매퍼에서 그대로 넘김
- 라우트 판별용 코스 상세 선조회(useNavigateToCourseDetail) 제거
- 이동수단 라벨 prop 이름을 transport로 바로잡아 코스 타입과 구분

* fix: 찜 목록이 쓰는 코스 상세 이동 훅을 되살린다

develop의 찜 목록이 useNavigateToCourseDetail을 쓰기 시작했다.
LikedResponse에는 courseType이 없어 선조회가 필요하다.
후기는 응답에 courseType이 있어 계속 buildCourseDetailPath로 바로 이동한다.

* feat: 코스 후기 이미지 키와 작성자 성별을 반영한다

- 코스별 후기 응답이 imageUrls에서 images(imageKey 포함)로 바뀐 것을 반영
- 코스 상세와 후기 전체보기에서 막아 뒀던 수정 진입을 연다
- 작성자 메타에 성별을 붙여 20대 여 형태로 표시

* 회원가입 버튼 연동 작업

* 코드래빗 수정

---------

Co-authored-by: Junehuk Lee <junhuk2472@gmail.com>
Co-authored-by: 윤선민 <yoonsunmin@yunseonmin-ui-MacBookAir.local>
* feat: 관리자 여기도 추천 코스 등록 UI 구현

지역 선택부터 방문 순서 설정, 코스 상세 미리보기까지 8단계 관리자 코스
등록 플로우를 UI만 구현. 기존 관리자 행사 등록/local-recommendation의
컴포넌트와 store 패턴을 최대한 재사용하고, API 연동 없이 Mock 데이터로
동작하도록 구성. 코스 목록 화면은 yeogido-course 홈 구조를 그대로
재사용(자세히 보기/전체 보기, 상세 미리보기 포함)했고, 사이드바에 코스
관리자 진입 메뉴를 추가.

* fix: 관리자 코스 등록 코드리뷰 반영

방문 순서 스테일 상태, 이동수단 뱃지 아이콘 오매핑, 미리보기 가드 누락
등 리뷰에서 확인된 실제 버그를 고치고, 불필요한 중복 로컬 상태를
정리했습니다.

- setSelectedPlaces/setSelectedEvents가 항목 제거 시 visitOrder도 같이 정리
- useAdminCourseVisitOrder가 매번 현재 선택값 기준으로 새로 빌드하도록 수정
  (뒤로 가서 장소/행사를 추가·삭제해도 방문순서에 반영되지 않던 문제)
- VisitEvent id에 kind별 접두사를 붙여 장소/행사 id 충돌 방지
- TRANSPORT_BADGE_ICON.public을 실제 매퍼와 동일하게 'people'로 수정
- 미리보기 페이지에 visitOrder 비어있을 때 가드 추가, 뒤로가기를 이전
  단계(visit-order)로 통일
- region-selection/photo-tag의 스토어 값과 항상 같았던 중복 로컬 상태 제거
- 최근 검색 지역 localStorage 파싱 시 형태 검증 추가
* feat: 여행 기록 폴더 생성 화면 스타일 개선

* refactor: 지역 선택 화면 UI 공통화

* style: 여행 지도 표시 요소 조정

* fix: 사진이 한 장일 때도 스티커가 폴더와 사진 밖으로 나가지 않게 수정

사진 한 장은 두 장일 때보다 8px 내려간 전용 슬롯을 쓰는데, 드래그 경계와
클립 패스가 둘 다 두 장 슬롯 좌표를 그대로 들고 있었다. 그래서 사진이 없는
왼쪽 자리가 열려 있고, 정작 사진 아래쪽은 막혀 있었다. 클립 패스는 한 장일 때
사진 영역 자체가 없어 스티커를 올려도 그려지지 않았다.

- 사진 프레임 좌표를 FOLDER_PHOTO_FRAMES 한 곳으로 모아 양쪽이 같은 값을 보게 함
- 경계 판정이 실제로 사진이 놓인 슬롯만 통과시키도록 슬롯 목록을 받게 변경
- 드래그와 드롭, 클립 패스 세 경로에 아트워크와 같은 슬롯을 전달

* fix: 지역 선택 뒤로가기 버튼 터치 타깃을 44px로 확대

아이콘 크기인 24px이 그대로 히트 영역이라 최소 터치 타깃에 못 미쳤다.
프레임이 통째로 scale 변환되어 좁은 화면에서는 20px까지 줄어든다.

- 아이콘은 24px로 두고 패딩으로만 히트 영역을 넓힘
- 패딩만큼 음수 마진을 줘 아래 요소들의 위치는 유지
- 전역 border-box 때문에 패딩이 안쪽을 파고들지 않도록 box-content 지정
* refactor: 후기 응답의 코스 타입으로 상세 이동 경로를 정한다

- ReviewCourse에 추가된 courseType을 매퍼에서 그대로 넘김
- 라우트 판별용 코스 상세 선조회(useNavigateToCourseDetail) 제거
- 이동수단 라벨 prop 이름을 transport로 바로잡아 코스 타입과 구분

* fix: 찜 목록이 쓰는 코스 상세 이동 훅을 되살린다

develop의 찜 목록이 useNavigateToCourseDetail을 쓰기 시작했다.
LikedResponse에는 courseType이 없어 선조회가 필요하다.
후기는 응답에 courseType이 있어 계속 buildCourseDetailPath로 바로 이동한다.

* feat: 코스 후기 이미지 키와 작성자 성별을 반영한다

- 코스별 후기 응답이 imageUrls에서 images(imageKey 포함)로 바뀐 것을 반영
- 코스 상세와 후기 전체보기에서 막아 뒀던 수정 진입을 연다
- 작성자 메타에 성별을 붙여 20대 여 형태로 표시

* 회원가입 버튼 연동 작업

* 코드래빗 수정

* 소상공인 목록/상세 조회 api 연동 및 좋아요/태그칩 ui

* 코드래빗 리뷰 수정

* 태그칩 간격 수정

---------

Co-authored-by: Junehuk Lee <junhuk2472@gmail.com>
Co-authored-by: 윤선민 <yoonsunmin@yunseonmin-ui-MacBookAir.local>
* feat: 음료/게임/음악/스포츠 태그 아이콘 추가

- tag-chip, card-tag-chip에 drink/game/music/sport SVG 추가
- TagId, tagDefinitions, 해시태그 매핑 테이블에 반영
- card-tag-chip은 selected만 실사용되어 inactive 자리에도 동일 아이콘 사용

* fix: 술 태그 해시태그 매핑을 라벨과 일치시킴
* feat: 진행 중인 행사 조회 API 연동

- 문화콘텐츠 목록 조회(GET /contents) 대신 진행 중인 행사만 반환하는
  GET /contents/ongoing으로 홈/행사 탭 진행 중인 행사 영역을 교체
- 행사 탭 전체보기 페이지도 동일 API로 전환, 응답에 페이지네이션이
  없어 무한스크롤 제거

* fix: 진행 중인 행사 API 응답 스키마 변경 반영

- thumbnailImage -> thumbnailImageUrl, region -> regionName으로 필드명 변경
- liked 필드가 응답에서 빠져 getLiked 기본값을 false로 되돌림
  (기존 진행 중인 행사 미리보기/전체보기도 원래 liked 없이 false 기본값이라
  이번 변경으로 인한 회귀 아님)
- category/sort/cursor 파라미터는 여전히 서버가 받지 않는 것으로 재확인

* fix: 행사 상세페이지 시간 정보를 행사 기간으로 변경

- 영업시간 조회(festivalPlaceHours) 대신 행사 시작~종료일(period)을 표시
- period 날짜 표기를 YYYY-MM-DD에서 YYYY.MM.DD로 통일
* feat: 관리자 여기도 추천 코스 등록 API 연동

Mock 데이터로 구현해 둔 관리자 코스 등록 UI에 실제 API를 연결했다. UI/
디자인은 그대로 두고 데이터 소스만 교체했다.

- 지역 선택: getPopularRegions/searchRegions 실연동 (local-recommendation
  지역 선택 화면과 동일 패턴, 로딩/에러 UI 포함)
- 장소 검색: local-recommendation의 usePlaceSearch(카카오맵 SDK) 재사용
- 행사 검색: searchFestivals+getCultureContents 실연동, AdminCourseEventItem
  자체 타입을 실제 contentId를 담고 있는 FestivalItem으로 교체
- 이미지 업로드: presigned-url 스키마가 라이브 OpenAPI와 정확히 일치하는
  files.api.ts 기반으로 배치 업로드 헬퍼 신규 작성 (files.ts의 uploadCourseImages는
  스키마에 없는 directory 필드를 보내 사용하지 않음)
- 최종 등록: local-recommendation의 buildCourseRequest.ts와 동일한 enum
  매핑으로 요청 페이로드 구성, 기존 createLocalRecommendation(POST /courses)을
  그대로 재사용해 새 엔드포인트 없이 연동. 성공 시 코스 목록 쿼리
  invalidate + 토스트 + /admin/courses 이동, 실패 시 에러 메시지 유지
- 관리자 행사 등록 때와 동일한 "reset 전에 navigate" 레이스 버그를 다시
  만들었다가 실제 클릭 테스트로 발견해 수정 (reset은 다음 등록 시작할 때
  FAB에서만 호출)
- 더 이상 쓰지 않는 Mock 데이터 파일 삭제

* fix: 관리자 코스 등록 API 연동 코드리뷰 반영

- 행사 검색: 디바운스 대기 중 이전 검색 결과가 남아있던 문제 수정
- 행사/장소/지역 검색 상태 메시지에 전역 스케일 적용
- 장소 검색 로딩/에러 상태를 결과없음과 구분해서 표시
- 지역 검색 API 실패 시 로딩/에러 안내 추가

* fix: 코스 등록 성공 시 상세페이지로 이동하도록 수정

- /admin/courses(목록) 대신 실제 등록된 코스의 상세페이지
  (/yeogido-course/detail/:courseId)로 이동
- local-recommendation의 실제 등록 흐름과 동일한 패턴
junehuk and others added 5 commits August 7, 2026 15:54
* feat: 소상공인 사업자 인증 API 연동

제출이 화면만 이동하던 것을 POST /users/business-verify 연동으로 바꿨다.
요청 필수값인 place(카카오 장소)와 상호명을 받을 UI가 없어 함께 추가했다.

- 사업장 주소 필드를 카카오 장소 검색 선택형으로 교체하고 상호명 필드 추가
- 장소 주소에서 시·도를 뽑아 /regions 광역 지역과 매칭해 regionId 해석
- 사업자등록번호 하이픈 제거, 증빙 이미지는 presigned 업로드 후 키 전달
- 인증 성공 시 auth.store의 role을 갱신해 홍보 권한을 즉시 연다
- 프로필 사업장 목록을 GET /users/me/businesses 응답으로 교체

* feat: 사업자등록번호 입력 자동 하이픈 및 자리수 안내 추가

숫자만 입력해도 123-45-67890 형태가 되도록 입력 시점에 정형화하고,
10자리를 채우기 전까지 안내 문구를 보여준다.

- 숫자가 아닌 문자는 무시하고 10자리를 넘으면 잘라내 붙여넣기도 처리
- 지우는 방향에서 하이픈이 남지 않도록 자리수 기준으로 다시 조립
- 입력 전에는 안내를 띄우지 않고 값이 있고 미완성일 때만 노출
- 모바일 숫자 키패드를 위해 inputMode를 numeric으로 지정

* style: 상호명 필드 비활성 처리 및 장소 변경 버튼 크기 조정

상호명은 선택한 카카오 장소명을 그대로 쓰므로 직접 입력을 막고,
고칠 수 없다는 것이 보이도록 입력 영역만 비활성 스타일로 바꿨다.

- 입력 영역 배경과 텍스트를 흐리게 처리하고 라벨은 또렷하게 유지
- 장소를 지울 때 상호명도 함께 비워 이전 장소명이 남지 않게 수정
- 입력 경로가 없어진 상호명 변경 콜백 제거
- 장소 카드의 변경 버튼 글자를 장소명과 같은 크기로 확대

* fix: 사업장 regionId를 광역 대신 시군구로 해석하도록 수정

홍보 목록 운영 데이터를 보면 부산 수영구 사업장이 regionId 31,
대전 동구 사업장이 74를 쓴다. 각각 광역인 27, 72가 아니다.
홍보글의 지역은 인증된 사업장의 Place에서 오므로 PlaceRequest는
시군구 단위를 받아야 한다. 명세서 예시의 광역 ID는 실제와 다르다.

- 광역 해석과 시군구 해석을 분리하고 제출 시 두 단계로 좁힘
- 하위 지역이 없는 세종 제주 강원은 광역 ID로 폴백
- 경기는 일반구 없이 시군까지만 있어 두 번째 토큰이 그대로 맞는다

* feat: 사업자 인증 성공 모달 추가 및 소상공인 프로필을 목록 API로 연동

인증 성공 시 바로 화면을 옮기지 않고 완료 모달을 띄운 뒤 프로필로 보낸다.
프로필은 임시 미리보기 경로 대신 사업장 목록 조회 결과로 스스로 채운다.

- 취소할 수 없는 결과라 버튼 하나만 두고 Esc와 배경 탭도 확인과 동일하게 처리
- 인증 폼을 히스토리에서 치워 뒤로 가기로 재제출하는 경로를 막음
- 모달 치수와 타이포를 로그인 모달 및 앱 전역 규칙에 맞춤
- 프로필 화면을 BusinessInfoResponse 하나로 통일하고 임시 오버라이드 제거
- 임시 미리보기 경로와 그 전용 매퍼, 타입, 테스트 삭제

* fix: 국세청 연동 장애 시 재시도를 안내하는 문구로 교체

BUSINESS_VERIFY5001은 간헐적인 외부 API 장애로 발생해 사용자 입력과 무관하다.
서버 문구는 원인을 내부 사정으로 설명할 뿐 다음 행동을 알려주지 않아,
일시적 오류임을 밝히고 다시 시도하도록 안내한다.

- 폐업 미존재와 정보 불일치는 사용자가 할 일이 달라 서버 문구를 그대로 노출

* refactor: 권한 판단을 저장된 role 대신 내 프로필 조회로 이전

로그인 응답에 role이 없어 auth.store에 저장한 값은 재로그인 시점에 비워졌다.
그대로 두면 인증을 마친 사용자가 다시 로그인했을 때 소상공인 권한이 닫힌다.
백엔드도 토큰의 role이 아니라 userId로 DB를 조회해 인가하므로 같은 출처를 본다.

- auth.store에서 role을 제거하고 저장된 값을 떨어뜨리는 migrate 추가
- GET /users/me 연동과 useMyProfile, useIsBusinessUser 추가
- 조회 전이나 실패 시 권한이 열리지 않도록 false로 기울임
- 인증 성공 시 응답 role을 캐시에 먼저 반영하고 무효화로 서버 값 확정

* fix: 코드 리뷰 반영 - 계정별 캐시, 지역 폴백 제한, 모달 포커스, 여백 스케일

- 내 프로필과 사업장 조회 query key에 userId를 포함해 계정별로 분리했다.
  로그아웃이 쿼리 캐시를 비우지 않아, 다른 계정으로 로그인하면 재조회가
  끝나기 전까지 이전 계정의 사업장과 대표자명이 그대로 보였다.
- 하위 지역 목록이 있는데 매칭에 실패하면 광역 ID로 폴백하지 않고 멈춘다.
  그대로 보내면 사업장이 시군구가 아닌 광역에 조용히 묶였다. 광역 폴백은
  세종처럼 하위 지역이 아예 없는 경우로 제한한다.
- 성공 모달이 열릴 때 확인 버튼으로 포커스를 옮기고 탭을 가둔다.
  aria-modal은 포커스를 옮겨주지 않아 배경 버튼에 남아 있었다.
- 입력 필드 좌우 여백을 고정 픽셀에서 스케일 비례로 바꾸고, 검색 아이콘을
  없애면서 쓸모없어진 오른쪽 여백을 제거했다.

* fix: 성공 모달 포커스가 밖으로 새지 않도록 보강

Tab만 막고 있어 키 이벤트를 거치지 않는 경로로 포커스가 빠져나갔다.
주소창을 거쳐 돌아오거나 스크린 리더가 커서를 옮기는 경우가 그렇다.

- focusin을 감시해 다이얼로그 밖으로 나간 포커스를 확인 버튼으로 끌어옴
- 닫힐 때 열기 전 포커스 위치로 되돌림
- onConfirm을 ref로 옮겨 부모 리렌더마다 이펙트가 재실행되지 않게 함
- 스크롤 잠금과 포커스 처리를 별도 이펙트로 분리

* fix: 프로필 사는지역에 사업장 주소를 넣지 않도록 수정

사는지역은 가입할 때 고른 거주 지역이라 사업장 주소와 의미가 다르다.
사업장이 여럿이면 어느 쪽을 보여줄지 기준도 없었다.
사업장 주소는 아래 내 사업장 목록이 이미 보여준다.

- 거주 지역 값이 붙기 전까지는 빈 상태로 둔다
- 쓰이지 않게 된 businessAddress prop 제거
* feat: region-info에 여기도/우리동네 인기 코스 섹션 분리 추가

- 코스 목록 API(LOCAL+regionId 필터 지원)로 교체, usePopularCourses는
  LOCAL courseType에서 지역 필터를 지원하지 않아(COURSE4005) 사용 불가
- RegionCourseSection을 courseType/title/searchPath를 받는 범용
  컴포넌트로 변경하고 UI를 /yeogido-course, /local-course 홈과 동일한
  ContentCard 가로 스크롤 방식으로 통일
- "{지역}, 여기도 가볼까?"(OFFICIAL) / "{지역}, 우리 동네 어때?"(LOCAL)
  두 섹션을 나란히 렌더링

* fix: 인기 코스 섹션 코드리뷰 반영

- import 확장자 제거, 스크롤 여백을 다른 치수처럼 scale 곱 방식으로 통일
- 로딩 스켈레톤 개수를 REGION_COURSE_PREVIEW_COUNT 기준으로 생성
- 여기도/우리동네 검색 경로를 routes.ts의 COURSE_SEARCH_PATH 상수로 공통화
- tagDefinitions 순서를 Figma 순서(게임/음악/술/스포츠)로 정렬
- KeywordSelectionSection에 4번째 줄 추가 (우리동네/여기도/행사 등록
  세 화면이 이 컴포넌트를 공유해서 한 번에 반영됨)
- TagChip: 44px 터치 타깃은 유지하되 문서 흐름상 높이만 음수 마진으로
  아이콘 높이(25px)에 맞춰, 줄 간격이 Figma와 동일한 8px로 보이도록 수정
* docs: add local course detail mock data design

* Revert "docs: add local course detail mock data design"

This reverts commit 18ab8dd.

* feat: 내가 작성한 글 페이지 구현 및 관련 컴포넌트 수정

* [FIX] 내 게시물 리뷰 피드백 반영

* [FEAT] 내 게시물 삭제 및 상세 이동 연동

* feat: 리뷰 반영

* feat: course 수정 삭제 버튼 생성
* feat: 마이페이지 프로필 조회/수정/탈퇴 API 연동

* fix: 코드래빗 리뷰 반영 (탈퇴 시 프로필 캐시 정리, 빈 payload 저장 방지, 탈퇴버튼 로딩중 숨김)

* feat: regionId 기반 지역 표시, 탈퇴계정 로그인 에러 처리, 사이드바 이메일, 프로필 사진 업로드 연동

* fix: 코드래빗 수정

---------

Co-authored-by: 윤선민 <yoonsunmin@yunseonmin-ui-MacBookAir.local>
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.

4 participants