-
Notifications
You must be signed in to change notification settings - Fork 1
[Feat] 마이페이지 찜목록 및 프로필 편집 기능 구현 #79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
""" Walkthrough이 변경 사항은 사용자 프로필 수정 기능과 "찜" 목록(하트 기능) 관련 기능을 추가 및 확장합니다. 새로운 API 함수, React Query 훅, UI 컴포넌트, 라우트가 도입되어 프로필 수정 및 찜 목록 조회·관리, 상세 지도 페이지, 토스트 알림 등이 구현되었습니다. Changes
Suggested reviewers
Note ⚡️ AI Code Reviews for VS Code, Cursor, WindsurfCodeRabbit 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. Note ⚡️ Faster reviews with cachingCodeRabbit 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 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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
📒 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 처리가 적절합니다. 기존 코드 패턴을 일관되게 따르고 있으며, 데이터 로딩 상태에 따른 조건부 렌더링이 잘 구현되어 있습니다.
| <button onClick={() => navigate(-1)} className="w-8 h-8"> | ||
| <img src={BackIcon} alt="뒤로가기" className="w-8 h-8" /> | ||
| </button> |
There was a problem hiding this comment.
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.
| <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.
| onClick={handleSubmit} | ||
| className="w-full h-12 px-4 py-3 bg-primary-8 text-white b1 rounded-xl" | ||
| > | ||
| 저장 | ||
| </button> |
There was a problem hiding this comment.
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.
| 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.
| console.error("프로필 업데이트 실패:", err); | ||
| alert("프로필 업데이트에 실패했습니다."); | ||
| }, |
There was a problem hiding this comment.
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.
| <button onClick={handleUnlike} aria-label="찜 버튼"> | ||
| <img src={HeartIcon} alt="찜 아이콘" className="w-6 h-6" /> | ||
| </button> |
There was a problem hiding this comment.
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.
| <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.
| const { mutate: unlike, isLoading } = useMutation({ | ||
| mutationFn: () => unlikeCompany(companyId), | ||
| onSuccess: () => { | ||
| queryClient.invalidateQueries({ queryKey: ["userHearts"] }); | ||
| setToastVisible(true); | ||
| setTimeout(() => setToastVisible(false), 2000); | ||
| }, | ||
| }); |
There was a problem hiding this comment.
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.
| 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.
| <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> |
There was a problem hiding this comment.
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.
| if (!locationData?.latitude || !locationData?.longitude) { | ||
| console.warn("기업 좌표 정보가 누락되었습니다:", locationData); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위치 정보 누락 시 사용자 피드백 부족
현재 기업 좌표 정보가 누락된 경우 콘솔에만 경고를 출력하고 사용자에게는 아무런 피드백이 없습니다. 이는 사용자 경험을 저하시킬 수 있습니다.
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.
| 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]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
비동기 데이터 로딩 최적화
현재 구현된 데이터 로딩 로직에는 몇 가지 개선이 필요합니다:
useEffect내에서 비동기 함수를 직접 정의하고 호출하는 것은 메모리 누수의 원인이 될 수 있습니다- 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.
| 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>; |
There was a problem hiding this comment.
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.
| 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.
|
사용자가 이름을 비워놓고 변경 요청했을 때 모달처리 해줘야 할 것 같아요! |
|
사용자가 장소 정보를 모두 지우고 업데이트해도 기본으로 서울특별시이니까 서울특별시 글자는 지워지지 않게끔 하면 더 좋을거같아요 |
기존에 받아온 주소에서 서울특별시를 지우면 아래 안내 문구가 보입니다. 편집 페이지라서 서울특별시 글자도 지워져도 된다고 생각했고 받아오는 데이터 자체에 서울특별시가 붙어있어서 서울특별시를 고정 문구로 하는게 오래 걸릴 거 같아요 |


🌟 어떤 이유로 MR를 하셨나요?
📝 세부 내용 - 왜 해당 MR이 필요한지 작업 내용을 자세하게 설명해주세요
📸 작업 화면 스크린샷
📢 로컬 실행 시 유의사항
🚨 관련 이슈 번호
Summary by CodeRabbit
신규 기능
스타일/UX
버그 수정
문서화