Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
852c83a
Feat: 성향 플로우 공통 스키마·컴포넌트 추가 (MM-188)
hin6150 Mar 15, 2026
89ebf83
Feat: 성향 입력·결과 미리보기 신규 페이지 추가 (MM-188)
hin6150 Mar 15, 2026
36d3508
Feat: 기존 페이지 성향 플로우 연동 (MM-188)
hin6150 Mar 15, 2026
81c7a19
Feat: 결과 화면 CTA from 분기 및 카피 수정 (MM-188)
hin6150 Mar 15, 2026
a58ec24
Feat: 마이페이지 성향 칩 MBTI+애착유형 합산 표시 (MM-188)
hin6150 Mar 15, 2026
3013bc4
Feat: 홈 화면 성향 배너·카드 툴팁 추가 및 분기 로직 수정 (MM-188)
hin6150 Mar 15, 2026
a0b21a3
Chore: gitignore .omc 디렉토리 추가 및 모바일 URL 주석 수정 (MM-188)
hin6150 Mar 15, 2026
25088f0
Feat: 성향 플로우 진입 전 1.5초 로딩 화면 추가 (MM-188)
hin6150 Mar 15, 2026
ac765e9
Feat: key-message-banner 공통 컴포넌트 추가 및 홈 배너 교체 (MM-188)
hin6150 Mar 15, 2026
57a3620
Feat: my-attachment-select UX 개선 (MM-188)
hin6150 Mar 15, 2026
f94cf39
Feat: selectable-button 공통 컴포넌트 추가 및 재사용 적용 (MM-188)
hin6150 Mar 15, 2026
3acec6c
Feat: fixed-bottom 공통 컴포넌트 추가 및 결과 미리보기 페이지 적용 (MM-188)
hin6150 Mar 15, 2026
385cd40
Feat: personality-flow 네비게이션 헬퍼 추가 및 mbti 페이지 적용 (MM-188)
hin6150 Mar 15, 2026
aac97df
Refactor: use-member-update-mutation 훅 및 공통 유틸 적용으로 코드 중복 제거 (MM-188)
hin6150 Mar 15, 2026
71e5c92
Fix: attachment-test-guide 오버레이 전체화면 미적용 및 뒤로가기 시 잠깐 노출 버그 수정 (MM-188)
hin6150 Mar 15, 2026
9cbb2f7
Feat: partner-attachment-select 선택 UX 개선 (MM-188)
hin6150 Mar 15, 2026
2ef3c07
Feat: user-api-axios openapi 스펙 업데이트 (MM-188)
hin6150 Mar 19, 2026
0ae4a2b
Feat: members/partners API 연동 및 deprecated 온보딩 페이지 정리 (MM-188)
hin6150 Mar 19, 2026
56231a4
Feat: 마이페이지 진입 시 성향 플로우 완료 후 마이페이지 복귀 및 40017 에러 추적 제외 (MM-188)
hin6150 Mar 20, 2026
2568534
Refactor: 성향 플로우 라우팅 로직을 usePersonalityFlow hook으로 중앙화 (MM-188)
hin6150 Mar 20, 2026
403d717
Fix: 결과 미리보기에서 결과 상세 진입 후 닫기 시 미리보기로 복귀 (MM-188)
hin6150 Mar 20, 2026
c53e6a3
Fix: 테스트 서버 스웨거 반영 (MM-189)
LeeWxx Mar 20, 2026
2c32f33
Fix: 신규 파트너 프로필 API에 맞게 연동/온보딩 로직 정리 (MM-189)
LeeWxx Mar 20, 2026
613ba8e
Feat: 애착유형 결과 리뉴얼 (MM-189)
LeeWxx Mar 20, 2026
a4d9ee6
Chore: openapi generator 메타데이터 (MM-189)
LeeWxx Mar 20, 2026
a4f59ad
Merge branch 'fix/MM-189/love-type-result' into feat/MM-188 (MM-188)
hin6150 Mar 20, 2026
487b366
Fix: fix/MM-189 병합 충돌 해소 및 사이드 이펙트 수정 (MM-188)
hin6150 Mar 21, 2026
c7953d2
Feat: 성향 플로우 애착유형 결과지 진입/복귀 로직 수정 (MM-188)
hin6150 Mar 21, 2026
1f085ab
Fix: 마이페이지에서 상대 성향 완료 시 마이페이지 프로필로 이동 (MM-188)
hin6150 Mar 21, 2026
c2ca742
Feat: 성향 플로우 전반 리팩토링 - chat-entry → full-flow 전환 및 관련 수정 (MM-188)
hin6150 Mar 21, 2026
382090a
Refactor: 코드 리뷰 반영 - 타입 안전성 개선 및 mutation 중복 제거 (MM-188)
hin6150 Mar 21, 2026
29d4b92
Fix: 애착유형 검사 진입 후 뒤로가기 시 이전 페이지로 복귀 (MM-188)
hin6150 Mar 21, 2026
b2d8537
Fix: 내 성향 플로우에서 뒤로가기 시 MBTI로 복귀 (MM-188)
hin6150 Mar 21, 2026
598e38f
Fix: 내 성향 완료 후 마이페이지 복귀 로직 수정 (MM-188)
hin6150 Mar 21, 2026
43cdff8
Feat: 마이페이지 내 성향 변경 완료 시 토스트 메시지 추가 (MM-188)
hin6150 Mar 21, 2026
1fdd365
Feat: 상대 성향 변경 토스트 추가 및 채팅 안내 문구 수정 (MM-188)
hin6150 Mar 21, 2026
824ed97
Feat: 홈 연애 성향 카드 배지에 MBTI + 애착유형 표기 (MM-188)
hin6150 Mar 21, 2026
d88f367
Fix: 채팅 시작 버튼 - 미입력 항목에 따라 적절한 플로우로 진입 (MM-188)
hin6150 Mar 21, 2026
a669518
Refactor: 미완성 성향 플로우 결정 로직 순수함수로 중앙화 (MM-188)
hin6150 Mar 21, 2026
690ec57
Fix: 단일 플로우 애착유형 결과 페이지 경유 후 홈 이동 및 마이페이지 히스토리 스택 정리 (MM-188)
hin6150 Mar 21, 2026
1efa88a
Fix: 마이페이지 성향 변경 플로우 히스토리 스택 정리 (MM-188)
hin6150 Mar 21, 2026
ae6ebb7
Fix: 파트너 UNKNOWN 상태를 완성된 성향으로 처리 (MM-188)
hin6150 Mar 21, 2026
a990691
Feat: 사용자 및 파트너 애착 유형 선택 페이지 개선 및 UI 요소 수정 (MM-188)
hin6150 Mar 23, 2026
11b7343
Feat: 파트너 결과 페이지 및 프로필 페이지에서 플로우 네비게이션 개선 (MM-188)
hin6150 Mar 24, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ yarn-error.log*
.DS_Store
*.pem

# OMC
.omc/

# Expo
apps/mobile/ios/
apps/mobile/android/
Expand Down
1 change: 1 addition & 0 deletions apps/mobile/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default function App() {
const [isAuthReady, setIsAuthReady] = useState(false)

const webviewUrl = process.env.EXPO_PUBLIC_LOCAL_URL
// const webviewUrl = process.env.EXPO_PUBLIC_WEB_VIEW_URL
if (!webviewUrl) throw new Error('Webview URL is not set')

useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions apps/react/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
content="width=device-width, viewport-fit=cover, initial-scale=1, maximum-scale=1, user-scalable=0"
/>
<title>React</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/toss/tossface/dist/tossface.css" />

<!-- AMPLITUDE_SCRIPT -->
</head>
Expand Down
6 changes: 4 additions & 2 deletions apps/react/src/app/attachment-test/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { DetailHeaderBar } from '@/shared/ui/header-bar'

const searchSchema = z.object({
from: z.string().optional(),
flow: z.enum(['my-personality', 'partner-personality', 'full-flow']).optional(),
chatId: z.number().optional(),
})

export const Route = createFileRoute('/attachment-test/')({
Expand All @@ -25,7 +27,7 @@ function AttachmentTestPage() {
const navigate = useNavigate()
const goBack = useGoBack()
const { setStatusColor } = useTheme()
const { from } = useSearch({ from: Route.id })
const { from, flow, chatId } = useSearch({ from: Route.id })
const { userInfo } = useAuth()
const nickname = userInfo.nickname || '사용자'

Expand All @@ -38,7 +40,7 @@ function AttachmentTestPage() {
}, [])

const handleStartTest = wrapWithTracking(BUTTON_NAMES.START_TEST, CATEGORIES.ATTACHMENT, () =>
navigate({ to: '/attachment-test/question', search: { from }, replace: true })
navigate({ to: '/attachment-test/question', search: { from, flow, chatId }, replace: true })
)

return (
Expand Down
12 changes: 7 additions & 5 deletions apps/react/src/app/attachment-test/question/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createFileRoute, useSearch } from '@tanstack/react-router'
import { createFileRoute } from '@tanstack/react-router'
import { useState } from 'react'
import z from 'zod'

Expand All @@ -11,6 +11,7 @@ import {
QUESTION_CONFIG,
} from '@/features/attachment'
import { useAuth } from '@/features/auth'
import { usePersonalityFlow } from '@/features/profile/lib/personality-flow'
import { wrapWithTracking } from '@/shared/analytics'
import { BUTTON_NAMES, CATEGORIES } from '@/shared/analytics/constants'
import { Screen } from '@/shared/layout/screen'
Expand All @@ -19,6 +20,8 @@ import { DetailHeaderBar } from '@/shared/ui/header-bar'

const searchSchema = z.object({
from: z.string().optional(),
flow: z.enum(['my-personality', 'partner-personality', 'full-flow']).optional(),
chatId: z.number().optional(),
})

export const Route = createFileRoute('/attachment-test/question/')({
Expand All @@ -28,8 +31,9 @@ export const Route = createFileRoute('/attachment-test/question/')({

function AttachmentTestQuestionPage() {
const [isGuideOpen, setIsGuideOpen] = useState(true)
const { from } = useSearch({ from: Route.id })
const { userInfo } = useAuth()
const { from, flow, next } = usePersonalityFlow()

const {
loading,
error,
Expand All @@ -43,7 +47,7 @@ function AttachmentTestQuestionPage() {
handleNext,
handleSelectAnswer,
setQuestionRef,
} = useAttachmentQuestions({ from })
} = useAttachmentQuestions({ from, onComplete: flow ? () => next() : undefined })

// 트래킹이 적용된 핸들러들
const handleGoBackWithTracking = wrapWithTracking(BUTTON_NAMES.BACK_TEST, CATEGORIES.ATTACHMENT, handleGoBack)
Expand All @@ -56,7 +60,6 @@ function AttachmentTestQuestionPage() {

const handleSelectAnswerWithTracking = wrapWithTracking(
(_questionId: number, score: number) => {
// 선택한 옵션 번호에 따른 버튼 이름 결정 (1-5)
const buttonNameMap = {
1: BUTTON_NAMES.SELECT_OPTION_1,
2: BUTTON_NAMES.SELECT_OPTION_2,
Expand All @@ -75,7 +78,6 @@ function AttachmentTestQuestionPage() {
setIsGuideOpen(false)
)

// 로딩 페이지 렌더링
if (isSubmitting) {
return <SubmissionLoading nickname={userInfo.nickname || '사용자'} />
}
Expand Down
9 changes: 5 additions & 4 deletions apps/react/src/app/attachment-test/result/my/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { createFileRoute, redirect } from '@tanstack/react-router'
import z from 'zod'
import { z } from 'zod'

import { AttachmentResultContent } from '@/features/attachment/ui/result/attachment-result-content'
import { useAuth } from '@/features/auth'

const searchSchema = z.object({
from: z.string().optional(),
flow: z.enum(['my-personality', 'partner-personality', 'full-flow']).optional(),
})

export const Route = createFileRoute('/attachment-test/result/my/')({
validateSearch: searchSchema,
beforeLoad: async ({ context }) => {
// 인증되지 않은 경우 로그인 페이지로 리다이렉트
if (!context.auth?.authenticated) {
Expand All @@ -18,13 +20,12 @@ export const Route = createFileRoute('/attachment-test/result/my/')({
}
},
component: MyAttachmentResultPage,
validateSearch: searchSchema,
})

function MyAttachmentResultPage() {
const { userInfo } = useAuth()
const { from } = Route.useSearch()
const isFromChat = from === '/chat'
const fromProp = from === 'my-result-preview' ? 'my-result-preview' : from === 'my-page' ? 'my-page' : 'home'

return <AttachmentResultContent userInfo={userInfo} type="my" isFromChat={isFromChat} />
return <AttachmentResultContent userInfo={userInfo} type="my" from={fromProp} />
}
20 changes: 17 additions & 3 deletions apps/react/src/app/attachment-test/result/partner/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { createFileRoute, redirect } from '@tanstack/react-router'
import { z } from 'zod'

import { AttachmentResultContent } from '@/features/attachment/ui/result/attachment-result-content'
import { usePartnerInfo } from '@/features/member'
import { useAuth } from '@/features/auth'

const searchSchema = z.object({
from: z.string().optional(),
})

export const Route = createFileRoute('/attachment-test/result/partner/')({
validateSearch: searchSchema,
beforeLoad: async ({ context }) => {
// 인증되지 않은 경우 로그인 페이지로 리다이렉트
if (!context.auth?.authenticated) {
Expand All @@ -16,7 +22,15 @@ export const Route = createFileRoute('/attachment-test/result/partner/')({
})

function PartnerAttachmentResultPage() {
const { data: partnerInfo } = usePartnerInfo()
const { userInfo } = useAuth()
const { from } = Route.useSearch()
const fromProp =
from === 'partner-result-preview' ? 'partner-result-preview' : from === 'my-page' ? 'my-page' : undefined

const partnerData = {
loveTypeCategory: userInfo.partnerLoveTypeCategory,
personalityType: userInfo.otherPersonalityType,
}

return <AttachmentResultContent userInfo={partnerInfo} type="partner" />
return <AttachmentResultContent userInfo={partnerData} type="partner" from={fromProp} />
}
1 change: 0 additions & 1 deletion apps/react/src/app/chat/result/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ function RouteComponent() {
const navigate = useNavigate()
const goBack = useGoBack()
const queryClient = useQueryClient()

useEffect(() => {
setStatusColor('#FDEDF0')

Expand Down
23 changes: 8 additions & 15 deletions apps/react/src/app/mbti/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createFileRoute, useNavigate, useSearch } from '@tanstack/react-router'
import { createFileRoute } from '@tanstack/react-router'

import { useAuth } from '@/features/auth'
import { MbtiForm } from '@/features/onboarding/ui/mbti-form'
import { useMemberUpdateMutation } from '@/features/profile'
import { personalityFlowSearchSchema } from '@/features/profile/lib/personality-flow'
import { personalityFlowSearchSchema, usePersonalityFlow } from '@/features/profile/lib/personality-flow'
import { wrapWithTracking } from '@/shared/analytics'
import { BUTTON_NAMES, CATEGORIES } from '@/shared/analytics/constants'
import { useGoBack } from '@/shared/navigation/use-go-back'
Expand All @@ -17,25 +17,18 @@ export const Route = createFileRoute('/mbti/')({
})

function MbtiEditPage() {
const navigate = useNavigate()
const goBack = useGoBack()
const { userInfo } = useAuth()
const { flow, chatId } = useSearch({ from: Route.id })
const { flow, next } = usePersonalityFlow()

const updateMutation = useMemberUpdateMutation({
onSuccess: () => {
if (flow === 'my-personality') {
navigate({ to: '/my-attachment-select', search: { flow } })
if (!flow) {
toast.success('내 성향이 변경되었어요!')
goBack()
return
}

if (flow === 'chat-entry') {
navigate({ to: '/my-attachment-select', search: { flow, chatId } })
return
}

toast.success('내 성향이 변경되었어요!')
goBack()
next()
},
errorMessage: '내 성향 변경 중 오류가 발생했습니다',
})
Expand All @@ -53,7 +46,7 @@ function MbtiEditPage() {
return (
<MbtiForm
headerTitle={isFlowMode ? undefined : '내 성향'}
navCenter={getChatEntryProgressBar(flow === 'chat-entry', 1)}
navCenter={getChatEntryProgressBar(flow === 'full-flow', 1)}
contentTopSlot={getPersonalityStepDots(flow === 'my-personality', 1)}
title={
<>
Expand Down
94 changes: 94 additions & 0 deletions apps/react/src/app/my-attachment-select/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useState } from 'react'

import { ATTACHMENT_OPTIONS } from '@/features/attachment'
import { useAuth } from '@/features/auth'
import { TitleSection } from '@/features/onboarding/ui/title-section'
import { useMemberUpdateMutation } from '@/features/profile'
import { personalityFlowSearchSchema, usePersonalityFlow } from '@/features/profile/lib/personality-flow'
import { Screen } from '@/shared/layout/screen'
import { Button } from '@/shared/ui'
import { FixedBottom } from '@/shared/ui/fixed-bottom'
import { getChatEntryProgressBar } from '@/shared/ui/flow-progress-bar'
import { getPersonalityStepDots } from '@/shared/ui/flow-step-dots'
import { DetailHeaderBar } from '@/shared/ui/header-bar'
import { KeyMessageBanner } from '@/shared/ui/key-message-banner'
import { SelectableButton } from '@/shared/ui/selectable-button'
import { toast } from '@/shared/ui/toast'

import type { MemberDataLoveTypeCategoryEnum } from '@data/user-api-axios/api'

export const Route = createFileRoute('/my-attachment-select/')({
validateSearch: personalityFlowSearchSchema,
component: MyAttachmentSelectPage,
})

function MyAttachmentSelectPage() {
const navigate = useNavigate()
const { userInfo } = useAuth()
const { flow, from, next } = usePersonalityFlow()
const [selectedType, setSelectedType] = useState<MemberDataLoveTypeCategoryEnum | null>(
userInfo?.loveTypeCategory ?? null
)

const updateMutation = useMemberUpdateMutation({
onSuccess: () => {
if (from === 'profile') toast.success('내 성향이 변경되었어요!')
next()
},
errorMessage: '저장 중 오류가 발생했습니다',
})

const handleConfirm = () => {
if (!selectedType || updateMutation.isPending) return
updateMutation.mutate({ loveTypeCategory: selectedType })
}

const handleDontKnow = () => {
navigate({ to: '/attachment-test', search: { flow, from } })
}

return (
<Screen>
<Screen.Header behavior="overlay">
<DetailHeaderBar center={getChatEntryProgressBar(flow === 'full-flow', 2)} />
</Screen.Header>

<Screen.Content className="flex flex-1 flex-col bg-white">
{getPersonalityStepDots(flow === 'my-personality', 2)}
<TitleSection
title={
<>
나의 애착유형을
<br />
선택해주세요
</>
}
/>
<div className="mt-4 px-5">
<KeyMessageBanner title="내 애착유형을 모른다면? 테스트 GO" onClick={handleDontKnow} />
</div>

<div className="mt-10 flex flex-col gap-3 px-5">
<div className="mt-3 flex flex-col gap-3">
{ATTACHMENT_OPTIONS.map((option) => (
<SelectableButton
key={option.value}
selected={selectedType === option.value}
onClick={() => setSelectedType(option.value)}
disabled={updateMutation.isPending}
className="w-full text-left"
>
{option.label}
</SelectableButton>
))}
</div>
</div>

<FixedBottom>
<Button text="프로필 완성!" onClick={handleConfirm} disabled={!selectedType || updateMutation.isPending} />
</FixedBottom>
</Screen.Content>
</Screen>
)
}
Loading
Loading