프론트 수정
This commit is contained in:
149
src/app/components/ImgInputForm.tsx
Normal file
149
src/app/components/ImgInputForm.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import styles from './ImgInputForm.module.css'
|
||||
|
||||
const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB
|
||||
|
||||
const ImgInputForm = () => {
|
||||
const fileRef = useRef<HTMLInputElement | null>(null)
|
||||
const [fileName, setFileName] = useState('')
|
||||
const [preview, setPreview] = useState<string | null>(null)
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [success, setSuccess] = useState<string | null>(null)
|
||||
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:9001'
|
||||
|
||||
const handleFile = useCallback((file: File) => {
|
||||
setError(null)
|
||||
setSuccess(null)
|
||||
if (!file.type.startsWith('image/')) {
|
||||
setError('이미지 파일만 업로드할 수 있습니다.')
|
||||
return
|
||||
}
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
setError('파일 크기는 5MB 이하만 업로드 가능합니다.')
|
||||
return
|
||||
}
|
||||
|
||||
setFileName(file.name)
|
||||
setPreview(URL.createObjectURL(file))
|
||||
// keep the selected file in the hidden input
|
||||
if (fileRef.current) {
|
||||
const dataTransfer = new DataTransfer()
|
||||
dataTransfer.items.add(file)
|
||||
fileRef.current.files = dataTransfer.files
|
||||
}
|
||||
}, [])
|
||||
|
||||
const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
const file = e.target.files?.[0]
|
||||
if (file) handleFile(file)
|
||||
}
|
||||
|
||||
const onClickPick = () => fileRef.current?.click()
|
||||
|
||||
const onDrop: React.DragEventHandler<HTMLDivElement> = (e) => {
|
||||
e.preventDefault()
|
||||
setIsDragging(false)
|
||||
const file = e.dataTransfer.files?.[0]
|
||||
if (file) handleFile(file)
|
||||
}
|
||||
|
||||
const onDragOver: React.DragEventHandler<HTMLDivElement> = (e) => {
|
||||
e.preventDefault()
|
||||
setIsDragging(true)
|
||||
}
|
||||
|
||||
const onDragLeave: React.DragEventHandler<HTMLDivElement> = () => {
|
||||
setIsDragging(false)
|
||||
}
|
||||
|
||||
const upload = async () => {
|
||||
setError(null)
|
||||
setSuccess(null)
|
||||
const files = fileRef.current?.files
|
||||
if (!files || files.length === 0) {
|
||||
setError('업로드할 파일을 선택해주세요.')
|
||||
return
|
||||
}
|
||||
const file = files[0]
|
||||
if (!file.type.startsWith('image/')) {
|
||||
setError('이미지 파일만 업로드할 수 있습니다.')
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('image', file)
|
||||
const res = await fetch(`${API_BASE}/img/get-img`, {
|
||||
method: 'POST',
|
||||
body: fd,
|
||||
credentials: 'include'
|
||||
})
|
||||
if (!res.ok) throw new Error('업로드 실패')
|
||||
setSuccess('업로드 성공')
|
||||
} catch (err) {
|
||||
setError((err as Error).message || '업로드 중 오류가 발생했습니다')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div
|
||||
className={`${styles.dropzone} ${isDragging ? styles.dragging : styles.normal}`}
|
||||
onDrop={onDrop}
|
||||
onDragOver={onDragOver}
|
||||
onDragLeave={onDragLeave}
|
||||
>
|
||||
<input
|
||||
ref={fileRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className={styles.fileInput}
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
||||
<div className={styles.previewBox}>
|
||||
{preview ? (
|
||||
// preview kept modest in size
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img src={preview} alt="preview" className={styles.previewImg} onClick={onClickPick} />
|
||||
) : (
|
||||
<div className={styles.placeholder}>
|
||||
이미지를 끌어다 놓거나<br />
|
||||
클릭해 선택하세요
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.row}>
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={fileName}
|
||||
onClick={onClickPick}
|
||||
placeholder="선택된 파일 없음"
|
||||
className={styles.textInput}
|
||||
/>
|
||||
<button
|
||||
onClick={upload}
|
||||
disabled={loading}
|
||||
className={`${styles.btn} ${loading ? styles.btnLoading : styles.btnPrimary}`}
|
||||
>
|
||||
{loading ? '업로드 중...' : '업로드'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error && <p className={styles.errorMsg}>{error}</p>}
|
||||
{success && <p className={styles.successMsg}>{success}</p>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ImgInputForm
|
||||
Reference in New Issue
Block a user