OROR Forge: Figma to Code 도구 제작기 (2) 실전용으로 만들기

프롤로그

안녕하세요, OROR 프론트엔드 팀의 덴입니다! 1부에서는 Figma 디자인의 개념과 플러그인을 바탕으로 디자인을 CSS 코드로 변환하는 과정을 알아보았습니다. 또, 해당 글에서는 Figma의 디자인 개념을 역으로 HTML과 CSS 코드를 통해서 익혀보고 이해할 수 있는 시간을 가볍게 가져보았습니다.

이번 2부에서는 이렇게 만들어진 디자인 코드를 실전에서 활용할 수 있도록, 실제 서비스에서 자주 사용되는 Tailwind CSS와 React 조합의 코드 생성 도구를 구현한 과정에 대해 담아보았습니다. 본 내용에서 독자분들이 Deisgn To Code 세계에 딥다이브해, 다양한 인사이트를 얻어가기를 희망합니다.

인라인 스타일을 실전용 CSS로 만들기: TailwindCSS 활용하기

이렇게 디자인을 HTML와 CSS로 정확하게 만들어내는 과정은 완료했으나 이 코드는 아직 실제 현업에서 사용하기에는 어렵습니다. 인라인 스타일(Inline-style)의 코드는 유지 보수와 성능 최적화를 어렵게하기 때문입니다. 이러한 한계를 극복하고 실전성을 갖추기 위해, 저희 팀에서는 TailwindCSS를 사용하기로 결정했습니다.

TailwindCSS의 선택 이유

저희 팀에서 TailwindCSS를 선택한 이유는 아래와 같습니다.

  • 인라인 스타일과의 유사성: TailwindCSS는 인라인 스타일과 유사한 방식을 제공하면서도, CSS 파일로 분리하여 관리할 수 있는 유연성을 제공합니다.
  • 서식 기반 접근법: TailwindCSS는 서식 기반의 접근법을 취함으로써 인라인 스타일과 효율적으로 매칭됩니다.
  • 높은 사용률과 실용성: TailwindCSS는 널리 사용되고 있기 때문에 검증되었을 뿐 아니라 실용성도 갖춘 프레임워크입니다.

TailwindCSS로의 변환 프로세스

기존 인라인 스타일(Inline-style) CSS 속성들을 TailwindCSS로 만들기 위해, 이들을 TailwindCSS의 클래스로 적절하게 매핑하는 빌더 함수를 구현했습니다. 아래는 변환 예시를 보여주는 코드입니다.

				
					const createTailwindCSSBuilder = (node, cls = {}) => {
 function transform(prop, value) {
   value = String(value);


   switch (prop) {
     case "top":
     case "right":
     case "bottom":
     case "left":
       value = value.replace(/\s/g, "");
       if (value === "100%") return [prop + "-full"];
       if (value === "50%") return [prop + "-1/2"];
       return [prop, value];


     case "transform":
       if (value === "translate(-50%,-50%)") return ["-translate-x-1/2 -translate-y-1/2"];
       if (value === "translateX(-50%)") return ["-translate-x-1/2"];
       if (value === "translateY(-50%)") return ["-translate-y-1/2"];
       return ["transform", value];


     case "width":
       if (value === "100%") return ["w-full"];
       return ["w", value];


     case "min-width":
       return ["min-w", value];


     case "max-width":
       return ["max-w", value];


     // ... 추가적인 속성 변환 규칙
   }
 }
};


// Node 및 클래스 변환 사용 예시
const cssBuilder = createTailwindCSSBuilder(node);
				
			

이러한 변환을 통해, Figma 디자인을 실전용 CSS로 효과적으로 변환하고 이를 실제 프로젝트에 적용할 수 있게 되었습니다.

<실제 서비스에서 사용이 가능한 React와 TailwindCSS 조합의 코드를 생성하는 장면>

추가적인 CSS 지원을 위한 구조

처음부터 TailwindCSS로 만들지 않고 인라인 스타일(Inline-style)을 거쳐서 변환을 하게 하는 방식을 채택한 이유는, 추후 다른 CSS 프레임워크를 지원해야 할 때 보다 유연한 구조를 갖추기 위해서였습니다. 저희 팀에서는 이를 위해 어댑터 패턴(Adapter pattern)의 형태로 시스템을 설계하였으며, 추가적으로 SCSSStyled-components와 같은 다른 방식의 CSS 변환도 테스트를 하고 있는 중입니다.

