커스텀 훅 구현시 반환값은 어떻게 하는게 좋을까?

2022. 4. 14

자바스크립트 함수는 하나의 값을 반환한다.

최근에는 ES6 이상의 문법을 사용하게 되면서 하나의 값에서 구조 분해 할당을 통해 값을 해체하는 작업을 하여 하나의 값을 여러개의 변수에 담아 활용할 수 있게 되었다.

특히 리액트 훅이 등장하게 되면서 실제로 많이 구조 분해 할당을 활용하여 사용되고 있다. (물론 다른 곳에서도 많이 사용된다.)

최근에 여러 커스텀 훅의 개발과 코드들을 접하면서 개인적으로 반환값에 대한 궁금증이 생겼고, 평소에 이런 고민을 해보지 않았던 것 같아서 여러 아티클과 나름의 고민의 흔적을 공유하려 한다.

배열의 경우

대표적으로 useState가 있다.

const [count, setCount] = useState(0)

useState를 보통 사용할 때 배열 형태의 구조 분해 할당으로 상태 관련된 값을 변수에 담는다.

이는 사실 하나의 값(배열)이 반환되었지만, 구조 분해 할당을 통해 두 값을 별도의 변수에 담은 것이다.

만약 위처럼 구조 분해 할당을 하지 않았다면 아래와 같이 useState를 사용해야한다.

import { useState } from 'react'

export default function App() {
  const state = useState(0)

  const handleCountIncrease = () => {
    state[1]((count) => count + 1)
  }

  const handleCountDecrease = () => {
    state[1]((count) => count - 1)
  }

  return (
    <div className="App">
      <h1>count</h1>
      <h2>{state[0]}</h2>
      <button onClick={handleCountIncrease}>+</button>
      <button onClick={handleCountDecrease}>-</button>
    </div>
  )
}


bad

별거 아닌 것 같지만 생각보다 너무 읽기가 힘들다..

하지만 이를 구조 분해 할당을 하면?

import { useState } from 'react'

export default function App() {
  const [count, setCount] = useState(0)

  const handleCountIncrease = () => {
    setCount((count) => count + 1)
  }

  const handleCountDecrease = () => {
    setCount((count) => count - 1)
  }

  return (
    <div className="App">
      <h1>count</h1>
      <h2>{count}</h2>
      <button onClick={handleCountIncrease}>+</button>
      <button onClick={handleCountDecrease}>-</button>
    </div>
  )
}


cool

갑자기 편안- 해진다.

이처럼 자바스크립트의 반환값이 하나더라도 이를 어떻게 반환하고 구조 분해 할당 하냐에 따라서 사용감과 가독성이 확연히 차이가 나게된다.

객체의 경우

그러면 이러한 배열 사례 말고도 객체로 된 값도 있을 것이다.

실제 업무중에 사용 중인 특정 커스텀 훅을 예로들면 아래와 같다.

const {
  callGetNearByProductList,
  callGetAllSectionProductList,
  loadingNearBySection,
  loadingCommonSection,
  data,
  refetch,
} = useQuerySectionProductList()

위 커스텀 훅은 특정 섹션에 있는 상품들을 서버에 요청하고 반환을 받는 커스텀 훅이다.

특정 값이 담긴 변수, 요청 완료 여부, 재요청, 전체 데이터 등 엄청나게 많은 값이 하나의 객체 형태로 반환이 되었다. 물론 커스텀 훅을 추상화를 잘못 하거나 역할이 비대해진 이유도 있겠지만.. 😭

만약 어쩔 수 없는 상황이었다면 저 많은 값들을 가져오는 최적의 방법으로는 아마 객체 형태가 가장 편하다고 생각된다.

앞서 보았던 배열 형태의 구조 분해 할당의 경우를 보면, 반환에 대한 순서가 명시해야한다. 즉, useState의 반환값에 index가 있다는 의미다.

하지만 객체는 그렇지 않다.

구조 분해 할당시 배열처럼 특정 위치를 기억하지 않아도 되기 때문에 그에 대한 압박을 받지 않아도 된다.

const {
  loadingNearBySection,
  callGetNearByProductList,
  loadingCommonSection,
  data,
  refetch,
  callGetAllSectionProductList,
} = useQuerySectionProductList()

그리고 원하는 값만 구조 분해 할당해서 처리할 수도 있으며, 필요시 값 전체를 가져와 구조 분해 할당을 하지 않아도 된다.

const productDetailData = useQueryProductDetail()

return <Product {...productDetailData} />

이 처럼 특정 용도에 맞게 반환을 해주는 것이 개발함에 있어 한 번 고민해볼 필요가 있다.

결론

생각을 정리하자면.. 🤔

배열 반환의 경우

  • 반환할 값이 적을 때
  • 반환될 값이 추가될 여지가 없을 때
  • 하나의 컴포넌트에 여러개의 훅이 사용될 때

객체 반환의 경우

  • 반환할 값이 많을 때
  • 필요에 의해 부분적 혹은 전체로 사용될 여지가 있을 때
  • 하나의 컴포넌트에 단일로 사용될 때