Skip to content

[REFACTOR] MediaUpload, ImageGallery 컴포넌트 사용성개선#82

Merged
yougyung merged 5 commits intomainfrom
refactor/#81-image
Oct 19, 2025
Merged

[REFACTOR] MediaUpload, ImageGallery 컴포넌트 사용성개선#82
yougyung merged 5 commits intomainfrom
refactor/#81-image

Conversation

@yougyung
Copy link
Copy Markdown
Collaborator

@yougyung yougyung commented Oct 14, 2025

🔥 연관 이슈

🚀 작업 내용

  • 다중 업로드시 배경요소 넘침 현상 제어 e371ca8
AS-IS TO-BE
스크린샷 2025-10-14 오후 7 11 46 스크린샷 2025-10-14 오후 7 08 05
  • 아이콘 curor-pointer 적용 7da803b
  • dot 크기 및 간격 개선 f2cbe6f
AS-IS TO-BE
스크린샷 2025-10-14 오후 7 13 48 스크린샷 2025-10-14 오후 7 14 19
  • 편집모드를 지원하기 위해서 previewFiles, previewUrls를 속성으로 전달받고 onFileChange을 통해 부모에서 상태를 관리할 수 있도록 수정했어요. fdcbad2

Summary by CodeRabbit

  • Style

    • ImageGallery: 주요 이미지 영역 하단 여백 확대, 헤더 도트의 반응형 간격 및 크기/호버 조정. 도트와 이전/다음 화살표에 커서 포인터 적용으로 조작감 개선.
    • MediaUpload: 컨테이너에 내부 스크롤(overflow-auto) 추가로 콘텐츠 초과 시 스크롤 가능.
  • Refactor

    • MediaUpload: 미리보기 기반 외부 제어형으로 전환되어 업로드/삭제 동작이 외부로 전달됨(통합 방식 변경).
    • MediaPreview: 미디어 타입 자동 감지 추가로 비디오 재생 지원 및 빈 파일 처리 강화.
  • Docs/Stories

    • MediaUpload 예제 스토리에 미리보기 동기화 래퍼 추가.

@yougyung yougyung self-assigned this Oct 14, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 14, 2025

Walkthrough

ImageGallery의 프레젠테이션 조정, MediaUpload의 내부 상태 제거 후 prop·콜백 기반 제어로 API 변경, MediaUploadPreview에 미디어 타입 감지(파일 타입 또는 HEAD 요청) 추가 및 스토리에 외부 제어용 래퍼 도입이 포함됩니다.

Changes

Cohort / File(s) Summary
ImageGallery UI 변경
src/shared/ui/ImageGallery/ImageGallery.tsx
마진 조정(mt-2 → mt-3), 헤더 도트 간격 업데이트(md:gap-2.5), 도트에 cursor-pointer와 md 스케일 조정(md:h-2.5 md:w-2.5), Prev/Next 버튼에 cursor-pointer 추가 — 프레젠테이션 수정.
MediaUpload API/로직 리팩터
src/shared/ui/MediaUpload/MediaUpload.tsx
내부 상태(selectedFiles/previewUrls) 제거 및 prop 기반 전환(initialFilespreviewFiles, initialPreviewUrlspreviewUrls), onFileUpload 제거 → onFileChange(files, previewUrls) 추가, 파일 변경/삭제/리셋 동작을 onFileChange로 전달, 컨테이너에 overflow-auto 추가, RefreshButton 핸들러에서 e.preventDefault() 사용.
MediaUpload 스토리: 상태 래퍼 추가
src/shared/ui/MediaUpload/MediaUpload.stories.tsx
MediaUploadStoryWrapper 추가(내부 previewFiles/previewUrls 상태 보유 및 onFileChange 동기화), 모든 관련 스토리에서 래퍼 사용으로 프리뷰 제어 방식 변경(기존 initial* props 제거/대체).
MediaUploadPreview: 미디어 타입 감지 및 안전성 개선
src/shared/ui/MediaUpload/MediaUploadPreview.tsx
MediaPreviewItem에 isVideo 판단 로직 추가(파일 존재 시 file.type 검사, 없으면 HEAD 요청으로 Content-Type 판별), file prop optional로 변경, 멀티 모드에서 previewUrls 기준 반복으로 안전하게 렌더링, getMimeType(url) 헬퍼 추가.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant StoryWrapper as MediaUploadStoryWrapper
  participant MediaUpload
  participant Parent as App/Store

  Note over User,MediaUpload: 스토리 환경의 업로드 흐름
  User->>MediaUpload: 파일 선택
  MediaUpload->>StoryWrapper: onFileChange(files, previewUrls)
  StoryWrapper->>StoryWrapper: previewFiles/previewUrls 갱신
  StoryWrapper->>Parent: (선택적) 서버/상태 동기화
  StoryWrapper->>MediaUpload: 최신 previewFiles/previewUrls 전달 (props)