이로써 다양한 CSS 프레임워크와의 호환성을 높이며, 프로젝트의 여러 요구사항에 대응할 수 있는 능력을 갖추고자 하였습니다.

React Component 만들기

실무적용을 위한 React Component 변환

저희 팀은 Figma에서 추출한 HTML 코드를 실제 프로젝트에 적용할 수 있도록 React Component로의 변환을 진행했습니다. 이전에 인라인 스타일(Inline-style)을 상용 CSS 프레임워크로 변환한 것처럼, HTML 코드 역시 상용 웹 프레임워크로 전환하는 것이 필수적이었습니다.

이 기능은 HTML 코드를 JSX 문법에 맞게 변환하고 className에 TailwindCSS 코드를 적용하여 React Component의 형태로 생성하도록 해 구현할 수 있었습니다. 물론, 이 기능만으로도 충분히 실무에서 유용한 어시스턴트의 역할을 수행해 주었지만 컴포넌트에 Props와 데이터 바인딩을 적용한 이후, 디자인 변경이 있을 때마다 처음부터 이 과정을 다시 반복해야 하는 번거로움이 있었습니다.

그렇기에 디자인 변경에도 비즈니스 로직의 코드가 유지될 수 있도록 Props와 데이터 바인딩 코드를 Figma에서 생성할 수 있는 기능의 필요성을 느끼게 되었습니다.

추가 기능 개발 항목

이때, 저희가 목표한 추가 기능들은 아래와 같았습니다.

  1. 컴포넌트 Props 정의: 각 컴포넌트에 필요한 Props를 정의해, 외부에서 데이터를 주입하고 관리할 수 있는 기능을 개발해야 했습니다.
  2. 모델 및 데이터 바인딩 구현: 동적인 콘텐츠와 상호작용을 위해 UI 컴포넌트와 연결되는 데이터 모델을 정의하고 이를 통한 바인딩 기능을 구현해야 했습니다.
  3. 외부 컴포넌트와의 연동: 이미 개발된 외부 컴포넌트와의 효과적인 연동 방안도 중요한 고려사항이었습니다.
  4. 중복내용 자동 컴포넌트 화: 반복해서 사용되고 있는 형태의 코드가 존재한다면 자동으로 컴포넌트로 만들어주는 기능도 필요하다고 생각했습니다.

위와 같은 기능구현을 목표로 해, 컴포넌트의 기본적인 항목들을 작성할 수 있도록 UI를 제공하고 디자인이 변경이 되어도 별도의 수정 없이 생성된 코드를 바로 반영을 할 수 있도록 개발을 시작하였습니다.

Props: React Component Props 주입하기

Props의 중요성

React 컴포넌트에서 Props는 데이터를 전달하고 컴포넌트 간 상호작용을 가능하게 하는 핵심 요소입니다. Figma 플러그인을 통해 생성된 컴포넌트는 비즈니스 로직을 외부로부터 Props를 통해 받게 되며, 이를 통해 디자인과 기능을 분리할 수 있게 됩니다.

Props 정의 과정

아래의 과정으로 Props를 정의할 수 있었습니다.

  1. Props 정의: Figma 내에서 컴포넌트에 필요한 Props를 정의합니다. 이 과정에서 플러그인 사용자는 원하는 Props의 이름과 타입을 지정할 수 있습니다.
  2. 내부 스토리지 활용: Figma의 내부 스토리지 API를 이용하여 각 노드에 필요한 Props 정보를 저장합니다. 이 정보는 추후 코드 생성 과정에서 사용됩니다.
  3. 코드 생성: 정의된 Props 정보를 바탕으로 컴포넌트의 인터페이스와 함께 React 코드를 자동 생성합니다. 이 과정에서 Props는 ‘컴포넌트 인자’의 기능을 수행합니다.

이러한 방식으로 Props를 정의하고 관리함으로써, Figma 디자인과 React 컴포넌트 간의 연동이 효율적으로 이루어질 수 있게 되었습니다.

Props 코드 생성의 결과

