Skip to content

[DEPLOY] 리포트 페이지 프로그레스 바, 진행 상황 모달 추가#274

Merged
gomx3 merged 10 commits intomainfrom
develop
Mar 7, 2026
Merged

[DEPLOY] 리포트 페이지 프로그레스 바, 진행 상황 모달 추가#274
gomx3 merged 10 commits intomainfrom
develop

Conversation

@gomx3
Copy link
Contributor

@gomx3 gomx3 commented Mar 7, 2026

✅ Summary

PR에 대한 간단한 요약을 작성합니다.

📝 Description

  • PR에서 어떤 사항을 다루었는지에 대한 내용을 작성합니다.
  • UI에 대한 사항이라면 스크린샷을, 코드에 대한 사항이라면 코드블럭을 첨부하길 권장합니다.

💬 리뷰 요구 사항

  • 요구 사항이 없을 경우 지웁니다.

gomx3 added 10 commits December 23, 2025 01:57
1. 리포트 페이지에서 생성 완료 후 다른 페이지로 이동했을 때 우측 하단 모달 띄우지 않도록 수정
2. 개요보다 분석이 먼저 끝나는 경우에도 step2를 채우도록 수정
- 시작 전: 1
- 둘 다 미완료: 2
- overview만 완료: 3
- analysis만 완료: 3
- 둘 다 완료: 4
[DEV] 리포트 생성 요청 시 프로그레스바 기능 추가
@gomx3 gomx3 self-assigned this Mar 7, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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은 리포트 생성 페이지의 사용자 경험을 대폭 개선합니다. 기존의 주기적인 폴링 방식에서 벗어나 Server-Sent Events(SSE)를 활용하여 리포트 생성 진행 상황을 실시간으로 표시하는 프로그레스 바와 모달을 도입했습니다. 이를 통해 사용자는 리포트가 생성되는 동안 현재 단계를 명확하게 인지할 수 있으며, 전역 토스트 알림 시스템 추가로 애플리케이션 전반의 피드백이 강화되었습니다. 전반적인 리포트 상태 관리 로직도 최신 요구사항에 맞춰 재구성되었습니다.

Highlights

  • 실시간 진행 상황 표시: 리포트 생성 과정에 대한 실시간 프로그레스 바와 진행 상황 모달이 추가되어 사용자에게 명확한 피드백을 제공합니다.
  • SSE(Server-Sent Events) 도입: 기존 폴링 방식 대신 SSE를 사용하여 리포트 생성 상태를 실시간으로 업데이트하는 효율적인 메커니즘을 구현했습니다.
  • 전역 토스트 알림 시스템: 사용자 경험을 개선하기 위해 전역 토스트 알림 컴포넌트와 스토어가 추가되었습니다.
  • 리포트 상태 관리 개선: 리포트 생성 및 진행 상태를 관리하는 Zustand 스토어가 재구성되어 더욱 유연하고 직관적인 상태 관리가 가능해졌습니다.

