TL;DR

(2024년 9월 기준)

  1. 문자열을 날짜로 파싱할 때
    • Safari 브라우저에선 다음과 같은 문자열을 Date 객체로 파싱 하지 못한다.
        new Date("2015-05-19 11:02:00 +0900"); // -> Invalid Date
    • Safari 브라우저의 날짜 파싱 알고리즘은 수용할 수 있는 날짜 표기법이 다른 브라우저에 비해 부족하다.
    • 날짜를 표시하는 문자열을 사용할 때 ISO 8601 표준을 따르도록 하자
      new Date("2015-05-19T11:02:00+09:00"); // -> 👍
    • 다만 이 형식에선 다음과 같이 하이픈(-)을 슬래시(/)로 변환하면 사파리 브라우저에서도 사용할 수 있게 된다.
      new Date("2015/05/19 11:02:00 +0900"); // -> 👍
  2. 음수 스크롤
    • Safari 브라우저에선 기본으로 웹 페이지에 탄성 있는 스크롤 효과를 준다.
      (Elastic Overflow Scrolling 혹은 Rubber band 혹은 Bounce 효과)
    • 이때 스크롤 정도를 나타내는 값(ex. window.scrollY)이 음수가 될 수도 있다.
    • 이런 동작은 window.scrollY 같은 값을 기반으로 배열 인덱스를 계산하는 경우 등 특정 상황에서 오류를 야기하기도 한다.
  • 이처럼 브라우저의 종류에 따라 예상하지 못한 부분에서 오류를 겪을 수 있다.
  • 웹 페이지를 개발할 땐 최대한 다양한 웹 브라우저에서 테스트를 진행할 수 있도록 하자.

이하 그리 중요하진 않은 내용들

사파리는 나의 원수, 사파리를 죽이...진 맙시다.
오늘 글에 나오는 비난과 심술은 모두 농담입니다 사파리 그렇게까지 싫어하는건 아니에요.

내 개발 PC를 맥북으로 바꾼 지도 이제 몇 개월이나 지났다. 사실 아직도 기존에 쓰던 씽크패드 + Windows + WSL 조합에 비해 좀 불편하긴 하다. 개발자 입장에서의 사용성 측면은 그럭저럭 금방 적응할 만하지만, 아무래도 PC라는 물건이 근 20년간 나에게 Windows OS 기반으로만 동작해 왔었는데 이걸 이제 와서 바꾸려니 영. 아니 그리고 진짜 객관적으로 불편한 거 많아. 한영 전환의 미묘한 딜레이 때문에 몇 번이고 전환 키를 다시 누르질 않나, Notion이나 VSCode 같은 도구를 쓸 때도 받침이 하나씩 씹히는 바람에 번거롭게 영어로 한 번 바꿔줬다가 다시 한글로 써야지 그제야 증상이 사라지고, 간헐적으로 웹 브라우저에 입력한 검색어가 깨진 상태로 요청 파라미터에 들어가기도 한다. 이렇게 불만이 많은 상태지만 아쉽게도 오늘 할 이야기의 주인공은 macOS가 아니다. 오늘의 주인공은 애플 진영의 대표 웹 브라우저 사파리(Safari).

Safari is the new Internet Explorer

IT 분야에서 플랫폼에 따라 파벌이 나뉘고 갈등이 생기는 일은 허다하다. 사람들도 거참 애플 쓴다고 뭐라 하고, 갤럭시 쓴다고 뭐라 하고, 맥북 쓴다고 뭐라 하고, 맥북 안 쓴다고 뭐라 하고... 그리고 그들 말고도 알게 모르게 사용자들 간의 신경전이 살벌한 곳이 또 있으니 바로 웹 브라우저의 세계다. 무려 "전쟁"이라고 표현할 수준의 시장 경쟁을 두 차례나 벌여온 역사가 있을 정도.


역시 나만 이렇게 생각한 게 아니었어

