프론트 수정
This commit is contained in:
@@ -1,73 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from "react"
|
|
||||||
import { callServer } from "@/lib/Auth"
|
|
||||||
import { isLogin } from "@/utils/Login"
|
|
||||||
|
|
||||||
const ImgInputForm_client = () => {
|
|
||||||
|
|
||||||
//페이지 오픈 시 로그인 여부 확인
|
|
||||||
useEffect(() => {
|
|
||||||
isLogin
|
|
||||||
})
|
|
||||||
|
|
||||||
const fileRef = useRef<HTMLInputElement|null>(null)
|
|
||||||
const [fileName, setFileName] = useState('')
|
|
||||||
const [preview, setPreview] = useState<String|null>()
|
|
||||||
|
|
||||||
const imgInput = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const selected = e.target.files?.[0];
|
|
||||||
if (selected != null) {
|
|
||||||
setFileName(selected?.name)
|
|
||||||
setPreview(URL.createObjectURL(selected))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputFileClick = () => {
|
|
||||||
const file = fileRef.current;
|
|
||||||
if (file) file.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirm = async () => {
|
|
||||||
const files = fileRef.current?.files
|
|
||||||
if (files) {
|
|
||||||
const file = files[0]
|
|
||||||
const fomrData = new FormData()
|
|
||||||
fomrData.append("image", file)
|
|
||||||
console.log("here")
|
|
||||||
console.log(file.size)
|
|
||||||
await fetch("http://localhost:9001/img/get-img", {
|
|
||||||
method: "POST",
|
|
||||||
body: fomrData
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="flex items-center gap-3 max-w-xs max-h-40">
|
|
||||||
{ preview &&
|
|
||||||
<img src={preview} sizes=""/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<input type="text"
|
|
||||||
readOnly
|
|
||||||
value={fileName}
|
|
||||||
onClick={inputFileClick}
|
|
||||||
className="w-lg px-3 py-2 border rounded h-10
|
|
||||||
cursor-pointer
|
|
||||||
bg-white
|
|
||||||
text-gray-700
|
|
||||||
focus:outline-none
|
|
||||||
focus:ring-2 focus:ring-indigo-500" />
|
|
||||||
<input type="file" ref={fileRef} className="hidden" accept="image/*" onChange={imgInput} />
|
|
||||||
<button onClick={confirm} className="w-10 h-10 border rounded text-gray-700">확인</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ImgInputForm_client
|
|
||||||
59
src/app/components/ImgInputForm.module.css
Normal file
59
src/app/components/ImgInputForm.module.css
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
.wrapper {
|
||||||
|
@apply w-full max-w-md mx-auto p-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropzone {
|
||||||
|
@apply border-2 rounded-lg p-4 flex flex-col items-center justify-center transition-colors duration-150;
|
||||||
|
}
|
||||||
|
|
||||||
|
.normal {
|
||||||
|
@apply border-dashed border-gray-300 bg-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragging {
|
||||||
|
@apply border-indigo-400 bg-indigo-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileInput {
|
||||||
|
@apply hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewBox {
|
||||||
|
@apply w-32 h-32 rounded-md overflow-hidden bg-gray-100 flex items-center justify-center mb-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewImg {
|
||||||
|
@apply object-cover w-full h-full cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
@apply text-gray-400 text-center text-sm px-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
@apply w-full flex gap-2 items-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textInput {
|
||||||
|
@apply flex-1 px-3 py-2 border rounded h-10 cursor-pointer bg-white text-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
@apply px-4 py-2 rounded text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnLoading {
|
||||||
|
@apply bg-indigo-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnPrimary {
|
||||||
|
@apply bg-indigo-600 hover:bg-indigo-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorMsg {
|
||||||
|
@apply mt-3 text-sm text-red-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.successMsg {
|
||||||
|
@apply mt-3 text-sm text-green-600;
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
'useClient'
|
|
||||||
|
|
||||||
import axios from "axios"
|
|
||||||
|
|
||||||
const ImgInputForm_server = () => {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isLogin = axios.create({
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
export default ImgInputForm_server
|
|
||||||
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
|
||||||
@@ -1,16 +1,9 @@
|
|||||||
|
|
||||||
import ImgInputForm_client from '../components/ImgInputForm.client'
|
import ImgInputForm from '../components/ImgInputForm'
|
||||||
import ImgInputForm_server from '../components/ImgInputForm.server'
|
|
||||||
|
|
||||||
const MainPage = () => {
|
const MainPage = () => {
|
||||||
|
|
||||||
return (
|
return <ImgInputForm />
|
||||||
<>
|
|
||||||
<ImgInputForm_server />
|
|
||||||
<ImgInputForm_client />
|
|
||||||
</>
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user