grep

Engineering

웹 워드프로세서 기초 만들어 보기(1)

NHN

2018년 1월 4일

원문에서 보기 ↗

웹 워드프로세서 기초 만들어 보기(1)

웹 워드프로세서(이하 웹 워드)는 생각보다 오래된 역사를 가지고 있다. 구글 독스가 2006년에 소개되면서 많이 알려졌으니 벌써 십여 년의 역사가 있는 소프트웨어 분야이다. 씽크프리는 자바 애플릿 기반으로 동작해서 브라우저에서 구동되는 웹 워드를 표방했지만, 자바스크립트와 같은 웹 언어로 작성되지 않았기 때문에 웹 워드라고 보기는 어렵지 않을까 한다. 네이티브 워드프로세서는 각 OS를 기반으로 독자적인 그래픽 툴킷과 C++이나 자바와 같은 프로그래밍 언어로 구현되어 있지만, 웹 워드는 브라우저에서 구동되기 때문에 지원되는 브라우저만 있으면 어느 컴퓨터에서나 문서 편집을 할 수 있는 장점이 있다.

이 글은 웹 워드를 만드는 기본 원리를 소개한다.

웹 워드의 필요성과 종류

우선 웹 워드가 필요한 이유와 웹 워드로 불릴 수 있는 기준, 그리고 그 기준에 부합되는 웹 워드들의 종류들을 알아보자.

웹 워드의 필요성

웹의 큰 장점은 브라우저만 있으면 OS를 가리지 않고 원하는 콘텐츠를 표현할 수 있다는 것이다. 하지만 문서 편집에 관해서는 대부분 특정 OS나 소프트웨어, 특정 문서 포맷을 써야 한다는 제약 사항이 있었고, 특히 국내에서는 윈도우 기반의 MS 오피스, 한컴 오피스와 문서 포맷으로는 DOC, HWP 등이 대표적으로 사용되고 있다. 사용자 입장에서 이런 환경의 불편한 점은 지원하는 OS가 없거나 이들 소프트웨어가 설치된 컴퓨터가 없으면 문서 편집이 아예 불가능하거나 불필요하게 비용을 지불하는 경우가 생긴다는 것이다.

웹 기술의 발달로 웹으로 할 수 있는 것이 많아지고 있는데 웹 워드는 브라우저가 지원된다면 어떤 OS에서도, 특정 오피스 소프트웨어가 설치되어 있지 않은 컴퓨터에서도 문서 작성을 할 수 있다. 문서는 HTML, 자바스크립트, SVG, Canvas 등의 웹 언어로 표현되므로 사용자 입장에서는 실제로 문서의 포맷이 무엇인지 관여할 필요가 없다.

웹 워드의 분류 기준

네이티브 워드프로세서 기준으로 비추어 보았을 때, 나의 기준은 웹 워드 또한 문서의 쪽 표현 기능과 편집 기능이 있어야 한다는 것이다. 이 기준은 지극히 개인적인 기준일 수 있지만, 쪽 표현 기능이 없다면 네이티브 워드프로세서로 문서 작성을 하던 사용자들이 웹 워드로 넘어왔을 때 가장 이질적으로 생각하는 부분일 수 있기 때문에 세운 것이다. 우리가 교육을 받으면서 보았던 대부분의 문서들(시험지 포함)과 관공서, 기업에서 사용하는 문서들은 그것이 디지털 문서임에도 불구하고 책에서 비롯되었기 때문인지 쪽 표현 들어간 문서들이 매우 많으며 이는 쪽 표현 및 편집 기능을 제공하는 워드프로세서로 작성되었다.

쪽 표현 및 편집을 지원하는 웹 워드들

웹 워드를 개발하면서 벤치마킹했던 구글 독스, 씽크프리를 인수한 한컴 넷피스를 비롯하여 쪽 표현 및 편집을 지원하는 웹 워드들인데 생각보다 많은 기능을 제공하고 있고, 생각보다 아직 불편하기도 하다.

웹으로 워드를 구현하기 위해서는 여러 가지 구현 이슈들이 있겠지만 문서 포맷간 변환을 제외하고라도 아래와 같은 문서 표현 상 구현하기 다소 까다로운 것들이 있다.

구현에 고려할 사항들

그렇기 때문에 생각보다 많은 기능을 지원하지만, 생각보다 아직 불편하다. 네이티브 오피스와의 표현 결과물 차이는 문서의 내용보다 문서의 표현을 더욱 중요하게 생각하는 사용자나 조직 문화에서는 더욱 크게 부각된다. ODF(Open Document Format)의 ODT(Open Document Text-ODF의 문서포맷)는 오히려 문서의 내용에 충실한 스펙으로 보이며 이를 구현한 소프트웨어에서 문서 표현은 조금씩 다르기도 하다. 그 문서 표현의 중심에는 문서의 쪽 표현 기능과 편집 기능이 있다.