🧠 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
  • frontend/package.json
    • @microsoft/fetch-event-source 패키지를 추가했습니다.
  • frontend/pnpm-lock.yaml
    • @microsoft/fetch-event-source 의존성 정보를 포함하도록 잠금 파일을 업데이트했습니다.
  • frontend/src/App.tsx
    • GlobalReportPoller 컴포넌트를 제거하고 GlobalProcessingModalGlobalToast 컴포넌트를 추가했습니다.
  • frontend/src/api/report.ts
    • getReportStatus 함수의 반환 타입에 ResponseReportStatus를 명시했습니다.
  • frontend/src/components/GlobalProcessingModal.tsx
    • 리포트 생성 진행 상황을 표시하는 전역 모달 컴포넌트를 새로 추가했습니다.
  • frontend/src/components/GlobalReportPoller.tsx
    • 기존 리포트 폴링 로직을 담당하던 컴포넌트를 제거했습니다.
  • frontend/src/components/GlobalToast.tsx
    • 전역 토스트 알림을 위한 컴포넌트를 새로 추가했습니다.
  • frontend/src/constants/sse.ts
    • SSE 연결을 위한 URL 상수를 정의하는 파일을 새로 추가했습니다.
  • frontend/src/hooks/index.ts
    • useIsMobile 훅을 내보내도록 업데이트했습니다.
  • frontend/src/hooks/report/index.ts
    • 새로운 리포트 관련 훅(useReportStatus, useReportProgress, useGetVideoData)을 내보내도록 업데이트했습니다.
  • frontend/src/hooks/report/useGetVideoData.ts
    • useGetVideoData 훅의 내보내기 방식을 기본 내보내기에서 명명된 내보내기로 변경했습니다.
  • frontend/src/hooks/report/usePollReportStatus.ts
    • 리포트 상태 폴링 로직을 담당하던 훅을 제거했습니다.
  • frontend/src/hooks/report/usePostReportById.ts
    • 리포트 생성 시작/종료 및 보류 중인 리포트 ID 추가와 관련된 useReportStore 액션 호출을 제거했습니다.
  • frontend/src/hooks/report/usePostReportByUrl.ts
    • 리포트 생성 시작/종료 및 보류 중인 리포트 ID 추가와 관련된 useReportStore 액션 호출을 제거했습니다.
  • frontend/src/hooks/report/useReportProgress.ts
    • SSE를 사용하여 리포트 생성 진행률을 추적하는 새로운 훅을 추가했습니다.
  • frontend/src/hooks/report/useReportStatus.ts
    • 리포트 상태를 주기적으로 조회하는 새로운 훅을 추가했습니다.
  • frontend/src/hooks/useIsMobile.ts
    • useIsMobile 훅의 파일 경로를 frontend/src/hooks/main/useIsMobile.ts에서 frontend/src/hooks/useIsMobile.ts로 변경했습니다.
  • frontend/src/layouts/RootLayout.tsx
    • LoadingSpinner 컴포넌트와 GlobalReportPoller 임포트를 제거하고, GlobalProcessingModalGlobalToast 컴포넌트를 추가했습니다.
  • frontend/src/layouts/_components/navbar/NavbarLinksList.tsx
    • useIsMobile 훅의 임포트 경로를 업데이트했습니다.
  • frontend/src/pages/main/_components/UrlInputForm.tsx
    • useGetVideoData 훅의 임포트 경로를 업데이트했습니다.
  • frontend/src/pages/main/_components/UrlInputModal.tsx
    • useGetVideoData 훅의 임포트 경로를 업데이트했습니다.
  • frontend/src/pages/report/ReportPage.tsx
    • 리포트 페이지의 로직을 대폭 리팩토링하여 새로운 프로그레스 바 및 모달 로직을 통합하고, 기존 폴링 및 로딩 상태를 제거했습니다. 또한 새로운 리포트 스토어 액션을 통합했습니다.
  • frontend/src/pages/report/_components/ProgressBar.tsx
    • 리포트 생성 진행 상황을 시각적으로 표시하는 프로그레스 바 컴포넌트를 새로 추가했습니다.
  • frontend/src/pages/report/_components/RefreshButton.tsx
    • 리포트 새로고침 버튼 컴포넌트를 새로 추가했습니다.
  • frontend/src/pages/report/_components/UpdateModal.tsx
    • useReportStore에서 addPendingReportId 호출을 제거했습니다.
  • frontend/src/pages/report/_components/analysis/TabAnalysis.tsx
    • 리포트 상태를 useReportStore 대신 useReportStatus 훅을 통해 가져오도록 변경했습니다.
  • frontend/src/pages/report/_components/index.ts
    • ProgressBarRefreshButton 컴포넌트를 내보내도록 업데이트했습니다.
  • frontend/src/pages/report/_components/overview/TabOverview.tsx
    • 리포트 상태를 useReportStore 대신 useReportStatus 훅을 통해 가져오도록 변경했습니다.
  • frontend/src/stores/reportStore.ts
    • 리포트 스토어를 리팩토링하여 ProcessingReport 객체, completedReports 및 새로운 액션(addReport, removeReport, hideReport, addCompletedReport, removeCompletedReport, clearReports)을 관리하도록 변경했습니다. 기존 폴링 관련 상태 및 액션을 제거했습니다.
  • frontend/src/stores/toastStore.ts
    • 전역 토스트 알림을 관리하는 새로운 Zustand 스토어를 추가했습니다.
  • frontend/src/styles/animation.css
    • 애니메이션 관련 CSS 스타일(shadowPulse 포함)을 정의하는 파일을 새로 추가했습니다.
  • frontend/src/styles/global.css
    • animation.css 파일을 임포트하고 중복된 애니메이션 스타일을 제거했습니다.
  • frontend/src/types/report/new.ts
    • ReportStatusStatuses 타입을 수정하여 ideaStatus를 선택 사항으로 만들고 taskId를 제거했습니다.
  • frontend/src/utils/auth.ts
    • 로그아웃 시 useReportStore의 리포트 목록을 초기화하는 로직을 추가했습니다.