Props 코드를 생성한 결과는 아래와 같았습니다.

				
					interface Props {
 room: PlayRoom
 isMobile: boolean
 roomImage: ReactNode
 onRoomJoin: MouseEventHandler
 onAndroidDownload: MouseEventHandler
}


const PlayRoomSharePage = ({
 room,
 isMobile,
 roomImage,
 onRoomJoin,
 onAndroidDownload,
}: Props) => { ... }
				
			

Models: React Component에 커스텀 데이터 구조 주입하기

Model의 필요성

React에서는 복잡한 데이터 구조를 컴포넌트의 Props로 전달할 때 종종 객체(Object) 형태를 사용합니다. 위 예시에서도 PlayRoom이라고 하는 자체적인 데이터 구조를 사용하고 있습니다. 이러한 커스텀 데이터 구조를 적용하기 위해, Model이라고 하는 기능을 구현하게 되었습니다.

Model 정의 과정

아래의 과정으로 Model을 정의할 수 있었습니다.

  1. Model 구성: 사용자는 Figma 플러그인 내에서 원하는 데이터 구조를 Model로 정의할 수 있습니다. 이 Model은 객체의 형태로 여러 속성을 포함할 수 있으며, 추후 Props로 활용됩니다.
  2. Figma와의 연동: 정의된 Model은 Figma의 디자인 요소와 연결됩니다. 사용자는 특정 노드에 Model을 할당하여, 해당 노드가 어떤 데이터 구조를 사용할지 명시할 수 있습니다.
  3. 코드 생성 시 활용: Model이 할당된 노드는 코드 생성 과정에서 해당 Model을 기반으로 한 React 컴포넌트 Props를 자동으로 생성합니다. 이는 코드의 재사용성과 유지보수성을 크게 향상합니다.

 

이제 Model을 통해 복잡한 데이터 구조의 Props를 전달하는 코드도 생성이 가능하게 되었습니다.

				
					export interface PlayRoom {
 id: number
 title: string
 description: string
}
				
			

Attributes: React Component Data-Binding 기능 구현

바인딩의 중요성

React 컴포넌트에서 바인딩은 데이터와 UI 요소 간의 연결을 의미합니다. 즉, 특정 데이터가 UI 컴포넌트의 속성(Property)이나 이벤트(Event)와 어떻게 연결되는지를 정의하는 과정입니다. 뿐만 아니라 React에서 제공하는 조건부 렌더링과 컨텐츠를 제공할 수 있는 visible 옵션과 children 옵션을 제공하여, 실제 React에서 사용하는 모든 코드를 만들 수 있도록 하였습니다.

바인딩 과정

  1. 엘리먼트 선택: 사용자는 Figma 디자인에서 바인딩할 엘리먼트를 선택합니다. 이는 특정 데이터와 연동될 UI 요소를 명시하는 단계입니다.
  2. 바인딩 설정: 선택된 엘리먼트에 대해, 어떤 데이터 혹은 이벤트와 연결할지를 설정합니다. 이 설정은 Figma 플러그인의 인터페이스를 통해 직관적으로 진행할 수 있습니다.
  3. 코드 생성: 설정된 바인딩 정보는 최종적으로 React 컴포넌트의 코드에 반영됩니다. 이 과정에서 Props, 이벤트 핸들러 등이 자동으로 생성되어 개발자의 수작업을 줄여줍니다.

바인딩 기능을 통해 Figma 디자인의 변경이나 업데이트가 발생할 때, React 컴포넌트 코드 또한 자동으로 갱신됩니다. 이는 개발자가 디자인 변경에 따른 반복적인 수정 작업에서 벗어날 수 있게 해주는 중요한 기능입니다.

연동이 완료되어 생성된 코드

연동을 완료하여 자동으로 바인딩을 수행한 코드 예시는 아래와 같습니다.

				
					return (
 ...
 <div className="...">{roomImage}</div>
 ...
 <div className="...">{room.title}</div>
 <div className="...">{room.description}</div>
 ...
 {isMobile && (<div className="..." onClick={onRoomJoin}>플레이룸 입장</div>)}
 ...
 <div className="..." onClick={onAndroidDownload} />
 ...
)

				
			

Replacements: 외부 React Component 연동 기능

연동의 필요성