그중 두 번째 전쟁에서 타도의 대상이 되었던 브라우저가 마이크로소프트의 그 유명한 인터넷 익스플로러(Internet Explorer)였다. 한때 전국민적 공분을 샀던 이 브라우저는 결국 2024년 오늘날엔 역사 속으로 사라진 신세가 되었지만, 애플의 사파리 브라우저의 뒤에서 그 이름이 다시금 소환되고 있는 이유는 무엇일까. "Safari is the new Internet Explorer"라는 문장을 구글에 검색해 본 결과 나는 이 브라우저에 대한 다양한 성토들, 그리고 손수 도메인까지 구매해서 사파리를 규탄하고 있는 웹 사이트 까지 발견할 수 있었다.

개발자의 입장

다만 상황이 이렇긴 해도 사파리에 대한 비판은 아직까진 개발자 커뮤니티 내부 혹은 웹 브라우저에 조금 더 진심인 사용자층의 목소리 정도로 느껴진다. 인터넷 익스플로러는 불편함에 대한 증언이 인터넷 유머 게시판이나 뉴스 기사에서도 언급되는 등 일반 사용자에게까지 그 악명이 뻗쳐 버렸기 때문에 결국 명을 다했다. 솔직히 사파리 브라우저가 ActiveX 설치하라 하고 보안용 .exe 프로그램 설치하라 하는 수준까진 아니잖아. 사파리가 비판받는 가장 큰 이유를 보자.


출처

이 그래프는 크롬, 파이어폭스, 그리고 사파리 현재 가장 대표적인 세 웹 브라우저의 웹 표준 테스트 실패 수를 나타낸다. 사파리 혼자 그 수가 눈에 띄게 차이 나는 것을 볼 수 있다. 사파리의 문제는 사용하기 불편하다는 것이 아니다. 사파리는 구현이 덜 되었고, 구현이 다른 브라우저와 다르게 되어있다.


출처

그런 와중에 사파리는 전체 브라우저 중 점유율 2위라는 위치를 차지하고 있다. 이게 바로 개발자들이 사파리에 대한 불만을 쏟아내고 있는 이유일 것이다. 혼자만 동작이 다른데 차라리 얘가 시장을 지배하고 있는 것도 아니고, 그렇다고 무시하기엔 너무 파이가 크다. "사이트가 이상해요"라는 고객 문의에다 "사파리 쓰지 마세요"라고 답변할 수 없는 웹 개발자들은 오늘도 사파리의 이상한 동작을 보완하기 위한 예외 처리 코드를 작성하고 있다. CSS --webkit- 접두어 같은 거 말야.

게다가 애플은 10년도 더 전에 이미 사파리의 윈도우 OS 지원을 끊어버렸다. 나는 윈도우 PC로 개발하는 동안 내 사이트가 사파리에서 오류를 뱉고 있는지 전혀 알아차리지 못하고 있었다. 맥북을 새로 사고 나서야 비로소 알게 되었지, 포트폴리오 사이트가 사파리에서 오류를 내뱉고 완전히 먹통이 되어 있었단 사실을. 앞서 말했듯 맥북의 사용성에 아직도 불만을 품고 있음에도 불구하고 사파리가 있는 한은 다음 개발 PC 또한 눈물을 머금고 맥북을 선택해야 할 것 같다.


(24년 9월 기준) Velog 조차 일부 게시글이 사파리 브라우저에서 오류를 내뱉고 있다.

아무튼 요점은 묵묵히 맡은바 개발을 다 해야 할 개발자로서 불평하지 말고 최대한 다양한 웹 브라우저를 지원할 수 있도록 하자는 것. 이 글을 쓰면서 많은 부분을 참고하고 있는 글에서도 웹 개발에서 웹 표준 측면의 기준을 2년 전 버전 사파리 환경에 맞출 것을 제안하고 있다. 비록 애플이 iOS용 브라우저는 무조건 Webkit 엔진을 사용하도록 강제하다 EU에게 혼쭐이 난 덕에 올해 초가 되어서야 마침내 Webkit 엔진을 사용하지 않은 브라우저를 허용한 적이 있을 정도로 그들이 보여주는 행보가 우려스러운 면이 있긴 하지만, 절대 사파리 브라우저를 규탄하는 글이 아니라는 것을 다시금 언급하며 다음으로 넘어가자.

사파리에서 겪은 두 가지 오류