Loading
sequenceDiagram
  participant MediaPreviewItem
  participant CDN as URLHost

  Note over MediaPreviewItem,CDN: previewUrl의 미디어 타입 판정
  alt file 제공됨
    MediaPreviewItem->>MediaPreviewItem: file.type.startsWith('video/') 검사
  else file 없음
    MediaPreviewItem->>CDN: HEAD request (previewUrl)
    CDN-->>MediaPreviewItem: Content-Type 헤더 응답
    MediaPreviewItem->>MediaPreviewItem: isVideo 판정
  end
  MediaPreviewItem->>MediaPreviewItem: video 또는 img 렌더링
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ujinsim
  • keemsebin

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning raw_summary에 따르면 PR의 코드 변경 사항 중에는 이슈 #81의 명시된 요구사항 범위를 벗어나는 중대한 변경들이 포함되어 있습니다. MediaUpload 컴포넌트의 API가 상태 기반 관리에서 props 기반 제어로 완전히 리팩토링되었으며(initialFiles/initialPreviewUrls를 previewFiles/previewUrls로 변경, onFileUpload를 onFileChange로 변경), MediaUploadPreview에 비디오 감지 로직이 추가되었고, MediaUpload.stories.tsx에 새로운 wrapper 컴포넌트가도입되었습니다. 이러한 변경들은 PR 설명의 작업 내용(다중 업로드 넘침 제어, cursor-pointer, dot 개선)에 명시되지 않았습니다. MediaUpload의 광범위한 API 리팩토링과 비디오 감지 기능 추가는 이슈 #81의 UI/UX 개선 요구사항과 별개의 작업으로 보입니다. 이러한 주요 변경사항들이 PR 설명에 명시되지 않았으므로, PR 설명을 보충하여 모든 변경 사항의 의도와 범위를 명확히 하거나, 해당 리팩토링을 별도의 PR로 분리하는 것을 권장합니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description Check ⚠️ Warning 풀 리퀘스트 설명이 일부 필수 섹션을 충족하지 못합니다. 템플릿의 5가지 주요 섹션 중 PR 제목, 🤔 고민했던 내용, 💬 리뷰 중점사항이 완전히 비어 있습니다. 제공된 설명에는 연관 이슈(#81)와 구체적인 작업 내용(다중 업로드 배경요소 제어, 아이콘 cursor-pointer, dot 크기 개선)이 포함되어 있으며, 커밋 참조와 AS-IS/TO-BE 비교 이미지로 변경 사항을 잘 시각화했습니다. 그러나 PR 제목이 없고 의사결정 과정과 리뷰어의 검토 포인트에 대한 정보가 부족합니다. PR 제목을 [REFACTOR] 타입으로 추가하고, 🤔 고민했던 내용 섹션에 이 리팩토링 과정에서 고려한 사항들(예: 컴포넌트 상태 관리 패턴 변경 이유, API 설계 결정)을 작성해주세요. 또한 💬 리뷰 중점사항 섹션에 리뷰어가 특히 주목해야 할 부분(예: public API 변경 영향도, 기존 사용처 호환성)을 명시해주시기 바랍니다.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed PR 제목 "[REFACTOR] MediaUpload, ImageGallery 컴포넌트 사용성개선"은 변경 사항의 주요 내용을 명확하게 요약하고 있습니다. [REFACTOR] 타입이 명시되어 있으며, 영향을 받는 컴포넌트(MediaUpload, ImageGallery)와 작업의 목적(사용성개선)이 구체적으로 드러나 있습니다. 제목은 간결하고 명확하여 팀원이 커밋 히스토리를 스캔할 때 주요 변경 사항을 쉽게 파악할 수 있습니다.
Linked Issues Check ✅ Passed 연결된 이슈 #81의 요구사항은 세 가지입니다: 다중 업로드 배경 넘침 현상 제어, 아이콘 cursor-pointer 적용, dot 크기 및 gap 개선(스트라이크스루된 "상위 이벤트 전파 이슈" 제외). 코드 변경 사항을 검토하면 ImageGallery에서 cursor-pointer 스타일링과 gap 개선이 확인되고, MediaUpload에서 container에 overflow-auto가 추가되어 이슈의 요구사항을 충족하는 것으로 보입니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#81-image

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Oct 14, 2025