프로젝트를 개발하는 과정에서 종종 외부에서 제공되는 React 컴포넌트(예: 써드파티 UI 라이브러리, 공유 버튼 등)를 사용할 필요가 있습니다. 이러한 외부 컴포넌트를 Figma 디자인과 효율적으로 연동하는 기능은 개발 속도와 유연성을 크게 높일 수 있습니다.

외부 컴포넌트 연동 과정

외부 컴포넌트 연동 과정은 아래와 같습니다.

  1. Replacements 기능: Figma 플러그인에서는 ‘Replacements’라는 기능을 통해 기존에 생성된 DOM 태그를 외부 React 컴포넌트로 대체할 수 있습니다.
  2. 대체할 컴포넌트 선택: 사용자는 Figma의 UI에서 대체할 외부 컴포넌트를 선택합니다. 예를 들어, 일반적인 div 태그를 FacebookShareButton 컴포넌트로 대체할 수 있습니다.
  3. 설정 및 적용: 선택한 외부 컴포넌트에 대한 추가 설정(예: 속성, 스타일)을 정의한 후, 이를 Figma 디자인에 적용합니다. 이 과정은 빠르고 직관적으로 이루어집니다.

자동 생성된 외부 연동 관련 코드

				
					import { FacebookShareButton, TwitterShareButton } from "react-share"
...


return (
 ...
 <FacebookShareButton className="..." url={link}>...</FacebookShareButton>
 <TwitterShareButton className="..." url={link}>...</TwitterShareButton>
 ...
)

				
			

이러한 과정을 통해, Figma 디자인에 외부 React 컴포넌트를 쉽게 통합할 수 있습니다. 결과적으로, 기존 디자인 시스템이나 외부 라이브러리 컴포넌트 간의 원활한 연동을 가능하도록 하는 기능을 제공할 수 있었습니다.

Interaction Component 개발: 모달 예시

모달 컴포넌트의 중요성

인터랙티브 컴포넌트는 사용자 경험의 핵심 요소입니다. 이 중 모달은 정보 표시, 사용자 결정 요구 등 다양한 상황에서 사용되며, 효과적인 사용자 인터랙션을 위해 필수적인 컴포넌트입니다.

모달 컴포넌트 개발 과정

모달 컴포넌트를 개발한 과정은 아래와 같습니다.

  1. 태그 및 액션 등록: Figma 플러그인에서 모달로 만들고자 하는 컴포넌트에 ‘Modal’ 태그를 등록합니다. 이 태그를 통해 모달 관련 옵션(예: 오버레이 색상, 위치 등)을 설정할 수 있습니다.
  2. 모달 오픈 로직: 태그가 등록된 컴포넌트는 ‘Actions’ 메뉴에서 모달을 오픈하는 로직으로 활용됩니다. 사용자는 특정 이벤트(예: 버튼 클릭)에 모달을 연결할 수 있습니다.
  3. OROR Forge React Toolkit: 모달과 같은 인터랙션 컴포넌트를 쉽게 구현하기 위해 oror-forge/react라는 React 툴킷을 사용합니다. 이 툴킷은 모달 관련 훅(Hooks)과 기능을 제공하여 개발자가 쉽게 모달 로직을 구현할 수 있도록 돕습니다.

자동 생성된 모달 컴포넌트 코드

이 과정을 통해, Figma 디자인에서 직접 모달과 같은 인터랙션 컴포넌트를 생성하고, 이를 React 코드로 쉽게 변환할 수 있습니다. 이는 개발 효율성을 높이며, 디자인과 개발 간의 일관성을 유지하는 데 큰 도움이 됩니다.

				
					import { useModal } from '@oror-forge/react'
import ShareLinkModal from 'components/ShareLinkModal'
...


const PlayRoomSharePage = (...) => {
 const modal = useModal()
  const handleShareLinkModalOpen = () => {
   modal.open(ShareLinkModal, {
     hasBehindOverlay: true,
     overlayColor: "rgba(113, 113, 113, 0.3)",
     placement: "Centered",
     hasOutsideClose: true,
   })
 }
 ...
  return (
   ...
   <div className="..." onClick={handleShareLinkModalOpen}>
   ...
 )
}

				
			

실제 적용 및 시연 영상

끝으로…

