TFJS로 아이콘 ‘맞춤법 검사기’ 만들기

Joo Hyung Park (Jude)
10 min readMar 27, 2022

--

Discover

모바일이나 웹 화면을 디자인 하다보면 가장 많이 쓰는 UI 컴포넌트는 바로 아이콘입니다. 아이콘은 사용자들이 자연스럽게 프로덕트를 탐색하도록 도와주는 메타포일 뿐아니라 서비스의 인상을 결정짓는 디자인 파운데이션의 중요한 부분이기도 합니다.

아이콘이 이렇게 중요한 역할을 하지만 실제 업무 환경에서는 아이콘을 사용하는 것은 몇가지 문제점이 항상 있었습니다.

  • 사내 아이콘 라이브러리는 항상 나중에 만들어진다.
  • 또 사내 아이콘 라이브러리는 항상 모자라다..
  • 그래서 내 디자인 리소스에는 항상 레거시가 아이콘이 늘어난다…

즉, 회사가 아이콘 라이브러리를 갖춰놓고 제품 개발을 시작할 수는 없습니다. 서비스의 통일성, 일관성이라는 것도 결국 제품들이 늘어나고 프로젝트가 커지면서 따라오는 것이기 때분입니다. 그러다보니 아이콘도 ‘일단은 만들고, 나중에 통일하자’라는 생각으로 FontAwesomeMaterial Icon 같은 오픈 라이브러리를 쓰거나, 디자이너가 직접 그린 아이콘 SVG를 쓰게 됩니다.

네이버 설계에서도 이런 아이콘 문제는 피해갈 수 없었습니다. 하지만 더 큰 문제는 이런 아이콘을 기존 레거시 파일에서 한번에 걷어낼 수 있는 방법이 없다는 것입니다.

사람이 일일이 교체를 해줘야 하는데… 보통 노가다가 아니다 보니 차일 피일 미루기 일쑤고 어떤 경우에는 사내에 디자인 시스템 아이콘이 있어도 기존 레거시와 같이 사용하는 경우도 생깁니다. 지금도 충분히 바쁜데.. 누가 일일이 아이콘 오브젝트를 클릭해가면서 찾아고치고 싶을까요?

Define

이렇게 당장 나조차도 하고 싶지 않은 일을 일선의 프로덕트 디자이너들에게 해달라고 부탁하거나 강요할 수는 없었습니다. 그러던 중에 네이버의 우희택 리더님이 아주 흥미로운 아이디어를 제안했습니다.

기존에 실험중이던 Figma ML을 아이콘 전용 검사기로 개조하면 이 반복되는 노가다에서 디자이너들을 구원할 수 있잖을까요?

사실 저는 생각지도 못했던 접근이었습니다. FigmaML 프로젝트를 만들면서 늘 더 많은 UI오브젝트를 검출하는 것에만 집중하고 있었기 때문입니다. 하지만 오히려 검출 범위를 좁혀서 아이콘만 정확하게 검출하도록 만든다면 저를 포함한 모든 프로덕트 디자이너들의 아이콘 레거시 문제점을 자동으로 해결해줄 수 있는 획기적인 솔루션이 될 수도 있다고 생각했습니다.

바로 머리속에서 스파크가 튀었고.. 그때부터 TensorFlow JS프로젝트에서 브랜치를 만들어고 아이콘 전용 맞춤법 검사기 프로젝트를 시작했습니다. 기존 코드를 재활용하는 것이 많다보니 결과는 정말 빠르게 눈앞에 나타났습니다.

https://github.com/dusskapark/iconography-ml

Develop

기존 소스를 재활용한 부분이 많지만, 아이콘 검출기 역시 새로운 TensorFlow 모델이 필요했습니다. 따라서 다른 Object Detection 프로젝트와 마찬가지로 아래의 파이프라인에 따라서 데이터 준비부터 훈련과 배포 과정을 진행했습니다.

Object detection pipeline (source: TensorFlow Blog)

1. Prepare the data

데이터는 RICO 데이터셋에서 `Icon` 부분만 추출해서 사용 했습니다. Mobbin의 데이터셋을 사용해볼까 싶었지만, Mobbin 데이터셋에는 FAB와 같이 아이콘과 버튼이 구분되지 않는 경우가 종종 있어서 오히려 RICO가 더 나은 성능을 보여줄 것이라 보였기 때문입니다.

먼저 RICO의 사이트에서 원본 이미지와 Annotation (JSON) 파일을 다운로드 받았고, 원본 이미지 파일은 늘 그렇듯이 1/4로 줄였고 JSON 파일은 필요한 데이터만 뽑아서 PascalVOC 포멧의 XML 파일로 변환해서 준비했습니다.

직접 RICO 사이트에서 파일을 다운받아서 GUI로 작업하는 방법도 있겠지만… 10만개가 넘는 파일을 옮기고 조작하는 작업은 아마 상당히 고통스러울 것 입니다; 그래서 혹시 저의 이 포스팅을 보고 따라해보실 분들은 아래의 Colab 노트북을 이용해서 터미널에서 작업하시기를 권장합니다.

Icon 만 따로 추출하기?

아이콘 컴포넌트만 따로 추출하기 위해서 JSON → XML 데이터 추출과정에서 관련 로직을 추가했습니다. 저는 다차원의 JSON 파일을 1차원의 XML 파일로 만들기 위해서 아래와 같이 재귀함수를 만들어 사용했는데, 이 과정에서 componentLabel 값을 체크해서 “Icon”인 경우만 XML 파일을 생성하도록 만들었습니다.

