[번역] 새로운 리액트 문서에서 제시하는 9가지 권장 사항

eunbinn·2023년 12월 3일
198

FrontEnd 번역

목록 보기
26/38
post-thumbnail
post-custom-banner

출처: https://blog.testdouble.com/posts/2023-10-16-react-docs-recommendations/

일반적으로 새로운 소프트웨어 개발팀이 구성되면 팀이 코드를 정확히 어떻게 작성할 것인지에 대한 논의가 이루어집니다. 프로그래밍 언어나 주요 프레임워크의 메인테이너가 이에 대해 의견을 가지고 있다면 이러한 논의의 기본적인 출발점을 제공하는 데 도움이 될 수 있습니다.

리액트는 어떻게 코드를 써야하는 지에 대한 주장이 강하지 않은 것으로 알려져 있지만, 최신 리액트 문서에서는 어떻게 리액트 코드를 작성해야하는 지에 대해 꽤 많은 권장 사항을 제시하고 있습니다. 이 문서가 베타 버전으로 처음 출시된 이후, 다른 개발자들과 저는 팀에서 코드 스타일에 대해 대화할 때 이 문서를 기준으로서 점점 더 많이 참조해 왔습니다.

이 글에서는 리액트 코드 스타일에 대한 팀 토론에서 가장 자주 언급되는 리액트 문서의 권장사항을 이야기 합니다. 각각에 대해 간단한 요약과 코드 스니펫을 작성할 것입니다. 각 글에 포함된 링크를 따라 리액트 문서의 관련된 부분으로 이동하면 훨씬 더 많은 정보와 근거를 확인하실 수 있습니다.

이러한 권장 사항 중 일부는 중요하지 않은 코드 스타일에 대한 의견처럼 느껴질 수도 있습니다. 하지만 리액트 문서에서 설명하는 것처럼 권장 사항을 따르지 않는 것은 비용이나 위험이 따릅니다. 여러분은 자유롭게 결정할 수 있지만, 리액트 코어팀은 여러분이나 저보다 훨씬 더 많은 것을 생각했을 것임을 명심하세요. 물론 이것이 항상 옳다는 것을 의미하지는 않지만, 적어도 그들의 의견을 들어보는 것은 그만한 가치가 있을 것입니다.

1. 반복문에서 요소의 키를 선택할 때는 (배열 인덱스가 아닌) 동일한 항목에 대해 항상 동일한 값을 갖는 식별자를 사용하세요

리액트는 렌더링 전반에 걸쳐 리스트 요소를 추적하기 위해 키를 사용합니다. 요소가 추가, 삭제, 또는 순서가 변경되면 인덱스 키는 리액트가 잘못 추적하게 만들어 버그를 유발할 수 있습니다.

키에 대한 리액트 문서

// 🛑 잘못된 코드
return (
  <ul>
    {items.map((item, index) => (
      <li key={index}></li>
    ))}
  </ul>
);

// 🟢 올바른 코드, item.id가 안정적인 고유한 식별자라고 가정했을 때
return (
  <ul>
    {items.map((item, index) => (
      <li key={item.id}></li>
    ))}
  </ul>
);

2. 컴포넌트를 정의할 때는 다른 컴포넌트나 함수안에 중첩되지 않도록 하고 파일/모듈의 최상위 레벨에 정의하세요

때로는 다른 컴포넌트 안에 컴포넌트를 정의하는 것이 편리해 보일 수 있습니다. 하지만 이렇게 하면 렌더링할 때마다 컴포넌트가 재선언되어 성능이 저하될 수 있습니다.

컴포넌트 중첩에 대한 리액트 문서

// 🛑 잘못된 코드
function ParentComponent() {
  // ...
  function ChildComponent() {}

  return <div><ChildComponent /></div>;
}

// 🟢 올바른 코드
function ChildComponent() {}

function ParentComponent() {
  return <div><ChildComponent /></div>;
}

