Skip to content

[PR] 과제 업로드 페이지(UploadPage) 리펙토링#66

Merged
Dobbymin merged 13 commits intomainfrom
refector#54-upload-page-refector
Apr 6, 2026
Merged

[PR] 과제 업로드 페이지(UploadPage) 리펙토링#66
Dobbymin merged 13 commits intomainfrom
refector#54-upload-page-refector

Conversation

@TTOCHIwas
Copy link
Copy Markdown
Collaborator

@TTOCHIwas TTOCHIwas commented Mar 30, 2026

📝 요약 (Summary)

업로드 페이지의 구조를 정리해서 page가 더 단순한 역할만 하도록 개선했습니다.
업로드 폼 상태는 hook으로, 화면은 section과 하위 컴포넌트로 나누고, 파일 업로드 검증도 함께 보완했습니다.

✅ 주요 변경 사항 (Key Changes)

  • UploadPage에서 폼 상태와 제출 흐름을 useUploadPageForm으로 분리했습니다.
  • 업로드 화면을 title, assignment type, content/file, submit section으로 나눴습니다.
  • 파일 업로드 UI를 하위 컴포넌트로 분리했습니다.
  • 업로드 페이지에서 과제 유형 기본값과 옵션 목록을 한 곳에서 관리하도록 정리했습니다.
  • PDF, 이미지 파일만 업로드되도록 파일 형식 검증을 추가했습니다.
  • 파일 타입 검증 로직을 공통 utils로 분리했습니다.
  • 파일 개수 제한을 초과하거나 이미 가득 찬 상태에서 파일 추가를 시도할 경우 안내하는 토스트를 추가했습니다.

💻 상세 구현 내용 (Implementation Details)

1. 업로드 페이지 폼 로직 분리

useUploadPageForm으로 업로드 폼 상태와 제출 흐름을 분리했습니다.

  • 제목 입력 상태
  • 과제 유형 선택 상태
  • 내용 입력 상태
  • 파일 업로드 상태 조합
  • 제출 가능 여부 계산
  • 단계 수 계산
  • 업로드 시작 시 프로젝트 생성 요청 및 로딩 페이지 이동
  return {
    fields: {
      title,
      selectedType,
      content,
      setTitle,
      setSelectedType,
      setContent,
    },
    files: {
      uploadedFiles,
      isDragging,
      removeFile,
      handleDragOver,
      handleDragLeave,
      handleDrop,
      handleFileInput,
    },
    submit: {
      isPending,
      canSubmit,
      stepCount,
      startAnalysis,
    },
  };

반환값은 fields / files / submit으로 나누어 page와 section에서 읽기 쉽게 정리했습니다.

2. 업로드 페이지 화면 분리

업로드 화면을 아래 section으로 나눴습니다.

  • UploadTitleSection
image
  • UploadAssignmentTypeSection
image
  • UploadContentAndFileSection
image
  • UploadSubmitSection
image

이제 UploadPage는 직접 상태와 UI를 모두 들고 있지 않고, hook 호출과 section 조립만 합니다.

3. 파일 업로드 UI 분리

파일 업로드 영역은 하위 컴포넌트로 나눴습니다.

  • UploadFileDropzone
image
  • UploadFileList
image
  • UploadFileItem
image

이를 통해 UploadContentAndFileSection은 내용 입력과 파일 업로드 영역을 조합하는 역할만 하도록 정리했습니다.

4. 과제 유형과 파일 업로드 기준 정리

업로드 페이지에서 과제 유형 기본값과 옵션 목록을 한 곳(assignment-types)에서 관리하도록 정리했습니다.

  • UPLOAD_ASSIGNMENT_TYPES
  • DEFAULT_UPLOAD_ASSIGNMENT_TYPE
// assignment-types.ts
export const UPLOAD_ASSIGNMENT_TYPES: UploadAssignmentTypeOption[] = [
  { label: "글쓰기형", Icon: FilePenIcon },
  { label: "논문형", Icon: AcademicCapIcon },
  { label: "발표형", Icon: BoardTeacherIcon },
  { label: "실습형", Icon: BeakerIcon },
  { label: "요약형", Icon: DocumentTextIcon },
  { label: "학습형", Icon: StudyIcon },
];

export const DEFAULT_UPLOAD_ASSIGNMENT_TYPE: AssignmentType =
  UPLOAD_ASSIGNMENT_TYPES[0]!.label;

