frontend-engineering

5 개의 포스트

디자인 시스템 다시 생각해보기 (새 탭에서 열림)

디자인 시스템은 성장에 따라 경직되기 마련이며, 시스템이 제품 팀의 변화하는 요구사항을 제때 수용하지 못할 경우 팀은 시스템을 우회하거나 파편화된 코드를 생성하게 됩니다. 토스의 디자인 시스템(TDS)은 디자인 시스템을 통제 수단이 아닌 '하나의 제품'으로 정의하고, 수요자의 니즈에 따라 유연하게 대응할 수 있는 설계 구조를 지향합니다. 이를 위해 단순함과 유연함을 동시에 잡을 수 있는 하이브리드 API 전략을 도입하여 일관성과 생산성을 모두 확보하는 해결책을 제시합니다. ### 시스템의 경직성과 파편화 문제 * 조직이 커지고 제품이 다양해지면 기존 시스템의 제약 내에서 해결할 수 없는 UI 요구사항이 빈번하게 발생합니다. * 제품 팀은 빠른 해결을 위해 피그마 컴포넌트를 해제(detach)하거나 라이브러리 코드를 복제(fork)하여 로컬에서 수정해 사용하게 됩니다. * 이러한 우회 방식은 시스템 업데이트와의 연결을 끊어버려 UI 불일치를 초래하고, 장기적으로 디자인 시스템의 핵심 가치를 무너뜨립니다. * 결국 디자인 시스템이 팀의 속도를 늦추는 장애물이 되지 않으려면, 강력한 규칙보다 '우회할 이유를 줄이는 유연한 설계'가 필요합니다. ### 확장성을 고려한 컴포넌트 API 패턴 비교 * **Flat 패턴**: 내부 구조를 숨기고 모든 변형을 props로 처리하는 방식입니다. 사용이 직관적이고 간결하지만, 예외적인 요구사항이 늘어날수록 props가 기하급수적으로 증가하여 유지보수가 어려워집니다. * **Compound 패턴**: 하위 컴포넌트(Header, Body, Footer 등)를 제공하여 사용자가 직접 조합하는 방식입니다. 시스템이 예측하지 못한 레이아웃도 유연하게 구현할 수 있으나, 코드량이 늘어나고 구조에 대한 학습 비용이 발생한다는 단점이 있습니다. * 두 패턴은 상충하는 장단점을 가지고 있으므로, 단순히 하나의 패턴을 강요하는 것은 사용자의 이탈을 막기에 부족합니다. ### TDS의 하이브리드 전략과 Primitive 레이어 * TDS는 단순하고 빈번한 케이스를 위한 **Flat API**와 복잡한 커스텀을 위한 **Compound API**를 동시에 제공합니다. * 사용자는 별도의 커스텀이 필요 없을 때는 간결한 Flat 형식을 선택하고, 세밀한 제어가 필요할 때는 Compound 형식을 선택하여 시스템 내부에서 문제를 해결할 수 있습니다. * 디자인 시스템 팀은 관리 효율을 위해 **Primitive(기초 단위)** 레이어를 먼저 구축합니다. * 내부적으로는 동일한 Primitive 컴포넌트를 공유하면서 외부로 드러나는 API만 두 가지 형태로 노출함으로써, 유지보수 부담을 최소화하면서도 사용자 경험을 극대화합니다. 디자인 시스템은 팀을 가두는 울타리가 아니라 안전하게 안내하는 가드레일이 되어야 합니다. 중앙에서 모든 것을 통제하려 하기보다, 규칙에서 벗어난 예외 상황까지 시스템 안에서 지원할 수 있는 유연한 설계를 갖출 때 진정한 일관성을 유지할 수 있습니다.

버전 관리: UX 라이터 (새 탭에서 열림)

