Skip to content
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

[#156] - 포트폴리오 피드백 플로우 구현 #178

Merged
merged 14 commits into from
Mar 26, 2025

Conversation

qq8721443
Copy link
Collaborator

📌 연관된 이슈 번호

close #156

🌱 주요 변경 사항

  • feedback 도메인을 추가했습니다.
    • 피드백 조회 API 호출 함수 (use-get-portfolio-feedback), 최근 피드백 조회 API 호출 함수 (use-get-feedback-history), 피드백 시작 요청 API 호출 함수(use-get-start-feedback) 를 해당 도메인 하위로 이동했습니다.
  • 포트폴리오 업로드 -> 포트폴리오 피드백 요청 -> 포트폴리오 피드백 완료의 플로우를 구현했습니다.
    • 피드백 상태를 zustand를 사용해 전역으로 관리합니다.
    • 해당 상태에 따른 동작은 FeedbackStateObserver 컴포넌트가 수행합니다.
    • 기존 loading 페이지를 upload 페이지에서 조건부 렌더링으로 출력하도록 수정했습니다.

image

📸 스크린샷 (선택)

2025-03-25.12.59.26.mov

🗣 리뷰어에게 할 말 (선택)

  • 현재 피드백 조회 API가 더미 데이터를 내려주고 있어서 실제 테스트는 불가능합니다.
  • 로그인 기능 코드와 머지 되지 않아서 테스트할 때 임시로 Authorization 헤더에 액세스 토큰값을 문자열로 직접 넣어주었습니다.
  • 서버 상태는 tanstack-query가 수행하고, zustand는 전역 상태만 관리하고, 해당 상태에 따른 동작은 FeedbackStateObserver가 수행하도록 나누려 했는데, 좀 모호한 것 같기도 합니다. 해당 부분 피드백해주시면 감사하겠습니다.

@qq8721443 qq8721443 self-assigned this Mar 24, 2025
Copy link

vercel bot commented Mar 24, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
onepiece-fe ⬜️ Ignored (Inspect) Visit Preview Mar 26, 2025 2:16pm

@minh0518
Copy link
Member

minh0518 commented Mar 25, 2025

코드 잘 보았습니다. 무엇보다 저렇게 다이어그램으로 flow를 시각화 해주시니까 정말 읽기 편했어요 :)

피드백 요청하신 부분에 대해 우선 제 생각은 다음과 같습니다



  1. FeedbackStateObserver로 따로 분리한게 매우 좋다 생각합니다.

기능 기획대로 피드백 요청을 polling형태로 진행하면서 모든 페이지를 이동해야 하므로 옵져버 컴포넌트를 최상단에 배치해서 별도로 분리하는 패턴이 저도 최적이라 생각합니다.




  1. tanstack-query, zustand, localstorage의 혼재

언뜻 봤을 때, 각 기술은 아래의 사용처를 가지고 있는것 같습니다.

  • tanstack-query: 최근 피드백 리스트, 남은 피드백 횟수 같은 정보를 캐싱
  • zustand: polling을 진행하며 현재 피드백 상태를 동기화
  • localstorage: 유저 이탈 케이스를 대비하기 위한 체크포인트

3가지의 저장소가 동시에 동작하는 것 같기에 통합된 저장소가 있으면 좋을 것 같긴 하지만, 제가 봤을때도 지금 상황에는 어쩔 수 없는 것 같습니다.

  • tanstack-query는 UI에 반응하기 위해 캐싱 데이터로 1차적인 값을 보여줘야 하고

  • zustand는 polling을 진행하며 어쨌든 리액트의 상태 싸이클과 동기화돼서 페이지의 흐름을 조정해야 합니다.

  • 유저 이탈 시에는 리액트의 상태 싸이클과 무관하게 영속성을 가져야 하므로 localstorage를 사용할 수 밖에 없습니다.

그러므로 현재 정기님이 구현하신 기능 자체가 애초에 수많은 로직과 데이터가 사용되는 부분이라 다양한 저장소를 복합적으로 사용할 수 밖에 없는 것 같습니다. 저도 틀릴 수 있으니 보다 좋은 아이디어 있으시면 언제든 알려주세요!

<Outlet />
<Footer />
</div>
<FeedbackStateObserver>
Copy link
Member

Choose a reason for hiding this comment

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

혹시 FeedbackStateObserver 는 CommonLayout이 아닌, 라우팅 코드에서 처리하는게 어떠신가요?

왜냐하면, CommonLayout은 오로지 레이아웃에 대한 로직만 담당하고 그 외 FeedbackStateObserver같은 공통 기능들은 라우팅 코드에서 별도로 처리하는게 관심사 분리 측면에서 더 낫다고 생각이 듭니다.

마치 인증 관련 로직을 라우팅에서 처리한 것처럼요.

Copy link
Member

Choose a reason for hiding this comment

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

저도 이 부분은 동일하게 생각합니다!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵 좋습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

