웹 프로그램을 개발할 때 인터넷 익스플로러(IE)는 참 개발자를 괴롭게 한다. IE만의 예외 상황을 고려해야 하는 번거로운 경우가 얼마나 많은지… 특히 IE 8 이하는 발표된지 몇 년이 지났는데도 우리나라에서는 아직 사용자가 많다! 수년 전까지도 윈도 XP에서 IE 6을 사용하는 사용자가 가장 많았던 우리나라에서 개발자는 이래 저래 힘들다.
여기 그 문제 중 또 하나를 얘기해보려고 한다. 공백(whitespace) 문자의 범위에 대한 문제다.
현재 내가 진행 중인 프로젝트에서는 고객이 가지고 있는 과거의 HTML 자료를 프로그램적으로 처리하는 게 있다. 그 중 자바스크립트로 공백 문자를 제거하는 게 있었다.
text = text.replace(/^[\s\u00a0]+|[\s\u00a0]+$/g, '');
(위에서 \u00a0
은 HTML로는
, 즉 “줄바꿈 안하는 빈칸” 글자다.) 우리는 주로 크롬이나 IE 9 이상을 사용하고 있었기 때문에 잘 되는 줄 알았는데 얼마 후 사용자가 특정 표에서 불필요한 빈 행이 나타난다고 알려왔다. 다른 브라우저에서는 안 그러는데 IE 8 이하에서만 발생하는 문제였다. ‘올 것이 왔구나…’
확인해보니 사용자의 자료들은 아래아 한글이나 엑셀에서 HTML로 변환한 것이었는데 거기에 \u3000
"상형 문자의 빈칸"이 포함돼 있었고 IE 8에서는 그것을 공백 문자로 인식하지 않는 것이었다. 이미 그 전에도 IE 8 이하에서는 "줄바꿈 안하는 빈칸"을 공백 문자로 인식하지 않는다는 건 알고 위와 같이 처리하고 있었는데 이번 건 새로운 문제였다. \u3000
이라는 문자를 사용하는 거도 처음 봤는데 그걸 IE 8은 공백으로 인식하지 않는 문제가 겹친 것이다.
그럼 대체 자바스크립트에서 공백 문자란 무엇인가?
자바스크립트(JavaScript)는 ECMAScript 언어 표준에 따라 정의되고 구현되는데 1999년에 3판이 나왔고 (4판은 건너뛰고) 2009년에 5판이 나왔고 2011년에 5.1판이 최신으로 발표돼 있다. IE 8 이하는 2009년 전에 나왔으므로 1999년의 ECMAScript 언어 명세를 참고했을 텐데 7.2절을 보면 공백 문자를 \u0009
(탭), \u000B
(세로 탭), \u000C
(폼피드), \u0020
(빈칸), \u00A0
(줄바꿈 안하는 빈칸) 및 "Any other Unicode space separator”라고 정의하고 있다. 또 별개로 7.3절에 줄 바꿈 문자로 \u000A
, \u000D
, \u2028
, \u2029
를 정의하고 있는데 일반적으로 공백 문자에는 줄 바꿈 문자도 포함된다.
그래서 1999년에 나온 유니코드 2.0의 속성 목록을 찾아봤더니 다음과 같은 내용이 있었다.
Property dump for: 0x10000002 (Space)
0020
00A0
2000..200B (12 chars)
3000
FEFF
Property dump for: 0x10000004 (White space)
0000
0009..000D (5 chars)
0020
00A0
2000..200F (16 chars)
2028..202E (7 chars)
206A..206F (6 chars)
3000
FEFF
최소한 00A0이라든가 3000 글자는 “Space” 속성이므로 공백 문자에 포함돼야 하는데 IE 8 이하는 그것을 지키지 않은 것이다!
최근의 브라우저는 ECMAScript 언어 명세 표준 중 2009년에 나온 5판을 따를 것이므로 그 중 7.2절을 보면 공백 문자는 유니코드 3.0에 정의한 대로 인식해야 한다고 나와 있다.
ECMAScript implementations must recognize all of the white space characters defined in Unicode 3.0
그래서 유니코드 3.0의 속성 목록을 보면 공백 문자를 다음과 같이 정의한다.
0009..000D (5 chars)
0020
0085
00A0
1680
2000..200B (12 chars)
2028..2029 (2 chars)
202F
3000
위 각각의 문자 값을 유니코드 표에서 찾아보면 다음 의미다.
- U+0009–U+000D (제어 문자 - 탭, CR, LF 등)
- U+0020 빈칸(space)
- U+0085 NEL (제어 문자 - 다음 줄)
- U+00A0 줄바꿈 안하는 빈칸
- U+1680 OGHAM 빈칸 표시
- U+2000–U+200B (여러 종류의 빈칸들)
- U+2028 LS (줄 구분자)
- U+2029 PS (문단 구분자)
- U+202F NNBSP (줄바꿈 안하는 좁은 빈칸)
- U+3000 상형문자 빈칸
현재 유니코드 6.x대 버전에서는 U+180E 몽골 모음 구분자, U+205F MMSP 중간 수학 빈칸이 추가되어 조금 달라졌는데 아무튼 기본적으로 자바스크립트는 위의 글자들을 공백 문자로 처리해야 한다. 그래서 우리는 원래의 공백 제거 코드를 다음과 같이 수정했다.
text = text.replace(/^[\s\u00a0\u3000]+|[\s\u00a0\u3000]+$/g, '');
그 밖에...
자바스크립트에는 문자열의 앞뒤 공백을 제거하는 trim
함수가 정의돼 있는데 이게 IE8 이하에 없어서 우리 프로젝트에서는 직접 공백 제거 처리를 하고 있었다. 사실 jQuery에도 trim
함수가 있기 때문에 걔네들은 어떻게 하고 있는지 찾아봤다. 아래는 많이 사용하는 버전 중 하나인 1.8.3에 있는 trim
함수의 원형이다.
// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
Safari 5.0과 IE 너네 땜에 BOM과 NBSP는 별도로 추가해서 처리한다고 주석에 나와 있다. 여기도 \u3000
은 처리하지 않으므로 우리 프로젝트에서 jQuery로 trim
해도 IE 8은 계속 문제가 될 수 밖에 없었을 것이다. (그런데 \uFEFF
는 왜 공백으로 치는 것이지?)
자바(Java)는 어떨까?
String#trim
메서드의 경우는 주석을 보면 \u0020
, 즉 빈칸 이하의 글자들을 제거한다고 나와 있다. 헛, 그렇다면 유니코드에 공백이라고 정의되지 않은 제어 문자도 제거한다는!!??
Pattern
클래스의 경우는 \s
, 즉 공백 구문을 [ \t\n\x0B\f\r]
로 정의한다고 나와 있다. NBSP도 없고 \u3000
도 없다!
Character#isWhitespace
메서드의 경우는 다음으로 정의한다고 나와 있다.
- 빈칸 구분자, 줄 구분자, 문단 구분자이되 줄 바꿈 안하는 글자는 제외
- '\t', 가로 탭
- '\n', 줄 먹임
- '\u000B', 세로 탭
- '\f', 폼 먹임
- '\r', 캐리지 되돌림
- '\u001C', 파일 구분자
- '\u001D', 그룹 구분자
- '\u001E', 레코드 구분자
- '\u001F', 단위 구분자
자바스크립트는 눈부시게 발전하는데 비해 자바는 아직 멀었나보다…