1. callbackRef

DOM의 노드사이즈를 구해서 상태에 저장해야하는 로직이 필요했었고 당연히 아래와 같이 썼었다. (이전에도 당연히 아래와 같이 사용하곤 했었음)

const SomeComponent = () => {
  const ref = useRef<HTMLElement>();
  const [height, setHeight] = useState();

  useEffect(() => {
    if (!ref.current) return;
    setHeight(getBoundingClientRect().height);
  }, [ref.current]);

  return <div ref={ref}></div>;
};

근데..늘 이런 코드를 작성 할 때 과연 ref 의 변경사항을 useEffect 가 잘 감지해서 트리거될까? 라는 의문을 떨칠수가 없었다. 당연히 ref 는 컴포넌트의 리렌더링을 시키는 요소가 아니기 때문이다. 또한, ref 를 붙이는 노드가 특정 조건에 따라 달라지게 되면 상태 업데이트가 제대로 되지 않는 문제점도 마주했었다. 그럴 때의 해법은 useEffect 의 디펜던시 배열을 없애고, useEffect 가 매리렌더링마다 실행되게 하는 것이라고 생각했었다.

따라서, 저 코드에서 디펜던시 배열에 ref.current 를 넣는건 의미가 없다. 해당 useEffect가 잘 실행되는 이유는 ref 의 변경을 감지해서가 아니라, useEffect 가 초기 렌더링 때는 무조건 실행되기 때문이다.

따라서, 간단한 컴포넌트 혹은 훅에서는 괜찮겠지만 복잡한 상태와 useEffect 가 엮여있을 경우 원하는대로 ref 의 값이 업데이트 되지 않을 수도 있다.

해결방법 - callbackRef 사용하기

Refs and the DOM

const SomeComponent = () => {
  const [height, setHeight] = useState();

  const ref = useCallback(() => {
    if (!node) return;
    setHeight(node.getBoundingClientRect().height);
  }, []);

  return <div ref={ref}></div>;
};

그럼 저 callback 함수는 언제 실행되나?

  • ref 가 노드에 붙여질 때 실행한다.
  • 다른 노드에 붙여져도 또 다시 콜백을 실행한다.

따라서

  • useEffect 를 사용하면, 컴포넌트의 첫 렌더링 시점을 기준으로 ref를 계산한다.
  • callbackRef를 사용하면, ref가 노드에 붙여질 때 ref를 계산한다. (붙여지는 노드가 변경되면 그걸 감지해서 해당 함수를 또 실행하게 됨)

또한, useCallback 의 디펜던시 배열을 빈배열로 둠으로써, 리렌더링시에도 참조값이 변경되지 않게끔 할 수 있다.


2. 마주한 리액트 에러 - Maximum update depth exceeded

에러 발생한 상황은 아래와 같다.

const { data: order } = useOrderQuery();

const {} = useSomething({
  items: order?.items ?? [],
});

// useSomething 훅 내부 useEffect
useEffect(() => {
  // ... 생략
}, [items]);
  • 여기에서 items 가 null일때 널리쉬 연산자로 주입한 [] 은 배열(참조형) 이라서 매번 새로운 값(주소가 다르므로)이라고 인식함 -> 그래서 useEffect 가 실행됨 -> 그런데 새로운값이 아님 -> 리액트가 머라구 함

    • 따라서 해당 컴포넌트 내 다른 값들 변경될 때에도 items 에 주입된 [ ] 가 새로운 값이라고 판단해서 계속 해당 훅이 리렌더링이 되었다.
  • 해결 방법

const { data: order } = useOrderQuery();

