만약 OpenDocument 가 SQLite 를 쓴다면?
만약 OpenDocument 가 SQLite 를 쓴다면?
만약 OpenDocument 파일 형식, 특히 "ODP" 프레젠테이션 형식이 SQLite 기반으로 만들어졌다면 어떤 점이 좋아질지 상상해 보는 글을 풀어볼게요. 순수한 사고 실험(thought experiment)이라서, 실제로 OpenDocument 를 바꾸자고 제안하는 건 아니라는 점 참고해 주세요.
본문
소개 (Introduction)
OpenDocument 파일 형식, 특히 "ODP" 프레젠테이션 형식이 SQLite 기반으로 만들어졌다고 상상해 보세요. 얻을 수 있는 이점은 다음과 같아요:
- 더 작은 문서
- 더 빠른 File/Save 시간
- 더 빠른 시작 시간
- 더 적은 메모리 사용
- 문서 버전 관리(versioning)
- 더 나은 사용자 경험
참고로 이것은 단지 사고 실험일 뿐이에요. 우리는 OpenDocument 가 바뀌어야 한다고 제안하는 게 아니에요. 이 글이 현재 OpenDocument 설계를 비판하는 것도 아니고요. 이 수필의 요점은 미래의 파일 형식 설계를 개선할 방법을 제안하는 데 있어요.
OpenDocument 와 OpenDocument Presentation 에 대해
OpenDocument 파일 형식은 사무용 애플리케이션, 즉 워드 프로세서, 스프레드시트, 프레젠테이션에 사용돼요. 원래 OpenOffice 제품군을 위해 설계되었지만 이후 다른 데스크톱 애플리케이션 제품군에도 통합되었어요. OpenOffice 애플리케이션은 몇 번 포크되고 이름이 바뀌었죠. 이 글의 저자는 주로 Mac에서 NeoOffice, Linux와 Windows에서 LibreOffice로 슬라이드 프레젠테이션을 만드는 데 OpenDocument를 사용해요.
OpenDocument Presentation, 즉 "ODP" 파일은 프레젠테이션 슬라이드를 설명하는 XML 파일들과, 프레젠테이션의 일부로 포함된 각 이미지에 대한 별도의 이미지 파일들을 담고 있는 ZIP 아카이브예요. (OpenDocument 워드 프로세서와 스프레드시트 파일도 비슷하게 구성되지만 이 글에서는 다루지 않아요.) 읽는 분은 "zip -l" 명령을 사용해 ODP 파일의 내용을 쉽게 볼 수 있어요. 예를 들어, 다음은 2014년 SouthEast LinuxFest 컨퍼런스의 SQLite에 관한 49-슬라이드 프레젠테이션의 "zip -l" 출력이에요:
Archive: self2014.odp
Length Date Time Name
--------- ---------- ----- ----
47 2014-06-21 12:34 mimetype
0 2014-06-21 12:34 Configurations2/statusbar/
0 2014-06-21 12:34 Configurations2/accelerator/current.xml
0 2014-06-21 12:34 Configurations2/floater/
0 2014-06-21 12:34 Configurations2/popupmenu/
0 2014-06-21 12:34 Configurations2/progressbar/
0 2014-06-21 12:34 Configurations2/menubar/
0 2014-06-21 12:34 Configurations2/toolbar/
0 2014-06-21 12:34 Configurations2/images/Bitmaps/
54702 2014-06-21 12:34 Pictures/10000000000001F40000018C595A5A3D.png
46269 2014-06-21 12:34 Pictures/100000000000012C000000A8ED96BFD9.png
... 58 other pictures omitted...
13013 2014-06-21 12:34 Pictures/10000000000000EE0000004765E03BA8.png
1005059 2014-06-21 12:34 Pictures/10000000000004760000034223EACEFD.png
211831 2014-06-21 12:34 content.xml
46169 2014-06-21 12:34 styles.xml
1001 2014-06-21 12:34 meta.xml
9291 2014-06-21 12:34 Thumbnails/thumbnail.png
38705 2014-06-21 12:34 Thumbnails/thumbnail.pdf
9664 2014-06-21 12:34 settings.xml
9704 2014-06-21 12:34 META-INF/manifest.xml
--------- -------
10961006 78 files
ODP ZIP 아카이브에는 content.xml, styles.xml, meta.xml, settings.xml 이라는 서로 다른 네 개의 XML 파일이 들어 있어요. 이 네 파일이 슬라이드 레이아웃, 텍스트 콘텐츠, 스타일을 정의해요. 이 특정 프레젠테이션은 전체 화면 그림부터 아주 작은 아이콘까지 62개의 이미지를 담고 있는데, 각각 Pictures 폴더에 별도의 파일로 저장되어 있어요. "mimetype" 파일에는 한 줄의 텍스트가 들어 있는데 내용은 다음과 같아요:
application/vnd.oasis.opendocument.presentation
다른 파일과 폴더들의 용도는 현재 저자도 잘 모르겠지만, 알아내기 어렵지는 않을 거예요.
OpenDocument Presentation 형식의 한계
ZIP 아카이브로 XML 파일과 리소스를 감싸는 것은 애플리케이션 파일 형식에 대한 우아한 접근이에요. 커스텀 이진 파일 형식보다 분명히 우월해요. 하지만 ZIP 대신 SQLite 데이터베이스를 컨테이너로 사용한다면 더욱 우아해질 거예요.
ZIP 아카이브는 기본적으로 키/값 데이터베이스로, 한 번 쓰고 여러 번 읽는 경우( write-once/read-many )와 값으로 큰 BLOB를 갖는 비교적 적은 수(수백~수천 개)의 고유 키에 최적화되어 있어요. ZIP 아카이브는 "파일 더미(pile-of-files)" 데이터베이스로 볼 수 있어요. 이것은 동작하지만, SQLite 데이터베이스에 비해 다음과 같은 단점이 있어요:
-
증분 갱신이 어렵다. ZIP 아카이브의 개별 항목을 갱신하는 것은 어려워요. 특히 갱신 도중 컴퓨터가 정전되거나 크래시가 나도 전체 문서가 파괴되지 않도록 개별 항목을 갱신하는 것은 더욱 어려워요. 불가능한 건 아니지만 충분히 어려워서 실제로 아무도 그렇게 하지 않아요. 대신 사용자가 "File/Save"를 선택할 때마다 전체 ZIP 아카이브가 다시 쓰여져요. 그래서 "File/Save"는 특히 오래된 하드웨어에서 기대보다 오래 걸려요. 최신 기기는 더 빠르지만, 50MB 프레젠테이션에서 한 글자를 바꾸는 것이 SSD의 유한한 쓰기 수명을 50MB만큼 태우게 된다는 것은 여전히 신경 쓰이는 일이에요.
-
시작이 느리다. 파일 더미 테마에 맞춰 OpenDocument는 모든 슬라이드 콘텐츠를 "content.xml"이라는 하나의 큰 XML 파일에 저장해요. LibreOffice는 첫 슬라이드를 표시하기 위해 이 전체 파일을 읽고 파싱해요. LibreOffice는 모든 이미지도 메모리로 읽어들이는 것처럼 보이는데, 사용자가 "File/Save"를 하면 어차피 그 모두를 다시 써야 하기 때문에 말이 되는 동작이에요. 실제로는 하나도 변경되지 않았는데도요. 결과적으로 시작이 느려져요. OpenDocument 파일을 더블클릭하면 첫 슬라이드가 아니라 진행 표시줄(progress bar)이 뜨죠. 이는 나쁜 사용자 경험을 낳아요. 문서 크기가 커질수록 상황은 점점 더 짜증나요.
-
더 많은 메모리가 필요하다. ZIP 아카이브는 큰 콘텐츠 덩어리를 저장하는 데 최적화되어 있어서, 시작 시 전체 문서를 메모리로 읽고, 모든 편집을 메모리에서 수행한 다음, "File/Save" 동안 전체 문서를 디스크에 쓰는 프로그래밍 방식을 조장해요. OpenOffice와 그 후손들이 그 패턴을 따르고 있어요.
수 기가바이트 데스크톱 시대이므로 전체 문서를 메모리로 읽어도 괜찮다고 주장할 수도 있어요. 하지만 괜찮지 않아요. 우선 사용되는 메모리 양이 디스크의 (압축된) 파일 크기를 크게 초과해요. 그래서 50MB 프레젠테이션은 200MB 이상의 RAM을 차지할 수 있어요. 한 번에 문서 하나만 편집한다면 여전히 문제가 아니에요. 하지만 이 저자는 발표 자료를 작업할 때 (지난 프레젠테이션에서 슬라이드를 복사·붙여넣기하기 위해) 보통 10~15개의 서로 다른 프레젠테이션을 동시에 열어 두기 때문에 수 기가바이트의 메모리가 필요해요. 웹 브라우저 한두 개와 몇몇 데스크톱 앱을 더하면 갑자기 디스크가 돌고 머신이 스와핑하기 시작해요. 그리고 Ubuntu를 얹은 저렴한 Chromebook에서 작업할 때는 문서 하나만 열어도 문제가 돼요. 메모리를 덜 쓰는 것이 항상 낫죠.
-
크래시 복구가 어렵다. OpenOffice의 후손들은 상용 경쟁사보다 세그폴트가 더 자주 발생하는 경향이 있어요. 아마 이런 이유로 OpenOffice 포크들은 메모리 내 문서를 주기적으로 백업해서, 불가피한 애플리케이션 크래시가 발생해도 사용자가 모든 대기 중인 편집을 잃지 않도록 해요. 이로 인해 각 백업이 만들어지는 몇 초 동안 애플리케이션에 답답한 멈춤이 발생해요. 크래시 후 재시작하면 사용자에게 복구 과정을 안내하는 대화 상자가 표시돼요. 이런 식으로 크래시 복구를 관리하는 것은 많은 추가 애플리케이션 로직을 수반하고 일반적으로 사용자에게 성가신 일이에요.
-
콘텐츠에 접근할 수 없다. OpenDocument 프레젠테이션의 콘텐츠는 일반 도구로 쉽게 보거나 바꾸거나 추출할 수 없어요. OpenDocument 문서를 보거나 편집하는 합리적인 방법은 OpenDocument를 읽거나 쓰도록 특별히 설계된 애플리케이션(즉, LibreOffice나 그 사촌들)으로 여는 것뿐이에요. 상황이 더 나빠질 수도 있긴 해요. "zip" 아카이버 도구만으로도 프레젠테이션에서 개별 이미지(예를 들어)를 추출해 볼 수 있으니까요. 하지만 슬라이드에서 텍스트를 추출하려는 것은 합리적이지 않아요. 모든 콘텐츠가 하나의 "context.xml" 파일에 저장된다는 걸 기억하세요. 그 파일은 XML이므로 텍스트 파일이에요. 하지만 일반 텍스트 편집기로 다룰 수 있는 텍스트 파일은 아니에요. 위의 예제 프레젠테이션에서 content.xml 파일은 정확히 두 줄로 구성돼요. 파일의 첫 줄은 그냥:
두 번째 줄은 211792자의 난해한 XML을 담고 있어요. 네, 211792자가 전부 한 줄에 있어요. 이 파일은 텍스트 편집기에 좋은 스트레스 테스트예요. 다행히도 그 파일은 난해한 이진 형식은 아니지만, 접근성 측면에서는 클링온어로 쓰여진 것이나 마찬가지예요.
첫 번째 개선: ZIP 을 SQLite 로 바꾸기
OpenDocument가 파일을 저장하기 위해 ZIP 아카이브를 사용하는 대신, 다음과 같은 단일 테이블 스키마를 가진 매우 단순한 SQLite 데이터베이스를 사용한다고 가정해 보세요:
CREATE TABLE OpenDocTree(
filename TEXT PRIMARY KEY, -- Name of file
filesize BIGINT, -- Size of file after decompression
content BLOB -- Compressed file content
);
이 첫 번째 실험에서는 파일 형식의 다른 부분은 바꾸지 않아요. OpenDocument는 여전히 파일 더미이고, 다만 이제 각 파일이 ZIP 아카이브의 항목이 아니라 SQLite 데이터베이스의 한 행이에요. 이 단순한 변경은 관계형 데이터베이스의 힘을 활용하지 않아요. 그래도 이 단순한 변경은 몇 가지 개선을 보여줘요.
놀랍게도 ZIP 대신 SQLite를 사용하면 프레젠테이션 파일이 더 작아져요. 정말이에요. 관계형 데이터베이스 파일이 ZIP 아카이브보다 클 것이라고 생각하겠지만, 적어도 NeoOffice의 경우에는 그렇지 않아요. 다음은 같은 NeoOffice 프레젠테이션의 크기를, NeoOffice가 생성한 원래 ZIP 아카이브 형식(self2014.odp)과 SQLAR 유틸리티로 SQLite 데이터베이스로 다시 포장한 경우로 나란히 보여주는 실제 화면 캡처예요:
-rw-r--r-- 1 drh staff 10514994 Jun 8 14:32 self2014.odp
-rw-r--r-- 1 drh staff 10464256 Jun 8 14:37 self2014.sqlar
-rw-r--r-- 1 drh staff 10416644 Jun 8 14:40 zip.odp
SQLite 데이터베이스 파일("self2014.sqlar")은 동등한 ODP 파일보다 약 0.5% 더 작아요! 어떻게 이럴 수 있을까요? 보아하니 NeoOffice의 ZIP 아카이브 생성 로직이 효율적이지 않아요. 같은 파일 더미를 명령줄 "zip" 유틸리티로 다시 압축하면 위의 셋째 줄에서 볼 수 있듯이 여전히 또 0.5% 더 작은 파일("zip.odp")을 얻게 되거든요. 그래서 잘 작성된 ZIP 아카이브는 기대대로 동등한 SQLite 데이터베이스보다 약간 더 작을 수 있어요. 하지만 그 차이는 미미해요. 핵심은 SQLite 데이터베이스가 ZIP 아카이브와 크기 경쟁이 된다는 거예요.
ZIP 대신 SQLite를 사용하는 또 다른 이점은 문서를 이제 증분 갱신할 수 있고, 갱신 도중 정전이나 다른 크래시가 발생해도 문서가 손상될 위험이 없다는 거예요. (SQLite 데이터베이스에 대한 쓰기는 원자적이라는 걸 기억하세요.) 사실 모든 콘텐츠가 여전히 하나의 큰 XML 파일("content.xml")에 보관되므로, 한 글자만 바뀌어도 완전히 다시 써야 해요. 하지만 SQLite에서는 그 파일 하나만 바뀌면 돼요. 저장소의 다른 77개 파일은 변경되지 않은 채로 둘 수 있어요. 전부 다시 쓸 필요가 없으므로, 이는 "File/Save"를 훨씬 빠르게 만들어 주고 SSD의 마모도 줄여 줘요.
두 번째 개선: 콘텐츠를 더 작은 조각으로 나누기
파일 더미 방식은 콘텐츠를 몇 개의 큰 덩어리로 저장하도록 유도해요. ODP의 경우 프레젠테이션의 모든 슬라이드 레이아웃을 정의하는 XML 파일이 네 개뿐이에요. SQLite 데이터베이스는 정보를 몇 개의 큰 덩어리로 저장할 수 있지만, 많은 수의 작은 조각으로 정보를 저장하는 데에도 능숙하고 효율적이에요.
그래서 모든 슬라이드의 모든 콘텐츠를 하나의 과대한 XML 파일("content.xml")에 저장하는 대신, 각 슬라이드의 콘텐츠를 각각 저장하는 별도의 테이블이 있다고 가정해 보세요. 테이블 스키마는 대략 다음과 같을 거예요:
CREATE TABLE slide(
pageNumber INTEGER, -- The slide page number
slideContent TEXT -- Slide content as XML or JSON
);
CREATE INDEX slide_pgnum ON slide(pageNumber); -- Optional
각 슬라이드의 콘텐츠는 여전히 압축된 XML로 저장될 수 있어요. 하지만 이제 각 페이지가 별도로 저장돼요. 그래서 새 문서를 열 때 애플리케이션은 단순히 다음을 실행하면 돼요:
SELECT slideContent FROM slide WHERE pageNumber=1;
이 쿼리는 첫 슬라이드의 콘텐츠를 빠르고 효율적으로 반환하고, 이것을 빠르게 파싱해서 사용자에게 표시할 수 있어요. 첫 화면을 렌더링하려면 한 페이지만 읽고 파싱하면 되므로, 첫 화면이 훨씬 빨리 나타나고 더 이상 성가신 진행 표시줄이 필요 없어요.
애플리케이션이 모든 콘텐츠를 메모리에 유지하고 싶다면, 첫 페이지를 그린 후 백그라운드 스레드를 사용해 나머지 페이지를 계속 읽고 파싱할 수 있어요. 아니면 SQLite에서 읽기가 매우 효율적이므로, 애플리케이션은 메모리 사용량을 줄이기 위해 한 번에 한 슬라이드만 메모리에 유지하기로 선택할 수도 있어요. 아니면 다음 슬라이드로의 빠른 전환을 위해 현재 슬라이드와 다음 슬라이드를 메모리에 유지할 수도 있죠.
SQLite 테이블로 콘텐츠를 더 작은 조각으로 나누면 구현에 유연성이 생긴다는 점에 주목하세요. 애플리케이션은 시작 시 모든 콘텐츠를 메모리로 읽도록 선택할 수 있어요. 아니면 몇 페이지만 메모리로 읽고 나머지는 디스크에 유지할 수도 있어요. 아니면 한 번에 한 페이지만 메모리로 읽을 수도 있어요. 그리고 서로 다른 앱 버전이 파일 형식을 전혀 바꾸지 않고 다른 선택을 할 수 있어요. 모든 콘텐츠가 ZIP 아카이브의 하나의 큰 XML 파일에 있을 때는 이런 옵션이 없어요.
콘텐츠를 더 작은 조각으로 나누는 것은 File/Save 작업을 더 빠르게 하는 데에도 도움이 돼요. File/Save를 할 때 모든 페이지의 콘텐츠를 다시 쓰는 대신, 애플리케이션은 실제로 변경된 페이지만 다시 쓰면 돼요.
콘텐츠를 더 작은 조각으로 나누는 사소한 단점 하나는, 짧은 텍스트일수록 압축이 잘 안 되어 문서 크기가 늘어날 수 있다는 거예요. 하지만 문서 공간의 대부분은 이미지 저장에 사용되므로, 텍스트 콘텐츠의 압축 효율이 조금 줄어드는 것은 거의 눈에 띄지 않고, 개선된 사용자 경험을 위한 작은 대가예요.
세 번째 개선: 버전 관리 (Versioning)
각 슬라이드를 따로 저장한다는 개념에 익숙해지면, 프레젠테이션의 버전 관리를 지원하는 것은 아주 작은 한 걸음이에요. 다음 스키마를 고려해 보세요:
CREATE TABLE slide(
slideId INTEGER PRIMARY KEY,
derivedFrom INTEGER REFERENCES slide,
content TEXT -- XML or JSON or whatever
);
CREATE TABLE version(
versionId INTEGER PRIMARY KEY,
priorVersion INTEGER REFERENCES version,
checkinTime DATETIME, -- When this version was saved
comment TEXT, -- Description of this version
manifest TEXT -- List of integer slideIds
);
이 스키마에서는 각 슬라이드가 프레젠테이션 안에서의 순서를 결정하는 페이지 번호를 갖는 대신, 시퀀스에서의 위치와 무관한 고유한 정수 식별자를 가져요. 프레젠테이션에서 슬라이드의 순서는 VERSION 테이블의 MANIFEST 컬럼에 텍스트 문자열로 저장된 slideId 목록에 의해 결정돼요. VERSION 테이블에 여러 항목이 허용되므로, 같은 문서에 여러 프레젠테이션을 저장할 수 있다는 뜻이에요.
시작 시 애플리케이션은 먼저 어떤 버전을 표시할지 결정해요. versionId는 당연히 시간이 지나면서 증가하고 보통 최신 버전을 보고 싶어 하므로, 적절한 쿼리는 다음과 같을 거예요:
SELECT manifest, versionId FROM version ORDER BY versionId DESC LIMIT 1;
아니면 애플리케이션이 가장 최근의 checkinTime을 사용하고 싶을 수도 있어요:
SELECT manifest, versionId, max(checkinTime) FROM version;
위와 같은 단일 쿼리를 사용해 애플리케이션은 프레젠테이션의 모든 슬라이드에 대한 slideId 목록을 얻어요. 그런 다음 애플리케이션은 첫 슬라이드의 콘텐츠를 조회하고, 그 콘텐츠를 파싱해 표시해요. 이전처럼요.
(여담: 네, 위의 두 번째 "max(checkinTime)"을 사용한 쿼리는 정말 동작하고 SQLite에서 정말 잘 정의된 답을 반환해요. 그런 쿼리는 많은 다른 SQL 데이터베이스 엔진에서는 정의되지 않은 답을 반환하거나 오류를 생성하지만, SQLite에서는 기대대로 동작해요: 최대 checkinTime을 가진 항목의 manifest와 versionId를 반환해요.)
사용자가 "File/Save"를 하면, 애플리케이션은 이제 수정된 슬라이드를 덮어쓰는 대신, 추가되거나 변경된 슬라이드들에 대해서만 SLIDE 테이블에 새 항목을 만들 수 있어요. 그런 다음 수정된 manifest를 담은 VERSION 테이블에 새 항목을 만들어요.
위에 보인 VERSION 테이블에는 체크인 설명(아마 사용자가 제공)과 File/Save 동작이 발생한 시각·날짜를 기록하는 컬럼이 있어요. 또한 변경 이력을 기록하기 위해 부모 버전도 기록해요. 아마 manifest는 부모 버전으로부터의 델타로 저장될 수 있지만, 보통 manifest는 충분히 작아서 델타를 저장하는 것이 얻는 것보다 번거로울 수 있어요. SLIDE 테이블에도 derivedFrom 컬럼이 있는데, 슬라이드 콘텐츠를 이전 버전으로부터의 델타로 저장하는 것이 가치 있는 최적화라고 판단된다면 델타 인코딩에 사용할 수 있어요.
이 간단한 변경으로 ODP 파일은 이제 프레젠테이션의 가장 최근 편집뿐 아니라 모든 과거 편집의 이력을 저장해요. 사용자는 보통 프레젠테이션의 가장 최신판만 보길 원하지만, 원한다면 이제 시간을 거슬러 같은 프레젠테이션의 과거 버전을 볼 수도 있어요.
아니면 같은 문서 안에 여러 프레젠테이션을 저장할 수도 있어요.
이런 스키마를 사용하면 애플리케이션은 더 이상 크래시 시 작업 손실을 피하기 위해 저장되지 않은 변경 사항을 별도 파일에 주기적으로 백업할 필요가 없어요. 대신 특별한 "pending" 버전을 할당하고 저장되지 않은 변경 사항을 pending 버전에 쓸 수 있어요. 전체 문서가 아니라 변경 사항만 쓰면 되므로, 대기 중인 변경 사항을 저장하는 것은 수 메가바이트가 아니라 몇 킬로바이트의 콘텐츠만 쓰는 일이고, 초가 아니라 밀리초가 걸려서, 자주 그리고 백그라운드에서 조용히 할 수 있어요. 그러다 크래시가 발생하고 사용자가 재부팅하면, 작업의 전부(또는 거의 전부)가 보존돼요. 사용자가 저장되지 않은 변경 사항을 버리기로 결정하면 이전 버전으로 그냥 돌아가면 돼요.
여기서 채워야 할 세부 사항들이 있어요. 모든 과거 변경 사항을 (아마 그래프와 함께) 표시해서 사용자가 보고 싶거나 편집할 버전을 선택할 수 있게 하는 화면이 제공될 수도 있어요. 버전 이력에서 발생할 수 있는 포크를 병합하는 기능이 제공될 수도 있어요. 애플리케이션이 오래되고 원치 않는 버전을 정리하는 수단을 제공할 수도 있겠죠. 핵심은 ZIP 아카이브 대신 SQLite 데이터베이스로 콘텐츠를 저장하면 이 모든 기능을 구현하기가 훨씬, 훨씬 쉬워지고, 그래서 결국 구현될 가능성이 높아진다는 거예요.
그래서 또 무엇이... (And So Forth...)
이전 섹션들에서 우리는 ZIP 아카이브로 구현된 키/값 저장소에서 겨우 세 개의 테이블을 가진 단순한 SQLite 데이터베이스로 옮겨가는 것이 애플리케이션 파일 형식에 얼마나 큰 기능을 더할 수 있는지 보았어요. 우리는 성능을 위한 인덱스 추가, 프로그래밍 편의를 위한 트리거와 뷰, 프로그래밍 오류에도 콘텐츠의 일관성을 강제하는 제약 조건과 함께 새 테이블로 스키마를 계속 확장할 수 있어요. 추가 개선 아이디어는 다음과 같아요:
- 자동화된 undo/redo 스택을 데이터베이스 테이블에 저장해서 Undo가 이전 편집 세션으로 거슬러 갈 수 있게 하기.
- 슬라이드 덱에, 또는 여러 슬라이드 덱에 걸쳐 전문 검색(full text search) 기능 추가하기.
- "settings.xml" 파일을 별도의 애플리케이션이 더 쉽게 보고 편집할 수 있는 SQL 테이블로 분해하기.
- 각 슬라이드의 "발표자 노트(Presenter Notes)"를 별도의 테이블로 분리해서 서드파티 애플리케이션 및/또는 스크립트에서 더 쉽게 접근하게 하기.
- 단순한 선형 슬라이드 시퀀스를 넘어, 청중의 반응에 따라 사이드 트랙과 일탈을 취할 수 있게 프레젠테이션 개념을 확장하기.
SQLite 데이터베이스는 이 수필이 겨우 발끝만 대본 아주 많은 능력을 갖고 있어요. 하지만 이 짧은 엿보기가 일부 독자에게 SQL 데이터베이스를 애플리케이션 파일 형식으로 쓰는 것이 한 번 더 볼 가치가 있다고 확신시켰으면 좋겠어요.
일부 독자는 엔터프라이즈 SQL 데이터베이스에 대한 기존 경험과 그 시스템들의 경고·한계 때문에 SQLite를 애플리케이션 파일 형식으로 사용하는 것을 꺼릴 수도 있어요. 예를 들어 많은 엔터프라이즈 데이터베이스 엔진은 큰 문자열이나 BLOB를 데이터베이스에 저장하지 말고, 큰 문자열과 BLOB를 별도의 파일로 저장하고 그 파일명을 데이터베이스에 저장하라고 권장해요. 하지만 SQLite는 그렇지 않아요. SQLite 데이터베이스의 어떤 컬럼이든 약 1기가바이트 크기까지의 문자열이나 BLOB를 담을 수 있어요. 그리고 100킬로바이트 이하의 문자열과 BLOB에 대해서는 I/O 성능이 별도 파일을 사용하는 것보다 더 좋아요.
일부 독자는 모든 SQL 데이터베이스 스키마가 제3정규형 (Third Normal Form, 3NF)으로 분해되어야 하고 문자열과 정수 같은 작은 원시 데이터 타입만 저장해야 한다는 생각을 주입받았기 때문에 SQLite를 애플리케이션 파일 형식으로 고려하는 것을 꺼릴 수도 있어요. 확실히 관계형 이론은 중요하고 설계자들은 그것을 이해하려고 노력해야 해요. 하지만 위에서 보여주었듯이, 복잡한 정보를 데이터베이스의 텍스트 필드에 XML이나 JSON으로 저장하는 것은 종종 상당히 괜찮아요. 데이터베이스 교수님이 해야 한다고 말한 것을 하는 게 아니라, 동작하는 것을 하세요.
SQLite 사용의 이점 정리
요약하면, 이 수필의 주장은 OpenDocument 같은 애플리케이션 파일 형식을 위한 컨테이너로 SQLite를 사용하고, 그 컨테이너에 많은 작은 객체를 저장하는 것이, 몇 개의 더 큰 객체를 담는 ZIP 아카이브를 사용하는 것보다 훨씬 잘 동작한다는 것이에요. 즉:
- SQLite 데이터베이스 파일은 같은 정보를 담는 ZIP 아카이브와 대략 같은 크기이며, 어떤 경우에는 더 작아요.
- SQLite의 원자적 갱신 능력 덕분에 작은 증분 변경이 문서에 안전하게 쓰여질 수 있어요. 이는 전체 디스크 I/O를 줄이고 File/Save 성능을 개선해 사용자 경험을 향상시켜요.
- 시작 시간은 애플리케이션이 초기 화면에 표시되는 콘텐츠만 읽도록 허용함으로써 줄어들어요. 이는 새 문서를 열 때 진행 표시줄을 보여줄 필요를 대부분 없애요. 문서가 즉시 나타나서 사용자 경험을 한층 더 향상시켜요.
- 애플리케이션의 메모리 사용량은 현재 표시와 관련된 콘텐츠만 불러오고 대부분의 콘텐츠는 디스크에 유지함으로써 극적으로 줄일 수 있어요. SQLite의 빠른 쿼리 능력 덕분에 이것은 모든 콘텐츠를 항상 메모리에 유지하는 대안보다 실현 가능해요. 그리고 애플리케이션이 메모리를 덜 쓰면 전체 컴퓨터가 더 반응적으로 되어 사용자 경험이 한층 더 향상돼요.
- SQL 데이터베이스의 스키마는 ZIP 아카이브 같은 키/값 데이터베이스보다 정보를 더 직접적이고 간결하게 표현할 수 있어요. 이는 문서 콘텐츠를 서드파티 애플리케이션과 스크립트에 더 접근 가능하게 하고, 내장된 문서 버전 관리와 크래시 복구를 위한 진행 중 작업의 증분 저장 같은 고급 기능을 용이하게 해요.
이것들은 SQLite를 애플리케이션 파일 형식으로 사용할 때의 이점 중 일부에 불과해요 — OpenOffice 같은 애플리케이션의 사용자 경험을 가장 개선할 것 같은 이점들이죠. 다른 애플리케이션은 다른 방식으로 SQLite의 혜택을 받을 수 있어요. 추가 아이디어는 Application File Format 문서를 참고해 주세요.
마지막으로, 이 수필은 사고 실험이라는 것을 다시 강조할게요. OpenDocument 형식은 잘 확립되어 있고 이미 잘 설계되어 있어요. 누구도 OpenDocument가 ZIP 대신 SQLite를 컨테이너로 사용하도록 바뀌어야 한다고 정말로 믿지 않아요. OpenDocument가 SQLite보다 먼저 있었으므로, 이 글이 OpenDocument가 SQLite를 컨테이너로 선택하지 않은 것을 비판하는 것도 아니에요. 오히려 이 글의 요점은, 미래 프로젝트를 위해 SQLite로 더 나은 애플리케이션 파일 형식을 어떻게 만들 수 있는지 보여주는 구체적인 예로 OpenDocument를 사용하는 것이에요.