Skip to content
Merged
Show file tree
Hide file tree
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
25 changes: 19 additions & 6 deletions app/extra-info/detail/_components/ScreenExtraInfoDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ const ScreenExtraInfoDetail = () => {
(profile.socialType?.toLowerCase() as "instagram" | "kakao") || null,
);
const [contactId, setContactId] = useState(profile.socialAccountId || "");
const [favoriteSong, setFavoriteSong] = useState(profile.favoriteSong || "");
const [advantages, setAdvantages] = useState<string[]>(profile.advantages || []);
const [favoriteSong, setFavoriteSong] = useState(profile.song || "");
const [advantages, setAdvantages] = useState<string[]>(
profile.tags?.map((t) => t.tag) || [],
);

const contactOptions = [
{
Expand Down Expand Up @@ -60,8 +62,19 @@ const ScreenExtraInfoDetail = () => {
? (contactType.toUpperCase() as SocialType)
: undefined,
socialAccountId: contactType ? contactId : undefined,
advantages: advantages.length > 0 ? advantages : undefined,
favoriteSong: favoriteSong.trim() || undefined,
tags:
advantages.length > 0
? advantages.map((tag) => ({
// 이모지 제거 (정규식 사용) 및 공백 제거
tag: tag
.replace(
/[\uD800-\uDBFF][\uDC00-\uDFFF]|\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDDFF]|\uD83D[\uDE00-\uDE4F]|\uD83E[\uDD00-\uDDFF]/g,
"",
)
.trim(),
Comment on lines +69 to +74
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

이모지 제거를 위한 정규식과 로직이 app/profile-image/_components/TermsDrawer.tsx 파일에서도 중복으로 사용되고 있습니다. 코드 중복을 피하고 유지보수성을 높이기 위해 이 로직을 lib/utils.ts와 같은 공통 유틸리티 파일로 추출하는 것을 권장합니다.

또한, 더 간결하고 표준적인 유니코드 속성 이스케이프(Unicode property escapes) \p{Emoji}를 사용하면 가독성을 향상시킬 수 있습니다.

              tag: tag.replace(/\p{Emoji}/gu, "").trim(),
References
  1. Pure functions, such as validation logic, should be extracted into common utility files to improve reusability and separate concerns.

}))
: undefined,
song: favoriteSong.trim() || undefined,
});
router.push("/profile-image");
};
Expand Down Expand Up @@ -205,8 +218,8 @@ const ScreenExtraInfoDetail = () => {
updateProfile({
socialType: undefined,
socialAccountId: undefined,
advantages: undefined,
favoriteSong: undefined,
tags: undefined,
song: undefined,
});
router.push("/profile-image");
}}
Expand Down
6 changes: 3 additions & 3 deletions app/main/_components/ProfileCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { Hobby, ProfileData } from "@/lib/types/profile";

Check warning on line 3 in app/main/_components/ProfileCard.tsx

View workflow job for this annotation

GitHub Actions / lint

