시그널 시스템을 직접 구축하고 배운 점

발행: (2026년 6월 8일 AM 11:27 GMT+9)
13 분 소요
원문: Dev.to

Source: Dev.to

Recap

이 시점에서, Signals와 미세한 반응성에 대한 연재는 일시적으로 마무리됩니다.
이 글은 새로운 기술적 세부 사항을 소개하지 않을 것입니다. 대신, 전체 시리즈를 쓰면서 느낀 개인적인 성찰과 생각을 나누고자 합니다.

어느 정도 이 연재 전체는 Solid.js의 창시자 Ryan Carniato에게 영감을 받았습니다.
Ryan은 언제나 React 개발에서 우리가 겪는 고통점을 직접 짚어줍니다. 시니어 React 엔지니어인 저는 종종 이렇게 생각합니다.

“맞아, 바로 내가 매일 마주치는 문제다.”

Solid.js는 의심할 여지 없이 훌륭한 프레임워크이지만, 초보자에게는 학습 곡선이 가파를 수 있습니다.
그럼에도 제 입장에서는 Solid.js의 많은 설계 선택이 React가 취한 방향보다 더 합리적으로 느껴집니다. 예를 들어:

  • 의존성 배열이 없는 Effect
  • 마운트 로직과 정리 로직을 분리
  • Virtual DOM이 없는 JSX

이러한 것들은 React 커뮤니티에서 오래전부터 요구되었지만, 실제로는 크게 우선순위가 잡히지 않았습니다.

지난 몇 년간 React의 방향은 Next.js 생태계의 요구에 점점 더 영향을 받게 되었습니다.
Server Components는 중요한 아키텍처 변화를 가져왔지만, 동시에 많은 프론트엔드 엔지니어가 일상적인 애플리케이션 개발에서 아직 미성숙하다고 느끼는 모델을 채택하도록 만들었습니다.

분명히 말하자면, Server Components가 쓸모 없다는 것이 아닙니다. 서버‑사이드 데이터 접근, 스트리밍, 클라이언트‑사이드 JavaScript 감소와 같은 실제 문제들을 해결합니다. 하지만 프론트엔드 개발자의 관점에서는 React의 사고 모델을 크게 복잡하게 만들기도 합니다.

솔직히 말해, 저는 가끔 “Server Components 없는 Next.js” 시절이 그리울 때가 있습니다. 그때 React는 더 단순했죠. 문제는 명확했지만, 경계가 이해하기 쉬웠고, 스스로 해결책을 선택할 자유도 더 많았습니다.

커뮤니티에서는 “React가 signals를 도입해야 할까?” 혹은 “Virtual DOM 없이 React를 탐구해야 할까?” 같은 아이디어가 제시되곤 합니다. 이러한 아이디어는 현재 React와 Next.js의 제품 방향 및 인센티브와 자연스럽게 맞물리지는 않지만, 여전히 진지하게 논의할 가치가 있다고 생각합니다.

제가 말하고 싶은 것은 React가 틀렸다는 것이 아니라 Virtual DOM이 쓸모 없다는 것이 아닙니다.
Virtual DOM은 여전히 많은 상황에서 합리적인 추상화입니다. 예를 들어 React Native와 같은 크로스‑플랫폼 환경에서는 선언형 UI 모델과 다양한 렌더링 타깃 사이의 실용적인 절충안을 제공합니다.

제가 강조하고 싶은 점은 더 좁습니다:

Virtual DOM이 틀린 것은 아니다. 다만 이제는 프론트엔드 아키텍처의 유일한 중심이 될 필요가 없다는 것이다.

문제가 미세한 데이터 의존성, 업데이트 정밀도, 스케줄러 정확도일 때, signals는 충분히 고려할 만한 다른 모델을 제공합니다.

오늘날 저는 React 생태계가 더 갈등적인 단계에 들어섰다고 봅니다. TanStack 같은 프로젝트가 개발자 경험을 크게 개선하고 있지만, 앞으로 성숙한 대안이 등장한다면 기본적으로 Next.js를 선택하지 않을 수도 있습니다.

커뮤니티에서는 가끔 Jotai—또는 원자 상태 모델 전체—가 사실상 signals와 동일하다고 말합니다. 일부는 프레임워크 생태계에 더 가깝게 구축돼 있어 개발자 친화적이라고 주장하기도 합니다.

하지만 이 시리즈를 따라가며 개념을 단계별로 구현해 보았거나, 직접 반응형 시스템을 만든 경험이 있다면 두 접근 방식이 매우 다른 설계 철학에 기반한다는 것을 알 수 있습니다.

핵심 차이는 시스템이 의존성을 어떻게 다루는가에 있습니다.

아래는 간단한 개념 비교입니다.

