ImageMagick으로 앱 아이콘 만들기: SVG가 안 그려지고 마스크가 투명해진 이유
앱 하나를 두 스토어에 올리면서 이미지 21개를 만들어야 했습니다. 아이콘, 적응형 아이콘 레이어들, 스플래시, 파비콘, 그리고 꾸민 스토어 스크린샷 12장. 디자인 도구를 열지 않고 전부 명령줄에서 만들었습니다.
쓴 도구는 ImageMagick입니다. 이미 깔려 있었습니다.
Version: ImageMagick 7.1.2-3 Q16-HDRI aarch64간단히 말하면 이미지 편집을 명령어로 하는 도구입니다. 크기 조절, 자르기, 도형 그리기, 글자 얹기, 합성을 셸에서 합니다. 사람이 한 장 만드는 데는 GUI가 빠르지만, 같은 작업을 12장에 반복하거나 문구를 고쳐 전부 다시 뽑아야 할 때는 이쪽이 압도적으로 빠릅니다. 실제로 스크린샷 문구를 한 번 잘못 잡아 12장을 통째로 다시 만들었는데, 스크립트라 명령 한 줄로 끝났습니다.
버전 7부터 명령어가 magick 하나로 통일됐습니다. 인터넷에 돌아다니는 예제의 convert, composite, identify는 6 시절 이름이고 지금은 magick, magick composite, magick identify로 씁니다.
세 번 막혔고, 셋 다 에러 메시지가 친절하지 않았습니다.
SVG를 줬는데 배경만 그려졌습니다
아이콘을 SVG로 그려서 PNG로 뽑을 생각이었습니다. 십자가와 접힌 지도를 <path>로 정의하고 변환했더니 배경 사각형만 남고 나머지가 사라졌습니다. 에러는 없었습니다. 종료 코드도 0이었습니다.
델리게이트 목록을 보고 이유를 알았습니다.
$ magick -list delegate | grep svg
svg => "rsvg-convert' --dpi-x %x --dpi-y %y -o '%o' '%i"
$ command -v rsvg-convert
(없음)ImageMagick은 SVG를 직접 렌더링하지 않습니다. rsvg-convert라는 외부 프로그램에 넘깁니다. 그게 없으니 내장된 최소 파서로 떨어졌고, 그 파서는 단순한 사각형만 이해하고 path는 조용히 버립니다. 실패를 실패라고 말하지 않는 게 가장 나쁜 부분이었습니다.
brew install librsvg로 해결할 수 있었지만, 도형 몇 개짜리 아이콘 하나 때문에 의존성을 늘리고 싶지 않아서 SVG를 버리고 -draw로 직접 그렸습니다.
-draw는 별도의 언어입니다
접힌 지도 3면과 라틴 십자가를 좌표로 정의했습니다. 1024 캔버스 기준입니다.
P1="path 'M 192 504 L 396 436 L 396 824 L 192 892 Z'"
P2="path 'M 416 440 L 608 504 L 608 892 L 416 828 Z'"
P3="path 'M 628 504 L 832 436 L 832 824 L 628 892 Z'"
CV="rectangle 472,136 552,672"
CH="rectangle 352,264 672,344"
magick -size 1024x1024 "xc:#0F172A" \
-stroke none -fill '#F6E7C8' -draw "$P1" -draw "$P2" -draw "$P3" \
-fill '#E9B44C' -draw "$CV" -draw "$CH" \
icon.png여기서 두 번째로 막혔습니다. 선 끝을 둥글게 하려고 -linecap round를 붙였더니 인식하지 못한다고 나왔습니다. -strokelinecap도 마찬가지였습니다.
-draw 안쪽은 CLI 옵션과 다른 별개의 미니 언어입니다. 그리기 관련 설정은 옵션이 아니라 그리기 문자열 안에 써야 합니다.
-draw "stroke-linecap round bezier 10,90 40,10 60,90 90,10"fill, stroke, stroke-width, stroke-linecap, translate, rotate 전부 이 문자열 안에서 씁니다. SVG 속성 이름과 거의 같아서 익숙해지면 오히려 편합니다.
접힌 선을 그릴 때는 획을 긋는 대신 면 사이에 간격을 뒀습니다. 위 좌표에서 396과 416처럼 20px 벌어져 있는 부분입니다. 획으로 그으면 Android 13 테마 아이콘처럼 전체가 단색으로 칠해질 때 선이 사라져 형태를 알아볼 수 없게 됩니다. 구멍은 단색화돼도 남습니다.
마스크가 통째로 투명해졌습니다
제일 오래 붙잡은 문제입니다.
스토어 스크린샷을 꾸미면서 안쪽 캡처의 위 모서리만 둥글게 깎으려고 마스크를 만들었습니다.
magick -size "${SW}x${SH}" xc:black \
-draw "roundrectangle 0,0 $((SW-1)),$((SH+CORNER)) $CORNER,$CORNER" \
mask.png
magick shot.png mask.png -alpha off -compose CopyOpacity -composite rounded.png결과물의 스크린샷 자리가 텅 비었습니다. 배경과 헤드라인은 멀쩡한데 그 아래가 아무것도 없었습니다. 좌표를 의심해서 한참 헤맸는데 좌표는 맞았습니다.
CopyOpacity는 마스크의 밝기를 알파 값으로 읽습니다. 흰 부분은 불투명, 검은 부분은 투명입니다. 그런데 -draw의 기본 fill은 검정입니다. 검정 캔버스에 검정 둥근 사각형을 그린 셈이라 전부 밝기 0, 즉 전부 투명이었습니다.
한 마디를 넣어 고쳤습니다.
magick -size "${SW}x${SH}" xc:black -fill white \
-draw "roundrectangle ..." mask.png마스크를 만들 때는 -fill white를 반드시 적는다고 외우는 게 낫습니다. 이것도 에러가 나지 않습니다. 그냥 조용히 빈 이미지가 나옵니다.
알아두니 쓸모 있던 것들
한쪽 치수만 지정하기. -resize "780x"는 폭만 780으로 맞추고 높이는 비율대로 따라옵니다. x780이면 반대입니다.
그림자. 이 조합을 통째로 외워두면 됩니다.
\( image.png \( +clone -background black -shadow 34x28+0+10 \) +swap -background none -layers merge +repage \)+clone으로 복제하고, 그 복제본을 그림자로 흐리고, +swap으로 앞뒤를 바꿔 원본이 위로 오게 한 다음 합칩니다. 순서를 바꾸면 그림자가 이미지를 덮습니다.
글자는 두 가지 방법이 있습니다. label:"텍스트"는 글자 크기에 딱 맞는 새 이미지를 만들고, -annotate는 기존 이미지 위에 얹습니다. 뱃지 칩처럼 글자 폭을 재서 배경 알약을 그려야 할 때는 label:로 만든 뒤 magick identify -format '%w'로 폭을 읽는 방식이 편했습니다.
한글 폰트. macOS에 기본으로 있는 /System/Library/Fonts/AppleSDGothicNeo.ttc를 지정하면 됩니다. 따로 설치할 게 없습니다.
중간 확인. magick identify -format '%f %wx%h %[size]\n' *.png 한 줄이면 만들어진 파일 목록과 크기를 한눈에 봅니다. 스토어 규격에 맞는지 확인할 때 매번 썼습니다.
다시 한다면
SVG로 시작하려면 magick -list delegate | grep svg로 렌더러가 실제로 있는지부터 확인하겠습니다. 없다면 librsvg를 깔거나 처음부터 -draw로 가는 게 낫습니다. 어중간하게 시작했다가 조용한 실패를 디버깅하는 게 제일 손해였습니다.
그리고 조용한 실패가 이 도구의 성격입니다. 세 번 막혔는데 셋 다 에러 메시지가 없었고, 결과 이미지를 눈으로 보고서야 알았습니다. 스크립트를 짤 때 단계별 중간 결과를 파일로 남겨두면 어디서 틀어졌는지 찾기가 훨씬 쉽습니다. 저도 마스크 문제를 잡을 때 mask.png를 열어보고 나서야 원인을 알았습니다.
함께 읽기
- 스토어 이미지 규격 정리: 스크린샷·아이콘·그래픽에 실제로 필요한 것앱 하나를 App Store와 Google Play에 동시에 올리면서 이미지를 몇 개나 만들었는지 세어봤습니다. 스크린샷 12장, 스토어 전용 그래픽 2개, 앱 번들에 들어가는 아이콘 자산 7개. 합쳐서 21개입니다. 코드를 다 짜놓고 시작한 마무리 작업인데 결국 여기에 시간을 더 썼습니다.
- Google Play 출시 트랙 정리: 내부·비공개·공개·프로덕션과 App Store 비교오늘 성경 지도라는 앱을 App Store와 Google Play 양쪽에 올렸습니다. iOS를 먼저 끝내고 Play Console로 넘어왔는데, 출시 개요 페이지에서 카드 네 장을 보고 잠깐 멈췄습니다.
- App Store Connect API 키: Team Key로 할 수 있는 것과 없는 것새 앱을 App Store Connect에 등록하면서, 화면을 계속 왔다 갔다 하기 귀찮아서 API로 자동화할 수 있는 부분은 자동화해보려고 했습니다. 결론부터 적으면 절반만 성공했습니다. 조회·수정은 되는데, 앱 생성 자체는 API로 안 됩니다.
- RN 앱 두 번째 배포: 재사용되는 인증서, 새로 해야 하는 등록몇 주 전에 만든 고객 포털 앱을 반쯤 복사해서 새 RN 앱을 하나 더 만들었습니다. 성경 속 여정을 지도에 그리는 화면 하나를 웹에서 옮겨온 것뿐인데, 옮기고 나니 "인증서까지 그대로 가져다 쓸 수 있나"가 궁금해졌습니다. 결론부터 적으면 절반만 맞았습니다.
- 스토어 메타데이터 API 정리: 항목별로 되는 것과 안 되는 것앱 설명과 키워드를 채우면서 두 스토어의 API 를 항목별로 다 두드려 봤습니다. 콘솔에 들어가지 않고 어디까지 갈 수 있는지 궁금했거든요.