3. 상태에 무엇을 저장할지 결정할 때는 필요한 것을 계산하는 데 사용할 수 있는 최소한의 정보를 저장하세요

이렇게 하면 버그 발생 없이 상태를 쉽게 업데이트할 수 있습니다. 서로 다른 상태 항목이 서로 맞지 않거나 일관성이 떨어지는 것을 방지할 수 있습니다.

상태 구조화에 대한 리액트 문서

// 🛑 잘못된 코드
const [allItems, setAllItems] = useState([]);
const [urgentItems, setUrgentItems] = useState([]);

function handleSomeEvent(newItems) {
  setAllItems(newItems);
  setUrgentItems(newItems.filter((item) => item.priority === "urgent"));
}

// 🟢 올바른 코드
const [allItems, setAllItems] = useState([]);
const urgentItems = allItems.filter((item) => item.priority === "urgent");

function handleSomeEvent(newItems) {
  setAllItems(newItems);
}

4. useMemo, useCallback 혹은 React.memo를 사용하여 캐싱할지 여부를 고려한다면 성능 문제가 발견될 때까지 캐싱을 미루세요

항상 메모하는 것이 단점은 아니지만, 사소한 단점은 코드의 가독성이 떨어진다는 것입니다.

useMemo에 대한 리액트 문서, useCallback에 대한 리액트 문서, React.memo에 대한 리액트 문서에서 더 많은 정보를 확인하실 수 있습니다.

// 🛑 잘못된 코드
const [allItems, setAllItems] = useState([]);
const urgentItems = useMemo(
  () => (allItems.filter((item) => item.status === "urgent"), [allItems])
);

// 🟢 올바른 코드 (성능 문제가 발견되기 전까지)
const [allItems, setAllItems] = useState([]);
const urgentItems = allItems.filter((item) => item.priority === "urgent");

5. 공통된 코드를 함수로 추출할 때, 다른 훅을 호출하는 경우에만 훅으로 이름을 지정하세요

함수가 다른 훅을 호출하는 경우, 그 함수도 훅이어야 리액트의 훅 동작에 대한 제한을 적용할 수 있습니다. 함수가 다른 훅을 호출하지 않는다면 이러한 제한을 적용할 이유가 없습니다. 함수는 조건부 내부를 포함해 어디에서나 호출할 수 있기 때문에 훅이 아닐 때 더욱 다양하게 활용될 수 있습니다.

"use" 접두사에 대한 리액트 문서

// 🛑 잘못된 코드
function useDateColumnConfig() {
  // 훅 제한이 적용됩니다
  return {
    dataType: "date",
    formatter: prettyFormatDate,
    editorComponent: DateEditor,
  };
}

// 🟢 올바른 코드
function getDateColumnConfig() {
  // 어디에서나 호출할 수 있습니다
  return {
    dataType: "date",
    formatter: prettyFormatDate,
    editorComponent: DateEditor,
  };
}

function useNameColumnConfig() {
  // useTranslation 훅을 호출하기 때문에 훅이어야 합니다
  const { t } = useTranslation();
  return {
    dataType: "string",
    title: t("columns.name"),
  };
}

6. 프로퍼티 변경에 따라 상태를 조정해야 하는 경우 effect가 아닌 컴포넌트 함수에(렌더링 중에) 직접 상태를 설정하세요

프로퍼티 변경에 따라 상태를 조정하고자 한다면, 먼저 정말 필요한 것이 맞는지 확인하는 것이 좋습니다. 렌더링 중에 데이터를 도출하거나 (권장 사항 3 참조) 키를 사용하여 모든 상태를 초기화할 수 있다면 그렇게 하는 것이 더 좋습니다.

상태의 일부를 꼭 조정해야 한다면, 리액트 문서에서 effect에 대해 언급한 다음의 핵심 사항을 고려할 필요가 있습니다. "effect는 리액트 패러다임에서 벗어날 수 있는 탈출구로, 리액트 '외부로 나가서' 컴포넌트를 외부 시스템과 동기화 할 수 있습니다". 프로퍼티 변경에 대해 상태 업데이트만 수행하고자 한다면 이정도의 복잡성은 필요하지 않습니다.

