Skip to content

Commit e655d50

Browse files
authored
Merge pull request #150 from YAPP-Github/refactor/PRODUCT-271
refactor: 스토리 페이지 이미지 등록 방식 변경 (#149)
2 parents eb61b74 + 6c1477d commit e655d50

File tree

11 files changed

+64
-45
lines changed

11 files changed

+64
-45
lines changed

src/app/(home)/_components/Story/StoryList/StoryList.css.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ export const storyImageInner = style({
1919
export const storyImage = style({
2020
display: "block",
2121
borderRadius: radius.circle,
22+
objectFit: "cover",
2223
cursor: "pointer",
2324
});

src/app/(home)/_components/Story/StoryList/StoryList.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@ export const StoryList = () => {
2727
>
2828
<div className={styles.storyImageInner}>
2929
<Image
30-
src={story.imageUrl}
30+
src={story.images?.[0]?.url ?? ""}
3131
width={80}
3232
height={80}
3333
alt={`스토리 이미지 ${story.storyId}`}
3434
className={styles.storyImage}
35-
objectFit='cover'
3635
// TODO: 추후 제거
3736
unoptimized
3837
/>

src/app/(store)/stores/[storeId]/_components/StoreStories/StoreStories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const StoreStoriesContent = ({ kakaoId }: { kakaoId: string }) => {
6868
<Link href={`/story/${data.storyId}`} key={data.storyId}>
6969
<div className={styles.storyWrapper}>
7070
<Image
71-
src={data.imageUrl}
71+
src={data.images?.[0]?.url ?? ""}
7272
alt='스토리 이미지'
7373
className={styles.image}
7474
width={124}

src/app/story/[id]/_api/detail.types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
import type { ImageResponse } from "@/types";
2+
13
export type StoryDetailResponse = {
24
storeKakaoId: string;
35
category: string;
46
storeName: string;
57
storeDistrict: string;
68
storeNeighborhood: string;
79
description: string | null;
8-
imageUrl: string;
10+
images: ImageResponse[];
911
memberId: number;
1012
memberNickname: string;
1113
storeId: number | null;

src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const StoryDetailContent = ({ storyId }: StoryDetailContentProps) => {
9090
onClick={handleNextStory}
9191
/>
9292
<Image
93-
src={story.imageUrl}
93+
src={story.images?.[0]?.url ?? ""}
9494
alt={`${story.storeName} 스토리`}
9595
fill
9696
priority

src/app/story/_api/stories.types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { type ImageResponse } from "@/types";
2+
13
export type Story = {
24
storyId: number;
3-
imageUrl: string;
5+
images: ImageResponse[];
46
};
57

68
export type StoriesResponse = {
@@ -9,7 +11,7 @@ export type StoriesResponse = {
911

1012
export type StoryByKakaoId = {
1113
storyId: number;
12-
imageUrl: string;
14+
images: ImageResponse[];
1315
memberId: number;
1416
memberNickname: string;
1517
};

src/app/story/register/_api/register.api.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,15 @@ import {
88
/**
99
* 스토리 등록 API
1010
* @param {StoryRegisterRequest} storyRequest - 스토리 등록 요청 데이터
11-
* @param {File} imageFile - 업로드할 이미지 파일
1211
*
1312
* @returns {Promise<StoryRegisterResponse>} 등록된 스토리 ID 반환
1413
*/
1514
export const postStory = async (
16-
storyRequest: StoryRegisterRequest,
17-
imageFile: File
15+
storyRequest: StoryRegisterRequest
1816
): Promise<StoryRegisterResponse> => {
19-
const formData = new FormData();
20-
21-
formData.append(
22-
"request",
23-
new Blob([JSON.stringify(storyRequest)], { type: "application/json" })
24-
);
25-
26-
formData.append("image", imageFile);
27-
2817
return await authHttp
2918
.post("api/stories", {
30-
body: formData,
19+
json: storyRequest,
3120
})
3221
.json<StoryRegisterResponse>();
3322
};

src/app/story/register/_api/register.queries.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,12 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
22

33
import { storiesQueryKeys } from "../../_api";
44
import { postStory } from "./register.api";
5-
import type { StoryRegisterRequest } from "./register.types";
65

76
export const usePostStoryMutation = () => {
87
const queryClient = useQueryClient();
98

109
return useMutation({
11-
mutationFn: ({
12-
storyRequest,
13-
imageFile,
14-
}: {
15-
storyRequest: StoryRegisterRequest;
16-
imageFile: File;
17-
}) => {
18-
return postStory(storyRequest, imageFile);
19-
},
10+
mutationFn: postStory,
2011
onSuccess: () => {
2112
queryClient.invalidateQueries({
2213
queryKey: storiesQueryKeys.lists(),

src/app/story/register/_api/register.types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import { type ImageRequest } from "@/types";
2+
13
export type StoryRegisterRequest = {
24
storeKakaoId: string;
35
storeName: string;
46
description: string | null;
7+
images: ImageRequest[];
58
};
69

710
export type StoryRegisterResponse = {

src/app/story/register/page.tsx

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { zodResolver } from "@hookform/resolvers/zod";
44
import { useRouter } from "next/navigation";
55
import { FormProvider, useForm } from "react-hook-form";
66

7+
import { getPresignedUrl, uploadImageToS3 } from "@/app/_api/image/image.api";
78
import CancelIcon from "@/assets/cancel.svg";
89
import { GNB } from "@/components/ui/GNB";
910
import { Spacer } from "@/components/ui/Spacer";
1011
import { VStack } from "@/components/ui/Stack";
12+
import { type ImageRequest } from "@/types/image.types";
1113

1214
import { usePostStoryMutation } from "./_api";
1315
import { StoryDescription } from "./_components/StoryDescription";
@@ -39,25 +41,51 @@ export default function StoryRegisterPage() {
3941
});
4042

4143
const onSubmit = async (data: StoryRegisterFormData) => {
42-
postStory(
43-
{
44-
storyRequest: {
44+
try {
45+
let imageData: ImageRequest[] = [];
46+
47+
if (data.image) {
48+
const { urls: presignedUrls } = await getPresignedUrl([
49+
{
50+
order: 0,
51+
contentType: data.image.type,
52+
fileSize: data.image.size,
53+
},
54+
]);
55+
56+
const { url, key, order, contentType } = presignedUrls[0]!;
57+
await uploadImageToS3(url, data.image);
58+
59+
imageData = [
60+
{
61+
imageKey: key,
62+
orderIndex: order,
63+
contentType,
64+
fileSize: data.image.size,
65+
},
66+
];
67+
}
68+
69+
postStory(
70+
{
4571
storeKakaoId: data.storeKakaoId,
4672
storeName: data.storeName,
4773
description: data.description || null,
74+
images: imageData,
4875
},
49-
imageFile: data.image,
50-
},
51-
{
52-
onSuccess: response => {
53-
clearUpload();
54-
router.push(`/story/${response.storyId}`);
55-
},
56-
onError: error => {
57-
console.error("스토리 등록 실패:", error);
58-
},
59-
}
60-
);
76+
{
77+
onSuccess: response => {
78+
clearUpload();
79+
router.push(`/story/${response.storyId}`);
80+
},
81+
onError: error => {
82+
console.error("스토리 등록 실패:", error);
83+
},
84+
}
85+
);
86+
} catch (error) {
87+
console.error("이미지 업로드 실패:", error);
88+
}
6189
};
6290

6391
return (

0 commit comments

Comments
 (0)