✏️/React

[원티드 프리온보딩 챌린지] 그동안 내 코드에 없었던 것들

naoo 2022. 8. 15. 19:13

*원티드 프리온보딩 8월 챌린지 강연을 1주차를 듣고 정리한 글 입니다.

생각해보고 코드를 짰나요?

- 선언형과 명령형은 뭐가 다를까?

- 코드의 추상화와 구체화 

- 관심사의 분리

 

창피하게도 그동안의 나는 코드를 짤 때 1. 기능 구현 우선 -> 2. 그다음에 리팩토링의 순서대로 진행을 했었다. 처음에 폴더 구조와 컴포넌트 분리에 대해 설계를 하더라도 중간에 수정되는 일은 빈번했고 당연히 선언형? 추상화?에 대한 고민은 해본 적도 없고 무지했다.

(이래서 공부를 끝 없이 해야하나 보다)

 

대충 어떤 개념인지, 왜 이렇게 가야 하는지는 알겠어도 아직 제대로 이해하지 못한 영역들이 많다. 

다행히 이에 대해선 너무 좋은 글과 정보가 많기 때문에 조금이라도 공부한 것을 남겨본다. 

 

1. What과 How ?

먼저 명령형 프로그래밍선언형 프로그래밍에 대해 어느 정도 알고 있는가? 나는 모른다...

간단히 말하자면 명령형은 특정한 동작을 어떻게(How) 달성할 것인지에 집중하고, 선언형무엇을(What) 할지에 집중한다.

예제를 보고 이해해 보자

pov - 택시를 탔을 때 택시기사에게 하는 말

명령형(HOW)  : 직진해서 3번째 블록에서 우회전하신 다음 이마트까지 쭉 직진한 두 번째 신호등에서 좌회전한 다음 2번째 골목으로 들어가주세요. 
선언형(WHAT) : oo동 ㅁㅁ아파트로 가주세요~

명령형 방식은 "어떻게" 집에 갈지에 대한 단계를 하나하나 나열한다. 그와 정 반대로 선언형 방식은 "무엇을" 원하는지에 대해 더 집중하고 있다. 

그렇다면 리액트의 문법인 Jsx와 바닐라JS와 비교해 보자 

// 1. JSX
const App = () => <div>Hello World!</div>

//2. Vanilla JS
const hello = dovument.creatElement("div")
hello.innerText = "Hello World!"
body.appendChild(hello)

참고 : https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction

리액트에서 선언적 방식으로 UI를 만들 수 있는 점은 큰 장점으로 꼽힌다. 

 

React는 상호작용이 많은 UI를 만들 때 생기는 어려움을 줄여줍니다.
애플리케이션의 각 상태에 대한 간단한 뷰만 설계하세요.
그럼 React는 데이터가 변경됨에 따라 적절한 컴포넌트만 효율적으로 갱신하고 렌더링합니다.

선언형 뷰는 코드를 예측 가능하고 디버그하기 쉽게 만들어 줍니다.
- React 공식문서 -

선언형 프로그래밍은 다른 사람이 봐도 어떤 것을(What)을 추구하는지 알 수 있도록 이름을 짓고, 추상화하고, 인터페이스를 설계하는 방식이다.  특정 부분은 명령형으로 작성할 수 있겠지만, 사용자 측에서는 가급적 무엇을 하고 싶은지만 신경 쓸 수 있도록 하면 보다 직관적인 코드를 작성할 수 있다. 

 

참고하기 좋은 글

https://jbee.io/react/error-declarative-handling-1/#usage

 

React에서 선언적으로 비동기 다루기

에러를 효율적으로 다루기 위해 선언적으로 에러를 정의하고 처리하는 방법을 고민했고 그 결과물을 공유합니다. Suspense와 ErrorBoundary를 사용하여 비동기 컴포넌트를 보다 효율적으로 처리하는

jbee.io

 

2.  적절한 추상화

