Skip to content

Commit 14bf0f7

Browse files
authored
Merge pull request #58 from JECT-Study/feat/55-home-artwork-space-feed
fix: 이미지 URL 표시 경로 보정
2 parents aaa3463 + f283b0c commit 14bf0f7

13 files changed

Lines changed: 75 additions & 38 deletions

File tree

src/app/art/[id]/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import RegionText from "@/components/archive-detail/RegionText";
99
import ImageSwiper from "@/components/archive-detail/ImageSwiper";
1010
import NicknameCard from "@/components/archive-detail/NicknameCard";
1111
import { getArtworkDetail } from "@/services/artworks";
12+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
1213

1314
function formatDate(date: string | null) {
1415
if (!date) return "-";
@@ -27,7 +28,11 @@ export default function ArtDetailPage() {
2728
});
2829

2930
const artwork = query.data;
30-
const artworkImages = artwork?.imageUrls?.filter(Boolean) ?? [];
31+
const artworkImages =
32+
artwork?.imageUrls?.flatMap(url => {
33+
const normalized = normalizeImageUrl(url);
34+
return normalized ? [normalized] : [];
35+
}) ?? [];
3136

3237
return (
3338
<div className="min-h-screen bg-white pb-32">

src/app/space/[id]/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ExpandableText from "@/components/archive-detail/ExpandableText";
77
import ImageSwiper from "@/components/archive-detail/ImageSwiper";
88
import NicknameCard from "@/components/archive-detail/NicknameCard";
99
import { getSpaceDetail } from "@/services/spaces";
10+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
1011

1112
export default function SpaceDetailPage() {
1213
const router = useRouter();
@@ -20,7 +21,11 @@ export default function SpaceDetailPage() {
2021
});
2122

2223
const space = query.data;
23-
const spaceImages = space?.imageUrls?.filter(Boolean) ?? [];
24+
const spaceImages =
25+
space?.imageUrls?.flatMap(url => {
26+
const normalized = normalizeImageUrl(url);
27+
return normalized ? [normalized] : [];
28+
}) ?? [];
2429

2530
return (
2631
<div className="min-h-screen bg-white pb-32">

src/components/chat/ChatListItem.tsx

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { UserRound } from "lucide-react";
22
import { formatRelativeTime } from "@/utils/formatChatTime";
3+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
34
import type { ChatRoomListItem } from "@/types/chat";
45

56
interface ChatListItemProps {
@@ -8,49 +9,46 @@ interface ChatListItemProps {
89
}
910

1011
export default function ChatListItem({ room, onClick }: ChatListItemProps) {
11-
const { context, counterparty, lastMessage, unreadCount, lastMessageAt } =
12-
room;
12+
const { context, counterparty, lastMessage, unreadCount, lastMessageAt } = room;
1313
const relativeTime = formatRelativeTime(lastMessageAt);
14+
const profileImageUrl = normalizeImageUrl(counterparty.profileImage);
1415

1516
return (
1617
<li>
1718
<button
1819
type="button"
1920
onClick={onClick}
20-
className="flex w-full items-center gap-4 px-5 py-4 text-left transition-colors hover:bg-bg-primary-darker"
21+
className="hover:bg-bg-primary-darker flex w-full items-center gap-4 px-5 py-4 text-left transition-colors"
2122
>
2223
{/* 아바타: profileImage 없으면 placeholder (시안 갭) */}
23-
{counterparty.profileImage ? (
24+
{profileImageUrl ? (
2425
// eslint-disable-next-line @next/next/no-img-element
2526
<img
26-
src={counterparty.profileImage}
27+
src={profileImageUrl}
2728
alt=""
28-
className="h-12 w-12 shrink-0 rounded-full border border-border-primary object-cover"
29+
className="border-border-primary h-12 w-12 shrink-0 rounded-full border object-cover"
2930
/>
3031
) : (
3132
<div
3233
aria-hidden
33-
className="flex h-12 w-12 shrink-0 items-center justify-center rounded-full border border-border-primary bg-bg-primary-darker text-text-disabled"
34+
className="border-border-primary bg-bg-primary-darker text-text-disabled flex h-12 w-12 shrink-0 items-center justify-center rounded-full border"
3435
>
3536
<UserRound size={24} />
3637
</div>
3738
)}
3839

3940
<div className="flex min-w-0 flex-1 flex-col gap-1">
4041
{/* 작품/공간 이름 */}
41-
<div className="truncate text-body-1 font-semibold text-text-primary">
42+
<div className="text-body-1 text-text-primary truncate font-semibold">
4243
{context.title ?? "대화 중인 작품/공간"}
4344
</div>
4445

4546
{/* 닉네임 · 시각 (2px 점 구분) */}
46-
<div className="flex items-center gap-1 text-label font-regular text-text-secondary">
47+
<div className="text-label font-regular text-text-secondary flex items-center gap-1">
4748
<span className="truncate">{counterparty.nickname ?? "상대방"}</span>
4849
{relativeTime && (
4950
<>
50-
<span
51-
aria-hidden
52-
className="size-0.5 shrink-0 rounded-full bg-text-secondary"
53-
/>
51+
<span aria-hidden className="bg-text-secondary size-0.5 shrink-0 rounded-full" />
5452
<span className="shrink-0">{relativeTime}</span>
5553
</>
5654
)}
@@ -59,11 +57,11 @@ export default function ChatListItem({ room, onClick }: ChatListItemProps) {
5957
{/* 마지막 메시지 + 안읽음 배지 (양 끝 정렬) */}
6058
{(lastMessage || unreadCount > 0) && (
6159
<div className="flex items-center justify-between gap-2">
62-
<div className="min-w-0 flex-1 truncate text-label font-regular text-text-secondary">
60+
<div className="text-label font-regular text-text-secondary min-w-0 flex-1 truncate">
6361
{lastMessage ?? ""}
6462
</div>
6563
{unreadCount > 0 && (
66-
<span className="flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full bg-object-red px-1.5 text-caption font-regular text-text-invert">
64+
<span className="bg-object-red text-caption font-regular text-text-invert flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full px-1.5">
6765
{unreadCount}
6866
</span>
6967
)}

src/components/chat/ChatRoomInfo.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,22 @@ import { ImageIcon } from "lucide-react";
55
import ProposeExhibitionSheet from "@/components/chat/ProposeExhibitionSheet";
66
import { CHAT_CONTEXT_TYPE_LABEL, CHAT_PROPOSE_EXHIBITION_LABEL } from "@/constants/chat";
77
import type { ChatContext } from "@/types/chat";
8+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
89

910
interface ChatRoomInfoProps {
1011
context: ChatContext;
1112
}
1213

1314
export default function ChatRoomInfo({ context }: ChatRoomInfoProps) {
1415
const [proposeOpen, setProposeOpen] = useState(false);
16+
const thumbnailUrl = normalizeImageUrl(context.thumbnailUrl);
1517

1618
return (
1719
<div className="border-b-border-primary bg-bg-primary flex w-full items-center gap-3 border-b px-4 py-3">
1820
{/* 썸네일 (없으면 placeholder) */}
19-
{context.thumbnailUrl ? (
21+
{thumbnailUrl ? (
2022
// eslint-disable-next-line @next/next/no-img-element
21-
<img
22-
src={context.thumbnailUrl}
23-
alt=""
24-
className="h-12 w-12 shrink-0 rounded-lg object-cover"
25-
/>
23+
<img src={thumbnailUrl} alt="" className="h-12 w-12 shrink-0 rounded-lg object-cover" />
2624
) : (
2725
<div
2826
aria-hidden

src/components/chat/ProposalCard.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { useAcceptProposal } from "@/hooks/useAcceptProposal";
1515
import { useProposal } from "@/hooks/useProposal";
1616
import { useRejectProposal } from "@/hooks/useRejectProposal";
17+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
1718

1819
interface ProposalCardProps {
1920
proposalId: number;
@@ -54,6 +55,7 @@ export default function ProposalCard({ proposalId, myUserId }: ProposalCardProps
5455
const isPending = proposal.status === "PENDING";
5556
const isMutating = accept.isPending || reject.isPending;
5657
const actionError = accept.error ?? reject.error;
58+
const artworkThumbnailUrl = normalizeImageUrl(proposal.artwork.thumbnailUrl);
5759

5860
return (
5961
<div className={`flex ${isProposer ? "justify-end" : "justify-start"}`}>
@@ -76,10 +78,10 @@ export default function ProposalCard({ proposalId, myUserId }: ProposalCardProps
7678
<Field label={PROPOSAL_CARD_SPACE_LABEL} value={proposal.space.title} />
7779
</div>
7880

79-
{proposal.artwork.thumbnailUrl ? (
81+
{artworkThumbnailUrl ? (
8082
// eslint-disable-next-line @next/next/no-img-element
8183
<img
82-
src={proposal.artwork.thumbnailUrl}
84+
src={artworkThumbnailUrl}
8385
alt=""
8486
className="h-14 w-14 shrink-0 rounded-lg object-cover"
8587
/>

src/components/exhibition-consent/ExhibitionProgressInfoCard.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ConsentExhibitionInfo } from "@/types/exhibitionConsent";
2+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
23

34
interface ExhibitionProgressInfoCardProps {
45
exhibition: ConsentExhibitionInfo;
@@ -9,9 +10,13 @@ function formatDateRange(startDate: string, endDate: string) {
910
}
1011

1112
function InfoAvatar({ src, alt }: { src: string | null; alt: string }) {
13+
const displaySrc = normalizeImageUrl(src);
14+
1215
return (
1316
<div className="bg-object-disabled h-12 w-12 shrink-0 overflow-hidden rounded-full">
14-
{src ? <img src={src} alt={alt} className="h-full w-full object-cover" /> : null}
17+
{displaySrc ? (
18+
<img src={displaySrc} alt={alt} className="h-full w-full object-cover" />
19+
) : null}
1520
</div>
1621
);
1722
}

src/components/exhibition-consent/SignatureSection.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { RefreshCw } from "lucide-react";
44

55
import SignaturePad from "@/components/exhibition-consent/SignaturePad";
66
import type { ConsentMode } from "@/types/exhibitionConsent";
7+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
78

89
interface SignatureSectionProps {
910
mode: ConsentMode;
@@ -32,6 +33,7 @@ export default function SignatureSection({
3233
}: SignatureSectionProps) {
3334
const isReadOnly = mode === "readonly";
3435
const displayDate = formatSignatureDate(signedAt);
36+
const displaySignatureUrl = normalizeImageUrl(signatureDataUrl);
3537

3638
const handleClear = () => {
3739
onSignatureChange?.(null);
@@ -58,9 +60,9 @@ export default function SignatureSection({
5860

5961
{isReadOnly ? (
6062
<div className="bg-bg-primary-darker border-border-primary flex h-[152px] items-center justify-center overflow-hidden rounded-lg border">
61-
{signatureDataUrl ? (
63+
{displaySignatureUrl ? (
6264
<img
63-
src={signatureDataUrl}
65+
src={displaySignatureUrl}
6466
alt="저장된 서명"
6567
className="h-full w-full object-contain"
6668
/>

src/components/exhibition-status/ExhibitionDetailSummary.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CalendarDays, Images, MapPin } from "lucide-react";
22

33
import ExhibitionStatusBadge from "@/components/exhibition-status/ExhibitionStatusBadge";
44
import type { ExhibitionDetail } from "@/types/exhibition";
5+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
56

67
interface ExhibitionDetailSummaryProps {
78
exhibition: ExhibitionDetail;
@@ -14,14 +15,16 @@ function formatDate(value: string) {
1415
}
1516

1617
export default function ExhibitionDetailSummary({ exhibition }: ExhibitionDetailSummaryProps) {
18+
const artworkThumbnailUrl = normalizeImageUrl(exhibition.artwork.thumbnailUrl);
19+
1720
return (
1821
<section className="border-border-primary border-b px-5 py-5">
1922
<div className="bg-bg-primary-darker text-text-disabled flex aspect-4/3 w-full items-center justify-center overflow-hidden rounded-lg">
20-
{exhibition.artwork.thumbnailUrl ? (
23+
{artworkThumbnailUrl ? (
2124
<div
2225
aria-hidden="true"
2326
className="h-full w-full bg-cover bg-center"
24-
style={{ backgroundImage: `url(${exhibition.artwork.thumbnailUrl})` }}
27+
style={{ backgroundImage: `url(${artworkThumbnailUrl})` }}
2528
/>
2629
) : (
2730
<Images size={34} />

src/components/exhibition-status/ExhibitionStatusCard.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CalendarDays, ChevronRight, FileText, Images, MapPin } from "lucide-rea
22

33
import ExhibitionStatusBadge from "@/components/exhibition-status/ExhibitionStatusBadge";
44
import type { ExhibitionListItem } from "@/types/exhibition";
5+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
56

67
interface ExhibitionStatusCardProps {
78
exhibition: ExhibitionListItem;
@@ -19,18 +20,20 @@ function formatPeriod(startDate: string, endDate: string) {
1920
}
2021

2122
export default function ExhibitionStatusCard({ exhibition, onClick }: ExhibitionStatusCardProps) {
23+
const thumbnailUrl = normalizeImageUrl(exhibition.thumbnailUrl);
24+
2225
return (
2326
<button
2427
type="button"
2528
onClick={onClick}
2629
className="border-border-primary bg-bg-primary active:bg-bg-primary-darker flex w-full gap-4 rounded-lg border p-4 text-left transition-colors"
2730
>
2831
<div className="bg-bg-primary-darker text-text-disabled flex size-22 shrink-0 items-center justify-center overflow-hidden rounded-lg">
29-
{exhibition.thumbnailUrl ? (
32+
{thumbnailUrl ? (
3033
<div
3134
aria-hidden="true"
3235
className="h-full w-full bg-cover bg-center"
33-
style={{ backgroundImage: `url(${exhibition.thumbnailUrl})` }}
36+
style={{ backgroundImage: `url(${thumbnailUrl})` }}
3437
/>
3538
) : (
3639
<Images size={26} />

src/components/home/ContentCard.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Images } from "lucide-react";
22
import Link from "next/link";
3+
import { normalizeImageUrl } from "@/utils/normalizeImageUrl";
34

45
interface ContentCardProps {
56
title: string;
@@ -10,12 +11,14 @@ interface ContentCardProps {
1011
}
1112

1213
export default function ContentCard({ title, imageUrl, author, type, href }: ContentCardProps) {
14+
const displayImageUrl = normalizeImageUrl(imageUrl);
15+
1316
const content = (
1417
<div className="w-42 shrink-0">
1518
{/* 썸네일*/}
1619
<div className="bg-bg-primary-darker aspect-4/3 overflow-hidden rounded-lg">
17-
{imageUrl ? (
18-
<img src={imageUrl} alt={title} className="h-full w-full object-cover" />
20+
{displayImageUrl ? (
21+
<img src={displayImageUrl} alt={title} className="h-full w-full object-cover" />
1922
) : (
2023
<div className="text-text-disabled flex h-full w-full items-center justify-center">
2124
<Images size={28} />

0 commit comments

Comments
 (0)