Conversation
[FIX] 리포트 내역 삭제 딜레이 수정
[DEV] 사이드바 피드백 버튼 추가
[DEV] 회원 탈퇴 api 연동
Summary of ChangesHello @drddyn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 PR은 사용자 경험 개선과 데이터 기반 의사결정을 위한 중요한 업데이트를 포함합니다. GA4 통합을 통해 사용자 행동에 대한 심층적인 인사이트를 얻고, 회원 탈퇴 기능을 제공하여 사용자에게 더 많은 제어권을 부여합니다. 또한, 리포트 삭제 시 즉각적인 UI 반응을 제공하여 애플리케이션의 전반적인 반응성을 높였습니다. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
이 PR은 GA4를 통한 사용자 행동 추적 기능 추가, 회원 탈퇴 기능 구현, 그리고 리포트 삭제 시의 사용자 경험 개선을 포함하고 있습니다. 전반적으로 코드 변경 사항이 많지만, 각 기능의 구현이 체계적으로 잘 이루어졌습니다. 특히 GA 추적 로직이 애플리케이션 전반에 걸쳐 상세하게 추가된 점이 인상적입니다. 몇 가지 개선점을 제안드렸으며, 특히 리포트 삭제 시의 낙관적 업데이트 로직과 이벤트 추적 방식에 대한 검토가 필요해 보입니다.
| onMutate: async ({ reportId }) => { | ||
| if (typeof channelId !== 'number') return {} | ||
| const queryKey = ['my', 'report', channelId] | ||
| await queryClient.cancelQueries({ queryKey }) | ||
| const previousData = queryClient.getQueryData<MyReportQueryData>(queryKey) | ||
| queryClient.setQueryData<MyReportQueryData>(queryKey, (old) => { | ||
| if (!old) return old | ||
| return { | ||
| ...old, | ||
| reportList: old.reportList.filter((report) => report.reportId !== reportId), | ||
| totalElements: old.totalElements - 1, | ||
| } | ||
| }) | ||
| return { previousData } | ||
| }, | ||
| onError: (_error, _variables, context) => { | ||
| if (typeof channelId === 'number' && context?.previousData) { | ||
| queryClient.setQueryData(['my', 'report', channelId], context.previousData) | ||
| } | ||
| alert('리포트 삭제 중 오류가 발생했습니다.') | ||
| }, |
There was a problem hiding this comment.
리포트 목록에 대한 낙관적 업데이트를 구현하신 점이 좋습니다. 하지만 현재 onMutate와 onError에서 사용된 setQueryData와 getQueryData는 정확한 쿼리 키를 필요로 합니다. 리포트 목록 쿼리는 페이징 및 필터링(type)을 포함하므로, ['my', 'report', channelId]와 같은 부분적인 키로는 캐시된 데이터를 정확히 찾아 업데이트하거나 롤백하기 어렵습니다. 이로 인해 낙관적 업데이트가 UI에 반영되지 않을 가능성이 높습니다.
모든 관련 페이지 캐시에 대해 업데이트를 적용하려면 setQueriesData를 필터와 함께 사용하는 것을 고려해 보세요. 이 로직이 의도한 대로 동작하는지 다시 한번 확인해 보시는 것을 추천합니다.
| export const trackEvent = ({ category, action, label, value }: EventParams) => { | ||
| // 비동기로 실행하여 메인 로직 블로킹 방지 | ||
| setTimeout(() => { | ||
| try { | ||
| if (!isInitialized) return | ||
|
|
||
| ReactGA.event({ | ||
| category, | ||
| action, | ||
| label, | ||
| value, | ||
| }) | ||
| } catch (error) { | ||
| console.error('[GA4] 이벤트 트래킹 실패:', error) | ||
| } | ||
| }, 0) | ||
| } |
There was a problem hiding this comment.
trackEvent 함수를 setTimeout으로 감싸 비동기로 처리하셨는데, 이는 메인 스레드를 블로킹하지 않으려는 좋은 의도입니다. 하지만 사용자가 이벤트를 발생시키는 행동(예: 링크 클릭) 후 페이지가 바로 이동하는 경우, setTimeout의 콜백이 실행되기 전에 페이지가 언로드되어 이벤트가 유실될 수 있습니다. 예를 들어, 로그인 버튼 클릭 후 즉시 페이지를 리디렉션하는 경우에 해당합니다.
이벤트 추적의 정확성을 높이기 위해 setTimeout을 제거하고 동기적으로 처리하는 것을 고려해 보세요. ReactGA.event 호출로 인한 성능 영향은 대부분의 경우 미미합니다.
export const trackEvent = ({ category, action, label, value }: EventParams) => {
try {
if (!isInitialized) return
ReactGA.event({
category,
action,
label,
value,
})
} catch (error) {
console.error('[GA4] 이벤트 트래킹 실패:', error)
}
}| // ✅ 페이지 진입 시 해당 리포트 ID로 상태가 없을 때만 일회성으로 서버에 상태 조회 | ||
| const needsPolling = useMemo(() => pendingReportIds.includes(reportId), [pendingReportIds, reportId]) | ||
| usePollReportStatus(reportId, { enabled: needsPolling }) | ||
|
|
||
| // ✅ 리포트 생성에 실패한 경우 | ||
| // ✅ 해당 리포트 ID가 PENDING 중일 경우 로컬 폴링 | ||
| const isKnownToHaveFailed = useMemo(() => { |
There was a problem hiding this comment.
코드의 가독성을 위해 남겨주신 주석들이 실제 코드 내용과 일치하지 않는 것 같습니다. 33행의 주석은 폴링에 대한 내용이어야 하고, 37행의 주석은 실패 케이스 처리에 대한 내용이어야 합니다. 주석을 코드에 맞게 수정하면 나중에 코드를 이해하는 데 도움이 될 것입니다.
| // ✅ 페이지 진입 시 해당 리포트 ID로 상태가 없을 때만 일회성으로 서버에 상태 조회 | |
| const needsPolling = useMemo(() => pendingReportIds.includes(reportId), [pendingReportIds, reportId]) | |
| usePollReportStatus(reportId, { enabled: needsPolling }) | |
| // ✅ 리포트 생성에 실패한 경우 | |
| // ✅ 해당 리포트 ID가 PENDING 중일 경우 로컬 폴링 | |
| const isKnownToHaveFailed = useMemo(() => { | |
| // ✅ 해당 리포트 ID가 PENDING 중일 경우 로컬 폴링 | |
| const needsPolling = useMemo(() => pendingReportIds.includes(reportId), [pendingReportIds, reportId]) | |
| usePollReportStatus(reportId, { enabled: needsPolling }) | |
| // ✅ 리포트 생성에 실패한 경우 | |
| const isKnownToHaveFailed = useMemo(() => { |
✅ Summary
이하의 작업들을 완료하여 main에 병합합니다
📝 Description