Project

[MD]Next에서 Quill의 CSS FOUC 깜빡임 문제

꼬드리 2025. 2. 7. 17:17

문제: 그니까 왜 새로고침 할 때마다 툴바가 자꾸 혼자 깜빡이는 거냐고.

늦게 나타나는 ui 요소의 깜빡임은 사용자의 눈을 피로하게 만든다.

대체 어떤 개발자나 사용자가 깜빡임 심한 화면을 갖고 싶겠느냐마는... 

 

어쨌든 끔찍하게 못생긴 기본 폰트와 html 구조가 나오다가 3초 뒤 좀 있어 보이는(?) 화면으로 마법처럼 교체되는 경험을 누구나 한번쯤 해봤을 것이다. 전문 용어로는 flash of unstyled content, FOUC라고 부르는데, 쉽게 말해서 외부의 CSS가 불러오기 전 잠시 스타일이 적용되지 않은 웹 페이지가 나타나는 현상이라고 한다.

주로 폰트 같은 곳에서 찾아보기 쉽고, 여간 거슬리는 일이 아닐 수 없다.

 

아래서 깜빡! 깜빡! 깜빡!

 

안그래도 못생긴 툴바가 계속 깜빡거리니까 심각한 상태가 되었다. 내 정서적으로도 좋지 않다.

그런데 왜 다른 요소들은 멀쩡한데 커스텀 툴바만 문제가 생기냐고.

 

 

📍그래서 간단히 시도해본 방법:

1. useEffect로 2초 정도의 시간이 지난 뒤 툴바 보여주기 - 기본 툴바 노출 시간이 줄긴 했는데 그럼에도 살짝 보이는 게 더 거슬렸다. 그렇다고 네트워크 환경에 따라 어떻게 될 지 모르는데 무제한으로 대기하게 할 수도 없고... 

2. useState로 상태 설정해서 툴바의 로딩이 끝날 때 0이던 opacity가 1로 바뀌도록 조건 주기 - 솔직히 될 것 같았는데. css가 엉망이라고 해도 기본 툴바는 이미 존재하고 있어서 그런지, 생각처럼 작동하지 않았다. 

 

 

이건 좀더 근본적인 부분을 뜯어봐야 할 것 같아서 원인을 찾던 찰나,

next가 SSR 기반이기 때문에 간극이 생길 수도 있겠다는 생각이 들었다. 이 부분이 의심스러워서 Next에서 dynamic으로 quill 라이브러리를 가져오는 부분에 loading을 표시해보았다. 그리고 Network 탭을 열어 어떤 순서로 받아오는지 천천히 살펴보았다. 

const QuillWrapper = dynamic(() => import('react-quill-new'), {
  ssr: false,
  loading: () => <p>Loading editor...</p>
});

 

좀더 자세히 보기 위해 Fast 4G로 슬로우 걸어놨다. 하단의 툴바에 주목하길 바란다.

 

예상했던 문제가 맞았다. 초기 quill loading 상태에서 표시되는 'Loading editor...'과 못생긴 기본 툴바의 유지 시간이 일치한다. 즉, quill을 완전하게 불러와 커스텀 CSS 툴바가 적용되기까지 1초 정도 텀이 생기고, 바로 이 텀 동안 Quill의 기본 툴바 스타일을 보여주고 있다는 뜻이 된다. 

네트워크를 열어 보아도 다른 필수 요소들이 불러와진 뒤, 마지막에 가서야 quill 관련을 받아오는 것을 확인할 수 있었다. 

 

react quill은 document 등 브라우저 기반으로 이루어진 라이브러리이기 때문에, Next에서 쓰려면 무조건 dynamic을 통해 클라이언트 사이드에서만 되도록 조치를 해주어야 한다. 그러니까 클라이언트  사이드에서 quill을 쓸 수 있을 때까지 어찌 되었건 조금 기다려야 한다는 건데, ... ...

이 구조 자체를 어떻게 손볼 방법은 지금으로선 없어 보인다. 그럼... 어떻게 해결하지? 

 

 

 

고민하다가 아래와 같은 어찌 보면 좀 무식한 방법을 시도해보았다.

  useEffect(() => {
    const checkQuillLoaded = setInterval(() => {
      if (document.querySelector('.ql-toolbar')) {
        setIsQuillLoaded(true);
        clearInterval(checkQuillLoaded);
      }
    }, 100);

 

현재 깜빡임 문제의 핵심 원인은 Quill이 클라이언트에서 비동기적으로 로드되면서 스타일이 늦게 적용되기 때문이다. 그래서 아예 Quill이 DOM에 렌더링되었는지 체크하는 방식으로 해결해보기로 했다. 위 코드는 로딩 여부를 담는 useState와 useEffect를 사용하여 quill에서 제공하는 .ql-toolbar가 무사히 로딩 되었는지 100ms마다 감시하며 확인한다.

이후 로딩이 끝났다면 클래스 이름을 바꿔주어 기존까지 opacity가 0이었던 툴바가 담긴 div를 가시화 한다.

            <div
              className={
                isQuillLoaded ? styles.visibleToolbar : styles.hiddenToolbar
              }
            >
              <CustomToolbar />
            </div>

 

CSS는 하단과 같이 주어, 자연스럽게 드러나도록 최선을 다했다.

.hiddenToolbar {
  display: none;
}

.visibleToolbar {
  display: flex;
  opacity: 0;
  animation: fadeIn 0.3s forwards;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

 

 

💡고친 결과

 

quill이 완전히 준비된 뒤 fade로 하단 툴바가 나타난다.

 

 

React를 썼으면 좀 나았을까? 이론상으로는 문제 없었을 것 같기도 하다. (대체 어쩌다 내가 Next로...)

사실 툴바만 맨 마지막에 나오는 게 보기 좀 거슬리긴 하는데, 네트워크가 원활하다면 1초 정도기도 하고 어차피 quill이 로드 끝날 때까지는 에디터를  쓸 수도 없을 뿐더러...

초기 화면에 아무것도 안 나오고 빈 공백인 게 더 사용자 입장에서는 불편할 듯 하여 지금은 이 정도로 보류하기로 한다. 

 

유저가 페이지 들어오자마자 곧바로 에디터에 글을 입력할 수 있다면 가장 좋을 텐데... 아쉽... 

그러려면 아예 자체 구현을 해야할 것 같기도 하고... ... 작동하지 않는 가짜 툴바만 먼저 보여주는 방법도 생각해봤는데, 일단은 중요도가 낮다 생각하여 보류. 영 거슬리면 loading 동안 기다려달라는 메세지를 보여주다가 로딩이 끝난 뒤 툴바와 전체 에디터를 한번에 제공하는 방법도 괜찮을 것 같다. 어차피 로딩 되는 중에는 에디터에 글자 입력도 안 된다.

 

무슨 문제인지는 알았으니 후에 이 부분을 수정할 일 있으면 고민할 시간을 덜었다.