Figma는 레이어 패널의 깊은 계층 구조에서 발생하는 가독성 문제를 해결하기 위해 세 가지 가로 스크롤 방식을 탐색했습니다. 단순히 기술적인 구현을 넘어, 사용자가 레이어의 맥락을 잃지 않으면서도 긴 이름을 쉽게 읽을 수 있도록 하는 최적의 UX를 찾는 것이 핵심이었습니다. 최종적으로 Figma는 성능과 사용성을 모두 잡기 위해 구조적 아이콘은 고정하고 텍스트만 유연하게 반응하는 정교한 인터랙션 방식을 채택했습니다. ### 단순 가로 스크롤의 한계와 초기 탐색 * 가장 직관적인 방법인 `overflow-x: auto`를 적용했을 때, 레이어 이름이 길어질수록 왼쪽의 계층 구조 아이콘(눈 아이콘, 잠금 아이콘 등)이 화면 밖으로 사라지는 문제가 발생했습니다. * 사용자가 레이어 이름을 확인하기 위해 오른쪽으로 스크롤하면 해당 레이어의 현재 상태를 파악하거나 제어할 수 없게 되어, 도구로서의 사용성이 크게 저하되었습니다. * 또한, 단순히 가로 스크롤바를 추가하는 것만으로는 수천 개의 레이어가 중첩된 복잡한 디자인 파일에서의 성능 최적화 문제를 완벽히 해결하기 어려웠습니다. ### 가변형 레이아웃과 호버 인터랙션 시도 * 레이어 패널의 전체 폭을 고정하지 않고 내용물에 따라 가변적으로 늘어나는 방식을 검토했으나, 이는 캔버스 영역을 침범하여 디자인 작업 공간을 좁게 만드는 부작용이 있었습니다. * 마우스 커서를 올렸을 때만 레이어 이름이 확장되는 방식도 고려되었지만, 빈번한 레이아웃 시프트(Layout Shift)로 인해 시각적 피로도가 증가하고 의도치 않은 클릭 실수를 유발하는 한계가 있었습니다. * 이 과정에서 Figma 팀은 '맥락 유지(Context Preservation)'와 '가독성 확보'라는 두 마리 토끼를 잡기 위한 더 정밀한 엔지니어링 접근이 필요함을 확인했습니다. ### 스티키 요소와 가변 인덴트를 활용한 최종 솔루션 * Figma는 레이어의 계층 관계를 나타내는 인덴트(들여쓰기) 가이드라인과 주요 제어 아이콘을 왼쪽에 고정(Sticky)하는 하이브리드 방식을 채택했습니다. * 사용자가 가로로 스크롤할 때, 레이어 이름 텍스트는 유연하게 움직이지만 해당 레이어가 속한 부모 계층의 위치 정보와 상태 아이콘은 화면 왼쪽 끝에 머물도록 설계했습니다. * 이를 위해 복잡한 가상 리스트(Virtual List) 환경 내에서 스크롤 오프셋을 계산하고, 수천 개의 돔(DOM) 요소가 실시간으로 반응하면서도 60fps의 부드러운 성능을 유지하도록 렌더링 로직을 최적화했습니다. 복잡한 계층 구조를 다루는 소프트웨어라면 표준 스크롤 기능에 의존하기보다 정보의 우선순위를 정의하는 것이 중요합니다. Figma의 사례처럼 사용자가 항상 확인해야 하는 '제어 요소'와 필요할 때만 확인하는 '상세 이름'을 분리하여 설계하면, 한정된 UI 공간 내에서 정보 밀도를 효과적으로 관리할 수 있습니다.

디자인에서 코드로의 자동화를 (새 탭에서 열림)