3616734 GlobalRouteConfig 컴포넌트를 만들어 라우트 내부에 전역으로 적용되어야 할 컴포넌트를 모아두도록 했습니다!

if (feedbackId) {
// 5초마다 피드백 상태 확인
// 5초는 임의로 설정한 값
intervalId = setInterval(() => {
Copy link
Member

@minh0518 minh0518 Mar 25, 2025

Choose a reason for hiding this comment

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

관련 아티클
setInterval보단 중첩 setTimeout를 사용하는건 어떤가요?

아티클에 언급되어 있듯이, 실행 간격을 확실히 보장받을 수 있기 때문입니다. 특히 API호출같은 사이드 이펙트가 주기적으로 실행되는 경우 내부 동작 시간이 예측 불가능하기 때문에 중첩 setTimeout이 좀 더 좋아보입니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

호오 Polling의 경우 실행 시간의 보장이 크게 중요하지 않다고 생각해서 신경쓰지 않고 있었는데, 수정해두겠습니다!
좋은 피드백 감사합니다! 🙇

onError?: (error: Error) => unknown;
}

export default function PortfolioUpload({ onSuccess, onError }: PortfolioUploadProps) {
Copy link
Member

Choose a reason for hiding this comment

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

onSuccess와 onError를 PortfolioUpload 에서 직접 선언하지 않고 props로 받은 이유에 대해 알고 싶습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

그러게요..? 평소에 API 호출을 페이지 컴포넌트(여기서는 업로드 페이지 컴포넌트)에서 하다보니 호출 함수를 외부에 정의하게 되고, 성공 실패 로직을 PortfolioUpload 컴포넌트 외부에서 받아야겠다고 생각했습니다.
지금보니 굳이 그럴 필요가 없었네요! 수정해두겠습니다! 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

0d2f698 수정했습니다!

Copy link
Member

@subsub-e subsub-e left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!! 의도에 맞게 zustand, react query, localstorage 사용하신 부분이 좋은거 같아요!! 다이어그램도 한번에 플로우가 파악 가능해서 인상깊습니다.

<Outlet />
<Footer />
</div>
<FeedbackStateObserver>
Copy link
Member

Choose a reason for hiding this comment

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

저도 이 부분은 동일하게 생각합니다!!

@@ -0,0 +1,53 @@
import { create } from 'zustand';

type ProcessState = 'PENDING' | 'IN_PROGRESS' | 'ERROR' | 'COMPLETE' | 'IDLE';
Copy link
Member

Choose a reason for hiding this comment

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

ProcessState 에서 PENDING, IN_PROGRESS가 각각 어떤 역할을 하는지 궁금합니다!! 왜 나뉘어진건가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이 부분은 서버에서 내려주는 상태 값과 동일한 값을 가지도록 했는데요!
서버의 의도는
PENDING: 피드백 요청은 받았지만 AI에게 전달하지 않은 상태
IN_PROGRESS: 피드백 요청 받은 후 AI가 포트폴리오를 분석하는 중인 상태
라고 합니다!

현재 서버에서는 overallStatusprojectStatus 두 개의 상태 값을 내려주고 있습니다.
위 값을 토대로 클라이언트에서는
overallStatus === 'PENDING' && projectStatus === 'PENDING' 이면 PENDING 상태
overallStatus === 'IN_PROGRESS' || projectStatus === 'IN_PROGRESS' 이면 IN_PROGRESS 상태
라고 판단하고 있습니다!


const getFeedbackState = useCallback(() => {
const { overallStatus, projectStatus } = feedback?.result || {};
if (overallStatus === 'COMPLETE' && projectStatus === 'COMPLETE') {
Copy link
Collaborator

Choose a reason for hiding this comment

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

피드백 생성 성공, 생성 실패, 생성 중, 생성 대기 중 조건에 이름을 붙이는 방식으로 적용하면 가독성이 더 좋아질 것 같아요!

const isComplete = overallStatus === 'COMPLETE' && projectStatus === 'COMPLETE';
const isError = overallStatus === 'ERROR' || projectStatus === 'ERROR';
const isInProgress = overallStatus === 'IN_PROGRESS' || projectStatus === 'IN_PROGRESS';
const isPending = overallStatus === 'PENDING' && projectStatus === 'PENDING';

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

무지성 코드 작성에 절여져서 이런 부분 신경 안쓰고 넘어갈 때가 많은데 앞으로 줄여보도록 하겠습니다!
감사합니다! 🙇

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

dbb7e46 수정했습니다!

@qq8721443 qq8721443 force-pushed the feat-feedback-flow branch from dbb7e46 to 3616734 Compare March 26, 2025 14:16
@minh0518 minh0518 merged commit 560f238 into develop Mar 26, 2025
2 checks passed
@minh0518 minh0518 deleted the feat-feedback-flow branch March 26, 2025 14:19
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.

[Feature] - 포트폴리오 파일 업로드 -> 피드백 요청 -> 피드백 조회 플로우 구현
4 participants