Skip to content

Conversation

@shroqkf
Copy link
Collaborator

@shroqkf shroqkf commented May 20, 2025

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

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

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

  • 마이 페이지에 찜 목록 기능을 구현했습니다.
  • 마이 페이지에 프로필 편집 기능을 구현했습니다.
  • 이야기 페이지의 응원하기 버튼 클릭 시 알림 모달 추가했습니다. (응원 취소 기능은 디자인이 알맞지 않은 것 같아 반영안했습니다.)

📸 작업 화면 스크린샷

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

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

📢 로컬 실행 시 유의사항

  • 프로필 편집 부분 디자이너분과 상의하여 한 페이지에서 프로필, 이름, 주소 편집 가능하도록 했습니다.

🚨 관련 이슈 번호

Summary by CodeRabbit

  • 신규 기능

    • 마이페이지에서 프로필 편집 페이지 및 기능이 추가되었습니다.
    • 사용자가 찜한(저장한) 장소를 볼 수 있는 "찜" 카테고리가 마이페이지 상세에 통합되었습니다.
    • 회사 상세 지도 페이지가 추가되어 회사 위치 정보를 지도에서 확인할 수 있습니다.
    • 회사 찜(하트) 목록에서 상세 정보와 거리, 찜 해제 기능을 제공합니다.
    • 스토리 상세에서 좋아요 성공 시 토스트 알림이 표시됩니다.
    • 신규 SVG 아이콘이 추가되었습니다.
  • 스타일/UX

    • 프로필 편집 및 찜/지도 관련 UI 개선 및 시각적 피드백이 추가되었습니다.
  • 버그 수정

    • 없음
  • 문서화

    • 해당 변경 사항에 대한 사용자 가이드가 추가되었습니다.

@shroqkf shroqkf self-assigned this May 20, 2025
@shroqkf shroqkf added Feat Improvements or additions to documentation Design This doesn't seem right labels May 20, 2025
@vercel
Copy link

vercel bot commented May 20, 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 21, 2025 7:27am

@coderabbitai
Copy link

coderabbitai bot commented May 20, 2025

"""

Walkthrough

이 변경 사항은 사용자 프로필 수정 기능과 "찜" 목록(하트 기능) 관련 기능을 추가 및 확장합니다. 새로운 API 함수, React Query 훅, UI 컴포넌트, 라우트가 도입되어 프로필 수정 및 찜 목록 조회·관리, 상세 지도 페이지, 토스트 알림 등이 구현되었습니다.

Changes

파일/경로 요약 변경 내용 요약
src/apis/member/postUpdateProfile.js postUpdateProfile API 함수 신설: 이름, 위치, 프로필 색상 정보를 POST 요청으로 서버에 전달하는 비동기 함수 추가.
src/apis/member/queries.js useUpdateProfile React Query mutation 훅 추가: 프로필 수정 API 연동.
src/apis/myPage/getDetail.js getHearts 함수 추가: 찜(하트) 목록을 GET 요청으로 받아오는 비동기 함수 신설.
src/apis/myPage/queries.js useGetHearts React Query 훅 추가: 찜 목록 조회용 커스텀 쿼리 훅 구현, enabled 옵션 지원.
src/pages/myPage/MyPage.jsx 프로필 섹션의 편집 버튼에 클릭 시 "/mypage/edit"로 이동하는 onClick 핸들러 추가.
src/pages/myPage/components/MyPageEdit.jsx 프로필 수정 UI 컴포넌트 신설: 이름, 위치, 색상 선택, 서울 외 거주 체크박스, 토스트 및 성공/오류 처리 포함.
src/pages/myPageDetail/MyPageDetailPage.jsx "찜" 카테고리 지원 추가: hearts 데이터 fetch, 개수 표시, 목록/빈 상태 렌더링, HeartItem 컴포넌트 활용 등 구현.
src/pages/myPageDetail/components/HeartItem.jsx HeartItem 컴포넌트 신설: 찜한 회사 정보 표시, 하트 취소(삭제) 기능, 토스트 알림, 상세 페이지 이동 등 구현.
src/pages/myPageDetail/components/MapCompanyPage.jsx MapCompanyPage 컴포넌트 신설: 회사별 지도 상세, 하트 상태 연동, 지도 마커, 바텀시트, 네비게이션 등 포함.
src/pages/story/components/StoryDetail.jsx 좋아요 성공 시 ToastModal 알림 표시 기능 추가: 상태 변수, onSuccess 핸들러, 토스트 렌더링 로직 도입.
src/routes/router.jsx /map/:companyId(MapCompanyPage), /mypage/edit(MyPageEdit) 신규 라우트 추가 및 관련 컴포넌트 import.
src/assets/svgs/modal/index.js IcError, IcWarnning SVG React 컴포넌트로 내보내기 추가: 에러 및 경고 아이콘 SVG 컴포넌트화.

Suggested reviewers

  • dbstj0403
    """

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 30th. 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.


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0dbeac1 and 671c385.