피그마(Figma)는 코드 생성(Codegen)의 진정한 가치가 단순한 디자인-코드 변환을 넘어, 디자인 시스템과 실제 프로덕션 코드 사이의 간극을 메우는 '연결'에 있다고 주장합니다. 무분별한 AI 기반 코드 생성보다 이미 검증된 사내 컴포넌트 라이브러리를 디자인 요소와 정밀하게 매핑하는 'Code Connect' 방식이 실질적인 개발 생산성을 높이는 핵심입니다. 결과적으로 이를 통해 디자이너와 개발자는 동일한 언어로 소통하며 제품의 일관성을 비약적으로 높일 수 있습니다. **기존 핸드오프 방식과 범용 코드 생성의 한계** * 전통적인 핸드오프 도구는 주로 CSS 속성이나 단순한 스타일 값만 제공하므로, 개발자가 이를 프로젝트의 실제 아키텍처에 맞춰 다시 해석하고 구현해야 하는 번거로움이 있었습니다. * 범용적인 AI 코드 생성기는 프로젝트 고유의 컨벤션이나 디자인 시스템의 세부 규칙을 알지 못하기 때문에, 생성된 코드를 그대로 사용하기 어렵고 오히려 유지보수 비용을 증가시키는 경우가 많았습니다. * 디자인 도구에서 제공하는 정적인 정보와 실제 코드 저장소의 동적인 상태가 분리되어 있어, 시스템 업데이트 시 양측의 동기화가 깨지는 문제가 빈번히 발생했습니다. **Code Connect: 신뢰할 수 있는 소스의 연결** * Figma의 'Code Connect'는 개발자가 이미 작성해 둔 프로덕션 환경의 컴포넌트 코드를 Figma 디자인 요소와 직접 연결하는 방식을 취합니다. * 단순히 코드를 새로 생성하는 것이 아니라, CLI 도구를 통해 실제 코드 베이스에 존재하는 컴포넌트의 API와 Figma의 컴포넌트 속성(Properties)을 일대일로 매핑합니다. * 이를 통해 개발자는 Figma 화면에서 해당 디자인 요소를 클릭했을 때, 자사 디자인 시스템의 명세에 완벽히 부합하는 코드 스니펫을 즉각적으로 확인할 수 있습니다. **개발 워크플로우 생산성 및 정합성 향상** * 개발자는 디자인 의도를 파악하기 위해 문서를 뒤적이는 대신, 실제 구현에 필요한 Props와 구조가 포함된 코드를 바로 복사하여 사용할 수 있어 개발 속도가 체감될 정도로 빨라집니다. * React, SwiftUI, Jetpack Compose 등 다양한 프레임워크를 지원하여 웹과 모바일 환경 모두에서 일관된 개발 경험을 제공합니다. * 디자인 시스템이 업데이트되면 연결된 코드 스니펫도 함께 관리되므로, 디자인과 개발 사이의 'Source of Truth(신뢰할 수 있는 단일 출처)'를 유지하기 용이합니다. **도구 중심에서 협업 중심으로의 전환** * Code Connect는 단순한 자동화 도구를 넘어 디자이너와 개발자가 컴포넌트의 이름, 속성, 상태에 대해 합의된 명칭을 사용하게 만드는 강한 구속력을 제공합니다. * 개발자는 디자인 요소 뒤에 숨겨진 실제 코드 논리를 즉시 파악할 수 있어, 디자인 시스템 채택률(Adoption rate)을 높이는 데 기여합니다. 기술적 자동화의 궁극적인 목표는 사람이 하던 일을 완전히 대체하는 것이 아니라, 사람과 시스템 사이의 마찰을 최소화하는 것입니다. 디자인 시스템을 구축 중인 팀이라면 단순한 스타일 가이드를 넘어, 기존의 코드 자산을 디자인 도구와 유기적으로 결합하는 '연결 중심의 자동화'를 도입했을 때 가장 큰 효용을 얻을 수 있습니다.

다크 모드 밝히 (새 탭에서 열림)

