개발일기장
[원티드 프리온 보딩] week 1 - 2
고래강이
2025. 1. 12. 20:47
💎 리팩토링
하는 시기
- 어떤 작업이 끝났을 때 기본적으로 한 번 다듬고 PR을 날리는 것이 좋다.
- UI와 비즈니스 로직을 떨어뜨리는 경우에 리팩토링하면 좋다.
UI와 비즈니스 로직
옛날에는 MVC pattern을 통해서 손 쉽게 비즈니스 로직을 분리시킬 수 있었다.(대신 복잡한 구조가 아니였다.) 이 때는 자연스럽게 페이지 일부만 변경하는 방식에서 javascript, jQuery, Ajax를 이용해서 클라이언트의 무언가를 변경시켰다. 이는 일부의 view만 변경하는 것이 굉장히 힘들었다. 이후 리액트에서 Component를 사용해서 좀 더 쉽게 view를 변경할 수 있게 되었으며 이를 CSR방식으로 상호작용하는 것으로 변경되었다. 이는 로직이 view에 녹기 너무 쉬워지는 단점이 있었다. 이렇게 비즈니스 로직이 view에 녹아있으면 한 눈에 보기 힘들며, 비즈니스 로직에서 수정해야할 부분이 생긴다면 모든 view를 찾아다니면서 수정을 해야하는 번거로움이 생기기에 비즈니스 로직이 view에 녹아 있는 것은 치명적인 단점이다.
- 응집도가 약해진다.(비즈니스 로직과 view가 분리가 되지 않는다.)
- 코드량이 많아진다.(중복 코드가 많아져서 수정해야할 부분과 관리해야할 부분이 많아진다.)
리팩토링 원칙
성능과 품질(클린 코드)을 개선하기 위해서 사용하며 주로 최적화를 해야하는 타이밍에 적용된다.
- SOLID: 경험이 많지 않을 경우에는 적용하기 힘들기에 참고하면 되지 무조건적으로 맞추려면 힘들다.
- DRY: 반복하지 말 것, KISS: 정말 간단히 할 것, GRASP: 일반적인 책임 할당 소프트웨어 패턴(단일책임 원칙), YAGNI:나중에 필요 없어질 것을 고려해라
- SOLID를 지킨다면 밑에 4개는 어느정도 따라온다.
최소한의 리팩토링 규칙 만들기
빡빡하게 처음부터 규칙을 짜지말고 점진적으로 규칙을 세워가자 그래야 결과가 나온다.
- 코드 중복을 피하자. (같은 코드 2번짜지 말자)
- 하나의 함수는 하나의 일만하게 만들자.
- 비슷한 기능을 가졌다면 한 곳에 모아두자.
- 간단하게 짜자
- 상황 별로 분리를 꼭 하자 (성공했을 경우와 실패했을 경우에 대한 내용을 가급적 분리를 해서 결과에 따라 어떤 행동인지 눈에 띄게 하자.)
⚙️ 추상화
- 어떠한 기능을 가진 Component를 만들었을 때 이 컴포넌트의 usage만 보고 어떤 것을 의미하는 지 명확하게 알 수 있다면 추상화가 낮은 것이다. 추상화가 높을수록 활용성이 떨어지기에 낮은 추상화를 구현하고, 정보를 전달하는 식으로 만들면 더 좋을 듯??
- 요구사항이 있을 때 추상화를 하는 것이 좋지 미리 앞서나가지 말라! -> 항상 내가 잘 못하는 부분이 듯
- 프로젝트에 상관 없는 내용을 추가하려고 시간을 낭비하면 안된다!
- 추상화를 할 때 객체를 전달하는 방식이나 children을 전달하는 방식 두 가지 중 하나로 통일하는 방식이 팀원들 간에 의사소통을 하는 데 있어서 좀 더 효율적이다.(추상화 방식을 통일해라)
예시를 보면서 서버 컴포넌트와 클라이언트 컴포넌트 구분해서 클라이언트 컴포넌트가 무거워지는 것이 마냥 좋은 것이 아님 이렇게 되면 page.tsx 자체에서 loginForm이 추상화 단계가 높게 되어 추측하기 힘들어지기에 각 기능에 맞게 단계를 나누는 것이 훨씬 보기 좋다.
에러바운더리를 통해서 에러를 처리하는 것은 단순히 fetch의 error만 식별하지 않고 나올 수 있는 다양한 case에 대항할 수 있기에 사용하는 것이 좋다.
상태관리 라이브러리 effector
UI와 비즈니스 로직을 떨어뜨리는 데 최적화된 솔루션을 제공하는 것이 목적인 상태관리 라이브러리
현재 나는 크게 쓸 일이 없을 것 같다.
🪨 내 코드 둘러보기
가장 최근에 내가 팀 프로젝트로 진행한 Dart라는 프로젝트가 있는데 여기서 내가 login 파트를 맡게 되어 비교해서 내가 어떻게 개선을 하면될지 한 번 둘러보겠다.
내 코드
import { LoginForm, LoginLayout, SocialButtons } from './components';
import * as S from './styles';
const LoginPage = () => {
return (
<S.Container>
<LoginLayout>
<LoginForm />
<SocialButtons />
</LoginLayout>
</S.Container>
);
};
export default LoginPage;
- 기본적으로 봤을 때 추상화가 제대로 되어있지 않다고 생각이 든다. 좀 더 명확하게 Component의 이름을 적어야겠다 생각
- Container와 Layout 두 개로 나눠진 것이 각자 뭘 의미할까? -> 코드를 봤을 때 의문이 듦
import { useForm } from 'react-hook-form';
import { Icon, InputField } from '@/components';
import useLogin from '../../hooks/usePostLogin';
import { LoginFormData } from '@/types/member';
import LoginLinkButton from '../loginLinkButton';
import { loginButtons, loginFormData } from '@/consts/login';
import * as S from './styles';
const LoginForm = () => {
const {
register,
formState: { errors },
handleSubmit,
} = useForm<LoginFormData>();
const { mutate: Login, isPending } = useLogin();
const onSubmit = (data: LoginFormData) => Login(data);
return (
<S.Container onSubmit={handleSubmit(onSubmit)}>
<div>
{loginFormData.map(({ label, registerOptions, value }) => (
<InputField
key={value}
register={register(value, registerOptions)}
label={label}
value={value}
error={errors[value]}
/>
))}
</div>
<S.ButtonBox>
{loginButtons.map((props, index) => (
<LoginLinkButton key={index} {...props} />
))}
<S.SubmitButton type="submit">
<Icon value="arrow" color="white" $active={!isPending} />
</S.SubmitButton>
</S.ButtonBox>
</S.Container>
);
};
export default LoginForm;
- 여기서는 나름 잘 구분되어있는 것이 비즈니스 로직과 UI로직의 분리가 잘 되어있다고 생각이 된다. 현재 페이지에서는 최대한 UI로직만을 구성하려고 노력하였고, useForm과 같은 경우에는 UI state이기에 따로 분리하지 않는 것이 낫다고 생각했다.
- 다만 실제 return되는 부분을 보면 살짝 바로 이해하기 힘든 부분도 있었기에 이 부분은 개선을 어떻게 하면될지 좀 더 생각해 보자.