현재 로그인 / 회원가입 페이지의 경우 <User />
라는 컴포넌트 하나로 관리되고 있고
라우팅에 따라 다른 화면이 나타나도록 구현되어 있었다.
그리고 크게 3개의 기능으로 이루어져 있는데
위의 기능을 포함한채 <User />
라는 로그인 회원가입 페이지의 공통 컴포넌트를 구현하다 보니
관련 기능을 구현한 내용이 한눈에 보이지 않는 단점이 나타났다
따라서 이전에 수정한 이력이 있는 유효성 검사를 제외한 두 가지 기능에 대해
custom hook
과 fetch함수의 분리
로 리팩토링을 진행하였다.
As-is
🔸 User.js
const initialUserInfo = {
email: '',
password: '',
userName: '',
phoneNumber: '',
};
const [userInfo, setUserInfo] = useState(initialUserInfo);
🔸 Input.js
const saveUserInfo = (e) => {
const { name, value } = e.target;
setUserInfo(prev => ({ ...prev, [name]: value }));
};
To-be
🔸 useUserInfo.js
import { useState } from 'react';
export const initialUserInfo = {
email: '',
password: '',
userName: '',
phoneNumber: '',
};
export const useUserInfo = () => {
const [userInfo, setUserInfo] = useState(initialUserInfo);
const saveUserInfo = e => {
const { name, value } = e.target;
setUserInfo(prev => ({ ...prev, [name]: value }));
};
return [userInfo, setUserInfo, saveUserInfo];
};
🔸 User.js
const [userInfo, setUserInfo, saveUserInfo] = useUserInfo();
userInfo
와 setUserInfo
, saveUserInfo
를 리턴해주는 Custom Hook
을 만들어 줌으로써
사용하는 쪽의 컴포넌트에서는 각각 필요한 값만 뽑아서 사용할 수 있도록 변경하였다.
<User />
컴포넌트의 코드가 1차적으로 단순하게 변하여 코드의 30%가량이 줄어들었다
As-is
🔸 User.js
const handleFormSubmit = e => {
e.preventDefault();
if (allVaild) {
let userData =
title === '로그인'
? {
email: userInfo.email,
password: userInfo.password,
}
: {
email: userInfo.email,
password: userInfo.password,
name: userInfo.userName,
phoneNumber: userInfo.phoneNumber,
};
let fetchUrl = title === '로그인' ? FETCH_SIGN_IN_API : FETCH_SIGN_UP_API;
fetch(fetchUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(userData),
})
.then(res => res.json())
.then(data => {
console.log(data);
if (title === '회원가입') {
alert('회원가입이 완료되었습니다!');
setUserInfo(initialUserInfo);
navigate('/login');
}
if (data.accessToken) {
alert('로그인이 완료되었습니다!');
localStorage.setItem('accessToken', data.accessToken);
navigate('/');
}
});
} else {
alert('Validation Error!');
}
};
To-be
🔸 config.js
import { FETCH_SIGN_IN_API, FETCH_SIGN_UP_API } from '../../../config';
export const fetchUser = async (title, userInfo) => {
let userData =
title === '로그인'
? {
email: userInfo.email,
password: userInfo.password,
}
: {
email: userInfo.email,
password: userInfo.password,
name: userInfo.userName,
phoneNumber: userInfo.phoneNumber,
};
let fetchUrl = title === '로그인' ? FETCH_SIGN_IN_API : FETCH_SIGN_UP_API;
try {
const res = await fetch(fetchUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(userData),
});
return res.json();
} catch (e) {
return e;
}
};
🔸 User.js
const handleFormSubmit = async e => {
e.preventDefault();
const allVaild = validCondition.email && validCondition.password;
if (!allVaild) {
alert('Validation Error!');
return;
}
const data = await fetchUser(title, userInfo);
if (data.accessToken) {
alert('로그인이 완료되었습니다!');
localStorage.setItem('accessToken', data.accessToken);
navigate('/');
}
if (data.message === 'SIGNUP_SUCCESS') {
alert('회원가입이 완료되었습니다!');
setUserInfo(initialUserInfo);
navigate('/login');
}
};
코드의 가독성을 위해 else문
에 해당하는 사항을 위쪽으로 끌어올려 예외상황에서 return문이 먼저 실행되도록 변경하였고, fetch
함수를 페이지 내에 별도의 config
파일로 저장하여 Promise 핸들링이 완료된 결과값만 받도록 코드를 간소화 시켜주었다.
마지막으로 allValid
조건은 위 함수에서만 사용하므로 안쪽으로 이동시켜 주었다.
이번 리팩토링은 코드가 쉽게 읽히는 것과 관심사의 분리 부분만 신경쓰며 진행하였지만
다음 번에는 리렌더링을 최소화하는 방향도 같이 확인해야겠다는 생각이 머리를 스친다.
우선 전체 코드량 감소 및 가독성이 증가하였으므로 만족하고 넘어가야겠다.
As-Is | To-Be |
---|