Articlestack.convex.dev·2026년 7월 8일·0

Why Convex Queries are the Ultimate Form of Derived State

Quick Summary

이 글은 파생 상태를 로컬 React 계산에서 서버 기반 다중 클라이언트 동기화 문제로 확장해 설명하고, Convex 쿼리가 반응형 데이터베이스 위에서 프런트엔드가 필요한 파생 상태를 항상 최신으로 제공하는 방식이라고 주장한다.

Why Convex Queries are the Ultimate Form of Derived State 관련 대표 이미지

🖼️ 인포그래픽

Why Convex Queries are the Ultimate Form of Derived State 내용을 설명하는 본문 이미지

🖼️ 4컷 인포그래픽

Why Convex Queries are the Ultimate Form of Derived State 내용을 설명하는 본문 이미지

💡 한 줄 요약

이 글은 파생 상태를 로컬 React 계산에서 서버 기반 다중 클라이언트 동기화 문제로 확장해 설명하고, Convex 쿼리가 반응형 데이터베이스 위에서 프런트엔드가 필요한 파생 상태를 항상 최신으로 제공하는 방식이라고 주장한다.

📌 핵심 요약

  • 저자는 파생 상태를 프로그래밍에서 매우 강력하지만 충분히 평가받지 못하는 개념으로 소개하며, 단순 변수에서 출발해 이름과 나이로 메시지를 만드는 예시로 개념을 설명한다.
  • React 컴포넌트에서는 상태가 바뀌면 렌더링 결과라는 파생 상태가 자동으로 최신화되지만, 이 구조는 기본적으로 한 사용자의 로컬 상태를 다룰 때 가장 자연스럽게 작동한다.
  • 상태를 여러 클라이언트가 공유하려면 서버로 옮겨야 하며, 단순 fetch나 페이지 reload 방식은 다른 클라이언트의 변경을 즉시 반영하지 못해 동기화 문제가 생긴다.
  • Convex는 스키마, 쿼리, 뮤테이션을 통해 서버에 저장된 상태와 그로부터 계산된 파생 상태를 다루며, React 훅인 useQuery와 useMutation으로 클라이언트가 자동 갱신을 받을 수 있게 한다.
  • 저자는 Convex 쿼리가 특정 필터 조건과 여러 테이블 조합까지 반응형으로 추적할 수 있기 때문에, 프런트엔드에 필요한 서버 파생 상태를 만드는 강력한 backend-for-frontend 방식이라고 결론짓는다.

🧩 주요 포인트

  1. 저자는 파생 상태를 프로그래밍에서 매우 강력하지만 충분히 평가받지 못하는 개념으로 소개하며, 단순 변수에서 출발해 이름과 나이로 메시지를 만드는 예시로 개념을 설명한다.
  2. React 컴포넌트에서는 상태가 바뀌면 렌더링 결과라는 파생 상태가 자동으로 최신화되지만, 이 구조는 기본적으로 한 사용자의 로컬 상태를 다룰 때 가장 자연스럽게 작동한다.
  3. 상태를 여러 클라이언트가 공유하려면 서버로 옮겨야 하며, 단순 fetch나 페이지 reload 방식은 다른 클라이언트의 변경을 즉시 반영하지 못해 동기화 문제가 생긴다.
  4. Convex는 스키마, 쿼리, 뮤테이션을 통해 서버에 저장된 상태와 그로부터 계산된 파생 상태를 다루며, React 훅인 useQuery와 useMutation으로 클라이언트가 자동 갱신을 받을 수 있게 한다.
  5. 저자는 Convex 쿼리가 특정 필터 조건과 여러 테이블 조합까지 반응형으로 추적할 수 있기 때문에, 프런트엔드에 필요한 서버 파생 상태를 만드는 강력한 backend-for-frontend 방식이라고 결론짓는다.

🧠 상세 정리

1. 파생 상태의 출발점: 원본 상태를 바꾸지 않고 다른 형태로 계산하기

