JiSoo's Devlog

🛠️ SPA 데이터 로딩 깜빡임 해결하기 본문

카테고리 없음

🛠️ SPA 데이터 로딩 깜빡임 해결하기

지숭숭숭 2025. 11. 17. 19:58

 

 

 

 

여러분은 위에 영상에서 보이시는 것처럼

데이터가 로딩되는 동안 기본값이나 placeholder 값이 잠깐 보였다가

이후에 API 응답을 받고 실제 값으로 바뀌는 "깜빡임 문제"를 경험해 보신 적이 있으신가요?

 

저는 최근에 4주차 과제를 하면서 로그인한 유저 정보를 받아올 때 저런 현상을 발견했는데요.

페이지를 새로고침 하면 폼 영역이 초기값을 한 번 보여준 뒤에 API에서 받아온 실제 값으로 다시 렌더링 되면서 정보가 나타나는 부분이 깜빡이는 현상이 나타났어요.

 

사실 기술적으로 큰 오류도 아니고 데이터도 잘 불러와져서 기능적인 문제는 아니지만

사용자 입장에서 정보를 불러오는 화면에서 이런 사소한 깜빡임이 UX를 해칠 수 있다고 생각해요.

 

 

 

👀 깜빡임이 생기는 이유

이런 깜빡임이 생기는 이유는 초기 렌더링 값과 실제 API 응답 값이 서로 다르기 때문이에요.

아래 코드를 보면 초기 렌더링에서는 항상 fallbackUser가 먼저 화면에 그려지고

API 응답이 도착했을 때 실제 값으로 다시 렌더링 되기 때문에 사용자가 값이 한 번 바뀌는 깜빡임 현상을 보게 됩니다

 

const [user, setUser] = useState<User>(fallbackUser);

useEffect(() => {
  let cancelled = false;

  const fetchUser = async () => {
    try {
      const response = await axios.get(
        "http://example.com/api/v1/users/137",
        { timeout: 4000 }
      );

      if (!cancelled) {
        const normalized = normalizeUser(response.data);
        setUser(normalized); // ← 여기서 화면이 두 번째로 렌더링됨
      }
    } catch (err) {
      console.log(err);
    }
  };

  fetchUser();

  return () => {
    cancelled = true;
  };
}, []);

 

 

이 코드를 실제 렌더링 흐름이 맞춰 다시 보면 아래와 같은 단계로 동작해요.

 

1. 초기 렌더링 → 아직 데이터가 없기 때문에 placeholder나 빈 값 같은 기본 UI 렌더링

2. API 요청 → 서버에 데이터 요청

3. 두 번째 렌더링 → API 응답이 오면 비어 있던 UI가 실제 데이터로 다시 그려짐

 

이렇게 깜빡임 현상은 오류가 아니라 SPA 구조 자체에서 생기는 자연스러운 현상이에요.

 

특히 폼 입력 화면이나 정보 제공 화면처럼 값의 변화가 바로 눈에 보이는 UI에서는 이 전환이 더욱 뚜렷하게 잘 보이게 돼요.

그리고 혹시나 API 응답 속도가 조금이라도 느려지면 fallback 값이 보이는 시간이 길어져 깜빡임이 더 크게 느껴질 수도 있답니다.

 

하지만 이렇게 구조적인 문제라고 하더라도 충분히 해결이 가능해요!

 

☘️ 해결 방법

이 깜빡임 문제를 해결하기 위해 중요한 건 초기 렌더링 때 표시할 값을 컨트롤할 방법이 필요했어요.

기존 방식에서는 항상 fallbackUser가 먼저 렌더링 되고 이후에 API 응답이 오면 그때 실제 데이터로 갱신되기 때문에 화면이 두 번 바뀌는 구조가 유지됩니다.

 

그래서 저는 직전에 사용하던 데이터를 그대로 초기값으로 쓴다면 이 깜빡임이 사라지지 않을까 하는 생각이 들었어요.

 

새로고침을 하더라도, API 응답이 아직 오지 않았더라도

유저 입장에서는 항상 최신 데이터가 채워진 UI를 처음부터 보게 되는 거죠 ✨

 

이걸 가능하게 하는 간단한 방법이 바로

localStorage 캐싱입니다!!

 

