개발일기장

[원티드 프리온 보딩] 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개는 어느정도 따라온다.

 

최소한의 리팩토링 규칙 만들기

빡빡하게 처음부터 규칙을 짜지말고 점진적으로 규칙을 세워가자 그래야 결과가 나온다.

  1. 코드 중복을 피하자. (같은 코드 2번짜지 말자)
  2. 하나의 함수는 하나의 일만하게 만들자.
  3. 비슷한 기능을 가졌다면 한 곳에 모아두자.
  4. 간단하게 짜자
  5. 상황 별로 분리를 꼭 하자 (성공했을 경우와 실패했을 경우에 대한 내용을 가급적 분리를 해서 결과에 따라 어떤 행동인지 눈에 띄게 하자.)

⚙️ 추상화

  • 어떠한 기능을 가진 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되는 부분을 보면 살짝 바로 이해하기 힘든 부분도 있었기에 이 부분은 개선을 어떻게 하면될지 좀 더 생각해 보자.