Update: 2025년 10월 18일 22시 48분 58초
Storybook 미리보기: https://677a9a76c58a295e9421d3ef-uxtueknlua.chromatic.com/

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9345ad8 and f2cbe6f.

📒 Files selected for processing (2)
  • src/shared/ui/ImageGallery/ImageGallery.tsx (4 hunks)
  • src/shared/ui/MediaUpload/MediaUpload.tsx (1 hunks)
🔇 Additional comments (3)
src/shared/ui/ImageGallery/ImageGallery.tsx (3)

55-55: LGTM!

여백 증가로 시각적 개선이 이루어졌습니다.


65-65: LGTM!

반응형 간격 조정과 dot 크기 개선, cursor-pointer 추가로 사용성이 향상되었습니다. cn() 함수를 사용한 조건부 스타일링도 적절합니다.

Also applies to: 74-77


99-99: LGTM!

arrow 버튼에 cursor-pointer를 명시적으로 추가하여 일관성이 개선되었습니다.


return (
<div className="max-h-[500px] w-full">
<div className="max-h-[500px] w-full overflow-auto">
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

세로 스크롤만 필요하다면 overflow-y-auto 사용을 권장합니다.

overflow-auto는 가로/세로 양방향 스크롤을 활성화합니다. 의도한 것이 세로 스크롤만이라면 overflow-y-auto가 더 정확한 선택입니다.

다음 diff를 적용하여 세로 스크롤만 활성화하세요:

-    <div className="max-h-[500px] w-full overflow-auto">
+    <div className="max-h-[500px] w-full overflow-y-auto">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="max-h-[500px] w-full overflow-auto">
<div className="max-h-[500px] w-full overflow-y-auto">
🤖 Prompt for AI Agents
In src/shared/ui/MediaUpload/MediaUpload.tsx around line 120, the div currently
uses overflow-auto which enables both horizontal and vertical scrolling; replace
it with overflow-y-auto to allow only vertical scrolling (keep the existing
max-h-[500px] and w-full intact) so horizontal scroll is prevented while
preserving vertical overflow behavior.

Copy link
Copy Markdown
Contributor

@keemsebin keemsebin left a comment

Choose a reason for hiding this comment

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

수고하셨습니다~!

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/shared/ui/MediaUpload/MediaUpload.stories.tsx (1)

34-41: Story args control 타입 정합성 제안

acceptedFormats는 string[]입니다. Storybook control을 'text' 대신 'object'(또는 'array')로 두면 실사용 타입과 일치합니다. 기능엔 지장 없으나 오입력 방지에 도움 됩니다.

src/shared/ui/MediaUpload/MediaUploadPreview.tsx (2)

57-61: 미디어 프리뷰 최적화(경량 메타데이터만 로드)

프리뷰 용도라면 video에 preload="metadata"를 지정해 초기 로드를 줄일 수 있습니다.

-        <video
+        <video
           src={previewUrl}
           controls
+          preload="metadata"
           className="h-full max-h-[500px] w-full max-w-[500px] object-contain"
         />

19-31: 멀티 프리뷰 삭제 버튼 접근성 OK

type="button", 시맨틱 버튼, 닫기 아이콘 제공 모두 적절합니다. aria-label 추가를 고려하면 더 좋아집니다(예: "미리보기 삭제").

src/shared/ui/MediaUpload/MediaUpload.tsx (3)

88-101: 이벤트 핸들러에서 예외 throw 금지 — UX 중단 및 오류 경계 미적용

React 이벤트 핸들러에서 throw는 사용자 흐름을 끊고 에러 바운더리로도 포착되지 않습니다. 파일 입력을 초기화하고 조용히 무시하는 쪽이 안전합니다.

   const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
     const files = Array.from(event.target.files || []);
     if (files.length === 0) {
       return handleReset();
     }
     const oversizedFiles = files.filter((file) => file.size / GB > maxSize);
     if (oversizedFiles.length > 0) {
-      throw new Error(`${maxSize}GB 이하의 파일로 등록해주세요.`);
+      // 용량 초과: 입력 초기화 후 종료
+      event.currentTarget.value = '';
+      return;
     }

추가로 검증 실패콜백(onError 등)을 도입하면 소비자가 메시지를 표시할 수 있습니다.


170-177: submit 방지 목적이면 type="button"으로 명시

button 기본 type은 submit입니다. preventDefault 대신 type="button"을 지정해 폼 제출 부작용을 근본적으로 차단하세요.

   return (
     <Flex
       as="button"
-      onClick={(e) => {
-        e.preventDefault();
-        if (isSelected) handleReset();
-      }}
+      type="button"
+      onClick={() => {
+        if (isSelected) handleReset();
+      }}
       alignItems="center"

29-33: 공개 API에서 사용되지 않는 onFileUpload 제거

전체 코드베이스를 검색한 결과, onFileUpload는 정의되기만 할 뿐 어떤 파일에서도 사용되지 않습니다. 컴포넌트 구현에서 이 prop을 destructure하지 않으며, 대신 onFileChange 콜백을 사용하고 있습니다. 사용되지 않는 공개 API를 유지하면 혼란을 초래하므로 Props 타입에서 제거해야 합니다.

제거 대상: src/shared/ui/MediaUpload/MediaUpload.tsx, 29-33줄 (onFileUpload 속성 정의)

♻️ Duplicate comments (1)
src/shared/ui/MediaUpload/MediaUpload.tsx (1)

114-114: 세로 스크롤만 필요 시 overflow-y-auto 권장

이전 리뷰와 동일 제안입니다. 가로 스크롤을 방지하려면 overflow-y-auto가 더 정확합니다.

-    <div className="max-h-[500px] w-full overflow-auto">
+    <div className="max-h-[500px] w-full overflow-y-auto">
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2cbe6f and fdcbad2.

📒 Files selected for processing (3)
  • src/shared/ui/MediaUpload/MediaUpload.stories.tsx (4 hunks)
  • src/shared/ui/MediaUpload/MediaUpload.tsx (5 hunks)
  • src/shared/ui/MediaUpload/MediaUploadPreview.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/shared/ui/MediaUpload/MediaUploadPreview.tsx (1)
src/shared/ui/MediaUpload/MediaUpload.tsx (1)
  • Props (9-60)
src/shared/ui/MediaUpload/MediaUpload.stories.tsx (1)
src/shared/ui/MediaUpload/MediaUpload.tsx (2)
  • Props (9-60)
  • MediaUpload (64-141)
🪛 GitHub Actions: Check Pull Request
src/shared/ui/MediaUpload/MediaUpload.stories.tsx

[warning] 16-16: Step 'npm run lint' reported 1 ESLint warning: 'Must use destructuring assignment' in MediaUpload.stories.tsx:16:60. ESLint found too many warnings (maximum: 0). Process completed with exit code 1.

🔇 Additional comments (1)
src/shared/ui/MediaUpload/MediaUpload.tsx (1)

122-128: 프리뷰 데이터 흐름 전환(LGTM)

previewFiles/previewUrls + onFileChange로의 제어형 전환 좋습니다. 상위에서 상태 일원화되어 스토리/소비자 제어가 쉬워졌습니다.

Comment on lines +15 to +32
function MediaUploadStoryWrapper(args: Props & { serverResponseUrl?: string[] }) {
const [previewUrls, setPreviewUrls] = useState<string[]>(args.serverResponseUrl ?? []);
const [previewFiles, setPreviewFiles] = useState<File[]>([]);

const handleFileChange = (files: File[] | null, urls: string[]) => {
setPreviewFiles(files || []);
setPreviewUrls(urls);
};

return (
<MediaUpload
{...args}
previewUrls={previewUrls}
previewFiles={previewFiles}
onFileChange={handleFileChange}
/>
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

ESLint 경고로 파이프라인 실패 — 구조 분해 할당으로 수정하고 불필요한 prop 전파 차단

  • args.serverResponseUrl 직접 접근으로 prefer-destructuring 경고 발생(최대 경고 0 설정으로 CI 실패).
  • 또한 {...args}로 MediaUpload에 전달 시 정의되지 않은 serverResponseUrl prop이 하위로 전파될 수 있습니다.

아래처럼 구조 분해 + 나머지 전달로 해결하세요.

-function MediaUploadStoryWrapper(args: Props & { serverResponseUrl?: string[] }) {
-  const [previewUrls, setPreviewUrls] = useState<string[]>(args.serverResponseUrl ?? []);
+function MediaUploadStoryWrapper(args: Props & { serverResponseUrl?: string[] }) {
+  const { serverResponseUrl = [], ...rest } = args;
+  const [previewUrls, setPreviewUrls] = useState<string[]>(serverResponseUrl);
   const [previewFiles, setPreviewFiles] = useState<File[]>([]);
@@
-  return (
-    <MediaUpload
-      {...args}
+  return (
+    <MediaUpload
+      {...rest}
       previewUrls={previewUrls}
       previewFiles={previewFiles}
       onFileChange={handleFileChange}
     />
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function MediaUploadStoryWrapper(args: Props & { serverResponseUrl?: string[] }) {
const [previewUrls, setPreviewUrls] = useState<string[]>(args.serverResponseUrl ?? []);
const [previewFiles, setPreviewFiles] = useState<File[]>([]);
const handleFileChange = (files: File[] | null, urls: string[]) => {
setPreviewFiles(files || []);
setPreviewUrls(urls);
};
return (
<MediaUpload
{...args}
previewUrls={previewUrls}
previewFiles={previewFiles}
onFileChange={handleFileChange}
/>
);
}
function MediaUploadStoryWrapper(args: Props & { serverResponseUrl?: string[] }) {
const { serverResponseUrl = [], ...rest } = args;
const [previewUrls, setPreviewUrls] = useState<string[]>(serverResponseUrl);
const [previewFiles, setPreviewFiles] = useState<File[]>([]);
const handleFileChange = (files: File[] | null, urls: string[]) => {
setPreviewFiles(files || []);
setPreviewUrls(urls);
};
return (
<MediaUpload
{...rest}
previewUrls={previewUrls}
previewFiles={previewFiles}
onFileChange={handleFileChange}
/>
);
}
🧰 Tools
🪛 GitHub Actions: Check Pull Request

[warning] 16-16: Step 'npm run lint' reported 1 ESLint warning: 'Must use destructuring assignment' in MediaUpload.stories.tsx:16:60. ESLint found too many warnings (maximum: 0). Process completed with exit code 1.

🤖 Prompt for AI Agents
In src/shared/ui/MediaUpload/MediaUpload.stories.tsx around lines 15 to 32, the
code reads args.serverResponseUrl directly and spreads {...args} into
MediaUpload which triggers an ESLint prefer-destructuring warning and may pass
an undefined serverResponseUrl prop down; fix by destructuring serverResponseUrl
from args (e.g. const { serverResponseUrl, ...rest } = args), initialize
previewUrls from serverResponseUrl, and spread only the remaining props into
<MediaUpload {...rest} ...> so the undefined prop is not forwarded.

Comment on lines +42 to +49
useEffect(() => {
if (file) return setIsVideo(file.type.startsWith('video/'));
getMimeType(previewUrl).then((type) => {
if (type) setIsVideo(type.startsWith('video/'));
else setIsVideo(false);
});
}, [file, previewUrl]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

HEAD 요청에 중단/정합성 처리 없음 — 언마운트/빠른 전환 시 상태 경쟁 조건과 불필요 네트워크 발생

  • fetch(HEAD) 결과가 늦게 도착하면 언마운트 후 setState가 실행될 수 있습니다.
  • res.ok 체크가 없어 4xx/5xx도 신뢰할 수 없는 Content-Type을 반환할 수 있습니다.

AbortController로 취소를 지원하고 res.ok를 확인하세요.

-  useEffect(() => {
-    if (file) return setIsVideo(file.type.startsWith('video/'));
-    getMimeType(previewUrl).then((type) => {
-      if (type) setIsVideo(type.startsWith('video/'));
-      else setIsVideo(false);
-    });
-  }, [file, previewUrl]);
+  useEffect(() => {
+    if (file) {
+      setIsVideo(file.type.startsWith('video/'));
+      return;
+    }
+    const ac = new AbortController();
+    let cancelled = false;
+    getMimeType(previewUrl, ac.signal).then((type) => {
+      if (!cancelled) setIsVideo(Boolean(type?.startsWith('video/')));
+    });
+    return () => {
+      cancelled = true;
+      ac.abort();
+    };
+  }, [file, previewUrl]);
@@
-async function getMimeType(url: string): Promise<string | null> {
+async function getMimeType(url: string, signal?: AbortSignal): Promise<string | null> {
   try {
-    const res = await fetch(url, { method: 'HEAD' });
-    return res.headers.get('Content-Type');
+    const res = await fetch(url, { method: 'HEAD', signal });
+    if (!res.ok) return null;
+    return res.headers.get('Content-Type');
   } catch {
     return null;
   }
 }

추가로, 외부 CDN CORS 정책으로 HEAD가 막힐 수 있어 캐시(Map<url, mime>)를 두어 재시도/중복요청을 줄이는 것도 권장합니다.

Also applies to: 73-80

Copy link
Copy Markdown
Contributor

@keemsebin keemsebin left a comment

Choose a reason for hiding this comment

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

변경 사항 확인했습니다!

@yougyung yougyung merged commit d4a8e2d into main Oct 19, 2025
9 checks passed
@yougyung yougyung deleted the refactor/#81-image branch October 19, 2025 02:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] MediaUpload, ImageGallery 컴포넌트 사용성개선

2 participants