- 오래된 대화를 검색만으로 다시 읽기 어렵자, 가장 큰 iMessage 대화를 물리적인 책으로 바꿔 날짜별로 넘겨볼 수 있게 함
- iPhone 백업의
sms.db를 SQLite로 열어 메시지를 추출했지만, 일부 본문은message.text가 아니라message.attributedBody의 바이너리 blob에 들어 있어 추가 처리가 필요했음 - 직접 파서를 만들지 않고 Rust의 imessage-database crate를 활용해 iMessage DB를 구조화된 데이터로 읽고, 단일 대화만 가져오도록 SQL을 조정함
- 책 본문은 LaTeX로 생성했고, emoji 렌더링과 인쇄 비용 문제를 해결하기 위해 XeLaTeX와 Google Noto Emoji 흑백 폰트를 사용함
- 3년치 메시지는 6" x 9" 기준 약 1,300쪽이 되어 3권으로 나뉘었고, Barnes & Noble Press에서 배송 포함 30달러에 인쇄됨
왜 iMessage를 책으로 만들었나
- 과거의 구체적인 일을 떠올릴 때 텍스트 메시지 기록이 자주 단서가 됨
- iPhone의 iMessage 전체 기록 검색은 빨라졌지만, 유용하게 쓰려면 찾는 메시지의 정확한 문구 일부를 기억해야 함
- 검색 결과에서 오래된 대화로 들어가 주변 맥락을 보려 하면 스크롤 로딩이 매우 느림
- 특정 날짜로 바로 이동하는 기능이 없어, 대화를 무작위 지점에서 책처럼 넘겨보는 경험을 직접 만들기로 함
iPhone 백업에서 메시지 꺼내기
- 메시지를 가공하려면 먼저 iPhone 밖으로 데이터를 꺼내야 했음
- iPhone Wiki에 따르면 iPhone 백업에서
sms.db를 가져오면 SQLite 데이터베이스로 메시지를 다룰 수 있음 - Mac Finder로 표준 백업을 만든 뒤 백업 폴더를 열면, 루트에 16진수 바이트 이름의 디렉터리들이 있음
sms.db파일명은 iPhone Wiki에 나온3d0d7e5fb2ce288813306e4d4636395e047a3d28였고, 이를imessage.db로 복사해sqlite3CLI로 열 수 있었음- DB에는
message,attachment,chat,handle,chat_message_join같은 테이블이 들어 있음
SQL 조회와 본문 추출 문제
- 실제 대화를 뽑으려면
message,chat_message_join,chat테이블을 조인해야 했음 - 특정
chat_identifier를 기준으로 날짜순 메시지를 조회하자, 일부 메시지의 본문이 비어 있었음 - 비어 보이는 메시지 일부는
message.text가 아니라message.attributedBody컬럼의 인코딩된 NSMutableAttributedString 바이너리 blob에 저장돼 있었음 sqlite3에서 바이너리를 hex로 꺼내xxd로 확인하자 CVS 문자 내용이 실제로 들어 있음을 볼 수 있었음- 직접 바이너리 포맷 파서를 구현하는 대신 imessage-database crate를 사용함
- 이 crate는 iMessage 데이터베이스를 읽어 Rust 데이터 구조로 출력함
- 함께 제공되는
imessage-exporter바이너리는 대화를 텍스트나 HTML로 생성할 수 있음 - 단일 대화만 좁혀 가져오도록 라이브러리의 SQL 문을 약간 수정함
LaTeX로 책 본문 만들기
- 목표는 HTML이나 텍스트 파일이 아니라 손에 들고 넘길 수 있는 물리 책이었기 때문에 LaTeX를 출력 대상으로 선택함
- LaTeX의 텍스트 기반 소스는 템플릿과 자동 생성에 잘 맞아, 각 메시지를 LaTeX 코드로 내보내는 방식을 사용함
- 초기 렌더링은 단순한 형태로 시작함
- 내가 보낸 메시지는 왼쪽, 상대 메시지는 오른쪽에 정렬함
- 이미지가 있는 곳에는 첨부파일 표시 텍스트를 넣음
- 반응과 답장처럼 렌더링하기 번거로운 요소는 건너뜀
- 날짜 기준으로 챕터를 나누고 시각적으로 조금 조정함
emoji와 XeLaTeX 처리
- 일반 LaTeX는 unicode를 지원하지 않아, emoji가 포함된 범위까지 렌더링하면 컴파일러가 실패했음
- emoji를 제거할 수도 있었지만, 현대 커뮤니케이션에서 emoji가 중요하다고 보고 제외하지 않음
- XeLaTeX로 전환해 unicode 폰트 지원을 사용함
- 생성된 LaTeX 소스에서 각 emoji를
{\emojifont X}형태로 감싸고,\emojifont를 emoji 폰트로 지정해 inline emoji 렌더링을 성공시킴 - 컬러 인쇄 비용을 피하려고 Google Noto Emoji의 흑백 emoji 폰트를 사용함
- 각 메시지마다
\markrightLaTeX 명령을 넣어, 헤더가 현재 날짜를 따라가게 함
1,300쪽을 3권으로 나누기
- 3년치 메시지를 표준 6" x 9" 페이지 크기로 컴파일하자 1,000쪽을 훨씬 넘는 PDF가 생성됨
- 대상 기간이 정확히 3년이어서 전체를 3권으로 나눠 각 권을 인쇄 가능한 크기로 줄임
- 최종 분량은 총 약 1,300쪽이었음
Barnes & Noble Press로 인쇄
- 여러 인쇄 옵션을 검토한 뒤 Barnes and Noble Press를 선택함
- Lulu나 Amazon KDP 같은 다른 옵션보다 다소 비쌌지만, 개인용 책을 인쇄할 때 책을 “출판”하지 않아도 되는 점이 더 적합했음
- 세 권, 약 1,300쪽 전체를 배송 포함 30달러에 인쇄할 수 있었음
- 본문 PDF를 업로드하면 B&N Press 사이트가 표지에 필요한 치수를 생성했고, 이 치수에 맞춰 Inkscape로 각 권의 표지를 만듦
- B&N Press 웹사이트는 전반적으로 매우 느렸고, 주문 중 결제 페이지가 24시간 넘게 열리지 않는 문제가 있었음
- 문제가 해결된 뒤 주문이 진행됐고, 몇 주 후 세 권의 실제 책을 받음
- 물리 책으로 넘겨보는 방식은 휴대폰에서 과거 대화를 다시 찾는 것보다 훨씬 쉬웠음
직접 만들어보기
- 소스 코드는 정리 상태가 거칠고 Cargo 바이너리로 패키징되지는 않았지만, 코드 양은 많지 않음
- 프로젝트 코드는 bkettle/message-book에 공개돼 있음