웹 워드를 만들기 위해서도 쪽을 표현하고 편집하는 기능이 필수적이라고 보고 기본적인 구현 원리에 대해서 알아본다.

웹 워드의 구현

앞서 말했듯이 웹 워드의 핵심 기준과 첫 걸음은 쪽 표현과 편집 기능이라고 생각한다. 왜냐하면 콘텐츠의 입력, 삭제, 삽입, 붙여넣기, 이미지, 표 기능 등의 구현을 하고 난 후에, 쪽 관련 기능을 구현하였을 때 많은 부분들을 쪽 표현에 맞춰서 새로 구현해야 했기 때문이다.

그리고 또 중요하게 선택해야 할 것은 글자 입력 및 표현 방법을 선택하는 것이다.

contentEditable

글자의 입력과 문서의 표현을 Canvas로 표현한 에디터도 있고 WebODF의 ODT Editor처럼 SVG를 사용할 수도 있다. HTML을 사용하지만 커서의 입력 기능과 커서 렌더링을 따로 구현하는 Toast UI Editor도 있다. contentEditable은 말도 많고 탈도 많은 기능이지만 누가 뭐래도 가장 빠르게 편집 기능을 지원할 수 있는 방법이다.

contentEditable의 장점

장점을 먼저 소개하자면

contentEditable의 단점

역시 단점은 브라우저의 구현에 의존하다 보니 생기는 단점이다.

HTML에서 쪽 표현 방법

여기서는 HTML 표현도구로 설명한다. HTML은 보통 세로 방향으로 렌더링하고 A4용지와 같은 종이의 개념으로 문서를 표현하지는 않는다. page-break-before, page-break-after와 같은 기능은 출력할 때와 쪽 나누기와 같은 기능을 구현할 때는 매우 유용하지만 쪽을 표현하는데 쓰기는 어렵다.

그러면 쪽 표현 기능의 간단한 요구사항을 정의해보자.

이 요구사항을 기반으로 HTML 문서를 쪽으로 나누는 단계(쪽 레이아웃)를 알아보자.

쪽 사이에 걸쳐진 문단을 나누는 단계

쪽 레이아웃은 아래와 같은 단계로 접근할 수 있다. 먼저 내용이 표현될 공간을 위해 쪽 높이와 쪽 여백을 정의한다.(A4용지의 경우 210mm x 297mm이며 여백을 제외한 크기가 실제 문서가 표현될 크기이다. DOM에서 쪽 Container가 태그가 있어야 한다고 전제하고 설명한다.) 그런 다음,

  1. n번째 쪽 Container 태그 내부에 전체 html을 넣는다.(n은 1부터 시작한다.)
  2. n번째 쪽 내부의 block 타입의 태그를 위에서부터 순차적으로 탐색하면서 (쪽의 bottom < block의 bottom)인 첫 태그를 찾는다. 없으면 종료한다.
  3. 찾은 block 태그는 쪽에 걸쳐서 표현되어야 하므로 block을 줄 단위로 나눈다
  4. 나누어진 줄을 위에서부터 순차적으로 탐색하면서 (쪽의 bottom < 줄의 bottom)인 첫 줄을 찾는다.
  5. 찾은 줄을 기준으로 block을 분리하여 block-1, block-2로 만든다.
  6. n + 1 쪽 Container 태그를 만든다.
  7. block-2부터 이후 모든 태그를 n + 1번째 쪽 태그로 이동한다.
  8. 2의 단계를 반복한다.

문단을 줄로 나누는 단계

  1. 문단(block태그)의 글자 하나하나씩 span태그로 감싼다. (Text노드만으로는 글자의 좌표를 파악할 수 없으므로 태그로 감싸 주어야 한다.)
  2. 태그로 감싼 글자들을 순차적으로 돌면서 (n번째 글자의 bottom < n+1번째 글자의 top)인 글자를 찾는다.
  3. n+1번째 글자를 개행된 줄로 처리한다.
  4. 2번을 반복하여 문단을 모든 줄로 나눈다.
  5. 각 줄에서 가장 큰 bottom을 찾는다.
  6. [쪽 사이에 걸쳐진 문단을 나누는 단계]의 4번 단계에서 각 줄의 bottom을 사용한다.
  7. 삽입한 span 태그를 모두 제거하여 원래 문단(block태그)로 되돌린다.