오늘 글에선 단 두 가지 사례를 소개하겠지만, 앞서 말했듯 사파리 브라우저는 타 브라우저에 비해 훨씬 많은 수의 표준을 지키지 못하고 있기 때문에 앞으로 더 많은 수의 오류를 만나볼 수 있을 것이다. 찾아보면 이런 글도 있고, 이런 글도 있고. 아주 다양하다. 그리고 이런 주제의 글들은 대부분 사파리에 대한 불평이 포함되어 있다. 내가 최근 프로젝트에서 경험한 두 가지 오류는 다음과 같다.

1. 문자열을 날짜로 파싱할 때

JavaScript에서 시간을 다룰 땐 Date 객체를 사용한다. 이렇게 쉽게 한 줄로 요약 가능한 기능이지만 조금 풀어서 설명할 수도 있는데, 프로그래밍 언어에서 Date 객체는 "숫자로 시간을 인식하는 컴퓨터"와 "텍스트로 시간을 인식하는 인간" 사이의 중간 다리 역할을 한다. 2015년 5월 19일이라고 표현할 수 있는 이 날짜는 프로그램상에서 1431993600000이라는 숫자로 저장된다. 이때 한쪽을 다른 한쪽으로 변환해 주는 역할을 하는 객체가 Date인 것이다.


국가별 날짜 표기법

문제는 전 우주 컴퓨터의 공용어인 숫자와 달리 텍스트로 날짜를 표시하는 방법은 국가별로 너무 다양하다는 것. 다양한 언어와 기호들로 년/월/일 뿐만이 아니라 시/분/초, 요일, 심지어 밀리초까지... 이 모든 가능성을 포함하는 문자열을 숫자로 변환하는 알고리즘을 만들기는 쉽지 않다. 이런 문제를 역시 가만히 둘 리 없는 국제표준화기구(ISO)에선 날짜 데이터에 대한 표준 규격 ISO 8601을 아래와 같이 제시했다.

날짜 : 2015-05-19  
시간대가 표시된 날짜 및 시간 : 2015-05-19T17:13:40+00:00 || 2015-05-19T17:13:40Z || 20150519T171340Z

하지만 안타깝게도 세상 엔지니어 모두가 얌전히 이런 형태를 따라주진 않는다. 수년 전 여러 업체로부터 데이터를 받는 DB를 어깨너머로 볼 수 있는 기회를 가진 적이 있었는데, 그 당시에도 특정 업체만 유독 날짜 형식을 다르게 줘서 서버 측에서 예외 처리를 해야 했던 것이 어렴풋이 기억난다. 그래도 프론트엔드 개발자 입장에서 다행인 점은 있다. JavaScript 엔진 대부분은 Date 객체에 전달된 날짜 문자열이 꼭 엄격하게 위의 ISO 8601 규격을 지키지 않더라도 날짜로서 잘 인식될 수 있도록 알고리즘을 구현해 뒀다. 그래서 크롬이나 파이어폭스 브라우저에선 다음과 같은 형식의 문자열도 Date 객체를 생성할 수 있다.

new Date("2015-05-19 11:02:00 +0900");

나 또한 현재 이 블로그에서 이 형식을 기반으로 게시글에 작성일을 기록하고 있다. 이전 Jekyll 버전 블로그에서 기본으로 이 날짜 표기법이 적용되어 있었던 탓. ISO 8601에 비해 타이핑하기도 쉽고, 읽기도 쉬운 데다 오랫동안 잘 사용해 온 형식이라 굳이 바꿀 생각을 가지고 있진 않았다. 특히, Jekyll 같은 정적 블로그 생성기(Static Site Generator)는 어차피 서버 측에서 HTML을 빌드하는 방식이기 때문에 브라우저의 종류를 가릴 일도 없으니까 말이다. 그런데 이번에 블로그를 Next.js 환경으로 전환하면서, 그리고 날짜를 나타내는 컴포넌트에 클라이언트 사이드 렌더링을 사용하면서 이러한 오류를 발견하게 되었다.

지금 사용 중인 브라우저가 크롬이나 파이어폭스라면 위 화면엔 정상적으로 변환된 날짜가 나타날 것이다. 그런데 사파리 브라우저를 사용 중이라면 다음과 같은 결과를 얻을 것이다.

