디자인 생산성을 높이는 피그마 플러그인을 만들어보자 2부: 개발 편

Apr.22.2025 이재희

Design Web Frontend

들어가며

"디자이너님! 혹시 다음 기획 피그마 시안 나왔을까요?"

프론트엔드 개발자라면 한 번쯤 남겨봤을 질문일 겁니다.

디자이너는 UI/UX를 설계하고 프론트엔드 개발자는 이를 구현합니다. 두 역할은 밀접하게 연결되어 있지만, 서로의 작업 방식과 프로세스를 깊이 이해하기는 쉽지 않죠.

개발자들은 반복 작업을 자동화하는 스크립트를 작성하거나 필요한 도구를 직접 구현하며 생산성을 높이기 위해 노력합니다. 디자이너도 마찬가지입니다. 비효율적인 과정을 개선하고자 끊임없이 고민합니다. 하지만 디자이너가 효율을 높이기 위한 도구를 직접 만들기는 쉽지 않습니다. 마치 개발자에게 새로운 피그마 시안을 만들어보라는 것과 같겠죠.

디자인옵스TF는 바로 이 고민에서 출발했습니다.
디자이너와 개발자가 모여 생산성을 높이고 디자인 프로세스를 개선하는 도구를 개발하는 조직입니다.

배달의민족 서비스는 빠르게 변화하며 새로운 기능이 출시되는 주기도 짧습니다.
자연스럽게 유저 테스트를 위한 프로토타입이나 디자인 시안을 빈번하게 만들어야 하죠.

하지만 디자이너들은 아래 이미지와 같은 시안을 만들 때마다 가격, 이미지, 평점 같은 데이터를 일일이 찾고 교체하는 과정에 너무 많은 시간을 쏟고 있었습니다. 더 좋은 프로덕트와 유저 경험을 고민하기에도 시간이 충분치 않은데, 대부분의 디자이너가 같은 문제를 경험하고 있었다면 지금껏 얼마나 많은 시간을 썼던 걸까요?

데이터 교체 과정의 비효율을 해결하고자 TF에 속한 두 명의 디자이너와 세 명의 개발자가 머리를 맞댔습니다. 긴 고민 끝에 목표를 정했습니다.

"클릭 한 번으로, 실제 데이터를 피그마에 적용해 보자"

이번 글에서는 개발자의 시선에서, 피그마 플러그인 ‘데이터브릿지’를 만들어간 이야기를 풀어보려고 합니다.


피그마 플러그인의 실행 구조

우리의 목표는 클릭 한 번으로 배달의민족 앱의 실제 데이터를 피그마 디자인 시안에 바로 적용하는 것입니다. 이를 위해서는 다음과 같은 동작을 수행할 수 있어야 하겠죠.

  1. UI/UX: 클릭과 같은 상호작용을 위한 사용자 인터페이스가 필요해요.
  2. API 연동: 외부 서버에서 진짜 데이터를 불러올 수 있어야 하고요.
  3. 피그마 문서 수정: 가져온 데이터를 피그마의 텍스트나 이미지 레이어에 직접 적용할 수 있어야 합니다.

이 모든 동작은 피그마 플러그인으로 구현할 수 있습니다.

하지만 피그마 플러그인이라니 생경하기만 합니다. 디자인 시안을 확인하는 문서로써 피그마를 사용하는 것은 익숙하지만, 직접 UI/UX를 설계하고 플러그인을 활용해 자유자재로 다뤄본 경험은 없었기 때문이겠죠.

피그마 플러그인 구조를 살펴보기 전에, 피그마 애플리케이션의 전체 구조를 확인해 볼게요.

피그마는 피그마 서버(figma.com)로부터 디자인 문서 데이터를 불러와, 자체 로직(Figma Code)을 통해 브라우저(데스크톱 애플리케이션)에 문서들을 렌더링합니다. 여기까지는 일반적인 웹 애플리케이션의 동작 방식과 동일합니다.

이번엔 피그마 플러그인이 실행되는 구조를 살펴볼 차례입니다.

피그마 플러그인은 일반적인 웹 애플리케이션과 조금 다르게, 보안과 제약조건으로 인해 독특한 구조로 작동합니다.

피그마 플러그인의 코드는 QuickJS라는 자그마한 샌드박스 환경에서 실행됩니다. 이 환경은 JavaScript ES6+를 지원하지만 Browser API에는 접근할 수 없습니다. 그렇다면 어떻게 플러그인의 UI를 렌더링하거나 Broswer API에 접근할 수 있을까요? 생각보다 간단합니다. figma.showUI()라는 짧은 코드를 호출하면 됩니다.