프로퍼티로부터의 상태 변경에 대한 리액트 문서

아래 스니펫의 기반을 제공해준 리액트 문서의 예제 코드에 감사드립니다!

// 🛑 잘못된 코드
function List({ items }) {
  const [selection, setSelection] = useState(null);

  useEffect(() => {
    setSelection(null);
  }, [items]);
  //...
}

// 🟢 올바른 코드
function List({ items }) {
  const [prevItems, setPrevItems] = useState(items);
  const [selection, setSelection] = useState(null);

  if (items !== prevItems) {
    setPrevItems(items);
    setSelection(null);
  }
  //...
}

7. 데이터를 페칭해야 하는 경우, useEffect보다 라이브러리를 사용하는 것이 좋습니다.

useEffect로 데이터를 페칭할 경우 미세한 버그가 발생할 수 있고 이를 해결하기 위해서는 많은 양의 보일러 플레이트가 필요합니다. 리액트 문서에서는 좋은 데이터 페칭 라이브러리를 몇 가지 제안하고 있습니다.

데이터 페칭에 대한 리액트 문서

// 🛑 잘못된 코드
const [items, setItems] = useState();
useEffect(() => {
  api.loadItems().then((newItems) => setItems(newItems));
}, []);

// 🟢 올바른 코드 (하나의 라이브러리 사용 예시)
import { useQuery } from "@tanstack/react-query";

const { data: items } = useQuery(["items"], () => api.loadItems());

8. 이벤트 발생에 대한 응답으로 어떠한 액션을 취해야 하는 경우, useEffect가 아닌 이벤트 핸들러에 코드를 작성하세요

이렇게 하면 이벤트가 한 번 발생할 때 코드도 한 번만 실행됨을 보장할 수 있습니다.

effects vs 이벤트에 대한 리액트 문서를 읽어보거나 effects vs 이벤트에 대한 컨퍼런스를 확인해보세요.

const [savedData, setSavedData] = useState(null);
const [validationErrors, setValidationErrors] = useState(null);

// 🛑 잘못된 코드
useEffect(() => {
  if (savedData) {
    setValidationErrors(null);
  }
}, [savedData]);

function saveData() {
  const response = await api.save(data);
  setSavedData(response.data);
}

// 🟢 올바른 코드
async function saveData() {
  const response = await api.save(data);
  setSavedData(response.data);
  setValidationErrors(null);
}

9. useEffect 종속성으로 원치 않는 리렌더링(무한 루프 포함)이 발생하는 경우, 배열에서 종속성을 제거할 것이 아니라 effect 함수에서도 종속성을 제거하세요

이 변경을 하는 것이 왜 가치가 있는지 이해하기 어려울 수 있는데, 리액트 문서에서 useEffect에 대해 다룬 페이지를 읽어보시면 이해에 도움이 될 것입니다. 간단히 요약하자면, 의존성 배열에 나열하지 않은 의존성을 사용한다는 것은 effect가 의도한 목적이 아닌 다른 용도, 즉 동기화에 사용되고 있다는 의미일 수 있습니다. 이는 곧 찾아내기 어려운 버그로 이어질 가능성이 높습니다.

effect 종속성 제거에 대한 리액트 문서

실행에 옮기기

리액트 문서에 있는 이 요점들이 새로운 기술을 배우거나, 기술을 더 깊이 이해하거나, 다른 사람에게 기술을 설명하는 데 도움이 되기를 바랍니다. 리액트 프로젝트에서 이러한 권잔 사항을 따르고 계신가요? 리액트 문서에서 제안하는 다른 권장 사항 중 여러분께 자주 적용되는 다른 항목이 있으신가요?

post-custom-banner

0개의 댓글