문제해결

[문제해결] - JavaScript에 의한 상태 변경 지연 - CSR/SSR

hyeeoooook 2026. 1. 20. 22:54
import { auth } from "./firebase";
import { onAuthStateChanged } from "firebase/auth";

DevloginState();

// index.html에 있는 페이지와 연동함
function DevloginState() {
  const DevLogActionSignedIn = document.getElementById("DevLogActionSignedIn");
  const DevLogActionSignedOut = document.getElementById("DevLogActionSignedOut");

  onAuthStateChanged(auth, (user) => {
    if (user) {
      DevLogActionSignedOut.style.display = "none";
      DevLogActionSignedIn.style.display = "block";
    } else {
      console.log("로그아웃 상태");
      DevLogActionSignedOut.style.display = "block";
      DevLogActionSignedIn.style.display = "none";
    }
  });
}
<!-- 로그인 후에 오픈 -->
                        <div id="DevLogActionSignedIn" class="flex flex-grow justify-center items-center gap-4">
                            <button
                                class="cursor-pointer border w-30 h-10 rounded-full border-gray-400 shadow-sm hover:bg-gray-300 bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600">글쓰기</button>
                            <button
                                class="cursor-pointer border w-30 h-10 rounded-full border-gray-400 shadow-sm hover:bg-gray-300 bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600">수정</button>
                        </div>

                        <!--로그인 안되어있을때 오픈-->
                        <div id="DevLogActionSignedOut">
                            <div class="flex flex-grow justify-center items-center gap-4 mt-5">
                                <a href="./login.html"
                                    class="cursor-pointer border text-center content-center w-30 h-10 rounded-full border-gray-400 shadow-sm hover:bg-gray-300 bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600">로그인</a>
                                <a href="./signup.html"
                                    class="cursor-pointer border text-center content-center w-30 h-10 rounded-full border-gray-400 shadow-sm hover:bg-gray-300 bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600">회원가입</a>
                            </div>
                        </div>

 


JavaScript 상태 변경 지연

JavaScript가 렌더링 되는 순서는 다음과 같다.

브라우저 렌더링 순서

1. HTML 파싱 → DOM 생성
2. CSS 파싱  → CSSOM 생성
3. DOM + CSSOM → Render Tree
4. Layout
5. Paint (첫 페인트 발생)
6. JS 실행 (script 위치/속성에 따라 달라짐)
7. Reflow / Repaint

 

내가 겪은 내용은 firebase를 통해서 로그인 상태를 불러와 로그인과 로그아웃 상태에 따른 변경사항인 HTML을 JS로 제어하려고한 부분에서 발생한 문제이다.

 

위의 JavaScript에서 보듯이 display를 none과 block 처리를 실행했고, 이는 화면이 렌더링될때 아주 잠깐이지만 사용자의 눈에 먼저 보인다는것이 문제이다.

 

결론

이를 해결하기 위해서 로그인 상태의 경우 localStorage를 이용하여 초기 렌더링 시점의 시각적 문제를 완화했다

 

import { auth } from "./firebase";
import { onAuthStateChanged } from "firebase/auth";

DevloginState();
devLoginChange();

function DevloginState() {
  onAuthStateChanged(auth, (user) => {
    if (user) {
      console.log("로그인 상태");
    } else {
      console.log("로그아웃 상태");
    }
  });
}

function devLoginChange() {
  const DevLogActionSignedIn = document.getElementById("DevLogActionSignedIn");
  const DevLogActionSignedOut = document.getElementById("DevLogActionSignedOut");

  const isLogin = localStorage.getItem("isLogin") === "true";

  if (!isLogin) {
    DevLogActionSignedOut.classList.remove("hidden");
    DevLogActionSignedIn.classList.add("hidden");
  } else {
    console.log("로그아웃 상태");
    DevLogActionSignedIn.classList.remove("hidden");
    DevLogActionSignedOut.classList.add("hidden");
  }
}

 위의 코드와 같이 localStorage를 통해서 true 값과 false 값을 이용해 쿠키에 저장을 한뒤에 다음 페이지가 렌더링 할때 사용자가 로그인을 했던 상황인지에 대한 기억을 해서 isLogin의 값이 ture인지 false인지를 확인하고 업데이트하는 방식이다.

 

[단점은 가장 아래에 개시]


과정

1차 개선

생각해낸 방법은 "HTML의 body보다 먼저 실행이 된다면 문제가 없지 않을까?" 라는 생각이었다.

 

    <script>
        const isLogin = localStorage.getItem("isLogin") === "true";
        document.documentElement.dataset.login = isLogin;
    </script>
    
    <style>
        /* 로그아웃 상태일 때 */
        [data-login="false"] #logout,
        [data-login="false"] #user,
        [data-login="false"] #DevLogActionSignedIn { 
            display: none !important; 
        }
        
        /* 로그인 상태일 때 */
        [data-login="true"] #login,
        [data-login="true"] #signup,
        [data-login="true"] #DevLogActionSignedOut { 
            display: none !important; 
        }
    </style>

때문에 localStorage의 정의와 CSS를 직접 header에 선언해줬다.

 

결론적으로 다음과 같았다.

 

개선전

 

1차 개선후

결국 눈에 보이기에만 개선을 완료한 억지성 코드이다.


2차 개선

import { auth } from "./firebase";
import { onAuthStateChanged } from "firebase/auth";

DevloginState();


function DevloginState() {
  const DevLogActionSignedIn = document.getElementById("DevLogActionSignedIn");
  const DevLogActionSignedOut = document.getElementById("DevLogActionSignedOut");

  onAuthStateChanged(auth, (user) => {
    if (user) {
      DevLogActionSignedOut.classList.remove("hidden");
      DevLogActionSignedIn.classList.add("hidden");
    } else {
      console.log("로그아웃 상태");
      DevLogActionSignedIn.classList.remove("hidden");
      DevLogActionSignedOut.classList.add("hidden");
    }
  });
}

html의 요소는 모두 hidden 처리를 완료하는것을 채택 해보았다.

 

결론적으로 Firebase auth는 비동기적으로 인증상태를 복원하며, 이 과정이 최초 렌더링보다 늦게 완료될 수 있다.

2차 개선후

 


단점

CSR의 단점은 브라우저의 렌더링을 거스를수없다는 단점이 있었다.

 

때문에, 해당 문제를 해결하기 위해서 알아본 내용들로는 SSR(Server-Side Rendering)를 이용한 방법이 있다.

 

에초에 서버를 이용하여 미리 빌드되어있던 html을 상태에 따라 렌더링 하는 방식이다.

하지만 현재 처럼 개인이 운영하는 개발 블로그에 서버까지 동원을 하는것은 아직 과한 선택이라고 생각이 들기 때문에 최대한 CSR로 해결을 해보았다.