옵클(Opkle)은 창작자를 위한 다양한 앱과 시스템을 제공하는 개발사입니다. 전자책 에디터 앱 'Opkle editor'를 출시했고, 관련 전자책 클래스를 제공하고 있습니다.
EDITORCLASSBLOGLOGIN표현한다는 것의무한한 가능성,새로운 형태로 담아내다.새로운 형태의 콘텐츠Opkle은 코드 없이 웹을 마음껏 만들고, 누구나 자기 화면을 그릴 수 있도록 에디터를 만들고, 그 결과물을 어디서나 즐길 수 있게 돕는 팀입니다.텍스트와 화면과의 조화를 통해, 웹을 짓는다는 것이 그저 단순한 코딩이 아닌, 상상을 펼치고 감각을 깨우는 과정이 될 수 있도록 좋은 도구를 만들어 냅니다.옵클 에디터 개발기: 첫 번째 구현은 코드 편집기였습니다dev2옵클 에디터 개발기: 미리보기는 곧 또 하나의 편집기였습니다dev3옵클 에디터 개발기: 미디어쿼리까지 편집하는 EPUB 에디터dev4옵클 에디터 개발기: CSS 애니메이션으로 영상을 흡수하는 방식dev5옵클 에디터 개발기: 책을 만들기 위한 LLM을 설계하다dev6678910...2 옵클 에디터 개발기: 첫 번째 구현은 코드 편집기였습니다EPUB 에디터를 만들겠다고 마음먹은 뒤에는 바로 구현으로 들어갔습니다. 그때의 판단은 지금 생각하면 꽤 직선적이었습니다. EPUB은 결국 압축된 웹문서 묶음이고, 본문은 XHTML, 스타일은 CSS, 패키지 정보는 OPF와 NCX 또는 nav 문서로 구성됩니다. 그러니 웹 개발자가 EPUB 에디터를 만든다면 TypeScript와 Node.js를 기반으로 파일 트리, 패키징, 코드 편집, 미리보기, 저장 구조를 직접 통제하는 쪽이 가장 자연스럽다고 생각했습니다.처음 프로젝트는 Node.js + TypeScript 구조로 잡았습니다. 서버 쪽에서는 EPUB 프로젝트를 하나의 작업 디렉터리처럼 다루고, 프론트에서는 각 XHTML/CSS 리소스를 독립된 편집 단위로 열 수 있게 만들었습니다. 에디터의 중심에는 CodeMirror 6를 붙였습니다. CM6는 확장 구조가 좋고, language package와 transaction/update listener를 통해 입력 상태를 세밀하게 감지할 수 있었기 때문에, 단순 textarea보다 훨씬 안정적으로 코드 편집 경험을 만들 수 있었습니다.그때 제가 만들려던 것은 사실상 “Sigil보다 자동화된 웹 기반 EPUB IDE”에 가까웠습니다. EPUB의 `META-INF/container.xml`, `OEBPS/content.opf`, 본문 XHTML, CSS, 이미지, 폰트 리소스를 프로젝트 트리로 만들고, 파일을 열 때마다 해당 리소스에 대응하는 CodeMirror 인스턴스를 생성했습니다. 사용자가 본문을 입력하면 update listener가 변경 내용을 감지하고, 내부 상태에 반영한 뒤, 필요한 경우 HTML fragment를 다시 정리하는 방식이었습니다. 지금 돌아보면 첫 구현은 창작자용 에디터라기보다 EPUB 패키지를 다루는 개발 도구에 가까웠습니다.EPUB 프로젝트를 먼저 파일 시스템처럼처음에는 EPUB을 창작자의 화면보다 패키지 구조에서 바라봤습니다. 새 책을 만들면 기본 폴더 트리를 생성하고, OPF 파일에는 metadata, manifest, spine을 세팅했습니다. 본문 파일을 추가하면 manifest에 XHTML item을 넣고, spine에도 읽기 순서를 반영하는 식이었습니다. CSS 파일은 별도의 리소스로 등록하고, 본문 XHTML에서 link 태그로 연결되게 했습니다.이 접근은 개발자로서는 명확했습니다. EPUB은 결국 reading system이 읽어야 하는 규칙을 가진 파일 묶음이기 때문에, 에디터가 그 규칙을 일관되게 관리하면 됩니다. 어떤 파일이 manifest에 빠져 있으면 안 되고, spine에 없는 본문은 읽기 순서에서 사라지며, 이미지나 폰트도 OPF에 등록되지 않으면 패키지 안에서 고아 리소스가 됩니다. 그래서 처음 구현에서는 프로젝트 트리와 OPF 상태를 강하게 연결하려고 했습니다.파일 하나를 추가하는 행위도 단순히 UI 목록에 항목을 추가하는 문제가 아니었습니다. 내부적으로는 리소스 path, media-type, manifest id, spine 포함 여부, 본문에서 참조되는 상대 경로까지 함께 맞아야 했습니다. 특히 EPUB은 ZIP으로 묶인 뒤에도 내부 경로가 유지되어야 하므로, 프론트에서 보이는 이름과 실제 패키지 경로를 분리해서 관리할 필요가 있었습니다.당시에는 이 구조를 자동으로 잘 처리해 주면 사용자가 훨씬 편해질 것이라고 생각했습니다. 사용자는 폴더 트리를 직접 만들지 않아도 되고, OPF를 손으로 수정하지 않아도 되며, 리소스를 추가하면 manifest가 알아서 갱신됩니다. 그러나 이 판단에는 전제가 있었습니다. 사용자가 최소한 “EPUB은 이런 파일들의 묶음”이라는 사실을 받아들이고, 그 파일들을 편집하는 작업 방식에 익숙해져야 한다는 전제였습니다.CodeMirror6 와 HTML 편집본문 편집은 CodeMirror 6를 중심으로 만들었습니다. 각 XHTML 파일마다 독립된 CM6 EditorView를 만들고, 문서 상태는 EditorState로 관리했습니다. 입력이 들어오면 update listener에서 transaction을 받아 변경 여부를 확인하고, 바뀐 문자열을 내부 document model에 반영했습니다. 저장 시점에는 이 문자열을 다시 XHTML 파일 내용으로 직렬화하는 방식이었습니다.처음에는 사용자가 완전히 손으로 HTML을 작성하지 않아도 되게 만들고 싶었습니다. 예를 들어 일반 텍스트를 입력하면 문단 단위로 `
` 태그를 씌우고, 특정 영역을 선택한 뒤 더블 클릭하거나 우클릭하면 태그 이름을 바꾸거나 속성을 추가할 수 있게 했습니다. 선택한 영역에 class를 붙이고, 그 class를 CSS 쪽에서 선택자로 연결하는 흐름도 생각했습니다. 코드 편집기 위에 창작자용 조작 레이어를 얹는 방식이었습니다.
기술적으로는 꽤 재미있는 작업이었습니다. CM6에서 selection range를 읽고, 해당 range가 HTML 문자열의 어느 태그 내부에 있는지 추적하고, context menu에서 tagName이나 attribute를 바꾸면 원본 문자열을 수정해야 했습니다. 단순 텍스트 편집이 아니라, 문자열 안의 HTML 구조와 사용자의 selection을 계속 맞춰야 했습니다. 잘못하면 커서 위치가 틀어지고, 태그가 깨지고, undo stack과 내부 상태가 어긋납니다.이 문제를 다루면서 저는 꽤 많은 시간을 “코드를 덜 보이게 하는 코드 편집기”에 썼습니다. 사용자가 직접 `
`를 치지 않아도 되게 하고, 속성 편집 UI를 붙이고, 우클릭으로 태그를 바꾸게 만들었습니다. 하지만 핵심 구조는 여전히 HTML 문자열이었습니다. 사용자는 결국 태그와 속성, class와 selector의 세계 안에서 작업해야 했습니다.