Activity
  • gomx3 님이 리포트 페이지에 실시간 프로그레스 바와 진행 상황 모달을 추가하는 작업을 진행했습니다.
  • 기존 폴링 기반의 리포트 상태 업데이트 로직을 Server-Sent Events(SSE) 기반으로 전환하는 리팩토링이 이루어졌습니다.
  • 전역 토스트 알림 시스템과 리포트 진행 상황을 관리하는 새로운 Zustand 스토어가 구현되었습니다.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gomx3 gomx3 merged commit da90295 into main Mar 7, 2026
5 checks passed
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

이번 PR은 리포트 생성 상태를 추적하는 방식을 기존의 폴링에서 SSE와 react-query 폴링을 조합한 새로운 시스템으로 리팩터링하는 중요한 변경을 포함하고 있습니다. 이를 통해 프로그레스 바와 백그라운드 진행 모달 등 사용자 경험을 크게 개선했습니다. 전반적으로 훌륭한 개선이지만, 몇 가지 유지보수성 및 안정성 측면에서 개선할 수 있는 부분을 발견하여 리뷰에 포함했습니다. 특히 환경 변수 관리, 에러 처리, 상태 관리 로직의 견고성에 대한 제안을 확인해 주시면 좋겠습니다.

@@ -0,0 +1 @@
export const SSE_URL = 'https://api.chaneling.com/sse/connect'
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

SSE_URL이 하드코딩되어 있습니다. 개발, 스테이징, 프로덕션 등 다양한 환경에 배포할 때 문제가 될 수 있습니다. 이 URL은 환경 변수로 관리하는 것이 좋습니다.

Suggested change
export const SSE_URL = 'https://api.chaneling.com/sse/connect'
export const SSE_URL = import.meta.env.VITE_SSE_URL || 'https://api.chaneling.com/sse/connect'

}, [currentStep])

const channelId = useAuthStore((state) => state.channelId)
const { mutate: deleteReport } = useDeleteMyReport({ channelId: channelId || 0 })
Copy link
Contributor

Choose a reason for hiding this comment

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

high

channelIdnull 또는 undefined일 경우 0을 기본값으로 사용하고 있습니다. 만약 0이 유효한 ID로 처리되거나 백엔드에서 에러를 유발할 수 있다면 의도치 않은 동작을 일으킬 위험이 있습니다. channelId를 그대로 전달하고, deleteReport를 호출하는 useEffect 내부(52번째 줄)에서 channelId의 유효성을 검사하는 것이 더 안전합니다.

// 52번째 줄의 useEffect 내부
if (channelId) {
  deleteReport({ reportId: item.reportId });
}
Suggested change
const { mutate: deleteReport } = useDeleteMyReport({ channelId: channelId || 0 })
const { mutate: deleteReport } = useDeleteMyReport({ channelId })

Comment on lines +33 to +36
const tokenRaw = window.localStorage.getItem(LOCAL_STORAGE_KEY.accessToken)
const token = tokenRaw ? JSON.parse(tokenRaw) : null

