● 2차 목표
그간,, 2차목표를 진행했었는데 포스팅하는걸 잊었으빈다.. ㅠ 오늘은 3차목표를 진행중이어서 오늘 리팩토링했던 부분을 정리해 올립니다.
아래는 그간 진행했던 2차 목표입니다. 대체로 로직분리가 절반이상이었습니다.
❍ PetInfo.tsx (다은)
query mutaion 훅으로 분리handle부분 로직 분리 처리
❍ PetProfile.tsx (준석)
뭔가 cropper함수와 이미지 처리 다 함수로 뺐으면 좋겠어요 로직분리
❍ Spin(준석)
styled로 처리
❍ PetContainer (다은)
popup 로직 state로 하나로 만 구분해서 쓰자.. 입니다.. 이거 제가 할게요 50줄이 들어드는 매직여기도 mutation 처리필요합니다
● 3차 목표
❍ Info(다은)
- Error Message 분리하기
- mutation 훅으로 분리
- jsx 의 회원가입과 회원 수정 부분이 코드가 같은데, 페이지를 분리하는 것이 좋을지. 같은 컴포넌트로 관리하는게 좋을까?
- 여기도 팝업 state하나로 관리할 수 있어요.
- 핸들러 로직분리하기
- styled 쓰지 않은부분 styled로 만들기
- import 해서 쓰는 styled부분 모두 import as * SC 활용해 적기
❍ MyPage(준석)
- import 줄여보기..
- infinity query 훅으로 관리하기
- 분기처리 정리하기
- 데이터가 모두 로딩인 경우 LoadingComponent
- 데이터를 모두 받아온 경우 Container
- ListBox 컴포넌트로 분리하기
- currenntTab 숫자에 따라 데이터만 다르고 jsx 형식은 같기에 컴포넌트로 분리하는게 좋지 않을까?
● Info 로직 분리
type QuitProps = {
quitText: string;
accessToken: string | null;
};
const ERROR_MESSAGE = {
NONE: '텍스트 필수입니다.',
EXTRA: '공백과 특수기호를 제거해주세요.',
LENGTH: '10자 이내로 작성해주세요.',
DUPLICATE: '중복을 확인해주세요.',
EXIST: '이미 아이디가 있습니다.',
ENABLE: '사용가능한 아이디입니다.',
};
export function Component() {
const navigate = useNavigate();
const { userId } = useParams();
const location = useLocation();
const matchInfo = useMatch(Path.Info);
...
먼저 Info페이지 내에서 쓰이는 Prop과 type부분들은 따로 파일을 만들어 로직을 분리하였습니다.
또한 안에서 핸들러의 코드 양이 너무 길어 가독성이 많이 떨어진다고 느껴, 핸들러 내부 로직들을 분리하기로 하였습니다.
아래는 핸들러 로직을 분리하지 않았을 때 코드입니다.
// 중복확인 핸들러
const handleCheckNickname = async (e: React.MouseEvent) => {
e.preventDefault();
// 중복확인할 닉네임이 현재 닉네임과 동일하면 return
if (location.state.nickname === nicknameValue) {
setDuplicated(true);
return;
}
if (errors.nickname) {
if (errors.nickname.message !== ERROR_MESSAGE.DUPLICATE) return;
}
const result = await axios.post(
`${SERVER_URL}/members/nickname-check/${nicknameValue}`,
);
if (result.data.enable) {
setDuplicated(true);
setError('nickname', { message: ERROR_MESSAGE.ENABLE });
setValue('nickname', nicknameValue);
} else {
setDuplicated(false);
setError('nickname', { message: ERROR_MESSAGE.EXIST });
}
};
따로 로직분리를 하는 과정에서, setter함수나 mutation을 prop으로 전달받아야해서 prop으로 넘겨주어야할 값이 조금 있었지만, 그래도 넘겨주고난 뒤 코드가 깨끗해지는 과정을 보니 하지 않을 수가 업엇읍니다..
interface SubmitProp {
data: InfoProps;
zip: string;
accessToken: string | null;
isDuplicated: boolean;
matchInfo: PathMatch<string> | null;
userId: string | undefined;
setError: UseFormSetError<InfoProps>;
userInfoFillMutation: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mutate: UseMutateFunction<any, unknown, PatchUserInfo, unknown>;
};
}
const handleOnSubmit = ({
data,
zip,
accessToken,
isDuplicated,
matchInfo,
userId,
setError,
userInfoFillMutation,
}: SubmitProp) => {
const url = `${SERVER_URL}/members/status`;
data = {
...data,
address: zip.trim(),
url,
accessToken,
};
if (isDuplicated && matchInfo) {
userInfoFillMutation.mutate(data);
}
if (isDuplicated && !userId && matchInfo === null) {
return setError('nickname', {
message: ERROR_MESSAGE.DUPLICATE,
});
}
if (!isDuplicated) {
return setError('nickname', {
type: 'required',
message: ERROR_MESSAGE.DUPLICATE,
});
}
// userId params가 존재하면 userInfoEditMutation
userInfoFillMutation.mutate(data);
};
export default handleOnSubmit;
그리고 useLocation이나 useMatch같은 것들도 모두 type을 'react-router-dom'에서 지원해주고 있다는 것을 알 수 있었습니다. 이외에 핸들러가 한가지더 있었는데, 거의 유사하게 로직 분리를 해주었습니다.
이외에 JSX 코드에서 리턴되는부분이 중복되는 부분이 있었습니다.
if (userId) {
return (
<>
<div
className="w-[320px] max-sm:w-[220px] mx-auto flex items-center gap-2 cursor-pointer"
onClick={() => navigate(-1)}>
<ArrowLeft className="w-7 h-7 stroke-deepgreen" />
<Logo width="400" className="ml-8 max-sm:w-80 max-sm:mx-auto " />
</div>
<FormContainer>
<InfoForm onSubmit={handleSubmit(onSubmit)}>
{/* 이름 */}
<InputContainer>
<Label htmlFor="name">이름</Label>
<InputText id="nickname" value={location.state.name} disabled />
</InputContainer>
{/* 닉네임*/}
<InputContainer>
<Label htmlFor="nickname">닉네임</Label>
<InputText
id="nickname"
{...register('nickname', {
required: ERROR_MESSAGE.NONE,
pattern: {
value: /^(?!.*\s)[\p{L}\p{N}]+$/u,
message: ERROR_MESSAGE.EXTRA,
},
maxLength: {
value: 10,
message: ERROR_MESSAGE.LENGTH,
},
onChange: () => setDuplicated(false),
})}
error={errors.nickname?.message}
duplicated={`${isDuplicated}`}
defaultValue={location.state.nickname}
/>
<ConfirmButton
onClick={e => handleCheckNickname(e)}
isduplicated={`${isDuplicated}`}>
중복확인
</ConfirmButton>
<ErrorMessage
errors={errors}
name="nickname"
render={({ message }) => (
<ErrorNotice messagetext={message}>{message}</ErrorNotice>
)}
/>
</InputContainer>
{/* 이메일 */}
<InputContainer>
<Label htmlFor="email">이메일</Label>
<InputText
id="email"
value={
location.state.email
? location.state.email
: 'guest@email.com'
}
disabled
/>
</InputContainer>
{/* 주소 */}
<div>
<Label>주소</Label>
<Select size="lg" direction="column" setZip={setZip} />
</div>
<Button size="lg" text="회원정보 수정" isgreen="true" />
</InfoForm>
{/* 탈퇴버튼 */}
<div className="flex flex-col items-center gap-1 mt-3">
<span className="text-xs">
탈퇴를 원하신다면 '탈퇴할게요'를 적어주세요🥲
</span>
<form
className="flex gap-1"
onSubmit={quitHandleSubmit(onSubmitQuit)}>
<input
className="w-full bg-lightgray p-1 text-xs rounded-md"
{...quitRegister('quitText', {
validate: value =>
value === '탈퇴할게요' || '정확하게 입력해주세요',
})}
/>
<button className="p-1 text-xs bg-deepgreen flex-shrink-0 rounded-md text-defaultbg">
확인
</button>
</form>
<p className="text-xs text-rose-500">
{quitErrors.quitText?.message}
</p>
</div>
</FormContainer>
{isError && (
<Popup
title={AlertText.Failed}
popupcontrol={() => {
setIsError(false);
}}
handler={[
() => {
setIsError(false);
},
]}
/>
)}
</>
);
} else {
return (
<>
<ExtraInfoLogo>
<Like className="stroke-deepgreen fill-deepgreen w-6 h-6" />
<span className="ml-2 text-xl font-black">추가정보 입력</span>
</ExtraInfoLogo>
<FormContainer>
<InfoForm onSubmit={handleSubmit(onSubmit)}>
{/* 이름 */}
<InputContainer>
<Label htmlFor="name">이름</Label>
<InputText id="nickname" value={location.state.name} disabled />
</InputContainer>
{/* 닉네임*/}
<InputContainer>
<Label htmlFor="nickname">닉네임</Label>
<InputText
id="nickname"
{...register('nickname', {
required: ERROR_MESSAGE.NONE,
pattern: {
value: /^(?!.*\s)[\p{L}\p{N}]+$/u,
message: ERROR_MESSAGE.EXTRA,
},
maxLength: {
value: 10,
message: ERROR_MESSAGE.LENGTH,
},
onChange: () => setDuplicated(false),
})}
error={errors.nickname?.message}
duplicated={`${isDuplicated}`}
/>
<ErrorMessage
errors={errors}
name="nickname"
render={({ message }) => (
<ErrorNotice messagetext={message}>{message}</ErrorNotice>
)}
/>
<ConfirmButton
onClick={e => handleCheckNickname(e)}
isduplicated={`${isDuplicated}`}>
중복확인
</ConfirmButton>
</InputContainer>
{/* 이메일 */}
<InputContainer>
<Label htmlFor="email">이메일</Label>
<InputText id="email" value={location.state.email} disabled />
</InputContainer>
{/* 주소 */}
<div>
<Label>주소</Label>
<Select size="lg" direction="column" setZip={setZip} />
</div>
<Button size="lg" text="회원가입" isgreen="true" />
</InfoForm>
</FormContainer>
</>
);
}
계속해서 같은 부분이 두번이 쓰이고 있었고, 'userId'에 따라 다른 부분들이 한두개 있었어서 내부적으로 수정을 조금만 보면 될 것 같다고 생각하여, 컴포넌트로 분리하였습니다.
컴포넌트 분리하고 난 뒤, 코드
if (userId) {
return (
<>
<SC.CloseBtn onClick={() => navigate(-1)}>
<ArrowLeft className="w-7 h-7 stroke-deepgreen" />
<Logo width="400" className="ml-8 max-sm:w-80 max-sm:mx-auto " />
</SC.CloseBtn>
<FormBox
register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit}
location={location}
isDuplicated={isDuplicated}
setDuplicated={setDuplicated}
setZip={setZip}
errors={errors}
setError={setError}
setValue={setValue}
nicknameValue={nicknameValue}
quitErrors={quitErrors}
quitHandleSubmit={quitHandleSubmit}
onSubmitQuit={onSubmitQuit}
quitRegister={quitRegister}
userId={undefined}
/>
{isError && (
<Popup
title={AlertText.Failed}
popupcontrol={() => {
setIsError(false);
}}
handler={[
() => {
setIsError(false);
},
]}
/>
)}
</>
);
} else {
return (
<>
<SC.ExtraInfoLogo>
<Like className="stroke-deepgreen fill-deepgreen w-6 h-6" />
<SC.Additional>추가정보 입력</SC.Additional>
</SC.ExtraInfoLogo>
<FormBox
register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit}
location={location}
isDuplicated={isDuplicated}
setDuplicated={setDuplicated}
setZip={setZip}
errors={errors}
setError={setError}
setValue={setValue}
nicknameValue={nicknameValue}
quitErrors={quitErrors}
quitHandleSubmit={quitHandleSubmit}
onSubmitQuit={onSubmitQuit}
quitRegister={quitRegister}
userId={undefined}
/>
</>
);
}
중복을 많이 제거할 수 있었습니다..!!
● 2차 목표
그간,, 2차목표를 진행했었는데 포스팅하는걸 잊었으빈다.. ㅠ 오늘은 3차목표를 진행중이어서 오늘 리팩토링했던 부분을 정리해 올립니다.
아래는 그간 진행했던 2차 목표입니다. 대체로 로직분리가 절반이상이었습니다.
❍ PetInfo.tsx (다은)
query mutaion 훅으로 분리handle부분 로직 분리 처리
❍ PetProfile.tsx (준석)
뭔가 cropper함수와 이미지 처리 다 함수로 뺐으면 좋겠어요 로직분리
❍ Spin(준석)
styled로 처리
❍ PetContainer (다은)
popup 로직 state로 하나로 만 구분해서 쓰자.. 입니다.. 이거 제가 할게요 50줄이 들어드는 매직여기도 mutation 처리필요합니다
● 3차 목표
❍ Info(다은)
- Error Message 분리하기
- mutation 훅으로 분리
- jsx 의 회원가입과 회원 수정 부분이 코드가 같은데, 페이지를 분리하는 것이 좋을지. 같은 컴포넌트로 관리하는게 좋을까?
- 여기도 팝업 state하나로 관리할 수 있어요.
- 핸들러 로직분리하기
- styled 쓰지 않은부분 styled로 만들기
- import 해서 쓰는 styled부분 모두 import as * SC 활용해 적기
❍ MyPage(준석)
- import 줄여보기..
- infinity query 훅으로 관리하기
- 분기처리 정리하기
- 데이터가 모두 로딩인 경우 LoadingComponent
- 데이터를 모두 받아온 경우 Container
- ListBox 컴포넌트로 분리하기
- currenntTab 숫자에 따라 데이터만 다르고 jsx 형식은 같기에 컴포넌트로 분리하는게 좋지 않을까?
● Info 로직 분리
type QuitProps = {
quitText: string;
accessToken: string | null;
};
const ERROR_MESSAGE = {
NONE: '텍스트 필수입니다.',
EXTRA: '공백과 특수기호를 제거해주세요.',
LENGTH: '10자 이내로 작성해주세요.',
DUPLICATE: '중복을 확인해주세요.',
EXIST: '이미 아이디가 있습니다.',
ENABLE: '사용가능한 아이디입니다.',
};
export function Component() {
const navigate = useNavigate();
const { userId } = useParams();
const location = useLocation();
const matchInfo = useMatch(Path.Info);
...
먼저 Info페이지 내에서 쓰이는 Prop과 type부분들은 따로 파일을 만들어 로직을 분리하였습니다.
또한 안에서 핸들러의 코드 양이 너무 길어 가독성이 많이 떨어진다고 느껴, 핸들러 내부 로직들을 분리하기로 하였습니다.
아래는 핸들러 로직을 분리하지 않았을 때 코드입니다.
// 중복확인 핸들러
const handleCheckNickname = async (e: React.MouseEvent) => {
e.preventDefault();
// 중복확인할 닉네임이 현재 닉네임과 동일하면 return
if (location.state.nickname === nicknameValue) {
setDuplicated(true);
return;
}
if (errors.nickname) {
if (errors.nickname.message !== ERROR_MESSAGE.DUPLICATE) return;
}
const result = await axios.post(
`${SERVER_URL}/members/nickname-check/${nicknameValue}`,
);
if (result.data.enable) {
setDuplicated(true);
setError('nickname', { message: ERROR_MESSAGE.ENABLE });
setValue('nickname', nicknameValue);
} else {
setDuplicated(false);
setError('nickname', { message: ERROR_MESSAGE.EXIST });
}
};
따로 로직분리를 하는 과정에서, setter함수나 mutation을 prop으로 전달받아야해서 prop으로 넘겨주어야할 값이 조금 있었지만, 그래도 넘겨주고난 뒤 코드가 깨끗해지는 과정을 보니 하지 않을 수가 업엇읍니다..
interface SubmitProp {
data: InfoProps;
zip: string;
accessToken: string | null;
isDuplicated: boolean;
matchInfo: PathMatch<string> | null;
userId: string | undefined;
setError: UseFormSetError<InfoProps>;
userInfoFillMutation: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mutate: UseMutateFunction<any, unknown, PatchUserInfo, unknown>;
};
}
const handleOnSubmit = ({
data,
zip,
accessToken,
isDuplicated,
matchInfo,
userId,
setError,
userInfoFillMutation,
}: SubmitProp) => {
const url = `${SERVER_URL}/members/status`;
data = {
...data,
address: zip.trim(),
url,
accessToken,
};
if (isDuplicated && matchInfo) {
userInfoFillMutation.mutate(data);
}
if (isDuplicated && !userId && matchInfo === null) {
return setError('nickname', {
message: ERROR_MESSAGE.DUPLICATE,
});
}
if (!isDuplicated) {
return setError('nickname', {
type: 'required',
message: ERROR_MESSAGE.DUPLICATE,
});
}
// userId params가 존재하면 userInfoEditMutation
userInfoFillMutation.mutate(data);
};
export default handleOnSubmit;
그리고 useLocation이나 useMatch같은 것들도 모두 type을 'react-router-dom'에서 지원해주고 있다는 것을 알 수 있었습니다. 이외에 핸들러가 한가지더 있었는데, 거의 유사하게 로직 분리를 해주었습니다.
이외에 JSX 코드에서 리턴되는부분이 중복되는 부분이 있었습니다.
if (userId) {
return (
<>
<div
className="w-[320px] max-sm:w-[220px] mx-auto flex items-center gap-2 cursor-pointer"
onClick={() => navigate(-1)}>
<ArrowLeft className="w-7 h-7 stroke-deepgreen" />
<Logo width="400" className="ml-8 max-sm:w-80 max-sm:mx-auto " />
</div>
<FormContainer>
<InfoForm onSubmit={handleSubmit(onSubmit)}>
{/* 이름 */}
<InputContainer>
<Label htmlFor="name">이름</Label>
<InputText id="nickname" value={location.state.name} disabled />
</InputContainer>
{/* 닉네임*/}
<InputContainer>
<Label htmlFor="nickname">닉네임</Label>
<InputText
id="nickname"
{...register('nickname', {
required: ERROR_MESSAGE.NONE,
pattern: {
value: /^(?!.*\s)[\p{L}\p{N}]+$/u,
message: ERROR_MESSAGE.EXTRA,
},
maxLength: {
value: 10,
message: ERROR_MESSAGE.LENGTH,
},
onChange: () => setDuplicated(false),
})}
error={errors.nickname?.message}
duplicated={`${isDuplicated}`}
defaultValue={location.state.nickname}
/>
<ConfirmButton
onClick={e => handleCheckNickname(e)}
isduplicated={`${isDuplicated}`}>
중복확인
</ConfirmButton>
<ErrorMessage
errors={errors}
name="nickname"
render={({ message }) => (
<ErrorNotice messagetext={message}>{message}</ErrorNotice>
)}
/>
</InputContainer>
{/* 이메일 */}
<InputContainer>
<Label htmlFor="email">이메일</Label>
<InputText
id="email"
value={
location.state.email
? location.state.email
: 'guest@email.com'
}
disabled
/>
</InputContainer>
{/* 주소 */}
<div>
<Label>주소</Label>
<Select size="lg" direction="column" setZip={setZip} />
</div>
<Button size="lg" text="회원정보 수정" isgreen="true" />
</InfoForm>
{/* 탈퇴버튼 */}
<div className="flex flex-col items-center gap-1 mt-3">
<span className="text-xs">
탈퇴를 원하신다면 '탈퇴할게요'를 적어주세요🥲
</span>
<form
className="flex gap-1"
onSubmit={quitHandleSubmit(onSubmitQuit)}>
<input
className="w-full bg-lightgray p-1 text-xs rounded-md"
{...quitRegister('quitText', {
validate: value =>
value === '탈퇴할게요' || '정확하게 입력해주세요',
})}
/>
<button className="p-1 text-xs bg-deepgreen flex-shrink-0 rounded-md text-defaultbg">
확인
</button>
</form>
<p className="text-xs text-rose-500">
{quitErrors.quitText?.message}
</p>
</div>
</FormContainer>
{isError && (
<Popup
title={AlertText.Failed}
popupcontrol={() => {
setIsError(false);
}}
handler={[
() => {
setIsError(false);
},
]}
/>
)}
</>
);
} else {
return (
<>
<ExtraInfoLogo>
<Like className="stroke-deepgreen fill-deepgreen w-6 h-6" />
<span className="ml-2 text-xl font-black">추가정보 입력</span>
</ExtraInfoLogo>
<FormContainer>
<InfoForm onSubmit={handleSubmit(onSubmit)}>
{/* 이름 */}
<InputContainer>
<Label htmlFor="name">이름</Label>
<InputText id="nickname" value={location.state.name} disabled />
</InputContainer>
{/* 닉네임*/}
<InputContainer>
<Label htmlFor="nickname">닉네임</Label>
<InputText
id="nickname"
{...register('nickname', {
required: ERROR_MESSAGE.NONE,
pattern: {
value: /^(?!.*\s)[\p{L}\p{N}]+$/u,
message: ERROR_MESSAGE.EXTRA,
},
maxLength: {
value: 10,
message: ERROR_MESSAGE.LENGTH,
},
onChange: () => setDuplicated(false),
})}
error={errors.nickname?.message}
duplicated={`${isDuplicated}`}
/>
<ErrorMessage
errors={errors}
name="nickname"
render={({ message }) => (
<ErrorNotice messagetext={message}>{message}</ErrorNotice>
)}
/>
<ConfirmButton
onClick={e => handleCheckNickname(e)}
isduplicated={`${isDuplicated}`}>
중복확인
</ConfirmButton>
</InputContainer>
{/* 이메일 */}
<InputContainer>
<Label htmlFor="email">이메일</Label>
<InputText id="email" value={location.state.email} disabled />
</InputContainer>
{/* 주소 */}
<div>
<Label>주소</Label>
<Select size="lg" direction="column" setZip={setZip} />
</div>
<Button size="lg" text="회원가입" isgreen="true" />
</InfoForm>
</FormContainer>
</>
);
}
계속해서 같은 부분이 두번이 쓰이고 있었고, 'userId'에 따라 다른 부분들이 한두개 있었어서 내부적으로 수정을 조금만 보면 될 것 같다고 생각하여, 컴포넌트로 분리하였습니다.
컴포넌트 분리하고 난 뒤, 코드
if (userId) {
return (
<>
<SC.CloseBtn onClick={() => navigate(-1)}>
<ArrowLeft className="w-7 h-7 stroke-deepgreen" />
<Logo width="400" className="ml-8 max-sm:w-80 max-sm:mx-auto " />
</SC.CloseBtn>
<FormBox
register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit}
location={location}
isDuplicated={isDuplicated}
setDuplicated={setDuplicated}
setZip={setZip}
errors={errors}
setError={setError}
setValue={setValue}
nicknameValue={nicknameValue}
quitErrors={quitErrors}
quitHandleSubmit={quitHandleSubmit}
onSubmitQuit={onSubmitQuit}
quitRegister={quitRegister}
userId={undefined}
/>
{isError && (
<Popup
title={AlertText.Failed}
popupcontrol={() => {
setIsError(false);
}}
handler={[
() => {
setIsError(false);
},
]}
/>
)}
</>
);
} else {
return (
<>
<SC.ExtraInfoLogo>
<Like className="stroke-deepgreen fill-deepgreen w-6 h-6" />
<SC.Additional>추가정보 입력</SC.Additional>
</SC.ExtraInfoLogo>
<FormBox
register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit}
location={location}
isDuplicated={isDuplicated}
setDuplicated={setDuplicated}
setZip={setZip}
errors={errors}
setError={setError}
setValue={setValue}
nicknameValue={nicknameValue}
quitErrors={quitErrors}
quitHandleSubmit={quitHandleSubmit}
onSubmitQuit={onSubmitQuit}
quitRegister={quitRegister}
userId={undefined}
/>
</>
);
}
중복을 많이 제거할 수 있었습니다..!!