프론트 수정

This commit is contained in:
2026-02-02 23:14:53 +09:00
parent 1c1a4383e1
commit fd8ea31fd8
5 changed files with 210 additions and 95 deletions

View File

@@ -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

View 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;
}

View File

@@ -1,13 +0,0 @@
'useClient'
import axios from "axios"
const ImgInputForm_server = () => {
}
export const isLogin = axios.create({
})
export default ImgInputForm_server

View 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

View File

@@ -1,16 +1,9 @@
import ImgInputForm_client from '../components/ImgInputForm.client'
import ImgInputForm_server from '../components/ImgInputForm.server'
import ImgInputForm from '../components/ImgInputForm'
const MainPage = () => {
return (
<>
<ImgInputForm_server />
<ImgInputForm_client />
</>
)
return <ImgInputForm />
}