Skip to content

[DEV] 리포트 생성 요청 시 프로그레스바 기능 추가#273

Merged
gomx3 merged 9 commits intodevelopfrom
feat/progress-bar
Mar 7, 2026
Merged

[DEV] 리포트 생성 요청 시 프로그레스바 기능 추가#273
gomx3 merged 9 commits intodevelopfrom
feat/progress-bar

Conversation

@gomx3
Copy link
Contributor

@gomx3 gomx3 commented Mar 7, 2026

💡 Related Issue

closes #260

✅ Summary

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

📝 Description

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

💬 리뷰 요구 사항

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

@gomx3 gomx3 self-assigned this Mar 7, 2026
@gomx3 gomx3 added the ✨Feature New Feature label 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
    • ResponseReportStatus 타입 임포트가 추가되었고, getReportStatus 함수의 반환 타입이 업데이트되었습니다.
  • 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/main/useIsMobile.ts
    • 파일 이름이 frontend/src/hooks/useIsMobile.ts로 변경되었고, 내보내기 방식이 명명된 내보내기로 변경되었습니다.
  • 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
    • usePostReportById 훅에서 리포트 스토어 액션 (startGenerating, endGenerating, addPendingReportId)이 제거되었습니다.
  • frontend/src/hooks/report/usePostReportByUrl.ts
    • usePostReportByUrl 훅에서 리포트 스토어 액션 (startGenerating, endGenerating, addPendingReportId)이 제거되었습니다.
  • frontend/src/hooks/report/useReportProgress.ts
    • SSE를 사용하여 리포트 생성 진행률을 추적하는 새 훅이 추가되었습니다.
  • frontend/src/hooks/report/useReportStatus.ts
    • 리포트 상태를 가져오고 관리하는 새 훅이 추가되었습니다.
  • frontend/src/layouts/RootLayout.tsx
    • LoadingSpinnerisReportGenerating 상태 관련 로직이 제거되었고, 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
    • 리포트 상태 처리 로직이 리팩토링되었고, GeneratingModal, Metadata, GlobalReportPoller 관련 로직이 제거되었으며, ProgressBarRefreshButton이 통합되었습니다.
  • frontend/src/pages/report/_components/ProgressBar.tsx
    • 리포트 생성 진행률을 표시하는 새 프로그레스 바 컴포넌트가 추가되었습니다.
  • frontend/src/pages/report/_components/RefreshButton.tsx
    • 새로고침 버튼 컴포넌트가 추가되었습니다.
  • frontend/src/pages/report/_components/UpdateModal.tsx
    • usePostReportById 성공 콜백에서 addPendingReportId 호출이 제거되었습니다.
  • frontend/src/pages/report/_components/analysis/TabAnalysis.tsx
    • 분석 상태를 위해 useReportStore 대신 useReportStatus 훅을 사용하도록 업데이트되었습니다.
  • frontend/src/pages/report/_components/index.ts
    • 새로운 컴포넌트 (VideoSummarySkeleton, ProgressBar, RefreshButton)가 내보내기 목록에 추가되었습니다.
  • frontend/src/pages/report/_components/overview/TabOverview.tsx
    • 개요 상태를 위해 useReportStore 대신 useReportStatus 훅을 사용하도록 업데이트되었습니다.
  • frontend/src/stores/reportStore.ts
    • 리포트 스토어가 ProcessingReport 객체, completedReports 및 관련 액션을 관리하도록 리팩토링되었으며, isReportGenerating, statuses, pendingReportIds, cleanupReportIds가 제거되었습니다.
  • frontend/src/stores/toastStore.ts
    • 토스트 알림 관리를 위한 새로운 Zustand 스토어가 추가되었습니다.
  • frontend/src/styles/animation.css
    • shadow-pulse를 포함한 애니메이션을 위한 새로운 CSS 파일이 추가되었습니다.
  • frontend/src/styles/global.css
    • animation.css가 임포트되었고, 이전에 포함되었던 애니메이션 관련 스타일이 제거되었습니다.
  • frontend/src/types/report/new.ts
    • ReportStatus 타입이 수정되어 taskId가 제거되고 ideaStatus가 선택 사항이 되었습니다.
  • frontend/src/utils/auth.ts
    • 로그아웃 시 리포트를 지우기 위해 useReportStore가 추가되었습니다.
