From ac13021f7428c4d3e91fea679724583a4eb0b271 Mon Sep 17 00:00:00 2001 From: Soohyun Myung <80390638+Soohyuniii@users.noreply.github.com> Date: Mon, 9 Jun 2025 09:19:21 +0900 Subject: [PATCH 01/19] Update README.md --- README.md | 113 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 74872fd..045c206 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,63 @@ -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: - -- Configure the top-level `parserOptions` property like this: - -```js -export default tseslint.config({ - languageOptions: { - // other options... - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - }, -}) -``` - -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` -- Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: - -```js -// eslint.config.js -import react from 'eslint-plugin-react' - -export default tseslint.config({ - // Set the react version - settings: { react: { version: '18.3' } }, - plugins: { - // Add the react plugin - react, - }, - rules: { - // other rules... - // Enable its recommended rules - ...react.configs.recommended.rules, - ...react.configs['jsx-runtime'].rules, - }, -}) -``` +# MBTips - 성격 유형 기반 팁 공유 플랫폼 + +🌐 [https://mbtips.kr](https://mbtips.kr) + +MBTips는 MBTI 성격 유형에 따라 다양한 상황별 팁을 확인하고, 다른 유형과 소통할 수 있는 웹 서비스입니다. +사용자는 상대의 성격 유형을 유추해보거나, 유형별 상황 대응법을 확인하고, 궁금한 MBTI와 실시간 채팅을 통해 대화를 나눌 수 있습니다. + +> ⏱️ **개발 기간**: 2025년 2월 ~ 2025년 5월 (약 4개월) +> 🎨 **프론트엔드 담당**: UI/UX 설계, 컴포넌트 구현, 상태 관리, 라우팅 및 API 연동 + +--- + +## 🧩 주요 기능 + +- 🔍 **상대 MBTI 추측 검사하기** + 질문에 답하면서 상대방의 MBTI를 유추해보는 인터랙티브 검사 + +- 💡 **MBTI별 상황별 팁 보기** + 유형별로 상황(연애 등)에 따른 대화 팁과 조언 제공 + +- 💬 **MBTI별 채팅 기능** + 내가 궁금한 MBTI 유형과 실시간 채팅으로 대화 체험 + +--- + +## 🚀 기술 스택 + +### 프론트엔드 + +| 기술 | 설명 | +|------|------| +| **React 18** | SPA 프레임워크 | +| **Vite 6** | 빠른 번들링 및 개발 서버 | +| **TypeScript** | 정적 타입 지원 | +| **Zustand** | 상태 관리 라이브러리 | +| **React Router DOM v7** | 클라이언트 사이드 라우팅 | +| **Tailwind CSS 4** | 유틸리티 기반 CSS 프레임워크 | +| **Axios** | HTTP 클라이언트 | +| **React GA4** | 구글 애널리틱스 연동 | +| **ESLint + Prettier** | 코드 스타일 자동화 + +--- + +## 📁 프로젝트 구조 + +```bash +src/ +├── api/ # API 요청 함수 모음 +├── components/ # 재사용 가능한 UI 컴포넌트 +├── constants/ # 상수 정의 (ex. MBTI 목록, 메시지 등) +├── hooks/ # 커스텀 React Hooks +├── libs/ # 외부 라이브러리 래퍼 (예: GA 등) +├── mock/ # 목업 데이터 및 정적 페이지용 데이터 +├── pages/ # 라우팅되는 주요 페이지 컴포넌트 +├── store/ # Zustand를 활용한 상태 관리 +├── types/ # 전역 TypeScript 타입 정의 +├── utils/ # 공통 유틸리티 함수 +├── App.tsx # 루트 컴포넌트 +├── main.tsx # 앱 진입점 +├── index.css # 글로벌 스타일 +├── global.d.ts # 글로벌 타입 선언 +├── vite-env.d.ts # Vite 환경 타입 선언 +└── ... From 7089412760bd81decdee0693a654881f5b073949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=95=9C=EC=9E=88=EB=8A=94-=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=EB=84=A4=EC=9E=84?= Date: Tue, 22 Jul 2025 10:08:16 +0900 Subject: [PATCH 02/19] =?UTF-8?q?feat:=20=EC=98=A4=ED=94=88=EC=B1=84?= =?UTF-8?q?=ED=8C=85(=EC=A3=BC=EC=A0=9C=EB=B3=84=EC=B1=84=ED=8C=85)=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=ED=99=88=20=EB=B0=8F=20=ED=8F=BC=20UI=20?= =?UTF-8?q?=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .nvmrc | 1 + src/components/Profile.tsx | 126 ++++++--- src/components/ProfileContainer.tsx | 1 + src/components/SubTitle.tsx | 10 +- src/components/TopicProfileContainer.tsx | 29 +++ src/pages/Home.tsx | 14 +- src/pages/SelectInfo.tsx | 319 ++++++++++++++--------- 7 files changed, 340 insertions(+), 160 deletions(-) create mode 100644 .nvmrc create mode 100644 src/components/TopicProfileContainer.tsx diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..4f831d5 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.20.7 \ No newline at end of file diff --git a/src/components/Profile.tsx b/src/components/Profile.tsx index 19bae99..af7b06b 100644 --- a/src/components/Profile.tsx +++ b/src/components/Profile.tsx @@ -5,14 +5,27 @@ import { VirtualFriend } from "@/types/virtualFreind"; import trackClickEvent from "@/utils/trackClickEvent"; interface ProfileProps { - info: VirtualFriend; - deleteIndex: number; - setVirtualFriendList: React.Dispatch>; + mode: "friend" | "topic"; + info?: VirtualFriend; + deleteIndex?: number; + setVirtualFriendList?: React.Dispatch>; + topicData?: { + chatTitle: string; + description: string; + }; } -const Profile = ({ info, deleteIndex, setVirtualFriendList }: ProfileProps) => { +const Profile = ({ + mode, + info, + deleteIndex, + setVirtualFriendList, + topicData +}: ProfileProps) => { const navigate = useNavigate(); const handleDelete = async () => { + if (!info || !setVirtualFriendList || deleteIndex === undefined) return; + trackClickEvent("홈", "친구 - 삭제"); const res = await authInstance.delete( `/api/virtual-friend/${info.virtualFriendId}` @@ -25,49 +38,104 @@ const Profile = ({ info, deleteIndex, setVirtualFriendList }: ProfileProps) => { }; const handleNavigate = () => { - trackClickEvent("홈", "친구 - 바로 대화하기"); - navigate("/chat", { - state: { - mode: "virtualFriend", - mbti: info.mbti, - id: info.virtualFriendId, - name: info.virtualFriendName - } - }); + if (mode === "friend" && info) { + trackClickEvent("홈", "친구 - 바로 대화하기"); + navigate("/chat", { + state: { + mode: "virtualFriend", + mbti: info.mbti, + id: info.virtualFriendId, + name: info.virtualFriendName + } + }); + } else if (mode === "topic" && topicData) { + trackClickEvent("홈", "주제별 대화방"); //FIXME: 기획 내용 정해지면 수정 + navigate("/select-info", { + state: { + type: "topicChat", + chatTitle: topicData.chatTitle, + description: topicData.description + } + }); + } + }; + + const getImageSrc = () => { + if (mode === "friend" && info) { + return `/public/image/${info.mbti}_profile.png`; + } else if (mode === "topic" && topicData) { + //FIXME: 디자인 정해지면 수정 + return "/icon/starbubble.svg"; + } + return ""; + }; + + const getTitle = () => { + if (mode === "friend" && info) { + return info.virtualFriendName; + } else if (mode === "topic" && topicData) { + return topicData.chatTitle; + } + return ""; + }; + + const getSubtitle = () => { + if (mode === "friend" && info) { + return info.mbti; + } else if (mode === "topic" && topicData) { + return ""; + } + return ""; + }; + + const getDescription = () => { + if (mode === "friend" && info) { + return `${info.virtualFriendAge} · ${info.virtualFriendSex} · ${info.virtualFriendRelationship}`; + } else if (mode === "topic" && topicData) { + return topicData.description; + } + return ""; + }; + + const getButtonText = () => { + return mode === "friend" ? "바로 대화하기" : "오픈채팅 입장하기"; }; return (
- + {mode === "friend" && ( + + )} Profile

