모든 글

운영 테이블을 지키는 별도 조회 모델 설계

운영 테이블은 비즈니스 개념에 맞게 설계하고, 복잡한 이력·어드민 검색은 별도 조회 모델에서 제공하는 방법을 다룬다.

  • database
  • architecture
  • query-models

출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다. gpt-5.6-sol 모델을 사용해 생성·편집했습니다.

운영 스키마는 서비스의 중요한 개념을 분명하게 표현해야 한다. 반면 검색 화면은 필터, 조인, 이력, 조회 속도에 맞춰 정리된 데이터를 원한다. 이 두 목적을 같은 테이블로 모두 만족시키려 하면 애플리케이션 모델은 변경하기 어려워지고 조회 코드도 여전히 복잡해질 수 있다.

실용적인 해법은 운영 테이블의 초점을 지키면서 까다로운 조회를 위한 별도 모델을 만드는 것이다. 모든 서비스에 새로운 저장소가 필요하다는 규칙은 아니다. 이력, 어드민, 검색 요구가 애플리케이션 본연의 구조와 맞지 않기 시작했을 때 검토할 수 있는 설계 선택지다.

애플리케이션이 소유한 개념에서 시작하라

대출 서비스가 대출 신청, 상환, 연체를 각각 나누어 저장한다고 가정해 보자. 각 테이블이 서로 다른 개념과 생명주기를 보존한다는 점에서 이 구분은 의미가 있다. 스키마가 서비스의 역할을 설명하고, 한 개념의 변경이 다른 개념까지 함부로 비틀지 않게 한다.

그런데 어드민에서 한 사람의 전체 이력을 확인해야 한다면 상황이 달라진다. 신청부터 모든 상환과 연체 사건, 그 과정의 이슈까지 한 번에 살펴보려면 개념적으로 깔끔했던 분리가 까다로운 조회 조건이 된다. 한 쿼리로 해결하려면 여러 조인이 필요하다. 조인을 피한다고 문제가 사라지지도 않는다. 부분 조회를 차례로 실행하고 애플리케이션에서 조합하는 장황한 흐름으로 복잡성이 옮겨갈 뿐이다.

이때 특정 화면에 맞춰 운영 테이블 전체를 다시 설계하는 것은 좋은 대응이 아니다. 요구사항은 계속 바뀌고 어드민 검색 조건은 늘어나는 경향이 있다. 서비스를 실행하는 테이블이 가능한 모든 과거 질문에 답하려 들면, 지켜야 할 개념보다 우연히 생긴 검색 요구가 구조를 지배하기 시작한다.

복잡한 조회에는 그 목적에 맞는 모양을 주어라

별도 조회 모델은 이력이나 활동 테이블처럼 단순하게 시작할 수 있다. 점검에 필요한 레코드를 필터링하고 보여주기 쉬운 형태로 그곳에 쌓는다. 검색과 어드민 코드는 매번 메인 서비스 테이블에서 같은 이야기를 재구성하지 않고 이 조회 모델을 읽는다.

요구사항이 정당화한다면 별도 조회 저장소나 검색에 적합한 엔진을 사용할 수도 있다. 중요한 것은 제품 이름이 아니다. 운영을 위한 쓰기와 넓은 범위의 검색이 서로 다른 모양을 가진다는 사실을 인정하는 것이다. 여러 해의 레코드에 계속 늘어나는 어드민 조건을 적용하는 일은 올바른 상환이나 연체를 기록하는 일과 책임이 다르다.

이렇게 경계를 나누면 양쪽을 모두 지킬 수 있다. 운영 테이블은 애플리케이션의 개념을 계속 표현하고, 조회 영역은 실제 검색 패턴에 맞게 데이터를 구성한다. 새로운 필터나 이력 화면이 생겨도 모든 표현 요구를 핵심 스키마에 밀어 넣지 않고 조회 모델을 바꿔 대응할 수 있다.

조회 모델이 있다는 이유로 운영 원본의 의미가 약해지는 것은 아니다. 어떤 상태를 확정하는 쓰기와 정합성 판단은 계속 운영 모델이 책임진다. 조회 모델은 그 결과를 검색하기 좋은 모양으로 재구성한다. 두 저장소가 서로 다른 답을 줄 때 어느 쪽을 기준으로 복구할지 정해 두어야 복제본이 또 하나의 권위 있는 원본으로 변하는 일을 막을 수 있다.

조회 데이터를 채우는 방법을 선택하라

조회 모델을 분리했다면 데이터가 그곳에 도달하는 경로를 정해야 한다. 선택지는 여러 가지다.

  • 조회용 데이터를 비동기로 기록하거나 복사한다.
  • 늦게 반영되어도 괜찮다면 배치로 주기적으로 적재한다.
  • 별도 엔진에 이벤트를 보낸다.
  • 운영 원본과 조회 저장소를 명시적인 다른 절차로 동기화한다.

이 선택들은 더 큰 인프라로 가기 위한 필수 단계가 아니다. 전용 테이블 하나로 충분할 수 있다. 검색 행위와 데이터 규모가 필요성을 보여줄 때만 별도 엔진이 가치를 갖는다. 허용 가능한 지연도 중요하다. 데이터를 덩어리로 늦게 가져와도 되는 조회라면 배치가 맞지만, 다른 조회는 더 빠른 반영이 필요할 수 있다.

따라서 저장소 분리는 설계 작업을 없애지 않는다. 대신 결정해야 할 것을 드러낸다. 팀은 적재 경로, 필요한 최신성, 목적별로 어떤 모델을 신뢰할지 정해야 한다. 그 비용을 운영 조회에 이미 쌓이고 있는 복잡성과 비교해야 한다.

조회 모델은 복제된 데이터이므로 갱신 실패도 운영 대상이 된다. 배치가 중간에 멈추거나 이벤트가 누락됐을 때 다시 채울 수 있는지, 원본과 차이가 생겼음을 어떻게 발견할지 정해야 한다. 검색 화면에서 약간 늦은 데이터가 허용되는지 여부도 기능마다 다를 수 있다. 이런 질문에 답할 수 없다면 별도 저장소가 조회 코드를 줄이는 대신 더 어려운 정합성 문제를 만든다.

작은 범위라면 전용 이력 테이블 하나부터 시작할 수 있다. 별도 검색 엔진이나 복잡한 비동기 파이프라인을 먼저 선택할 이유는 없다. 실제 필터 수, 이력 길이, 조회 비용이 더 큰 도구를 정당화할 때 확장하면 된다.

화면을 최적화하기 전에 서비스를 보존하라

핵심은 애플리케이션이 맡은 역할에 맞는 설계를 먼저 하는 것이다. 코드와 데이터에서 중요한 개념을 읽을 수 있게 유지하자. 복잡한 조회가 여러 개념을 가로지른다면 운영 모델을 억지로 휘기보다 그 조회에 맞는 모델을 따로 만드는 편이 낫다.

작은 서비스와 단순한 검색은 직접 조회만으로 충분할 수 있다. 조인, 잘게 나뉜 부분 조회, 긴 이력, 늘어나는 어드민 필터가 반복해서 복잡성을 만들 때 분리의 가치가 생긴다. 그 시점의 별도 이력 테이블이나 조회 저장소는 장식적인 아키텍처가 아니다. 운영 설계와 조회 설계가 각자 맡은 문제를 풀게 하는 경계다.

결정 순서는 단순하다. 먼저 현재 조회가 실제로 운영 모델을 훼손하는지 확인한다. 직접 조회로 감당할 수 있다면 그대로 둔다. 반복되는 검색 비용과 변경 압력이 확인되면 가장 작은 조회 모델을 만들고, 적재와 복구를 운영할 수 있을 때만 더 큰 저장 기술로 확장한다. 측정 없이 도구부터 늘리지 않는다.