@@ -20,8 +20,11 @@ const LetterRegisterPage = () => {
2020 const { showToast } = useToast ( ) ;
2121 const [ sender , setSender ] = useState < string > ( "" ) ;
2222 const [ content , setContent ] = useState < string > ( "" ) ;
23- const [ images , setImages ] = useState < string [ ] > ( [ ] ) ;
23+ const [ images , setImages ] = useState < string [ ] > ( [ ] ) ; // 서버 전송용
24+ const [ previewImages , setPreviewImages ] = useState < string [ ] > ( [ ] ) ; // 미리보기용
2425 const [ isButtonDisabled , setIsButtonDisabled ] = useState < boolean > ( false ) ;
26+ const [ isImageUploadLoading , setImageUploadLoading ] =
27+ useState < boolean > ( false ) ; // 서버 이미지 업로드 상태
2528
2629 const [ letterState , setLetterState ] = useRecoilState ( registerLetterState ) ;
2730 const [ isToastShown , setIsToastShown ] = useState ( false ) ;
@@ -34,11 +37,16 @@ const LetterRegisterPage = () => {
3437 setSender ( letterState . senderName ) ;
3538 setContent ( letterState . content ) ;
3639 setImages ( letterState . images ) ;
40+ setPreviewImages ( letterState . previewImages ) ;
3741 }
3842 } , [ letterState ] ) ;
3943
4044 const handleSenderChange = ( newValue : string ) => {
4145 setSender ( newValue ) ;
46+ setLetterState ( ( prevState ) => ( {
47+ ...prevState ,
48+ senderName : newValue ,
49+ } ) ) ;
4250 } ;
4351
4452 const handleContentChange = ( newValue : string ) => {
@@ -48,65 +56,68 @@ const LetterRegisterPage = () => {
4856 } else {
4957 setContent ( newValue ) ;
5058 }
59+ setLetterState ( ( prevState ) => ( {
60+ ...prevState ,
61+ content : newValue ,
62+ } ) ) ;
63+ } ;
64+
65+ const handleShowToast = ( ) => {
66+ /* 토스트 메세지 보여지기 전*/
67+ if ( previewImages . length >= 4 && ! isToastShown ) {
68+ showToast ( "사진 첨부는 최대 4장까지 가능해요." , {
69+ icon : true ,
70+ close : false ,
71+ bottom : "113px" ,
72+ } ) ;
73+ setIsToastShown ( true ) ;
74+ }
5175 } ;
5276
5377 const handleAddImages = async (
5478 event : React . ChangeEvent < HTMLInputElement >
5579 ) => {
5680 const files = event . target . files ;
5781 if ( files ) {
58- const selectedImages : File [ ] = Array . from ( files ) . slice ( 0 , 4 ) ;
59- const totalImages = images . length + selectedImages . length ;
60-
61- /* 토스트 메세지 이미 보여짐 */
62- if ( isToastShown ) {
63- if ( totalImages >= 4 ) {
64- setIsButtonDisabled ( true ) ;
65- return ;
66- }
82+ const selectedImages : File [ ] = Array . from ( files ) ;
83+ const existingImageCount = ( previewImages || [ ] ) . length ;
6784
68- if ( totalImages > 4 ) {
69- const additionalImagesNeeded = 4 - images . length ;
70- const newImages = [
71- ...images ,
72- ...selectedImages
73- . slice ( 0 , additionalImagesNeeded )
74- . map ( ( file ) => URL . createObjectURL ( file ) ) ,
75- ] ;
76- setImages ( newImages ) ;
77- return ;
78- }
79- } else {
80- /* 토스트 메세지 보여지기 전*/
81- if ( totalImages > 4 ) {
82- showToast ( "사진 첨부는 최대 4장까지 가능해요." , {
83- icon : true ,
84- close : false ,
85- bottom : "113px" ,
86- } ) ;
87- setIsToastShown ( true ) ;
88- setIsButtonDisabled ( true ) ;
89- const newImages = [
90- ...images ,
91- ...selectedImages
92- . slice ( 0 , 4 - images . length )
93- . map ( ( file ) => URL . createObjectURL ( file ) ) ,
94- ] ;
95- setImages ( newImages ) ;
96- return ;
97- }
98- }
85+ // 총 이미지 개수를 4개로 제한
86+ const additionalImagesNeeded = Math . max ( 0 , 4 - existingImageCount ) ;
9987
100- setIsButtonDisabled ( false ) ;
88+ // 초과된 이미지는 제외한 업로드 가능한 이미지
89+ const validImages = selectedImages . slice ( 0 , additionalImagesNeeded ) ;
90+
91+ // 미리보기 이미지 업데이트
92+ const newPreviewImages = [
93+ ...( previewImages || [ ] ) ,
94+ ...validImages . map ( ( file ) => URL . createObjectURL ( file ) ) ,
95+ ] ;
96+
97+ setPreviewImages ( newPreviewImages ) ;
98+
99+ // 총 이미지가 4개를 초과하려고 할 때 (토스트 메세지 보여지기 전)
100+ if ( selectedImages . length > additionalImagesNeeded && ! isToastShown ) {
101+ showToast ( "사진 첨부는 최대 4장까지 가능해요." , {
102+ icon : true ,
103+ close : false ,
104+ bottom : "113px" ,
105+ } ) ;
106+ setIsToastShown ( true ) ;
107+ setIsButtonDisabled ( false ) ;
108+ }
101109
102110 const imageUrls : string [ ] = [ ] ;
103- for ( const file of selectedImages ) {
111+ for ( const file of validImages ) {
104112 const compressedFile = await imageCompression ( file , {
105- maxSizeMB : 1 ,
106- maxWidthOrHeight : 1024 ,
113+ maxSizeMB : 0. 1,
114+ maxWidthOrHeight : 512 ,
107115 useWebWorker : true ,
108116 } ) ;
117+
109118 try {
119+ setImageUploadLoading ( true ) ;
120+
110121 const response = await postImage ( compressedFile ) ;
111122 console . log ( "이미지 업로드 성공" , response . data ) ;
112123 imageUrls . push ( response . data . imageUrl ) ;
@@ -115,23 +126,51 @@ const LetterRegisterPage = () => {
115126 }
116127 }
117128 setImages ( ( prevImages ) => [ ...prevImages , ...imageUrls ] ) ;
129+ setImageUploadLoading ( false ) ;
130+
131+ setLetterState ( ( prevState ) => ( {
132+ ...prevState ,
133+ images : [ ...( prevState . images || [ ] ) , ...imageUrls ] ,
134+ previewImages : newPreviewImages ,
135+ } ) ) ;
118136 }
119137 } ;
120138
121139 const handleDeleteImages = ( id : number ) => {
122- if ( images . length - 1 < 4 ) {
140+ const updatedImages = images . filter ( ( _ , index ) => index !== id ) ;
141+ const updatedPreviewImages = previewImages . filter (
142+ ( _ , index ) => index !== id
143+ ) ;
144+
145+ if ( previewImages . length - 1 < 4 ) {
123146 setIsButtonDisabled ( false ) ;
124147 }
125- setImages ( ( prevImages ) => prevImages . filter ( ( _ , index ) => index !== id ) ) ;
148+
149+ // 상태 업데이트
150+ setImages ( updatedImages ) ;
151+ setPreviewImages ( updatedPreviewImages ) ;
152+
153+ // setLetterState에 변경된 상태 반영
154+ setLetterState ( ( prevState ) => ( {
155+ ...prevState ,
156+ images : updatedImages ,
157+ previewImages : updatedPreviewImages ,
158+ } ) ) ;
126159 } ;
127160
128- const handleAddNext = ( ) => {
161+ useEffect ( ( ) => {
162+ console . log ( "images" , images ) ;
163+ console . log ( "previewImages" , previewImages ) ;
164+ } , [ images , previewImages ] ) ;
165+
166+ const handleAddNext = async ( ) => {
129167 /* 다음 페이지 */
130168 setLetterState ( ( prevState ) => ( {
131169 ...prevState ,
132170 senderName : sender ,
133171 content : content ,
134172 images : images ,
173+ previewImages : previewImages ,
135174 } ) ) ;
136175 if ( letterId ) {
137176 if ( independent === "true" ) {
@@ -176,7 +215,7 @@ const LetterRegisterPage = () => {
176215 placeholder = "최대 1000자까지 입력이 가능해요"
177216 height = "228px"
178217 />
179- { images . length === 0 ? (
218+ { ( previewImages || [ ] ) . length === 0 ? (
180219 < AddImageLabel >
181220 < input
182221 type = "file"
@@ -189,20 +228,22 @@ const LetterRegisterPage = () => {
189228 </ AddImageLabel >
190229 ) : (
191230 < ImagesList >
192- < AddImagesLabel >
193- < input
194- type = "file"
195- accept = "image/*"
196- multiple
197- onChange = { handleAddImages }
198- style = { { display : "none" } }
199- disabled = { isButtonDisabled }
200- />
231+ < AddImagesLabel onClick = { handleShowToast } >
232+ { previewImages . length < 4 && (
233+ < input
234+ type = "file"
235+ accept = "image/*"
236+ multiple
237+ onChange = { handleAddImages }
238+ style = { { display : "none" } }
239+ disabled = { isButtonDisabled }
240+ />
241+ ) }
201242 +< br />
202- { images . length } /4
243+ { previewImages . length } /4
203244 </ AddImagesLabel >
204245 < ImagesWrapper >
205- { images . map ( ( image , index ) => (
246+ { previewImages . map ( ( image , index ) => (
206247 < ImageDiv >
207248 < Image
208249 src = { image }
@@ -229,8 +270,12 @@ const LetterRegisterPage = () => {
229270 < Button
230271 buttonType = "primary"
231272 size = "large"
232- text = "다음"
233- disabled = { ! sender || ( ! content && images . length === 0 ) }
273+ text = { isImageUploadLoading ? "Loading..." : "다음" }
274+ disabled = {
275+ ! sender ||
276+ ( ! content && previewImages ?. length === 0 ) ||
277+ isImageUploadLoading
278+ }
234279 onClick = { handleAddNext }
235280 />
236281 </ ButtonWrapper >
0 commit comments