프로그래밍에는 DRY(Don't Repeat Yourself)라는 원칙이 있다.

중복 코드를 만들지 말라는 뜻인데, 코드의 중복을 줄이면 타이핑하는 코드가 줄어든다는 직관적인 장점뿐만 아니라, 로직의 관리 포인트가 줄어들어 유지 보수 측면에서도 장점이 있다. 

일단 더러운.. 나의 코드를 리팩토링해보자

export const getTodos = () => {
  const token = localStorage.getItem("token")
    ? `Bearer ${localStorage.getItem("token")}`
    : undefined;
  const headers = token
    ? { headers: { Authorization: token, "Content-Type": "application/json" } }
    : undefined;
  return axios.get(`${BASE_URL}/todos`, headers);
};

export const getTodoById = (id: string) => {
  const token = localStorage.getItem("Authorization")
    ? `Bearer ${localStorage.getItem("Authorization")}`
    : undefined;
  const headers = token
    ? { headers: { Authorization: token, "Content-Type": "application/json" } }
    : undefined;
  return axios
    .get(`${BASE_URL}/todos/${id}`, headers)
    .then((res) => console.log(res))
    .catch((res) => console.log(res));
};

이번에 과제에 작성한 api 호출을 모아둔 파일이다.  딱 보기에도 중복된 코드가 많다. 호출 과정에서 전부 token이 있는지 확인하고 헤더에 넣어주는 작업을 넣다 보니 6줄이나 되는 코드들이 계속 중복되었다.

getTodoById: async ({
    toDoId,
    authToken,
  }: IdAndToken): Promise<{ data: ToDoData }> => {
    const { data } = await baseApi.get<{ data: ToDoData }>(
      `/todos/${toDoId}`,
      {
        headers: {
          Authorization: authToken,
        },
      }
    );
    return data;
  },

url 호출을 create.axios로 빼주고 authToken을 따로 생성해서 간결하게 줄였다. 

간혹 공통된 로직을 추출하다 보면 어떤 부분은 추상화 되기도하는데, 이럴 때 단일 책임 원칙을 따르라는 말을 듣게 된다. 

하나의 함수가 한 가지 일을 하도록 코드를 작성하라는 뜻이다. 

하지만 주의해야 할 점은 <클린 아키텍처> 의 저자 로버트 C.마틴은 단일 책임 원칙이 '함수는 하나의 일만 해야 한다' 가 아닌 '하나의 모듈(소스코드)은 오직 하나의 액터(사용자 혹은 이해관계자)에 대해서만 책임져야 한다' 라고 강조한다. 

// 적용 전 - 1) 수정하는 사용자, 2) 등록하는 사용자가 하나의 함수를 사용한다
const getInitialData = (type: 'edit' | 'register') => {
  if (type ==== 'edit') {
    // 수정 시에 필요한 초기 데이터를 불러오는 로직
  } else {
    // 등록 시에 필요한 초기 데이터를 불러오는 로직
  }
};
// 적용 후 - 1) 수정하는 사용자, 2) 등록하는 사용자가 서로 다른 함수를 사용한다
const getInitialDataForEdit = () => {
  // 수정 시에 필요한 초기 데이터를 불러오는 로직
};
const getInitialDataForRegister = () => {
  // 등록 시에 필요한 초기 데이터를 불러오는 로직
};

3. 관심사 분리 

'한 번에 한 가지만 걱정해도 괜찮도록' 각각의 관심사에 따라 코드를 분리하는 기법을 관심사의 분리 라고 한다.

관심사의 분리를 달성할 수 있는 대표적인 예로는 보통 유저 인터페이스(View)비지니스(Business, Domain)로직의 분리를 들 수 있다. 이 두 개를 분리하기에 기준은 사람마다 다른 이견을 가지고 있고 자신이 정하기 나름이기 때문에 각자의 기준을 세우는 것이 중요하다. 

 

이에 대해 참고하기 좋은 글

https://velog.io/@teo/MVI-Architecture

 

프론트엔드에서 비즈니스 로직과 뷰 로직 분리하기 (feat. MVI 아키텍쳐)

오늘 해볼 이야기는 상태 관리, 비즈니스와 뷰 로직의 분리 프론트엔드 개발의 구조 등 프론트엔드의 아키텍쳐에 대한 이야기입니다. 프론트엔드를 하다보면 많이들 물어보는 (저 역시 지금도

velog.io

 

 

글을 정리하다 보니 결론적으로는 전부 같은 맥락과 목표를 가지고 있는 개념들이라고 느꼈다. 

기능이 구현되는 것도 중요하지만 궁극적으로 목표해야 할 지점에 대해 감이 잘 안 잡혔는데 그동안 내가 놓친 점과 고민해야 할 방향을 제시한 것 같아 많은 깨달음을 얻었던 시간이었다.