여둘까 Office Layout TIL 13
어디부터 보나
PDF가 너무 컸다
방 안쪽을 흰색으로 채우는 기능을 넣고 PDF로 뽑아 보니 파일이 너무 컸다. 그래서 용량을 줄여 달라고 했다. 압축을 켜는 것으로 91MB가 0.2MB가 됐다.
곧이어 배치도 여러 개를 한 PDF로 묶는 기능에 여덟 개를 넣었더니, 이번엔 PDF가 아예 안 만들어졌다. 큰 도면 두 장·세 장으로 여러 번 재현해 봤지만 전부 정상이었다.
콘솔 한 줄이 원인을 다 말하고 있었다
넣은 파일은 .ol 여덟 개, 합계 17.2MB였고, 브라우저 콘솔(개발자 도구 안의 오류 기록 창)에는 RangeError: Invalid string length가 떠 있었다. 이 한 줄을 보자마자 원인이 나왔다.
그래서 앞으로는 문제가 생기면 콘솔부터 확인하자고 했다. 증상은 겪은 사람이 보는 결과고, 콘솔은 프로그램이 남긴 원인이다. 증상만 들고 재현부터 시작하면 원인에 닿기까지 몇 배가 걸린다.
재현이 안 된다고 정상인 것은 아니었다
두 장·세 장은 되고 여덟 장에서 터지는 문제였다. 한도를 넘기는 지점이 그 사이에 있었다.
재현이 통과했다는 건 “문제가 없다”가 아니라 “그 조건이 문제를 안 건드렸다”는 뜻이다. 개수·크기가 방아쇠인 문제는 겪은 사람의 숫자를 그대로 맞춰 봐야 한다.
두 증상이 한 뿌리였다
앞의 “PDF가 너무 크다”와 뒤의 “PDF가 안 만들어진다”는 다른 문제로 보였다. 그런데 뿌리가 같았다 — 압축이 꺼져 있어 그림이 통째로 들어가고 있었다. 파일이 컸던 이유와 여덟 장에서 터진 이유가 같다.
시간차를 두고 온 증상 두 개는 따로 고치기 쉽다. “같은 원인일 수 있나”를 먼저 한 번 물어보는 편이 낫다.
올려야 확인할 수 있었다
고쳤으니 다시 해 보라는데, 고친 코드는 아직 올라가 있지 않았다. 나는 배포된 주소에서 쓰니 push(고친 코드를 저장소에 올려 배포되게 하는 일)를 해 줘야 확인할 수 있다고 했다.
증상을 알려 준 쪽이 확인하는 곳이 배포본이라면, 고친 뒤 올리는 데까지가 한 묶음이다. 올리고 나서 다시 돌려 보니 여덟 장이 제대로 묶여 나왔다.
알게 된 원리
PDF 안의 그림은 스트림이고, 압축은 선택이다
PDF는 그림을 스트림(덩어리로 담긴 데이터)으로 품고, 그 스트림을 어떤 방식으로 압축했는지 함께 적어 둔다. 압축을 지정하지 않으면 픽셀 값이 그대로 들어간다.
7500 × 5300 도면 한 장이면 약 4천만 픽셀이고, 픽셀당 3바이트니 원본만 100MB가 넘는다. 실제로 도면 한 장짜리 PDF가 91MB로 나왔다. 압축을 켜니 같은 그림이 0.2MB가 됐다.
jsPDF(브라우저 안에서 PDF를 만드는 자바스크립트 라이브러리)에서 압축은 켜야 켜지는 선택 항목이다. 기본값이 안전한 쪽일 거라고 믿으면 이런 일이 난다.
자바스크립트 문자열에는 최대 길이가 있다
jsPDF는 PDF 문서 전체를 문자열 하나에 이어 붙인 뒤 파일로 내보낸다. 그런데 크롬이 쓰는 자바스크립트 엔진에는 문자열 최대 길이가 있다(대략 5억 자).
압축이 꺼져 있으면 도면 한 장이 그 한도의 3분의 1쯤을 먹는다. 그래서 두세 장까지는 되고 여덟 장에서 터진다. RangeError: Invalid string length가 정확히 그 말이었다.
용량 문제는 “파일이 크다”로 끝나지 않는다. 어느 지점에선가 한도를 넘기면 기능 자체가 죽는다.
무손실과 손실은 그림 종류에 따라 승자가 바뀐다
PNG는 무손실이다 — 원본 픽셀을 그대로 되살린다. 같은 색이 넓게 이어지면 아주 잘 줄어드는데, 선 도면이 딱 그런 그림이다.
JPEG는 손실이다 — 눈에 잘 안 띄는 정보를 버려서 줄인다. 스캔한 도면처럼 픽셀마다 잡티가 있으면, PNG는 그 잡티까지 전부 보존하느라 오히려 커진다.
같은 크기로 재보니 이렇게 갈렸다.
- 선 도면: PNG 0.2MB
- 잡티 있는 도면: PNG 26MB · JPEG 8.6MB
그래서 무손실을 기본으로 두되, PNG가 일정 크기를 넘으면 그때만 JPEG로 바꾸게 했다. “무조건 무손실”도 “무조건 압축”도 답이 아니었고, 그림 성격이 기준이 된다.
손실 압축을 쓸지는 눈으로 확인해야 안다. 같은 자리를 1:1 크기로 잘라 나란히 놓고 보니 선도 글자도 구분되지 않았다. 축소한 화면에서 “괜찮네” 하는 건 확인이 아니다.
결과를 말하지 않으면 성공도 실패처럼 보인다
여덟 장이 안 만들어졌을 때 화면은 아무 말이 없었다. 앞선 TIL 10에서 정리한 원칙 — 결과를 안 보여 주면 성공도 실패처럼 보인다 — 이 여기서도 그대로였다. 이번엔 「PDF 만드는 중」, 결과 용량, 실패 사유를 상태 문구에 적게 했다. 다시 돌렸을 때 「8개 변환 완료 · 2.4MB」가 떠서 성공을 바로 알았다.