OROR Forge는 상용 도구를 개발한다는 생각보다는 당장 저희에게 필요한 도구를 만든다는 생각으로 만들기 시작한 도구입니다. 실제 구현 이후, 저희 팀에서는 이벤트 페이지, 홈페이지 및 서비스 페이지를 만드는 과정에서 OROR Forge의 힘을 많이 빌릴 수 있었고, 디자인을 코드로 옮기는 과정에서 효율성을 크게 올릴 수 있었습니다.

하지만 저희가 하나하나 구현하여 팀에 도움이 되었던 도구들조차 다른 사람들이 사용하는 경우에도 반드시 편리하다는 보장이 없기에, 외부 사용자들이 편리하게 OROR Forge를 사용하기 위해서는 넘어야 할 또 다른 장벽들이 많이 있었습니다. 또, 아직은 UI가 불친절하고 지원할 수 있는 프레임워크나 CSS도 한계가 있는 상황입니다. 이러한 부분에서 비록 상용 도구가 우리에게 맞지 않았기 때문에 직접 개발을 하게 되었지만, 결국 상용 도구로 나아가기 위해서는 고민해야 할 것들이 정말로 많다는 것을 느꼈습니다.

그렇지만 기존의 상용도구들이 대부분 그러했듯 모두에게 맞는 도구를 만들기 위해 노력하다 애매해지기보다는, 적어도 누군가에게는 꼭 맞는 도구가 되기를 바라면서 OROR Forge를 구현하고 있습니다. 무엇보다 반응형을 지원하고 자동으로 컴포넌트를 생성하는 등, 기존 도구와는 확연히 다른 뾰족함을 구현하고자 하고 있습니다.

프론트엔드 개발에서 디자인을 코드로 변환하는 과정을 프로그램으로 만들고자 하다 보니, 단순 CSS inspector와는 다른 복잡함이 내재되어 있다는 것을 느꼈습니다.

디자인과 코드 간의 간극을 줄이는 것은 단순히 코드를 변환하는 것이 아닙니다. 개발자에게 유용하고 높은 품질의 코드가 만들어지기 위해서는 디자인 단계에서부터 디자인 구조가 잘 짜여있어야 합니다. 그리고 이런 구조를 잘 만들 수 있도록, 자체적인 디자인 시스템을 구축하고 제공하고 있는 Figma에 다시 한번 감탄을 하였습니다. 또한, Figma의 디자인 개념을 CSS로 옮기는 과정을 개발하는 경우, 혹은 반대로 CSS를 작성할 때에도 Figma에 대해 생각하게 되면서 좋은 CSS 코드란 무엇인지 다시 한번 생각해 볼 수 있는 계기가 되었습니다.

이 글에서 설명하고 있는 Figma의 디자인 개념과 CSS 간의 매칭되는 개념들은 저에게 큰 도움이 되었으며, 독자분들에게도 제가 구현 과정에서 얻은 지식과 인사이트들이 잘 전달이 되기를 바랍니다.

디자이너와 개발자 간의 협업방식은 앞으로도 진화할 것이며, 점차 이러한 도구들이 생겨날 거라고 생각합니다. 디자인을 코드로 만들어 내는 것 또한 개발자의 일이기에 이러한 개념을 확장하고 도구를 만들어가는 경험에서 많은 것들을 느꼈습니다.

그리고 그동안 암묵적으로 행해왔던 Props를 만들거나, 치환자를 넣고 이벤트 핸들러를 추가하고 컴포넌트를 나누는 과정들을 하나씩 코드와 기능으로 제공을 하려고 하다 보니, 하나의 작업에도 많은 사고의 과정과 필요한 절차들이 존재했다는 것을 느낍니다.

또한, 제가 수행하는 개발 과정을 도식화하고 기능으로 풀어내는 과정에서 재미를 느꼈고, 수많은 과정과 사고방식들을 담아내는 UI를 고민하고 기능들을 만들어보며 컴포넌트를 개발하는 Best Practice가 무엇인지도 생각해 보는 계기가 되었습니다.

이 글에 모든 과정을 다 온전히 담아내지는 못했지만, 저희의 경험들이 고스란히 전달되었기를 바랍니다. 이 글을 통해서 간접적으로나마 저희의 여정과 함께 해주셔서 감사합니다.

OROR Forge를 개발하면서 테오와 나눈 회고 내용을 공유드리면서 글을 마무리하고자 합니다.

긴 글 함께 해주셔서 감사합니다. 🙂