아래는 이 캐싱을 실제 코드로 구현한 코드입니다.

const USER_CACHE_KEY = "user-cache";

const loadCachedUser = () => {
  try {
    const saved = localStorage.getItem(USER_CACHE_KEY);
    return saved ? JSON.parse(saved) : fallbackUser;
  } catch {
    return fallbackUser;
  }
};

const saveCachedUser = (user: User) => {
  try {
    localStorage.setItem(USER_CACHE_KEY, JSON.stringify(user));
  } catch {}
};

function App() {
  // fallbackUser 대신 캐시된 유저 정보로 초기 렌더링
  const [user, setUser] = useState<User>(() => loadCachedUser());

  useEffect(() => {
    let cancelled = false;

    const fetchUser = async () => {
      const response = await axios.get("http://example.com/api/v1/users/137");
      if (cancelled) return;

      const normalized = normalizeUser(response.data);
      setUser(normalized);       // 화면 업데이트
      saveCachedUser(normalized); // 캐시 동기화
    };

    fetchUser();
    return () => (cancelled = true);
  }, []);

  ...
}

 

캐시를 사용하면 아까 봤던 흐름이

 

1. 초기 렌더링 → fallbackUser 대신 로컬스토리지에 저장되어 있던 마지막 유저 정보를 그대로 렌더링

2. API 요청 → 사용자는 이미 완성된 UI를 보고 있는 상태

3. 응답 도착 → 캐시 업데이트 & localStorage 갱신

4. 로그아웃, 탈퇴 시 캐시 삭제 → 다른 계정에서 내 정보가 보이는 문제 방지

 

이렇게 바뀌게 됩니다.

아래 영상을 보시면 새로고침을 계속해서 하는데도 더 이상 깜빡이지 않는 거 보이시나요?

 

이렇게 실제 화면에서도 확연하게 느껴진답니다🤩🤩

 

 

✨ 캐싱 방식의 효과

❶ 초기 렌더링부터 실제 데이터가 보이기 때문에 화면 전환이 거의 느껴지지 않고 깜빡임이 사라진다.
❷ 네트워크 속도나 API 응답 지연에 영향을 받지 않고 안정적인 초기 화면을 제공할 수 있다.
❸ 구현 방식이 단순해 기존 구조를 크게 바꾸지 않고도 적용할 수 있으며 유지보수가 쉽다.

 

 

🐙 다른 방식과의 차이점

초기 렌더링 깜빡임 문제를 해결하는 방법은 로컬스토리지 캐싱만 있는 것은 아니에요.

SPA 환경에서는 여러 접근 방식이 있고 각각의 장단점이 있답니다.

 

이번에 제가 사용한 방식은 단순히 axios로 사용자 정보를 불러오는 구조였기 때문에  

localStorage 캐싱만으로도 깜빡임 문제를 충분히 해결할 수 있었어요.  

구현도 가볍고 기존 코드 흐름을 거의 건드리지 않아도 된다는 점에서 작은 규모나  단일 데이터만 다루는 경우에는

이 방식만으로도 깜빡임 문제를 충분히 해결할 수 있다고 생각해요.

 

하지만 프로젝트 규모가 커지거나 관리해야 하는 상태가 많아질수록  

이런 단순 캐싱 방식만으로는 한계가 분명해요.  

여러 컴포넌트에서 같은 데이터를 공유한다거나 데이터 동기화 타이밍이 중요한 상황이라면

TanStack Query, SWR, Zustand persist 같은 전문적인 상태 관리·캐싱 도구를 사용하는 편이 훨씬 안정적이에요!

 

특히 TanStack Query의 `initialData`, `staleTime`, `persistQueryClient` 같은 기능을 사용하면  

제가 직접 구현했던 localStorage 캐싱과 동일한 효과를 자동으로 제공하면서

데이터 갱신이나 로딩 상태 관리까지 체계적으로 해주기 때문에

규모가 있는 서비스에서는 이런 도구들을 적극적으로 추천드립니다!!

 

 

🔗 참고

https://tanstack.com/query/v5/docs/framework/react/overview

 

Overview | TanStack Query React Docs

TanStack Query (formerly known as React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes fetching, caching, synchronizing and...

tanstack.com

 

 

728x90