최근 개인 프로젝트 중 섹션 단위 이동을 구현하다 smooth 스크롤링이 적용되지 않는 것을 발견했다. 해당 문제는 크롬, 엣지에서 동일하게 발견되고 파이어폭스에서는 발견되지 않았다.
찾아보니 2022년도 즈음부터 스택오버플로우와 OKKY 등 여러 커뮤니티에서 언급되는 문제였다. 검색 결과도 그렇고, 파이어폭스로 돌렸을때 정상작동 하는 것도 그렇고 크롬 문제가 맞는 것 같은데... 그럼 예전에 회사에서 적용해놓은 페이지들은 어떻게 되는거지?
scrollIntoView({
behavior: "smooth",
block: "start",
});
위 코드에서 behavior: "smooth" 옵션을 넣을 시 scrollIntoView가 작동하지 않는다. 외부 라이브러리를 사용하라는 해결책도 있고, chrome://flags/#smooth-scrolling 에서 옵션을 Enabled로 변경하라는 해결책도 있는데...(https://stackoverflow.com/questions/61885401/scrollintoview-is-not-working-in-chrome-version-81-behaviour-smooth-is-not-hap)
나의경우 둘 다 정상적으로 작동하지 않는다. 애초에 사용자 크롬 세팅에서 적용하는 건 의미가 없지 않나 싶기도 하고...
마땅한 방법을 찾는다면 업데이트 하겠다. 지금으로써는 자바스크립트로 억지로라도 구현해보는것을 고려중이다.
4/15일 추가
며칠간의 구글링 끝에 문제를 해결했다. 사실 한참 전에 해결한 이슈였는데 블로그 포스팅을 한번 날려먹은 후 잠시 좌절의 시간을 가지고 돌아왔다. 여러분들은 수정중인 글 임시저장을 믿지 마시라...
나의 이슈를 다시 정리해보자면
해결의 전말은 이렇다.
며칠동안 구글링을 해도 해결책은 찾지 못하고, 스크롤 이슈에 대한 이야기만 잔뜩 찾을 수 있던 나는 (예를들어 https://github.com/mdn/browser-compat-data/issues/20217) 아무래도 크롬 엔진을 사용한 브라우저에서 발생하는 문제로 생각하고 대신 각 섹션의 위치를 계산해 scroll이나 scrollTo 메소드를 활용해 섹션단위 이동을 구현하기로 마음먹는다. 하지만... 상대위치를 가지고 섹션의 절대 위치를 구현하는 방식은 영 깔끔하지 않아보이고... 구현은 잘 되는데 가끔 삐걱거리고... scrollIntoView에 대한 미련은 버리지 못하겠고... 그러던 중 한 사이트를 발견하게 된다.(이곳)
화면 단위 이동 구현을 시작할 때 들린 곳이었는데 이곳에서는 scrollIntoView를 통한 화면 단위 이동을 설명하고 있었다. 첨부된 온라인 IDE를 돌려보던 나는 역시 이곳에서도 화면 단위 이동이 제대로 돌아가지 않는 것을 보고 뒤로가기를 하려다... 별 생각 없이 새 창으로 ide를 켜 테스트하기를 누른다. 그리고 그곳에서 아주 잘 돌아가고 있는 기능을 발견한다. 그러고는 생각한다.
가상환경에서는 잘만 돌아가네... 역시 크롬 문젠가...
잠시 뒤 나는 내가 아주 말도 안되는 착각을 하고 있었다는 것을 알아차린다. 라이브 IDE라고 해도 결국 내 크롬 브라우저 안에서 돌아가고 있는건데? 그렇다면 문제는 내 코드에 있다. 헐.
나는 한참동안 내 로직과 사이트의 로직을 비교했다. 애초에 로직이라고 할 것도 없이 간단한 코드라 리액트와 바닐라자바스크립트의 접근 방식 차이 같은 해당 이슈와는 전혀 상관 없어 보이는 차이들 사이에서 나는 도대체 뭐가 문제라서 나는 안되고 너는 되는가를 한참 고민했다. 그러다 사이트 body의 overflow 속성이 hidden으로 들어가 있는 것을 발견한 나는 별다른 기대 없이 내 css에 overflow:hidden 한 줄을 추가한다. 그리고는 모든 기능이 정상적으로 작동하는 것을 알아차린다.
나도 안다. 화면 단위 이동을 구현하려면 보통 스크롤 기능을 막는다. 그런데 나는 이게 단지 미적인 이유로(화면 단위 이동에 스크롤바가 있으면 안이쁘니까), 스크롤바를 통해 이동할 경우 다음 이동할 섹션과 지금 페이지가 맞지 않을 수 있으니까. 뭐 그런 이유로 막는 것이라 생각했다. 미적인 이유라면 스크롤바 모양을 이쁘게 다듬으면 되는거고, 이동 로직이 꼬이는건 intersection observer를 통해 극복할 수 있는 문제라 생각했다. 스크롤을 막지 않았다는 게 하필 scrollIntoView의 특정 옵션만을 방해할 것이라고는 생각조차 하지 못했다. 이렇게, 해당 이슈는 싱겁게 종료되었다. 그렇다면 도대체 왜? 왜 smooth만 동작하지 않는거지? 왜 파이어폭스에서는 문제가 없는거지?
| body의 스크롤이 막히지 않은 경우 이벤트 로그 | body의 스크롤이 막힌 경우 이벤트 로그 |
|---|---|
![]() | ![]() |
크롬 이벤트 로그를 확인해보았다. 이번에 새로 알게 된 사실인데 크롬은 smooth한 이동을 scroll 이벤트를 여러번 발생시키면서 구현한다. 문제는 마우스 휠을 눌러 wheel 이벤트를 발생시킬때 body의 스크롤이 막혀있지 않은 경우 wheel 이벤트가 발생 후 바로 scroll 이벤트가 발생하게 된다는 것이다. 이 5ms후 발생한 scroll 이벤트는 scrollIntoView가 발생시킨 scroll 이벤트를 끊어버리고!(scrollend 이벤트) 동작한다. 그러니 제대로 동작할 리가 없지.
| body의 스크롤이 막히지 않고, behavior가 instant인 경우 |
|---|
![]() |
behavior가 instant 인 경우 이벤트 발생 직후 한번에 스크롤 이벤트가 발생한다. 때문에 휠 내림으로 인한 스크롤 이벤트가 발생해도 해당 이벤트를 방해하지 못한다.
| 파이어폭스의 경우 & body의 스크롤이 막히지 않은 경우 & smooth인 경우 이벤트 로그 |
|---|
![]() |
파이어폭스의 경우 크롬과 이벤트 네임이 너무 달라서 제대로 알아볼 수는 없지만... scroll 이벤트가 wheel 이벤트보다 먼저 발생하는 것을 확인할 수 있었다. 때문에 scroll 이벤트가 wheel 이벤트를 방해하지 않는 게 아닐까? 이벤트 네임을 검색해봐도 별다른 자료가 나오지 않아서 이 이상의 추론은 힘들다. 아무튼 해결했다. 전체적인 코드는 이쁘게 정리한 다음에 올리도록 하겠다.