
애플리케이션 실행 시 페이지의 컨텐츠가 로딩되기까지 일시적으로 보여주는 화면을 말한다. 모바일 애플리케이션의 경우 로고가 중앙에 박혀있는 화면을 가장 많이 사용한다.
아래의 gif를 보면, 앱 실행 시 splash-screen 요소가 렌더링되고 사라지면서 HTML 요소도 함께 변경되는 것을 볼 수 있다.
추가로, splash-screen과는 별도로 앱의 콘텐츠가 로드됨에 따라 아래쪽의 loading-start, loading-end 요소에서 로딩 시작과 끝을 알려준다.

우선, 아래와 같이 리액트 앱 외부에서 SplashScreen을 렌더링할 div를 만들어준다.
Splash Screen은 앱의 다른 요소와는 독립적으로 동작해야 하므로, 별도의 DOM 노드에 렌더링하여 레이아웃 관리가 용이하도록 했다.
<!-- index.html -->
<!doctype html>
<html lang="ko-KR">
<head>
<meta charset="UTF-8" />
<title>해마디</title>
<!-- ... -->
<script type="module" src="src/main.jsx"></script>
</head>
<body>
<noscript>이 앱을 사용하려면 JavaScript 활성화가 필요합니다.</noscript>
<div id="splash-screen"></div>
<div id="root"></div>
</body>
</html>
그 후에 App.jsx에 SplashScreen 컴포넌트를 렌더링하도록 추가해준다.
이 때, 경로에 따라 렌더링하는 콘텐츠를 변경하는 Router와는 분리되어야 하기 때문에, 외부에 넣어준다.
(별도의 SEO를 제공하지도 않기 때문에 HelmetProvider에도 포함하지 않았다.)
// App.jsx
import { HelmetProvider } from 'react-helmet-async';
import SplashScreen from './components/SplashScreen/SplashScreen';
import AppRouter from './router';
function App() {
return (
<>
<SplashScreen />
<HelmetProvider>
<AppRouter />
</HelmetProvider>
</>
);
}
export default App;
아래는 SplashScreen 컴포넌트이고, 다음의 순서대로 렌더링된다.
1. index.html에 존재하는 splash-screen 요소를 참조하고 있다.
2. 앱이 실행되면, createPortal을 통해 splash-screen 요소에 이미지와 제목 등을 렌더링한다. (화면에 보여지는 SplashScreen 뒤에서는 콘텐츠 로드가 이루어지는 중)
3. useLayoutEffect를 사용해 2초 뒤에 splash-screen의 HTML 코드를 비우라는 타이머를 설정한다.
4. 2초 뒤에 SplashScreen의 콘텐츠는 사라지고 로딩 스피너 혹은 로딩이 완료된 콘텐츠가 보인다.
// SplashScreen.jsx
import { memo, useLayoutEffect } from 'react';
import { createPortal } from 'react-dom';
import styles from './SplashScreen.module.css';
// splash-screen 요소
// 컴포넌트 초기화 과정에서 1회만 필요하므로 컴포넌트 외부에서 참조
const splashScreenElement = document.getElementById('splash-screen');
// SplashScreen 컴포넌트
function SplashScreen({ fadeOutTime = 2000 }) {
// 레이아웃 이펙트
useLayoutEffect(() => {
// 페이드 아웃 타임이 되면 스플래시 스크린 콘텐츠 삭제
const clearId = setTimeout(() => {
splashScreenElement.innerHTML = '';
}, fadeOutTime);
return () => {
// 설정된 타이머 정리
clearTimeout(clearId);
};
}, [fadeOutTime]);
// ReactDOM의 포털을 사용해 splash-screen 요소에 렌더링
// 스플래시 스크린의 적절한 제목 설정 필요
return createPortal(
<div className={styles.splashScreen}>
<div className={styles.textBox}>
<p>감정 관리를 위한 일기 서비스</p>
<h1>해마디</h1>
</div>
// ...
</div>,
splashScreenElement
);
}
// 컴포넌트 속성이 변경되지 않을 경우 불필요한 리-렌더링 차단
export default memo(SplashScreen);