우와 사파리는 대체 얼마나 엄격하게 ISO 8601 표준을 잘 따르는 거야? 그런데 사파리는 정작 아래와 같은 방식의 표기법은 또 문제없이 잘 받아들인다.

new Date("2015/05/19 11:02:00 +0900"); // Safari, Chrome, Firefox 모두 동작

그래도 다행인 점은 이렇게 쓴다고 다른 브라우저에서 역으로 날짜를 인식하지 못하는 일은 일어나지 않는다는 것이다. 덕분에 주어진 문자열의 특정 기호만 변환하는 로직을 구현해 문제를 간단하게 해결할 수도 있다. 아무래도 다른 문자열을 ISO 표준에 맞추기엔 구조가 조금 복잡하긴 하거든. 아무튼 결국 사파리 혼자만 파싱 알고리즘이 약한 것이 문제 아니냐는 생각은 들지만 이런 불평은 지금 상황에 도움 되는 것은 아니기 때문에, 앞으로 JavaScript에서 시간을 다룰 땐 다음을 고려하자.

  1. 시간 표기 형식을 ISO 8601에 맞추자. (대부분의 JavaScript 엔진은 이 형식을 파싱할 수 있는 능력을 갖췄을 것이다.)
  2. 상황에 따라 하이픈(-)을 슬래시(/)로 변환해 간단하게 처리할 수도 있다.
    new Date(dateString.replace(/-/g, "/"))

2. 음수 스크롤

window.scrollY

웹 JavaScript 실행 환경에서 전역 변수로 제공해 주는 window 객체엔 현재 JavaScript가 실행되고 있는 브라우저 창에 대한 정보가 담겨있다. window 객체를 통해 화면의 크기, 현재 페이지가 그리고 있는 HTML 문서, 쿠키나 로컬 스토리지에 대한 참조, 그리고 현재 화면의 스크롤 위치 따위를 얻을 수 있다. 특히 window.scrollY 값의 경우 현재 페이지가 수직으로 얼마나 스크롤 됐는지를 나타내는 값으로, 페이지가 최상단에 위치한 상태라면 0을 반환한다. 그렇기 때문에 이 값을 사용할 때 그 범위를 당연히 "0 이상의 수"라고 생각했다면, 당신은 함정에 빠진 것.

크롬과 파이어폭스와는 달리 사파리는 (overscroll-behavior 속성이 none로 설정되어 "bounce" 효과가 비활성화되지 않았을 때) 최대 스크롤 위치를 넘어 스크롤이 더 되었을 때 scrollY 값을 변경합니다. 예를 들어, 문서가 이미. 최상단에 있을 때 추가로 스크롤이 입력된다면 scrollY 값은 음수가 될 수도 있습니다.
mdn - Window: scrollY property

사파리에선 스크롤 값이 음수가 될 수 있다. 위처럼 mdn web docs에서도 특별히 한 문단을 할애해 사파리의 특별한 동작에 대해 설명하고 있다. 사파리는 기본적으로 웹 페이지에 Elastic Overflow Scrolling 혹은 Rubber band 혹은 Bounce 라고 불리는 효과를 적용한다. 간단히 말하면 움직이는 물체에 탄성을 준다는 것이다. 아래 애니메이션처럼.


출처

이렇게 부여된 탄성은 움직이는 물체가 정지선에 닿는 순간 그 지점을 약간 넘어섰다가 다시 되돌아가는 것처럼 보이게 만든다. 역시 미적 감각이 뛰어난 애플의 대표 브라우저답게 이런 부분에 신경을 쓰는군.

사파리 브라우저를 사용해 이 샌드박스를 스크롤해보자. 특히 마우스 휠보다는 트랙패드나 키보드의 화살표 버튼으로 스크롤을 했을 때 그 효과를 더 잘 볼 수 있다. 사파리 브라우저가 없다면, 아래 캡처를 참고하자.

