Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 39 additions & 36 deletions components/UserProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,35 @@ import Spinner from './Spinner';
/**
* UserProfile 컴포넌트
* 사용자의 프로필 정보를 표시하고 편집하는 컴포넌트
* PC와 모바일/태블릿 환경에서 각각 다른 레이아웃으로 표시됨
* PC, 태블릿, 모바일 환경에 따라 레이아웃이 반응형으로 변경
*
* @param data - 사용자 프로필 데이터
* @param isEditing - 편집 모드 여부 (기본값: false)
* @param data - 사용자 프로필 데이터 객체
* @param isEditing - 편집 모드 여부
* @param onDataChange - 데이터 변경 시 호출되는 콜백 함수
*/
function UserProfile({
data,
isEditing = false,
onDataChange,
}: UserProfileProps) {
// 추가 정보 영역의 펼침/접힘 상태 관리
// 추가 정보 영역의 펼침/접힘 상태 관리
const { isExpanded, toggleExpand } = useExpandedState();

// 프로필 이미지 관련 상태와 핸들러
// 프로필 이미지 관련 상태와 이벤트 핸들러
const {
isLoading,
previewImage,
fileInputRef,
handleImageClick,
handleFileChange,
isLoading, // 이미지 로딩 상태
previewImage, // 이미지 미리보기 URL
fileInputRef, // 파일 입력 요소 참조
handleImageClick, // 이미지 클릭 핸들러
handleFileChange, // 파일 변경 핸들러
} = useProfileImage((url) => onDataChange('image', url));

/**
* 프로필 필드를 렌더링하는 헬퍼 함수
* 편집 모드에서는 입력 필드로, 조회 모드에서는 텍스트로 표시
*
* @param label - 필드 레이블
* @param field - 데이터 객체의
* @param field - data 객체의 키값
* @returns 렌더링된 필드 컴포넌트
*/
const renderField = (label: string, field: keyof typeof data) => {
Expand Down Expand Up @@ -77,32 +77,35 @@ function UserProfile({
type="button"
disabled={isLoading}
>
{/* 프로필 이미지 표시 */}
<Image
src={
previewImage !== null
? previewImage
: data.image && data.image !== ''
? data.image
: '/icon/icon-profile.svg'
}
alt="Profile"
width={100}
height={100}
priority
className={`rounded-full object-cover ${
{/* 이미지 컨테이너 */}
<div
className={`relative ${
isEditing
? 'mo:size-[62px] ta:size-[71px] pc:size-48'
: 'mo:size-[62px] ta:size-[71px] pc:size-48'
}`}
/>
{/* 편집 모드일 때 이미지 위에 오버레이 표시 */}
>
{/* 프로필 이미지 */}
<Image
src={
previewImage !== null
? previewImage
: data.image && data.image !== ''
? data.image
: '/icon/icon-profile.svg'
}
alt="Profile"
fill
sizes="(max-width: 768px) 62px, (max-width: 1024px) 71px, 192px"
priority
className="rounded-full object-cover"
/>
</div>
{isEditing && (
<div className="absolute left-0 top-0 flex size-full items-center justify-center rounded-full bg-black/50">
{isLoading ? (
<Spinner />
) : (
// 카메라 아이콘 표시
<Image
src="/icon/icon-camera.svg"
alt="Edit photo"
Expand All @@ -114,23 +117,24 @@ function UserProfile({
</div>
)}
</button>
{/* 숨겨진 파일 입력 필드 */}
{/* 숨겨진 파일 입력 필드: 실제 파일 선택 다이얼로그를 위한 요소 */}
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleFileChange}
className="hidden"
aria-label="Upload profile image"
disabled={!isEditing || isLoading}
/>
</div>

{/* 프로필 정보 섹션 */}
{/* 프로필 정보 섹션: 사용자 정보를 표시하는 영역 */}
<div
className={`${isEditing ? 'mt-4 mo:justify-center' : 'mo:ml-4 mo:flex-1 mo:pl-[20px] ta:ml-4 ta:flex-1 ta:pl-[40px] pc:mt-4'} mt-6 mo:pc:w-full`}
>
<div className="space-y-3">
{/* 기본 정보 영역 */}
{/* 기본 정보 영역: 항상 표시되는 필수 정보들 */}
<div
className={`${
isEditing
Expand All @@ -144,7 +148,7 @@ function UserProfile({
{isEditing && renderField('SNS 계정', 'sns')}
</div>

{/* 추가 정보 영역 - 편집 모드와 조회 모드에서 다르게 표시 */}
{/* 추가 정보 영역: 편집 모드와 조회 모드에서 다르게 표시 */}
{isEditing ? (
// 편집 모드: 모든 필드를 그리드로 표시
<div className="mo:space-y-3 ta:grid ta:grid-cols-2 ta:place-items-center ta:gap-3 pc:space-y-3">
Expand All @@ -153,15 +157,14 @@ function UserProfile({
{renderField('혈액형', 'bloodType')}
{renderField('국적', 'nationality')}
{renderField('가족관계', 'family')}
{renderField('자기소개', 'content')}
</div>
) : (
// 조회 모드: PC와 모바일/태블릿에서 다르게 표시
<>
{/* 모바일/태블릿용 펼침/접힘 섹션 */}
<div className="pc:hidden">
{!isExpanded ? (
// 접힌 상태: 펼치기 버튼
// 접힌 상태: 펼치기 버튼 표시
<div className="flex justify-center">
<button
onClick={toggleExpand}
Expand All @@ -176,7 +179,7 @@ function UserProfile({
</button>
</div>
) : (
// 펼친 상태: 추가 정보와 접기 버튼
// 펼친 상태: 추가 정보와 접기 버튼 표시
<div>
<div className="space-y-3">
{renderField('SNS 계정', 'sns')}
Expand All @@ -202,7 +205,7 @@ function UserProfile({
)}
</div>

{/* PC용 추가 정보 섹션 - 항상 표시 */}
{/* PC용 추가 정보 섹션: 항상 표시 */}
<div className="hidden space-y-3 pc:block">
{renderField('SNS 계정', 'sns')}
{renderField('생일', 'birthday')}
Expand Down
Loading