Atomic state

  • 핵심 아이디어: 상태를 가능한 가장 작은 단위(보통 state atom)로 나눈다. 이들 사이에 암묵적인 의존성 추적은 존재하지 않는다.
  • 구성 모델: selector 혹은 derived function을 사용해 로직을 조직하고 상태를 조합한다.
  • 업데이트 모델: 상태가 변하면 시스템이 자동으로 하위 종속자를 알리지 않는다. 대신 selector가 순수 함수로 다시 실행된다.
  • 특징
    • 함수형으로 이해하기 쉬움: 상태는 값이고 selector는 순수 함수이다.
    • 그러나 의존성 그래프가 명시적이지 않다. 모든 읽기가 재계산을 요구할 수 있어 중복 계산이 발생한다.

Signals

  • 핵심 아이디어: signals도 미세한 상태 단위이지만, 명시적인 의존성 추적 그래프를 포함한다.
  • 구성 모델: computed와 effect는 실행 중에 자동으로 의존성을 등록한다. 이후 원본 값이 바뀌면 하위 구독자에게 자동으로 알린다.
  • 업데이트 모델: 보통 푸시 기반이다. 원본 값이 바뀌면 하위 노드가 ‘stale’ 상태가 되고, 스케줄러가 언제 업데이트를 실행할지 결정한다.
  • 특징
    • 런타임이 누가 누구에게 의존하는지 알기 때문에 불필요한 재계산을 피한다.
    • 업데이트 전파가 더 빠르고 정확하다.
    • 의존성 그래프를 런타임이 관리해야 한다.

한 문장 요약

  • Atomic state: 상태를 쪼개지만 의존성을 깊게 관리하지 않는다. 시스템은 함수를 통해 “다시 계산”해야 한다.
  • Signals: 상태를 쪼개고 의존성 추적을 추가한다. 시스템이 누가 업데이트돼야 하는지 안다.

의존성을 다루지 않으면 모델을 쓰고 이해하기는 더 쉬워집니다. Jotai 역시 프레임워크 자체의 상태 모델에 의존하기 때문에 프레임워크 라이프사이클과 자연스럽게 맞물려 동기화 문제를 많이 회피합니다.
하지만 그 대가로 이 모델은 프레임워크 재렌더링에 의존해 정확성을 보장해야 합니다. 반면 signals는 런타임 수준의 의존성 추적을 통해 더 미세한 업데이트를 구현할 수 있습니다.

또 다른 흔한 비교는 signals(특히 Vue나 React와 결합된 경우)를 전통적인 OOP 세계의 Dependency Injection (DI) 과 비슷하게 보는 것입니다.
겉보기엔 비슷해 보이지만 본질적인 차이는 다음과 같습니다.

  • DI는 객체 의존성의 구성 및 관리를 해결한다.
  • Signals데이터 의존성의 추적 및 업데이트 시점을 해결한다.

표면적으로는 비슷해 보이지만, 해결하고자 하는 문제 차원이 완전히 다릅니다.

DISignals
답하는 질문“어떤 객체가 어떤 서비스를 필요로 하는가?”“어떤 데이터가 다른 데이터에 의존하는가?”
시점구성 시점런타임
키워드의존 관계, 객체 구성데이터 추적, 상태 업데이트 전파
예시class Car { constructor(private engine: Engine) {} }const totalPrice = computed(() => count.get() * unitPrice.get());

DI는 객체가 필요한 서비스를 얻도록 도와주고, signals는 데이터가 변할 때 누가 업데이트돼야 하는지를 시스템에 알려줍니다.

많은 사람들이 signals를 또 다른 형태의 state management라고 생각합니다. 하지만 이번 시리즈에서 구현 세부 사항을 살펴보면 signals가 단순히 상태 관리 그 이상을 다루고 있음을 알 수 있습니다.

  • State management 라이브러리는 주로 상태가 어디에 존재하고 어떻게 업데이트되는지를 조직하는 데 도움을 줍니다.
  • Signal system은 한 단계 더 깊이 들어가서 다음을 묻습니다.
    • 데이터 의존성은 어떻게 추적되는가?
    • 무효화는 어떻게 전파되는가?
    • 파생 값은 언제 재계산되는가?
    • Effect는 언제 실행되는가?
    • 스케줄러는 일관성을 어떻게 유지하는가?

따라서 signals를 “그냥 또 다른 상태 관리 도구”로 축소하면 핵심을 놓치는 셈입니다.

signals가 도전하는 것은 state management 자체가 아니라, 오랫동안 Virtual DOM이 프론트엔드 아키텍처의 중심이어야 한다는 가정입니다.
이는 Virtual DOM이 가치가 없다는 뜻이 아니라, UI 개발을 위한 유일한 정당한 사고 모델이 아니라 여러 아키텍처 트레이드오프 중 하나로 바라봐야 한다는 의미입니다.

기술적 진화는 결코 코드만의 문제가 아닙니다. 인센티브, 기존 생태계, 그리고 사람들의 사고 방식까지 모두 얽혀 있습니다.


class Engine {}

class Car {
  constructor(private engine: Engine) {}
}
0 조회
Back to Blog

관련 글

더 보기 »