원티드 프리온보딩

1/6 프론트엔드 온보딩 인턴십 내용 정리.

jdy8739 2023. 1. 7. 11:10

1. 깨끗한 코드를 작성하는 법은?

깨끗한 코드를 작성할 때는 관심사의 분리를 뜻하는 SoC (Separation of Concerns)의 법칙을 실천하는 것이 매우 중요하다.

그렇다면 "관심사"란 무엇인가? “관심사"를 간단히 말하면 하나의 모듈이 수행하고자 하는 목적을 말한다.

여기서 모듈이란 함수, 클래스 등의 단위로 해석할 수 있다.

따라서, 관심사의 분리란 각 모듈들이 한번에 여러 관심사를 처리하려고 하지 않고, 하나의 관심사만 처리하도록 분리하는 것을 의미한다.

 

2. 그렇다면 React에서는 어떻게 관심사 분리가 이루어지는가?

과거 class 컴포넌트를 사용할 때에는 관심사의 분리를 위해 container & presentational 패턴을 주로 사용했다.

container 컴포넌트에는 UI의 상태와 이 상태를 변경하는 로직을 정의하고 presentational 컴포넌트에 이 요소들을 props로 전달한다. 그리고, presentational 컴포넌트에는 이 props들을 받아 UI들을 구성하여 사용자들에게 단순히 보여주기만 하는 역할을 수행한다.

이렇게 이 패턴에서는 데이터의 처리와 출력이라는 두 관심사로 컴포넌트를 분리하여 코드를 설계하는 방식이 과거에는 주로 쓰였다.

하지만, 현재 함수형 컴포넌트가 개발된 이후로 custom hook 방식이 React에서 관심사를 분리하는데 가장 많이 쓰이는 패턴이 되었다.

 

3. custom hook이란 무엇인가?

custom hook 패턴 역시 컴포넌트의 상태와 그것을 변경하는 로직(처리)을 UI 컴포넌트(출력)을 서로 분리한다는 점에서 container & presentational 패턴과 그 용도가 동일하다.

하지만, custom hook 패턴이 관심사를 분리하는 가장 널리쓰이는 패턴이 된 이유는 짚고 넘어갈 필요가 있다.

container & presentationa 패턴이 더이상 쓰이지 않는 이유는 class 컴포넌트에서 container & presentational 패턴을 사용할 경우 container 컴포넌트가 상태와 그 처리 로직을 presentational 컴포넌트에 전달하기 위해서는 꼭 render 메소드 내부에 이 요소들을 전달받을 컴포넌트를 명시하고 props 형태로 그것들을 내려주어야하기 때문이다.

// CommentListContainer.js
import React from "react";
import CommentList from "./CommentList";

class CommentListContainer extends React.Component {
  constructor() {
    super();
    this.state = { comments: [] };
  }

  componentDidMount() {
    fetch("/my-comments.json")
      .then(res => res.json())
      .then(comments => this.setState({ comments }));
  }

  render() {
    return <CommentList comments={this.state.comments} />;
  }
}

// 출저 https://kyounghwan01.github.io/blog/React/container-presenter-dessign-pattern/#gist-예제

위의 예시 코드블럭이 보여주듯이 return 메소드에서 해당 상태를 props로 전달받을 컴포넌트를 명시해주고 있다.

때문에, 이 방법은 container와 presentational의 역할을 하는 각 컴포넌트가 항상 강하게 결합될 수 밖에 없고 코드의 유연성을 크게 저하시킨다.

여기서 알 수 있는 사실은 class 컴포넌트는 자신이 가진 상태와 이것을 다루는 로직을 전달할 수 있는 방법이 render 메소드를 사용해 이 요소들을 다른 컴포넌트에 props로서 보내는 방식 외에는 방법이 없기 때문에 그 한계가 분명하다는 것이다.

그러나 함수형 컴포넌트를 이용한 custom hook 방식은 이 한계를 극복해내었다.

함수형 컴포넌트 방식에서는 함수에서 관리하고싶은 상태와 그 상태를 처리하는 로직을 정의하고 그것들을 단순히 반환(return)하여 컴포넌트에 전달할 수 있다.

