✨ Feat: 사진 업로드 파이프라인 구현 - #58
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
euoonw
left a comment
There was a problem hiding this comment.
고생하셨습니다! 실패 시나리오들 꼼꼼하게 잘 커버된것같아요! 복잡한 작업인데 정말 고생하셨습니다...
| const selectedGroups = selection.groups | ||
| .map((group) => group.photos.slice(selection.excludedCounts[group.id] ?? 0)) | ||
| .filter((photos) => photos.length > 0) | ||
| .reverse(); |
There was a problem hiding this comment.
업로드는 오래된 순으로 하는게 더 효율적인가요? 상관은없는데 그냥 단순 궁금증
There was a problem hiding this comment.
아뇨 이유없습니다
ㅋㅋㅋㅋㅋㅋ
|
|
||
| try { | ||
| await dependencies.clearJob(); | ||
| } catch { | ||
| // 실패 화면을 빠져나가는 동작은 남은 임시 파일 정리에 막히지 않는다. | ||
| } |
There was a problem hiding this comment.
canceling은 서버 취소 확인 후에 정리하는건데, 나머지 경우에는 서버에는 따로 취소요청 안하고 로컬만 지우는건가요? 서버에 고아 분석이 남는 경우가 생길 수도 있을 것 같아요.
There was a problem hiding this comment.
로컬 잡 삭제 시 서버 조회 한번 하는 식으로 반영해 놓았습니다
| <View className="items-center justify-center flex-1 px-6 bg-black/70"> | ||
| <View accessibilityViewIsModal className="w-full gap-6 p-6 bg-gray-900 rounded-3xl"> | ||
| <Text className="text-center text-white text-body-01"> | ||
| 분석 중인 사진들이 있어요.{`\n`}분석을 계속 진행할까요? | ||
| </Text> | ||
|
|
||
| <Pressable | ||
| accessibilityRole="button" | ||
| className="items-center justify-center py-3 bg-white rounded-full" | ||
| onPress={onConfirm} | ||
| > | ||
| <Text className="text-black text-body-03">확인</Text> | ||
| </Pressable> | ||
| </View> |
There was a problem hiding this comment.
취소 옵션은 없어도 괜찮을까요? 지금은 재진입시에 무조건 이어가기 밖에 선택지가 없는건데, 분석을 그만하고 싶어할 경우도 생각해봐도 좋을 것 같아요
무조건 분석 계속하게끔 하는게 의도된 정책이라면 상관없습니다
There was a problem hiding this comment.
상태마다 동작이 달라서 그냥 한번 끝을 보게 하는 식으로 하는게 단순해서 이렇게 두었습니다
cchaeyoung
left a comment
There was a problem hiding this comment.
업로드 파이프라인이 생각보다 복잡하네요.. 고생 많으셨어요!
| try { | ||
| await dependencies.startAnalysis(analysisId); | ||
| } catch (error) { | ||
| if (error instanceof NetworkError) return resolveStartOutcome(analysisId, dependencies); | ||
| throw error; | ||
| } |
There was a problem hiding this comment.
PREPARING과 CANCELING 단계에서는 실패 원인(NetworkError/HttpError)과 관계없이 서버 상태를 확인한 뒤 로컬 정리를 하는데, STARTING은 NetworkError일 때만 서버 상태를 확인하는 것 같아요.
STARTING만 다르게 처리하신 이유가 있을까요?
There was a problem hiding this comment.
HttpError 일 때는 모두 서버를 확인하지 않습니다
예외적으로 CANCELING 일 때 409일때만 한번 확인합니다!
🔍 PR 요약
사진 선택 화면에서 고른 그룹을 압축해 GCS에 업로드하고, 서버 분석을 시작한 뒤 완료될 때까지 기다리는 모바일 업로드 파이프라인을 연결했습니다.
업로드 도중 앱이 종료돼도 저장된 단계부터 이어갈 수 있도록 작업 스냅샷과 이벤트 로그를 영속화하고, presigned URL 만료·일시적 PUT 실패·분석 시작 경계를 복구하도록 구성했습니다.
apps/mobile/src/features/photo-upload/model 위주로만 보면 됩니다.
upload-job.ts: 상태와 전이 규칙upload-runner.ts: 파이프라인 실행기upload-storage.ts: 상태 영속화photo-upload-service.ts: 화면에서 사용하는 진입점stateDiagram-v2 [*] --> PREPARING: CTA / 작업 저장 PREPARING --> PUTTING: 분석 ID와 사진 ID 매핑 기록 PUTTING --> PUTTING: 사진 업로드 완료 기록 PUTTING --> STARTING: 모든 사진 업로드 완료 PUTTING --> CANCELING: 사진 업로드 최종 실패 STARTING --> [*]: 분석 시작 확인 / 로컬 작업 삭제 CANCELING --> [*]: 서버 취소 확인 / 로컬 작업 삭제 CANCELING --> CANCELING: 취소 확인 실패 / 다음 실행에서 재시도 note right of PREPARING 분석 생성 응답이 유실되면 서버 active 상태와 대조 end note note right of PUTTING 앱 재실행 시 완료된 사진은 건너뛰고 미완료 사진만 다시 업로드 end note note right of STARTING start 응답이 유실되면 서버 상태 확인 후 재호출 여부 결정 end note🧾 관련 이슈
🧠 의도 및 배경
한 번에 90~100개 그룹, 실제로는 그룹 내부 사진까지 100장 이상을 처리하기 때문에 CTA를 누른 뒤 압축부터 시작하면 대기 시간이 길어집니다. 사진을 고르는 동안 후보 사진을 미리 압축하고, CTA에서는 선택 상태만 고정한 뒤 BoardScreen으로 바로 이동하도록 했습니다.
또한 모바일 업로드는 네트워크 전환이나 앱 종료로 중단될 수 있으므로 메모리 Promise만으로 관리하지 않고, 로컬 파일과 서버 photoId 매핑 및 업로드 성공 기록을 디스크에 남겨 재진입 시 복구하도록 했습니다.
🛠️ 주요 변경 사항
boardId를 브릿지 payload로 전달하고 PhotoSelectScreen에서 업로드 대상 보드를 확정POST /analysis요청 형태로 변환job.json + events.log + 작업 이미지로 영속화PREPARING → PUTTING → STARTING → CANCELING상태를 순수 reducer로 복구POST /analysis응답의 presigned URL로 GCS PUT 수행/reissue결과로 재시도하며, 동시 403은 재발급 요청 한 번을 공유/analysis/{id}를 DELETE하고 전체 작업 취소POST /analysis/{id}/start호출 및 2초 간격 상태 폴링COMPLETED이면 보드 웹뷰 표시FAILED이면 재선택 또는 보드 이동이 가능한 실패 모달 표시/analysis/active를 확인해 진행 중 업로드 복구/reissueURL로 재업로드/start중복 호출 방지POST /analysis응답이 유실되면/analysis/active로 서버 상태 확인ANALYZING이면 기존 분석을 이어서 대기UPLOADING분석이 있으면 취소한 뒤 분석을 다시 생성/start응답이 유실되면 분석 상태를 조회해 이미 시작된 요청은 중복 호출하지 않음CANCELING작업을 보존✅ 검증
/analysis/{id}/start202 응답 확인🚨 트러블슈팅 (선택)
기타