구조화된 실험 데이터 모델을 기반으로, 중복 코드를 줄이면서 목록 편집·집계·관계 탐색 등 다양한 데이터 관리/탐색 기능을 제공하는 ‘구조화된 로딩’ 시스템을 소개한다.
기업용 애플리케이션의 핵심은 데이터 관리입니다. 데이터 입력을 넘어 데이터를 탐색하고 분석할 수 있는 능력은 회사의 생산성을 높여 줍니다. Uncountable의 R&D 솔루션은 실험부터 랩 요청(lab request), 작업, 재고 관리, 인증 등으로 점점 더 넓은 범위의 데이터를 다룹니다. 각각은 고유한 비즈니스 목적을 갖고 있지만, 우리는 지속적으로 데이터를 활용하는 더 많은 방법을 찾아 왔습니다. 필터 가능한 목록, 값 편집에서부터 더 야심찬 피벗 테이블과 차트까지 — 고객 데이터의 더 많은 부분을 노출하고 항상 존재하는 성능 요구사항을 충족해야 했던 필요가 우리를 새로운 해법으로 이끌었습니다. 바로 구조화된 로딩(structured loading) 입니다.
Uncountable의 제품 차별점은 구조화된 실험 정보를 저장하기 위한 표현력이 뛰어난 데이터 모델에 있습니다. 우리는 이미 이 데이터에 대한 접근을 추상화하기 시작했는데, 목록의 표현 방식부터 시작해 프런트엔드 코드를 재사용하기 위한 목적이었습니다. 공통 필터 표현식을 사용했지만, 일부 쿼리가 공유되더라도 대부분의 로더(loader)는 각 데이터 타입에 특화된 상태로 남아 있었습니다. 표준 객체-관계 매핑(ORM)의 한계에 다다르면서 더 나은 해결책이 필요했습니다. 우리는 중복 코드를 만들지 않으면서도 다양한 데이터 관리 및 탐색 기능을 제공하기 위해 구조화된 로딩 을 만들었습니다.
이 글에서는 몇 가지 기본 개념과, 구조화된 로딩 이 다른 시스템과 구별되는 점을 살펴보겠습니다.
다음은 구조화된 로딩 의 상위 수준 기능들입니다:
데이터 모델링의 핵심 구성 요소는 스토어(store)라고 부릅니다. 우리는 시스템의 각 엔터티 타입마다 스토어를 정의합니다. 예를 들어 랩 요청에는 랩 요청 스토어가 있고, 프로젝트에는 프로젝트 스토어가 있으며, 실험에도 실험 스토어가 있는 식입니다. 각 스토어는 데이터베이스의 한 테이블에 대응되며, 최소한 테이블의 각 컬럼을 설명합니다.

예를 들어 랩 요청(lab request)의 스토어는 아래와 같은 필드들을 정의합니다:
LabRequest:
table: lab_request
fields:
name:
column: name
value_type: text
name: Name
lab:
column: lab_id
value_type: id
id_source: entity/lab
name: Lab
dueDate:
column: due_date
value_type: date
name: Due Date
이 정보만으로도 시스템의 대부분 기능을 만들 수 있습니다. 예를 들어 사용자가 랩 요청 페이지에 있다면, 이 테이블의 컬럼 목록을 제시할 수 있습니다. 데이터베이스 테이블과 컬럼을 알고 있으니, 이 데이터를 로드하는 데 필요한 SQL을 생성할 수 있습니다. 또한 높은 수준의 value_type 정보를 통해 사용자에게 적절한 폼 필드를 보여줄 수 있고, 사용자가 값을 입력하면 테이블에 저장할 수도 있습니다. 더불어 어떤 종류의 필터를 해당 필드에서 지원할 수 있는지도 알 수 있어, 사용자에게 선택지를 제시할 수 있습니다.
가장 단순한 경우 스토어는 데이터베이스 모델에 존재하는 컬럼을 사용자에게 직접 노출합니다. 하지만 어떤 경우에는 사용자가 기본 데이터베이스 모델의 ‘파생된’ 버전만 보아야 하기도 합니다. 예를 들어 사용자는 랩 요청을 “워치(watch)”할 수 있는데, 이는 해당 랩 요청에 대한 알림을 받는다는 뜻입니다. 이 정보는 별도의 테이블에 저장되며, 필터가 걸린 조인 조건을 갖습니다. 사용자는 랩 요청 목록에서 누가 해당 랩 요청을 워치하고 있는지 보고 싶을 수도 있지만, 더 중요한 것은 자신이 워치 중인 랩 요청만 필터링해서 보고 싶어한다는 점입니다. 이런 컬럼 중 일부는 직접 모델링하기가 어려워서, 우리는 커스텀 로더(custom loader)를 도입했습니다. 이에 대한 스토어 엔트리는 아래처럼 생길 수 있습니다:
watchers:
type: ExtraColumn
name: Watchers
loader: LabRequestWatchers
value_type: ids
여기서 LabRequestWatchers는 쿼리의 세부 사항을 구현하는 파이썬 클래스입니다. 하지만 여기에는 트레이드오프가 있습니다. 기본 데이터베이스 모델 전체를 접근 가능하게 만들지 않으면, 향후 사용자 요구를 충족하기 위해 더 많은 ExtraColumn을 만들어야 할 수 있습니다. 우리는 더 많이 노출할수록 애플리케이션을 확장하기가 더 쉬워진다는 사실을 깨닫고 있습니다. 꼭 ‘직접’ 노출일 필요는 없고, 대신 일반적인 의미(semantic)를 가진 개념을 만들려고 합니다. 예를 들어 최근에는 ORM 스타일의 백링크(backlink)를 추가했는데, 간접 필드 참조를 통해 스토어 간 조인을 할 수 있게 해줍니다.
이름 시스템은 우리 애플리케이션의 핵심입니다. 사용자가 엔터티를 볼 때 모두 이름을 부여해 주는 메커니즘입니다. 그 이상으로, 이는 데이터 관계의 기반으로 사용되어 엔터티 간 내비게이션을 가능하게 하고, 정보를 더 깊게 파고들어(drill down) 확인할 수 있게 합니다.
예시에는 id 값 타입을 가진 lab이라는 필드가 있습니다. 이는 데이터베이스에서 전형적인 외래 키(foreign key)입니다. 랩 요청 테이블의 lab_id에는 다른 테이블(여기서는 lab)의 기본 키(primary key)가 들어 있습니다. 사용자가 이 필드를 볼 때 id를 보고 싶어하진 않고, 랩의 이름을 보고 싶어합니다. 이는 목록의 정적 표시에서도, 폼에서 편집할 때도 마찬가지입니다.
필드의 id_source는 해당 id가 어떤 엔터티 타입을 참조하는지 알려줍니다. 예시에서 entity/lab 소스는 lab 필드가 lab 엔터티를 참조한다는 뜻입니다.
간단히 말해 id-source 시스템은 다음 기능을 제공합니다:
id-source 시스템은 구조화된 로딩 이전부터 존재했지만, 두 시스템은 성장하면서 서로 형제(sibling) 시스템이 되었고 기능이 촘촘히 얽혔습니다. 구조화된 로딩 시스템은 우리가 id-source 시스템을 위해 수동으로 작성하던 많은 부분을 대체했습니다. 이제 스토어에서 “필수 필드(essential fields)”에 대한 몇 가지 규칙만 따르면 이름 지정이 자동으로 이루어집니다.
id-source 시스템은 우리 애플리케이션에서 많은 난제를 해결해 주었습니다.
우리 시스템에는 이미 범용 폼(generic form) 시스템이 있었습니다. 우리는 이 시스템을 구조화된 로딩 에 통합했습니다. 사용자 정의 필드는 하드코딩된 스토어 필드와 마찬가지로 목록에서 사용할 수 있었습니다. 그리고 폼 안에 목록을 표시하는 기능을 추가했으며, 일부 입력 시스템에서는 목록에서 컬럼을 꺼내 개별 필드로 배치할 수 있게 했습니다.
목록에서 값을 편집하는 기능은 처음에는 폼을 뒷받침하던 시스템에 의해 추진되었습니다. 하지만 어느 시점에서 스토어가 필요한 정보를 모두 갖고 있다는 사실을 깨달았습니다. 그래서 소프트웨어의 여러 중간 계층을 다시 작성하여 폼 시스템이 스토어를 기반으로 동작하게 만들었습니다. 시스템 간 경계가 흐려지면서 중복 코드를 대폭 제거할 수 있었습니다.
결국 우리는 스토어를 목록 시스템과는 별개의 개념으로 다루는 것이 논리적으로 타당하다는 결론에 이르렀습니다. 이제 스토어는 구조화된 로딩 과 폼 시스템의 기반을 이룹니다. 범용 시스템을 향한 추진은 계속해서 새로운 기능을 드러내고, 코드의 표면적(유지해야 할 영역)을 줄여 줍니다.
구조화된 로딩 시스템은 우리 아키텍처에서 활발히 개발되는 구성 요소입니다. 우리는 계속해서 그 경계를 확장하고, 고객이 데이터에서 더 많은 가치를 얻도록 돕는 새로운 방법을 찾고 있습니다. 우리는 작은 데이터 모델링만으로도 풍부한 데이터 관리 및 탐색 도구 세트를 노출하는 시스템을 만들었습니다.
독특한 시스템이기 때문에, 새로운 프로그래머는 팀에 합류하면 이를 익혀야 합니다. 한번 익히고 나면 개발 시간을 셀 수 없이 절약해 줍니다. 기본적인 데이터 관리 및 접근 API에 신경 쓰는 대신, 코드의 더 상위 수준 비즈니스 기능에 집중할 수 있습니다.
분명 오랜 역사와 함께 복잡해진 시스템입니다. 앞으로는 특정 측면과 하위 시스템, 그리고 흥미롭다고 생각하는 비기능적 측면에 대해서도 후속 글을 쓸 예정입니다. 데이터베이스 자체가 할 수 있는 범위를 훨씬 넘어서는 쿼리 생성과 최적화 같은, 더 많은 것들이 아직 남아 있습니다.