const items = useMemo(() => {
    if(!order) {
        return [];
    }
    return order.items.filter((item) => item.status === true);
 }

const { } = useSomething({
    items,
});
  • items 에 들어갈 배열을 useMemo 로 감싸준다.
  • 불필요한 리렌더링이 N번 일어날 경우, 새로운 참조값을 계속해서 인자로 넘겨주면, 해당 컴포넌트 혹은 훅이 N 번 리렌더링 된다. (내부에 useEffect 있으면 useEffect도 N번 리렌더링 된다)
  • useMemo, useCallback 을 왜 적재적소에 잘 써야하는지 다시 한번 더 깨달음

3. API 스키마 검증 레이어의 필요성

백엔드에서 받아오는 API 스키마를 프론트엔드에서 검증할 필요가 있을까?

내가 필요성을 느낀 경험들

  1. 백엔드 리소스 부족 🥲 -> 테스트 잘 못달아주심 -> 요상한 응답값 튀어나옴

    • 예전에는 어차피 백엔드에서 알아서 테스트하고 내려주는거니까 굳이 프론트에서 검증할 필요성을 못느꼈는데, 최근에 백엔드 리소스가 부족해지면서 테스트가 제대로 되지 않아서 명세 된 형식과 다른 응답값을 받은 적이 있음
    • 또, 내가 테스트로 잘못된 타입의 값을 mutate 요청 했는데, 고걸 백엔드에서 따로 검증처리 안하고 바로 저장해서 요상한 형태의 값이 날라온적도 있음
  2. 외부/타팀 API 연동

    • 커뮤니케이션이 직접적으로 발생하는 백엔드 API를 꽂는게 아니라 외부 혹은 타팀 API 를 연동해야할시에 필요했음. 그쪽에서 물론 릴리즈노트에 잘 작성해주겠지만, 그래도 일을 하다보면 그런 업데이트를 놓치는 경우가 파다함. 따라서 이 때 갑작스런 프론트측 런타임 에러를 방지하기 위해서 필요하다.
  3. 1번과 2번의 이유로 프론트의 에러 트랙킹 시간 감소를 위해

    • 갑자기 프론트 화면이 터지면 에브리바디 프론트엔드 개발자에게 물어본다! 그러면 프론트 개발자는 당황+압박감을 우선적으로 느끼게 되는데, API 스키마 검증을 하면 프론트에서 에러 트래킹 하기 더 수월해지지 않을까 하는 생각

어떻게 할까?

  • zod, joi 와 같은 라이브러리를 사용하자

그 외 좋았던 아티클

How To Engineer Kindness

Ask open-ended questions instead of making strong or opinionated statements. Being open-ended about your feedback, about what you’re wondering, gives people an opportunity to fill in that missing gap of understanding you might have. It gives them a better chance to explain The Why to you. This is about meeting them where they are to help them, not dragging them kicking and screaming to whatever “standard” the team has.

  • 코드리뷰를 할 때는 강압적인 태도보다 더 열린 질문을 해서, 리뷰이가 리뷰어에게 부족하게 설명한 것들을 채워나가게끔 만들기
  • 리뷰이도 그런 질문을 받으면서 자신이 놓쳤던 부분을 깨달을 수 있음

시니어를 꿈꿨던

  • 좋은 팀이 되기 위해서는 ‘시니어 개발자’가 아니라, ‘좋은 동료’가 필요하다. 재밌게 같이 문제를 풀어나갈 수 있는 동료! 그런 동료가 되기 위해서 노력하자

크고 복잡한 제품, 과감하게 갈아엎기

  • 유저 행동에 맞게 제품 쪼개기
  • 우리를 괴롭히는 운영 리소스 최소화하기
    • 모든 운영리소스를 한번에 끊어내는 것이 때로는 더 빠른 속도와 큰 효율을 가져올 수 있다.
  • 사용가치 중점으로 제품을 바라보기

마무리 + 일상

한동안 블로그를 잘 못썼었다. 꾸준히 아티클을 읽기는 했지만 정리를 못했었다. 반성흑흑. 그래도 아예 안하는 것 보다 가끔씩이라도 하는게 좋다고 생각한다. (가능하면 100점이 좋겠지만 0점보다는 70점이 낫지!)

요즘은, 일 할 때 요구사항 파악과 일정산정을 열심히 하려고 노력한다. 하지만 여전히 초기 요구사항을 보고 아주 머언 개발관점까지 파악하는 것은 쉽지 않은듯 🥲 열심히 해야겠당.

제텔카스텐 메모

1

요즘 옵시디언이라는 어플리케이션으로 메모를 한다. 메모 주제는 업무/개발/경제/그 외 단상 등등 다양함. 아주 잘 제텔카스텐식 으로 하고 있지는 않지만 그래도 아토믹 메모법을 지키려고 노력하고, 메모를 하다보니까 평소에는 쉽게 흘려보낼법한 생각들을 모래알 모으듯이 모아놓는 것 같아서 좋다. 언젠가 작은 모래알들이 모아져서 큰 모래사장이 되겠지😙

운동

2 달리기를 열심히 하는 중! 최근에 처음으로 10km를 뛰었다. 🥹 힘들었지만 뭔가 오늘은 될 것 같은데! 오늘 안하면 못할 것 같은데! 하는 마음으로 달리다보니 정말 해냈다.

주말에는 동네 수영장으로 자유수영도 갔다. 거의 10년만에 수영했는데 여전히 자유형과 배영의 영법을 기억하고 있었고, 나름 잘 떴음..!🫧 아무튼 요즘은 정말 열심히 생활체육인이 되어가는 중 이다🫡

독서

요즘은 재택하는 날에는 출근 전에 30분동안 독서하고, 잠들기 30분전에 독서하고 있다. 출근하는 날에는 지하철에서 독서를 하고 있다. 독서 자체가 주는 즐거움도 있고, 독서를 하다보면 집중력이 튼튼해진다.

벌써 9월이라니! 남은 2023년도 부지런하고 건강하게 보내쟈!