이 코드를 수행하면 피그마는 코드에서 정의한 400 X 300 사이즈의 iframe을 생성하고 UI를 렌더링합니다. 이 iframe은 유저가 정의한 HTML, CSS, Javascript를 렌더링합니다. 중요한 점은 iframe은 내부적으로 독립되어 있어 플러그인 코드와 직접 정보를 주고받을 수 없습니다. 따라서 IPC(Inter-Process Communication) 통신과 유사하게 Figma Code가 매개체 역할을 하여 Message Passing 방식으로 통신합니다.

마지막으로 용어를 정리해 볼게요.

  • Core: 피그마 애플리케이션을 구동하는 핵심 코드
  • Main Process: 플러그인 코드가 실행되는 샌드박스 환경
  • UI Process: 플러그인 UI가 렌더링되는 iframe
  • IPC: 두 프로세스가 메시지 전달(Message Passing) 방식으로 소통하는 방법

두 개의 독립적인 프로세스와 메시지 통신 방식, 뭔가 작은 운영체제 같지 않나요?

플러그인 메시지 통신 구조

다음으로는 플러그인 속 세상을 들여다 보겠습니다.

피그마 플러그인은 일반적인 웹 애플리케이션 환경과 다르게, 피그마 애플리케이션 내부의 샌드박스 환경에서 실행되고 있죠. 또한 UI Process와 Main Process로 분리된 구조를 가지고 있고, 두 개의 프로세스는 Message를 기반으로 통신합니다. 그렇다면 각 프로세스 파일들을 어떻게 피그마에서 불러오는 걸까요?

프로세스 파일들을 피그마에서 불러오는 과정은 바로 manifest.json 파일에서 시작됩니다. 이 파일은 플러그인의 이름, 각 프로세스별 entry 파일의 경로, 권한, API 버전 등 플러그인의 전체 구성 정보를 정의합니다.

manifest.json에 포함된 mainui에 해당하는 entry 파일들은 각 프로세스에 대응되어 실행됩니다. 유저가 피그마 플러그인을 직접 설치하거나, 개발자 옵션(Plugins > Development > Import plugin from manifest)에서 manifest.json 파일을 불러오면, 이 정보를 바탕으로 플러그인을 실행하는 것이죠.

이제는 각 프로세스의 코드들이 구체적으로 어떤 역할을 하고 소통하는지 알아볼 차례입니다.
피그마가 작은 운영체제였다면, 플러그인은 하나의 애플리케이션입니다. 대부분의 웹 애플리케이션이 그렇듯, 플러그인도 클라이언트와 서버, DB로 구성돼 있어요. 아래 다이어그램을 살펴볼게요.

1. UI Process

UI Process는 클라이언트, 즉 유저 인터페이스를 렌더링하고 유저와 상호작용하는 프로세스입니다. 독립된 iframe으로 렌더링되며, 피그마 내부의 샌드박스 환경인 Chromium 기반 WebView 위에서 실행됩니다.

iframe은 피그마 애플리케이션의 DOM에 직접 포함되지 않으며, UI 레벨에서 피그마 문서를 직접 조작하려면 Main Process로 요청을 보내야 합니다.

또한 iframe 내부에서 네트워크 요청을 보내는 경우, 보안상의 이유로 window.origin"null"로 설정됩니다. 따라서 fetchXMLHttpRequest로 외부 API와 통신할 때 CORS 문제가 발생할 수 있습니다. 이는 중요한 제약 사항이니 뒤에서 심도 있게 다뤄보겠습니다.

2. Main Process

Main Process는 서버의 역할을 담당합니다. DB에서 정보를 읽어오고 수정하는 것처럼, 피그마 문서를 직접 조작하거나 데이터를 읽고 수정합니다. 단지 피그마에서 제공하는 Figma API를 이용한다는 것이 다를 뿐입니다.

POST 메소드와 같이 figma.createNode()로 새로운 노드를 만들거나, GET 메소드처럼 figma.currentPage로 현재 페이지에 저장된 문서 객체(노드 트리 구조)에 접근하는 것도 가능합니다.

3. IPC(Inter-Process Communication)

두 개의 Process는 서로 직접 접근할 수 없고, 독립되어 있어 Figma Code를 거쳐 메시지를 주고받는 방식으로 통신해야만 합니다.

통신 인터페이스는 다음과 같습니다.

UI Process는 Browser API인 window.parent.postMessagewindow.onmessage 두 개의 메소드로 송수신합니다.