저자는 먼저 상태라는 개념을 아주 단순한 변수 예시로 설명한다. name이라는 값은 직접 다른 값으로 바꿀 수 있는 원본 상태이고, 여기에 age를 더하면 두 값을 조합해 message라는 값을 만들 수 있다. 이 message는 name과 age에서 계산된 값이므로 파생 상태에 해당한다. 핵심은 원본 상태를 그대로 두면서도 그 상태를 다른 표현이나 관계로 바꿔 사용할 수 있다는 점이다. 저자는 이 단순한 예시를 통해 이후 React와 서버, Convex로 이어지는 논의를 위한 기본 구조를 잡는다.

2. 함수와 React 컴포넌트에서 반복되는 파생 상태 패턴

원본 상태인 name과 age가 계속 바뀔 수 있다면, message도 그 관계를 유지하며 새로 계산되어야 한다. 이를 위해 저자는 getMessage처럼 name과 age를 받아 메시지를 반환하는 함수를 예로 든다. React 세계에서도 같은 패턴이 자주 등장하는데, 함수형 컴포넌트는 입력 상태를 받아 React 요소라는 파생 상태를 반환한다. useState로 컴포넌트 내부 상태를 보관하고 버튼 클릭으로 값을 바꾸면, 렌더링 결과는 React의 동작 방식에 따라 최신 상태를 반영한다. 여기까지는 로컬 사용자에게 보여줄 상태를 다루는 데 자연스럽고 편리한 모델이다.

3. 로컬 상태에서 서버 상태로 옮길 때 생기는 첫 번째 문제

문제는 이 상태를 한 명의 로컬 사용자만 보는 것이 아니라 여러 클라이언트가 함께 보아야 할 때 시작된다. 저자는 이 경우 보통 상태를 서버로 옮기고, React 컴포넌트가 마운트될 때 서버에서 JSON을 fetch해 name과 age를 로컬 상태로 설정하는 방식을 보여준다. 로딩 중에는 undefined 상태를 기준으로 처리하고, 받은 값을 바탕으로 다시 파생 상태를 만든다. 그러나 버튼을 눌러 age를 바꾸면 이 변경은 현재 클라이언트의 로컬 상태에만 적용된다. 즉 다른 사용자에게 같은 앱이나 웹사이트를 보여주려는 목표와는 맞지 않는 상태 불일치가 발생한다.

4. 서버 업데이트와 페이지 reload 방식의 한계

저자는 이 문제를 고치기 위해 버튼 클릭 시 서버에 상태 업데이트 메시지를 보내고, 이후 전체 페이지를 다시 로드하는 방식을 제시한다. 이렇게 하면 페이지가 다시 뜰 때 서버에서 최신 상태를 fetch하고, 그 결과로 파생 상태를 다시 계산할 수 있다. 이어서 그는 name과 age를 클라이언트에서 받아 계산하는 대신, 서버가 message라는 파생 상태 자체를 직접 반환하도록 구조를 단순화한다. 이 방식은 요청 시점에는 서버에서 계산된 파생 상태를 받을 수 있으므로 한 단계 진전이다. 하지만 다른 클라이언트가 상태를 변경했을 때 현재 클라이언트가 이를 알 방법이 없다는 근본 문제는 여전히 남는다.

5. 다중 클라이언트 동기화 문제와 Convex로의 전환

저자는 한 클라이언트는 age가 21인 화면을 보고, 다른 클라이언트는 age가 20인 화면을 보는 상황을 예로 든다. 이상적인 경험이라면 두 클라이언트가 같은 시점에 같은 상태를 보아야 하지만, 서버가 변경 사실을 다른 클라이언트에 알려주지 못하면 그런 동기화가 일어나지 않는다. 여기서 웹소켓, 서버 전송 이벤트, 서버 사이드 렌더링 같은 주제로 갈 수도 있지만, 저자는 곧바로 Convex가 이 문제를 어떻게 풀어내는지 보여준다. Convex는 우선 데이터베이스이며, 특히 반응형 데이터베이스로 소개된다. 서버에 지속 저장되는 상태 구조를 스키마와 테이블로 정의하는 것이 출발점이다.

6. Convex의 스키마, 쿼리, 뮤테이션으로 상태와 파생 상태 다루기

