EDITORCLASSBLOGLOGINimg default descriptionimg default descriptionimg default description표현한다는 것의무한한 가능성,새로운 형태로
담아내다.
img default descriptionimg default descriptionimg default descriptionimg default descriptionimg default descriptionimg default descriptionimg default description새로운 형태의 콘텐츠Opkle은 코드 없이 웹을 마음껏 만들고, 누구나 자기 화면을 그릴 수 있도록 에디터를 만들고, 그 결과물을 어디서나 즐길 수 있게 돕는 팀입니다.텍스트와 화면과의 조화를 통해, 웹을 짓는다는 것이 그저 단순한 코딩이 아닌, 상상을 펼치고 감각을 깨우는 과정이 될 수 있도록 좋은 도구를 만들어 냅니다.
옵클 에디터 개발기: 그래픽 엔진 안의 선형대수dev12옵클 에디터 개발기: 인디자인처럼 편집하는 레이아웃 편집기dev13옵클 에디터 개발기: 원고가 다음 페이지로 흐르는 편집기dev14옵클 에디터 개발기: EPUB과 IDML을 편집 가능한 페이지로dev15옵클 에디터 개발기: 한 번 만든 레이아웃을 EPUB과 인쇄물로dev1689101112...16 옵클 에디터 개발기: 한 번 만든 레이아웃을 EPUB과 인쇄물로레이아웃 에디터가 외부 문서를 편집 가능한 페이지로 받아들이게 된 다음에는 반대 방향을 완성할 차례였습니다. 페이지 안에서 만든 결과가 레이아웃 에디터에 갇혀 있으면 제작 과정의 마지막 도구가 될 수 없습니다. 이미지는 벡터 에디터로, 표는 표 편집기로 다시 보내 수정할 수 있어야 하고, 완성된 지면은 반응형 EPUB과 웹 문서 또는 인쇄용 PDF로 나갈 수 있어야 했습니다.이 출력들은 같은 문서를 사용하지만 요구하는 정보가 서로 다릅니다. 벡터 편집 왕복에서는 오브젝트의 안정적인 정체성과 원본 프로젝트 연결이 중요합니다. EPUB은 고정 좌표보다 reading order와 반응형 관계가 중요하고, 인쇄 PDF는 반대로 정확한 page box와 폰트, 도련과 재단선이 중요합니다. 하나의 범용 export 함수로 모두 처리하면 어느 쪽도 정확해지지 않습니다.그래서 레이아웃 상태를 읽는 단계만 공유하고, 목적에 따라 변환기를 분리했습니다. 편집기 왕복은 object id와 blob 생명주기를 유지하고, fixed-to-reflow 변환은 geometry에서 문서 구조를 추론합니다. PDF 경로는 모든 페이지와 폰트를 확정한 뒤 물리 규격으로 렌더합니다. 입력에서 시작한 변환 고리를 다른 편집기와 EPUB, 웹, 인쇄물까지 완전히 닫았습니다.Editor round trip레이아웃 안의 이미지를 벡터 에디터로 보낼 때 가장 먼저 한 일은 현재 페이지를 저장하는 것이었습니다. 아직 id가 없는 오브젝트에는 안정적인 object id를 부여하고, 그 값이 page 문자열에 들어간 뒤에만 외부 편집기를 열었습니다. 편집을 마치고 돌아왔을 때 index나 선택 순서가 아니라 정확히 같은 오브젝트를 찾기 위한 기준입니다.교환 데이터는 작은 metadata와 큰 binary를 분리했습니다. metadata에는 현재 프로젝트와 layout map, 대상 object id, 새 작업인지 기존 벡터 프로젝트를 다시 여는지, 돌아와야 할 session id를 넣었습니다. 실제 seed image와 최종 결과 blob은 IndexedDB의 임시 교환 row에 두었습니다. 브라우저 storage 문자열에 대용량 base64를 넣지 않고도 iframe 교체와 앱 재진입을 견딜 수 있습니다.부모 에디터와 자식 편집기 사이의 화면 전환은 `postMessage`로 요청하되 데이터 자체는 message에 싣지 않았습니다. 메시지는 어떤 편집기를 열고 어느 session을 읽을지만 알려줍니다. 자식 편집기는 session row에서 seed와 metadata를 읽고 작업한 뒤 결과를 같은 교환소에 flush합니다. 레이아웃 에디터가 다시 열리면 자신의 map id와 session이 맞는 결과만 회수합니다.빠른 더블 클릭이나 중복 우클릭으로 두 작업이 동시에 열리지 않도록 공통 in-flight guard를 두었습니다. 새 벡터 작업은 흰 캔버스와 연결된 placeholder image를 먼저 본문 마진 안에 만들고, 기존 작업은 오브젝트가 기억한 원본 프로젝트를 엽니다. 시작에 실패하거나 사용자가 취소하면 원래 오브젝트와 blob은 그대로 유지하고 session metadata만 정리했습니다.결과를 제자리로벡터 편집 결과가 돌아오면 page DOM을 새로 만드는 대신 기존 오브젝트를 가능한 한 제자리에서 갱신했습니다. 정적 결과는 image blob을 교체하고, 애니메이션이나 영상 결과는 같은 bounds를 가진 media object로 바꿨습니다. x와 y, 표시 폭, z-order, object id는 그대로 두어 레이아웃의 배치와 다시 편집할 연결이 끊기지 않게 했습니다.결과의 원본 pixel width와 height가 달라졌다면 기존 표시 폭을 기준으로 새 종횡비에 맞는 높이를 계산했습니다. 사용자가 페이지에서 잡은 폭은 유지하고 콘텐츠 비율만 새 결과에 맞춥니다. 영상으로 바뀔 때는 재생 자산과 poster를 각각 영구 blob으로 등록하고, 정적 이미지로 돌아오면 더 이상 필요하지 않은 video 참조를 정리했습니다.표 편집기도 같은 왕복 계약을 사용하지만 session collection과 project marker는 분리했습니다. 새 표는 본문 영역 왼쪽 위에 4:3 placeholder를 만들고, 결과 JPEG가 돌아오면 같은 image object의 blob과 높이를 교체합니다. 표 내용이 비어 있거나 편집이 취소되면 placeholder를 지우지 않고 table project id만 유지해 다시 열 수 있게 했습니다.이전 blob은 새 결과를 저장한 뒤에 정리했습니다. 프로젝트의 모든 page와 master, 현재 DOM에서 같은 id를 참조하는 오브젝트가 있는지 확인하고 더 이상 참조되지 않을 때만 row를 삭제했습니다. 새 blob 생성, 오브젝트 repoint, 프로젝트 저장, 고아 자산 삭제 순서를 지켜 중간 실패가 보이는 이미지를 잃는 일로 이어지지 않게 했습니다.Fixed의 reading order고정 페이지를 EPUB 본문으로 돌려보낼 때는 x와 y를 그대로 HTML의 absolute position으로 옮기지 않았습니다. 그렇게 만든 문서는 특정 화면 크기에서만 같은 모양을 보이고, 폭이 바뀌면 읽기 순서와 접근성이 모두 무너집니다. 레이아웃에서 사람이 만든 시각적 관계를 읽어 반응형 문서 구조로 다시 해석해야 했습니다.먼저 표지를 제외한 페이지를 순서대로 읽고 모든 visible object의 page local bounds를 수집했습니다. 기본 정렬은 y가 먼저이고 x가 다음이지만, y가 조금 다르다는 이유만으로 같은 행의 두 오브젝트를 서로 다른 문단으로 볼 수는 없습니다. 세로 범위가 일정 비율 이상 겹치는 오브젝트를 하나의 band로 묶고 band 안에서는 x 순서로 정렬했습니다.같은 band에서 가로 영역이 겹치지 않는 요소는 서로 다른 column 후보가 됩니다. 반대로 한 오브젝트가 band 폭 대부분을 차지하면 독립 block으로 판단합니다. 명시적인 Flex 컨테이너는 geometry를 다시 추측하지 않고 이미 저장된 direction과 wrap, gap, padding, item order를 우선 사용했습니다. 자유 배치 영역에서만 overlap과 간격을 이용해 row와 column 구조를 추론했습니다.겹침이 강한 도형과 이미지, 텍스트는 별개의 흐름으로 풀면 원래 합성 관계가 사라집니다. 이런 경우에는 하나의 visual group으로 묶거나 필요한 범위만 raster asset으로 변환했습니다. 모든 좌표를 억지로 reflow시키는 것이 아니라 읽기 구조로 표현할 수 있는 관계와 고정 합성으로 남겨야 하는 관계를 구분했습니다.Responsive structure텍스트 프레임은 내부 paragraph node를 순서대로 읽어 `

` 구조로 평탄화했습니다. paragraph style의 정렬, line-height, 첫 줄 들여쓰기, 앞뒤 여백을 CSS로 옮기고 character style span과 링크는 inline markup으로 유지했습니다. thread 때문에 여러 프레임으로 나뉜 같은 원고도 page와 frame 순서를 따라 하나의 reading flow로 이어집니다.