📒 Files selected for processing (1)
  • src/pages/myPage/components/MyPageEdit.jsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/myPage/components/MyPageEdit.jsx
✨ 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: 10

🔭 Outside diff range comments (1)
src/pages/myPageDetail/MyPageDetailPage.jsx (1)

32-38: ⚠️ Potential issue

로딩 상태 처리에 문제가 있습니다

로딩 스피너 표시 조건에 isLoadingHearts가 포함되어 있지 않습니다. 이로 인해 찜 목록 데이터가 로딩 중일 때도 페이지가 렌더링될 수 있어 사용자 경험이 저하될 수 있습니다.

다음과 같이 수정하는 것이 좋습니다:

-  if (isLoadingReviews || isLoadingCheers) {
+  if (isLoadingReviews || isLoadingCheers || isLoadingHearts) {
    return (
      <div className="flex justify-center items-center container">
        <Spinner />
      </div>
    );
  }
🧹 Nitpick comments (5)
src/pages/myPage/components/MyPageEdit.jsx (1)

78-80: 성능 최적화 필요

trimmed 변수는 렌더링마다 계산되며, showWarning 상태도 매번 재계산됩니다. 이는 useMemo로 최적화하여 성능을 개선할 수 있습니다.

- const trimmed = location.trim();
- const showWarning =
-   (!checked && !trimmed.startsWith("서울특별시")) || checked;

+ const trimmed = useMemo(() => location.trim(), [location]);
+ const showWarning = useMemo(() => 
+   (!checked && !trimmed.startsWith("서울특별시")) || checked,
+   [checked, trimmed]
+ );

이를 위해 상단에 useMemo를 import 해야 합니다:

- import { useState, useEffect } from "react";
+ import { useState, useEffect, useMemo } from "react";
src/pages/myPageDetail/components/HeartItem.jsx (3)

45-48: 찜 취소 버튼 UX 개선

사용자가 찜 취소 중에 중복 클릭을 방지하기 위한 시각적 피드백이 없습니다. 로딩 중 상태를 시각적으로 표현하면 사용자 경험이 향상됩니다.

const handleUnlike = (e) => {
  e.stopPropagation();
  if (!isLoading) unlike();
};

// 렌더링 부분에서 버튼 스타일 수정
<button 
  onClick={handleUnlike} 
  aria-label="찜 취소하기"
  aria-pressed="true"
  disabled={isLoading}
+ className={`transition-opacity ${isLoading ? 'opacity-50' : ''}`}
>
  <img src={HeartIcon} alt="찜 아이콘" className="w-6 h-6" />
</button>

33-34: 상태 관리 최적화

queryClient를 컴포넌트 내부에서 선언하면 매 렌더링마다 재생성됩니다. 이를 성능상의 이유로 리액트 훅 규칙에 맞게 수정하는 것이 좋습니다.

- const queryClient = useQueryClient();
- const [toastVisible, setToastVisible] = useState(false);

+ const queryClient = useQueryClient();
+ const [toastVisible, setToastVisible] = useState(false);

이 코드는 변경이 없어 보이지만, 현 위치에서는 ESLint 규칙에 따라 훅이 컴포넌트 최상단에 선언되어 있는지 재확인하는 것이 중요합니다. React 컴포넌트 내 모든 훅은 컴포넌트 함수 시작 부분에 호출되어야 합니다.


58-63: 더 명확한 마크업 구조 필요

현재 회사 이름과 카테고리를 표시하는 마크업이 최적이 아닙니다. 접근성과 의미론적 HTML을 개선하기 위해 구조를 수정하는 것이 좋습니다.

- <p className="h4 text-gray-12">
-   {companyName}
-   <span className="ml-1 b5 text-gray-6">
-     {businessTypeNameMap[companyCategory] ?? companyCategory}
-   </span>
- </p>

+ <div className="flex items-baseline">
+   <h3 className="h4 text-gray-12">{companyName}</h3>
+   <span className="ml-1 b5 text-gray-6">
+     {businessTypeNameMap[companyCategory] ?? companyCategory}
+   </span>
+ </div>
src/pages/myPageDetail/components/MapCompanyPage.jsx (1)

59-67: useToggleLike 훅 사용 최적화

현재 useToggleLike 훅은 여러 빈 콜백 함수와 함께 사용되고 있어 코드 이해를 어렵게 만듭니다. 이 훅을 더 효율적으로 사용하거나 필요한 기능만 추출하는 것이 좋겠습니다.

- const { toggleLike } = useToggleLike({
-   placesWithDistance: place ? [place] : [],
-   setPlacesWithDistance: () => {},
-   setFilteredPlaces: () => {},
-   selectedPlace: place,
-   setSelectedPlace: setPlace,
-   showOnlyLiked: false,
-   onRequireLogin: () => {},
- });

+ // 간소화된 버전의 toggleLike 함수 구현
+ const toggleLike = async () => {
+   if (!place) return;
+   
+   try {
+     const isAuthenticated = await useAuthStore.getState().checkAuth();
+     if (!isAuthenticated) {
+       // 로그인 요구 로직 (필요 시 구현)
+       return;
+     }
+     
+     // 찜하기/취소하기 로직 구현
+     // 기존 useToggleLike 훅의 내부 로직을 참고하여 필요한 기능만 추출
+     
+     // 상태 업데이트
+     setPlace(prev => ({
+       ...prev,
+       liked: !prev.liked
+     }));
+   } catch (err) {
+     console.error("찜하기 상태 변경 실패:", err);
+   }
+ };

또는 해당 훅의 내부 구현을 검토하여 불필요한 콜백 함수를 제거하는 방향으로 리팩토링하는 것도 고려해 보세요.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1ffbcda and 7095425.

📒 Files selected for processing (11)
  • src/apis/member/postUpdateProfile.js (1 hunks)
  • src/apis/member/queries.js (2 hunks)
  • src/apis/myPage/getDetail.js (1 hunks)
  • src/apis/myPage/queries.js (1 hunks)
  • src/pages/myPage/MyPage.jsx (1 hunks)
  • src/pages/myPage/components/MyPageEdit.jsx (1 hunks)
  • src/pages/myPageDetail/MyPageDetailPage.jsx (3 hunks)
  • src/pages/myPageDetail/components/HeartItem.jsx (1 hunks)
  • src/pages/myPageDetail/components/MapCompanyPage.jsx (1 hunks)
  • src/pages/story/components/StoryDetail.jsx (3 hunks)
  • src/routes/router.jsx (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/apis/myPage/queries.js (1)
src/apis/myPage/getDetail.js (2)
  • getHearts (3-6)
  • getHearts (3-6)
src/apis/member/queries.js (1)
src/apis/member/postUpdateProfile.js (2)
  • postUpdateProfile (3-10)
  • postUpdateProfile (3-10)
🔇 Additional comments (17)
src/apis/myPage/getDetail.js (1)

3-6: 좋아 보입니다! API 구현이 잘 되었네요.

getHearts 함수는 간결하고 기존 API 함수들과 일관된 패턴을 따르고 있습니다. 비동기 호출을 올바르게 처리하고 있으며, 응답 데이터를 적절히 반환하고 있습니다.

src/apis/member/postUpdateProfile.js (1)

3-10: API 구현이 잘 되었네요.

프로필 업데이트 API 함수가 잘 구현되었습니다. 필요한 파라미터(이름, 위치, 프로필 색상)를 올바르게 서버에 전송하고 있습니다.

src/pages/story/components/StoryDetail.jsx (3)

15-15: 상태 관리가 잘 구현되었습니다.

토스트 모달을 위한 상태 변수가 적절히 추가되었습니다.


27-30: 성공 콜백 처리가 잘 되었습니다.

응원하기 성공 시 토스트 메시지를 보여주고 3초 후 자동으로 닫히도록 구현한 부분이 좋습니다.


127-132: 토스트 모달 구현이 잘 되었습니다.

토스트 모달을 조건부로 렌더링하고 메시지와 닫기 핸들러를 적절히 전달하고 있습니다.

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

107-110: 편집 버튼 기능 구현이 잘 되었습니다.

프로필 편집 페이지로 이동하는 기능이 적절히 구현되었습니다. 사용자가 편집 아이콘을 클릭하면 /mypage/edit 경로로 이동하도록 처리되었습니다.

src/apis/myPage/queries.js (1)

2-10: 새로운 useGetHearts 훅이 잘 구현되었습니다!

useGetHearts 훅이 기존 패턴을 잘 따르고 있으며, 사용자의 찜 목록을 가져오는 기능을 적절하게 구현했습니다. enabled 옵션을 받아 조건부로 쿼리를 실행할 수 있게 한 점도 좋습니다.

src/routes/router.jsx (3)

20-21: 컴포넌트 import가 적절히 추가되었습니다

필요한 새 컴포넌트들을 적절히 import했습니다. 기존 import 패턴을 잘 따르고 있습니다.


43-46: 지도 상세 페이지 라우트가 잘 구현되었습니다

companyId 파라미터를 사용하여 동적 라우팅을 구현한 것이 좋습니다. 기존 지도 관련 라우트 바로 다음에 위치시켜 코드 구조도 논리적입니다.


101-104: 프로필 편집 페이지 라우트가 적절히 추가되었습니다

프로필 편집 페이지가 하단 탭이 없는 별도 페이지로 구현된 것이 적절합니다. 일반적인 편집 UI 패턴을 잘 따르고 있습니다.

src/apis/member/queries.js (2)

1-6: import 문이 잘 업데이트되었습니다

useMutation과 postUpdateProfile 함수가 적절히 import 되었습니다. 수정 기능 추가에 필요한 의존성을 정확히 가져왔습니다.


39-43: useUpdateProfile 훅이 잘 구현되었습니다

프로필 업데이트를 위한 mutation 훅이 정확히 구현되었습니다. 기존 코드 스타일과 일관성을 유지하면서 필요한 기능을 제공합니다.

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

5-9: import 문이 적절히 업데이트되었습니다

useGetHearts와 HeartItem 컴포넌트를 포함하도록 import 문이 잘 업데이트되었습니다. 구조화된 import 방식으로 변경한 것도 코드 가독성을 높입니다.

Also applies to: 14-14


21-23: useGetHearts 훅 사용이 적절합니다

조건부 쿼리 실행을 위해 enabled 옵션을 잘 활용했습니다. 다른 데이터 훅과 일관된 패턴을 따르고 있습니다.


58-58: UI 간격이 적절히 조정되었습니다

아이콘과 텍스트 사이의 간격을 gap-2에서 gap-4로 늘려 UI가 더 깔끔해졌습니다.


62-62: 찜 목록 개수 표시가 적절히 구현되었습니다

heartsData의 길이를 사용하여 총 찜 개수를 올바르게 표시하고 있습니다.


76-91: 찜 목록 UI가 잘 구현되었습니다

찜 목록 데이터가 있을 때와 없을 때의 UI 처리가 적절합니다. 기존 코드 패턴을 일관되게 따르고 있으며, 데이터 로딩 상태에 따른 조건부 렌더링이 잘 구현되어 있습니다.

Comment on lines +88 to +90
<button onClick={() => navigate(-1)} className="w-8 h-8">
<img src={BackIcon} alt="뒤로가기" className="w-8 h-8" />
</button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

접근성 개선 필요

뒤로가기 버튼에 적절한 접근성 속성이 부족합니다. 스크린 리더 사용자를 위한 명확한 정보를 제공해야 합니다.

- <button onClick={() => navigate(-1)} className="w-8 h-8">
+ <button 
+   onClick={() => navigate(-1)} 
+   className="w-8 h-8"
+   aria-label="뒤로가기">
  <img src={BackIcon} alt="뒤로가기" className="w-8 h-8" />
</button>
📝 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
<button onClick={() => navigate(-1)} className="w-8 h-8">
<img src={BackIcon} alt="뒤로가기" className="w-8 h-8" />
</button>
<button
onClick={() => navigate(-1)}
className="w-8 h-8"
aria-label="뒤로가기"
>
<img src={BackIcon} alt="뒤로가기" className="w-8 h-8" />
</button>
🤖 Prompt for AI Agents
In src/pages/myPage/components/MyPageEdit.jsx around lines 88 to 90, the back
button lacks accessibility attributes for screen reader users. Add an aria-label
attribute to the button element with a descriptive label such as "Go back" to
provide clear context for assistive technologies.

Comment on lines +180 to +184
onClick={handleSubmit}
className="w-full h-12 px-4 py-3 bg-primary-8 text-white b1 rounded-xl"
>
저장
</button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

저장 버튼 상태 처리 개선

현재 저장 버튼은 프로필 업데이트 중에도 클릭 가능한 상태로 유지됩니다. 여러 번 클릭하면 중복 요청이 발생할 수 있습니다. 업데이트 진행 중일 때 버튼 비활성화와 로딩 상태를 표시하는 것이 좋습니다.

+ const { mutate: updateProfile, isLoading } = useUpdateProfile();

// ...

<button
  onClick={handleSubmit}
- className="w-full h-12 px-4 py-3 bg-primary-8 text-white b1 rounded-xl"
+ className={`w-full h-12 px-4 py-3 text-white b1 rounded-xl ${
+   isLoading ? "bg-primary-5" : "bg-primary-8"
+ }`}
+ disabled={isLoading}
>
- 저장
+ {isLoading ? "저장 중..." : "저장"}
</button>
📝 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
onClick={handleSubmit}
className="w-full h-12 px-4 py-3 bg-primary-8 text-white b1 rounded-xl"
>
저장
</button>
// Add at the top of your component (where hooks are initialized)
const { mutate: updateProfile, isLoading } = useUpdateProfile();
// …
<button
onClick={handleSubmit}
className={`w-full h-12 px-4 py-3 text-white b1 rounded-xl ${
isLoading ? "bg-primary-5" : "bg-primary-8"
}`}
disabled={isLoading}
>
{isLoading ? "저장 중..." : "저장"}
</button>
🤖 Prompt for AI Agents
In src/pages/myPage/components/MyPageEdit.jsx around lines 180 to 184, the save
button remains clickable during profile updates, allowing multiple submissions.
Modify the button to be disabled while the update is in progress by linking its
disabled attribute to the loading state. Additionally, add a visual loading
indicator or change the button text to reflect the ongoing operation, preventing
duplicate requests and improving user feedback.

Comment on lines +62 to +64
console.error("프로필 업데이트 실패:", err);
alert("프로필 업데이트에 실패했습니다.");
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리 개선 필요

현재 프로필 업데이트 실패 시 콘솔 에러와 alert를 사용하고 있습니다. 사용자 경험 향상을 위해 ToastModal 컴포넌트를 활용하여 일관된 에러 피드백을 제공하는 것이 좋겠습니다.

onError: (err) => {
  console.error("프로필 업데이트 실패:", err);
-  alert("프로필 업데이트에 실패했습니다.");
+  setToastVisible(true);
+  setTimeout(() => {
+    setToastVisible(false);
+  }, 1500);
},

추가로 아래와 같이 ToastModal 컴포넌트에 에러 메시지를 전달할 수 있도록 상태 변수를 추가하세요:

const [toastVisible, setToastVisible] = useState(false);
+ const [toastMessage, setToastMessage] = useState("");

// onSuccess에서:
setToastVisible(true);
+ setToastMessage("프로필이 저장되었습니다.");

// onError에서:
setToastVisible(true);
+ setToastMessage("프로필 업데이트에 실패했습니다.");

// 그리고 ToastModal 컴포넌트에서:
{toastVisible && (
  <ToastModal
-    message="프로필이 저장되었습니다."
+    message={toastMessage}
    duration={1800}
    onClose={() => setToastVisible(false)}
  />
)}

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

🤖 Prompt for AI Agents
In src/pages/myPage/components/MyPageEdit.jsx around lines 62 to 64, replace the
current error handling that uses console.error and alert with the ToastModal
component for consistent user feedback. Add a state variable to hold the error
message and update it when the profile update fails, then render the ToastModal
with this error message to display the error in a user-friendly modal instead of
an alert.

Comment on lines 72 to 74
<button onClick={handleUnlike} aria-label="찜 버튼">
<img src={HeartIcon} alt="찜 아이콘" className="w-6 h-6" />
</button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

접근성 개선 필요

현재 찜 취소 버튼에는 aria-label이 있지만, 상태를 나타내는 속성이 부족합니다. 스크린 리더 사용자에게 버튼의 현재 상태를 명확히 전달하기 위해 추가 속성이 필요합니다.

- <button onClick={handleUnlike} aria-label="찜 버튼">
+ <button 
+   onClick={handleUnlike} 
+   aria-label="찜 취소하기" 
+   aria-pressed="true"
+   disabled={isLoading}
+ >
  <img src={HeartIcon} alt="찜 아이콘" className="w-6 h-6" />
</button>
📝 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
<button onClick={handleUnlike} aria-label="찜 버튼">
<img src={HeartIcon} alt="찜 아이콘" className="w-6 h-6" />
</button>
<button
onClick={handleUnlike}
aria-label="찜 취소하기"
aria-pressed="true"
disabled={isLoading}
>
<img src={HeartIcon} alt="찜 아이콘" className="w-6 h-6" />
</button>
🤖 Prompt for AI Agents
In src/pages/myPageDetail/components/HeartItem.jsx around lines 72 to 74, the
unlike button has an aria-label but lacks an attribute to convey its current
state to screen readers. Add an aria-pressed attribute to the button that
reflects whether the item is currently liked or not, so assistive technologies
can communicate the toggle state clearly.

Comment on lines +36 to +43
const { mutate: unlike, isLoading } = useMutation({
mutationFn: () => unlikeCompany(companyId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["userHearts"] });
setToastVisible(true);
setTimeout(() => setToastVisible(false), 2000);
},
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

찜 취소 실패에 대한 에러 처리 부족

현재 코드에서는 찜 취소(unlike) 실패 시 에러 처리가 없습니다. 사용자에게 적절한 피드백을 제공하기 위해 에러 핸들링을 추가해야 합니다.

const { mutate: unlike, isLoading } = useMutation({
  mutationFn: () => unlikeCompany(companyId),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["userHearts"] });
    setToastVisible(true);
    setTimeout(() => setToastVisible(false), 2000);
  },
+ onError: (error) => {
+   console.error("찜 취소 실패:", error);
+   setToastVisible(true);
+   setToastMessage("찜 취소에 실패했습니다. 다시 시도해주세요.");
+   setTimeout(() => setToastVisible(false), 2000);
+ },
});

