[디자인 시스템] 아이콘 자동 추출 피그마 플러그인 만들기

한낱·2024년 6월 2일
1

디자인 시스템

목록 보기
7/9
post-thumbnail

서론

이번 디자인 시스템 과제는 다른 기업에서는 어떻게 아이콘과 관련된 자동화를 진행하고 있는지 조사하고 따라서 실습해보기였다.
당근, 채널톡, 쏘카 등을 참고해보니 대부분 두 종류의 자동화가 존재했다.

  1. 피그마에서 아이콘을 자동으로 추출해 github PR 날리기
  2. 아이콘 파일을 토대로 아이콘 컴포넌트 생성하기

이 중 첫번째 자동화를 실습해보았다.

피그마 플러그인

피그마 플러그인을 개발하기 위해서 배민 블로그를 꽤 참고했다. 해당 블로그에 따르면 피그마 플러그인이란

외부의 시스템/데이터를 필요로 하거나 특정 상황에 더 특화된 기능을 필요로 할 때, 사용자가 직접 본인에게 필요한 기능을 정의할 수 있도록 제공되는 시스템

을 말한다.

아키텍쳐는 sandbox를 통해 figma canvas에 접근할 수 있는 api를 사용할 수 있고, UI 역할을 담당하는 iframe을 통해 유저의 입력을 받거나 브라우저 api를 사용할 수 있다.
이 둘 사이의 통신은 reactNative나 pwa에서도 사용했던 postMessage를 통해 주고 받는다.

보일러 플레이트

피그마 플러그인을 React로 개발하기 위해서는 까다로운 설정을 좀 거쳐야 한다.
다행히도 참고하던 블로그에 리액트 설정이 되어 있는 보일러 플레이트가 존재했다.
해당 코드를 빌드한 뒤 피그마에서 실행해보면 다음과 같이 확인할 수 있다.

아이콘 자동 추출 피그마 플러그인 실습

운 좋게도, 채널톡과 당근 레파지토리에서 개발하고자 하는 피그마 플러그인에 대한 자료를 찾을 수 있었다.
둘을 사용해본 결과, 사용 과정에서 주된 차이는 당근 레파지토리의 경우 아이콘 컴포넌트라는 것을 특정하여 가져오기 위해 아이콘이 존재하는 frame의 이름을 제한하여 해당 이름 하위에 존재하는 모든 컴포넌트를 가져오는 방식으로 동작하고 있었다.
반면, 채널톡의 경우, 유저가 선택한 컴포넌트를 추출하는 방식으로 동작하고 있었고, 기왕이면 사용자에게 어떤 제약도 주고 싶지 않아 채널톡을 참고해 보았다.

코드

일단 코드는 크게 plugin 부분과 UI 부분으로 나뉜다.

plugin

// 채널톡 코드는 컴포넌트로 구분된 노드를 찾아 svg로 변환한다.
// findAllComponentNode는 컴포넌트들을 찾아내기 위한 코드이다.
export const findAllComponentNode = (rootNode: SceneNode) => {
  const result: ComponentNode[] = [];
  function findComponentNode(node: SceneNode) {
    if (isComponentNode(node)) {
      result.push(node);
      return;
    }
    if ('children' in node) {
      node.children.forEach(findComponentNode);
    }
  }
  findComponentNode(rootNode);
  return result;
};

// 찾은 컴포넌트들을 svg로 변경하고,
// postMessage를 통해 전달한다.
async function extractIcon() {
  const componentNodes = figma.currentPage.selection
    .map(findAllComponentNode)
    .flatMap((v) => v);

  const svgs = await Promise.all(
    componentNodes.map(async (node) => {
      const svg = await node.exportAsync({ format: 'SVG_STRING' });
      const id = node.name;
      return {
        svg,
        id,
      };
    }),
  );

  const svgByName = svgs.reduce((acc, cur) => {
    if (!cur?.id) {
      return acc;
    }
    acc[cur.id] = cur;
    return acc;
  }, {} as SvgByName);
  
  const pluginMessage: ExtractIconPluginMessage = {
    type: 'extractIcon',
    payload: {
      svgByName,
    },
  };

  figma.ui.postMessage(pluginMessage);
}

UI

화면으로는 추출 성공 화면, 홈 화면, 아이콘 추출 화면이 존재하는데, 리액트 환경으로 개발할 수 있도록 webpack을 설정해놓은 덕분에 일반 웹 화면 개발하듯이 개발할 수 있다.

// Home 화면
import React, { useCallback } from 'react';

import { useNavigate } from 'react-router-dom';

function Home() {
  const navigate = useNavigate();

  const handleClickExtract = useCallback(() => {
    navigate('extract');
  }, [navigate]);

  return (
    <div>
      <button type="button" onClick={handleClickExtract}>
        아이콘 추출
      </button>
    </div>
  );
}

export default Home;

hooks

채널톡 피그마 플러그인에서 눈에 띄는 것은 hook인데 octokit이라는 라이브러리를 활용하여 손쉽게 PR을 날릴 수 있다.
octokit에서 getCommit, getRef, getTree, createBlob, createCommit, create 등의 함수를 지원해주기 때문에 사용하는 깃허브에 대한 정보를 넣어주면 쉽게 PR 생성을 할 수 있다.

// pr 생성 함수
const createPullRequest = useCallback(
    async (params: {
      title: CreatePullRequestParameters['title'];
      body: CreatePullRequestParameters['body'];
      head: CreatePullRequestParameters['head'];
      base: CreatePullRequestParameters['base'];
    }) => {
      const { data } = await octokit.rest.pulls.create({
        owner,
        repo,
        ...params,
      });
      return data;
    },
    [octokit, owner, repo],
  );

결과


(실제 작성된 전체 코드는 링크에서 확인하실 수 있습니다.)

profile
제일 재밌는 개발 블로그(희망 사항)

0개의 댓글