Main Process는 figma.ui.postMessagefigma.ui.onmessage 로 Figma API를 사용해서 송수신할 수 있습니다.

메시지 통신을 조금 더 편리하게

본격적으로 데이터브릿지를 구현하기 위해서 이제는 데이터를 피그마 레이어에 적용할 차례입니다.

플러그인의 버튼을 눌렀을 때, 피그마의 특정 텍스트 레이어의 글자를 수정하는 기능을 구현하고 싶다면 어떻게 해야 할까요?

  1. iframe에 <button>을 렌더링하고, 클릭 이벤트를 정의합니다.
  2. 수정할 텍스트를 payload로 담아 요청합니다.
  3. 요청을 수신하면 Figma API를 활용해서 레이어의 텍스트를 수정합니다.
  4. 텍스트 수정의 성공, 실패 여부를 iframe에 응답으로 전달합니다.

코드를 쉽게 표기하고자 React, TypeScript를 사용하여 작성했습니다.

UI Process

Main Process

요구조건은 단순한데 코드가 길고 가독성이 떨어지지 않나요?

메시지 송신과 수신이라는 두 개의 동작만 있지만, 처리하는 Process의 위치에 따라 코드가 다르기 때문에 총 4개의 다른 형태가 존재합니다. 기능이 확장될수록 이런 이벤트 동작을 처리하는 코드도 많아질 테고, 자연스레 가독성도 떨어지겠죠. 이 과정을 보다 간단하게 추상화해 볼게요.

먼저 인터페이스를 정의합니다. 요청과 응답은 방향성을 내포하고 있기에, 단순히 보내고 받는 메시지의 단위를 Event라고 표현할 수 있습니다. 요청된 동작을 저장하는 메모리 공간인 eventHandler가 필요합니다.

다음은 요청을 보내는 함수입니다. window의 존재 여부에 따라 처리하는 프로세스를 분기하여 이벤트를 전송합니다.

마지막으로 요청을 수신하는 함수입니다. on 메소드는 이벤트를 수신하면 이벤트의 핸들러를 저장해둡니다. 그리고 invokeEventHandler가 실행되어 프로세스에 따라 메시지 리스너를 동작시키고 이벤트 핸들러에서 알맞은 키를 찾아 핸들러를 실행시킵니다.

그러면 처음에 본 코드를 다시 한번 작성해볼 시간입니다.

코드의 가독성과 재사용성이 전보다 나아졌어요. 더 복잡한 비즈니스 로직과 이벤트 사이클들의 체이닝이 발생한다고 해도 관리가 용이해집니다.

피그마 플러그인의 Message Passing 방식이 이제는 조금 더 친근하게 느껴지나요? 드디어 피그마 레이어에 데이터를 적용하는 기능은 완성이 되었어요.

데이터를 적용하며 마주한 문제

"클릭 한 번으로, 실제 데이터를 피그마에 적용해 보자"

다시 한 번 본래 목표를 되짚어보겠습니다.

지금까지의 과정으로 클릭 한 번으로 피그마 레이어에 데이터를 적용하는 것은 달성했습니다. 이번에는 실제 API를 호출해서 데이터를 적용 가능한 형태로 정제할 단계입니다. 외부 네트워크 접근 권한 허용을 위해서는 manifest.json 파일에서 networkAccess.allowedDomains에 필요한 도메인을 추가해 주어야 합니다. 하지만 권한 설정을 해주었음에도 API가 제대로 호출되지 않는 문제가 발생했습니다. 왜 그런걸까요?

[문제 1: CORS 이슈]

피그마 플러그인에서 fetchXHR과 같은 Browser API는 UI Process(iframe)에서만 사용할 수 있습니다. 하지만 iframesandbox 속성은 보안상의 이유로 window.origin이 기본값 "null" 로 설정됩니다. 즉, 저희가 원하는 데이터를 요청하기 위해 배민 앱에서 사용 중인 API를 직접 호출하면 CORS(Cross-Origin Resource Sharing) 오류를 피할 수 없습니다.

[해결 방법: 프록시 서버 도입]

배민 앱의 백엔드에서 모든 origin에 대해 요청을 열어줄 수는 없겠죠. CORS 문제를 해결하기 위해 프록시 서버를 도입하기로 했습니다. 요청을 프록시 서버로 우회하고, 이 서버가 API와 통신하여 데이터를 전달하도록 설계해 보았습니다.

하지만 이 역시도 보안 측면을 간과해서는 안 됩니다. 프록시 서버는 사내 VPN 환경에서만 호출할 수 있도록 방화벽을 설정하여 외부 접근을 막았습니다.