// 상태 변수 추가
+ const [toastMessage, setToastMessage] = useState("저장이 취소되었습니다");

// ToastModal 수정
{toastVisible && (
  <ToastModal
-   message="저장이 취소되었습니다"
+   message={toastMessage}
    onClose={() => setToastVisible(false)}
  />
)}
📝 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
const { mutate: unlike, isLoading } = useMutation({
mutationFn: () => unlikeCompany(companyId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["userHearts"] });
setToastVisible(true);
setTimeout(() => setToastVisible(false), 2000);
},
});
// (somewhere alongside your existing toastVisible state)
const [toastMessage, setToastMessage] = useState("저장이 취소되었습니다");
const { mutate: unlike, isLoading } = useMutation({
mutationFn: () => unlikeCompany(companyId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["userHearts"] });
setToastVisible(true);
setTimeout(() => setToastVisible(false), 2000);
},
onError: (error) => {
console.error("찜 취소 실패:", error);
setToastVisible(true);
setToastMessage("찜 취소에 실패했습니다. 다시 시도해주세요.");
setTimeout(() => setToastVisible(false), 2000);
},
});
// … later in your JSX …
{toastVisible && (
<ToastModal
message={toastMessage}
onClose={() => setToastVisible(false)}
/>
)}
🤖 Prompt for AI Agents
In src/pages/myPageDetail/components/HeartItem.jsx around lines 36 to 43, the
unlike mutation lacks error handling for failure cases. Add an onError callback
to the useMutation hook that handles errors from unlikeCompany, providing user
feedback such as displaying an error toast or message to inform the user that
the unlike action failed.