피그마는 프로토타이핑 도구에 물리 기반의 스프링 애니메이션을 도입하여 더욱 생동감 있고 자연스러운 사용자 경험을 구현했습니다. 기존의 시간 기반(easing curve) 방식이 가진 한계를 극복하기 위해 물리 법칙인 '조화 진동자(Harmonic Oscillator)' 모델을 채택했으며, 이를 통해 애니메이션이 도중에 중단되거나 변경되어도 속도의 연속성을 유지하며 부드럽게 이어지도록 설계했습니다. 결과적으로 디자이너는 복잡한 수식 없이도 직관적인 컨트롤러를 통해 실제 사물처럼 반응하는 정교한 인터랙션을 제작할 수 있게 되었습니다. ### 이징 곡선의 한계와 물리 기반 애니메이션의 필요성 * 전통적인 애니메이션 방식인 '큐빅 베지어(Cubic Bezier)' 곡선은 정해진 시간 내에 움직임이 완료되어야 하므로, 사용자의 급격한 입력 변화나 애니메이션 중첩 시 끊기는 듯한(jerkiness) 느낌을 줍니다. * 스프링 애니메이션은 물리학의 질량, 강성, 감쇠 개념을 도입하여 애니메이션이 고정된 시간이 아닌 '속도'와 '힘'에 반응하게 만듭니다. * 이를 통해 요소가 목적지에 도달할 때의 탄성(bounce)을 자연스럽게 표현할 수 있으며, 이전 동작의 운동량을 다음 동작으로 매끄럽게 전달할 수 있습니다. ### 스프링 동작을 결정하는 수학적 모델: 감쇠 조화 진동자 * 피그마는 스프링의 움직임을 계산하기 위해 미분 방정식인 '감쇠 조화 진동자' 공식($mx'' + cx' + kx = 0$)을 사용합니다. * 애니메이션의 상태는 세 가지로 구분됩니다: * **저감쇠(Underdamped):** 마찰이 적어 목표 지점을 지나쳐 앞뒤로 흔들리며 탄성이 발생하는 상태입니다. * **임계 감쇠(Critically Damped):** 흔들림 없이 가장 빠르게 목표 지점에 도달하는 최적의 상태입니다. * **과감쇠(Overdamped):** 마찰이 강해 목표 지점까지 아주 천천히 도달하는 상태입니다. * 피그마는 매 프레임마다 수치 해석적 시뮬레이션을 돌리는 대신, 해석적 해(Analytical solution)를 사용하여 특정 시간 $t$에서의 위치를 즉각 계산함으로써 성능 효율을 극대화했습니다. ### 디자이너를 위한 직관적인 파라미터 설계 * 질량(Mass), 강성(Stiffness), 감쇠(Damping)라는 물리 용어는 디자이너가 직관적으로 이해하기 어려울 수 있습니다. * 피그마는 이를 시각적인 그래프와 미리보기를 통해 제공하며, 특히 '탄성(Bounce)'과 '속도'를 조절하는 직관적인 UI를 구현하여 물리 법칙을 모르는 사용자도 원하는 느낌을 쉽게 찾을 수 있도록 했습니다. * 또한, 이론적으로 스프링은 영원히 멈추지 않으므로, 움직임이 특정 임계값(Epsilon) 이하로 떨어지면 애니메이션이 종료된 것으로 간주하는 로직을 정교하게 설계하여 불필요한 리소스 소모를 방지했습니다. ### 연속성을 보장하는 애니메이션 인터럽트 처리 * 사용자가 애니메이션이 끝나기 전에 다른 인터랙션을 시도할 경우, 피그마의 스프링 모델은 현재의 위치와 '속도'를 그대로 다음 애니메이션의 시작점으로 전달합니다. * 기존 방식은 속도가 0에서 다시 시작하여 시각적 단절이 발생하지만, 스프링 모델은 이전의 운동 에너지를 보존하므로 물리적으로 타당하고 매끄러운 전환이 가능합니다. 현대적인 인터페이스 디자인에서 '부드러움'은 단순한 심미적 요소를 넘어 제품의 완성도를 결정짓는 핵심 요소입니다. 피그마의 사례처럼 물리 기반의 스프링 모델을 도입하면 개발자와 디자이너 모두가 납득할 수 있는 수학적 근거 위에서 사용자에게 가장 자연스러운 반응형 인터랙션을 제공할 수 있습니다. 특히 인터랙션이 빈번한 모바일 앱이나 복잡한 웹 대시보드를 개발할 때, 고정된 시간 기반의 애니메이션보다는 물리 법칙을 활용한 접근을 권장합니다.

피그마 내부 이야기: 엄 (새 탭에서 열림)