하지만 산 넘어 산이라고 새로운 문제를 마주하게 됩니다.

[문제 2: 실시간 데이터의 한계]

API는 실시간 데이터를 제공하기 때문에, 시간이 지남에 따라 정보가 바뀌는 경우가 많습니다. 예를 들어볼게요.

  • 오후 10시가 지나자 상품이 품절 상태가 되었어요.
  • 공휴일이라 가게가 문을 닫았어요.
  • 5분 전과 리뷰 평점, 할인율이 달라졌어요.

프로토타입을 만드는 중에 데이터가 바뀌어버리면, 일관성 있는 디자인 테스트를 수행하기 어렵게 됩니다. 같은 데이터가 필요한데 5분 만에 정보가 바뀌어버린다면 곤란하겠죠?

[해결 방법: API 스냅샷 저장]

API 응답의 스냅샷을 추출하여 S3 버킷에 저장하는 방식을 도입했습니다. 마치 테스트 코드 작성 시 API 응답을 모킹하는 방식처럼요. 여기까지의 구조를 다시 정리해보겠습니다.

API의 응답을 스냅샷으로 저장하여, S3 버킷에 업로드합니다.
피그마 플러그인에서 데이터를 요청하면, 프록시 서버를 통해 S3 버킷에서 필요한 데이터를 가져옵니다.
데이터를 디자인 규칙에 맞게 정제하여, 피그마 레이어에 적용합니다.

실시간 데이터의 불안정성에서 벗어나, 일관성 있게 데이터를 활용할 수 있게 되었습니다. 필요한 정보를 언제든 플러그인에 불러올 수 있고, 레이어에 적용하는 이벤트를 구현했다면 MVP를 출시할 차례입니다.

버튼 클릭 하나만으로 몇 초 만에 수십 개의 레이어에 데이터가 적용되는 모습입니다. 디자이너가 일일이 손으로 수정했다면 꽤나 오랜 시간이 걸렸을 작업이지만, 더 이상 어렵고 복잡한 과정이 아닙니다. 클릭 몇 번이면 뚝딱 수정되기 때문입니다.

기능 확장을 위한 고도화

플러그인에 점점 더 유용하고 편리한 기능을 추가하다 보니, 기술적으로 해결해야 할 문제들도 함께 늘어났습니다. 특히 피그마 플러그인은 일반적인 웹사이트와 작동 방식이 조금 달라서, 익숙하지 않은 새로운 방법들을 찾아야 하는 도전 과제들이 있었죠. 이러한 기술적 장벽들을 어떻게 넘어섰는지 이야기해 드릴게요.

[In-Memory 라우팅 적용]

데이터브릿지 플러그인은 배달의민족 서비스처럼 푸드, 커머스 등 다양한 종류의 데이터를 다룹니다. 이 모든 정보를 한 화면에 보여주기보다는, 카테고리별로 화면을 나누고 필요에 따라 단계적으로 보여주는 것이 디자이너분들이 사용하기에 훨씬 직관적이라고 생각했어요. 마치 웹사이트에서 메뉴를 클릭하면 해당 페이지로 이동하는 것처럼요.

일반적인 브라우저 라우터는 window.location을 활용하여 웹사이트의 주소창에 주소가 바뀌면서 페이지가 이동합니다. 하지만 이는 피그마 플러그인에서는 동작하지 않아, 일반 웹사이트와 달리 플러그인 창 내부에서 자유롭게 페이지를 이동하는 기능(URL 기반 라우팅)을 만들기 어렵다는 제약이 있었습니다.

이 문제를 해결하기 위해, 주소를 바꾸지 않고도 플러그인 내부에서 마치 페이지가 이동하는 것처럼 보이게 하는 기술인 react-router-domMemoryRouter를 적용했습니다. MemoryRouter는 히스토리를 메모리 상에만 유지하기 때문에, 브라우저 주소를 변경하지 않고도 라우팅이 가능합니다. 이를 통해 페이지와 레이아웃을 계층화하고, 페이지 간 전환도 자연스럽게 구현할 수 있었습니다.

[유저 액션 수집 로그 적용]

플러그인을 계속 발전시키려면, 디자이너분들이 어떤 기능을 자주 사용하고 어떤 점을 불편하게 느끼시는지 아는 것이 중요했습니다. 이를 위해 유저 액션의 로그 수집 도입을 고민하였습니다. 어떤 데이터를 빈번하게 사용하는지, 개선을 해야하는 사용성이 낮은 기능은 무엇인지 파악할 수 있기 때문입니다.

