쓰레기 수집(Garbage Collection)의 미학
제9화
발행일: 2025년 05월 20일
Recoil과의 조우는 다이시 카토에게 새로운 자극과 영감을 주었지만, 동시에 또 다른 골치 아픈 문제를 수면 위로 떠오르게 만들었다. 그것은 바로 ‘메모리 관리’, 특히 더 이상 사용되지 않는 원자(atom)들을 어떻게 효율적으로 시스템에서 제거할 것인가 하는 문제였다.
“만들기만 해서는 안 돼… 사라지게도 만들어야지.”
그는 중얼거리며 빈 화면에 코드를 끄적였다. Jotai의 아토믹 모델은 상태 원자들이 동적으로 생성되고 소멸될 수 있다는 유연성을 전제로 했다. 예를 들어, 사용자가 특정 페이지에 진입했을 때만 필요한 원자를 로드하고, 페이지를 벗어나면 해당 원자를 더 이상 사용하지 않는 시나리오였다.
만약 이렇게 더 이상 아무도 참조하지 않는 ‘죽은 원자’들이 메모리 속에 계속 남아있다면? 처음에는 미미하겠지만, 애플리케이션이 오래 실행되거나 상태 변화가 빈번하게 일어나면 메모리 누수(Memory Leak)가 발생하여 시스템 전체를 느리게 만들거나 최악의 경우 다운시킬 수도 있었다. 마치 집 안에 쓰지 않는 물건들이 계속 쌓여 발 디딜 틈조차 없어지는 것과 같았다.
“이 ‘쓰레기’들을 어떻게 자동으로 청소하지?”
Zustand는 상대적으로 이 문제가 덜했다. 하나의 거대한 스토어 객체 안에서 상태를 관리했기 때문에, 가비지 컬렉션(Garbage Collection, GC)은 주로 자바스크립트 엔진의 영역이었다. 하지만 Jotai는 달랐다. 수많은 독립적인 원자들이 동적으로 생성되고, 서로 복잡한 의존성 그래프를 형성하며, 컴포넌트들과 연결되는 구조였기에. 라이브러리 자체적으로 이들의 생명 주기를 추적하고, 더 이상 필요 없어졌을 때 안전하게 메모리에서 해제하는 메커니즘이 필수적이었다.
단순히 참조 카운트(Reference Counting)를 세는 방식으로는 부족했다. 순환 참조 문제가 발생하면 영원히 메모리에 남게 될 수 있었다. 그렇다고 주기적으로 모든 원자를 검사하여 사용 여부를 확인하는 것은 성능에 큰 부담을 줄 터였다.
“자바스크립트 엔진의 GC처럼… 똑똑하게 처리할 수는 없을까?”
카토는 자바스크립트의 내부 동작 원리로 다시 눈을 돌렸다. 엔진은 어떻게 더 이상 접근할 수 없는 객체들을 식별하고 메모리를 회수하는가? 그의 시선이 ‘WeakMap’과 ‘WeakSet’이라는 조금은 생소한 객체 타입에 꽂혔다.
WeakMap/WeakSet은 일반적인 Map/Set과 달리, 키(key)로 사용된 객체에 대한 ‘약한 참조(weak reference)’를 유지한다. 이게 무슨 의미인가? 만약 WeakMap의 키로 사용된 객체를 참조하는 다른 코드가 하나도 없다면, 자바스크립트 엔진은 가비지 컬렉션 과정에서 해당 객체를 메모리에서 제거할 수 있다는 뜻이다! WeakMap 자체가 객체를 붙잡고 있지 않기 때문이다. 마치 끈으로 묶어두지 않고 살짝 올려놓기만 한 물건처럼, 아무도 찾지 않으면 자연스럽게 사라질 수 있는 것이다.
“유레카!”
카토는 무릎을 탁 쳤다. 바로 이거였다! Jotai 내부의 상태 저장소나 의존성 그래프를 관리하는 데 WeakMap을 활용하는 것이다. 원자 설정 객체 자체를 WeakMap의 키로 사용하면, 해당 원자를 참조하는 컴포넌트나 다른 파생 원자가 모두 사라졌을 때, 자바스크립트 엔진이 알아서 관련 데이터를 메모리에서 정리해줄 수 있는 가능성이 열리는 것이다!
물론 실제 구현은 훨씬 복잡했다. 원자의 상태 값 자체는 강한 참조로 유지해야 하고, 구독 관계나 의존성 정보 등을 WeakMap과 어떻게 효과적으로 조합하여 관리할지 정교한 설계가 필요했다. 자칫 잘못하면 필요한 데이터까지 사라져 버릴 수도 있었다.
그는 마치 시한폭탄을 해체하는 전문가처럼 신중하게 코드를 설계하고 테스트하기 시작했다. WeakMap을 이용하여 원자의 메타데이터와 구독자 목록을 관리하고, 상태 값 자체는 별도의 저장소에 보관하되, 원자가 더 이상 활성 상태가 아닐 때 해당 상태 값을 정리하는 로직을 추가했다. 자바스크립트 엔진의 GC 메커니즘과 최대한 협력하면서도, Jotai만의 영리한 메모리 관리 전략을 구축해 나갔다.
“작고 가벼워야 하니까.”
그는 끊임없이 되뇌었다. Jotai의 핵심 가치 중 하나는 경량성(Lightweight)이었다. 불필요한 메모리 사용은 이 가치를 훼손하는 가장 큰 적이었다. 그는 메모리 사용량을 측정하는 도구를 동원하여 자신의 코드가 실제로 효율적으로 작동하는지, 메모리 누수는 없는지 꼼꼼하게 검증했다.
쓰레기 수집. 개발자에게는 보이지 않는 영역에서 묵묵히 일하는 청소부와 같은 존재. 하지만 그 중요성은 이루 말할 수 없었다. 카토는 이 보이지 않는 영역의 ‘미학’에 집중했다. 단순히 기능을 구현하는 것을 넘어, 자원의 효율적인 사용과 시스템의 안정성까지 고려하는 깊이 있는 고민. 그것이야말로 진정한 장인정신이라고 그는 생각했다.
WeakMap과 GC 메커니즘을 활용한 영리한 메모리 관리 전략. 이것은 Jotai가 단순한 아이디어를 넘어, 실제 프로덕션 환경에서도 안정적으로 작동할 수 있는 견고한 기반을 다지는 중요한 발걸음이었다. 보이지 않는 거미줄을 관리하는 문제에 이어, 이제는 그 거미줄에서 더 이상 필요 없는 부분을 깔끔하게 제거하는 기술까지 연마한 것이다. Jotai는 점점 더 정교하고 강력한 존재로 진화하고 있었다. 그리고 다음 도전 과제는, 현대 웹의 필수 요소인 ‘비동기(Async)’의 세계였다.