- {info.virtualFriendName} - {info.mbti} + {getTitle()} + {getSubtitle() && ( + {getSubtitle()} + )}

-

- {info.virtualFriendAge} · {info.virtualFriendSex} ·{" "} - {info.virtualFriendRelationship} +

+ {getDescription()}

); diff --git a/src/components/ProfileContainer.tsx b/src/components/ProfileContainer.tsx index dd98c7d..69d02f4 100644 --- a/src/components/ProfileContainer.tsx +++ b/src/components/ProfileContainer.tsx @@ -15,6 +15,7 @@ const ProfileContainer = ({ {list.map((el, index) => ( { +const SubTitle = ({ + mode +}: { + mode: "빠른대화" | "친구목록" | "주제별대화방"; +}) => { const [needLoginModalIsOpen, setNeedLoginModalIsOpen] = useState(false); const { isLoggedIn } = useAuthStore(); const navigate = useNavigate(); @@ -17,6 +21,10 @@ const SubTitle = ({ mode }: { mode: "빠른대화" | "친구목록" }) => { 친구목록: { title: "친구 목록", description: "친구 정보와 대화 내용을 저장해요." + }, + 주제별대화방: { + title: "주제별 대화방", + description: "관심 있는 주제로 대화해보세요" } }; diff --git a/src/components/TopicProfileContainer.tsx b/src/components/TopicProfileContainer.tsx new file mode 100644 index 0000000..4085b79 --- /dev/null +++ b/src/components/TopicProfileContainer.tsx @@ -0,0 +1,29 @@ +import Profile from "@/components/Profile"; + +type TopicData = { + chatTitle: string; + description: string; +}; + +const topicData: TopicData[] = [ + { + chatTitle: "T의 대화", + description: "mbti t인사람들 모임" + }, + { + chatTitle: "F의 대화", + description: "mbti f인사람들 모임" + } +]; + +const TopicProfileContainer = () => { + return ( +
+ {topicData.map((topic, index) => ( + + ))} +
+ ); +}; + +export default TopicProfileContainer; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index d3499a0..5ff0cdd 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -10,6 +10,7 @@ import ChatStartButton from "@/components/button/ChatStartButton"; import Header from "@/components/header/Header"; import useAuthStore from "@/store/useAuthStore"; import ProfileContainer from "@/components/ProfileContainer"; +import TopicProfileContainer from "@/components/TopicProfileContainer"; const Home = () => { const navigate = useNavigate(); @@ -40,6 +41,7 @@ const Home = () => {
+
@@ -48,11 +50,21 @@ const Home = () => {
+ +
+
+ +
+
+ +
+
+
-
+
{isLoggedIn && virtualFreindList.length > 0 ? ( { const navigate = useNavigate(); const location = useLocation(); - const { type, mbti: testResultMBTI } = location.state; // type: fastFriend, virtualFriend 두 종류 존재 - const isNameRequired = type === "virtualFriend"; - const headerTitle = - type === "fastFriend" ? "상대방 정보선택" : "친구 저장하기"; - const selectInfoTitle = - type === "fastFriend" + const { type, mbti: testResultMBTI, chatTitle, description } = location.state; // type: fastFriend, virtualFriend, topicChat + const isFastFriend = type === "fastFriend"; + const isVirtualFriend = type === "virtualFriend"; + const isTopicChat = type === "topicChat"; + const isNameRequired = isVirtualFriend; + + const headerTitle = isTopicChat + ? "내 정보입력" + : isFastFriend + ? "상대방 정보선택" + : "친구 저장하기"; + + const selectInfoTitle = isTopicChat + ? `오픈채팅에서 사용할\n닉네임과 MBTI를 입력해 주세요` + : isFastFriend ? `상대방의 MBTI를 선택하면\n대화를 시뮬레이션 해볼 수 있어요` : `친구의 MBTI를\n선택해주세요`; @@ -56,8 +65,7 @@ const SelectInfo = () => { ? testResultMBTI : undefined; - const confirmButtonText = - type === "fastFriend" ? "대화 시작하기" : "친구 저장하기"; + const confirmButtonText = isVirtualFriend ? "친구 저장하기" : "대화 시작하기"; const [selectedMBTI, setSelectedMBTI] = useState<{ [key: string]: string | null; @@ -159,6 +167,30 @@ const SelectInfo = () => { const isMBTIComplete = Object.values(selectedMBTI).every( (val) => val !== null ); + + // topicChat일 때는 이름만 필수 + if (isTopicChat) { + if (!name) { + return showToast("이름을 입력해주세요"); + } + // topicChat은 바로 채팅으로 이동 + trackEvent("Click", { + page: "내 정보입력", + element: "대화 시작하기" + }); + navigate("/chat", { + state: { + mbti: "ENFP", // 기본 MBTI 또는 선택된 MBTI + mode: "fastFriend", + id: Date.now().toString(), + name, + chatTitle, + description + } + }); + return; + } + // 선택한 MBTI값이 하나라도 부재할 경우 if (!isMBTIComplete) { return showToast("MBTI를 선택해주세요"); @@ -176,23 +208,21 @@ const SelectInfo = () => { interests: interest }; - const selectedData = - type === "virtualFriend" - ? { - ...commonData, - friendName: name, - age: mapAgeToNumber(age), - relationship - } - : { - ...commonData, - fastFriendName: name, - fastFriendAge: mapAgeToNumber(age), - fastFriendRelationship: relationship - }; - - const apiUrl = - type === "virtualFriend" ? "api/virtual-friend" : "api/fast-friend"; + const selectedData = isVirtualFriend + ? { + ...commonData, + friendName: name, + age: mapAgeToNumber(age), + relationship + } + : { + ...commonData, + fastFriendName: name, + fastFriendAge: mapAgeToNumber(age), + fastFriendRelationship: relationship + }; + + const apiUrl = isVirtualFriend ? "api/virtual-friend" : "api/fast-friend"; try { const response = await authInstance.post( @@ -201,13 +231,13 @@ const SelectInfo = () => { ); const responseData = response.data.data; - if (type === "virtualFriend" && isVirtualFriendResponse(responseData)) { + if (isVirtualFriend && isVirtualFriendResponse(responseData)) { trackEvent("Click", { page: "친구 저장", element: "친구 저장하기" }); navigate("/"); - } else if (type === "fastFriend" && typeof responseData === "number") { + } else if (isFastFriend && typeof responseData === "number") { trackEvent("Click", { page: "빠른 대화 설정", element: "대화 시작하기" @@ -227,7 +257,7 @@ const SelectInfo = () => { }; return ( -
+
@@ -252,123 +282,154 @@ const SelectInfo = () => {
+ {/* 구분선 */}
-
-
-

- 정보 추가 입력 -

+ {!isTopicChat && ( +
+
+

+ 정보 추가 입력 +

- {/* 이름 입력 */} -
- - -
+ {/* 이름 입력 */} +
+ + +
- {/* 나이 선택 */} -
-

- 나이 -

-
- {ageOptions.map((option) => ( - handleButtonClick(option, setAge, age)} - > - {option} - - ))} + {/* 나이 선택 */} +
+

+ 나이 +

+
+ {ageOptions.map((option) => ( + handleButtonClick(option, setAge, age)} + > + {option} + + ))} +
-
- {/* 성별 선택 */} -
-

- 성별 -

-
- {genderOptions.map((option) => ( - handleButtonClick(option, setGender, gender)} - > - {option} - - ))} + {/* 성별 선택 */} +
+

+ 성별 +

+
+ {genderOptions.map((option) => ( + handleButtonClick(option, setGender, gender)} + > + {option} + + ))} +
-
- {/* 관계 선택 */} -
-

- 상대방과 나의 관계 -

-
- {relationshipOptions.map((option) => ( - - handleButtonClick(option, setRelationship, relationship) - } - > - {option} - - ))} + {/* 관계 선택 */} +
+

+ 상대방과 나의 관계 +

+
+ {relationshipOptions.map((option) => ( + + handleButtonClick(option, setRelationship, relationship) + } + > + {option} + + ))} +
-
- {/* 관심사 선택 */} -
-

- 관심사 -

-
- {interestOptions.map((option) => ( - handleInterestSelect(option)} - > - {option} - - ))} + {/* 관심사 선택 */} +
+

+ 관심사 +

+
+ {interestOptions.map((option) => ( + handleInterestSelect(option)} + > + {option} + + ))} +
+
+
+
+ )} + + {/* topicChat일 때만 이름 입력 필드 표시 */} + {isTopicChat && ( +
+
+ {/* 이름 입력 */} +
+ +
+ )} - {toastMessage && ( - setToastMessage(null)} - /> - )} + {toastMessage && ( + setToastMessage(null)} + /> + )} - {/* 대화 시작 버튼 */} + {/* 대화 시작 버튼 */} +
- )} - - {toastMessage && ( - setToastMessage(null)} - /> - )} - - {/* 대화 시작 버튼 */} -
-
-
+ ); }; From fc83946cb4de6f64d3fdad3645ed0c8162b5f23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=95=9C=EC=9E=88=EB=8A=94-=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=EB=84=A4=EC=9E=84?= Date: Tue, 29 Jul 2025 08:43:55 +0900 Subject: [PATCH 04/19] =?UTF-8?q?feat:=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/image/content_banner_1_lg.svg | 477 +++++++++++++++++++++++++++ public/image/content_banner_1_md.svg | 477 +++++++++++++++++++++++++++ public/image/content_banner_2_lg.svg | 131 ++++++++ public/image/content_banner_2_md.svg | 131 ++++++++ src/components/Banner.tsx | 16 +- src/constants/CONTENT.ts | 338 +++++++++++-------- 6 files changed, 1422 insertions(+), 148 deletions(-) create mode 100644 public/image/content_banner_1_lg.svg create mode 100644 public/image/content_banner_1_md.svg create mode 100644 public/image/content_banner_2_lg.svg create mode 100644 public/image/content_banner_2_md.svg diff --git a/public/image/content_banner_1_lg.svg b/public/image/content_banner_1_lg.svg new file mode 100644 index 0000000..66a98de --- /dev/null +++ b/public/image/content_banner_1_lg.svg @@ -0,0 +1,477 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/public/image/content_banner_1_md.svg b/public/image/content_banner_1_md.svg new file mode 100644 index 0000000..2d85150 --- /dev/null +++ b/public/image/content_banner_1_md.svg @@ -0,0 +1,477 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/public/image/content_banner_2_lg.svg b/public/image/content_banner_2_lg.svg new file mode 100644 index 0000000..b782942 --- /dev/null +++ b/public/image/content_banner_2_lg.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/image/content_banner_2_md.svg b/public/image/content_banner_2_md.svg new file mode 100644 index 0000000..2c02c90 --- /dev/null +++ b/public/image/content_banner_2_md.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index e59ed0a..248c869 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -19,16 +19,16 @@ const bannerImages: BannerImage[] = [ description: "내가 궁금한 그 사람의 MBTI는?" }, { - sm: "/image/home_banner2_sm.png", - md: "/image/home_banner2_md.png", - lg: "/image/home_banner2_lg.png", - description: "썸탈 때 대화주제 추천 MBTI별 대백과" + sm: "/image/content_banner_1_md.svg", + md: "/image/content_banner_1_md.svg", + lg: "/image/content_banner_1_lg.svg", + description: "너와 결혼까지 생각했어.. 최애의 결혼생활 망상하기(mbti별)" }, { - sm: "/image/home_banner3_sm.png", - md: "/image/home_banner3_md.png", - lg: "/image/home_banner3_lg.png", - description: "MBTI별 피해야 할 대화스타일 및 주제" + sm: "/image/content_banner_2_md.svg", + md: "/image/content_banner_2_md.svg", + lg: "/image/content_banner_2_lg.svg", + description: "버블로 어떤 칭찬을 해줘야 좋아할까? (mbti별)" } ]; diff --git a/src/constants/CONTENT.ts b/src/constants/CONTENT.ts index 394b8a0..29813b4 100644 --- a/src/constants/CONTENT.ts +++ b/src/constants/CONTENT.ts @@ -1,152 +1,210 @@ export const CONTENT_DATA = [ { id: 1, - title: "썸탈 때 대화주제 추천 MBTI별 대백과", + title: "너와 결혼까지 생각했어.. 최애의 결혼생활 망상하기(mbti별)", image: { - sm: "/image/home_banner2_sm.png", - md: "/image/home_banner2_md.png", - lg: "/image/home_banner2_lg.png" + sm: "/image/content_banner_1_md.svg", + md: "/image/content_banner_1_md.svg", + lg: "/image/content_banner_1_lg.svg" }, - subTitle: "썸탈 때 대화주제 추천(MBTI별)", - content: `썸 탈 때 이런 주제를 활용해보세요! MBTI 별로 추천드려요. 조금 더 자세한 대화주제 추천을 원한다면? 대화를 시작해보세요~ - - ISTJ (청렴한 관리자): - - "이번 주말에 뭐 할 계획이야? 나는 친구들과 등산 가기로 했어." - - 설명: 구체적인 계획에 대해 이야기하며 서로의 일정을 공유하는 것을 좋아해요! - - ISFJ (세심한 보호자): - - "가족이랑 휴가는 보통 어떻게 보내?" - - 설명: 가족과의 관계나 전통적인 활동에 대해 이야기하는 것을 선호해요! - - INFJ (통찰력 있는 상담자): - - "최근에 읽은 책 중에 감명 깊었던 게 있어? 나는 '인간의 조건'이라는 책을 읽었는데, 정말 많은 생각을 하게 해." - - 설명: 깊이 있는 주제에 대해 이야기하며 서로의 감정을 나누는 것을 좋아해요! - - INTJ (전략가): - - "5년 후에 어떤 모습이 되고 싶어?" - - 설명: 미래에 대한 비전이나 목표에 대해 이야기하는 것을 선호해요! - - ISTP (유연한 문제 해결사): - - "혹시 취미가 있어? 나는 드론 촬영에 빠져 있어." - - 설명: 취미나 액티비티에 대한 이야기를 나누며 실용적인 대화를 좋아해요! - - ISFP (자유로운 예술가): - - "혹시 전시회 좋아해? 나는 미술 전시회에 갔었는데, 정말 감동적이었어." - - 설명: 예술적 경험이나 감정에 대해 이야기하는 것을 선호해요! - - INFP (이상적인 중재자): - - "이 다음에 하고 싶은 일 이써? 나는 언젠가 나만의 책을 쓰고 싶어." - - 설명: 개인의 가치관이나 꿈에 대해 이야기하며 감정적인 연결을 중요시해요! - - INTP (논리적인 사색가): - - "양자역학에 대해 들어봤어?" - - 설명: 새로운 아이디어나 이론에 대한 논의, 호기심을 자극하는 주제를 좋아해요! - - ESTP (모험적인 활동가): - - "혹시 스카이다이빙 해봤어? 정말 짜릿했어!" - - 설명: 새로운 경험이나 모험에 대한 이야기를 나누는 것을 선호해요! - - ESFP (사교적인 연예인): - - "이번 주말에 ㅇㅇ컨퍼런스 가는 거 어때? 재미있을 것 같아!" - - 설명: 즐거운 경험이나 사회적 활동에 대한 이야기를 나누는 것을 좋아해요! - - ENFP (열정적인 활동가): - - "너는 어떤 가능성을 탐색하고 있어? 나는 새로운 프로젝트를 시작할까 고민 중이야." - - 설명: 다양한 가능성이나 창의적인 아이디어에 대한 이야기를 나누는 것을 선호해요! - - ENTP (발명가): - - "너는 AI의 미래에 대해 어떻게 생각해?" - - 설명: 논쟁적인 주제나 새로운 아이디어에 대한 토론을 즐겨요! - - ESTJ (효율적인 관리자): - - "최근에 일 할 때 가장 뿌듯했던 경험있어?" - - 설명: 목표 달성이나 성과에 대한 이야기를 나누는 것을 선호해요! - - ESFJ (사교적인 조정자): - - "친구들이랑 보통 뭐하고 놀아?" - - 설명: 사람들과의 관계나 사회적 활동에 대한 이야기를 나누는 것을 좋아해요! - - ENFJ (카리스마 있는 리더): - - "사람들과의 관계에서 가장 중요하다고 생각하는 건 뭐야? 나는 신뢰가 가장 중요하다고 생각해." - - 설명: 사람들의 감정이나 관계에 대한 이야기를 나누는 것을 선호해요! - - ENTJ (결단력 있는 지휘관): - - "회사에서 좋은 리더란 어떤 리더일까?" - - 설명: 전략적 사고나 리더십에 대한 이야기를 나누는 것을 좋아해요!` + subTitle: "너와 결혼까지 생각했어.. 최애의 결혼생활 망상하기(mbti별)", + content: + `🧠 INTJ – 계획적인 로맨스, 전략적 사랑 + INTJ 최애는 신혼집에 가전 배치 설계도까지 만들고 들어온다. + 식기세척기 모델도 이미 비교 끝. 당신이 “이거 어때?” 하면 “그거 A/S 약해.” … (이미 리뷰 1,200개 읽음). + 결혼 3년 차엔 커플 자산관리 툴을 직접 만들고, 주말마다 투자 스터디를 함께 한다. + 단점은? 서프라이즈 없음. 깜짝 이벤트 하려다 계획서 쓰는 사람이니까. + + 🧠 INTP – 같이 누워만 있어도 행복한 결혼 + INTP 최애와의 신혼은 말 그대로 무한 넷플릭스 앤 칠. + 조용히 각자 태블릿을 들고 누워 있다가, “나 이거 재밌었어” 하며 링크 공유. + 말은 없지만, 포스트잇에 “밥 차려놓음 🍳” 붙여주는 스윗함 있음. + 단점은? 쓰레기 버리기 미루다 한 달간 방치된 휴지 심들. + + 🎯 ENTJ – 워커홀릭이지만 당신에겐 무릎 꿇음 + ENTJ 최애는 회의실에선 냉철하지만, 집에선 “여보야~” 하며 무릎 베개 찜. + 데이트도 구글 캘린더 공유되어 있고, 신혼여행은 여섯 가지 옵션 비교표로 투표받음. + 연애도, 결혼도 최적화된 루트로 진행하지만, 감정에는 진심. + 단점은? 싸울 땐 당신도 토론 배틀에 말려든다는 거… + + 🎯 ENTP – 시도 때도 없이 웃기고 키스하려 듦 + ENTP 최애는 결혼 후에도 연애 초처럼 드립과 장난이 끊이질 않음. + “나 오늘 예뻐?” → “오늘도? 어제도 예뻤는데 또?” + 야식 먹자고 하다가 해외 디저트 직구 사이트까지 들어가는 모험가. + 단점은? 무계획 데이트 & 즉흥 여행, 피곤한 당신은 체력 고갈… + + 💕 INFJ – 당신만을 위한 헌신, 조용한 깊은 사랑 + INFJ 최애는 말보다 행동으로 사랑을 보여줌. + 당신이 피곤해 보이면 말 없이 따뜻한 물과 안마 준비됨. + 주말엔 같이 그림 그리거나, 한강 산책하며 인생 이야기 나눔. + 단점은? 삐져도 “아냐 괜찮아”만 하고 속앓이함. 나중에 눈치 못 채면 진짜 큰일. + + 💕 INFP – 동화 같은 결혼, 감정과몰입 max + INFP 최애는 결혼기념일마다 손편지를 씀. + 눈 내리는 날엔 감성 폭발해서 창문에 “사랑해” 쓰고 있음. + 자기 전엔 꼭 “하루 어땠어?” 라며 감정일기 나누는 의식도 있음. + 단점은? 현실 문제 생기면 살짝 도피 모드 ON… “일단 감정 추슬러야 해…” + + 💃 ENFJ – 헌신의 끝판왕, 과잉 사랑 주의보 + ENFJ 최애는 당신 생일을 1달 전부터 준비하고, 부모님 생신도 챙김. + “내 사람은 무조건 행복해야 해”라는 마인드로 요리, 청소, 감정 케어까지 ALL IN. + 아플 땐 7종 영양제 세트 챙겨주고, 출장 가면 손편지와 핫팩 몰래 짐에 넣음. + 단점은? 혼자 애쓰고 지쳐서 몰래 운다 😢 + + 💃 ENFP – 친구 같고 애인 같고, 매일이 이벤트 + ENFP 최애는 기상송 틀고 춤추며 아침 깨움. + 오늘도 깜짝 데이트, 내일은 홈캠핑, 모레는 방구석 콘서트. + “우리집은 놀이터야~”라며 포토부스도 설치함. + 단점은? 집안일 분담하다 갑자기 “우리 왜 싸우려 해ㅠ” 하며 눈물 찔끔. + + 🛠️ ISTJ – 신뢰감 100%, 가정의 기둥 + ISTJ 최애는 매달 가계부 정산하고 보험료 비교하는 짠내남. + 하지만 매년 기념일엔 같은 식당, 같은 메뉴, 같은 포옹으로 일관된 사랑을 줌. + 집안 고장나면 바로 수리 예약, 당신이 잊은 일도 대신 챙김. + 단점은? 계획 안 지켜지면 말없이 입 꾹… 공기 싸해짐. + + 🛠️ ISFJ – 따뜻한 엄마/아빠 같은 배우자 + ISFJ 최애는 당신 퇴근 시간에 맞춰 반찬 다섯 가지 차려놓음. + 몸 안 좋다 하면 병원 예약부터 약국까지 싹 다 해결. + 말은 조용한데, 행동은 사랑 가득. + 단점은? 자기 감정 숨기고 참는 편이라, “왜 말 안 해?” 해야 알 수 있음. + + ⚙️ ESTJ – 내 사람은 내가 책임진다 + ESTJ 최애는 결혼 준비도 엑셀 시트로 정리해서 공유함. + 친구, 부모님, 동네 사람까지 다 챙기는 바쁜 사람. + 당신을 위해서라면 어디든 뛰어가는 의리의 화신. + 몸 안 좋다 하면 병원 예약부터 약국까지 싹 다 해결. + 말은 조용한데, 행동은 사랑 가득. + 단점은? 의견 충돌 시 고집 좀 셈… 당신도 논리 무장 필요함. + + ⚙️ ESFJ – 결혼식마저 영화처럼 연출함 + ESFJ 최애는 웨딩플래너 뺨치게 버튼 하나하나 골라서 식장 꾸밈. + 신혼집도 셀프 인테리어로 감성폭발. + 매일 “사랑해” “고마워” “미안해” 세트로 챙기고, 친구 부부랑 홈파티도 자주 열음. + 단점은? 지나치게 “우리 부부는 완벽해야 해” 강박이 있을 수 있음. + + 🧩 ISTP – 말 없지만 행동은 다정 + ISTP 최애는 아침마다 말없이 커피 내려놓고 가는 타입. + 공구 하나로 집안 수리 다 하고, 캠핑도 혼자 다 세팅해줌. + 말로 사랑을 표현하진 않지만, 옆에 있어주고 불도 꺼줌. + 단점은? 대화는 적고, 자기만의 공간이 꼭 필요함. + + 🧩 ISFP – 감성에 취하고 사랑에 녹는다 + ISFP 최애는 주말마다 꽃 사오고, 벽엔 커플 사진 콜라주. + 음악 들으면서 요리하다가, 갑자기 당신 안고 춤추기도 함. + 감성 한가득이지만, 상처도 잘 받음. + 단점은? 갈등 생기면 그림 그리고 음악 듣느라 대화가 지연됨… + + 🎡 ESTP – 인생은 한판 게임, 결혼도 모험 + ESTP 최애는 결혼 후에도 매일이 롤러코스터. + 바이크 타고 드라이브, 스카이다이빙 커플 체험도 함. + 당신이 힘들어하면 “야! 떠나자!” 하며 당일치기 여행 예약함. + 단점은? 감정표현보다는 행동만 있는 사랑이라 서운할 수 있음. + + 🎡 ESFP – 사랑은 쇼타임! + ESFP 최애는 아침부터 “자기야~ 오늘도 예뻐!” + 하루에 몇 번씩 포옹하고 뽀뽀하고, 티 없이 밝은 에너지로 당신 기분도 업! + 결혼기념일엔 플래시몹 준비할 사람. + 단점은? 감정기복 있을 때 사랑받고 싶음이 폭발할 수도 있음.` }, { id: 2, - title: "MBTI별 피해야 할 대화스타일 및 주제", + title: "버블로 어떤 칭찬을 해줘야 좋아할까? (mbti별)", image: { - sm: "/image/home_banner3_sm.png", - md: "/image/home_banner3_md.png", - lg: "/image/home_banner3_lg.png" + sm: "/image/content_banner_2_md.svg", + md: "/image/content_banner_2_md.svg", + lg: "/image/content_banner_2_lg.svg" }, - subTitle: "MBTI 별 피해야할 대화스타일 & 주제", - content: `MBTI 별로 피해야 하는 대화 스타일, 주제를 알아보아요! - - ISTJ (청렴한 관리자): - - 비효율적인 대화, 감정적인 논의. - - 예시 : 계획이나 규칙을 무시하고 즉흥적으로 행동하는 사람과의 대화. 감정적인 불만이나 개인적인 이야기를 과도하게 나누는 것. - - ISFJ (세심한 보호자): - - 비판적이거나 공격적인 대화. - - 예시 : 자신의 가치관이나 선택을 비난하는 대화. 갈등을 일으키는 주제, 예를 들어 정치적 논쟁. - - INFJ (통찰력 있는 상담자): - - 피상적인 대화. - - 예시 : 날씨나 소소한 일상 이야기만 하는 것. 깊이 있는 감정이나 철학적 주제를 회피하는 대화. - - INTJ (전략가): - - 비논리적이거나 비효율적인 대화. - - 예시 : 감정에 치우친 주장이나 비합리적인 결정에 대한 논의. 구체적인 근거 없이 의견을 제시하는 것. - - ISTP (유연한 문제 해결사): - - 지나치게 감정적인 대화. - - 예시 : 감정적인 문제를 과도하게 분석하거나, 감정 표현을 강요하는 대화. - - ISFP (자유로운 예술가): - - 강압적인 대화나 비판. - - 예시 : 자신의 창의적인 아이디어를 부정하거나, 강제로 의견을 바꾸려는 대화. - - INFP (이상적인 중재자): - - 비인간적이거나 냉정한 대화. - - 예시 : 사람의 감정을 무시하고 논리만 강조하는 대화. 비인간적인 결정이나 정책에 대한 논의. - - INTP (논리적인 사색가): - - 비논리적이거나 감정적인 대화. - - 예시 : 감정에 기반한 주장을 하거나, 논리적 근거 없이 의견을 제시하는 것. - - ESTP (모험적인 활동가): - - 지루한 대화나 지나치게 이론적인 논의. - - 예시 : 복잡한 이론이나 과도한 분석을 요구하는 대화. 즉흥적인 행동을 비판하는 것. - - ESFP (사교적인 연예인): - - 부정적인 대화나 지나치게 심각한 주제. - - 예시 : 우울한 이야기나 비관적인 전망을 이야기하는 것. 너무 진지한 주제로 대화하는 것. - - ENFP (열정적인 활동가): - - 제한적이거나 비판적인 대화. - - 예시 : 새로운 아이디어를 억누르거나, 비판적인 태도로 접근하는 대화. - - ENTP (발명가): - - 고정관념이나 전통적인 대화. - - 예시 : 기존의 방식을 고수하고 새로운 아이디어를 받아들이지 않는 대화. - - ESTJ (효율적인 관리자): - - 비효율적인 대화나 감정적인 논의. - - 예시 : 감정적인 문제를 과도하게 다루거나, 실질적인 해결책 없이 불만을 토로하는 것. - - ESFJ (사교적인 조정자): - - 갈등을 일으키는 대화. - - 예시 : 사람들 간의 갈등을 부추기거나, 비판적인 태도로 대화하는 것. - - ENFJ (카리스마 있는 리더): - - 비협조적인 대화. - - 예시 : 팀워크를 무시하고 개인적인 이익만을 추구하는 대화. - - ENTJ (결단력 있는 지휘관): - - 비효율적이거나 비논리적인 대화. - - 예시 : 목표 달성을 방해하는 비효율적인 논의나, 감정적인 요소를 과도하게 강조하는 것.` + subTitle: "버블로 어떤 칭찬을 해줘야 좋아할까? (mbti별)", + content: + `🔥 ENTJ – "능력 + 신뢰 + 영향력" + "너 보면 그냥… 뭐든 해낼 것 같은 확신이 들어." + "사람을 리드할 줄 아는 사람이라는 거, 말 안 해도 느껴져." + → ENTJ는 능력 인정 + 전략적 사고 칭찬에 기분 좋아함. + 👉 Tip: '결과 중심' '믿고 따라갈 사람' 이런 표현에 약함. + + 🧊 INTJ – "통찰 + 존재감 + 깊이" + "넌 말 안 해도, 말보다 훨씬 많은 걸 전하는 사람 같아." + "생각의 결이 다르다. 진짜." + → INTJ는 지적 깊이 + 감정 절제 속 진심에 심쿵함. + 👉 Tip: "남들은 모르는 걸 나는 알아봤다" 식 칭찬이 효과적. + + 🧱 ISTJ – "믿음 + 꾸준함 + 디테일" + "당연하다고 느꼈던 게 알고 보니 네 덕분이었더라." + "네가 있어서 안심되는 느낌이 있어." + → ISTJ는 묵묵한 노력 + 신뢰감에 대한 칭찬에 약함. + 👉 Tip: 눈에 잘 띄지 않는 성실함을 정확히 짚어줘야 진정한 감동. + + 🎭 ENTP – "센스 + 유쾌함 + 똑똑함" + "너 말하는 방식 보면 진짜 똑똑한데 재밌어. 흔치 않아." + "가볍게 툭 던진 말인데 사람 머리에 오래 남는 타입임." + → ENTP는 재치 + 깊이를 동시에 인정받는 칭찬을 좋아함. + 👉 Tip: "웃긴데 똑똑한 사람" 프레임이 가장 효과적. + + 🧠 INTP – "아이디어 + 독특함 + 쿨함" + "너만의 사고방식이 있다는 거, 그게 진짜 멋있어." + "넌 설명 없이도 납득되는 사람." + → INTP는 존중 + 이해받는 느낌의 칭찬에 흔들림. + 👉 Tip: "이해하려고 노력하지 않아도, 그냥 그 자체로 좋다"가 효과적. + + 🧚 ENFP – "감정표현 + 긍정에너지 + 연결감" + "너랑 얘기하면, 내가 좀 더 괜찮은 사람 같아져." + "네 말 하나에 분위기가 바뀌는 거 알지?" + → ENFP는 사람에게 미치는 긍정적 영향 칭찬에 기분이 확 좋아짐. + 👉 Tip: '분위기 메이커'보다 '사람 마음을 움직이는 사람'으로 표현해주는 게 좋음. + + 🧸 INFP – "섬세함 + 감정 깊이 + 존재 그 자체" + "넌 그냥… 감정이 조용히 번지는 사람 같아." + "어떤 말보다 네 태도가 오래 기억나더라." + → INFP는 내면/감정의 진정성 칭찬에 울컥함. + 👉 Tip: "그냥 너라서 좋아"는 INFP에게 마법의 문장. + + 💃 ESFP – "감각 + 활기 + 찐매력" + "너는 그냥 보고 있으면 기분이 좋아져. 설명이 안 돼." + "감각 있는 사람이란 게 이런 거구나 싶더라." + → ESFP는 눈에 보이는 매력 + 순간의 빛 칭찬에 약함. + 👉 Tip: 무대 위나 일상 속 반짝임을 포착해서 칭찬해야 효과적. + + 🎤 ENFJ – "따뜻함 + 영향력 + 마음씀씀이" + "너 하나로 분위기가 따뜻해지는 게 느껴졌어." + "사람 마음을 알아주는 게 너의 진짜 멋인 듯." + → ENFJ는 사람 챙기는 마음 + 공감력을 인정받을 때 뿌듯해함. + 👉 Tip: "다들 너에게 기대는 이유, 나도 알아" 같은 말에 강함. + + 🌱 ISFJ – "세심함 + 조용한 헌신 + 안정감" + "티 안 내고 다 챙기는 거, 나만 아는 줄 알았는데 아니더라." + "네가 있어서 오늘 하루가 좀 더 편했어." + → ISFJ는 사소한 것을 알아주는 칭찬에 진심으로 감동함. + 👉 Tip: "말 안 해도 느껴졌다" 식의 표현이 최고. + + 👀 ISFP – "분위기 + 감성 + 감각" + "네가 있는 공간은 그냥, 편해져." + "표정 하나에도 네 분위기가 느껴져. 그게 좋더라." + → ISFP는 존재 자체 + 감각적인 디테일 칭찬에 반응함. + 👉 Tip: 과한 표현보다 '그냥 그 순간이 예뻤다' 같은 자연스러운 표현이 효과적. + + 🔮 INFJ – "깊이 + 여운 + 이해받는 느낌" + "너는 그냥 말 한 마디에도 마음이 묻어나는 사람이야." + "다들 너 좋다고 하잖아.난 왜 좋은지까지 말해줄 수 있어. 그게 진짜 좋아하는 거니까." + → INFJ는 속을 봐주는 느낌의 칭찬에 약함. + 👉 Tip: "티 안 나게 섬세하다", "네 말은 오래 남는다" 같은 표현이 효과적. + + ⚡ ESTP – "센스 + 순간 대처력 + 쿨한 자신감" + "그 상황에서 침착했던 건 너밖에 없었어. 진짜 멋있었어." + "너는 진짜, 말보다 행동이 먼저 멋있는 사람이야." + → ESTP는 실행력 + 카리스마 + 현장감 있는 칭찬에 기분 좋아함. + 👉 Tip: 즉흥적인 상황에서 보인 매력을 언급해주는 게 핵심! + + 🫶 ESFJ – "배려 + 안정감 + 함께 있는 편안함" + "네가 있어서 분위기가 정말 따뜻해졌어." + "사람들 잘 챙기는 건 알겠는데… 넌 그걸 자연스럽게 하더라. 그게 진짜 멋있는 거야." + → ESFJ는 주변 사람을 편하게 만든다는 칭찬에 감동함. + 👉 Tip: "넌 진짜 내가 편해지는 사람이야"는 최고의 칭찬. + + 🧊 ISTP – "과묵한 센스 + 쿨한 실력 + 묵직한 존재감" + "딱히 말 많이 안 해도 멋있다는 걸 너가 증명해." + "무심한 척하는데, 섬세한 면이 다 보여. 그게 더 멋있어." + → ISTP는 티 내지 않고 모든 걸 해내는 사람이라는 칭찬에 반응함. + 👉 Tip: "네가 있어서 안정된다"는 말은 짧게 해도 묵직하게 전해짐.` } ]; From 2d723901abd8085ebfcfea2a5af8856cbb3767e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=95=9C=EC=9E=88=EB=8A=94-=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=EB=84=A4=EC=9E=84?= Date: Tue, 29 Jul 2025 09:33:08 +0900 Subject: [PATCH 05/19] =?UTF-8?q?feat:=20=EC=B9=9C=EA=B5=AC=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=ED=8F=BC=20=ED=95=AD=EB=AA=A9=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/FormButton.tsx | 26 +++++--- src/pages/SelectInfo.tsx | 88 ++++++++++------------------ 2 files changed, 48 insertions(+), 66 deletions(-) diff --git a/src/components/button/FormButton.tsx b/src/components/button/FormButton.tsx index 94eafdd..1810c00 100644 --- a/src/components/button/FormButton.tsx +++ b/src/components/button/FormButton.tsx @@ -13,17 +13,26 @@ const FormButton = ({ children, onClick }: FormButtonProps) => { - const baseStyles = "flex justify-center items-center rounded-lg transition"; + const baseStyles = + "flex justify-center items-center rounded-lg transition-colors transform-none focus:transform-none active:transform-none select-none"; const sizeStyles = size === "sm" ? "min-w-[68px] w-auto h-[40px]" : "w-[70px] h-[72px]"; - const fontSize = - size === "md" - ? "text-[var(--text-2xl)]" - : selected - ? "text-[16px]" - : "text-[14px]"; + const getFontSize = () => { + if (size === "md") return "text-[var(--text-2xl)]"; + + // 긴 텍스트인 경우 작은 폰트 적용 + const isLongText = children.length > 4; + + if (selected) { + return isLongText ? "text-[13px]" : "text-[16px]"; + } else { + return isLongText ? "text-[12px]" : "text-[14px]"; + } + }; + + const fontSize = getFontSize(); const fontWeight = size === "md" ? "font-bold" : selected ? "font-bold" : "font-medium"; @@ -34,8 +43,9 @@ const FormButton = ({ return ( diff --git a/src/pages/SelectInfo.tsx b/src/pages/SelectInfo.tsx index 4634d91..8a32583 100644 --- a/src/pages/SelectInfo.tsx +++ b/src/pages/SelectInfo.tsx @@ -79,8 +79,8 @@ const SelectInfo = () => { const [name, setName] = useState(""); const [age, setAge] = useState(null); const [gender, setGender] = useState(null); - const [relationship, setRelationship] = useState(null); - const [interest, setInterest] = useState([]); + const [job, setJob] = useState(null); + const [freeSetting, setFreeSetting] = useState(""); const [toastMessage, setToastMessage] = useState(null); useEffect(() => { @@ -97,27 +97,15 @@ const SelectInfo = () => { const mbtiOptions = ["E", "N", "F", "P", "I", "S", "T", "J"]; const ageOptions = ["10대", "20대", "30대 이상"]; const genderOptions = ["여자", "남자"]; - const relationshipOptions = [ - "부모", - "자녀", - "친구", - "짝사랑", - "이별", - "연인", - "선생님", - "직장동료" - ]; - const interestOptions = [ - "연애", - "결혼", - "취미", - "사회생활", - "여행", - "운동", - "심리", - "뷰티/패션", - "음식", - "인간관계" + const jobOptions = [ + "연습생", + "아이돌", + "스포츠선수", + "배우", + "작가", + "스트리머", + "유튜버", + "프로게이머" ]; const handleMBTISelect = (option: string) => { @@ -133,18 +121,8 @@ const SelectInfo = () => { return selectedMBTI[group] === option; }; - const handleInterestSelect = (option: string) => { - if (interest.includes(option)) { - setInterest((prevInterests) => - prevInterests.filter((item) => item !== option) - ); - } else { - setInterest((prevInterests) => [...prevInterests, option]); - } - }; - - const isInterestSelected = (option: string) => { - return interest.includes(option); + const handleFreeSettingChange = (e: ChangeEvent) => { + setFreeSetting(e.target.value); }; const handleNameChange = (e: ChangeEvent) => { @@ -206,7 +184,7 @@ const SelectInfo = () => { const commonData = { gender: gender === "남자" ? "MALE" : gender === "여자" ? "FEMALE" : null, mbti, - interests: interest + freeSetting }; const selectedData = isVirtualFriend @@ -214,13 +192,13 @@ const SelectInfo = () => { ...commonData, friendName: name, age: mapAgeToNumber(age), - relationship + job } : { ...commonData, fastFriendName: name, fastFriendAge: mapAgeToNumber(age), - fastFriendRelationship: relationship + fastFriendJob: job }; const apiUrl = isVirtualFriend ? "api/virtual-friend" : "api/fast-friend"; @@ -387,20 +365,18 @@ const SelectInfo = () => {
- {/* 관계 선택 */} + {/* 직업 선택 */}

- 상대방과 나의 관계 + 직업

- {relationshipOptions.map((option) => ( + {jobOptions.map((option) => ( - handleButtonClick(option, setRelationship, relationship) - } + selected={job === option} + onClick={() => handleButtonClick(option, setJob, job)} > {option} @@ -408,22 +384,18 @@ const SelectInfo = () => {
- {/* 관심사 선택 */} + {/* 자유 설정 */}

- 관심사 + 자유 설정

-
- {interestOptions.map((option) => ( - handleInterestSelect(option)} - > - {option} - - ))} +
+