728x90

프로필 이미지 업로더 구현하기(feat. 이미지 미리보기)📸
🚀구현 목표
- 사용자가 프로필 이미지를 클릭하면 업로드 가능
- 이미지를 선택하면 즉시 미리보기로 반영
- 편집 모드일 때만 업로드 가능 (일반 모드에서는 그냥 이미지 보여주기)

ProfileImageUploader 컴포넌트 만들기
먼저 이미지 업로드 기능을 담당하는 컴포넌트를 만들었음!
1️⃣ 초기 상태 (기본 이미지 출력)
useState로 초기 미리보기 이미지 (preview) 를 설정함!
const [preview, setPreview] = useState(profileImageUrl || profilePlaceholder)
2️⃣ 파일 선택 (input 이벤트 발생)
파일 업로드 버튼을 누르면 handleImageChange 함수가 실행됨!
const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0] // 사용자가 선택한 파일 가져오기
if (file) {
setPreview(URL.createObjectURL(file)) // 미리보기용 URL 생성
onImageChange(file) // 부모 컴포넌트에 전달
}
}
3️⃣ 미리보기 이미지 변경
파일 선택 후, 선택한 이미지가 즉시 보임!
<div className="relative">
<Image
src={preview}
alt="프로필 이미지"
className="size-30 rounded-full object-cover md:size-40"
width={120}
height={120}
/>
<input
type="file"
accept="image/*"
onChange={handleImageChange}
className="absolute inset-0 cursor-pointer opacity-0"
/>
</div>
- inset-0: top: 0; right: 0; bottom: 0; left: 0; → 부모 영역을 꽉 채움
- opacity-0: opacity: 0; → 완전히 투명하게 만들어서 보이지 않음
→ input[type="file"]는 보이지 않지만, div를 클릭하면 파일 업로드 창이 뜨도록 동작함!
→ 사용자는 이미지를 클릭하는 것처럼 느끼지만, 사실 보이지 않는 input을 클릭하는 거임
풀코드)
import { ChangeEvent, useState } from 'react'
import Image, { StaticImageData } from 'next/image'
import profilePlaceholder from '@/assets/icons/profile.png'
type ProfileImageUploaderProps = {
profileImageUrl: string | StaticImageData
onImageChange: (file: File | null) => void
}
export default function ProfileImageUploader({
profileImageUrl,
onImageChange,
}: ProfileImageUploaderProps) {
const [preview, setPreview] = useState(profileImageUrl || profilePlaceholder)
const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) {
setPreview(URL.createObjectURL(file)) // 이미지 미리보기
onImageChange(file) // 상위 컴포넌트에 파일 전달
}
}
return (
<div className="relative">
<Image
src={preview}
alt="프로필 이미지"
className="size-30 rounded-full object-cover md:size-40"
width={120}
height={120}
/>
<input
type="file"
accept="image/*"
onChange={handleImageChange}
className="absolute inset-0 cursor-pointer opacity-0"
/>
</div>
)
}
ProfileImageUploader를 상위컴포넌트에 넣어주자
- isEditing이 true일 때만 업로더 보여줌
- imagePreviewUrl: 사용자가 새로 선택한 이미지의 미리보기 URL
- profileEdit: 편집 모드일 때 보여줄 기본 이미지
- setSelectedImage: 이미지 선택 시 File 객체 저장하는 함수
const [isEditing, setIsEditing] = useState(false)
const [selectedImage, setSelectedImage] = useState<File | null>(null)
const [imagePreviewUrl, setImagePreviewUrl] = useState<string>('')
// 업로드 시 미리보기 URL 저장
const handleImageChange = (file: File | null) => {
if (file) {
setSelectedImage(file)
setImagePreviewUrl(URL.createObjectURL(file))
}
}
<div className="rounded-full border-6 md:border-8">
{isEditing ? (
<ProfileImageUploader
profileImageUrl={imagePreviewUrl || profileEdit}
onImageChange={setSelectedImage}
/>
) : (
<Image
src={user?.profileImageUrl || profile}
alt="프로필 이미지"
className="size-30 rounded-full object-cover transition-all md:size-40"
width={120}
height={120}
/>
)}
</div>
구현 완!🥳🥳🥳
변경 전)

편집 중)

변경 후)

'✨FRONTEND > 📍Next.js' 카테고리의 다른 글
| Next.js App Router에서 React Query 쓰면서 metadata 쓰기 (0) | 2025.09.06 |
|---|---|
| Next.js App Router + Tailwind + Prettier + ESLint + husky 세팅하기 (2) | 2025.05.17 |
| 검색 기능 구현(feat. 디바운스) (0) | 2025.04.07 |
| 드롭다운 메뉴 직접 구현하기 (0) | 2025.04.04 |
| Next.js 마크다운 미리보기 적용하기 (0) | 2025.03.30 |