Convex 예시에서 저자는 name과 age를 가진 테이블을 정의하고, 클라이언트가 서버 쿼리를 통해 이 데이터에 접근하게 한다. getMessage 쿼리는 데이터베이스에서 첫 번째 행을 가져오고, 존재하지 않으면 오류를 던지며, 가져온 name과 age로 message라는 파생 상태를 만든다. 상태를 바꿀 때는 reverseAging이라는 뮤테이션을 작성해 같은 행을 가져온 뒤 현재 age에서 1을 뺀 값으로 patch한다. 이 구조에서는 원본 상태가 서버 데이터베이스에 있고, 파생 상태도 서버 쿼리 안에서 계산된다. 저자는 이렇게 서버 쪽에서 관계를 정의해 두면 클라이언트는 그 결과를 요청하고 사용할 수 있다고 설명한다.

7. React 클라이언트에서 useQuery와 useMutation으로 자동 최신화하기

클라이언트 쪽에서는 Convex React 클라이언트와 자동 생성된 API를 가져와 기존 fetch 코드를 대체한다. 처음에는 convex.query로 getMessage를 호출하고, 뮤테이션이 끝난 뒤 페이지를 reload하는 식으로 기존 구현과 일대일로 대응시킬 수 있다. 하지만 저자는 Convex를 쓰면 더 나아갈 수 있다고 말하며 useQuery와 useMutation 훅을 도입한다. useQuery는 클라이언트가 특정 파생 상태에 관심이 있음을 선언하게 만들고, name이나 age가 바뀌었을 때 최신 상태를 유지하는 책임을 Convex가 맡게 한다. 이 덕분에 뮤테이션 이후 페이지를 다시 로드할 필요가 없고, 화면은 끊김 없이 즉시 갱신된다.

8. 필터링된 쿼리와 프런트엔드 전용 서버 파생 상태의 확장성

저자는 채팅 애플리케이션의 메시지 테이블 예시로 Convex 쿼리의 파생 상태 개념이 더 복잡한 경우에도 적용된다고 설명한다. 메시지 테이블에는 channel, body, from 같은 필드가 있고, channel 기준 인덱스를 사용해 특정 채널의 메시지만 반환하는 getMessagesInChannel 쿼리를 만들 수 있다. 중요한 점은 이 결과도 반응형 파생 상태라는 것이다. 예를 들어 클라이언트가 random 채널을 구독 중이라면 general 채널에 새 메시지가 추가되어도 해당 쿼리는 갱신되지 않는다. 저자는 이 세밀한 반응성이 프런트엔드가 정확히 필요로 하는 상태를 서버 쿼리로 만들게 해 주며, 이를 backend-for-frontend 방식의 빠르고 강력한 개발 모델로 본다.

🧾 핵심 주장 / 시사점

  • 파생 상태의 핵심 가치는 단순 계산 자체가 아니라, 원본 상태가 바뀔 때 그 관계를 일관되게 유지하는 데 있다.
  • 서버에서 파생 상태를 계산하더라도 변경 알림과 자동 갱신이 없다면 다중 클라이언트 환경에서는 여전히 오래된 화면이 남을 수 있다.
  • Convex 쿼리의 강점은 프런트엔드가 필요한 데이터 모양을 서버 쪽 파생 상태로 정의하면서도, 그 결과를 세밀하게 반응형으로 유지한다는 점이다.

✅ 액션 아이템

  • 파생 상태를 단일 사용자 렌더링 가정이 아닌 다중 클라이언트 동기화 데이터 흐름으로 재정의하고 갱신 경계를 정한다.
  • React 단일 사용자 파생 상태 패턴과 fetch/reload 동기화 패턴을 동일 기능군에서 나란히 구현해 지연 및 불일치 사례를 비교한다.
  • Convex 스키마·쿼리·뮤테이션·useQuery/useMutation을 조합해 필터와 다중 테이블 파생 상태를 서버에서 자동 갱신되도록 구성한다.

❓ 열린 질문

  • 로컬 파생 상태와 서버 파생 상태를 분리할 때 데이터 소유권 기준은 어떤 규칙으로 정하면 되는가?
  • 다중 클라이언트에서 즉시 동기화가 필요한 범위를 정할 때 어떤 상태만 서버 파생 상태로 승격해야 하는가?
  • 필터 조건과 다중 테이블 조합을 포함한 Convex 쿼리에서 반응형 갱신의 비용과 지연은 어느 정도까지 허용할 것인가?

관련 문서

공통 태그와 주제 흐름을 기준으로 같이 보면 좋은 문서를 이어서 제안합니다.