Skip to content

Commit f3995d2

Browse files
committed
[WRFE-70](feat): 이미지 업로드 API 연결
1 parent 6b0fee0 commit f3995d2

File tree

3 files changed

+85
-15
lines changed

3 files changed

+85
-15
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import apiClient from '@/shared/api/apiClient';
2+
3+
interface PresignedUrlResponse {
4+
presignedUrls: string[];
5+
}
6+
7+
interface UploadRequest {
8+
fileKeys: string[];
9+
}
10+
11+
export const useImageUpload = () => {
12+
const getImagePresignedUrl = async (fileName: string) => {
13+
const response = await apiClient.post<PresignedUrlResponse, UploadRequest>(
14+
'/files/upload',
15+
{body: {fileKeys: [fileName]}, withAuth: true},
16+
);
17+
return response.data.presignedUrls[0];
18+
};
19+
20+
const uploadImageToS3 = async (
21+
presignedUrl: string,
22+
file: File,
23+
): Promise<void> => {
24+
const contentType = file.type || 'image/jpeg';
25+
26+
await fetch(presignedUrl, {
27+
method: 'PUT',
28+
body: file,
29+
mode: 'cors',
30+
credentials: 'omit',
31+
headers: {
32+
'Content-Type': contentType,
33+
'Cache-Control': 'public, max-age=31536000',
34+
},
35+
});
36+
};
37+
38+
return {
39+
getImagePresignedUrl,
40+
uploadImageToS3,
41+
};
42+
};

apps/front/wraffle-webview/src/features/image-handle/ui/ImageCardWithDelete.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ export const ImageCardWithDelete = ({
2323
<button type='button' className='absolute right-2 top-2' onClick={onClick}>
2424
<Image src={'/icons/ic_close.svg'} alt='close' width={12} height={12} />
2525
</button>
26-
<Image
27-
alt='thumbnail'
28-
width={160}
29-
height={160}
30-
src={url}
31-
className='h-full w-full object-cover'
32-
/>
26+
<img alt='thumbnail' src={url} className='h-full w-full object-cover' />
3327
</div>
3428
);

apps/front/wraffle-webview/src/widgets/product-list/create/ui/ImageStepList.tsx

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,37 @@
1+
import {useState} from 'react';
12
import {AddItemCard, ImageCardWithDelete} from '@/features/image-handle';
3+
import {useImageUpload} from '@/features/image-handle/api/useImageUpload';
24
import {Button, Label, Typography} from '@wraffle/ui';
35

4-
// TODO
5-
// image api 연동
6-
// 이미지 추가 부분은 api연동하며 구현하겠습니다
76
export const ImageStep = ({onNext}: {onNext: (images: string[]) => void}) => {
8-
const images: string[] = [''];
7+
const [images, setImages] = useState<string[]>([]);
8+
const {getImagePresignedUrl, uploadImageToS3} = useImageUpload();
9+
10+
const handleImageUpload = async (e: Event) => {
11+
const input = e.target as HTMLInputElement;
12+
const files = input.files;
13+
14+
if (!files) return;
15+
16+
try {
17+
const file = files[0];
18+
const fileName = encodeURIComponent(file.name);
19+
20+
const presignedUrl = await getImagePresignedUrl(fileName);
21+
22+
await uploadImageToS3(presignedUrl, file);
23+
24+
const fileUrl = presignedUrl;
25+
setImages(prev => [...prev, fileUrl].slice(0, 4));
26+
} catch (error) {
27+
console.error('이미지 업로드 실패:', error);
28+
}
29+
};
30+
31+
const handleImageDelete = (index: number) => {
32+
setImages(prev => prev.filter((_, i) => i !== index));
33+
};
34+
935
return (
1036
<div className='flex h-full flex-col px-5 pb-20'>
1137
<div className='mb-5'>
@@ -27,18 +53,26 @@ export const ImageStep = ({onNext}: {onNext: (images: string[]) => void}) => {
2753
</div>
2854

2955
<div className='flex flex-wrap gap-4'>
30-
{images.map(url => (
56+
{images.map((url, index) => (
3157
<ImageCardWithDelete
3258
key={url}
3359
url={url}
34-
onClick={() => {}}
60+
onClick={() => handleImageDelete(index)}
3561
className='h-40 w-40'
3662
/>
3763
))}
3864
{images.length < 4 && (
3965
<AddItemCard
4066
label='이미지 추가'
41-
onClick={() => {}}
67+
onClick={() => {
68+
// 웹뷰 연결 후 input 파일 변경
69+
const input = document.createElement('input');
70+
input.type = 'file';
71+
input.accept = 'image/*';
72+
input.multiple = false;
73+
input.onchange = handleImageUpload;
74+
input.click();
75+
}}
4276
className='h-40 w-40'
4377
/>
4478
)}
@@ -48,7 +82,7 @@ export const ImageStep = ({onNext}: {onNext: (images: string[]) => void}) => {
4882
<Button
4983
type='button'
5084
className='mb-5 mt-3 disabled:text-[#A1A1AA]'
51-
// disabled={images.length === 0} // api연동 후 풀게요
85+
disabled={images.length === 0}
5286
onClick={() => onNext(images)}
5387
>
5488
다음

0 commit comments

Comments
 (0)