쪽 레이아웃을 수행하면 아래와 같이 문서를 쪽 형태로 표현한 웹 워드 형태로 화면이 구성된다.

(예시는 폴라리스 웹 에디터를 사용한 쪽 레이아웃 데모 화면으로 마크다운으로 작성된 이 글이 7쪽에 걸쳐서 레이아웃 되었다.)

1.png

다시 쪽 레이아웃 수행이 필요한 이벤트

위에서 설명한 쪽 레이아웃을 수행해야 하는 여러 가지 편집 동작들이 있다. 예를 들면,

이 이벤트들이 발생되면 쪽 레이아웃을 수행 해야한다.

성능

웹 워드에서 쪽 표현 및 편집 기능을 탑재하고 있는 제품이 생각보다 많지 않은데, 그 이유 중 하나가 바로 성능이다. 위와 같은 원리로 쪽 표현을 하게 될 경우 브라우저가 연산해야 할 DOM 조작, 레이아웃이 매우 많아진다. 특히 문단을 줄로 나누는 과정에서 글자 하나하나마다 span태그로 감싸주었는데 DOM조작의 양이 매우 많고 레이아웃한 결과까지 받아서 글자의 위치 값을 처리해야 하므로 비용이 매우 큰 작업이다. 이 과정을 타이핑할 때마다 수행해야 한다고 생각해보면 만만찮은 과정이라는 것을 직감할 것이다.

많은 웹 워드프로세스를 테스트해 본 것은 아니지만, 보통의 경우에 이런 식으로 구현한 경우 한 글자 한 글자 타이핑이 느리다는 느낌을 많이 받을 것이다. 입력한 내용이 많으면 많을수록 더 느리다는 반응이 나올 것이다. 구글 독스의 경우도 단순히 글자만 있는 내용을 가지고 테스트 해봤을 때 10쪽이 넘어가면 쓰기가 조금씩 어려워진다는 느낌을 받았다.

이 계산을 줄이기 위한 방법 중에 두 가지 팁을 소개한다.

  1. 변경이 일어난 쪽과 문단(block태그) 이후의 태그들을 대상으로만 다시 계산한다.
  2. 쪽 나누기가 삽입된 경우는 해당 쪽 이전 쪽까지만 계산한다.

성능을 고려하면서 구현할 때 또 주의해야 할 점은 자바스크립트 최적화 기법을 최대한 활용하라는 것이다. 그리고 브라우저 force layout 유발을 최소화해야 한다. 안티패턴을 익혀 성능을 저해하는 것을 코드들을 제거하자. 레이아웃 혹은 리플로우의 개념을 이해하고 불필요한 레이아웃이 일어나지 않도록 해야 한다. 아시다시피 DOM의 특정 프로퍼티들은 읽기만 하여도 강제 레이아웃이 일어난다.

다음 글에서는

다음 글에서는 실제 코드를 통해 간단한 쪽 표현과 편집 기능을 구현하는 방법에 대해서 소개한다. contentEditable을 사용하여 쪽의 편집 기능 처리와 문서의 내용 변경(글자 삽입, 삭제 등)의 이벤트 후에 다시 쪽 레이아웃을 하는 과정까지 소개할 할 것이다.

웹 워드의 흐름

WebGL, WebAssembly, HTML5의 File, DB 등의 많은 API들의 등장을 보면, 웹을 하나의 플랫폼으로 보고 전통적으로 하던 업무와 기능, 서비스들을 웹에서 처리하려고 하고 있고 이는 계속 발전 중이다. 네이티브 워드프로세서에서 웹 워드로 옮겨 가는 것도 그 일환이라고 볼 수 있다. 크롬북의 경우 아예 네이티브는 없고 구글독스를 통한 웹 워드만 제공하는 환경도 있다.

국내에서도 이러한 흐름은 지속적으로 나타나고 있다. 그리고 관공서를 중심으로 ActiveX 기반의 워드프로세서를 비표준으로 정의하면서 웹 워드를 도입하려고 애쓰고 있는 중이다. 그러나 문서의 내용보다는 표현에 더욱 비중을 두는 국내 사용자들의 정서를 생각하면 웹 워드는 이제 시작 단계가 아닌가 생각한다. 프론트엔드 개발자가 여기에 기여할 수 있는 것들이 많다.

특히 국내 시장에서 IE11이하의 사용이 거의 없어지고 Edge로 넘어간다면 네이티브에서 웹 워드로의 흐름은 더욱 빨라지지 않을까 생각한다.