Figma는 복잡한 협업 환경에서 수많은 댓글을 60fps의 부드러운 속도로 렌더링하기 위해 React의 기본 성능 한계를 극복한 과정을 공유합니다. 단순히 컴포넌트를 최적화하는 수준을 넘어, 브라우저의 레이아웃 계산 방식을 이해하고 불필요한 리렌더링을 원천 차단하는 아키텍처적 변화를 시도했습니다. 그 결과, 수천 개의 댓글이 있는 파일에서도 끊김 없는 사용자 경험을 제공할 수 있게 되었습니다. ### 대규모 댓글 목록의 성능 병목 현상 * 수천 개의 댓글이 존재할 때 React가 모든 요소를 DOM에 유지하면 메모리 사용량과 렌더링 시간이 기하급수적으로 증가하는 문제가 발생했습니다. * 사용자가 스크롤할 때마다 발생하는 상태 업데이트가 전체 컴포넌트 트리를 재평가하게 만들어, 브라우저가 초당 60프레임을 유지하지 못하고 화면이 버벅이는 현상이 나타났습니다. * 특히 각 댓글의 내용에 따라 높이가 가변적인 특성 때문에, 브라우저가 레이아웃을 다시 계산(Reflow)하는 과정에서 막대한 CPU 자원을 소모했습니다. ### 가상 리스트(Windowing)와 동적 높이 관리 * 화면에 현재 보이는 부분만 렌더링하는 가상화(Windowing) 기법을 적용하여 실제 DOM 노드 수를 수천 개에서 수십 개 수준으로 압축했습니다. * 댓글마다 높이가 다른 문제를 해결하기 위해, 렌더링 전에 각 요소의 높이를 측정하고 이를 캐싱하는 메커니즘을 구현하여 스크롤 위치를 정확하게 계산했습니다. * 사용자가 빠르게 스크롤할 때 빈 화면이 보이지 않도록 '오버스캔(Overscan)' 영역을 설정하여 위아래로 여분의 컴포넌트를 미리 렌더링했습니다. ### React 상태 관리의 탈중앙화와 구독 모델 * React의 전형적인 단방향 데이터 흐름은 상위 컴포넌트의 상태 변경 시 하위 트리 전체를 리렌더링하므로, 대규모 목록에서는 부적합하다고 판단했습니다. * 이를 해결하기 위해 각 댓글 컴포넌트가 중앙 스토어를 직접 구독(Subscription)하게 하여, 특정 댓글이 수정될 때 해당 컴포넌트만 정밀하게 업데이트되도록 설계했습니다. * 이러한 '밀어내기(Push)' 방식의 업데이트를 통해 불필요한 VDOM 비교(Reconciliation) 과정을 생략하고 CPU 부하를 획기적으로 줄였습니다. ### 브라우저 렌더링 엔진 최적화 * CSS의 `contain` 속성(예: `contain: layout`)을 활용하여 특정 댓글의 변화가 전체 페이지의 레이아웃에 영향을 주지 않도록 브라우저에게 명시적인 힌트를 제공했습니다. * `requestIdleCallback` API를 도입하여 사용자 상호작용에 즉각 필요하지 않은 비핵심 작업들은 브라우저의 유휴 시간에 처리되도록 스케줄링했습니다. * 마우스 오버 효과와 같은 고빈도 인터랙션은 React 상태를 거치지 않고 CSS 클래스 조작이나 직접적인 DOM 접근을 통해 처리하여 즉각적인 반응성을 확보했습니다. 대규모 웹 애플리케이션에서 극도로 매끄러운 성능을 달성하려면 React의 추상화 계층에만 의존하지 말고 브라우저의 실제 렌더링 메커니즘을 깊게 제어해야 합니다. 초기 개발 단계에서는 생산성을 위해 표준 React 패턴을 따르되, 성능 임계점에 도달한 복잡한 UI에서는 가상화, 상태 구독 모델, 레이아웃 격리 등의 로우레벨 최적화 기법을 도입하는 것을 권장합니다.