From 94c0a87b1a24797bc9a63830cc546a40cdb5f077 Mon Sep 17 00:00:00 2001 From: fr0gydev Date: Wed, 13 Aug 2025 14:55:41 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=ED=94=BC=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/feeds/getFeedDetail.ts | 4 +- src/api/feeds/updateFeed.ts | 82 +++++++ .../creategroup/BookSelectionSection.tsx | 10 +- src/components/createpost/PhotoSection.tsx | 85 ++++++-- src/hooks/useUpdateFeed.ts | 78 +++++++ src/pages/feed/FeedDetailPage.tsx | 5 +- src/pages/index.tsx | 2 + src/pages/post/UpdatePost.tsx | 204 ++++++++++++++++++ 8 files changed, 445 insertions(+), 25 deletions(-) create mode 100644 src/api/feeds/updateFeed.ts create mode 100644 src/hooks/useUpdateFeed.ts create mode 100644 src/pages/post/UpdatePost.tsx diff --git a/src/api/feeds/getFeedDetail.ts b/src/api/feeds/getFeedDetail.ts index 65a33fb1..75e5d338 100644 --- a/src/api/feeds/getFeedDetail.ts +++ b/src/api/feeds/getFeedDetail.ts @@ -6,7 +6,7 @@ export interface FeedDetailData { creatorId: number; creatorNickname: string; creatorProfileImageUrl: string; - aliasName: string; + alias: string; aliasColor: string; postDate: string; isbn: string; @@ -18,6 +18,7 @@ export interface FeedDetailData { commentCount: number; isSaved: boolean; isLiked: boolean; + isPublic: boolean; tagList: string[]; } @@ -40,4 +41,5 @@ export const getFeedDetail = async (feedId: number) => { const feedDetail = await getFeedDetail(123); console.log(feedDetail.data.feedId); // 123 console.log(feedDetail.data.tagList); // ["태그1", "태그2"] +console.log(feedDetail.data.isPublic); // true or false */ diff --git a/src/api/feeds/updateFeed.ts b/src/api/feeds/updateFeed.ts new file mode 100644 index 00000000..b5a14750 --- /dev/null +++ b/src/api/feeds/updateFeed.ts @@ -0,0 +1,82 @@ +import { apiClient } from '../index'; + +/** 피드 수정 요청 바디 타입 */ +export interface UpdateFeedBody { + contentBody: string; + isPublic: boolean; + tagList?: string[]; // 선택 필드 + remainImageUrls?: string[]; // 기존 이미지 중 유지할 URL들 +} + +/** 성공 응답 */ +export interface UpdateFeedSuccess { + isSuccess: true; + code: number; + message: string; +} + +/** 실패 응답 */ +export interface UpdateFeedFail { + isSuccess: false; + code: number; + message: string; +} + +export type UpdateFeedResponse = UpdateFeedSuccess | UpdateFeedFail; + +/** + * 피드 수정 API + * - multipart/form-data + * - request: application/json (Blob로 감싸 전송) + * - 이미지 추가는 불가능, 기존 이미지 삭제만 가능 + */ +export const updateFeed = async ( + feedId: number, + body: UpdateFeedBody, +): Promise => { + const form = new FormData(); + + // request 파트(JSON) - 필수 + form.append('request', new Blob([JSON.stringify(body)], { type: 'application/json' })); + + // 수정 모드에서는 새 이미지 추가 불가 + + const { data } = await apiClient.patch(`/feeds/${feedId}`, form, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + + return data; +}; + +/* +사용 예시: + +// 기존 이미지 일부 유지 (새 이미지 추가는 불가) +const updateBody: UpdateFeedBody = { + contentBody: "수정된 글 내용입니다!", + isPublic: true, + tagList: ["한국소설", "책추천", "역사"], + remainImageUrls: ["https://img.domain.com/1.jpg"] // 기존 이미지 중 유지할 것들 +}; + +try { + const result = await updateFeed(123, updateBody); + if (result.isSuccess) { + console.log('피드 수정 성공:', result.message); + } else { + console.error('피드 수정 실패:', result.message); + } +} catch (error) { + console.error('네트워크 오류:', error); +} + +// 모든 이미지 삭제 후 텍스트만 수정 +const textOnlyUpdate: UpdateFeedBody = { + contentBody: "텍스트만 수정", + isPublic: false, + tagList: [], + remainImageUrls: [] // 모든 기존 이미지 삭제 +}; + +const result = await updateFeed(123, textOnlyUpdate); +*/ diff --git a/src/components/creategroup/BookSelectionSection.tsx b/src/components/creategroup/BookSelectionSection.tsx index 73a2e7c5..e65b7e99 100644 --- a/src/components/creategroup/BookSelectionSection.tsx +++ b/src/components/creategroup/BookSelectionSection.tsx @@ -16,19 +16,21 @@ interface BookSelectionSectionProps { selectedBook: { cover: string; title: string; author: string } | null; onSearchClick: () => void; onChangeClick: () => void; + readOnly?: boolean; } const BookSelectionSection = ({ selectedBook, onSearchClick, onChangeClick, + readOnly = false, }: BookSelectionSectionProps) => { return (
책 선택 {selectedBook ? ( <> @@ -41,14 +43,16 @@ const BookSelectionSection = ({ {selectedBook.author} 저 - 변경 + {!readOnly && 변경} ) : ( <> 검색 - 검색해서 찾기 + + {readOnly ? '책 정보' : '검색해서 찾기'} + )} diff --git a/src/components/createpost/PhotoSection.tsx b/src/components/createpost/PhotoSection.tsx index 5a3cd9dc..f61899b6 100644 --- a/src/components/createpost/PhotoSection.tsx +++ b/src/components/createpost/PhotoSection.tsx @@ -13,19 +13,34 @@ import plusDisabledIcon from '../../assets/post/plus-disabled.svg'; import closeIcon from '../../assets/post/close.svg'; interface PhotoSectionProps { - photos: File[]; + photos: File[]; // 새로 추가할 이미지들 onPhotoAdd: (files: File[]) => void; onPhotoRemove: (index: number) => void; + existingImageUrls?: string[]; // 기존 이미지 URL들 (수정 시에만 사용) + onExistingImageRemove?: (imageUrl: string) => void; // 기존 이미지 제거 함수 (수정 시에만 사용) + readOnly?: boolean; // 읽기 전용 모드 + isEditMode?: boolean; // 수정 모드 (사진 추가 버튼 숨김) } -const PhotoSection = ({ photos, onPhotoAdd, onPhotoRemove }: PhotoSectionProps) => { +const PhotoSection = ({ + photos, + onPhotoAdd, + onPhotoRemove, + existingImageUrls = [], + onExistingImageRemove, + readOnly = false, + isEditMode = false, +}: PhotoSectionProps) => { const fileInputRef = useRef(null); const handleFileInputClick = () => { + if (readOnly || isEditMode) return; fileInputRef.current?.click(); }; const handleFileChange = (e: React.ChangeEvent) => { + if (readOnly || isEditMode) return; + const files = Array.from(e.target.files || []); if (files.length > 0) { onPhotoAdd(files); @@ -38,34 +53,64 @@ const PhotoSection = ({ photos, onPhotoAdd, onPhotoRemove }: PhotoSectionProps) return URL.createObjectURL(file); }; - const isDisabled = photos.length >= 3; + const totalImageCount = existingImageUrls.length + photos.length; + const isDisabled = totalImageCount >= 3 || readOnly || isEditMode; return (
사진 추가 - - 사진 추가 - + {/* 새 이미지 추가 버튼 - 수정 모드에서는 숨김 */} + {!readOnly && !isEditMode && ( + + 사진 추가 + + )} + + {/* 기존 이미지들 (수정 모드에서만 표시) */} + {existingImageUrls.map((imageUrl, index) => ( +
+ + {!readOnly && onExistingImageRemove && ( + onExistingImageRemove(imageUrl)}> + 삭제 + + )} +
+ ))} + + {/* 새로 추가된 이미지들 */} {photos.map((photo, index) => ( -
- - onPhotoRemove(index)}> - 삭제 - +
+ + {!readOnly && ( + onPhotoRemove(index)}> + 삭제 + + )}
))} - {photos.length}/3개 - + + {totalImageCount}/3개 + + {!readOnly && !isEditMode && ( + + )}
); diff --git a/src/hooks/useUpdateFeed.ts b/src/hooks/useUpdateFeed.ts new file mode 100644 index 00000000..7f558b8f --- /dev/null +++ b/src/hooks/useUpdateFeed.ts @@ -0,0 +1,78 @@ +import { useState } from 'react'; +import { updateFeed, type UpdateFeedBody, type UpdateFeedResponse } from '@/api/feeds/updateFeed'; +import { usePopupActions } from './usePopupActions'; + +interface UseUpdateFeedProps { + onSuccess?: (feedId: number) => void; +} + +export const useUpdateFeed = (options?: UseUpdateFeedProps) => { + const [loading, setLoading] = useState(false); + const { openSnackbar, closePopup } = usePopupActions(); + + const updateExistingFeed = async (feedId: number, body: UpdateFeedBody) => { + try { + setLoading(true); + + // ===== 클라이언트 선검증 ===== + if (body.tagList) { + // 최대 5개 + if (body.tagList.length > 5) { + openSnackbar({ + message: '태그는 최대 5개까지 입력할 수 있어요.', + variant: 'top', + onClose: closePopup, + }); + return { success: false as const }; + } + // 중복 제거 체크 + const trimmed = body.tagList.map(t => t.trim()).filter(Boolean); + const uniq = new Set(trimmed); + if (uniq.size !== trimmed.length) { + openSnackbar({ + message: '태그는 중복될 수 없어요.', + variant: 'top', + onClose: closePopup, + }); + return { success: false as const }; + } + } + // ===== 선검증 끝 ===== + + const res: UpdateFeedResponse = await updateFeed(feedId, body); + + if (res.isSuccess) { + openSnackbar({ + message: '피드가 수정되었습니다.', + variant: 'top', + onClose: closePopup, + }); + + if (options?.onSuccess) { + options.onSuccess(feedId); + } + + return { success: true as const, feedId }; + } else { + openSnackbar({ + message: res.message || '피드 수정에 실패했습니다.', + variant: 'top', + onClose: closePopup, + }); + return { success: false as const, error: res.message }; + } + } catch (error) { + console.error('피드 수정 실패:', error); + openSnackbar({ + message: '피드 수정 중 오류가 발생했습니다.', + variant: 'top', + onClose: closePopup, + }); + return { success: false as const, error: '피드 수정 중 오류가 발생했습니다.' }; + } finally { + setLoading(false); + } + }; + + return { updateExistingFeed, loading }; +}; diff --git a/src/pages/feed/FeedDetailPage.tsx b/src/pages/feed/FeedDetailPage.tsx index a87c727f..048346ba 100644 --- a/src/pages/feed/FeedDetailPage.tsx +++ b/src/pages/feed/FeedDetailPage.tsx @@ -87,7 +87,10 @@ const FeedDetailPage = () => { const handleMoreClick = () => { openMoreMenu({ - onEdit: () => console.log('수정하기 클릭'), + onEdit: () => { + closePopup(); + navigate(`/post/update/${feedId}`); + }, onClose: () => { closePopup(); }, diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 343e91c2..b7cba507 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -11,6 +11,7 @@ import SignupNickname from './signup/SignupNickname'; import SignupDone from './signup/SignupDone'; import CreateGroup from './group/CreateGroup'; import CreatePost from './post/CreatePost'; +import UpdatePost from './post/UpdatePost'; import Group from './group/Group'; import Feed from './feed/Feed'; import GroupSearch from './groupSearch/GroupSearch'; @@ -51,6 +52,7 @@ const Router = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/pages/post/UpdatePost.tsx b/src/pages/post/UpdatePost.tsx new file mode 100644 index 00000000..1b266a1e --- /dev/null +++ b/src/pages/post/UpdatePost.tsx @@ -0,0 +1,204 @@ +import { useState, useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import TitleHeader from '../../components/common/TitleHeader'; +import BookSelectionSection from '../../components/creategroup/BookSelectionSection'; +import PostContentSection from '../../components/createpost/PostContentSection'; +import PhotoSection from '../../components/createpost/PhotoSection'; +import PrivacyToggleSection from '../../components/createpost/PrivacyToggleSection'; +import TagSelectionSection from '../../components/createpost/TagSelectionSection'; +import leftarrow from '../../assets/common/leftArrow.svg'; +import { Container } from './CreatePost.styled'; +import { Section } from '../group/CommonSection.styled'; +import { useUpdateFeed } from '@/hooks/useUpdateFeed'; +import { usePopupActions } from '@/hooks/usePopupActions'; +import { getFeedDetail } from '@/api/feeds/getFeedDetail'; +import type { UpdateFeedBody } from '@/api/feeds/updateFeed'; + +interface Book { + id: number; + title: string; + author: string; + cover: string; + isbn: string; +} + +const UpdatePost = () => { + const navigate = useNavigate(); + const { feedId } = useParams<{ feedId: string }>(); + const [selectedBook, setSelectedBook] = useState(null); + const [postContent, setPostContent] = useState(''); + const [selectedPhotos] = useState([]); // 수정 모드에서는 사용하지 않음 + const [remainImageUrls, setRemainImageUrls] = useState([]); // 유지할 기존 이미지들 + const [isPrivate, setIsPrivate] = useState(false); + const [selectedTags, setSelectedTags] = useState([]); + const [loading, setLoading] = useState(true); + + const { openSnackbar, closePopup } = usePopupActions(); + const { updateExistingFeed, loading: updateLoading } = useUpdateFeed({ + onSuccess: feedId => { + console.log('피드 수정 성공! 피드 ID:', feedId); + navigate(`/feed/${feedId}`); + }, + }); + + // 피드 상세 정보 로드 + useEffect(() => { + const loadFeedDetail = async () => { + if (!feedId) { + openSnackbar({ + message: '잘못된 피드 ID입니다.', + variant: 'top', + onClose: closePopup, + }); + navigate(-1); + return; + } + + try { + setLoading(true); + const response = await getFeedDetail(Number(feedId)); + const data = response.data; + + // 기존 데이터로 폼 초기화 + setSelectedBook({ + id: 0, // API에서 bookId가 없다면 임시값 + title: data.bookTitle, + author: data.bookAuthor, + cover: '', // API에서 bookCover가 없다면 빈 문자열 + isbn: data.isbn, + }); + + setPostContent(data.contentBody); + setIsPrivate(!data.isPublic); // isPublic 필드 사용 + setSelectedTags(data.tagList || []); + setRemainImageUrls(data.contentUrls || []); // 처음에는 모든 기존 이미지 유지 + } catch (error) { + console.error('피드 상세 정보 로드 실패:', error); + openSnackbar({ + message: '피드 정보를 불러오는데 실패했습니다.', + variant: 'top', + onClose: closePopup, + }); + navigate(-1); + } finally { + setLoading(false); + } + }; + + loadFeedDetail(); + }, [feedId, navigate, openSnackbar, closePopup]); + + const handleBackClick = () => { + navigate(-1); + }; + + const handleCompleteClick = async () => { + if (!isFormValid) { + openSnackbar({ + message: '글 내용을 입력해주세요.', + variant: 'top', + onClose: closePopup, + }); + return; + } + + if (!feedId) return; + + const body: UpdateFeedBody = { + contentBody: postContent.trim(), + isPublic: !isPrivate, + ...(selectedTags.length ? { tagList: selectedTags } : {}), + ...(remainImageUrls.length ? { remainImageUrls } : {}), + }; + + // 수정 모드에서는 새 이미지 추가 불가 + const result = await updateExistingFeed(Number(feedId), body); + + if (!result?.success) { + // 에러는 useUpdateFeed에서 이미 처리됨 + return; + } + }; + + // 새로 추가할 이미지 핸들러 (수정 모드에서는 사용하지 않음) + const handlePhotoAdd = () => { + // 수정 모드에서는 새 이미지 추가 불가 + return; + }; + + // 새로 추가한 이미지 제거 (수정 모드에서는 사용하지 않음) + const handlePhotoRemove = () => { + // 수정 모드에서는 새 이미지 추가 불가 + return; + }; + + // 기존 이미지 제거 (remainImageUrls에서 제외) + const handleExistingImageRemove = (imageUrl: string) => { + setRemainImageUrls(prev => prev.filter(url => url !== imageUrl)); + }; + + const handlePrivacyToggle = () => setIsPrivate(v => !v); + + const handleTagToggle = (tag: string) => { + setSelectedTags(prev => (prev.includes(tag) ? prev.filter(t => t !== tag) : [...prev, tag])); + }; + + const isFormValid = postContent.trim() !== ''; + + // 로딩 중 + if (loading) { + return ( +
+ 피드 정보를 불러오는 중... +
+ ); + } + + return ( + <> + } + title="글 수정" + rightButton={updateLoading ? '수정 중...' : '완료'} + onLeftClick={handleBackClick} + onRightClick={handleCompleteClick} + isNextActive={isFormValid && !updateLoading} + /> + + {/* 책 정보는 수정할 수 없음 (읽기 전용으로 표시) */} + {}} // 비활성화 + onChangeClick={() => {}} // 비활성화 + readOnly={true} // 읽기 전용 모드 + /> + +
+ + + +
+ + {/* 기존 이미지 삭제만 가능, 추가는 불가 */} + + +
+ + + +
+ + + + + ); +}; + +export default UpdatePost; From f22057d9eb3b4b2751fb66b2c0c65b9e4f0073fb Mon Sep 17 00:00:00 2001 From: fr0gydev Date: Wed, 13 Aug 2025 14:58:00 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=EB=AC=B4=ED=95=9C=20=EB=A0=8C?= =?UTF-8?q?=EB=8D=94=EB=A7=81=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/post/UpdatePost.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/post/UpdatePost.tsx b/src/pages/post/UpdatePost.tsx index 1b266a1e..1d5f4c95 100644 --- a/src/pages/post/UpdatePost.tsx +++ b/src/pages/post/UpdatePost.tsx @@ -61,17 +61,17 @@ const UpdatePost = () => { // 기존 데이터로 폼 초기화 setSelectedBook({ - id: 0, // API에서 bookId가 없다면 임시값 + id: 0, title: data.bookTitle, author: data.bookAuthor, - cover: '', // API에서 bookCover가 없다면 빈 문자열 + cover: '', isbn: data.isbn, }); setPostContent(data.contentBody); - setIsPrivate(!data.isPublic); // isPublic 필드 사용 + setIsPrivate(!data.isPublic); setSelectedTags(data.tagList || []); - setRemainImageUrls(data.contentUrls || []); // 처음에는 모든 기존 이미지 유지 + setRemainImageUrls(data.contentUrls || []); } catch (error) { console.error('피드 상세 정보 로드 실패:', error); openSnackbar({ @@ -86,7 +86,7 @@ const UpdatePost = () => { }; loadFeedDetail(); - }, [feedId, navigate, openSnackbar, closePopup]); + }, [feedId]); // 의존성 배열에서 함수들 제거 const handleBackClick = () => { navigate(-1); From 0bf6ec7456887fe4b655905de59e557c8e7d84c6 Mon Sep 17 00:00:00 2001 From: fr0gydev Date: Wed, 13 Aug 2025 14:58:53 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=EB=B9=88=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20src=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../creategroup/BookSelectionSection.tsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/components/creategroup/BookSelectionSection.tsx b/src/components/creategroup/BookSelectionSection.tsx index e65b7e99..2c345ef8 100644 --- a/src/components/creategroup/BookSelectionSection.tsx +++ b/src/components/creategroup/BookSelectionSection.tsx @@ -36,7 +36,25 @@ const BookSelectionSection = ({ <> - {selectedBook.title} + {selectedBook.cover && selectedBook.cover.trim() !== '' ? ( + {selectedBook.title} + ) : ( +
+ 책표지 +
+ )}
{selectedBook.title} From fcf18eadb49464adc4aa799237d440fe6bdccba1 Mon Sep 17 00:00:00 2001 From: fr0gydev Date: Wed, 13 Aug 2025 15:04:00 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=ED=94=BC=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20API=20=EC=9A=94=EC=B2=AD=20=ED=98=95=EC=8B=9D?= =?UTF-8?q?=EC=9D=84=20JSON=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/feeds/updateFeed.ts | 31 +++++++++++++++++++++++-------- src/pages/post/UpdatePost.tsx | 8 ++++++-- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/api/feeds/updateFeed.ts b/src/api/feeds/updateFeed.ts index b5a14750..a4d6265d 100644 --- a/src/api/feeds/updateFeed.ts +++ b/src/api/feeds/updateFeed.ts @@ -34,18 +34,33 @@ export const updateFeed = async ( feedId: number, body: UpdateFeedBody, ): Promise => { - const form = new FormData(); + // FormData 대신 JSON으로 시도 + console.log('수정 API 요청 (JSON):', { + url: `/feeds/${feedId}`, + body: body, + }); - // request 파트(JSON) - 필수 - form.append('request', new Blob([JSON.stringify(body)], { type: 'application/json' })); + try { + const { data } = await apiClient.patch(`/feeds/${feedId}`, body, { + headers: { 'Content-Type': 'application/json' }, + }); - // 수정 모드에서는 새 이미지 추가 불가 + console.log('수정 API 응답:', data); + return data; + } catch (error) { + console.error('수정 API 에러:', error); - const { data } = await apiClient.patch(`/feeds/${feedId}`, form, { - headers: { 'Content-Type': 'multipart/form-data' }, - }); + // FormData로 재시도 + console.log('FormData로 재시도...'); + const form = new FormData(); + form.append('request', new Blob([JSON.stringify(body)], { type: 'application/json' })); - return data; + const { data } = await apiClient.patch(`/feeds/${feedId}`, form, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + + return data; + } }; /* diff --git a/src/pages/post/UpdatePost.tsx b/src/pages/post/UpdatePost.tsx index 1d5f4c95..1e1a2792 100644 --- a/src/pages/post/UpdatePost.tsx +++ b/src/pages/post/UpdatePost.tsx @@ -111,11 +111,15 @@ const UpdatePost = () => { ...(remainImageUrls.length ? { remainImageUrls } : {}), }; - // 수정 모드에서는 새 이미지 추가 불가 + // API 요청 전 데이터 확인 + console.log('수정 요청 데이터:', { + feedId: Number(feedId), + body: body, + }); + const result = await updateExistingFeed(Number(feedId), body); if (!result?.success) { - // 에러는 useUpdateFeed에서 이미 처리됨 return; } }; From f1c5f03cd49b0da4df6ee6d438ccf14094dd1fbc Mon Sep 17 00:00:00 2001 From: fr0gydev Date: Wed, 13 Aug 2025 15:07:10 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20console.log=20=EB=B0=8F=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/feeds/updateFeed.ts | 13 ++--------- src/components/createpost/PhotoSection.tsx | 13 ++++------- src/pages/post/UpdatePost.tsx | 27 ++++++---------------- 3 files changed, 14 insertions(+), 39 deletions(-) diff --git a/src/api/feeds/updateFeed.ts b/src/api/feeds/updateFeed.ts index a4d6265d..a6426b28 100644 --- a/src/api/feeds/updateFeed.ts +++ b/src/api/feeds/updateFeed.ts @@ -4,8 +4,8 @@ import { apiClient } from '../index'; export interface UpdateFeedBody { contentBody: string; isPublic: boolean; - tagList?: string[]; // 선택 필드 - remainImageUrls?: string[]; // 기존 이미지 중 유지할 URL들 + tagList?: string[]; + remainImageUrls?: string[]; } /** 성공 응답 */ @@ -34,24 +34,15 @@ export const updateFeed = async ( feedId: number, body: UpdateFeedBody, ): Promise => { - // FormData 대신 JSON으로 시도 - console.log('수정 API 요청 (JSON):', { - url: `/feeds/${feedId}`, - body: body, - }); - try { const { data } = await apiClient.patch(`/feeds/${feedId}`, body, { headers: { 'Content-Type': 'application/json' }, }); - console.log('수정 API 응답:', data); return data; } catch (error) { console.error('수정 API 에러:', error); - // FormData로 재시도 - console.log('FormData로 재시도...'); const form = new FormData(); form.append('request', new Blob([JSON.stringify(body)], { type: 'application/json' })); diff --git a/src/components/createpost/PhotoSection.tsx b/src/components/createpost/PhotoSection.tsx index f61899b6..d95911e1 100644 --- a/src/components/createpost/PhotoSection.tsx +++ b/src/components/createpost/PhotoSection.tsx @@ -13,13 +13,13 @@ import plusDisabledIcon from '../../assets/post/plus-disabled.svg'; import closeIcon from '../../assets/post/close.svg'; interface PhotoSectionProps { - photos: File[]; // 새로 추가할 이미지들 + photos: File[]; onPhotoAdd: (files: File[]) => void; onPhotoRemove: (index: number) => void; - existingImageUrls?: string[]; // 기존 이미지 URL들 (수정 시에만 사용) - onExistingImageRemove?: (imageUrl: string) => void; // 기존 이미지 제거 함수 (수정 시에만 사용) - readOnly?: boolean; // 읽기 전용 모드 - isEditMode?: boolean; // 수정 모드 (사진 추가 버튼 숨김) + existingImageUrls?: string[]; + onExistingImageRemove?: (imageUrl: string) => void; + readOnly?: boolean; + isEditMode?: boolean; } const PhotoSection = ({ @@ -61,14 +61,12 @@ const PhotoSection = ({ 사진 추가 - {/* 새 이미지 추가 버튼 - 수정 모드에서는 숨김 */} {!readOnly && !isEditMode && ( 사진 추가 )} - {/* 기존 이미지들 (수정 모드에서만 표시) */} {existingImageUrls.map((imageUrl, index) => (
))} - {/* 새로 추가된 이미지들 */} {photos.map((photo, index) => (
{ const { feedId } = useParams<{ feedId: string }>(); const [selectedBook, setSelectedBook] = useState(null); const [postContent, setPostContent] = useState(''); - const [selectedPhotos] = useState([]); // 수정 모드에서는 사용하지 않음 - const [remainImageUrls, setRemainImageUrls] = useState([]); // 유지할 기존 이미지들 + const [selectedPhotos] = useState([]); + const [remainImageUrls, setRemainImageUrls] = useState([]); const [isPrivate, setIsPrivate] = useState(false); const [selectedTags, setSelectedTags] = useState([]); const [loading, setLoading] = useState(true); @@ -86,7 +86,7 @@ const UpdatePost = () => { }; loadFeedDetail(); - }, [feedId]); // 의존성 배열에서 함수들 제거 + }, [feedId]); const handleBackClick = () => { navigate(-1); @@ -111,12 +111,6 @@ const UpdatePost = () => { ...(remainImageUrls.length ? { remainImageUrls } : {}), }; - // API 요청 전 데이터 확인 - console.log('수정 요청 데이터:', { - feedId: Number(feedId), - body: body, - }); - const result = await updateExistingFeed(Number(feedId), body); if (!result?.success) { @@ -124,19 +118,14 @@ const UpdatePost = () => { } }; - // 새로 추가할 이미지 핸들러 (수정 모드에서는 사용하지 않음) const handlePhotoAdd = () => { - // 수정 모드에서는 새 이미지 추가 불가 return; }; - // 새로 추가한 이미지 제거 (수정 모드에서는 사용하지 않음) const handlePhotoRemove = () => { - // 수정 모드에서는 새 이미지 추가 불가 return; }; - // 기존 이미지 제거 (remainImageUrls에서 제외) const handleExistingImageRemove = (imageUrl: string) => { setRemainImageUrls(prev => prev.filter(url => url !== imageUrl)); }; @@ -169,12 +158,11 @@ const UpdatePost = () => { isNextActive={isFormValid && !updateLoading} /> - {/* 책 정보는 수정할 수 없음 (읽기 전용으로 표시) */} {}} // 비활성화 - onChangeClick={() => {}} // 비활성화 - readOnly={true} // 읽기 전용 모드 + onSearchClick={() => {}} + onChangeClick={() => {}} + readOnly={true} />
@@ -183,14 +171,13 @@ const UpdatePost = () => {
- {/* 기존 이미지 삭제만 가능, 추가는 불가 */}