LIAN KIM
ResumeProjectsBlog
이력서

On this page

문제검토와 판단그리드 1칸 = 실물 1mm다중 선택인쇄 컬러저장과 출력트러블슈팅: 캡처 레이아웃이 깨진 문제상태

의류 케어라벨 WYSIWYG 에디터 개발

보이는 대로 저장하지 않은 이유

의류에 부착되는 케어라벨을 웹에서 디자인하고, 결과물이 라벨사로 넘어가 실물로 인쇄되는 도구를 만들었다. 화면에서 편집한 것이 실물 치수 그대로 인쇄되어야 하는 에디터다.

문제#

케어라벨 하나가 만들어지기까지 여러 도구와 사람을 거친다. 브랜드는 Adobe Illustrator 같은 도구로 라벨을 디자인하고, 메신저로 라벨사와 원단이나 인쇄 방식을 주고받는다. 디자이너가 없는 작은 브랜드는 디자인 자체를 라벨사에 맡기기도 한다.

이 과정에는 문제가 많았다. 디자인 파일은 메신저와 각자의 컴퓨터에 흩어져 관리가 어렵다. 인쇄 옵션처럼 반복되는 내용의 소통이 문서로 남지 않아 매번 다시 오간다. 지금 어디까지 진행됐는지 알 방법이 없다. 규격에 대해 학습이 필요한 것도 부담이었다. 시접과 여백, 접지 같은 인쇄 규격은 복잡하고 국가마다 다르기도 해서, 브랜드가 알아서 챙기기 어려운 영역이었다.

그래서 디자인부터 발주까지를 한 흐름으로 묶는 에디터를 만들기로 했다.

검토와 판단#

요소를 자유롭게 배치하는 에디터라면 Canvas나 SVG로 직접 그리는 방법이 먼저 떠오른다. 그 방법들을 두고 react-grid-layout 기반의 그리드 에디터를 택한 데에는 몇 가지 이유가 있었다.

가장 큰 이유는 저장과 재편집이다. 이 에디터는 디자인을 저장하고 다시 불러와 편집할 수 있어야 했다. 그러려면 각 요소가 위치와 크기 같은 속성을 가진 독립된 객체로 남아야 한다. Canvas는 한번 그리면 픽셀로 합쳐져 개별 요소를 다시 다루기 어렵지만, react-grid-layout은 각 요소를 layout 객체(x, y, w, h)로 관리해 JSON으로 직렬화하고 불러온 뒤에도 요소를 개별적으로 제어할 수 있다.

mm 단위 정합도 이 방식과 잘 맞았다. react-grid-layout은 요소의 위치와 크기를 픽셀이 아니라 정수 칸 단위로 다룬다. 칸 수를 라벨의 실측 너비에, 한 칸 높이를 1mm에 맞추면 한 칸이 곧 1mm가 되고, 요소를 옮기거나 키워도 좌표가 항상 정수 칸에 떨어지므로 위치와 크기가 mm 단위로 정렬된다.

도메인 특성도 맞았다. 케어라벨은 포스터나 리플렛처럼 자유로운 배치가 필요한 디자인이 아니라, 규격이 정해진 정형화된 양식에 가깝다. 픽셀 단위의 자유 배치보다 mm 격자에 정렬되는 그리드 방식이 오히려 적합하다고 생각했다.

마지막은 현실적인 판단이었다. 개발 기간이 빠듯한 상황에서, 드래그와 리사이즈, 그리드 배치처럼 이미 구현되고 사용자가 많아 검증된 기능을 기반으로 삼는 편이 빈 화면에서 시작하는 것보다 안전하다고 봤다. 부족한 부분만 직접 더하는 방향을 택했다.

그리드 1칸 = 실물 1mm#

좌표계의 기준을 화면 픽셀이 아니라 실물 mm로 잡았다. 그리드의 칸 수를 라벨의 실폭 mm에, 한 칸의 화면 크기를 1mm의 인쇄 픽셀 값에 맞춘다. 40mm 폭 라벨이면 그리드가 40칸이 되는 식이다. 요소를 3칸 옮기는 것이 실물에서 3mm 옮기는 것과 같아지므로, 편집 단위와 라벨사가 쓰는 실측 단위가 일치한다.

그리드 1칸이 실물 1mm에 대응하는 케어라벨 편집 영역
그리드 1칸이 실물 1mm에 대응하는 케어라벨 편집 영역

mm와 픽셀 사이의 변환은 인쇄 표준 300 DPI 기준의 함수 하나로 격리했다. 편집 캔버스 자체를 인쇄 해상도의 픽셀 공간으로 그리고, 줌은 CSS transform 배율로만 처리한다. 화면용 좌표계와 출력용 좌표계를 따로 두지 않았기 때문에, 둘 사이의 변환 버그가 생길 가능성이 없다.

시접과 여백, 접지 같은 인쇄 규격은 라벨 포맷의 속성으로 두고, 실제 편집 가능한 영역을 그 값들에서 계산한다. 접이식 라벨이면 시접이 위아래로 잡히는 식의 규격 지식이 도구 안에 들어가 있어서, 사용자가 규격을 외우지 않아도 규격에 맞는 편집 영역이 계산된다.