일반 웹사이트에서는 GA(Google Analytics)와 같은 도구를 사용하여 유저를 식별하고, 행동을 트래킹합니다. 이 과정에서 일반적으로 쿠키(Cookie)가 사용됩니다. 하지만 피그마 플러그인은 엄격한 CSP(Content Security Policy)와 보안 정책으로 사용자 컴퓨터에 쿠키나 로컬 스토리지 같은 정보를 함부로 저장하거나 사용할 수 없습니다. 사용을 위해서는 sandbox="allow-same-origin"이 설정되어 있어야 하지만 피그마 플러그인은 허용하지 않기 때문입니다.

다행히 피그마에서는 자체적으로 플러그인 내부에 정보를 안전하게 저장할 수 있는 공간인 figma.clientStorage를 제공하고 있었습니다. 여기에 사용자를 식별할 수 있는 최소한의 정보(figma.currentUser)를 저장하고, 이 정보를 분석 서버로 보내는 방법을 고민했습니다. 외부 서버 통신을 위해서는 manifest.json 파일에 해당 서버의 origin을 등록하면 전송이 가능하겠다는 생각이 들었습니다.

이러한 접근 방식의 구현 가능성을 검토하던 중, 감사하게도 mixpanel-figma 이라는 라이브러리를 찾았습니다. 이 라이브러리는 분석 도구인 Mixpanel의 JS SDK를 피그마 플러그인 환경에 맞게 경량화한 버전으로, 쿠키 없이 유저 정보를 외부에서 주입받을 수 있도록 설계되어 있었습니다. 덕분에 피그마의 보안 제약 환경 내에서도 사용자 행동 데이터를 효과적으로 수집하고 분석할 기반을 마련할 수 있었습니다.

이렇게 수집한 사용 데이터를 실제로 플러그인 개선에 어떻게 활용했는지 보여드릴게요.

아래는 저희가 수집한 데이터를 시각화한 대시보드 예시입니다. 보시면 유독 보라색 막대(커머스 상품의 데이터 적용)의 클릭 수가 다른 항목보다 훨씬 높다는 것을 알 수 있습니다.

분석해보니, 이 카테고리는 선택할 수 있는 데이터 종류가 너무 많아서, 원하는 데이터를 찾기 위해 사용자들이 여러 번 반복해서 ‘랜덤 생성’ 버튼을 누르고 있었기 때문이었습니다

이 불편함을 해결하기 위해 두 가지 개선을 진행했습니다.

  1. 최근 사용 데이터 퀵메뉴: 최근 사용했던 데이터를 바로 다시 선택할 수 있는 기능을 추가했습니다.
  2. 검색 기능: 원하는 데이터를 직접 검색해서 빠르게 찾을 수 있도록 검색창을 만들었습니다.

이처럼 데이터를 기반으로 사용자의 불편함을 정확히 파악하고, 실제 플러그인 기능 개선으로 이어지도록 노력하고 있습니다.

마치며

지금까지 지난 6개월 간 달려왔던 여정을 공유드렸습니다.

함께 프로덕트를 만들어 왔던 TF의 팀원들은 사실 모두 다른 팀에 속해있습니다. 데이터브릿지는 각자의 업무를 하면서 틈날 때마다 조금씩 노력을 쌓아 올려 만든 프로덕트입니다.

그럼에도 계속해서 달려올 수 있었던 원동력은, 저희의 고민과 노력이 함께 하는 동료들의 업무 효율에 큰 도움이 되고 있음을 피부로 느낄 수 있었기 때문입니다. 자그마한 기능을 제공했던 첫 MVP부터 지금까지 따뜻한 피드백과 함께 많은 관심을 가져주셨던 분들 덕분입니다.

프론트엔드 개발자는 프로덕트를 구현하는 사람이면서도, 동시에 프로덕트를 가장 잘 이해하고 사용하는 유저라고 생각합니다.

때로는 익숙지 않은 아키텍처를 이해해야 하고 여러 시행착오를 헤쳐 나가는 고된 과정을 겪으면서도, 내 손으로 만드는 프로덕트가 누군가에게 도움이 되고 좋은 경험을 제공할 수 있을 때 가장 큰 보람을 느끼곤 합니다.

그 과정을 담은 이 글이 피그마 플러그인을 구현하고 있거나, 더 나은 생산성을 고민하고 있는 분들께 도움이 되었길 바랍니다.

데이터브릿지의 더 자세한 기능들과 개발 과정이 궁금하시다면, 디자이너의 이야기도 함께 읽어 보시기 바랍니다.

👉 디자인 생산성을 높이는 피그마 플러그인을 만들어보자 1부: 디자인 편