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


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

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

웹 워드의 필요성과 종류

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

웹 워드의 필요성

웹의 큰 장점은 브라우저만 있으면 OS를 가리지 않고 원하는 콘텐츠를 표현할 수 있다는 것이다. 하지만 문서를 편집할 때는 대부분 특정 OS나 소프트웨어, 특정 문서 포맷을 써야 한다는 제약 사항이 있었다. 특히 한국에서는 Windows 기반의 마이크로소프트 오피스와 한컴오피스가 대표적으로 사용되고 있다. 문서 포맷으로는 DOC, HWP 등이 대표적이다. 하지만 이러한 문서 형식을 지원하는 OS가 없거나 이들 소프트웨어가 설치된 컴퓨터가 없으면 아예 문서를 편집할 수 없다는 단점이 있다. 문서를 편집하기 위해 Word나 한컴오피스를 구매해야 할 수도 있다.

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

웹 워드의 분류 기준

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

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

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

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

구현에 고려할 사항들

  • 쪽 표현 및 쪽 표현을 지원하는 편집 기능 구현의 복잡성
  • 커서 제어 및 복잡한 DOM 조작
  • 다국어 지원 및 다양한 폰트 제공의 어려움
  • 문서를 네이티브 워드프로세서와 같이 비슷하게 표현하기

    • 줄 간격
    • 문단 간격
    • 웹상에서 줄 및 문단의 세로 맞춤(Top, Middle, Bottom)의 표현 차이점
    • 브라우저마다 다른 폰트 렌더링
    • 각종 CSS(브라우저 렌더링 엔진)와 특정 문서 포맷의 표현 차이

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

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

웹 워드의 구현

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

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

contentEditable

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

contentEditable의 장점

장점은 다음과 같다.

  • 어느 정도 수준의 기능까지는 빠르게 구현이 가능하다.
  • 브라우저의 입력 체계를 활용할 수 있다. 특히 영어를 제외한 조합 입력이 필요한 아시아권 언어를 지원해야 하는 경우 유리하다.
  • 브라우저의 커서 컨트롤 활용, 방향키, Home/End, PageUp/PageDown, Del, 커서 렌더링을 활용할 수 있다.
  • 브라우저의 복사 붙여 넣기 기능을 활용할 수 있다.

contentEditable의 단점

단점은 브라우저의 구현에 의존하기 때문에 생긴다.

  • 브라우저별로 폰트 표현과 CSS 박스 표현이 미세하게 다를 수 있다.(IE11의 경우 폰트 9가 폰트 10보다 크게 보이는 기이한 경우도 발생하고 자간의 표현이 다른 브라우저보다 더 좁게 표현되는 폰트도 있다.)
  • 관련 버그가 있을 경우 해당 브라우저가 패치해줄 때까지 방법이 없는 경우가 많다(IE11 이하의 경우는 지원이 중단된 상태이고, 버그 리포트가 올라오고 심지어 패치가 되었음에도 Edge에만 반영되고 있다.)
  • div block 태그의 경우 contentEditable 속성이 지정되면 Resize Handler 테두리가 생기기 때문에 자연스럽지 않게 보인다. Resize Handler가 보이지 않도록 처리해 주어야 하지만 은근히 까다롭고 딱히 정해진 방법도 찾기가 어렵다.(테스트해 본 브라우저는 IE11, Firefox이며, 특히 IE11에서 contentEditable 속성을 넣고 빼고 하다가 보면 제거했던 Resize Handler가 다시 나타나는 경우가 종종 있다.)

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번째 페이지 Container 태그로 이동한다.
  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쪽에 걸쳐서 레이아웃 되었다.)

image

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

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

  • 글자를 포함한 내용(이미지, 글상자, 표 등)의 입력, 삭제(붙여넣기 포함)
  • 글자를 포함한 내용의 스타일 변경(글자 크기, 폰트, 문단/줄 간격 변경, 글머리 기호, 들여쓰기, 내어쓰기 등)
  • 쪽 모양 편집(용지의 가로/세로, 용지 크기 변경)
  • 쪽 나누기 표시 삽입/삭제

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

성능

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

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

브라우저의 연산을 줄이기 위한 방법 중에 두 가지 팁을 소개한다.

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

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

다음 글에서는

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

웹 워드의 흐름

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

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

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

유동식2017.12.22
Back to list