From c738ffe0c1cf3e8bdd596d583851ba216bdcf755 Mon Sep 17 00:00:00 2001 From: Sage Date: Wed, 19 Nov 2025 23:19:45 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(avatar):=20=EA=B3=A0=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=95=84=EC=9D=B4=EC=BD=98=20=ED=8F=B4=EB=B0=B1=EC=9D=B4=20?= =?UTF-8?q?=ED=8F=AC=ED=95=A8=EB=90=9C=20Avatar=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자 이미지가 없을 때 ghost.svg 아이콘을 표시하는 Avatar 컴포넌트 구현 - ghost.svg 아이콘 에셋 신규 추가 - 이미지 로딩 실패 시 자동으로 폴백 아이콘 표시되도록 처리 --- package-lock.json | 11 ++++++++ src/assets/icons/ghost.svg | 45 ++++++++++++++++++++++++++++++++ src/components/Avatar/Avatar.tsx | 25 ++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/assets/icons/ghost.svg create mode 100644 src/components/Avatar/Avatar.tsx diff --git a/package-lock.json b/package-lock.json index 9822503..01c307f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9987,6 +9987,17 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/vite": { "name": "rolldown-vite", "version": "7.1.14", diff --git a/src/assets/icons/ghost.svg b/src/assets/icons/ghost.svg new file mode 100644 index 0000000..c683167 --- /dev/null +++ b/src/assets/icons/ghost.svg @@ -0,0 +1,45 @@ + + + Entertainment Events Hobbies Horror Ghost Streamline Icon: https://streamlinehq.com + + entertainment-events-hobbies-horror-ghost + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/Avatar/Avatar.tsx b/src/components/Avatar/Avatar.tsx new file mode 100644 index 0000000..304a495 --- /dev/null +++ b/src/components/Avatar/Avatar.tsx @@ -0,0 +1,25 @@ +import type { ComponentPropsWithoutRef } from "react"; +import { twMerge } from "tailwind-merge"; + +import Ghost from "@/assets/icons/ghost.svg?react"; + +export interface AvatarProps extends Omit, "src"> { + src?: string | null; +} + +export default function Avatar({ src, alt = "사용자 아바타", className, ...props }: AvatarProps) { + return ( +
+ {src ? ( + {alt} + ) : ( + + )} +
+ ); +} From 2ff618d273043f5cf9bd3f70af6b8b2e92a4c9ee Mon Sep 17 00:00:00 2001 From: Sage Date: Wed, 19 Nov 2025 23:27:28 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor(avatar):=20Avatar=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EC=95=84=EB=B0=94?= =?UTF-8?q?=ED=83=80=20=EB=A0=8C=EB=8D=94=EB=A7=81=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 친구 목록, 친구 요청, 사용자 검색, 갤러리 상세, 홈 화면, 댓글 아이템 등에서 사용하던 개별 및 폴백 처리 로직을 Avatar 컴포넌트로 일원화 - 중복된 아바타 렌더링 코드를 제거하여 유지보수성과 UI 일관성 개선 - 이미지 로딩 실패 시 Avatar 컴포넌트의 공통 폴백 아이콘이 자동 적용되도록 변경 --- src/features/friends/list/List.tsx | 9 +++------ src/features/friends/request/Request.tsx | 10 +++------- src/features/friends/search/Search.tsx | 10 +++------- .../minihome/gallery/GalleryDetailPanel.tsx | 12 ++---------- src/features/minihome/home/HomePage.tsx | 18 +++++------------- .../minihome/post/detail/CommentItem.tsx | 11 +++-------- 6 files changed, 19 insertions(+), 51 deletions(-) diff --git a/src/features/friends/list/List.tsx b/src/features/friends/list/List.tsx index 69aba57..7d679c3 100644 --- a/src/features/friends/list/List.tsx +++ b/src/features/friends/list/List.tsx @@ -1,6 +1,7 @@ import { Home, UserMinus2 } from "lucide-react"; import { useEffect, useState } from "react"; +import Avatar from "@/components/Avatar/Avatar"; import Button from "@/components/Button/Button"; import BevelScrollContainer from "@/components/Container/BevelScrollContainer"; import { useAuthStore } from "@/stores/useAuthStore"; @@ -62,12 +63,8 @@ export default function List() { return (
  • -
    - 프로필 아바타 +
    +
    {profile.nickname} diff --git a/src/features/friends/request/Request.tsx b/src/features/friends/request/Request.tsx index 5e98126..a2f15a8 100644 --- a/src/features/friends/request/Request.tsx +++ b/src/features/friends/request/Request.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; +import Avatar from "@/components/Avatar/Avatar"; import Button from "@/components/Button/Button"; import BevelScrollContainer from "@/components/Container/BevelScrollContainer"; import { useAuthStore } from "@/stores/useAuthStore"; @@ -59,13 +60,8 @@ export default function Request() { {requests.map((request) => (
  • -
    - 프로필 아바타 +
    +
    diff --git a/src/features/friends/search/Search.tsx b/src/features/friends/search/Search.tsx index 4cb131d..45e1073 100644 --- a/src/features/friends/search/Search.tsx +++ b/src/features/friends/search/Search.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; +import Avatar from "@/components/Avatar/Avatar"; import Button from "@/components/Button/Button"; import BevelScrollContainer from "@/components/Container/BevelScrollContainer"; import Input from "@/components/Input/Input"; @@ -75,13 +76,8 @@ export default function Search() { {results.map((Profile) => (
  • -
    - 프로필 아바타 +
    +
    {Profile.nickname} diff --git a/src/features/minihome/gallery/GalleryDetailPanel.tsx b/src/features/minihome/gallery/GalleryDetailPanel.tsx index bbeb174..a3b34a1 100644 --- a/src/features/minihome/gallery/GalleryDetailPanel.tsx +++ b/src/features/minihome/gallery/GalleryDetailPanel.tsx @@ -3,6 +3,7 @@ import { ChevronLeft, Heart, Send, X } from "lucide-react"; import { useEffect, useState } from "react"; import { twMerge } from "tailwind-merge"; +import Avatar from "@/components/Avatar/Avatar"; import Button from "@/components/Button/Button"; import Input from "@/components/Input/Input"; import { useAuthStore } from "@/stores/useAuthStore"; @@ -304,22 +305,13 @@ export default function GalleryDetailPanel({ comments.map((comment) => { const nickname = comment.author?.nickname ?? "알 수 없음"; const avatar = comment.author?.avatar_url; - const fallbackInitial = nickname.charAt(0); const body = stringifyCommentContent(comment.content); return (
    - {avatar ? ( - {`${nickname} - ) : ( - fallbackInitial - )} +
    diff --git a/src/features/minihome/home/HomePage.tsx b/src/features/minihome/home/HomePage.tsx index 050d383..0e62d6e 100644 --- a/src/features/minihome/home/HomePage.tsx +++ b/src/features/minihome/home/HomePage.tsx @@ -1,8 +1,9 @@ import dayjs from "dayjs"; -import { MessageCircle, UserRound, Star } from "lucide-react"; +import { MessageCircle, Star } from "lucide-react"; import { useEffect, useState, type Dispatch, type SetStateAction } from "react"; import { twMerge } from "tailwind-merge"; +import Avatar from "@/components/Avatar/Avatar"; import { useAuthStore } from "@/stores/useAuthStore"; import type { Database } from "@/types/database.types"; import type { MiniHomeTabs } from "@/types/minihome.types"; @@ -205,21 +206,12 @@ export default function HomePage({ }; return ( -
    +
    {/* 왼쪽 프로필 영역 */}
    {/* 프로필 이미지 */} -
    - {avatarUrl ? ( -
    - -
    - ) : ( -
    - -
    - )} -
    + + {/* 이름 */}

    {nickname}

    diff --git a/src/features/minihome/post/detail/CommentItem.tsx b/src/features/minihome/post/detail/CommentItem.tsx index 67e0716..c59faec 100644 --- a/src/features/minihome/post/detail/CommentItem.tsx +++ b/src/features/minihome/post/detail/CommentItem.tsx @@ -1,8 +1,9 @@ import dayjs from "dayjs"; import "dayjs/locale/ko"; import relativeTime from "dayjs/plugin/relativeTime"; -import { Trash, User } from "lucide-react"; +import { Trash } from "lucide-react"; +import Avatar from "@/components/Avatar/Avatar"; import { useAuthUser } from "@/hooks/useAuthUser"; import type { CommentWithProfile } from "@/types/post.types"; @@ -24,13 +25,7 @@ export default function CommentItem({ comment, onDelete, isMyHome }: CommentItem return (
    - {avatarUrl ? ( - {authorName} - ) : ( -
    - -
    - )} +