'Hobby' is defined but never used
import Image from "next/image";
import { Send } from "lucide-react";
import React, { useRef } from "react";
Expand Down Expand Up @@ -150,8 +150,8 @@
<div className="flex flex-col gap-1">
<span className="typo-12-600 text-[#777777]">장점</span>
<div className="flex flex-wrap gap-1">
{profile.advantages && profile.advantages.length > 0 ? (
profile.advantages.map((adv) => <Tag key={adv} text={adv} />)
{profile.tags && profile.tags.length > 0 ? (
profile.tags.map((t) => <Tag key={t.tag} text={t.tag} />)
) : (
<Tag text="없음" />
)}
Expand All @@ -162,7 +162,7 @@
<div className="flex flex-col gap-1">
<span className="typo-12-600 text-[#777777]">좋아하는 노래</span>
<span className="typo-16-600 text-color-text-black">
{profile.favoriteSong || "아직 없어요!"}
{profile.song || "아직 없어요!"}
</span>
</div>

Expand Down
7 changes: 4 additions & 3 deletions app/main/_components/ScreenMainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import BusinessInfo from "@/components/common/BusinessInfo";
import NoticeSection from "./NoticeSection";
import ProfileSlider from "./ProfileSlider";
import { ProfileData, ContactFrequency } from "@/lib/types/profile";

Check warning on line 14 in app/main/_components/ScreenMainPage.tsx

View workflow job for this annotation

GitHub Actions / lint

'ContactFrequency' is defined but never used
import ChargeRequestWaiting from "./ChargeRequestWaiting";

import { useMatchingHistory } from "@/hooks/useMatchingHistory";
Expand Down Expand Up @@ -48,12 +48,13 @@
const profileList: ProfileData[] =
historyData?.data.content.map(({ partner }) => ({
...partner,
// API에서 null로 올 수 있는 필드들만 안전하게 변환
// API에서 필드명이 변경되었거나 null로 올 수 있는 필드들만 안전하게 변환
profileImageUrl: partner.profileImageKey,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

partner.profileImageKey는 이미지 URL이 아닌 S3와 같은 스토리지의 키(key)로 보입니다. ProfileCard 컴포넌트는 profileImageUrl prop으로 전체 이미지 URL을 기대하므로, profileImageKey를 전체 URL로 변환하는 로직이 필요합니다. 예를 들어, 유틸리티 함수를 사용하여 https://<your-cdn-domain>/${partner.profileImageKey}와 같은 형태로 만들어야 합니다. 현재 코드는 깨진 이미지를 표시할 가능성이 높습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Image key passed to image 🐞 Bug ✓ Correctness

ScreenMainPage assigns partner.profileImageKey into profileImageUrl, but ProfileCard passes
profileImageUrl directly to next/image as src. Since profileImageKey is an object key (e.g.
"default_bear" / S3 key) rather than a URL/path starting with "/" or "http", next/image will error
or render broken images on the main page.
Agent Prompt
### Issue description
The main page maps `partner.profileImageKey` into `ProfileData.profileImageUrl` and then uses it directly as the `src` for `next/image`. A key like `default_bear` or an S3 object key is not a valid image URL/path for `next/image`, leading to runtime errors/broken images.

### Issue Context
- API and upload flow both treat the value as an *image key*.
- UI rendering expects a valid URL/path.

### Fix Focus Areas
- app/main/_components/ScreenMainPage.tsx[48-58]
- app/main/_components/ProfileCard.tsx[36-48]
- hooks/useProfileSignUp.ts[6-57]
- app/profile-image/_components/TermsDrawer.tsx[103-123]
- lib/types/profile.ts[34-60]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

intro: partner.intro ?? undefined,
socialType: partner.socialType ?? undefined,
socialAccountId: partner.socialAccountId ?? "",
advantages: partner.advantages ?? undefined,
favoriteSong: partner.favoriteSong ?? undefined,
tags: partner.tags ?? undefined,
song: partner.song ?? undefined,
})) || [];

return (
Expand Down
13 changes: 8 additions & 5 deletions app/profile-image/_components/DefaultProfileDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import Button from "@/components/ui/Button";
import ProfileBottomSheet from "./ProfileBottomSheet";

export const DEFAULT_PROFILES = [
{ id: "bear", name: "곰", image: "/profile/bear.svg" },
{ id: "cat", name: "고양이", image: "/profile/cat.svg" },
{ id: "dog", name: "강아지", image: "/profile/dog.svg" },
{ id: "cat", name: "고양이", image: "/profile/cat.svg" },
{ id: "bear", name: "곰", image: "/profile/bear.svg" },
{ id: "rabbit", name: "토끼", image: "/profile/rabbit.svg" },
{ id: "fox", name: "여우", image: "/profile/fox.svg" },
{ id: "panda", name: "판다", image: "/profile/panda.svg" },
{ id: "hamster", name: "햄스터", image: "/profile/hamster.svg" },
{ id: "deer", name: "사슴", image: "/profile/deer.svg" },
{ id: "penguin", name: "펭귄", image: "/profile/penguin.svg" },
{ id: "dinosaur", name: "공룡", image: "/profile/dinosaur.svg" },
{ id: "otter", name: "수달", image: "/profile/otter.svg" },
{ id: "wolf", name: "늑대", image: "/profile/wolf.svg" },
{ id: "snake", name: "뱀", image: "/profile/snake.svg" },
{ id: "horse", name: "말", image: "/profile/horse.svg" },
{ id: "frog", name: "개구리", image: "/profile/frog.svg" },
];

interface DefaultProfileDrawerProps {
Expand Down
23 changes: 13 additions & 10 deletions app/profile-image/_components/TermsDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,27 +119,30 @@ const TermsDrawer = ({ children }: TermsDrawerProps) => {
birthDate: profile.birthDate || "",
mbti: profile.mbti || "ISTJ",
intro: profile.intro || "",
profileImageUrl: finalImageUrl || profile.profileImageUrl || "",
socialType: profile.socialType || null,
socialAccountId: profile.socialAccountId || null,
university: profile.university || "가톨릭대학교",
profileImageKey: finalImageUrl || "default",
socialType:
profile.socialType && profile.socialAccountId
? profile.socialType
: null,
socialAccountId:
profile.socialType && profile.socialAccountId
? profile.socialAccountId
: null,
Comment on lines +123 to +130
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

소셜 정보 유효성을 확인하는 profile.socialType && profile.socialAccountId 조건이 중복으로 사용되고 있습니다. 가독성을 높이고 실수를 줄이기 위해 이 조건을 변수로 추출하는 것을 고려해 보세요.

예시:

const hasSocialInfo = profile.socialType && profile.socialAccountId;
const submitData: ProfileSubmitData = {
  // ...
  socialType: hasSocialInfo ? profile.socialType : null,
  socialAccountId: hasSocialInfo ? profile.socialAccountId : null,
  // ...
};

university: profile.university || "",
major: profile.major || "",
contactFrequency: profile.contactFrequency || "NORMAL",
hobbies: mapHobbies(profile.hobbies || []),
intros: profile.intros || [],
advantages:
profile.advantages && profile.advantages.length > 0
? profile.advantages
: null,
favoriteSong: profile.favoriteSong || null,
tags: profile.tags && profile.tags.length > 0 ? profile.tags : null,
song: profile.song || null,
};

// 3. 백엔드로 전송
signUp(submitData, {
onSuccess: () => {
setIsOpen(false);
clearProfile();
router.push("/main"); // 가입 완료 페이지로 이동
router.push("/main"); // 가입 완료 주소로 이동
},
onError: (error) => {
console.error("Signup failed:", error);
Expand Down
6 changes: 3 additions & 3 deletions hooks/useMatchingHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ export interface MatchingPartner {
birthDate: string;
mbti: MBTI;
intro: string | null;
profileImageUrl: string;
profileImageKey: string;
socialType: SocialType | null;
socialAccountId: string | null;
university: string;
major: string;
contactFrequency: ContactFrequency;
hobbies: Hobby[];
intros: IntroItem[];
advantages: string[] | null;
favoriteSong: string | null;
tags: { tag: string }[] | null;
song: string | null;
}

export interface MatchingHistoryItem {
Expand Down
12 changes: 6 additions & 6 deletions lib/types/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,25 @@ export interface ProfileData {
contactFrequency?: ContactFrequency;
hobbies?: Hobby[] | string[]; // UI에서는 string[], 제출 시 Hobby[]
intros?: IntroItem[];
advantages?: string[];
favoriteSong?: string;
tags?: { tag: string }[];
song?: string;
}

// 백엔드 전송용 타입 (필수 필드)
// 백엔드 전송용 타입 (필수 필드 및 변경된 스펙 반영)
export interface ProfileSubmitData {
nickname: string;
gender: Gender;
birthDate: string;
mbti: MBTI;
intro: string;
profileImageUrl: string;
profileImageKey: string;
socialType: SocialType | null;
socialAccountId: string | null;
university: string;
major: string;
contactFrequency: ContactFrequency;
hobbies: Hobby[];
intros: IntroItem[];
advantages: string[] | null;
favoriteSong: string | null;
tags: { tag: string }[] | null;
song: string | null;
}
Loading