Q) 이 과제를 하면서 어떤 것들을 느꼈나요?

이전에는 몰랐던 프론트엔드 개발방식에 대해서 생각할 수 있는 계기가 되었던 것 같아요. 월터가 이런 것들을 한번 해보라고 하지 않았다면, 이런 생각의 관점을 바꾸려고 하지 않고 전통적인 방식으로 개발을 계속했을 것 같습니다. 이제 앞으로는 프론트엔드 개발을 할 때 디자인 팀과 협업을 하는 과정에서 더 좋은 형태로 구조화하고 자동화하는 부분에 대해서 생각을 하게 될 것 같습니다.

그리고 무엇보다 리액트로 개발을 하는 것이 아니라, 리액트 코드를 생성하는 로직을 작성하는 과정이 있었기 때문에, 프론트엔드와 리액트 자체에 대해 더 깊이 있는 이해를 할 수 있는 시간이 되었다고 생각합니다.

Q) 앞으로의 개선 과제나 방향성이 있다면요?

우리에게 필요한 도구로 발전시키기 위해서 상용도구를 선택하는 것이 아니라 자체 개발을 했지만, 지금 당장의 생성되는 결과물은 다른 개발자분들이 바로 쓰기에는 아직은 아쉬운 것 같아요. 기능뿐만 아니라, 사용하기 쉽게 UI 등의 부분에서 개선 작업도 필요하고 생각이 듭니다.

그리고 이 도구를 다른 프로젝트에서도 사용을 하고 있는데 모든 디자인에 100% 적용할 수 있는 건 아니지만 어시스턴스의 역할은 충분히 수행을 해주는 것 같습니다. 다만, 이것이 가능하기 위해서는 이미 잘 구조화된 디자인이 있어야 좋은 코드가 나온다는 것을 알게 되었기 때문에, 모든 디자인을 전부 좋은 코드로 변환할 수 없다는 걸 감안하면 그런 부분을 도와주는 기능도 필요하다고 생각합니다.

쓰다 보니 욕심이 생겨서, 알아서 컴포넌트화 기능도 가능하고 반응형 디자인 코드도 작성해 주고 … 말하다 보니 끝이 없네요(웃음). 또, CLI 같은 기능을 통해서 프로젝트에 쉽게 반영이 되는 기능도 있었으면 좋겠다고 생각합니다.

무엇보다 원래 카카오 iOS, Android 네이티브 개발자들의 UI 개발작업 간소화를 지원하기 위해서 시작한 프로젝트였고 지금은 웹을 먼저 지원해 보고 추후 네이티브 지원으로 확장하고자 했었는데, 아직 네이티브 쪽 지원은 시작을 못하고 있기에 계속 개발하여 iOS와 Android에서도 사용할 수 있는 툴이 되었으면 좋겠다고 생각합니다.

Q) 이 글을 읽고 있을 독자들에게 해주고 싶은 말이 있다면 해주세요.

지금 Figma 플러그인에서 OROR Forge로 검색하면 저희의 기능을 사용해 보실 수 있습니다. 앞으로 Forge를 계속해서 다듬어, 누구나 편하게 사용할 수 있는 플러그인이 될 수 있도록 계속해서 작업을 해보고 싶습니다. 앞으로 프론트엔드 개발과 디자인의 협업은 이런 방식으로 진화하고 발전할 수 있게 될 거라고 생각이 드네요.

디자인을 코드로 만들어내는 과정에서 자동화할 수 있는 부분들을 찾고 그 문제를 해결해 줌으로써 앞으로의 서비스 개발에 더 큰 도움이 될 수 있는 서비스를 만들고 싶습니다.

감사합니다.

written by denn.is, teo.v
edited by June.6

📚관련 글 목록:

카카오톡 공유 보내기 버튼

Latest Posts

큐라스: 메시지 광고 추천 플랫폼 / 제4회 Kakao Tech Meet

12월 12일에 진행한 제4회 Kakao Tech Meet의 발표 영상과 발표자 이야기를 공유합니다. https://youtu.be/OXQ-uk7tE94 #메시지광고추천플랫폼 #광고추천서비스 #mlops #추론시스템 #데이터파이프라인 #대용량트래픽 발표자 Cookie(김영찬님), Eric(신호석님)