토스 · SLASH 23 2023
Server-driven UI로 토스의 마지막 어드민 만들기
서버가 UI 구조를 결정하는 SDUI 패턴으로 어드민 시스템 구축 (발표: 조유성)
요약
핵심 토픽
학습 포인트
1. Server-Driven UI(SDUI) 패턴의 개념
SDUI는 서버가 UI의 구조와 동작을 데이터(JSON DSL)로 정의하고, 클라이언트(프론트엔드)는 이 데이터를 실제 컴포넌트로 렌더링하는 패턴입니다. 전통적인 방식과의 차이: 기존에는 '서버=데이터, 클라이언트=UI 로직'이었다면 SDUI는 'UI 구조까지 서버가 결정'. 새 화면 추가 시 프론트엔드 코드 수정·배포 없이 서버에 DSL만 등록하면 됩니다. 넷플릭스 홈화면의 레이아웃 변경, 에어비앤비의 동적 페이지가 대표 사례입니다.
핵심 용어
2. UI 렌더러 설계와 의존성 주입
렌더러는 DSL을 받아 React 컴포넌트를 반환하는 고차 함수입니다. `{ type: 'Button', label: '확인' }` → `<Button>확인</Button>` 변환. 의존성 주입으로 DSL 타입별 컴포넌트를 등록하면 (`registry[dsl.type](dsl.props)`) 새 DSL 타입 추가 시 렌더러 코어를 수정하지 않아도 됩니다. 이는 개방-폐쇄 원칙(OCP): 확장에는 열려있고, 수정에는 닫혀있는 설계입니다.
핵심 용어
3. JSON DSL 설계와 타입 안전성
DSL은 discriminated union 타입으로 설계하면 TypeScript가 type 필드에 따라 props 타입을 자동 추론합니다. `{ type: 'Button', label: string } | { type: 'Table', columns: Column[] }` 형태로 정의하면 렌더러에서 switch문으로 타입 안전하게 처리할 수 있습니다. 런타임 검증은 zod 스키마로 서버에서 오는 DSL을 검증하면 잘못된 데이터가 렌더러에 도달하기 전에 차단할 수 있습니다.
핵심 용어
4. SDUI 도입의 트레이드오프와 적합한 상황
SDUI가 적합한 상황: (1) 비슷한 구조의 화면이 매우 많고 자주 추가·변경되는 경우, (2) 프론트엔드 개발자 없이도 화면을 만들어야 하는 경우(어드민, 마케팅 페이지). 부적합한 상황: 복잡한 인터랙션이 필요한 제품 화면, 퍼포먼스가 극도로 중요한 경우. 초기 플랫폼 구축 비용(6개월)이 있지만 장기적으로 화면 추가 비용이 0에 수렴합니다. 보안·접근 제어를 중앙화할 수 있어 금융 같은 규제 환경에서 유리합니다.
핵심 용어
면접 질문
Q1. Server-Driven UI를 도입하면 어떤 장점이 있고, 어떤 상황에서 적합한가요?
힌트
[감점 답변] 정의만 반복하거나 "Server-Driven UI를 도입하면 어떤 장점이 있고, 어떤 상황에서 적합한가요?"에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] 장점: 프론트엔드 배포 없는 화면 변경, 보안 정책 중앙화, 일관된 UX를 설명하세요. 적합한 상황: 비슷한 화면이 반복적으로 추가되는 어드민·CMS, 비개발자가 화면을 추가해야 하는 경우를 언급하세요. 토스 사례(5~10일→1~2일 단축)처럼 구체적인 효과를 수치로 설명하면 좋습니다.
Q2. JSON으로 UI를 정의할 때 타입 안전성을 어떻게 보장하나요?
힌트
[감점 답변] 정의만 반복하거나 "JSON으로 UI를 정의할 때 타입 안전성을 어떻게 보장하나요?"에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] 두 레이어로 나눠 설명하세요: (1) 런타임 검증 — zod로 서버에서 오는 JSON의 구조를 검증해 잘못된 DSL이 렌더러에 도달하지 않게 한다, (2) 컴파일 타임 타입 안전성 — TypeScript discriminated union으로 type 필드에 따라 props 타입을 자동 추론해 렌더러 구현 오류를 방지한다. 두 레이어를 모두 갖춰야 완전한 타입 안전성이 보장된다는 점을 강조하면 됩니다.
Q3. 의존성 주입(Dependency Injection) 패턴이란 무엇이고 어떻게 활용하나요?
힌트
[감점 답변] 정의만 반복하거나 "의존성 주입(Dependency Injection) 패턴이란 무엇이고 어떻게 활용하나요?"에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] 의존성 주입의 핵심: 함수/클래스가 의존하는 객체를 내부에서 생성하지 않고 외부에서 주입받는 패턴이라고 설명하세요. SDUI 렌더러 예시: 렌더러가 `registry` 객체를 주입받으면 새 컴포넌트 타입 추가 시 렌더러 코어 수정 없이 registry에만 추가하면 됩니다. 테스트 용이성(mock 주입)과 확장성이 주요 장점임을 언급하면 됩니다.
Q4. 어드민 시스템에서 권한 관리를 구현할 때 고려해야 할 사항은?
힌트
[감점 답변] 정의만 반복하거나 "어드민 시스템에서 권한 관리를 구현할 때 고려해야 할 사항은?"에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] RBAC(Role-Based Access Control) 기본 개념을 설명하고, 어드민 특수 사항을 추가하세요: 개인정보 마스킹(전화번호 일부 가리기), 접근 로그 기록, 금융 규정 준수(5회 실패 시 잠금). SDUI 관점에서는 DSL 자체에 권한 정보를 포함시켜 서버에서 보내지 않으면 클라이언트에서도 렌더링이 불가하다는 중앙 통제 방식을 설명하면 됩니다.
Q5. 기존 화면이 많은 서비스에서 SDUI로 점진적으로 전환하는 전략은 무엇인가요?
힌트
[감점 답변] 정의만 반복하거나 "기존 화면이 많은 서비스에서 SDUI로 점진적으로 전환하는 전략은 무엇인가요?"에 대해 장단점 없이 단편적으로 답하면 감점 포인트입니다. 면접관은 실무 적용 경험이 부족하다고 판단합니다. [좋은 답변] Strangler Fig 패턴을 적용하세요: 기존 화면은 건드리지 않고 새로 추가되는 화면부터 SDUI로 만들고, 기존 화면 수정이 필요할 때 순차적으로 SDUI로 마이그레이션. 렌더러를 먼저 구축해 작은 단위(버튼, 폼)부터 DSL화하고, 검증이 완료된 후 더 복잡한 화면으로 확장. PoC 결과를 수치로 팀에 공유해 동의를 얻는 과정도 중요하다는 점을 언급하면 좋습니다.
선행 학습
- React 컴포넌트 설계와 고차 컴포넌트
- TypeScript 제네릭과 discriminated union
- REST API 설계 기초
핵심 타임스탬프
학습 방법
1단계: 텍스트 입력, 셀렉트박스, 버튼 세 가지 컴포넌트를 JSON으로 정의해 렌더링하는 미니 DSL 렌더러를 직접 구현해보세요. registry 패턴으로 컴포넌트를 등록하는 구조를 만들면 SDUI의 핵심을 체감할 수 있습니다. 2단계: zod를 사용해 런타임 DSL 검증을 추가하고, TypeScript discriminated union으로 타입 안전성을 보강해보세요. 3단계: 권한(role) 필드를 DSL에 추가해 역할에 따라 특정 필드를 숨기거나 마스킹하는 기능까지 구현해보면 실무 수준의 이해에 도달합니다. 4단계: 동료에게 "Server-Driven UI"의 핵심을 5분 안에 설명해보세요. 막히는 부분이 아직 구조적으로 이해되지 않은 지점입니다.