def recursive(child, result_out):
"""
원하는 역할을 하기 위해서 재귀호출을 할 수 있는 함수를 생성합니다.
Create a function that makes a recursive call.
"""
obj = Element("object")
# Detect Icons
if child['componentLabel'] == "Icon":
SubElement(obj, "name").text = child['componentLabel']
bounds = child['bounds']
SubElement(obj, "difficult").text = '0'
bndbox = SubElement(obj, "bndbox")
xmin=bounds[0]
ymin=bounds[1]
xmax=bounds[2]
ymax=bounds[3]

if(isvalidbdnbox(xmin,xmax,ymin,ymax)):
SubElement(bndbox, "xmin").text = str(xmin/4)
SubElement(bndbox, "ymin").text = str(ymin/4)
SubElement(bndbox, "xmax").text = str(xmax/4)
SubElement(bndbox, "ymax").text = str(ymax/4)
result_out.append(beautify(obj))
if 'children' not in child:
return
for ch in child.get('children', []):
recursive(ch, result_out)

또한 이렇게 생성된 XML이 있는 이미지 파일들만 따로 모아서 원본 데이터셋 구축을 완료했습니다. 이 과정을 통해서 처음에 준비한 RICO의 약 66,000 장의 원본 데이터는 48,208장의 Icon 전용 데이터셋으로 조정을 완료했습니다.

# Extract only available image filesimport os
import shutil
data_path = './image/'
if not os.path.exists(data_path):
os.mkdir(data_path)
xmls = os.listdir('./xml')
# print(files)
for xml in xmls:
xml = f'./xml/{xml}'
infile = xml.replace("xml", "jpg")
outfile = infile.replace("./jpg/", "./image/")
print(infile, outfile)
shutil.copy(infile, outfile)

2. Train and validate the model

모델 훈련과 검증 과정은 기존의 Figma ML의 노트북을 거의 그대로 사용했습니다. 데이터셋에서 검출해야 하는 UI가 25개 → 1개 로 줄어든 것외에는 전혀 달리진 것이 없기 때문에 기존에 결과가 좋았던 MobileNetV2 기반의 Fine-tuning 노트북을 그대로 사용했습니다.

트래이닝 결과에서 주목할 점은 … 기존에 RICO의 25개 UI를 학습시키던 결과와 비교해서 Localication, Classification 점수 모두 좋아졌습니다. 같은 이미지라도 수행하는 작업의 복잡도가 낮아진 덕분이 아닐까 생각되는데 조금 더 상세한 내용은 RCNN 쪽 논문을 좀 찾아보고 나중에 또 포스팅하겠습니다.

3. Deploy the model

모델 학습이 끝난 다음에는 모델을 추출하고, tensorflowjs_wizard 패키지를 이용해서 TFJS용 모델로 변환한 다음 다운로드 했습니다.

# install wizard
!pip install tensorflowjs
# Execute Tensorflow JS wizard
!tensorflowjs_converter \
--input_format=tf_saved_model \
--output_node_names='./web-model' \
--saved_model_tags=serve \
./fine_tuned_model/saved_model \
./fine_tuned_model/web_model

Figma 플러그인 코드의 최대 용량이 1Mb도 안되기 때문에 해당 모델을 Figma 플러그인에서 사용하려면 CDN에 model.json 파일과 Weight 파일들을 모두 업로드해야 합니다.

저는 Github에 해당 파일을 올려놓고, 자동으로 발행되는 CDN 주소를 그대로 코드에서 사용했습니다.

Deliver

마지막으로 이 모델을 플러그인 코드에 통합했습니다. 기존의 Figma ML의 코드를 재활용했으므로 대부분 비슷하지만, 아이콘 라이브러리에 특성에 맞게 아래 부분을 변경했습니다.

팀 라이브러리 아이콘‘만’ 추출

Figma에서는 로컬/팀 라이브러리를 모두 사용할 수 있습니다. 기존 코드는 라이브러리 출처와 상관없이 모두 검출하도록 설계했었지만, 아이콘 검출기에서는 사용 목적에 맞게 팀 라이브러리만 검출하도록 일부 로직을 수정했습니다.

아이콘 크기 비교 로직 추가

일반 UI 컴포넌트 들의 경우, 각각의 크기가 제각각 이지만 아이콘의 경우는 safe-area를 포함하면 모든 아이콘이 정사각형으로 균일합니다. 이런 특징을 반영해서 TFJS 검출 결과와 디자인시스템 값을 비교하는 로직을 조금 더 다듬었습니다.

# 예, 가까운 박스들끼리 비교하는 로직 추가 
# 오랜만에 다시만난 피타고라스
const pytha = (component, detection) => {
const x = Math.abs(component.bbox[0] - detection.bbox[0]);
const y = Math.abs(component.bbox[1] - detection.bbox[1]);
const length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
return length;
};

다음 포스팅 예고

이런 과정을 통해서 일단 아이콘을 검출하는 프로토타입을 만들었고, 실제로 Figma 화면에서 동작하고 있습니다. 하지만 실제 업무에서 바로 쓰기 위해서는 검출하는 아이콘을 커스터마이징 하는 기능이 꼭 필요했습니다.

다음 포스팅에서는 아이콘 모델을 디자인 시스템별로 사용자 정의하는 기능을 만들기 위해서 제가 했던 여러가지 실험들을 정리해서 올려보도록 하겠습니다.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Joo Hyung Park (Jude)
Joo Hyung Park (Jude)

Responses (1)

Write a response