다중 선택#

여러 요소를 한 번에 옮기고, 한 번에 키우고, 좌우와 중앙으로 정렬하고, 텍스트 여러 개의 폰트를 한 번에 바꾸는 것. 요소를 하나하나 다뤄야 한다면 반복 작업이 그대로 사용자에게 넘어가므로, 다중 선택은 당연히 필요한 기능이라고 봤다. 그런데 react-grid-layout은 요소를 하나씩만 다룰 수 있다.

그래서 바운딩 박스를 직접 구현했다. 여러 요소를 선택하면 그들을 감싸는 가상의 레이어가 만들어지고, 이 박스를 드래그하거나 리사이즈하면 이동량과 배율이 선택된 모든 요소에 적용된다.

바운딩 박스 사용 시 생기는 변수들이 있다. 이 변수들을 고려해서 적용 전에 검증하도록 구현했다. 박스를 움직일 때마다 선택된 모든 요소가 이동 후에도 라벨 영역 안에 남는지를 먼저 확인하고, 하나라도 벗어나면 전체 이동을 취소한다. 일부만 움직이고 일부는 걸리는 어긋남이 생기지 않도록, 그룹 변형은 전부 성공하거나 전부 일어나지 않는다.

인쇄 컬러#

인쇄 컬러 옵션이 있었다. 화이트 원단에는 블랙 인쇄, 블랙 원단에는 화이트나 금박, 은박 인쇄. 사용자는 이 조합을 고르고, 화면은 고른 대로 보여야 한다.

그런데 이 도메인에는 중요한 사실이 하나 있다. 어떤 옵션을 골라도 라벨사에 전달되는 인쇄 원본은 항상 블랙이어야 한다는 것이다. 금박 인쇄도 원본은 블랙으로 넘어가고, 박은 인쇄 공정에서 입혀진다.

사용자가 고른 컬러를 저장하고 보여주는 방식도 있다. 하지만 중요한 것은 대부분의 브랜드가 케어라벨에 브랜드 로고 이미지를 삽입한다는 것이다. 이미지는 텍스트나 도형과 달리 색상 값을 에디터가 바꿀 수 없다. 최종 인쇄에 필요한 것이 보이는 색이 아닌 이상, 로고는 흑백 원본으로 받는 것이 맞았다.

디자인 데이터는 블랙 하나로 저장하고, 화면 표시는 CSS 필터로 파생한다. 금박을 고르면 저장물은 그대로인 채 필터가 금박에 가까운 색으로 보여주는 방식이다. 실물과 최대한 비슷하게 보이도록 목업 미리보기는 원단 사진 위에 필터가 적용된 작업물과 질감 레이어를 겹쳐 합성했고, 같은 필터를 편집 뷰포트에도 적용해 편집 중에도 인쇄 결과에 가까운 색을 보게 했다.

저장된 데이터가 그대로 라벨사에 전달되는 원본이 된다. 인쇄 옵션이 늘어도 필터 하나만 추가하면 된다.

저장과 출력#

디자인은 JSON으로 직렬화해 저장하고, 불러오면 모든 요소가 다시 편집 가능한 상태로 돌아온다.

출력물은 화면 캡처가 아니라 인쇄를 위한 파일이다. 사용 중인 폰트를 임베딩한 고해상도 이미지로 캡처해 어떤 환경에서 열어도 폰트가 깨지지 않게 했고, 주문이 접수되면 이 결과물이 PDF로 라벨사에 전달된다.

트러블슈팅: 캡처 레이아웃이 깨진 문제#

편집한 라벨을 이미지로 캡처해 출력 파일과 썸네일을 만드는데, 처음 쓴 html2canvas에서 캡처 결과의 레이아웃이 깨졌다. 요소들이 제 위치가 아니라 한쪽에 뭉쳐 찍혔다.

원인은 요소의 배치 방식이었다. react-grid-layout은 성능을 위해 각 요소를 top/left가 아니라 transform: translate로 배치한다. 이 동작을 그대로 사용하고 있었는데, html2canvas가 CSS transform을 제대로 반영하지 못해, 캡처 시 요소들이 transform이 적용되기 전 위치로 찍힌 것이었다.

transform을 정확히 반영하는 html-to-image로 교체해 해결했다. 라이브러리가 요소를 어떻게 배치하는지 따라가다 원인을 찾은 경우였다.

html2canvas와 html-to-image의 캡처 결과 비교
html2canvas와 html-to-image의 캡처 결과 비교

상태#

이 에디터는 사내 검증 단계까지 개발됐다. 내부 데모에서 디자이너가 아닌 직원들이 직접 사용해 봤고, 여전히 어렵게 느껴지는 지점들이 있다는 피드백을 받았다. 보완할 지점들이 확인된 상태에서 더 급한 업무로 우선순위가 바뀌면서 개발이 중단됐다.