“하지만 이게 전부가 아닙니다.”
댄의 목소리가 장내에 울려 퍼졌다. 청중은 이미 useState와 useEffect의 마법에 매료되어 있었지만, 그는 더 보여줄 것이 남아있다는 듯 자신감에 차 있었다.
“우리가 이 여정을 시작했던 첫 번째 이유를 기억하십니까? 바로 ‘상태 로직의 재사용’이었습니다. 만약 다른 컴포넌트에서도 친구의 온라인 상태를 확인하고 싶다면 어떻게 해야 할까요?”
객석의 개발자들은 본능적으로 HOC나 렌더 프롭을 떠올렸다. 그것이 그들이 알고 있는 유일한 해답이었기 때문이다.
“과거에는 HOC나 렌더 프롭을 사용했겠죠. 하지만 이제, 우리에겐 더 좋은 방법이 있습니다.”
댄의 손가락이 다시 키보드로 향했다.
그는 방금 작성했던 FriendStatus 컴포넌트에서, useState와 useEffect 로직 덩어리를 그대로 잘라냈다.
그리고 컴포넌트 바깥에, 새로운 함수를 만들기 시작했다.
그는 use라는 접두사를 붙여 함수의 이름을 지었다.
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
}, [friendID]);
return isOnline;
}
그는 잘라냈던 로직을 이 새로운 함수 안에 그대로 붙여넣고, 최종 결과물인 isOnline 값을 반환하게 했다. useFriendStatus라는, 그가 ‘커스텀 훅’이라고 명명한 새로운 존재가 탄생하는 순간이었다.
이제, 그는 원래의 FriendStatus 컴포넌트와, 온라인 상태 표시가 필요한 또 다른 컴포넌트 FriendListItem을 믿을 수 없을 만큼 간단하게 수정했다.
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return <li style={{ color: isOnline ? 'green' : 'black' }}>{props.friend.name}</li>;
}
장내에는 완전한 침묵이 흘렀다.
수천 명의 개발자들이 자신의 눈을 의심했다.
const isOnline = useFriendStatus(props.friend.id);
단 한 줄.
저 한 줄의 코드가, 그들을 그토록 괴롭혔던 상태 로직 재사용 문제를 해결하고 있었다.
HOC의 래퍼 지옥도, 렌더 프롭의 피라미드도 없었다. 마치 평범한 유틸리티 함수를 호출하듯, 상태와 부수 효과를 포함한 복잡한 로직 덩어리를 통째로 가져다 쓰고 있었다.
이것은 마법이었다.
아니, 마법처럼 보이는 완벽한 공학이었다.
객석의 한 개발자가 자신도 모르게 나지막이 탄성을 내뱉었다.
“세상에… 저게 가능하다고?”
그의 중얼거림은 도화선이 되었다.
장내 곳곳에서 믿을 수 없다는 탄성과 수군거림이 파도처럼 번져나가기 시작했다. 개발자들은 HOC와 렌더 프롭의 악몽이 주마등처럼 스쳐 지나가는 듯한 표정으로, 스크린 위의 저 간결한 코드를 멍하니 바라보고 있었다.
댄은 키보드에서 손을 떼고, 청중의 반응을 조용히 지켜보았다.
그들의 표정에서, 그는 자신들의 승리를 예감했다.
이것은 단순한 기능 소개가 아니었다.
이것은 리액트 개발의 패러다임이 전환되는, 역사적인 순간이었다. 그리고 그 중심에, 댄 아브라모프가 서 있었다.