빈 문단은 삭제하지 않고 명시적인 간격 block으로 변환했습니다. 프레임 사이의 수직 간격도 앞 오브젝트의 bottom과 다음 오브젝트의 top 차이를 계산해 spacer로 옮겼습니다. 다만 fixed page의 큰 빈 영역을 그대로 rem으로 만들면 작은 화면에서 과도한 공백이 되므로 상한을 두었습니다. 페이지가 바뀌는 지점은 문서 전체의 기본 rhythm으로 정규화했습니다.폭과 왼쪽 간격은 본문 content box에 대한 percentage로 바꾸고, typography와 세로 간격은 기준 font size에 대한 rem으로 변환했습니다. 같은 행의 column 폭은 band 전체에서 차지하는 비율로 계산하고 gap도 상대 단위로 옮겼습니다. mobile에서 column이 너무 좁아지는 경우에는 media query에서 `flex-direction: column`으로 전환할 수 있는 class를 부여했습니다.이미지와 비디오는 EPUB 자산 경로로 보내고 원래 alt와 원본 project marker를 유지했습니다. SVG 도형과 path는 단독 vector로 보존할 수 있는지 먼저 판단하고, filter나 복잡한 합성이 있으면 투명 PNG로 rasterize했습니다. fixed layout의 모든 숫자를 보존하는 대신 텍스트 의미와 시각적 관계, 편집 가능한 자산 연결을 최대한 남기는 변환기로 만들었습니다.Preview와 동일한 결과변환된 HTML과 CSS는 바로 격리된 iframe 미리보기로 열었습니다. 프로젝트에 등록된 폰트와 이미지, 영상 blob을 새 object URL로 연결하고 CSS는 `srcdoc` 안에서만 적용했습니다. 모바일, 큰 모바일, 태블릿 폭을 전환해 column과 media query가 실제로 어떻게 바뀌는지 확인할 수 있게 했습니다.미리보기는 단순한 스크린샷이 아니라 최종 bundle 자체를 렌더합니다. 각 viewport를 전환할 때 HTML을 다시 생성하지 않고 같은 문서에 다른 width를 적용했습니다. 사용자가 특정 페이지만 선택했다면 reading order 계산과 asset 수집도 그 페이지 범위로 제한하고, 표지와 본문의 시작 규칙은 전체 문서와 같은 방식으로 유지했습니다.EPUB 에디터로 보낼 때 미리보기와 별도의 두 번째 변환을 실행하지 않았습니다. 사용자가 확인한 HTML, CSS, font와 asset map을 그대로 부모 에디터에 전달했습니다. 같은 레이아웃을 다시 분석하면 비동기 blob 해석이나 geometry 반올림으로 결과가 조금 달라질 수 있습니다. preview bundle을 canonical output으로 삼아 확인한 결과와 실제 삽입 결과를 일치시켰습니다.부모 에디터에서는 전달된 body와 style을 새 EPUB content block으로 받아 CodeMirror의 HTML·CSS 문자열과 IndexedDB 프로젝트 상태에 연결합니다. 레이아웃 에디터가 완성한 반응형 구조가 중간에 평탄화되거나 이미지로 바뀌지 않고 기존 EPUB 편집 pipeline 안으로 들어갑니다. fixed page와 reflow editor 사이의 왕복이 이 지점에서 완성되었습니다.Print DOMPDF 출력은 EPUB과 반대로 페이지 좌표를 그대로 확정해야 합니다. 추출 전에 lazy 상태의 모든 페이지를 로드하고 master를 적용한 뒤 실제 폰트가 준비될 때까지 기다렸습니다. 각 page objects layer를 clone하고 selection, guide, loading skeleton, isolation outline과 같은 편집 전용 transient 요소를 제거했습니다.Shadow DOM 본문은 그대로 직렬화되지 않으므로 저장된 wrapper와 live shadow 내용을 사용해 clone 안에 light DOM으로 펼쳤습니다. shadow style도 출력 package에 포함하고 내부 image와 background URL은 영구 blob에서 data URL이나 서버 asset 파일로 해소했습니다. 다음 세션에서 죽는 `blob:` 주소가 직렬화 문자열 어디에도 남지 않게 마지막 SVG 문자열 단계에서도 한 번 더 검사했습니다.영상은 정적 출력에 맞게 poster frame으로 치환했습니다. EPUB leaf와 native text는 서버 PDF 경로에서는 `foreignObject`의 HTML을 유지해 Chromium이 실제 폰트와 줄바꿈을 렌더하게 했습니다. 브라우저에서 직접 rasterize하는 경로에서는 호환되지 않는 `foreignObject`를 text outline이나 안전한 이미지로 바꾸고 잔존 요소를 제거했습니다. 같은 페이지라도 renderer의 성격에 맞는 DOM을 만들었습니다.페이지 배경과 master, 본문 오브젝트는 원래 z-order를 유지한 하나의 export SVG로 조립했습니다. 화면용 zoom과 pan transform은 제거하고 page local geometry만 남겼습니다. output page의 viewBox와 물리 width·height는 문서의 point 규격을 사용해 브라우저 화면 크기와 무관한 page box를 확정했습니다.Font subset프로젝트에 등록된 모든 폰트를 PDF에 넣으면 파일 크기가 불필요하게 커집니다. 실제 page DOM과 Shadow 본문을 순회해 사용된 font family, weight와 style을 수집하고, 문서에 등장하는 문자 집합을 만들었습니다. 한글과 영문, 숫자뿐 아니라 줄바꿈과 기본 구두점, 공백 문자까지 포함해 렌더링 중 fallback이 생기지 않게 했습니다.같은 family라도 400과 700은 별도의 face로 처리하고 실제 사용된 조합만 export manifest에 넣었습니다. fontTools 기반 subset 작업에는 해당 face가 사용하는 glyph 집합을 전달했습니다. subset 결과가 원본보다 커지거나 변환이 실패하면 원본 font를 사용하도록 했습니다. 처리 시간에도 예산을 두어 본문에서 많이 사용된 face를 먼저 줄이고 나머지는 안전한 원본 임베드로 넘어갔습니다.페이지별 HTML에는 공통 font CSS와 필요한 scoped style을 연결했습니다. 서버의 Playwright는 stylesheet가 준비되고 `document.fonts.ready`가 끝난 것만으로 완료를 판단하지 않았습니다. 등록한 각 FontFace의 load 상태를 확인하고 두 번의 animation frame을 지난 뒤 page-ready signal을 올렸습니다. 비동기 font가 마지막 순간에 들어와 줄바꿈과 페이지 모양이 바뀌는 것을 막았습니다.렌더러는 모든 페이지의 ready signal을 받은 뒤에만 PDF와 고해상도 이미지를 만들었습니다. 폰트가 하나라도 실패하면 무한히 기다리지 않고 timeout과 fallback을 적용하되 어떤 face가 원본으로 대체되었는지 출력 상태에 남겼습니다. 화면에서 본 typography를 PDF에서도 같은 line break로 재현하는 기준을 명확하게 만들었습니다.Print geometry화면용 viewer PDF와 인쇄용 PDF는 같은 page clone에서 시작하지만 출력 규격은 분리했습니다. viewer 모드는 trim size를 그대로 사용하고 이미지 품질을 읽기에 충분한 수준으로 조정해 파일 크기와 탐색 속도를 맞췄습니다. print 모드는 300 DPI의 페이지 raster와 vector mark를 사용하고 문서에 설정된 bleed를 page box 바깥으로 확장했습니다.기본 인쇄 경로에서는 3mm 도련과 별도의 slug 영역을 확보했습니다. trim box의 네 모서리에는 도련 바깥에서 시작하는 재단선을 그리고, 상하좌우 중앙에는 외곽 원과 내부 원, 관통 십자로 이루어진 registration mark를 배치했습니다. 선 두께와 길이는 화면 pixel이 아니라 millimeter를 point로 환산해 계산했습니다.하단 slug에는 cyan, magenta, yellow, black의 color bar를 실제 물리 크기로 배치했습니다. 중앙 registration mark와 겹치지 않도록 trim의 왼쪽 기준으로 정렬하고, page width와 height가 달라져도 mark 위치를 trim box와 slug 여백에서 역산했습니다. 인쇄 부가 요소도 페이지마다 임의 좌표를 쓰지 않고 같은 기하 모델에서 생성했습니다.완성된 page asset과 font package는 서버 렌더러로 보내고 최종 CMYK 변환 경로를 거쳐 print PDF를 만들었습니다. viewer와 print가 같은 옵션 이름만 다른 경로가 아니라 resolution, bleed, mark, color space와 asset 정책이 분리된 출력물이 되었습니다. 표지와 본문, 서로 다른 판형, 비대칭 도련에서도 trim과 실제 콘텐츠 위치를 반복 측정해 맞췄습니다.Portable project최종 결과뿐 아니라 레이아웃 프로젝트 자체도 다른 컴퓨터로 옮길 수 있게 했습니다. 문서 meta와 page·master 문자열, text style과 layout 분류, 이미지·영상·폰트 blob의 metadata를 하나의 manifest에 넣고 실제 binary는 package 안의 별도 경로에 보관했습니다. export 전에 모든 lazy page를 확정해 manifest가 일부 페이지만 가리키지 않게 했습니다.가져올 때 원래 database id를 그대로 쓰면 현재 장치의 다른 프로젝트와 충돌할 수 있습니다. package 안의 page DOM id와 blob id마다 새로운 id를 발급하고 old-to-new map을 만들었습니다. page와 master 문자열 안의 `data-blob-id`, font 참조, media poster와 project meta의 DOM 참조를 구조적으로 새 id로 치환했습니다.복원 순서는 create, swap, delete로 고정했습니다. 먼저 새 blob과 page 문자열을 모두 생성하고 실제로 읽을 수 있는지 확인합니다. 그다음 layout map이 새 참조를 가리키도록 한 번에 교체하고 프로젝트를 저장합니다. 이 과정이 성공한 뒤에만 교체 대상인 과거 row와 고아 blob을 삭제합니다. 중간 실패에서는 기존 프로젝트가 여전히 옛 참조를 가리키므로 원본이 보존됩니다.package import 후에는 font face와 image URL, Shadow DOM, master와 text thread를 일반 load pipeline으로 다시 복원했습니다. 별도의 축약된 미리보기 프로젝트가 아니라 편집 가능한 전체 상태가 돌아옵니다. 다른 컴퓨터에서도 page id와 자산 id가 새 환경에 맞게 바뀌면서 문서 내부 관계는 그대로 유지됩니다.닫힌 제작 시스템이 단계까지 오면서 레이아웃 에디터의 역할이 완전히 정리되었습니다. 페이지 위의 이미지와 표는 자신의 원본 편집 프로젝트를 기억한 채 다른 편집기를 왕복하고, 결과가 돌아와도 위치와 크기, z-order와 자산 관계를 유지합니다. fixed page는 geometry에서 reading order와 row·column 관계를 복원해 반응형 HTML과 EPUB으로 다시 들어갑니다.같은 문서는 인쇄 경로에서 전혀 다른 방식으로 확정됩니다. 모든 페이지와 master, Shadow 본문과 font를 로드하고 실제 사용 glyph만 package에 넣은 뒤 Playwright의 ready contract를 통과합니다. viewer PDF와 300 DPI 인쇄 PDF를 분리하고, 도련과 재단선, registration mark, CMYK color bar와 color space까지 물리 규격으로 완성합니다.벡터·표 왕복의 session 복구와 중복 실행, 이미지·영상 교체와 고아 blob 정리, 다양한 fixed layout의 reading order, 모바일·태블릿 preview, 폰트 지연 로드, 수백 페이지 PDF, 여러 판형의 인쇄 마크, 프로젝트 package의 id 재매핑을 반복 검수했습니다. 미리보기에서 확인한 결과와 EPUB 삽입물이 일치하고, 인쇄 PDF의 trim과 bleed가 설정값에 맞으며, package 복원 후에도 편집 상태가 그대로 이어지는 것을 모두 확인했습니다. 벡터 제작에서 시작해 레이아웃, EPUB과 웹, 실제 인쇄물로 이어지는 옵클의 제작 시스템은 이 단계에서 완전하게 닫히고 안정화되었습니다.
이전글목록으로다음글
저작권 고시Copyright Notice본 웹사이트의 모든 디자인 결과물 및 영상에 대한 저작권은 Abstract Cloud에 있으며, 저작권법 및 관련 법령에 의해 보호받습니다. 웹, 영상, 본문, 표지, 내지 디자인을 포함한 모든 콘텐츠는 저작권자의 자산으로, 사전 동의 없이 무단 복제, 배포, 2차 저작물 제작, 온라인 공유 등을 금지합니다. 이를 위반할 시, 저작권법에 따라 민형사상 책임을 질 수 있습니다. 정당한 구매와 저작권 보호는 창작자의 권리를 지키며, 더 나은 작품으로 보답할 힘이 됩니다.저작권자: Abstract Cloud | 대표자: 배창규(uragen)
© Abstract Cloud. All Rights Reserved.
HOMEFAQ이용 약관개인정보 이용방침help@opkle.app
010-2747-3403
상호 :추상적 형상 디자인(Abstract cloud)  |  대표자 :배창규사업자등록번호 :249-74-00533통신판매업 신고번호 :2025-의정부송산-0634주소 :경기도 의정부시 부용로 49, 108동 402호웹의 모든 콘텐츠, 디자인, 소스 코드에 대한
저작권은 Opkle에게 있습니다.
img default description