Comment on lines +73 to +82
<div
className="absolute mt-10 px-5 mb-4 z-[10001]"
onClick={() => navigate(-1)}
>
<img
src="/svgs/myPage/backIcon.svg"
alt="뒤로가기"
className="w-10 h-10 cursor-pointer"
/>
</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

접근성 개선 필요

뒤로가기 버튼에 적절한 접근성 속성이 부족합니다. 스크린 리더 사용자를 위해 명확한 정보를 제공해야 합니다.

- <div
-   className="absolute mt-10 px-5 mb-4 z-[10001]"
-   onClick={() => navigate(-1)}
- >
-   <img
-     src="/svgs/myPage/backIcon.svg"
-     alt="뒤로가기"
-     className="w-10 h-10 cursor-pointer"
-   />
- </div>

+ <button
+   className="absolute mt-10 px-5 mb-4 z-[10001]"
+   onClick={() => navigate(-1)}
+   aria-label="뒤로가기"
+ >
+   <img
+     src="/svgs/myPage/backIcon.svg"
+     alt="뒤로가기"
+     className="w-10 h-10"
+   />
+ </button>
🤖 Prompt for AI Agents
In src/pages/myPageDetail/components/MapCompanyPage.jsx around lines 73 to 82,
the back button lacks proper accessibility attributes. Add appropriate ARIA
attributes such as aria-label to the clickable div or replace the div with a
button element to provide clear context for screen reader users, ensuring the
element is keyboard accessible and announces its purpose clearly.

