(null);
-
return (
-
+
{tags && tags.length > 0 ? (
tags.map((tag) => (
diff --git a/src/domains/community/write/image-upload/ImageInput.tsx b/src/domains/community/write/image-upload/ImageInput.tsx
index b67fe25a..a0206e74 100644
--- a/src/domains/community/write/image-upload/ImageInput.tsx
+++ b/src/domains/community/write/image-upload/ImageInput.tsx
@@ -1,16 +1,13 @@
-import { getApi } from '@/app/api/config/appConfig';
import { UploadedItem } from '@/domains/recipe/types/types';
import ImageBox from '@/shared/assets/icons/imageBox_fill_24.svg';
import { useToast } from '@/shared/hook/useToast';
-import { Dispatch, SetStateAction } from 'react';
type Props = {
uploadedFile: UploadedItem[];
- setUploadedFile: Dispatch
>;
onAddImage: (newFiles: UploadedItem[]) => void;
};
-function ImageInput({ uploadedFile, setUploadedFile, onAddImage }: Props) {
+function ImageInput({ uploadedFile, onAddImage }: Props) {
const { toastError } = useToast();
const handleInputChange = async (e: React.ChangeEvent) => {
@@ -20,23 +17,22 @@ function ImageInput({ uploadedFile, setUploadedFile, onAddImage }: Props) {
const newFiles = Array.from(newFileList);
try {
- const urls: string[] = await uploadFiles(newFiles);
-
- const newItems: UploadedItem[] = urls.map((url, i) => ({
- file: newFiles[i],
- url,
- }));
-
- const totalLength = uploadedFile.length + newItems.length;
-
+ const totalLength = uploadedFile.length + newFiles.length;
if (totalLength > 10) {
toastError('최대 10개 파일까지 업로드할 수 있어요.');
return;
}
-
// 중복 제거
- const existingUrls = new Set(uploadedFile.map((item) => item.url));
- const filteredItems = newItems.filter((item) => !existingUrls.has(item.url));
+ const existingIdentifiers = new Set(
+ uploadedFile.map((item) => (item.file ? `${item.file.name}-${item.file.size}` : item.url))
+ );
+ const filteredItems = newFiles
+ .filter((file) => !existingIdentifiers.has(`${file.name}-${file.size}`))
+ .map((file) => ({
+ file,
+ url: URL.createObjectURL(file), // 미리보기용
+ isNew: true,
+ }));
if (filteredItems.length === 0) {
toastError('이미 업로드된 파일입니다.');
@@ -49,28 +45,6 @@ function ImageInput({ uploadedFile, setUploadedFile, onAddImage }: Props) {
}
};
- const uploadFiles = async (files: File[]): Promise => {
- if (files.length === 0) return [];
-
- const formData = new FormData();
- files.forEach((file) => formData.append('file', file));
-
- const res = await fetch(`${getApi}/file/upload`, {
- method: 'POST',
- body: formData,
- });
-
- if (!res.ok) throw new Error('파일 업로드 실패');
-
- const data = await res.json();
- const urlRegex = /(https?:\/\/[^\s]+)/g;
- const matched = data.data.match(urlRegex);
-
- if (!matched) throw new Error('URL 파싱 실패');
-
- return matched;
- };
-
return (
<>
(null);
const [isVisible, setIsVisible] = useState(false);
+ const [shouldRender, setShouldRender] = useState(false); // 실제로 렌더링 여부
+ const buttonRef = useRef
(null);
- // scrollTop 버튼 클릭 시
+ // scrollTop 버튼 클릭 시 - GSAP으로 자연스러운 스크롤 애니메이션
const scrollToTop = () => {
- const currentPosition = document.documentElement.scrollTop || document.body.scrollTop;
- if (currentPosition > 0) {
- animationRef.current = requestAnimationFrame(scrollToTop);
- window.scrollTo(0, currentPosition - currentPosition / 8);
- }
+ gsap.to(window, {
+ duration: 2.2,
+ scrollTo: { y: 0, autoKill: false },
+ ease: 'power2.out',
+ });
};
- // 사용자 스크롤 시 애니메이션 취소
+ // 버튼 나타나기/사라지기 애니메이션
useEffect(() => {
- const cancelScroll = () => {
- if (animationRef.current) {
- cancelAnimationFrame(animationRef.current);
- animationRef.current = null;
+ if (buttonRef.current) {
+ if (isVisible) {
+ setShouldRender(true); // 먼저 렌더링되게 함
+ // 나타날 때 애니메이션
+ gsap.fromTo(
+ buttonRef.current,
+ { scale: 0, opacity: 0, y: 80 },
+ { scale: 1, opacity: 1, y: 0, duration: 1.0, ease: 'back.out(1.7)' }
+ );
+ } else {
+ // 사라질 때 애니메이션
+ gsap.to(buttonRef.current, {
+ scale: 0,
+ opacity: 0,
+ y: 80,
+ duration: 1.0,
+ ease: 'back.in(1.7)',
+ onComplete: () => {
+ setShouldRender(false); // 애니메이션 끝난 뒤 렌더링 제거
+ },
+ });
}
- };
+ }
+ }, [isVisible]);
+ // 스크롤 이벤트 처리
+ useEffect(() => {
const handleScroll = throttle(() => {
const currentScroll = document.documentElement.scrollTop || document.body.scrollTop;
setIsVisible(currentScroll > 30);
}, 100);
window.addEventListener('scroll', handleScroll, { passive: true });
- window.addEventListener('wheel', cancelScroll, { passive: true });
- window.addEventListener('touchstart', cancelScroll, { passive: true });
+ window.addEventListener('wheel', handleScroll, { passive: true });
+ window.addEventListener('touchstart', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
- window.removeEventListener('wheel', cancelScroll);
- window.removeEventListener('touchstart', cancelScroll);
+ window.removeEventListener('wheel', handleScroll);
+ window.removeEventListener('touchstart', handleScroll);
};
}, []);
- if (!isVisible) return null;
return (