파일 업로드 허용 형식도 constants로 정리했습니다.

  • UPLOAD_FILE_ACCEPT
  • UPLOAD_FILE_TYPE_LABEL
// upload-files.ts
export const UPLOAD_FILE_ACCEPT = ".pdf,image/*";
export const UPLOAD_FILE_TYPE_LABEL = "PDF, 이미지";

이렇게 해서 page, section, 파일 input, 검증 로직이 같은 기준을 사용하도록 맞췄습니다.

5. 파일 형식 검증, 업로드 제한 안내 추가

기존에는 파일 선택 창의 형식만 설정되어 있었고, 파일 추가 시에는 실제 검증이 없었습니다.
이번에는 파일 형식 판별 로직을 공통 utils로 분리하고, useUploadFiles에서 직접 검증하도록 수정했습니다.

// file-type.ts
export const isPdfUploadFile = (file: File) =>
  file.type === "application/pdf" ||
  file.name.toLowerCase().endsWith(".pdf");

export const isImageUploadFile = (file: File) =>
  file.type.startsWith("image/");

export const isSupportedUploadFile = (file: File) =>
  isPdfUploadFile(file) || isImageUploadFile(file);
// useUploadFiles.ts
const addFiles = (newFiles: File[]) => {
  const validFiles = newFiles.filter(isSupportedUploadFile);
  const invalidFileCount = newFiles.length - validFiles.length;

  if (invalidFileCount > 0) {
    toaster.create({
      type: "error",
      description: `${UPLOAD_FILE_TYPE_LABEL} 파일만 업로드할 수 있습니다.`,
    });
  }

  if (validFiles.length === 0) {
    return;
  }

  const remaining = maxFiles - uploadedFiles.length;

  if (remaining <= 0) {
    toaster.create({
      type: "warning",
      description: `최대 ${maxFiles}개의 파일만 업로드할 수 있습니다.`,
    });
    return;
  }

  const toAdd = validFiles.slice(0, remaining);

  if (validFiles.length > remaining) {
    toaster.create({
      type: "warning",
      description: `최대 ${maxFiles}개까지만 업로드할 수 있어 일부 파일은 제외되었습니다.`,
    });
  }

  setUploadedFiles((prev) => [
    ...prev,
    ...toAdd.map((file) => ({ id: crypto.randomUUID(), file })),
  ]);
};
image
  • PDF 허용
  • 이미지 허용
  • 허용되지 않은 파일은 토스트 안내
  • 유효한 파일만 업로드 목록에 추가
  • 최대 개수를 초과한 경우에는 일부 파일만 추가하고 안내 토스트 표시
  • 이미 최대 개수에 도달한 경우에도 안내 토스트 표시

또한 파일 아이콘 표시도 같은 판별 함수를 사용하도록 했습니다.

🚨 트러블 슈팅 (Trouble Shooting)

해당 없음

⚠️ 알려진 이슈 및 참고 사항 (Known Issues & Notes)

현재 업로드 파일 검증은 파일 형식만 다루고 있습니다.
다음 이슈에서 파일 크기 제한을 추가로 진행해야할 것 같습니다.

📸 스크린샷 (Screenshots)

해당 없음

#️⃣ 관련 이슈 (Related Issues)

@TTOCHIwas TTOCHIwas requested a review from Dobbymin March 30, 2026 04:22
@TTOCHIwas TTOCHIwas self-assigned this Mar 30, 2026
@TTOCHIwas TTOCHIwas added the ⚒️ Refactor 작업한 코드를 리팩토링 하는 경우 label Mar 30, 2026
@TTOCHIwas TTOCHIwas linked an issue Mar 30, 2026 that may be closed by this pull request
Copy link
Copy Markdown

@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

This pull request refactors the UploadPage by modularizing the UI into specialized components and extracting logic into custom hooks, specifically useUploadFiles and useUploadPageForm. The changes introduce a more structured approach to handling file uploads and form state. Review feedback highlights an inconsistency in PDF file detection between the upload hook and the UI component, which could lead to incorrect icon rendering. It also suggests enhancing the user experience by adding toast notifications when the file upload limit is reached.

@Dobbymin Dobbymin merged commit d8d163e into main Apr 6, 2026
1 check passed
@Dobbymin Dobbymin deleted the refector#54-upload-page-refector branch April 6, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚒️ Refactor 작업한 코드를 리팩토링 하는 경우

Projects

None yet

Development

Successfully merging this pull request may close these issues.

과제 업로드 페이지(UploadPage) 리펙토링

2 participants