Opkle(옵클) - 창작자를 위한 앱과 시스템
옵클(Opkle)은 창작자를 위한 다양한 앱과 시스템을 제공하는 개발사입니다. 전자책 에디터 앱 'Opkle editor'를 출시했고, 관련 전자책 클래스를 제공하고 있습니다.
EDITOR
CLASS
BLOG
LOGIN
표현한다는 것의
무한한 가능성,
새로운 형태로
담아내다.
새로운 형태의 콘텐츠
Opkle은 코드 없이 웹을 마음껏 만들고, 누구나 자기 화면을 그릴 수 있도록 에디터를 만들고, 그 결과물을 어디서나 즐길 수 있게 돕는 팀입니다.
텍스트와 화면과의 조화를 통해, 웹을 짓는다는 것이 그저 단순한 코딩이 아닌, 상상을 펼치고 감각을 깨우는 과정이 될 수 있도록 좋은 도구를 만들어 냅니다.
옵클 에디터 개발기: Valkey 큐 서버의 기본 구조
dev
7
옵클 에디터 개발기: Worker loop와 AI 결과 검증
dev
8
옵클 에디터 개발기: 미리보기가 그대로 코드로
dev
9
옵클 에디터 개발기: 그래픽 에디터도 결국 직접 만들기로 했다
dev
10
옵클 에디터 개발기: Rust 공장을 그래픽 엔진으로 다듬다
dev
11
7
8
9
10
11
...
9
옵클 에디터 개발기: 미리보기가 그대로 코드로
큐 서버가 안정적으로 돌아가게 된 다음에는, AI가 옵클 에디터의 작업 방식을 익히게 해야 했습니다. 모델이 문장을 잘 쓰는 것과 에디터 안에서 제대로 일하는 것은 다른 문제입니다. 에디터 안에서는 사용자가 렌더링된 EPUB 미리보기를 보고 있고, 실제 저장되는 것은 문서 마크업과 스타일입니다. 사용자가 눈앞의 문단을 고쳤는데 뒤의 코드가 다르게 남으면 안 됩니다. AI도 마찬가지였습니다. 화면에서 한 일과 EPUB 안에 들어가는 코드가 같은 방향으로 움직여야 했습니다.
제가 만들고 싶었던 것은 “마크업을 써주는 AI”가 아니었습니다. 사용자는 코드 편집기를 보고 있지 않습니다. 문단을 선택하고, 이미지를 누르고, 서식을 바꾸고, 미리보기 위에서 바로 명령을 내립니다. 그러면 에디터는 선택된 대상의 현재 구조, 적용 중인 공유 스타일, 장 단위 스타일 문맥, 대상 주소, 사용자 명령을 묶어 AI에게 넘깁니다. AI는 그 상태를 읽고, 실제 EPUB에 들어갈 수 있는 교체 단위와 스타일 변경을 돌려줘야 합니다.
여기서 중요한 것은 그럴듯한 화면을 만드는 게 아니었습니다. 다시 적용할 수 있는 수정 결과를 만드는 것이었습니다. 미리보기에서 바뀐 모습과 저장되는 문서 표현이 같아야 하고, 편집기 문자열과 document state, 로컬 저장 상태까지 같은 변경으로 이어져야 합니다. 그래서 AI에게 자유롭게 설명하게 하지 않고, 적용 가능한 patch만 받도록 했습니다. AI가 할 수 있는 일은 선택 대상의 구조와 스타일을 고치거나, 에디터가 아는 UI 동작을 실행하는 것뿐입니다.
Preview as source
옵클 에디터에서 미리보기는 단순히 결과를 보는 화면이 아닙니다. 실제로 편집이 일어나는 표면입니다. 사용자는 EPUB 코드를 직접 보지 않아도 렌더링된 문단과 이미지와 표를 보면서 작업합니다. 그렇지만 EPUB은 결국 마크업과 스타일로 만들어집니다. 그래서 미리보기에서 일어난 편집은 반드시 코드로 되돌아갈 수 있어야 합니다. 이 연결이 느슨하면 미리보기는 편집기가 아니라 미리보기로만 남습니다.
그래서 선택된 대상은 단순 화면 node가 아니라 코드 주소를 가진 대상이어야 했습니다. 사용자가 어떤 문단을 선택하면, 에디터는 그 문단의 주소와 현재 전체 요소 표현, 적용 중인 스타일 문맥을 같이 잡습니다. AI에게도 똑같이 넘깁니다. “이 문장을 조금 더 크게 해줘”라는 말만 보내면 부족합니다. 지금 대상의 구조가 무엇인지, 어떤 공유 규칙과 국소 규칙이 영향을 주고 있는지까지 같이 줘야 합니다.
이렇게 해두면 미리보기와 코드 편집기의 역할이 분명해집니다. 미리보기는 사람이 만지는 표면이고, 마크업과 스타일은 저장과 export의 기준입니다. AI는 미리보기 DOM을 직접 휘젓는 것이 아니라, 미리보기에서 선택된 대상에 적용할 수 있는 코드 patch를 만들어야 합니다. 그래야 AI로 바꾼 결과가 화면에만 남지 않고 EPUB 파일 안에도 남습니다.
격리된 렌더 환경과도 이 방식은 잘 맞았습니다. 미리보기 안의 렌더링 결과는 독립적인 스타일 공간을 갖지만, 에디터는 그 안에서 선택된 대상을 document state의 원본과 연결할 수 있습니다. AI에게 전달되는 것은 화면에 보이는 텍스트 한 조각이 아니라, 다시 저장 가능한 target context입니다. 미리보기는 사람에게는 편집 화면이고, AI에게는 구조화된 작업 대상이 됩니다.
Selection-bound action space
AI가 미리보기 편집에 들어오려면 본문 생성 경로와 다른 공간이 필요했습니다. 본문 생성은 책에 들어갈 문장을 쓰거나 다듬습니다. 선택 기반 편집은 선택된 대상의 구조와 스타일을 고칩니다. 그래서 이 경로의 전제는 처음부터 action engine이었습니다. 사용자가 문서 안에서 하나 이상의 대상을 선택했고, 그 대상들의 현재 표현과 적용 스타일이 주어졌다는 가정 위에서 움직입니다.
입력은 대상별 전체 요소 표현과 스타일 문맥입니다. 요소는 현재 node를 통째로 대체할 수 있는 단위여야 하고, 스타일은 책 전체 공유 규칙과 장에 가까운 국소 규칙으로 나뉩니다. AI는 이 정보를 보고 사용자의 요청을 해석합니다. 결과는 선택 대상 자체를 바꾸는 변경과, 에디터 UI를 움직이는 동작으로 나뉩니다.
이 구분이 생각보다 중요했습니다. 사용자가 “이 문단을 파란색으로 바꿔줘”라고 하면 구조나 스타일 patch를 반환해야 합니다. 그런데 “서식 팝업 열어줘”라고 하면 문서를 건드리면 안 됩니다. UI 동작만 반환하면 됩니다. 또 “이 문단을 강조하고 서식 팝업도 열어줘”처럼 두 일이 섞이면 문서 변경과 UI 동작이 같이 나올 수 있습니다. 한 입력 안에서도 코드 수정과 UI 제어가 동시에 존재할 수 있는 구조입니다.
그래서 출력은 자유 문장이 아니라 고정된 적용 가능한 형태로 제한했습니다. 설명도, markdown도, 장식도 없어야 합니다. 에디터가 이 결과를 그대로 해석해서 적용하기 때문입니다. AI가 친절하게 설명하는 것보다, 에디터가 바로 실행할 수 있는 결과를 주는 것이 더 중요했습니다.
Addressable replace units
선택 기반 편집에서 구조 변경은 항상 전체 교체 단위로 받게 했습니다. 부분 조각만 받으면 적용 위치가 애매해집니다. 선택된 node를 통째로 교체할 수 있어야 하므로, AI는 해당 대상의 완전한 요소를 반환해야 합니다. 요청이 태그 변경을 요구하지 않는다면 기존 구조를 유지하고, 필요한 속성과 국소 스타일만 바꾸는 것이 기본입니다.
주소는 절대 바꾸면 안 됩니다. 대상 주소는 에디터가 patch를 다시 꽂는 좌표입니다. AI가 주소를 빼먹거나 새 주소를 만들면 결과를 적용할 수 없습니다. 그래서 지시문에서도 주소를 정확히 유지하라고 고정했고, 정리 단계에서도 주소가 없는 변경은 버리게 했습니다. 되돌려 꽂을 좌표가 없는 patch는 아무리 좋아 보여도 쓸 수 없습니다.
스타일만 바꾸는 경우에도 교체 단위의 shape는 같아야 했습니다. 공유 stylesheet에 override만 추가하는 작업이어도, 결과의 형태가 일정해야 적용 경로가 단순해집니다. 대신 실제로 바꾼 대상만 결과에 포함합니다. 바꾸지 않은 대상까지 매번 되풀이하면 불필요한 교체가 일어나고, 선택 상태나 되돌리기 스택이 괜히 흔들릴 수 있습니다.
이 계약을 두면 AI가 만든 구조는 미리보기와 document state 사이에서 안정적인 patch 단위가 됩니다. 사용자는 “이 문단 좀 더 잘 보이게 해줘”라고 말하지만, 서버는 대상 주소와 현재 표현을 받고, AI는 수정된 교체 단위를 반환하고, 에디터는 해당 node를 교체합니다. 그다음 직렬화된 문서, 편집기 문자열, 로컬 저장 상태가 같은 변경으로 따라옵니다.
Cascading write channels
스타일은 더 조심해서 다뤘습니다. AI에게 stylesheet를 마음대로 다시 쓰게 하면 EPUB 전체가 흔들릴 수 있습니다. 그래서 선택 기반 편집에서 스타일을 바꾸는 채널은 제한했습니다. 하나는 요소에만 붙는 국소 스타일이고, 다른 하나는 공유 stylesheet에 덧붙이는 override입니다. 장 단위 스타일은 읽기 전용 문맥으로만 줍니다. AI가 그 채널을 수정해서 반환하는 것은 막았습니다.
국소 스타일은 해당 요소 하나에만 적용되는 변경에 씁니다. 선택된 문단 하나의 글자색을 바꾸거나, 특정 이미지 하나의 여백을 조정하는 경우에는 그 요소 안에 직접 넣는 것이 안정적입니다. patch 범위가 분명합니다. 대상 하나에만 영향을 주기 때문에 다른 문단이나 서식이 예상치 않게 바뀌지 않습니다.
공유 stylesheet는 공통 선택자를 바꿔야 할 때 씁니다. 여러 요소에 함께 적용되는 규칙이나 특정 부류의 스타일을 바꿔야 한다면, 새 규칙만 뒤에 붙입니다. 여기서 중요한 것은 append-only입니다. 기존 stylesheet를 다시 쓰거나 그대로 되풀이하지 않습니다. 뒤에 붙는 규칙이 cascade에서 이기므로, 필요한 override만 반환하면 됩니다. 기존 스타일을 부수지 않고 변경을 쌓는 방식입니다.
국소 문맥 채널은 항상 수정하지 않게 했습니다. 그 정보는 AI가 현재 상태를 이해하기 위한 배경일 뿐, 쓰기 채널이 아닙니다. 국소 규칙이 어떤 값을 설정하고 있더라도, 바꿔야 한다면 요소 국소 스타일이나 공유 override로 처리합니다. 채널을 이렇게 제한해두면 스타일 patch가 어디에 들어가는지 분명해지고, EPUB export 시 stylesheet 구조도 예측할 수 있습니다.
Finite action alphabet
미리보기 편집에서 모든 요청이 구조·스타일 patch로 끝나는 것은 아닙니다. 사용자는 같은 입력창에서 미리보기를 열거나, 선택 모드를 켜거나, 장을 추가하거나, 서식 창을 여는 같은 UI 제어도 말할 수 있습니다. 이런 요청을 문서 수정으로 오해하면 안 됩니다. 그래서 에디터가 실제로 실행할 수 있는 동작의 유한 집합이 필요했습니다.
이 집합은 자연어 설명의 목록이 아니라, 실행 가능한 식별자의 alphabet입니다. AI는 새 동작 이름을 만들 수 없고, 집합 안의 원소만 반환해야 합니다. 선택 기반 편집 경로에서도 이 alphabet은 그대로 사용됩니다. 순수 UI 요청이면 문서 변경은 비어 있고 동작만 들어갑니다. 대상 편집과 UI 명령이 섞인 요청이면 둘 다 반환됩니다. 덕분에 미리보기 위에서 사용자가 하는 말이 코드 patch인지 UI action인지 같은 pipeline 안에서 처리됩니다.
동작 식별자를 alphabet으로 제한한 이유는 단순합니다. 실행할 수 없는 명령은 에디터에게 의미가 없습니다. 자연어 요청은 유연하게 받아도, 실행은 결정적 식별자로 고정해야 합니다. alphabet을 지시 문맥에 넣고, 정리 단계에서 모르는 원소를 걸러내면 실행 가능한 명령만 남습니다. AI가 판단하더라도 마지막 출력은 에디터의 언어로 내려와야 했습니다.
Pre-generative routing
특히 신경 쓴 것은, AI를 부르기 전에 확정할 수 있는 요청을 먼저 처리하는 일이었습니다. 미리보기, 새 프로젝트, 장 추가, 서식 창처럼 에디터 UI 동작으로 바로 매칭되는 입력은 모델까지 갈 필요가 없습니다. 그래서 정규화와 단계적 매칭으로 이루어진 rule-based router를 만들었습니다.
사용자 입력은 그대로 믿지 않습니다. 먼저 장식적인 접두를 떼고, 공백과 문장부호를 정리하고, 자주 흔들리는 표기를 보정합니다. 띄어쓰기 차이도 압축된 형태로 흡수합니다. 한국어 UI 명령은 사용자가 항상 정확히 입력하지 않기 때문에, 이 정리 단계가 꽤 중요했습니다.
다만 넓게 잡는 만큼 방어선도 필요했습니다. 본문을 쓰라는 요청을 서식 명령으로 착각하면 안 됩니다. 그래서 writing veto를 두었습니다. 쓰기, 다듬기, 교정, 번역, 요약, 이어쓰기처럼 생성 의도가 섞이면 UI 동작 매칭을 포기하고 본문 작업 경로로 넘깁니다. 반대로 짧은 UI 단어는 엄격한 집합으로 관리해서, 긴 문장 안에 우연히 들어갔다고 바로 동작으로 보내지 않게 했습니다.
선택 대상이 있을 때는 더 보수적인 gate를 사용했습니다. 사용자의 말이 대상 편집인지 UI 명령인지 애매할 수 있기 때문입니다. 그래서 선택 앞단의 UI 매칭은 완전 일치와 핵심 표현 위주로만 통과시킵니다. 넓은 퍼지 매칭은 일부러 배제했습니다. 애매하면 선택 문맥을 보고 판단하는 편이 더 낫습니다. 미리보기 편집에서는 이 보수성이 중요했습니다.
Output normalization
모델 응답도 그대로 믿지 않았습니다. UI 동작 응답은 원칙적으로 하나의 식별자여야 하지만, 실제로는 따옴표, 장식, 배열, 객체, 마침표, 문장 속 설명이 섞일 수 있습니다. 그래서 정규화 계층은 여러 겉모습을 흡수합니다. 구조 안에서 후보를 모으고, 문장 안에 식별자가 하나만 박혀 있으면 그것도 잡습니다. 복수 후보가 보이면 모호하므로 버립니다.
선택 기반 응답은 더 까다롭습니다. 구조와 스타일 값 안에는 중첩된 괄호가 들어갈 수 있기 때문에, 단순 부분 문자열로 객체를 자르면 안 됩니다. 그래서 균형을 맞추며 최상위 후보를 찾는 추출이 필요합니다. 문자열 리터럴 내부의 괄호는 무시하면서, 적용 가능한 변경과 동작을 담은 후보를 우선 파싱합니다. 앞뒤에 설명이 붙어도 실행 가능한 객체만 꺼낼 수 있어야 했습니다.
파싱된 변경 항목도 다시 정리합니다. 주소가 비어 있으면 버리고, 구조 값이 잘못되면 보정하거나 제외합니다. 스타일 채널은 문자열만 받고, 동작 목록은 alphabet 정규화를 통과시켜 모르는 원소를 제거합니다. 파싱 자체가 불가능하면 채택하지 않고 재시도하게 합니다.
이 계층이 있어야 AI가 조금 흐트러져도 에디터의 적용 규칙은 유지됩니다. AI에게 엄격한 출력을 요구하되, 서버에서는 현실적인 복구를 합니다. 다만 복구할 수 없는 경우에는 억지로 적용하지 않습니다. 주소 없는 변경, 모르는 동작, 해석 불가능한 구조는 버리거나 재시도합니다. 미리보기와 EPUB 코드의 일치를 지키려면 애매한 patch를 적용하는 것보다 적용하지 않는 편이 낫습니다.
Same EPUB
이 구조가 잡히면 사용자는 코드를 보지 않고도 EPUB을 편집할 수 있습니다. 미리보기에서 문단을 선택하고 “이 문단을 더 작게”, “이 이미지를 가운데로”, “이 표를 모바일에서 보기 좋게”, “서식 팝업 열어줘”처럼 말하면 됩니다. 에디터는 그 말이 코드 patch인지 UI 동작인지 판단하고, 선택 문맥을 구성하고, AI 응답을 적용 가능한 형태로 정리하고, 가능한 것만 document state에 반영합니다.
중요한 것은 화면에서만 바뀌지 않는다는 점입니다. 교체 단위가 바뀌고, 공유 stylesheet에 override가 붙고, UI 동작이 실행되고, 그 결과가 편집기 문자열과 로컬 저장 상태로 이어집니다. 미리보기, 코드 편집기, 저장소가 같은 변경을 공유합니다. AI는 따로 붙은 장식 기능이 아니라 에디터의 serialization pipeline 안에서 움직입니다.
결정적 routing과 선택 기반 편집은 서로 보완합니다. routing은 확정적인 UI 동작을 빠르게 처리하고, 선택 기반 경로는 대상의 구조·스타일 patch를 만듭니다. 본문 작성 경로로 들어간 요청이라도, 실제로는 UI 동작이라고 판정되면 다시 동작 alphabet으로 빠져나올 수 있습니다. 사용자는 자연어 입력 하나를 쓰지만, 내부에서는 본문 생성, UI routing, 선택 patch가 분리됩니다.
결과적으로 AI는 옵클 에디터의 EPUB 작성법을 따르는 작업자가 되었습니다. 어떤 스타일은 요소에만 들어가야 하고, 어떤 스타일은 공유 stylesheet 뒤에 붙어야 하며, 장 단위 스타일은 읽기 전용으로만 봐야 합니다. 어떤 요청은 교체 단위를 바꾸고, 어떤 요청은 동작 alphabet을 실행해야 합니다. 주소는 유지해야 하고, 구조는 완전한 교체 단위여야 하며, 출력은 엄격한 적용 가능 형태를 지켜야 합니다. 이 규칙들이 맞물리면서 미리보기 편집과 실제 EPUB 코드가 같은 결과를 바라보게 되었습니다.
이 단계도 안정적으로 완성되었습니다. 미리보기 선택 문맥, 적용 가능한 출력 계약, 주소 기반 교체, append-only 공유 스타일, 유한 동작 alphabet, 입력 정규화, writing veto, 보수적 pre-selection gate, 출력 정규화와 sanitizer까지 하나의 AI editing layer로 정리되었습니다. 반복 검수에서도 미리보기 편집, UI 동작 routing, 구조·스타일 patch 적용, 모르는 동작 제거, 재시도, document state 반영이 모두 안정적으로 통과했습니다. 옵클 에디터는 이제 사용자가 보는 EPUB 미리보기와 실제로 저장되는 EPUB 코드를 같은 편집 흐름 안에서 맞춰낼 수 있게 되었습니다.
이전글
목록으로
다음글
저작권 고시
Copyright Notice
본 웹사이트의 모든 디자인 결과물 및 영상에 대한 저작권은 Abstract Cloud에 있으며, 저작권법 및 관련 법령에 의해 보호받습니다. 웹, 영상, 본문, 표지, 내지 디자인을 포함한 모든 콘텐츠는 저작권자의 자산으로, 사전 동의 없이 무단 복제, 배포, 2차 저작물 제작, 온라인 공유 등을 금지합니다. 이를 위반할 시, 저작권법에 따라 민형사상 책임을 질 수 있습니다. 정당한 구매와 저작권 보호는 창작자의 권리를 지키며, 더 나은 작품으로 보답할 힘이 됩니다.
저작권자: Abstract Cloud | 대표자: 배창규(uragen)
© Abstract Cloud. All Rights Reserved.
HOME
FAQ
이용 약관
개인정보 이용방침
help@opkle.app
010-2747-3403
상호 :
추상적 형상 디자인(Abstract cloud) |
대표자 :
배창규
사업자등록번호 :
249-74-00533
통신판매업 신고번호 :
2025-의정부송산-0634
주소 :
경기도 의정부시 부용로 49, 108동 402호
웹의 모든 콘텐츠, 디자인, 소스 코드에 대한
저작권은 Opkle에게 있습니다.