if (!token) return
Copy link
Contributor

Choose a reason for hiding this comment

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

high

localStorage에 직접 접근하고 있으며, 토큰이 변경될 경우 SSE 연결이 갱신되지 않는 문제가 있습니다. useAuthStore를 사용하여 토큰을 가져오고 useEffect의 의존성 배열에 token을 추가하여 이 문제를 해결하는 것이 좋습니다. 이렇게 하면 코드가 더 안정적이고 예측 가능해집니다.

수정 제안:

  1. 훅의 최상단에서 const token = useAuthStore((state) => state.accessToken)를 사용하여 토큰을 가져옵니다.
  2. useEffect 내부에서는 if (!enabled || !token) return으로 토큰 유무를 확인합니다.
  3. useEffect의 의존성 배열(66번째 줄)에 token을 추가합니다.

Comment on lines +53 to +58
onerror(err) {
if (ctrl.signal.aborted) return
console.error('SSE Error:', err)
ctrl.abort()
throw err
},
Copy link
Contributor

Choose a reason for hiding this comment

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

high

onerror 콜백에서 에러를 throw하면 React 컴포넌트 트리 전체가 중단될 수 있습니다. 이는 의도치 않은 앱 충돌로 이어질 수 있습니다. 에러를 throw하는 대신, 에러 상태를 관리하여 UI에서 적절히 처리하거나, 라이브러리의 재시도 로직을 방해하지 않도록 하는 것이 더 안전합니다. fetchEventSource 문서에 따르면 에러를 throw하면 재시도를 막게 되는데, 이것이 의도된 동작이라 할지라도 앱 크래시를 유발할 수 있으므로 에러 바운더리 등으로 감싸거나 다른 방식으로 처리하는 것이 좋습니다.

Comment on lines +12 to +15
const renderIcon = () => {
if (type === 'error') return <ErrorIcon />
return <ErrorIcon />
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

renderIcon 함수에서 type이 'error'일 때와 그렇지 않을 때 모두 ErrorIcon을 반환하고 있어 코드가 중복됩니다. 다른 토스트 타입을 지원할 계획이 없다면 코드를 단순화할 수 있습니다.

Suggested change
const renderIcon = () => {
if (type === 'error') return <ErrorIcon />
return <ErrorIcon />
}
const renderIcon = () => {
// TODO: Add icons for other toast types like 'success'
return <ErrorIcon />
}

}

// 그 외(진행 중)에는 3초마다 재요청
return 3000
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

폴링 간격 3000이 하드코딩되어 있습니다. 이 값을 상수로 추출하여 관리하면 가독성과 유지보수성이 향상됩니다. 예를 들어, 파일 상단에 const POLLING_INTERVAL = 3000;와 같이 정의하고 사용할 수 있습니다.

Suggested change
return 3000
return 3000 // TODO: Extract to a constant

Comment on lines +178 to +196
useEffect(() => {
return () => {
const result = latestResultRef.current
if (!result) return

const { overviewStatus, analysisStatus } = result

const isCompleted = overviewStatus === 'COMPLETED' && analysisStatus === 'COMPLETED'

// 내가 완료를 직접 보지 않았고,
// 완료된 상태라면 → background 완료 처리
if (isCompleted && !hasSeenCompletionRef.current) {
addCompletedReport(reportId)
removeReport(reportId)
}
}
// 의존성 비움 (unmount 전용)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

컴포넌트가 unmount될 때의 상태 동기화 로직이 복잡하고 useRef에 의존하고 있어 이해하고 유지보수하기 어려울 수 있습니다. 이 로직을 useReportStore의 action으로 옮겨 캡슐화하는 것을 고려해 보세요. 예를 들어, handleUnmount(reportId, latestResult, hasSeenCompletion)와 같은 action을 만들어 사용하면 컴포넌트는 더 단순해지고, 상태 관리 로직을 한 곳에서 테스트하고 관리하기 용이해집니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant