Skip to content

Commit 6ae2285

Browse files
Merge pull request #75 from MoimService/feat/refactor/mypage-refactor/DEVING-71
Feat/refactor/mypage-refactor/DEVING-71 마이페이지
2 parents 666736e + 4cf03b7 commit 6ae2285

File tree

10 files changed

+208
-247
lines changed

10 files changed

+208
-247
lines changed

src/app/(user-page)/mypage/MyPageClient.tsx

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,9 @@ import {
77
import { useQueryClient } from '@tanstack/react-query';
88
import { useRouter, useSearchParams } from 'next/navigation';
99
import { useCallback, useEffect, useRef, useState } from 'react';
10+
import { getProfile } from 'service/api/mypageProfile';
1011

1112
import { TAB_TYPES } from '../../../constants/mypage/mypageConstant';
12-
import {
13-
ACTIVE_SECTION,
14-
TAB_ACTIVE,
15-
TAB_BUTTON,
16-
TAB_CONTAINER,
17-
TAB_INACTIVE,
18-
TAB_INDICATOR,
19-
} from '../../../constants/mypage/mypageCss';
2013
// 컴포넌트 임포트
2114
import BasicEdit from './_features/BasicEdit';
2215
import BasicInfo from './_features/BasicInfo';
@@ -134,10 +127,6 @@ const MyPageClient = () => {
134127
return () => window.removeEventListener('resize', handleResize);
135128
}, [updateIndicator]);
136129

137-
// 탭 버튼 클래스 생성 함수
138-
const getTabButtonClass = (tab: string) =>
139-
`${TAB_BUTTON} ${activeTab === tab ? TAB_ACTIVE : TAB_INACTIVE}`;
140-
141130
// 5. 조건부 렌더링 최적화
142131
const renderContent = () => {
143132
const isEditMode = editModeSection === activeTab;
@@ -175,12 +164,16 @@ const MyPageClient = () => {
175164
return (
176165
<div className="profile-manager flex flex-col">
177166
{/* 탭 네비게이션 */}
178-
<div className={TAB_CONTAINER}>
167+
<div className="tabs-container relative flex w-full md:mb-6">
179168
<button
180169
ref={(el) => {
181170
tabRefs.current[TAB_TYPES.BASIC] = el;
182171
}}
183-
className={getTabButtonClass(TAB_TYPES.BASIC)}
172+
className={`w-1/4 px-5 py-3 text-center font-medium transition-colors ${
173+
activeTab === TAB_TYPES.BASIC
174+
? 'text-main'
175+
: 'text-Cgray500 hover:text-Cgray400'
176+
}`}
184177
onClick={() => handleTabChange(TAB_TYPES.BASIC)}
185178
>
186179
기본정보
@@ -189,7 +182,11 @@ const MyPageClient = () => {
189182
ref={(el) => {
190183
tabRefs.current[TAB_TYPES.CONTACT] = el;
191184
}}
192-
className={getTabButtonClass(TAB_TYPES.CONTACT)}
185+
className={`w-1/4 px-5 py-3 text-center font-medium transition-colors ${
186+
activeTab === TAB_TYPES.CONTACT
187+
? 'text-main'
188+
: 'text-Cgray500 hover:text-Cgray400'
189+
}`}
193190
onClick={() => handleTabChange(TAB_TYPES.CONTACT)}
194191
>
195192
연락수단
@@ -198,7 +195,11 @@ const MyPageClient = () => {
198195
ref={(el) => {
199196
tabRefs.current[TAB_TYPES.TECH] = el;
200197
}}
201-
className={getTabButtonClass(TAB_TYPES.TECH)}
198+
className={`w-1/4 px-5 py-3 text-center font-medium transition-colors ${
199+
activeTab === TAB_TYPES.TECH
200+
? 'text-main'
201+
: 'text-Cgray500 hover:text-Cgray400'
202+
}`}
202203
onClick={() => handleTabChange(TAB_TYPES.TECH)}
203204
>
204205
기술스택
@@ -207,18 +208,25 @@ const MyPageClient = () => {
207208
ref={(el) => {
208209
tabRefs.current[TAB_TYPES.PASSWORD] = el;
209210
}}
210-
className={getTabButtonClass(TAB_TYPES.PASSWORD)}
211+
className={`w-1/4 px-5 py-3 text-center font-medium transition-colors ${
212+
activeTab === TAB_TYPES.PASSWORD
213+
? 'text-main'
214+
: 'text-Cgray500 hover:text-Cgray400'
215+
}`}
211216
onClick={() => handleTabChange(TAB_TYPES.PASSWORD)}
212217
>
213218
비밀번호변경
214219
</button>
215220

216221
{/* 애니메이션 underbar */}
217-
<div className={TAB_INDICATOR} style={indicatorStyle} />
222+
<div
223+
className="absolute bottom-0 h-1 bg-main transition-all duration-200 ease-in-out"
224+
style={indicatorStyle}
225+
/>
218226
</div>
219227

220228
{/* 활성화된 섹션만 렌더링 */}
221-
<div className={ACTIVE_SECTION}>{renderContent()}</div>
229+
<div className="active-section mt-4">{renderContent()}</div>
222230
</div>
223231
);
224232
};

src/app/(user-page)/mypage/_features/BasicEdit.tsx

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,6 @@ import {
1616
MAX_INTRO_LENGTH,
1717
POSITION_OPTIONS,
1818
} from '../../../../constants/mypage/mypageConstant';
19-
import {
20-
BUTTON_ACTIONS,
21-
BUTTON_OUTLINE,
22-
BUTTON_PRIMARY,
23-
ERROR_TEXT,
24-
FIELD_CONTAINER,
25-
FIELD_SECTION,
26-
FORM_CONTAINER,
27-
INPUT_FIELD_EDIT,
28-
LABEL_EDIT,
29-
SECTION_CONTAINER,
30-
TEXTAREA_FIELD_EDIT,
31-
TOGGLE_BUTTON_ACTIVE,
32-
TOGGLE_BUTTON_BASE,
33-
TOGGLE_BUTTON_INACTIVE,
34-
} from '../../../../constants/mypage/mypageCss';
3519
import { IFormData } from '../../../../types/mypageTypes';
3620

3721
interface BasicEditProps {
@@ -156,10 +140,6 @@ const BasicEdit = ({ onEditComplete }: BasicEditProps) => {
156140
[setValue],
157141
);
158142

159-
// 토글 버튼 스타일 결정 함수
160-
const getToggleButtonClass = (isActive: boolean) =>
161-
`${TOGGLE_BUTTON_BASE} ${isActive ? TOGGLE_BUTTON_ACTIVE : TOGGLE_BUTTON_INACTIVE}`;
162-
163143
// 취소 핸들러
164144
const handleCancel = () => {
165145
onEditComplete();
@@ -171,28 +151,59 @@ const BasicEdit = ({ onEditComplete }: BasicEditProps) => {
171151
}
172152

173153
return (
174-
<form onSubmit={handleSubmit(onSubmit)} className={FORM_CONTAINER}>
175-
<div className={SECTION_CONTAINER}>
154+
<form
155+
onSubmit={handleSubmit(onSubmit)}
156+
className="w-full rounded-[16px] border border-Cgray300 p-[32px]"
157+
>
158+
<div className="flex flex-col gap-[16px] md:gap-[32px]">
176159
{/* 이름 입력 필드 */}
177-
<div className={FIELD_CONTAINER}>
178-
<label htmlFor="name-input" className={LABEL_EDIT}>
160+
<div className="flex flex-col gap-[8px]">
161+
<label htmlFor="name-input" className="typo-head3 text-main">
179162
사용자 이름
180163
</label>
181164
<input
182165
id="name-input"
183166
type="text"
184-
{...register('name', { required: true })}
185-
className={INPUT_FIELD_EDIT}
167+
{...register('name', {
168+
required: true,
169+
validate: (value) => {
170+
// 한글만 있는지 확인
171+
const isKoreanOnly = /^[-]+$/.test(value);
172+
173+
// 영어만 있는지 확인
174+
const isEnglishOnly = /^[a-zA-Z\s]+$/.test(value);
175+
176+
if (isKoreanOnly) {
177+
return (
178+
value.length <= 5 ||
179+
'한글 이름은 최대 5글자까지 입력 가능합니다'
180+
);
181+
} else if (isEnglishOnly) {
182+
return (
183+
value.length <= 8 ||
184+
'영어 이름은 최대 8글자까지 입력 가능합니다'
185+
);
186+
} else {
187+
// 혼합 또는 다른 언어인 경우
188+
return '한글 또는 영어로만 이름을 입력해주세요';
189+
}
190+
},
191+
})}
192+
className="typo-button1 h-[40px] rounded-[16px] border-b border-Cgray300 bg-Cgray200 px-4 py-2 text-Cgray700 focus:outline-none md:h-[50px]"
186193
/>
187194
{errors.name && (
188-
<span className={ERROR_TEXT}>이름을 입력해주세요</span>
195+
<span className="text-sm text-warning">
196+
{errors.name.type === 'required'
197+
? '이름을 입력해주세요'
198+
: errors.name.message}
199+
</span>
189200
)}
190201
</div>
191202

192203
{/* 자기소개 텍스트 영역 */}
193-
<div className={FIELD_CONTAINER}>
204+
<div className="flex flex-col gap-2">
194205
<div className="flex items-center justify-between">
195-
<label htmlFor="intro-input" className={LABEL_EDIT}>
206+
<label htmlFor="intro-input" className="typo-head3 text-main">
196207
자기소개
197208
</label>
198209
</div>
@@ -205,30 +216,32 @@ const BasicEdit = ({ onEditComplete }: BasicEditProps) => {
205216
},
206217
})}
207218
rows={3}
208-
className={TEXTAREA_FIELD_EDIT}
219+
className="h-[140px] resize-none rounded-[16px] border-b border-Cgray300 bg-Cgray200 px-4 py-2 text-Cgray700 focus:outline-none"
209220
/>
210221
{errors.intro && (
211-
<span className={ERROR_TEXT}>{errors.intro.message}</span>
222+
<span className="text-sm text-warning">{errors.intro.message}</span>
212223
)}
213224
{introLength > MAX_INTRO_LENGTH && !errors.intro && (
214-
<span className={ERROR_TEXT}>
225+
<span className="text-sm text-warning">
215226
최대 {MAX_INTRO_LENGTH}자까지 작성 가능합니다
216227
</span>
217228
)}
218229
</div>
219230

220231
{/* 포지션 버튼 */}
221-
<div className={FIELD_SECTION}>
222-
<div className={LABEL_EDIT}>포지션</div>
232+
<div className="flex flex-col gap-[16px] border-b border-Cgray300 pb-[16px] md:pb-[32px]">
233+
<div className="typo-head3 text-main">포지션</div>
223234
<div className="flex w-full flex-wrap gap-2">
224235
<div className="rounded-4 typo-head3 flex w-full gap-[12px]">
225236
{POSITION_OPTIONS.map((option) => (
226237
<button
227238
key={option.value}
228239
type="button"
229-
className={getToggleButtonClass(
230-
currentPosition === option.value,
231-
)}
240+
className={`flex flex-1 flex-col items-center justify-center gap-1 rounded-md py-1 transition-colors duration-300 md:py-2 ${
241+
currentPosition === option.value
242+
? 'bg-default text-main'
243+
: 'bg-disable text-Cgray500'
244+
}`}
232245
onClick={() => setValue('position', option.value)}
233246
aria-label={option.value}
234247
>
@@ -248,14 +261,18 @@ const BasicEdit = ({ onEditComplete }: BasicEditProps) => {
248261
</div>
249262

250263
{/* 성별 토글 버튼 */}
251-
<div className={FIELD_SECTION}>
252-
<div className={LABEL_EDIT}>성별</div>
264+
<div className="flex flex-col gap-[16px] border-b border-Cgray300 pb-[16px] md:pb-[32px]">
265+
<div className="typo-head3 text-main">성별</div>
253266
<div className="typo-head3 flex w-full gap-[16px] rounded-md">
254267
{GENDER_OPTIONS.map((option) => (
255268
<button
256269
key={option.value}
257270
type="button"
258-
className={getToggleButtonClass(currentGender === option.value)}
271+
className={`flex flex-1 flex-col items-center justify-center gap-1 rounded-md py-1 transition-colors duration-300 md:py-2 ${
272+
currentGender === option.value
273+
? 'bg-default text-main'
274+
: 'bg-disable text-Cgray500'
275+
}`}
259276
onClick={() => setValue('gender', option.value)}
260277
aria-label={option.value}
261278
>
@@ -274,8 +291,8 @@ const BasicEdit = ({ onEditComplete }: BasicEditProps) => {
274291
</div>
275292

276293
{/* 연령대 드롭다운 */}
277-
<div className={FIELD_SECTION}>
278-
<div className={LABEL_EDIT}>연령대</div>
294+
<div className="flex flex-col gap-[16px] border-b border-Cgray300 pb-[16px] md:pb-[32px]">
295+
<div className="typo-head3 text-main">연령대</div>
279296
<Controller
280297
name="age"
281298
control={control}
@@ -294,8 +311,8 @@ const BasicEdit = ({ onEditComplete }: BasicEditProps) => {
294311
</div>
295312

296313
{/* 지역 드롭다운 */}
297-
<div className={FIELD_SECTION}>
298-
<div className={LABEL_EDIT}>지역</div>
314+
<div className="flex flex-col gap-[16px] border-b border-Cgray300 pb-[16px] md:pb-[32px]">
315+
<div className="typo-head3 text-main">지역</div>
299316
<Controller
300317
name="location"
301318
control={control}
@@ -314,18 +331,18 @@ const BasicEdit = ({ onEditComplete }: BasicEditProps) => {
314331
/>
315332
</div>
316333

317-
<div className={BUTTON_ACTIONS}>
334+
<div className="flex justify-between">
318335
<Button
319336
type="button"
320337
variant="outline"
321-
className={BUTTON_OUTLINE}
338+
className="h-[40px] w-[140px] md:h-[46px]"
322339
onClick={handleCancel}
323340
>
324341
취소
325342
</Button>
326343
<Button
327344
type="submit"
328-
className={BUTTON_PRIMARY}
345+
className="h-[40px] w-[140px] select-none md:h-[46px]"
329346
disabled={
330347
isSubmitting || isUpdating || introLength > MAX_INTRO_LENGTH
331348
}

0 commit comments

Comments
 (0)