이 방식은 class 컴포넌트처럼 이 요소들을 전달받을 컴포넌트를 명시하지 않고 단순히 반환하기 때문에 여러 컴포넌트에서 이 상태와 로직들을 사용할 수 있다.

즉, 결합성이 낮고 재사용성이 높아 container와 presentational 패턴보다 더욱 효율성이 높다.

이것이 custom hook 패턴이 React에서 관심사를 분리하는 주요 패턴이 된 가장 큰 이유라고 할 수 있다.

 

4. custom hook의 조건은 무엇인가?

1) 내부적으로 useState , useEffect를 사용한다.

2) 함수의 이름 앞에 'use'가 붙는다리액트 생태계의 암묵적인 약속이다.

 

5. custom hook을 잘 설계하는 방법?

직접 custom hook을 설계하여 사용한 경험이 없더라도, React를 사용한다면 다른 사람들이 만든 라이브러리를 다운받아 custom hook을 사용해 본 경험이 있을 것이다.

보통 custom hook이 반환하는 요소들은 하나의 배열이나 객체를 이루고 있기 때문에 우리는 그 custom hook이 반환하는 요소를 보통 destructuring 문법을 사용하여 변수에 할당한다.

그렇다면 어떤 경우에 배열을 사용하고 어떤 경우에 객체를 사용해 이 요소들을 반환해야 하는 것인가?

custom hook의 사용자가 반환값을 모두 사용할 수 밖에 없는 경우에는 반환값을 배열로 설계하는 것이 바람직하다.

배열에 요소들을 담아 반환한다면 이 요소들을 받아 destructuring하는 쪽에서도 custom hook 내부에서 요소들이 담긴 순서대로 반환값을 받을 수 밖에 없으며, 이 순서를 무시할 수 없기 때문에 상대적으로 배열의 앞쪽에 위치한 요소들은 컴포넌트 내부에서 사용될 일이 없더라도 무조건 변수에 할당될 수 밖에 없다.

const [isLightMode, _, changeMode] = useMode();

따라서, 사용되지 않을 hook 요소들은 위의 코드처럼 언더바 식별자에 할당하여 쓰이지 않을 것이라는 명시적 표현을 해주는 것이 바람직하다.

그렇다고 배열로 hook 요소들을 반환하는 것이 단점만 있는 것은 아닌데, 사용하는 컴포넌트에서 요소들을 hook 내부에서 정의된 식별자가 아닌 다른 식별자로 사용할 수 있다는 것이다.

즉, 요소들의 순서만이 중요할 뿐, 식별자의 이름은 지켜지지 않아도 무방하다.

반면, 객체로 hook의 요소들을 반환하면 식별자와 key값이 동일하여 그 식별자 그대로 컴포넌트에서 사용해야한다.

그러나, 배열과 다르게 그 순서는 전혀 상관이 없고 원하는 hook 요소들만 변수에 할당하여 사용이 가능하다는 이점이 있다.

 

6. custom hook은 어떻게 관리해야 하는가?

범용적으로 사용할 custom hook은 관심사에 맞게 별도의 폴더별로 분류하는것이 좋다.

반면, 한 컴포넌트만 사용될 custom hook은 굳이 그 컴포넌트와 분리를 하지 않아도 무방할 것이다.

 

7. 정리

위의 내용들을 정리하자면, 현재 React에서 관심사를 나누고 그 관심사를 표현하는 상태와 그 상태를 다루는 로직들은 custom hook 패턴을 사용해 만드는 것이 결합도와 재사용성 측면에서 컴포넌트를 설계하는 현재 가장 핫하고 베스트 케이스로 취급된다는 것이다.

그러나, custom hook을 사용한다는 것은 상태(state)와 그 상태를 다루는 로직(setState)을 다룬다는 것이기 때문에, 항상 불필요하고 의도하지 않은 재렌더링에 유의할 필요가 있다.

 

 

PS. 본 내용의 출저는 원티드 프리온보딩 프론트엔드 인턴십의 세션 내용입니다.