Comment on lines +32 to +35
if (!locationData?.latitude || !locationData?.longitude) {
console.warn("기업 좌표 정보가 누락되었습니다:", locationData);
return;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

위치 정보 누락 시 사용자 피드백 부족

현재 기업 좌표 정보가 누락된 경우 콘솔에만 경고를 출력하고 사용자에게는 아무런 피드백이 없습니다. 이는 사용자 경험을 저하시킬 수 있습니다.

if (!locationData?.latitude || !locationData?.longitude) {
  console.warn("기업 좌표 정보가 누락되었습니다:", locationData);
-  return;
+  // 사용자에게 알림을 표시하고 이전 페이지로 돌아가는 옵션 제공
+  alert("해당 기업의 위치 정보를 찾을 수 없습니다.");
+  navigate(-1);
+  return;
}

더 나은 사용자 경험을 위해서는 alert 대신 모달 또는 토스트 메시지를 사용하는 것이 좋습니다. 프로젝트에 적합한 컴포넌트를 활용하세요.

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

🤖 Prompt for AI Agents
In src/pages/myPageDetail/components/MapCompanyPage.jsx around lines 32 to 35,
the code only logs a warning to the console when locationData latitude or
longitude is missing, providing no user feedback. Replace the console.warn with
a user-visible notification by triggering a modal or toast message using the
project's existing UI components to inform the user about the missing location
information, improving the user experience.

Comment on lines +20 to +57
useEffect(() => {
const fetchData = async () => {
try {
const [preview, allCompanies] = await Promise.all([
getCompanyPreview(companyId),
getAllCompanies(),
]);

const locationData = allCompanies.find(
(c) => String(c.companyId) === String(companyId)
);

if (!locationData?.latitude || !locationData?.longitude) {
console.warn("기업 좌표 정보가 누락되었습니다:", locationData);
return;
}

const isAuthenticated = await useAuthStore.getState().checkAuth();
const likedList = isAuthenticated ? await getLikedCompanies() : [];
const liked = likedList.some((c) => c.companyId === Number(companyId));

const enriched = {
...preview,
coords: {
lat: locationData.latitude,
lng: locationData.longitude,
},
liked,
};

setPlace(enriched);
} catch (err) {
console.error("기업 정보 로딩 실패:", err);
}
};

fetchData();
}, [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

비동기 데이터 로딩 최적화

현재 구현된 데이터 로딩 로직에는 몇 가지 개선이 필요합니다:

  1. useEffect 내에서 비동기 함수를 직접 정의하고 호출하는 것은 메모리 누수의 원인이 될 수 있습니다
  2. API 호출 실패 시 사용자에게 적절한 피드백이 없습니다
useEffect(() => {
+ let isMounted = true;
  const fetchData = async () => {
    try {
      const [preview, allCompanies] = await Promise.all([
        getCompanyPreview(companyId),
        getAllCompanies(),
      ]);

      const locationData = allCompanies.find(
        (c) => String(c.companyId) === String(companyId)
      );

      if (!locationData?.latitude || !locationData?.longitude) {
        console.warn("기업 좌표 정보가 누락되었습니다:", locationData);
+       if (isMounted) {
+         // 사용자에게 알림 표시
+         alert("해당 기업의 위치 정보를 찾을 수 없습니다.");
+         navigate(-1);
+       }
        return;
      }

      const isAuthenticated = await useAuthStore.getState().checkAuth();
      const likedList = isAuthenticated ? await getLikedCompanies() : [];
      const liked = likedList.some((c) => c.companyId === Number(companyId));

      const enriched = {
        ...preview,
        coords: {
          lat: locationData.latitude,
          lng: locationData.longitude,
        },
        liked,
      };

+     if (isMounted) {
        setPlace(enriched);
+     }
    } catch (err) {
      console.error("기업 정보 로딩 실패:", err);
+     if (isMounted) {
+       // 사용자에게 에러 피드백 제공
+       alert("기업 정보를 불러오는데 실패했습니다. 다시 시도해주세요.");
+     }
    }
  };

  fetchData();
  
+ return () => {
+   isMounted = false;
+ };
}, [companyId, navigate]);

이 수정은 컴포넌트가 언마운트된 후에도 상태를 업데이트하는 것을 방지하고, 사용자에게 적절한 피드백을 제공합니다.

📝 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
useEffect(() => {
const fetchData = async () => {
try {
const [preview, allCompanies] = await Promise.all([
getCompanyPreview(companyId),
getAllCompanies(),
]);
const locationData = allCompanies.find(
(c) => String(c.companyId) === String(companyId)
);
if (!locationData?.latitude || !locationData?.longitude) {
console.warn("기업 좌표 정보가 누락되었습니다:", locationData);
return;
}
const isAuthenticated = await useAuthStore.getState().checkAuth();
const likedList = isAuthenticated ? await getLikedCompanies() : [];
const liked = likedList.some((c) => c.companyId === Number(companyId));
const enriched = {
...preview,
coords: {
lat: locationData.latitude,
lng: locationData.longitude,
},
liked,
};
setPlace(enriched);
} catch (err) {
console.error("기업 정보 로딩 실패:", err);
}
};
fetchData();
}, [companyId]);
useEffect(() => {
let isMounted = true;
const fetchData = async () => {
try {
const [preview, allCompanies] = await Promise.all([
getCompanyPreview(companyId),
getAllCompanies(),
]);
const locationData = allCompanies.find(
(c) => String(c.companyId) === String(companyId)
);
if (!locationData?.latitude || !locationData?.longitude) {
console.warn("기업 좌표 정보가 누락되었습니다:", locationData);
if (isMounted) {
// 사용자에게 알림 표시
alert("해당 기업의 위치 정보를 찾을 수 없습니다.");
navigate(-1);
}
return;
}
const isAuthenticated = await useAuthStore.getState().checkAuth();
const likedList = isAuthenticated ? await getLikedCompanies() : [];
const liked = likedList.some((c) => c.companyId === Number(companyId));
const enriched = {
...preview,
coords: {
lat: locationData.latitude,
lng: locationData.longitude,
},
liked,
};
if (isMounted) {
setPlace(enriched);
}
} catch (err) {
console.error("기업 정보 로딩 실패:", err);
if (isMounted) {
// 사용자에게 에러 피드백 제공
alert("기업 정보를 불러오는데 실패했습니다. 다시 시도해주세요.");
}
}
};
fetchData();
return () => {
isMounted = false;
};
}, [companyId, navigate]);
🤖 Prompt for AI Agents
In src/pages/myPageDetail/components/MapCompanyPage.jsx lines 20 to 57, the
useEffect defines and calls an async function directly, which can cause memory
leaks if the component unmounts before completion, and it lacks user feedback on
API failures. Refactor by adding a mounted flag to check component mount status
before calling setPlace to prevent state updates after unmount. Also, implement
user-facing error handling such as setting an error state or displaying a
message when API calls fail to provide proper feedback.

onRequireLogin: () => {},
});

if (!place) return <p className="text-center mt-10">로딩 중...</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

로딩 상태 UI 개선 필요

현재 로딩 상태는 단순 텍스트로만 표시되고 있습니다. 사용자 경험 향상을 위해 스켈레톤 UI나 로딩 스피너를 사용하는 것이 좋습니다.

- if (!place) return <p className="text-center mt-10">로딩 중...</p>;

+ if (!place) return (
+   <div className="flex flex-col items-center justify-center h-screen bg-gray-2">
+     <div className="w-8 h-8 border-4 border-primary-8 border-t-transparent rounded-full animate-spin"></div>
+     <p className="mt-4 text-gray-9">기업 정보를 불러오는 중입니다...</p>
+   </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
if (!place) return <p className="text-center mt-10">로딩 중...</p>;
if (!place) return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-2">
<div className="w-8 h-8 border-4 border-primary-8 border-t-transparent rounded-full animate-spin"></div>
<p className="mt-4 text-gray-9">기업 정보를 불러오는 중입니다...</p>
</div>
);
🤖 Prompt for AI Agents
In src/pages/myPageDetail/components/MapCompanyPage.jsx at line 69, the loading
state is currently shown as plain text, which is not user-friendly. Replace the
simple text "로딩 중..." with a more engaging loading indicator such as a skeleton
UI component or a spinner to improve the user experience during data loading.

@dbstj0403
Copy link
Collaborator

하트 아이콘 크기가 제각각인 이슈 있습니다!

스크린샷 2025-05-21 오전 9 06 52

@dbstj0403
Copy link
Collaborator

모바일 화면의 경우 편집 페이지 들어가자마자 이렇게 뜹니다! 기기 높이가 짧은 경우 스크롤이 되지 않아용

스크린샷 2025-05-21 오전 9 07 46

@dbstj0403
Copy link
Collaborator

사용자가 이름을 비워놓고 변경 요청했을 때 모달처리 해줘야 할 것 같아요!

@dbstj0403
Copy link
Collaborator

사용자가 장소 정보를 모두 지우고 업데이트해도 기본으로 서울특별시이니까 서울특별시 글자는 지워지지 않게끔 하면 더 좋을거같아요

@shroqkf
Copy link
Collaborator Author

shroqkf commented May 21, 2025

사용자가 장소 정보를 모두 지우고 업데이트해도 기본으로 서울특별시이니까 서울특별시 글자는 지워지지 않게끔 하면 더 좋을거같아요

기존에 받아온 주소에서 서울특별시를 지우면 아래 안내 문구가 보입니다. 편집 페이지라서 서울특별시 글자도 지워져도 된다고 생각했고 받아오는 데이터 자체에 서울특별시가 붙어있어서 서울특별시를 고정 문구로 하는게 오래 걸릴 거 같아요

@dbstj0403 dbstj0403 merged commit 0738a97 into develop May 21, 2025
4 checks passed
@dbstj0403 dbstj0403 deleted the feature/#78/mypage-liked-companies branch May 21, 2025 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Design This doesn't seem right Feat Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants