domain-specific-language

2 개의 포스트

figma

대규모 가시성: Figma가 (새 탭에서 열림)

피그마(Figma)는 서비스 규모가 확장됨에 따라 복잡해진 권한 관리 로직을 효율적으로 처리하기 위해 자체적인 권한 정의 언어(DSL)인 'Permit'을 구축했습니다. 기존의 파편화된 명령형 코드 방식에서 벗어나 선언적인 DSL을 도입함으로써 권한 정책의 일관성을 확보하고 보안 취약점 발생 가능성을 획기적으로 낮췄습니다. 이를 통해 복잡한 사용자-리소스 간의 관계를 명확하게 모델링하고 성능 저하 없이 대규모 시스템에 적용할 수 있는 권한 검증 인프라를 완성했습니다. ### 기존 시스템의 한계와 권한 관리의 복잡성 * 권한 체크 로직이 Go 애플리케이션 코드 곳곳에 흩어져 있어, 특정 리소스에 대한 접근 규칙을 한눈에 파악하거나 일관되게 수정하기 매우 어려웠습니다. * 팀, 프로젝트, 파일로 이어지는 계층 구조뿐만 아니라 엔터프라이즈 설정, 공유 링크 등 수많은 변수가 결합되면서 권한 로직 수정 시 예기치 않은 부작용(side effects)이 발생할 위험이 컸습니다. * 성능 최적화를 위해 데이터베이스 쿼리에 권한 로직을 직접 포함시켜야 하는 경우가 많았는데, 이는 비즈니스 로직과 권한 정책이 뒤섞여 코드 유지보수성을 떨어뜨리는 결과로 이어졌습니다. ### 관계 기반 접근 제어(ReBAC)와 Permit DSL 설계 * 구글의 Zanzibar 시스템에서 영감을 얻어, 객체 간의 관계를 중심으로 권한을 정의하는 ReBAC(Relationship-Based Access Control) 모델을 피그마의 환경에 맞게 커스텀화했습니다. * Permit DSL은 'Actor(사용자)', 'Resource(파일, 팀 등)', 'Action(편집, 보기 등)' 간의 관계를 선언적인 문법으로 정의합니다. * 예를 들어 "사용자가 파일이 속한 프로젝트의 편집자라면 해당 파일에 대한 편집 권한을 가진다"와 같은 전이적인(transitive) 관계를 직관적인 문법으로 표현할 수 있게 되었습니다. ### 컴파일러 및 성능 최적화 기술 * DSL로 작성된 정책을 런타임에 해석하는 대신, 효율적인 Go 코드로 변환하는 자체 컴파일러를 개발하여 실행 성능을 극대화하고 런타임 오버헤드를 최소화했습니다. * 컴파일 단계에서 정적 분석을 수행하여 순환 참조나 정의되지 않은 권한 사용 등 논리적 오류를 사전에 차단합니다. * 특히 '부분 평가(Partial Evaluation)' 기법을 도입하여, 권한 로직을 SQL 쿼리의 WHERE 절로 변환함으로써 수백만 개의 리소스 중 사용자가 접근 가능한 항목만 효율적으로 필터링할 수 있도록 구현했습니다. ### 안전한 전환을 위한 검증 및 배포 프로세스 * DSL 내부에 유닛 테스트를 직접 작성할 수 있는 기능을 포함시켜, 정책 변경이 기존의 기대 결과와 일치하는지 배포 전 즉시 검증할 수 있는 환경을 마련했습니다. * '섀도 모드(Shadow mode)'를 활용하여 실제 트래픽에서 기존의 레거시 권한 로직 결과와 새로운 Permit 시스템의 결과를 실시간으로 비교하며 데이터 정합성을 확인했습니다. * 성능 모니터링을 통해 권한 확인 작업이 전체 API 응답 시간에 미치는 영향을 정밀하게 추적하며 안정성을 확보했습니다. 권한 관리는 단순한 기능을 넘어 대규모 SaaS의 보안과 확장성을 결정짓는 핵심 인프라입니다. 피그마의 사례처럼 권한 로직을 비즈니스 코드에서 분리하여 '정책(Policy)'으로서 중앙 집중화하고 코드화하는 전략은, 제품의 복잡도가 높아질수록 개발 생산성과 시스템 안정성을 동시에 잡을 수 있는 가장 강력한 방법 중 하나입니다.

figma

Figma에서 커스텀 권한 (새 탭에서 열림)

Figma는 기존 Ruby 모놀리스의 `has_access?` 메서드에 모든 권한 로직을 집중시킨 결과, 복잡성·디버깅 난이도·계층형 권한의 한계·데이터베이스 부하 문제에 직면했다. 이를 해결하기 위해 자체 권한 도메인 특화 언어(DSL), 크로스플랫폼 권한 로직 엔진을 구축하고 핵심 권한 규칙을 새 시스템으로 이전했다. 목표는 권한 정확성과 성능을 높이는 동시에 개발자가 안전하게 권한 규칙을 변경할 수 있도록 만드는 것이었다. ## Figma의 권한 모델 - Figma의 협업 기능은 파일과 폴더, 팀, 조직 단위의 복잡한 권한 구조를 필요로 한다. - 파일 접근 방식은 크게 두 가지다. - **역할 기반 접근**: 상위 폴더·팀·조직에서 상속된 역할에 따라 접근한다. - **링크 기반 접근**: 링크를 가진 사용자의 접근 수준, 만료 기간, 비밀번호, 조직 정책 등을 조합해 결정한다. - 파일 삭제 여부, 계층 구조, 조직 제한, 결제 상태 등도 접근 가능 여부에 영향을 준다. - 기존에는 각 ActiveRecord 모델의 `has_access?` 메서드가 사용자와 리소스를 받아 접근 가능 여부를 boolean으로 반환했다. ## 기존 `has_access?` 방식의 한계 - 권한 판단에 필요한 모든 비즈니스 로직이 하나의 긴 메서드에 들어갔다. - 제품 엔지니어가 컨트롤러에서 이 메서드를 적절한 시점에 직접 호출해야 했다. - 작은 변경도 전체 권한 체계에 영향을 줄 수 있어 개발자들이 메서드 수정 자체를 꺼리게 됐다. - 권한 버그는 Figma의 모든 파일에 대한 접근 허용으로 이어질 수 있어 위험성이 컸다. - 디버깅 시 특정 규칙만 분리해 확인하기 어려웠고, 수십 개의 로그를 코드 곳곳에 추가해야 했다. ## 계층형 권한과 boolean 플래그의 문제 - 권한 수준을 정수로 표현했지만, 실제 동작은 여러 boolean 플래그에 의해 달라졌다. - 예시 메서드는 다음과 같은 선택적 인자를 포함했다. - `ignore_link_access` - `org_candidate` - `ignore_archived_branch` - 같은 권한 수준이라도 플래그 조합에 따라 결과가 달라져 개발자가 이해해야 할 경우의 수가 많았다. - 리소스마다 플래그의 의미와 동작이 달라 일관된 권한 모델을 만들기 어려웠다. - 예를 들어 `300` 수준의 편집 권한은 있어도, 특정 조건을 무시한 `100` 수준의 보기 권한 검사는 통과하지 못할 수 있었다. - 따라서 기존 계층 구조만으로는 표현하기 어려운, 서로 독립적인 세밀한 권한이 필요했다. - 새로운 시스템은 기존 계층형 권한을 지원하면서도 비계층적이고 독립적인 권한 체계를 추가할 수 있어야 했다. ## 권한 검사로 인한 데이터베이스 부하 - Figma의 사용자와 리소스 규모가 빠르게 증가하면서 권한 검사가 데이터베이스에 큰 부담을 줬다. - 전체 데이터베이스 부하 중 약 **20%**가 권한 검사에서 발생했다. - 데이터베이스를 수직·수평 확장하는 것만으로는 물리적 한계가 있었기 때문에, 권한 로직 자체가 데이터 계층에 가하는 부하를 줄여야 했다. - 새로운 권한 시스템에는 권한 데이터를 어떻게 조회하고 처리할지에 대한 더 세밀한 제어가 필요했다. ## 자체 권한 DSL과 로직 엔진 - Figma는 외부 솔루션을 우선 검토하는 일반적인 방침과 달리, 권한 문제에는 자체 시스템을 선택했다. - 구축한 구성 요소는 다음과 같다. - 권한 규칙을 명확하게 표현하는 **도메인 특화 언어(DSL)** - 여러 환경에서 동작하는 **크로스플랫폼 권한 로직 엔진** - 기존의 핵심 권한 규칙을 새 시스템으로 이전하는 마이그레이션 체계 - 이를 통해 권한 규칙을 하나의 거대한 조건문으로 관리하지 않고, 독립적이고 조합 가능한 규칙으로 다룰 수 있게 하는 것이 목표였다. - 결과적으로 권한 로직을 제품 기능과 분리하고, 규칙 추가·수정·삭제 시 기존 권한 체계를 모두 다시 이해해야 하는 부담을 줄이려 했다. ## 실용적인 결론 복잡한 권한 시스템에서는 단순한 `if/else` 함수와 호출 규약만으로 규모 확장을 감당하기 어렵다. 권한을 독립적인 규칙으로 모델링하고, 선언적인 DSL과 공통 실행 엔진을 사용하면 정확성·성능·개발자 경험을 함께 개선할 수 있다.