아무튼 음수가 되면 음수가 되는 건데, 여기서 어떤 문제가 발생할 수 있는가. 나는 개인 포트폴리오 사이트를 SPA로 만들며, 스크롤 값을 기반으로 화면을 다섯 영역으로 나눴었다. 그리고 화면 최상단에 해당하는 첫 번째 영역에서 스크롤을 한 번 더 올리는 순간 존재하지 않는 음수 섹션을 가리키며 고장 나버리는 웹 사이트를 보며 이 오류에 대해 알게 되었다. window.scrollY 값 하나만 특정해서 말해왔는데 사실 scrollTop 같이 스크롤 정도를 나타내는 또 다른 속성들도 존재하고, 이 값도 역시 사파리에선 음수가 될 수 있다. 정리하자면 이렇다.

  • 사파리에선 탄성 애니메이션으로 인해 스크롤 정도를 나타내는 값이 음수가 될 수도 있다. 스크롤 값을 사용할 때 이 경우를 고려하자.

웹 프론트 개발자의 자성自省

가끔 웹 환경을 마치 JVM과 비슷한 시각으로 바라보고 있다는 생각이 든다. 웹 페이지를 브라우저라는 가상 머신 위에서 돌아가는 어플리케이션이라고 보고 있다는 느낌이다. 복잡한 네이티브 친화 코드를 알 필요도 없이 JavaScript로 Web API만 열심히 사용하면 내 컴퓨터의 웹 브라우저에서 화려하게 잘 돌아가는 아주 편한 개발. 하지만 이 블로그에서 전에도 몇 번 말해왔듯 웹은 너무 성공했다. 내가 만든 웹 페이지엔 정말 다양한 사람들이 접속할 것이다. 당장 이 글을 마무리하고 있는 오늘 추석, 오랜만에 만난 누나의 아이폰엔 iOS 버전 사파리가 실행 중이었지. 나야 사파리 덕분에 조금 귀찮아져선 사파리에 이렇게 몽니를 부리고 있지만, 누나 같은 일반 사용자 입장에선 그게 무슨 유난이냐 싶지 않겠어. 개발자는 묵묵히 자기 할 일을 하자. 웹은 분명 범용적인 환경이다. 그리고 그 범용성을 진정 가능케 하는 이는 웹 브라우저가 아니라 프로그램 그 뒤의 엔지니어일 것이니.

그런 의미에서 이 글을 쓰면서 발견한 참고 자료가 아주 인상 깊었다. 개인적으로 요즘엔 부쩍 GeekNews도 대충 헤드라인만 보고 넘기는 일이 잦아졌는데, 이렇게 지금 이 시장의 흐름을 파악하고 대응할 역량을 키우는 것이 중요하겠지. 아무튼 아래는 그 참고 자료에서 말하는 결론. 2022년엔 이랬다.

TL;DR: 2022년 웹 개발에서 고려해야 할 환경은, 성능 측면에서는 저사양의 Android 기기, 웹 표준 측면에서는 2년 전의 Safari, 네트워크 측면에서는 4G입니다. 웹은 대체적으로 이와 같은 니즈에 적절히 대응하지 못하고 있습니다. 특히 성능 관점에서 JavaScript에 과도하게 의존하는 것과 같은 요소들이 웹 사이트의 성능을 끌어내리고 있습니다.

내가 배운 것

  • 생각보다 많은 개발자들이 사파리 브라우저에 불만을 표하고 있더라.
  • 실제로 표준 구현 수가 저 정도로 차이 나고 있을 줄은 몰랐다.
  • ISO 8601
  • 전역 변수 window에 대한 정의를 다시 보게 되었다.
  • 사파리라고 마냥 탄성 효과를 주는 건 아니고, CSS overscroll-behavior로 제어할 수도 있다는 것 같다.
    • 근데 이거 다른 용도에 더 쓰이는 거 아닌가 (스크롤 가능한 컴포넌트가 중첩됐을 시 스크롤의 제어에 대해)

그리고 참고 자료들

주제에선 조금 벗어나지만 우연히 JavaScript의 날짜 관련 객체가 변경될 수 있다는 글을 찾았다. 이것이 사파리의 날짜 문자열 파싱 문제를 처리할 수 있을까 아니면 사파리의 뒤쳐진 표준화 문제에 더 짐이 될 뿐일까. 어차피 결과는 먼 미래의 일이겠지만.