Activity
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.

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)를 사용하는 방식으로 성공적으로 리팩터링했습니다. GlobalProcessingModalGlobalToast 컴포넌트의 도입으로 사용자 경험이 크게 개선되었습니다. 코드는 전반적으로 잘 구조화되어 있지만, 설정 관리, 에러 처리, 코드 복잡성 측면에서 몇 가지 개선할 점이 보입니다. 특히 API URL 하드코딩, 훅의 에러 처리 방식, 컴포넌트 내 복잡한 로직 등은 상세 리뷰 의견을 참고하여 개선하면 코드의 안정성과 유지보수성을 더욱 높일 수 있을 것입니다.

Note: Security Review did not run due to the size of the PR.

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에 관계없이 항상 ErrorIcon을 반환하고 있습니다. 이로 인해 'success'나 'default' 같은 다른 토스트 타입에서도 에러 아이콘이 표시될 수 있습니다. 각 타입에 맞는 아이콘을 렌더링하도록 수정하는 것을 고려해 보세요.

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

Choose a reason for hiding this comment

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

medium

localStorage에서 가져온 값을 JSON.parse로 파싱할 때, 값이 유효한 JSON이 아닐 경우 에러가 발생하여 훅의 동작이 멈출 수 있습니다. try-catch 블록으로 감싸서 예외를 안전하게 처리하는 것이 좋습니다. 또한, 토큰을 가져오는 로직이 여러 곳에서 반복되므로 유틸리티 함수로 분리하는 것을 고려해볼 수 있습니다.

        const tokenRaw = window.localStorage.getItem(LOCAL_STORAGE_KEY.accessToken)
        let token = null;
        if (tokenRaw) {
            try {
                token = JSON.parse(tokenRaw);
            } catch (error) {
                console.error('Failed to parse auth token from localStorage', error);
            }
        }

Comment on lines +114 to +196
useEffect(() => {
if (!isProcessing) {
setDisplayStep(null)
firstVisibleRef.current = false
return
}

if (!firstVisibleRef.current) {
firstVisibleRef.current = true
setDisplayStep(1)

const timer = setTimeout(() => {
setDisplayStep(currentStep)
}, 500) // 최소 노출 시간

return () => clearTimeout(timer)
}

setDisplayStep(currentStep)
}, [isProcessing, currentStep])

// 리포트 생성 상태 동기화
useEffect(() => {
if (isProcessing && normalizedVideoData) {
addReport({
reportId,
videoId,
title: normalizedVideoData.videoTitle,
})
}
}, [isProcessing, reportId, videoId, normalizedVideoData, addReport])

useEffect(() => {
if (!isProcessing && rawResult) {
const { overviewStatus, analysisStatus } = rawResult

if (overviewStatus === 'COMPLETED' && analysisStatus === 'COMPLETED') {
hasSeenCompletionRef.current = true
}
}
}, [isProcessing, rawResult])

// 항상 최신 rawResult 저장
useEffect(() => {
latestResultRef.current = rawResult
}, [rawResult])

// 완료를 직접 본 경우 처리
useEffect(() => {
if (!rawResult) return

const { overviewStatus, analysisStatus } = rawResult
const isCompleted = overviewStatus === 'COMPLETED' && analysisStatus === 'COMPLETED'

if (!isCompleted) return

// 이 페이지에서 완료를 직접 본 경우
if (!isProcessing) {
hasSeenCompletionRef.current = true
removeReport(reportId)
}
}, [rawResult, isProcessing, reportId, removeReport])

// 진짜 unmount 시에만 실행
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

리포트 생성 상태를 관리하기 위한 useEffect 훅들이 많아 컴포넌트가 복잡해졌습니다. 이 로직들은 리포트의 생명주기와 관련이 깊으므로, useReportLifecycle과 같은 별도의 커스텀 훅으로 분리하면 ReportPage 컴포넌트의 가독성과 유지보수성을 크게 향상시킬 수 있습니다.

Comment on lines +58 to +62
isCurrent && [
step.id === 1 ? 'delay-0' : 'delay-500',
step.id === 4 ? 'w-full duration-300' : 'w-[85%] duration-[20000ms]',
],
!isActive && 'w-0 duration-0 delay-0'
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

프로그레스 바의 애니메이션 duration과 같은 스타일 값이 하드코딩되어 있습니다. 특히 20000ms와 같은 값은 별도의 상수로 관리하거나, 단계별 설정 객체에 포함시켜 관리하는 것이 가독성과 유지보수성 측면에서 더 좋습니다.

@gomx3 gomx3 merged commit f721256 into develop Mar 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feature New Feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DEV] 리포트 생성 요청 시 프로그레스바 기능

1 participant