11"use client" ;
22
33import { useRouter } from "next/navigation" ;
4- import { useEffect , useRef , useState } from "react" ;
4+ import { type ChangeEvent , useEffect , useRef , useState } from "react" ;
55import { useCreatePost } from "@/apis/community" ;
66import TopDetailNavigation from "@/components/layout/TopDetailNavigation" ;
77import { IconImage , IconPostCheckboxFilled , IconPostCheckboxOutlined } from "@/public/svgs" ;
@@ -17,6 +17,8 @@ const PostForm = ({ boardCode }: PostFormProps) => {
1717 const imageUploadRef = useRef < HTMLInputElement > ( null ) ;
1818 const router = useRouter ( ) ;
1919 const [ isQuestion , setIsQuestion ] = useState < boolean > ( false ) ;
20+ const [ selectedImage , setSelectedImage ] = useState < File | null > ( null ) ;
21+ const [ imagePreviewUrl , setImagePreviewUrl ] = useState < string | null > ( null ) ;
2022
2123 const createPostMutation = useCreatePost ( ) ;
2224
@@ -43,6 +45,32 @@ const PostForm = ({ boardCode }: PostFormProps) => {
4345 return ( ) => { } ;
4446 } , [ ] ) ;
4547
48+ useEffect ( ( ) => {
49+ if ( ! selectedImage ) {
50+ setImagePreviewUrl ( null ) ;
51+ return ;
52+ }
53+
54+ const objectUrl = URL . createObjectURL ( selectedImage ) ;
55+ setImagePreviewUrl ( objectUrl ) ;
56+
57+ return ( ) => {
58+ URL . revokeObjectURL ( objectUrl ) ;
59+ } ;
60+ } , [ selectedImage ] ) ;
61+
62+ const handleImageChange = ( event : ChangeEvent < HTMLInputElement > ) => {
63+ const file = event . target . files ?. [ 0 ] ?? null ;
64+ setSelectedImage ( file ) ;
65+ } ;
66+
67+ const removeSelectedImage = ( ) => {
68+ setSelectedImage ( null ) ;
69+ if ( imageUploadRef . current ) {
70+ imageUploadRef . current . value = "" ;
71+ }
72+ } ;
73+
4674 const submitPost = async ( ) => {
4775 createPostMutation . mutate (
4876 {
@@ -53,7 +81,7 @@ const PostForm = ({ boardCode }: PostFormProps) => {
5381 content,
5482 isQuestion,
5583 } ,
56- file : imageUploadRef . current ?. files ? Array . from ( imageUploadRef . current . files ) : [ ] ,
84+ file : selectedImage ? [ selectedImage ] : [ ] ,
5785 } ,
5886 {
5987 onSuccess : ( data ) => {
@@ -116,7 +144,7 @@ const PostForm = ({ boardCode }: PostFormProps) => {
116144 >
117145 < IconImage />
118146 </ button >
119- < input className = "hidden" ref = { imageUploadRef } type = "file" accept = "image/*" multiple />
147+ < input className = "hidden" ref = { imageUploadRef } type = "file" accept = "image/*" onChange = { handleImageChange } />
120148 </ div >
121149 </ div >
122150 < div >
@@ -127,6 +155,22 @@ const PostForm = ({ boardCode }: PostFormProps) => {
127155 onChange = { ( e ) => setContent ( e . target . value ) }
128156 />
129157 </ div >
158+ { imagePreviewUrl ? (
159+ < div className = "px-5 pb-2" >
160+ < p className = "mb-2 text-gray-250/87 typo-regular-4" > 첨부 이미지</ p >
161+ < div className = "relative h-24 w-24 overflow-hidden rounded-md border border-gray-c-100" >
162+ < img src = { imagePreviewUrl } alt = "업로드 이미지 미리보기" className = "h-full w-full object-cover" />
163+ < button
164+ type = "button"
165+ className = "absolute right-1 top-1 rounded bg-black/60 px-1 py-0.5 text-xs text-white"
166+ onClick = { removeSelectedImage }
167+ aria-label = "이미지 제거"
168+ >
169+ 삭제
170+ </ button >
171+ </ div >
172+ </ div >
173+ ) : null }
130174 < div className = "px-5 pt-2.5" >
131175 < p className = "text-gray-250/87 typo-sb-9" > { noticeTitle } </ p >
132176 < p className = "mt-2 whitespace-pre-line text-gray-100 typo-regular-4" > { noticeContent } </ p >
0 commit comments