파생된 상태, 함수로서의 존재

5

발행일: 2025년 05월 18일

독립적인 상태 원자(Atom). 다이시 카토의 머릿속을 사로잡은 이 매력적인 개념은 마치 어둠 속에서 길을 밝히는 등대와 같았다. 상태를 더 이상 쪼갤 수 없는 최소 단위로 분리하여 관리의 복잡성을 줄이고, 모듈성을 극대화한다. 아이디어 자체는 혁신적이고 우아했다.

하지만 카토는 알고 있었다. 현실 세계의 애플리케이션은 그렇게 단순하지 않다는 것을. 독립적인 원자들만으로는 모든 상태를 표현할 수 없었다.

‘문제는… 원자들의 관계야.’

그는 모니터 옆에 놓인 빈 종이에 간단한 예시를 그려보았다.

  • firstNameAtom = 'Daishi'
  • lastNameAtom = 'Kato'

이 두 개의 독립적인 원자는 존재한다. 하지만 사용자의 전체 이름(fullName)을 표시해야 한다면? 이 값은 명백히 firstNameAtomlastNameAtom에 의존하는 상태다. 두 원자의 값이 바뀌면 fullName도 당연히 바뀌어야 한다.

‘이런 파생된 상태(Derived State)는 어떻게 다루지?’

카토는 깊은 고민에 빠졌다. 만약 파생 상태를 위한 또 다른 종류의 ‘상태 저장소’를 만들어야 한다면, 결국 다시 복잡한 의존성 관리와 업데이트 로직의 늪에 빠지게 될 터였다. 그것은 원자적 접근 방식의 근본적인 매력을 퇴색시키는 일이었다. 상태 원자들의 독립성이라는 아름다운 원칙을 훼손하지 않으면서, 어떻게 이들의 관계를 자연스럽게 표현할 수 있을까?

그는 다양한 가능성을 떠올렸다. 복잡한 이벤트 리스너? 아니면 원자들 사이에 명시적인 연결 그래프를 구축해야 하나? 하지만 그런 방식들은 필연적으로 코드를 복잡하게 만들고, 개발자의 부담을 가중시킬 것 같았다.

‘더 단순한 방법… 더 함수적인 방법은 없을까?’

함수형 프로그래밍. 불변성, 순수 함수… 카토는 평소 자신이 추구하던 코드 스타일의 원칙들을 되짚어보았다. 상태 변경의 부작용(side effect)을 최소화하고, 예측 가능성을 높이는 방식.

순간, 그의 머릿속에 섬광이 내리꽂혔다. 마치 복잡하게 꼬인 매듭을 단번에 풀어내는 예리한 칼날처럼, 명쾌한 해답이 떠올랐다.

“파생 상태는… 그냥 함수(Function)잖아!”

그는 자신도 모르게 중얼거렸다. 그렇다! 파생 상태는 별도의 ‘상태’가 아니었다. 그것은 그저 다른 원자들을 입력(input)으로 받아 결과를 출력(output)하는 순수 함수일 뿐이었다!

fullNameAtomfirstNameAtomlastNameAtom을 읽어서(get), 두 문자열을 합친 새로운 문자열을 반환하는 계산 로직 그 자체인 것이다. 별도의 저장 공간도, 복잡한 업데이트 로직도 필요 없었다.

// 아토믹 모델의 파생 상태 구현 스케치
const firstNameAtom = atom('Daishi');
const lastNameAtom = atom('Kato');

// fullNameAtom은 상태를 저장하는 것이 아니라,
// 다른 아톰을 읽어(get) 값을 계산하는 함수 자체!
const fullNameAtom = atom((get) => {
  // 'get' 함수로 다른 아톰 값에 접근
  const firstName = get(firstNameAtom);
  const lastName = get(lastNameAtom);
  return `${firstName} ${lastName}`; // 계산된 값을 반환
});

“세상에…”

카토는 화면에 그려진 코드 스케치를 보며 전율했다. 이 얼마나 간결하고 아름다운가! 복잡한 의존성 관리나 상태 동기화 로직이 전혀 필요 없었다. 그저 어떤 원자들에 의존하는지만 함수 본문 안에서 get() 함수를 통해 명시해주면 끝이었다.

라이브러리는 이 get() 호출을 통해 자동으로 의존성을 파악할 수 있다. fullNameAtomfirstNameAtomlastNameAtom에 의존한다는 사실을 스스로 알아채는 것이다. 그리고 나중에 firstNameAtom이나 lastNameAtom의 값이 변경되면, 라이브러리는 알아서 fullNameAtom 함수를 다시 실행하여 새로운 값을 계산하고, 이 fullNameAtom을 사용하는 컴포넌트만 정확히 리렌더링해주면 된다.

자동 의존성 추적! 개발자는 더 이상 useMemouseCallback 같은 훅으로 수동적인 최적화를 할 필요가 없었다. 모든 것이 물 흐르듯 자연스럽게 처리될 터였다.

“이거야… 이게 진짜 상태 관리의 아름다움이야!”

Zustand의 셀렉터도 파생 상태를 다룰 수 있었지만, 그것은 여전히 거대한 상태 객체라는 컨텍스트 안에서 작동했다. 하지만 이 아토믹 모델에서의 파생 상태는, 그 자체로 완결된 하나의 함수이자 계산 로직이었다. 훨씬 더 선언적이고, 함수형 프로그래밍의 원칙에 부합하는 방식이었다.

독립적인 원자들, 그리고 그 원자들을 조합하여 새로운 값을 계산하는 순수 함수들. 이 두 가지 요소만으로 복잡한 상태 로직을 우아하게 표현할 수 있다는 확신이 들었다. 카토의 머릿속에서는 이미 이 아토믹 모델을 기반으로 한 새로운 상태 관리 라이브러리의 전체 그림이 그려지고 있었다.

그는 다시 키보드 위에 손을 올렸다. 심장이 터질 듯 뛰었다. 이제 이 아름다운 이론을 현실의 코드로 만들어낼 차례였다. 그의 손끝에서 새로운 역사가 쓰여질 준비를 하고 있었다. 그 역사의 첫 줄은, 곧 atom()이라는 이름의 함수로부터 시작될 터였다.