Skip to content

Conversation

@dbstj0403
Copy link
Collaborator

@dbstj0403 dbstj0403 commented May 19, 2025

🌟 어떤 이유로 MR를 하셨나요?

  • feature 병합()
  • 버그 수정(아래에 issue #를 남겨주세요)
  • 코드 개선
  • 코드 수정
  • 배포
  • 기타(아래에 자세한 내용 기입해주세요)

📝 세부 내용 - 왜 해당 MR이 필요한지 작업 내용을 자세하게 설명해주세요

  • 마이페이지에서 자신이 작성한 리뷰 목록을 확인하고 기업 찜 토글을 할 수 있는 기능을 구현했습니다.
  • 마이페이지에서 자신이 응원한 스토리 목록을 확인할 수 있습니다.

📸 작업 화면 스크린샷

May-19-2025 17-35-03

⚠️ MR하기 전에 확인해주세요

  • 로컬테스트를 진행하셨나요?
  • 머지할 브랜치를 확인하셨나요?
  • 관련 label을 선택하셨나요?

Summary by CodeRabbit

  • 신규 기능

    • 마이페이지에서 리뷰, 찜, 응원 내역을 상세하게 확인할 수 있는 상세 페이지가 추가되었습니다.
    • 마이페이지 상세 내역에서 리뷰와 응원 항목을 각각 리스트로 확인할 수 있습니다.
    • 리뷰, 응원 항목을 클릭 시 상세 페이지로 이동할 수 있습니다.
    • 스토어 찜, 리뷰, 응원 수를 클릭하면 해당 내역 상세 페이지로 이동합니다.
  • 개선 사항

    • 마이페이지 로딩 시 스피너 UI가 적용되어 사용자 경험이 향상되었습니다.
    • 스토리, 지원사업 등 여러 페이지의 로딩 메시지가 제거되어 UI가 간결해졌습니다.
    • 지원사업 목록 및 상세 페이지에서 스크롤바가 숨김 처리되어 깔끔한 화면을 제공합니다.
    • 지원사업 탭에서 공지사항 영역이 스와이프 가능한 캐러셀 형태로 개선되었습니다.
    • 리뷰 리스트, 스토리 캐러셀 등에서 드래그 스크롤 및 클릭 동작이 개선되었습니다.
  • 버그 수정

    • 리뷰 콘텐츠에서 border 표시 여부를 prop으로 제어할 수 있게 되어, 불필요한 구분선이 사라졌습니다.
  • 리팩터

    • 날짜 포맷 및 D-day 계산 로직이 유틸 함수로 분리되어 코드의 재사용성이 높아졌습니다.
    • 좋아요 상태 및 사용자 정보 관리가 전역 스토어(Zustand)로 통합되어 상태 관리가 일관성 있게 개선되었습니다.
  • 기타

    • 사용되지 않는 파일 및 주석이 대거 정리되어 코드가 간결해졌습니다.
    • 에러 페이지가 제거되고, 라우터에서 관련 설정이 삭제되었습니다.

@dbstj0403 dbstj0403 requested a review from shroqkf May 19, 2025 08:38
@dbstj0403 dbstj0403 self-assigned this May 19, 2025
@dbstj0403 dbstj0403 added the Feat Improvements or additions to documentation label May 19, 2025
@vercel
Copy link

vercel bot commented May 19, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
morak ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 19, 2025 8:38am

@coderabbitai
Copy link

coderabbitai bot commented May 19, 2025

Walkthrough

이번 변경 사항은 마이페이지 상세(리뷰/응원) 기능 추가, 관련 API 및 쿼리 훅 도입, 상태 관리(Zustand) 스토어 신설, 일부 불필요 파일 삭제, 여러 컴포넌트의 주석 및 불필요 코드 정리, 로딩 메시지 UI 개선, 날짜 유틸리티 함수 분리, 라우터 및 상세 페이지 경로 추가 등입니다.

Changes

파일/경로 요약 변경 내용 요약
src/apis/myPage/getDetail.js, src/apis/myPage/queries.js 마이페이지 상세 조회 관련 API 함수(getReviews, getCheers) 및 React Query 쿼리 훅(useGetReviews, useGetCheers) 신설
src/apis/review/getPresignedUrl.js, src/apis/review/likeToggle.js, src/apis/review/postImageKey.js, src/apis/review/uploadImageToS3.js 리뷰 관련 presigned URL, 좋아요 토글, 이미지 키 등록, S3 업로드 API 함수 파일 삭제
src/constants/iconMap.js, src/constants/map/socialEnterprise.js, src/pages/error/ErrorPage.jsx 아이콘 매핑, 샘플 장소 데이터, 에러 페이지 컴포넌트 파일 삭제
src/pages/myPageDetail/MyPageDetailPage.jsx, src/pages/myPageDetail/components/ReviewItem.jsx, src/pages/myPageDetail/components/StoryItem.jsx 마이페이지 상세 페이지 및 하위 리뷰/스토리 아이템 컴포넌트 신규 추가 및 내보내기
src/pages/myPage/MyPage.jsx Spinner 및 유저 정보 스토어 연동, 프로필 데이터 갱신, 카운트 클릭 시 상세 페이지 이동 로직 추가, 로딩 UI 개선
src/store/useLikeStore.js, src/store/userInfoStore.js 회사별 좋아요 상태 및 유저 정보 상태 관리용 Zustand 스토어 신설 및 내보내기
src/pages/support/utils/dateFunc.js 날짜 포맷 및 D-day 계산 유틸리티 함수 분리 및 내보내기
src/pages/support/components/FOACard.jsx, src/pages/support/components/FOAItem.jsx 날짜 관련 내부 함수 제거, 외부 유틸리티 함수(import)로 대체
src/pages/support/components/CompanyTab.jsx 공지사항 수평 스크롤 → Swiper 캐러셀로 교체, Spinner 적용, 드래그 훅 제거
src/pages/map/components/review/ReviewContent.jsx hasBorder prop 추가(디폴트 true), item 접근 옵셔널 체이닝 적용, 레이아웃 조건부 스타일 적용
src/pages/review/StoreReviewPage.jsx 뒤로가기 동작 커스텀(handleBack 함수), 로딩 메시지 제거
src/routes/router.jsx 에러 페이지 경로 및 import 제거, 마이페이지 상세(/mypage/detail) 경로 및 컴포넌트 추가
src/pages/story/components/carousel/StoryCarousel.jsx 드래그 스크롤 커스텀 훅 적용, 클릭/드래그 구분, pointer-events 등 UX 개선
src/pages/support/SupportListPage.jsx, src/pages/support/SupportPage.jsx 메인 컨테이너에 scrollbar-hide 클래스 추가(스크롤바 숨김)
src/pages/support/SupportItemPage.jsx, src/pages/support/components/step/Step6.jsx, src/pages/story/StoryPage.jsx, src/pages/story/components/StoryDetail.jsx, src/pages/story/components/carousel/BestStoryCarousel.jsx 로딩 오버레이에서 "잠시만 기다려주세요…" 텍스트 제거, Spinner만 표시
src/pages/map/components/picker/DatePickerSheet.jsx, src/pages/map/components/picker/Picker.jsx, src/pages/map/components/picker/TimePickerSheet.jsx, src/components/common/HaveToLoginModal.jsx, src/components/layout/BottomTab.jsx JSX 내 주석 삭제, 기능 변화 없음
src/components/layout/LayoutWithTab.jsx useLocation, useEffect 활용하여 pathname을 sessionStorage에 prevPath로 저장
src/pages/map/components/review/ReviewList.jsx useNavigate import 및 사용 코드, 일부 주석 삭제(기능 변화 없음)
src/pages/review/components/PlaceInfo.jsx 좋아요(빈 하트) 아이콘 이미지 경로 변경
src/styles/spinner.css 파일 상단 주석 및 공백 제거(스타일 자체 변화 없음)
src/pages/story/components/content/SlideContent.jsx JSX 내 주석 및 불필요 코드 제거, 제목 렌더링 포맷만 변경(기능 변화 없음)

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (9)
src/store/userInfoStore.js (1)

3-17: Zustand 스토어의 상태 관리 개선

Zustand 스토어 구현은 잘 되어 있으나, 몇 가지 개선 사항이 있습니다.

  1. 객체를 통째로 전달받는 방식으로 개선:
const useUserInfoStore = create((set) => ({
  userInfo: {
    name: "",
    address: "",
    profileColor: "",
  },
-  setUserInfo: (name, address, profileColor) =>
+  setUserInfo: (userInfo) =>
    set(() => ({
      userInfo: {
-        name,
-        address,
-        profileColor,
+        ...userInfo,
      },
    })),
}));

이렇게 변경하면 사용하는 컴포넌트에서 다음과 같이 간결하게 호출할 수 있습니다:

// 현재: setUserInfo(name, address, profileColor)
// 개선: setUserInfo({ name, address, profileColor })
  1. 부분 업데이트를 지원하도록 개선할 수도 있습니다:
setUserInfo: (userInfo) =>
  set((state) => ({
    userInfo: {
+     ...state.userInfo,
      ...userInfo,
    },
  })),
src/store/useLikeStore.js (1)

3-12: 회사 좋아요 상태 관리 개선 필요

Zustand를 활용한 좋아요 상태 관리 구현은 잘 되어 있으나, 몇 가지 개선 사항이 있습니다.

  1. companyId가 숫자인 경우(관련 코드 스니펫에서 Number(companyIdParam) 사용 확인), 객체 키로 사용될 때 문자열로 변환됩니다. 이 과정에서 타입 일관성을 유지하기 위한 처리가 필요할 수 있습니다.

  2. 좋아요 토글 기능을 추가하면 사용성 개선:

const useLikeStore = create((set) => ({
  likedMap: {},
  setLike: (companyId, liked) =>
    set((state) => ({
      likedMap: {
        ...state.likedMap,
        [companyId]: liked,
      },
    })),
+  toggleLike: (companyId) =>
+    set((state) => ({
+      likedMap: {
+        ...state.likedMap,
+        [companyId]: !state.likedMap[companyId],
+      },
+    })),
}));
  1. 상태 지속성 고려: 새로고침 후에도 좋아요 상태를 유지하기 위해 localStorage나 sessionStorage와의 연동을 고려할 수 있습니다.
src/apis/myPage/queries.js (1)

1-3: 중복 import 개선 필요

getReviews와 getCheers 함수를 중복으로 import하고 있습니다.

다음과 같이 중복 import를 제거하는 것이 좋습니다:

import { useQuery } from "@tanstack/react-query";
-import { getReviews } from "@/apis/myPage/getDetail";
-import { getCheers } from "@/apis/myPage/getDetail";
+import { getReviews, getCheers } from "@/apis/myPage/getDetail";
src/pages/myPageDetail/components/StoryItem.jsx (1)

14-15: item 속성에 일관된 옵셔널 체이닝 적용이 필요합니다.

이미지 URL을 사용할 때는 옵셔널 체이닝을 적용하지 않았지만, 24번 줄에서는 item?.storyLikes와 같이 옵셔널 체이닝을 사용하고 있습니다. 일관성을 위해 모든 item 속성 접근에 옵셔널 체이닝을 적용하는 것이 좋습니다.

-          src={item.imageUrl}
+          src={item?.imageUrl}
src/pages/myPageDetail/components/ReviewItem.jsx (2)

16-23: 사용되지 않는 더미 데이터를 제거하세요.

현재 코드에 정의된 dummyReviewItem 객체는 실제로 컴포넌트 내에서 사용되지 않고 있습니다. 불필요한 코드를 제거하여 코드베이스를 깔끔하게 유지하는 것이 좋습니다.

-const dummyReviewItem = {
-  name: "김소영",
-  profileColor: "pink",
-  temperature: 74.6,
-  reviewContent:
-    "제품 품질도 후드러고 청건하게 잘 관리되어 있어요. 다시 방문하고 싶어요!",
-  reviewCategories: ["GOOD_QUALITY", "CLEAN", "REVISIT"],
-};

107-122: 접근성 개선: 좋아요 버튼에 aria 속성 추가

현재 좋아요 버튼에는 접근성을 위한 aria 속성이 없습니다. 스크린 리더 사용자를 위해 버튼에 aria 속성을 추가하는 것이 좋습니다.

<button
  onClick={(e) => {
    e.stopPropagation();
    handleLikeClick();
  }}
+  aria-label={isLoggedIn && isGloballyLiked ? "좋아요 취소" : "좋아요"}
+  aria-pressed={isLoggedIn && isGloballyLiked}
>
  <img
    src={
      isLoggedIn && isGloballyLiked
        ? "/svgs/storeReview/fullHeartIcon.svg"
        : "/svgs/Ic_Heart-Empty.svg"
    }
    alt="좋아요 버튼"
    className="w-6 h-6"
  />
</button>
src/pages/support/components/CompanyTab.jsx (1)

38-52: Swiper 구성이 잘 설정되어 있으나 일부 속성에 대한 설명이 필요합니다.

Swiper 구성이 자동 재생, 루프, 마우스 호버 시 일시 정지 등 다양한 기능을 포함하고 있어 좋습니다. 그러나 threshold: 4와 같은 일부 속성의 목적을 주석으로 설명하면 더 좋을 것 같습니다.

<Swiper
  modules={[Autoplay, Pagination]}
  spaceBetween={12}
  slidesPerView="auto"
  loop={true}
  autoplay={{
    delay: 3000,
    disableOnInteraction: true,
    pauseOnMouseEnter: true,
  }}
  preventClicks={true}
  preventClicksPropagation={true}
-  threshold={4}
+  threshold={4} // 스와이프 인식을 위한 최소 이동 거리 설정
  className="w-full px-4"
>
src/pages/myPageDetail/MyPageDetailPage.jsx (2)

58-65: 불필요한 Fragment를 제거하세요.

리뷰 아이템을 렌더링하는 코드에서 불필요한 Fragment(<></>)가 사용되고 있습니다. 내부에 하나의 요소만 있으므로 Fragment를 제거해도 됩니다.

{kind === "리뷰" && (
-  <>
-    {reviewsData?.map((item, idx) => (
-      <ReviewItem data={item} key={idx} />
-    ))}
-  </>
+  reviewsData?.map((item, idx) => (
+    <ReviewItem data={item} key={idx} />
+  ))
)}

22-27: 뒤로 가기 버튼의 접근성을 개선하세요.

현재 뒤로 가기 버튼은 이미지만 사용하고 있어 스크린 리더 사용자에게 접근하기 어려울 수 있습니다. 버튼 요소를 사용하고 적절한 aria-label을 추가하는 것이 좋습니다.

-<div className="mt-10 px-5 mb-4" onClick={() => navigate(-1)}>
-  <img src="/svgs/myPage/backIcon.svg" className="cursor-pointer" />
-</div>
+<button 
+  className="mt-10 px-5 mb-4" 
+  onClick={() => navigate(-1)}
+  aria-label="뒤로 가기"
+>
+  <img src="/svgs/myPage/backIcon.svg" alt="" />
+</button>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f462c4 and 87660b9.

⛔ Files ignored due to path filters (12)
  • public/images/1.jpeg is excluded by !**/*.jpeg
  • public/images/2.jpeg is excluded by !**/*.jpeg
  • public/images/kakao_login_large_wide.png is excluded by !**/*.png
  • public/images/sample1.jpeg is excluded by !**/*.jpeg
  • public/images/sample2.jpeg is excluded by !**/*.jpeg
  • public/images/sample3.jpeg is excluded by !**/*.jpeg
  • public/images/sample4.jpeg is excluded by !**/*.jpeg
  • public/svgs/myPage/backIcon.svg is excluded by !**/*.svg
  • public/svgs/myPage/cheer.svg is excluded by !**/*.svg
  • public/svgs/myPage/heart.svg is excluded by !**/*.svg
  • public/svgs/myPage/noResult.svg is excluded by !**/*.svg
  • public/svgs/myPage/review.svg is excluded by !**/*.svg
📒 Files selected for processing (42)
  • src/apis/myPage/getDetail.js (1 hunks)
  • src/apis/myPage/queries.js (1 hunks)
  • src/apis/review/getPresignedUrl.js (0 hunks)
  • src/apis/review/likeToggle.js (0 hunks)
  • src/apis/review/postImageKey.js (0 hunks)
  • src/apis/review/postRecipt.js (0 hunks)
  • src/apis/review/postReview.js (1 hunks)
  • src/apis/review/uploadImageToS3.js (0 hunks)
  • src/components/common/HaveToLoginModal.jsx (0 hunks)
  • src/components/layout/BottomTab.jsx (1 hunks)
  • src/components/layout/LayoutWithTab.jsx (1 hunks)
  • src/constants/iconMap.js (0 hunks)
  • src/constants/map/socialEnterprise.js (0 hunks)
  • src/pages/error/ErrorPage.jsx (0 hunks)
  • src/pages/map/components/picker/DatePickerSheet.jsx (0 hunks)
  • src/pages/map/components/picker/Picker.jsx (0 hunks)
  • src/pages/map/components/picker/TimePickerSheet.jsx (0 hunks)
  • src/pages/map/components/review/ReviewContent.jsx (2 hunks)
  • src/pages/map/components/review/ReviewList.jsx (1 hunks)
  • src/pages/myPage/MyPage.jsx (4 hunks)
  • src/pages/myPageDetail/MyPageDetailPage.jsx (1 hunks)
  • src/pages/myPageDetail/components/ReviewItem.jsx (1 hunks)
  • src/pages/myPageDetail/components/StoryItem.jsx (1 hunks)
  • src/pages/review/StoreReviewPage.jsx (2 hunks)
  • src/pages/review/components/PlaceInfo.jsx (1 hunks)
  • src/pages/story/StoryPage.jsx (0 hunks)
  • src/pages/story/components/StoryDetail.jsx (0 hunks)
  • src/pages/story/components/carousel/BestStoryCarousel.jsx (0 hunks)
  • src/pages/story/components/carousel/StoryCarousel.jsx (2 hunks)
  • src/pages/story/components/content/SlideContent.jsx (1 hunks)
  • src/pages/support/SupportItemPage.jsx (0 hunks)
  • src/pages/support/SupportListPage.jsx (1 hunks)
  • src/pages/support/SupportPage.jsx (1 hunks)
  • src/pages/support/components/CompanyTab.jsx (2 hunks)
  • src/pages/support/components/FOACard.jsx (1 hunks)
  • src/pages/support/components/FOAItem.jsx (1 hunks)
  • src/pages/support/components/step/Step6.jsx (0 hunks)
  • src/pages/support/utils/dateFunc.js (1 hunks)
  • src/routes/router.jsx (2 hunks)
  • src/store/useLikeStore.js (1 hunks)
  • src/store/userInfoStore.js (1 hunks)
  • src/styles/spinner.css (0 hunks)
💤 Files with no reviewable changes (18)
  • src/pages/story/StoryPage.jsx
  • src/pages/story/components/StoryDetail.jsx
  • src/pages/story/components/carousel/BestStoryCarousel.jsx
  • src/styles/spinner.css
  • src/apis/review/postRecipt.js
  • src/components/common/HaveToLoginModal.jsx
  • src/pages/support/SupportItemPage.jsx
  • src/pages/map/components/picker/TimePickerSheet.jsx
  • src/pages/map/components/picker/Picker.jsx
  • src/pages/support/components/step/Step6.jsx
  • src/apis/review/postImageKey.js
  • src/pages/map/components/picker/DatePickerSheet.jsx
  • src/pages/error/ErrorPage.jsx
  • src/apis/review/likeToggle.js
  • src/apis/review/getPresignedUrl.js
  • src/constants/map/socialEnterprise.js
  • src/apis/review/uploadImageToS3.js
  • src/constants/iconMap.js
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/apis/review/postReview.js (3)
src/pages/review/StoreReviewPage.jsx (1)
  • companyId (15-15)
src/pages/writeReview/components/WriteText.jsx (2)
  • companyId (15-15)
  • text (13-13)
src/pages/writeReview/components/SelectTag.jsx (1)
  • companyId (14-14)
src/components/layout/LayoutWithTab.jsx (2)
src/pages/myPageDetail/MyPageDetailPage.jsx (1)
  • location (11-11)
src/components/layout/BottomTab.jsx (1)
  • location (6-6)
src/store/useLikeStore.js (2)
src/pages/myPageDetail/components/ReviewItem.jsx (1)
  • useLikeStore (35-35)
src/pages/review/StoreReviewPage.jsx (1)
  • companyId (15-15)
src/store/userInfoStore.js (2)
src/pages/myPageDetail/components/ReviewItem.jsx (1)
  • useUserInfoStore (33-33)
src/pages/myPage/MyPage.jsx (1)
  • useUserInfoStore (33-33)
src/pages/support/components/CompanyTab.jsx (3)
src/pages/support/components/FOACard.jsx (2)
  • navigate (5-5)
  • FOACard (4-52)
src/apis/announcement/queries.js (2)
  • useGetAnnouncement (5-10)
  • useGetAnnouncement (5-10)
src/components/common/Spinner.jsx (1)
  • Spinner (3-9)
src/apis/myPage/queries.js (1)
src/apis/myPage/getDetail.js (4)
  • getReviews (3-6)
  • getReviews (3-6)
  • getCheers (8-11)
  • getCheers (8-11)
🪛 Biome (1.9.4)
src/pages/myPageDetail/MyPageDetailPage.jsx

[error] 62-68: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

🔇 Additional comments (40)
src/components/layout/BottomTab.jsx (1)

6-6: 주석 제거 후의 코드가 더 깔끔해 보입니다.

불필요한 인라인 주석을 제거하여 코드 가독성이 향상되었습니다. 코드의 기능적인 변경 없이 깔끔하게 정리된 점이 좋습니다.

src/components/layout/LayoutWithTab.jsx (1)

3-4: 이전 경로 저장 기능 추가로 내비게이션 개선됨

현재 경로를 sessionStorage에 'prevPath'로 저장하는 기능이 적절하게 구현되었습니다. 이 변경은 다른 컴포넌트에서 뒤로가기 기능을 더 정확하게 처리할 수 있도록 해줍니다.

Also applies to: 7-11

src/pages/review/StoreReviewPage.jsx (2)

27-34: 이전 경로에 따른 조건부 뒤로가기 처리 추가

이전 경로에 따라 적절한 네비게이션 로직을 구현한 점이 좋습니다. /writeReview에서 왔을 경우 루트로 이동하고, 그 외에는 이전 페이지로 돌아가도록 구현된 부분이 사용자 경험에 도움이 됩니다.


58-58: 이전 하드코딩된 경로 대신 조건부 처리 함수 사용

기존의 하드코딩된 경로(/) 대신 handleBack 함수를 사용하여 더 유연한 네비게이션 처리를 구현했습니다. 이전 경로를 고려한 네비게이션은 사용자 경험을 개선하는 데 도움이 됩니다.

src/pages/support/SupportListPage.jsx (1)

10-10: UI 일관성: 스크롤바 숨김 스타일 적용
SupportPage.jsx와 동일하게 메인 컨테이너에 scrollbar-hide 클래스를 추가하여 스크롤바를 숨기고 스크롤 기능은 유지하도록 하였습니다. 의도한 대로 동작하는지 확인되었으며, 사용자 경험 관점에서도 깔끔한 레이아웃을 제공합니다.

src/pages/support/SupportPage.jsx (1)

39-39: UI 일관성: 스크롤바 숨김 클래스 추가
컨테이너에 scrollbar-hide 클래스를 추가하여 스크롤바를 감추면서도 스크롤이 가능하도록 처리하였습니다. SupportListPage와 같은 스타일 패턴이 적용되어 일관성이 확보되었습니다.

src/pages/support/utils/dateFunc.js (2)

8-20: calculateDday 함수 구현이 잘 되었습니다

날짜 차이를 계산하여 D-day 형식으로 반환하는 기능이 적절하게 구현되었습니다. 입력값 검증, 시간 성분 제거를 통한 일자만 비교, 그리고 음수 일수에 대한 처리까지 고려되었습니다.


1-6: ⚠️ Potential issue

날짜 포맷팅 함수의 구조적 문제 수정 필요

날짜 문자열을 한국어 형식("MM월 DD일까지")으로 변환하는 formatEndDate 함수에서 정규식 매칭 그룹과 배열 구조분해 간에 불일치가 있습니다.

다음과 같이 수정해 주세요:

export const formatEndDate = (raw) => {
  const match = raw?.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if (!match) return raw;
- const [, , mm, dd] = match;
+ const [, yyyy, mm, dd] = match;
  return `${parseInt(mm, 10)}월 ${parseInt(dd, 10)}일까지`;
};

현재 구현에서는 배열 구조분해가 정규식 캡처 그룹과 일치하지 않습니다. 정규식은 3개의 캡처 그룹(년, 월, 일)을 생성하며, 이는 match[1], match[2], match[3]으로 접근할 수 있습니다. 따라서 구조분해는 [, yyyy, mm, dd]로 수정해야 합니다.

Likely an incorrect or invalid review comment.

src/pages/support/components/FOAItem.jsx (2)

2-2: 날짜 유틸리티 함수 임포트 리팩토링이 잘 되었습니다

날짜 포맷팅 로직을 별도의 유틸리티 모듈로 분리하여 가져오는 방식으로 수정한 것은 매우 좋은 리팩토링입니다. 이를 통해 코드 재사용성과 일관성이 향상되었습니다.


6-7: 날짜 함수 호출 부분이 잘 적용되었습니다

유틸리티 모듈에서 가져온 formatEndDatecalculateDday 함수를 올바르게 사용하고 있습니다. 이전 인라인 구현을 모듈 함수로 대체하여 코드 가독성과 유지보수성이 향상되었습니다.

src/pages/support/components/FOACard.jsx (4)

2-2: 날짜 유틸리티 함수 임포트 리팩토링이 잘 되었습니다

날짜 포맷팅 로직을 별도의 유틸리티 모듈로 분리하여 가져오는 방식으로 수정한 것은 좋은 리팩토링입니다. 이를 통해 코드 재사용성과 일관성이 향상되었습니다.


7-8: 날짜 함수 호출 부분이 잘 적용되었습니다

유틸리티 모듈에서 가져온 formatEndDatecalculateDday 함수를 올바르게 사용하고 있습니다. 이전 인라인 구현을 모듈 함수로 대체하여 코드 가독성과 유지보수성이 향상되었습니다.


10-17: 이벤트 핸들러 리팩토링이 잘 되었습니다

인라인 익명 함수를 명명된 handleClick 함수로 리팩토링한 것은 좋은 개선입니다. 이를 통해 코드 가독성이 향상되고 이벤트 핸들링 로직이 명확해졌습니다.


21-21: 이벤트 핸들러 적용이 잘 되었습니다

명명된 handleClick 함수를 onClick 이벤트에 올바르게 적용하였습니다.

src/pages/story/components/content/SlideContent.jsx (1)

39-41: 코드 가독성이 향상되었습니다!

제목을 표시하는 JSX 요소를 여러 줄로 포맷팅하여 가독성이 향상되었습니다. 기능적인 변경 없이 코드 스타일만 개선된 좋은 변경입니다.

src/pages/map/components/review/ReviewList.jsx (2)

3-3: import 변경 - 네비게이션 로직 개선

useNavigate 대신 Link를 사용하도록 변경되었습니다. 이는 리뷰 더보기 버튼에 직접 링크를 적용하는 방식으로 네비게이션 로직을 단순화한 것으로 보입니다.


11-11: 주석 제거로 코드 정리

불필요한 주석이 제거되어 코드가 깔끔해졌습니다.

src/apis/review/postReview.js (2)

7-8: 불필요한 주석 제거

파라미터 라인에서 주석이 제거되어 코드가 더 깔끔해졌습니다.


15-16: 에러 핸들링 주석 정리

에러 처리 부분의 불필요한 주석이 제거되었습니다.

src/pages/review/components/PlaceInfo.jsx (1)

163-163: 아이콘 경로 통일성 개선

빈 하트 아이콘 경로가 /svgs/Ic_Heart-Empty.svg로 변경되었습니다. 이는 좋아요 관련 기능의 리팩토링과 일관성을 위한 변경으로 보입니다.

src/routes/router.jsx (2)

19-19: 마이페이지 상세 페이지 컴포넌트 추가

새로운 마이페이지 상세 기능을 위한 컴포넌트를 import 했습니다.


45-48: 마이페이지 상세 라우트 추가

사용자가 작성한 리뷰와 응원한 스토리를 확인할 수 있는 마이페이지 상세 기능을 위한 새로운 라우트가 추가되었습니다. 라우트 구성이 적절하게 이루어졌습니다.

src/pages/map/components/review/ReviewContent.jsx (3)

4-4: hasBorder 프롭 추가 적용이 잘 되었습니다.

컴포넌트에 hasBorder 프롭을 추가하여 재사용성을 높인 점이 좋습니다. 기본값을 설정하여 하위 호환성도 유지했습니다.


14-18: 조건부 클래스 스타일링 적용이 잘 되었습니다.

Tailwind CSS 클래스 적용 시 hasBorder 프롭에 따라 조건부로 스타일을 적용하는 방식이 깔끔합니다. 템플릿 리터럴을 활용한 문자열 조합도 잘 되었습니다.


21-21: 옵셔널 체이닝 적용이 잘 되었습니다.

item?.nameitem?.temperature에 옵셔널 체이닝을 적용하여 undefined나 null 값 처리를 안전하게 한 점이 좋습니다.

Also applies to: 27-27

src/pages/myPage/MyPage.jsx (3)

33-39: 유저 정보 글로벌 상태 관리 적용이 잘 되었습니다.

useEffect를 사용하여 데이터가 변경될 때마다 유저 정보 스토어를 업데이트하는 로직이 잘 구현되었습니다. 다른 컴포넌트에서 유저 정보를 쉽게 접근할 수 있게 되었습니다.


84-86: 로딩 UI 개선이 잘 되었습니다.

단순 텍스트 메시지 대신 Spinner 컴포넌트를 사용하여 사용자 경험을 향상시킨 점이 좋습니다. Flex를 활용한 중앙 정렬도 적절하게 적용되었습니다.


118-124: 상호작용 기능 추가가 잘 되었습니다.

클릭 이벤트 핸들러와 cursor-pointer 스타일을 추가하여 사용자가 상호작용할 수 있는 UI 요소임을 명확하게 표시한 점이 좋습니다. 또한 상태와 함께 페이지 이동 로직이 잘 구현되었습니다.

src/pages/story/components/carousel/StoryCarousel.jsx (4)

8-9: 드래그 스크롤 기능 구현이 잘 되었습니다.

useDragScroll 훅을 사용하여 드래그 스크롤 기능을 구현한 점이 좋습니다. 드래그 상태를 추적하기 위한 ref 사용도 적절합니다.


13-25: 드래그와 클릭 이벤트 처리가 잘 구현되었습니다.

드래그 중 원치 않는 내비게이션을 방지하기 위한 로직이 잘 구현되었습니다. 마우스 이벤트를 적절히 활용하여 사용자 경험을 개선한 점이 좋습니다.


32-37: 스크롤 컨테이너 스타일 및 이벤트 핸들링이 잘 되었습니다.

스크롤 가능한 컨테이너에 적절한 스타일을 적용하고, 드래그 이벤트 핸들러를 연결한 점이 좋습니다. cursor-grab을 사용하여 드래그 가능함을 시각적으로 표시한 것도 좋은 UX 개선입니다.


48-48: pointer-events-none 적용이 잘 되었습니다.

자식 요소에 pointer-events-none을 적용하여 드래그 스크롤 기능을 방해하지 않도록 한 점이 좋습니다. 이는 사용자 경험을 원활하게 유지하는 데 도움이 됩니다.

Also applies to: 54-54, 57-57

src/pages/myPageDetail/components/ReviewItem.jsx (3)

28-37: 전역 상태와 로컬 상태 동기화 로직이 명확합니다.

좋아요 상태를 로컬(isLiked)과 전역 상태(likedMap)로 모두 관리하는 것이 잘 구현되어 있습니다. 이 방식은 컴포넌트 간에 좋아요 상태를 일관되게 유지하는 데 효과적입니다.


63-90: 좋아요 토글 핸들러가 잘 구현되어 있습니다.

좋아요 토글 핸들러가 로그인 확인, 에러 처리, 로딩 상태 관리 등을 포함하여 잘 구현되어 있습니다. 특히 try-catch-finally 패턴을 사용하여 에러 핸들링과 로딩 상태 관리가 잘 되어 있습니다.


92-129: 잘 구성된 UI 컴포넌트입니다.

ReviewContent 컴포넌트와 잘 통합되어 있고, 클릭 시 상세 페이지로 이동하는 기능이 잘 구현되어 있습니다. border와 패딩 등 디자인 요소가 일관성 있게 적용되어 있습니다.

src/pages/support/components/CompanyTab.jsx (4)

3-10: Swiper 라이브러리 통합이 잘 되어 있습니다.

수평 스크롤 컨테이너를 Swiper 캐러셀로 교체한 것은 좋은 선택입니다. 필요한 모듈과 스타일을 명확하게 가져왔으며, 재사용 가능한 스타일을 활용한 점이 좋습니다.


14-14: 구조 분해 할당을 통한 코드 개선이 좋습니다.

useGetAnnouncement 훅의 반환값에서 dataisLoading을 구조 분해 할당으로 추출하고 기본값을 설정한 것은 좋은 방식입니다.


33-37: 로딩 UI가 일관되게 개선되었습니다.

텍스트 기반 로딩 메시지 대신 Spinner 컴포넌트를 사용하여 로딩 상태를 시각적으로 표시하는 것으로 개선한 것이 좋습니다. 이는 전체 애플리케이션에서 일관된 로딩 UI를 제공합니다.


63-63: 주석을 통한 코드 구조 명확화가 좋습니다.

섹션을 구분하는 주석을 추가한 것은 코드의 가독성을 높이는 좋은 방법입니다. 개발자가 코드의 구조를 쉽게 이해할 수 있게 해줍니다.

src/pages/myPageDetail/MyPageDetailPage.jsx (1)

9-21: 조건부 데이터 fetching이 잘 구현되어 있습니다.

kind 값에 따라 필요한 데이터만 가져오도록 enabled 옵션을 사용한 것은 효율적입니다. 불필요한 API 호출을 줄이는 좋은 방법입니다.

다만, 사용하지 않는 코드에 대한 주석은 다음과 같이 TODO 형식으로 명확하게 표시하면 좋을 것 같습니다:

-//   const { data: heartsData, isLoading: isLoadingHearts } = useGetHearts({ enabled: kind === "찜" });
+// TODO: '찜' 기능 구현 시 아래 코드 주석 해제
+// const { data: heartsData, isLoading: isLoadingHearts } = useGetHearts({ enabled: kind === "찜" });

Comment on lines +1 to +11
import api from "@/apis/instance/api";

export const getReviews = async () => {
const res = await api.get("/reviews/get-all-member-reviews");
return res.data;
};

export const getCheers = async () => {
const res = await api.get("/story/member-likes");
return res.data;
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

API 요청 함수의 오류 처리 개선이 필요합니다.

API 호출 시 오류 처리 로직이 없습니다. API 요청 중 발생할 수 있는 네트워크 오류나 서버 오류에 대한 처리가 필요합니다.

다음과 같이 try-catch 구문을 추가하여 개선할 수 있습니다:

export const getReviews = async () => {
+  try {
    const res = await api.get("/reviews/get-all-member-reviews");
    return res.data;
+  } catch (error) {
+    console.error("리뷰 데이터 가져오기 실패:", error);
+    throw error;
+  }
};

export const getCheers = async () => {
+  try {
    const res = await api.get("/story/member-likes");
    return res.data;
+  } catch (error) {
+    console.error("응원 데이터 가져오기 실패:", error);
+    throw error;
+  }
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import api from "@/apis/instance/api";
export const getReviews = async () => {
const res = await api.get("/reviews/get-all-member-reviews");
return res.data;
};
export const getCheers = async () => {
const res = await api.get("/story/member-likes");
return res.data;
};
import api from "@/apis/instance/api";
export const getReviews = async () => {
try {
const res = await api.get("/reviews/get-all-member-reviews");
return res.data;
} catch (error) {
console.error("리뷰 데이터 가져오기 실패:", error);
throw error;
}
};
export const getCheers = async () => {
try {
const res = await api.get("/story/member-likes");
return res.data;
} catch (error) {
console.error("응원 데이터 가져오기 실패:", error);
throw error;
}
};
🤖 Prompt for AI Agents
In src/apis/myPage/getDetail.js lines 1 to 11, the API request functions
getReviews and getCheers lack error handling, which can cause unhandled
exceptions during network or server errors. Wrap the API calls inside try-catch
blocks to catch any errors, and handle or rethrow them appropriately to ensure
the calling code can manage failures gracefully.

Comment on lines +5 to +11
export const useGetReviews = () => {
return useQuery({ queryKey: ["userReview"], queryFn: () => getReviews() });
};

export const useGetCheers = () => {
return useQuery({ queryKey: ["userCheers"], queryFn: () => getCheers() });
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

React Query 훅 개선 필요

React Query 훅 구현이 기본적인 형태로 되어 있어, 추가적인 옵션 설정이 필요합니다.

다음과 같이 staleTime, cacheTime, 에러 처리 등 개선된 옵션을 추가하는 것이 좋습니다:

export const useGetReviews = () => {
-  return useQuery({ queryKey: ["userReview"], queryFn: () => getReviews() });
+  return useQuery({ 
+    queryKey: ["userReview"], 
+    queryFn: getReviews,
+    staleTime: 5 * 60 * 1000, // 5분
+    cacheTime: 10 * 60 * 1000, // 10분
+    retry: 1,
+    onError: (error) => {
+      console.error("리뷰 데이터 가져오기 실패:", error);
+    }
+  });
};

export const useGetCheers = () => {
-  return useQuery({ queryKey: ["userCheers"], queryFn: () => getCheers() });
+  return useQuery({ 
+    queryKey: ["userCheers"], 
+    queryFn: getCheers,
+    staleTime: 5 * 60 * 1000, // 5분
+    cacheTime: 10 * 60 * 1000, // 10분
+    retry: 1,
+    onError: (error) => {
+      console.error("응원 데이터 가져오기 실패:", error);
+    }
+  });
};

이렇게 변경하면 다음과 같은 이점이 있습니다:

  • staleTime 설정으로 불필요한 refetch 방지
  • 에러 처리 로직 개선
  • 함수 호출 방식 단순화 (화살표 함수 제거)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const useGetReviews = () => {
return useQuery({ queryKey: ["userReview"], queryFn: () => getReviews() });
};
export const useGetCheers = () => {
return useQuery({ queryKey: ["userCheers"], queryFn: () => getCheers() });
};
export const useGetReviews = () => {
return useQuery({
queryKey: ["userReview"],
queryFn: getReviews,
staleTime: 5 * 60 * 1000, // 5분
cacheTime: 10 * 60 * 1000, // 10분
retry: 1,
onError: (error) => {
console.error("리뷰 데이터 가져오기 실패:", error);
}
});
};
export const useGetCheers = () => {
return useQuery({
queryKey: ["userCheers"],
queryFn: getCheers,
staleTime: 5 * 60 * 1000, // 5분
cacheTime: 10 * 60 * 1000, // 10분
retry: 1,
onError: (error) => {
console.error("응원 데이터 가져오기 실패:", error);
}
});
};
🤖 Prompt for AI Agents
In src/apis/myPage/queries.js around lines 5 to 11, the React Query hooks
useGetReviews and useGetCheers are implemented in a basic form without
additional options. Improve these hooks by adding options such as staleTime and
cacheTime to control data freshness and caching, include error handling logic to
manage query failures gracefully, and simplify the queryFn by passing the
function reference directly instead of wrapping it in an arrow function. This
will optimize performance and enhance error management.

Comment on lines +29 to +30
<p className="h4 mt-4">이음숲, 숲과 사람을 이어주는 따뜻한 다리</p>
<p className="b5 text-gray-9 mt-1">2023년 3월 2일</p>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하드코딩된 텍스트 대신 동적 데이터 사용 권장

제목과 날짜가 하드코딩되어 있습니다. item 객체에서 관련 데이터를 가져와 동적으로 표시하는 것이 더 적절할 것 같습니다. 그렇지 않으면 모든 아이템이 동일한 제목과 날짜를 보여주게 됩니다.

-      <p className="h4 mt-4">이음숲, 숲과 사람을 이어주는 따뜻한 다리</p>
-      <p className="b5 text-gray-9 mt-1">2023년 3월 2일</p>
+      <p className="h4 mt-4">{item?.storyTitle || "제목 없음"}</p>
+      <p className="b5 text-gray-9 mt-1">{item?.createdAt || ""}</p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p className="h4 mt-4">이음숲, 숲과 사람을 이어주는 따뜻한 다리</p>
<p className="b5 text-gray-9 mt-1">2023년 3월 2일</p>
<p className="h4 mt-4">{item?.storyTitle || "제목 없음"}</p>
<p className="b5 text-gray-9 mt-1">{item?.createdAt || ""}</p>
🤖 Prompt for AI Agents
In src/pages/myPageDetail/components/StoryItem.jsx around lines 29 to 30, the
title and date are hardcoded, causing all items to display the same text. Update
the JSX to use dynamic data from the item object by replacing the static strings
with expressions that access the appropriate properties on item for the title
and date, ensuring each StoryItem renders unique content.

Comment on lines +44 to +61
useEffect(() => {
const checkLoginAndLiked = async () => {
const isAuthenticated = await useAuthStore.getState().checkAuth();
setIsLoggedIn(isAuthenticated);

if (isAuthenticated && data?.companyId) {
const likedList = await getLikedCompanies();
const liked = likedList.some(
(c) => String(c.companyId) === String(data.companyId)
);
setIsLiked(liked);
setLike(data.companyId, liked);
}
setLoading(false);
};

if (data?.companyId) checkLoginAndLiked();
}, [data?.companyId]);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

성능 최적화 필요: 좋아요 상태 확인 로직

현재 구현에서는 각 리뷰 아이템마다 getLikedCompanies() API를 호출하여 좋아요 목록 전체를 가져옵니다. 이는 리뷰 목록이 많을 경우 불필요한 API 호출이 발생할 수 있습니다.

좋아요 상태를 부모 컴포넌트에서 한 번에 가져오거나, 커스텀 훅으로 분리하여 중복 호출을 줄이는 것이 좋습니다:

-useEffect(() => {
-  const checkLoginAndLiked = async () => {
-    const isAuthenticated = await useAuthStore.getState().checkAuth();
-    setIsLoggedIn(isAuthenticated);
-
-    if (isAuthenticated && data?.companyId) {
-      const likedList = await getLikedCompanies();
-      const liked = likedList.some(
-        (c) => String(c.companyId) === String(data.companyId)
-      );
-      setIsLiked(liked);
-      setLike(data.companyId, liked);
-    }
-    setLoading(false);
-  };
-
-  if (data?.companyId) checkLoginAndLiked();
-}, [data?.companyId]);
+useEffect(() => {
+  const checkLoginAndLiked = async () => {
+    const isAuthenticated = await useAuthStore.getState().checkAuth();
+    setIsLoggedIn(isAuthenticated);
+    setLoading(false);
+  };
+
+  checkLoginAndLiked();
+}, []);
+
+// likedMap에서 좋아요 상태 가져오기
+useEffect(() => {
+  if (data?.companyId) {
+    setIsLiked(likedMap[data.companyId] ?? false);
+  }
+}, [likedMap, data?.companyId]);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/pages/myPageDetail/components/ReviewItem.jsx around lines 44 to 61, the
current code calls getLikedCompanies() inside each ReviewItem component's
useEffect, causing redundant API calls when rendering multiple items. To fix
this, move the liked companies fetching logic to the parent component or a
custom hook so that the liked list is fetched once and passed down as props or
accessed via context, thereby avoiding repeated API calls in each ReviewItem.

Comment on lines +53 to +59
{announces.map((item) => (
<SwiperSlide key={item.id} className="!w-[320px]">
<div className="w-80 h-fit">
<FOACard data={item} />
</div>
</SwiperSlide>
))}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

SwiperSlide의 너비 스타일에 주의가 필요합니다.

!w-[320px] 클래스를 사용하는 것은 Tailwind의 중요(!) 수정자를 사용하는 것으로, Swiper가 관리하는 스타일을 덮어쓸 수 있습니다. 이로 인해 의도하지 않은 레이아웃 문제가 발생할 수 있습니다.


🌐 Web query:

Tailwind CSS !important 수정자와 Swiper 라이브러리 호환성 문제

💡 Result:

Tailwind CSS의 !important 수정자와 Swiper 라이브러리 호환성 문제

문제 개요

Tailwind CSS는 자체적으로 유틸리티 클래스에 대해 !important를 지정하는 특별한 문법이 없지만, @apply나 커스텀 CSS 작성 시 !important를 활용할 수 있습니다. 반면 Swiper는 자체 스타일시트와 인라인 스타일, 내부 우선순위가 높은 CSS를 통해 컴포넌트의 레이아웃과 버튼 등 주요 UI를 제어합니다. 이 때문에 Tailwind 스타일과 Swiper 기본 스타일이 충돌하거나, 원하는 스타일 덮어쓰기가 적용되지 않는 상황이 종종 발생합니다.

대표적인 호환성 문제

  • Tailwind의 클래스가 Swiper의 내부 스타일보다 우선 적용되지 않음
  • Swiper가 인라인 스타일이나 자체 CSS로 강하게 스타일 지정(!important 포함) → Tailwind로 오버라이드 불가
  • Swiper 네비게이션/페이징 버튼 등 class="swiper-button-next" 등에서 Tailwind @apply 후에도 스타일이 적용되지 않음

해결 방법

1. Swiper 기본 CSS의 우선순위 파악 및 재정의

  • Swiper의 기본 스타일(swiper-bundle.min.css 등)이 Tailwind 클래스보다 우선순위가 높거나 !important를 사용합니다.
  • 이를 해결하려면 Tailwind를 사용한 커스텀 CSS에 !important를 직접 적용해야 합니다.
  • 예시:
    .swiper-button-next {
      @apply text-white bg-blue-500 !important;
    }
    .swiper-pagination-bullet-active {
      @apply bg-mkOrange !important;
    }
    또는 일반 CSS 방식으로도 가능:
    .swiper-button-next {
      color: white !important;
      background-color: #3b82f6 !important;
    }
    Tailwind의 @apply만으로는 Swiper의 강한 CSS를 완전히 덮어쓸 수 없으니, !important를 함께 사용해야 합니다[3][7].

2. 클래스 우선순위 및 충돌 관리

  • 클래스 충돌이나 Tailwind 클래스 병합 이슈가 있다면 tailwind-merge와 같은 도구를 활용해 클래스 우선순위를 명확히 할 수 있습니다[5].

3. Swiper 인라인 스타일 및 React에서의 스타일 적용

  • Swiper가 인라인 스타일로 속성을 부여하는 경우, CSS로는 덮어쓰기 어렵고, 필요에 따라 React prop에서 style 속성을 직접 지정해야 할 수도 있습니다[8].

4. 명확한 셀렉터 사용

  • Swiper의 네비게이션, 페이징 등 UI 요소를 커스터마이즈할 땐 Swiper에서 사용하는 정확한 클래스를 타겟팅해야 하며, CSS 선택자 우선순위(CSS Specificity)를 높이기 위해 보다 구체적인 셀렉터 사용, 혹은 CSS 파일상에서 Tailwind CSS 클래스를 Swiper 관련 CSS 아래에 위치하도록 배치하는 것이 효과적입니다[7].

결론 및 실전 팁

  • Swiper와 Tailwind CSS를 함께 사용할 때 커스텀 스타일이 Swiper 기본 스타일에 묻히는 것은 스타일 우선순위와 !important 처리 문제 때문입니다.
  • 해결을 위해선 Tailwind의 @apply와 함께 직접 !important를 명시하거나, 커스텀 CSS 파일에서 Swiper 기본 스타일을 명확하게 오버라이드해야 합니다.
  • 복잡한 클래스 병합, 우선순위 문제는 [tailwind-merge][5] 같은 유틸리티 또는 명확한 CSS 선택자 지정으로 보완하세요.

예시 코드:

/* tailwind.css 또는 별도 CSS 파일에서 */
.swiper-button-next,
.swiper-button-prev {
  @apply text-2xl text-white bg-blue-600 rounded-lg !important;
}

.swiper-pagination-bullet-active {
  @apply bg-orange-500 !important;
}

이처럼 Tailwind의 유틸리티를 @apply로 활용하면서, 반드시 !important를 추가해 Swiper의 기본 스타일을 확실히 덮어써야 원하는 디자인을 구현할 수 있습니다[3][7].

추가 참고: Tailwind CSS 및 Swiper 공식문서에서 CSS 커스터마이징 가이드를 함께 참조하세요.

Citations:


SwiperSlide 너비 제어 방식 수정 필요

src/pages/support/components/CompanyTab.jsx (53-59줄)에서

<SwiperSlide key={item.id} className="!w-[320px]"></SwiperSlide>

처럼 Tailwind의 !w-[320px] 유틸리티를 쓰면 내부적으로 !important가 붙어 Swiper가 계산하는 슬라이드 너비(inline style)를 덮어써 레이아웃이 깨질 수 있습니다.

다음 중 한 가지 방법으로 변경하세요:

  • Swiper 옵션으로 제어
    • slidesPerView, spaceBetween, breakpoints 등
  • React style prop 사용
    <SwiperSlide key={item.id} style={{ width: '320px' }}></SwiperSlide>
  • CSS 파일에서 명시적 오버라이드
    /* src/styles/swiper-overrides.css */
    .swiper-slide { width: 320px !important; }
    (별도 CSS 관리 필요)
🤖 Prompt for AI Agents
In src/pages/support/components/CompanyTab.jsx around lines 53 to 59, avoid
using Tailwind's !w-[320px] class on SwiperSlide because it applies !important
and overrides Swiper's inline width styles, causing layout issues. Instead,
control the slide width by setting the width via Swiper's options like
slidesPerView or spaceBetween, or apply the width directly using the React style
prop (e.g., style={{ width: '320px' }}), or alternatively override the width in
a CSS file with a high specificity selector and !important if necessary.

Comment on lines +67 to +71
{kind === "응원" &&
cheersData.map((item) => (
<StoryItem item={item} key={item.storyId} />
))}
</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

cheersData가 undefined일 가능성에 대한 처리가 필요합니다.

cheersData가 로딩 중이거나 undefined일 경우 .map() 메서드 호출 시 오류가 발생할 수 있습니다. 조건부 렌더링 또는 옵셔널 체이닝을 추가하는 것이 좋습니다.

{kind === "응원" &&
-  cheersData.map((item) => (
+  cheersData?.map((item) => (
    <StoryItem item={item} key={item.storyId} />
  ))}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{kind === "응원" &&
cheersData.map((item) => (
<StoryItem item={item} key={item.storyId} />
))}
</div>
{kind === "응원" &&
cheersData?.map((item) => (
<StoryItem item={item} key={item.storyId} />
))}
</div>
🤖 Prompt for AI Agents
In src/pages/myPageDetail/MyPageDetailPage.jsx around lines 67 to 71, cheersData
may be undefined, causing an error when calling .map(). To fix this, add a check
to ensure cheersData is defined before mapping, either by using optional
chaining (cheersData?.map) or by conditionally rendering only if cheersData
exists.

Comment on lines +38 to +46
{kind === "찜" && (
<div className="flex justify-between items-center px-5">
<div className="flex gap-2 items-center">
<img src={heart} className="w-8 h-8" />
<p className="h3">저장한 장소</p>
</div>
<p className="b5 text-gray-9">총 0개</p>
</div>
)}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

'찜' 기능의 구현 상태를 사용자에게 알려주세요.

'찜' 섹션의 UI는 준비되어 있지만, 기능이 아직 구현되지 않았습니다. 사용자에게 이 기능이 준비 중임을 알려주는 메시지를 추가하는 것이 좋습니다.

{kind === "찜" && (
  <div className="flex justify-between items-center px-5">
    <div className="flex gap-2 items-center">
      <img src={heart} className="w-8 h-8" />
      <p className="h3">저장한 장소</p>
    </div>
    <p className="b5 text-gray-9">총 0개</p>
  </div>
)}

+{kind === "찜" && (
+  <div className="flex flex-col gap-4 w-full bg-gray-2 p-5 min-h-screen">
+    <div className="flex justify-center items-center h-40 bg-white rounded-md">
+      <p className="b4 text-gray-6">준비 중인 기능입니다.</p>
+    </div>
+  </div>
+)}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{kind === "찜" && (
<div className="flex justify-between items-center px-5">
<div className="flex gap-2 items-center">
<img src={heart} className="w-8 h-8" />
<p className="h3">저장한 장소</p>
</div>
<p className="b5 text-gray-9">총 0개</p>
</div>
)}
{kind === "찜" && (
<div className="flex justify-between items-center px-5">
<div className="flex gap-2 items-center">
<img src={heart} className="w-8 h-8" />
<p className="h3">저장한 장소</p>
</div>
<p className="b5 text-gray-9">총 0개</p>
</div>
)}
{kind === "찜" && (
<div className="flex flex-col gap-4 w-full bg-gray-2 p-5 min-h-screen">
<div className="flex justify-center items-center h-40 bg-white rounded-md">
<p className="b4 text-gray-6">준비 중인 기능입니다.</p>
</div>
</div>
)}
🤖 Prompt for AI Agents
In src/pages/myPageDetail/MyPageDetailPage.jsx around lines 38 to 46, the UI for
the '찜' feature is present but the functionality is not implemented yet. Add a
clear message within this section to inform users that the '찜' feature is
currently under development or coming soon, so they understand the feature is
not yet available.

Copy link
Collaborator

@shroqkf shroqkf left a comment

Choose a reason for hiding this comment

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

리뷰와 응원은 알맞게 보이는데 찜 부분이 없다고 보여서 확인 부탁드립니다
스크린샷 2025-05-19 오후 5 42 56

스크린샷 2025-05-19 오후 5 43 02

@shroqkf shroqkf merged commit 12b844d into develop May 19, 2025
4 checks passed
@shroqkf shroqkf deleted the feature/logOfMypage branch May 19, 2025 08:55
@dbstj0403 dbstj0403 changed the title Feat: 마이페이지 회원이 작성한 리뷰/응원한 스토리 확인 기능 [Feat] 마이페이지 회원이 작성한 리뷰/응원한 스토리 확인 기능 May 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feat Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants