- Published on
잘못된 스플래시 유저 경험을 개선하기
- Authors
- Name
TL;DR
포도스피킹의 프론트엔드 개발을 맡은 지 대략 1년이 지났습니다. 25년 3월 25일 React Native로 네이티브 영역을 페이스리프팅하고, 이후 안정화 및 최적화 작업을 수행하면서 그동안 간과했던 사용자 경험 요소들을 면밀히 검토하는 계기가 되었습니다.
사용자 피드백을 분석하던 중 앱 구동 속도에 대한 불만이 다수 발견되었습니다. 개발 과정에서 반복적으로 접하다 보니 둔감해졌지만, 신규 사용자 관점에서는 체감 속도가 느릴 수밖에 없었습니다. 병목 지점을 파악해보니 스플래시 화면 표시부터 웹뷰 초기 로딩 완료까지의 구간에서 상당한 지연이 발생하고 있었습니다.
그렇게 되어서 스플래시 관련하여 궁금한것을 GPT 한테 물어보았습니다.
하이브리드 웹앱에서 스플래시란 무엇일까??
하이브리드 웹앱의 스플래시는 앱을 실행했을 때 웹뷰와 리소스가 로드되는 동안 사용자가 보게 되는 첫 화면(로딩/브랜딩 화면) 입니다.
그러면 스플래시가 숨겨지는 타이밍은 어떻게 되는것이 좋을까?
좋은 스플래시 해제 타이밍은 “사용자가 멈춤을 느끼지 않으면서, 핵심 UI의 첫 인상이 깨지지 않는 시점”입니다.
아차 싶었던 포인트
다시 한번 포도의 스플래시 화면부터 첫 화면 진입까지의 과정을 면밀히 살펴보았습니다.

스플래시 이미지가 종료된 후 웹뷰를 로딩하고 화면이 그려지기 시작합니다. 그런데 문제는 여기서부터입니다. 웹뷰가 HTML 문서를 전달받아 화면에 렌더링한 이후에도, 여러 페이지를 거쳐 리다이렉션하면서 최종적으로 사용자가 실제로 사용할 화면에 도달하는 구조였습니다.
문제의 핵심
- 스플래시 종료 ≠ 앱 사용 가능
스플래시가 사라져도 사용자는 여전히 기다려야 합니다.
- 여러번의 리다이렉션
메인 도메인 → 인증 확인 → 콜백 처리 → 최종 목적지까지 최소 3~4번의 페이지 전환이 발생합니다.
- 각 단계마다 발생하는 지연
- 페이지 요청/응답 왕복 시간
- 각 페이지의 초기 렌더링 시간
- 리다이렉트 로직 실행 시간
결국 사용자는 스플래시 화면이 사라진 후에도 빈 화면이나 로딩 상태를 추가로 보게 되며, 이것이 "앱이 느리다"는 피드백의 주요 원인이었을 것입니다.
현재 리다이렉션 플로우 분석
정확히 어떤 단계를 거치는지 파악해보니 총 4단계의 리다이렉션이 발생하고 있었습니다.

각 단계를 거칠 때마다 다음과 같은 작업들이 순차적으로 실행됩니다.
- 초기 진입 페이지 로드 - destination 파라미터 확인
- 인증 API 호출 - 사용자 인증 상태 검증
- 리다이렉트 콜백 처리 - 인증 결과에 따른 분기 처리
- 최종 목적지 렌더링 - 실제 사용자가 볼 화면 표시
문제는 이 모든 과정이 직렬로 처리된다는 점입니다. 각 단계가 완료되어야 다음 단계로 넘어갈 수 있기 때문에, 네트워크 지연이나 처리 시간이 누적되어 체감 속도가 느려질 수밖에 없었습니다.
개선 방향 고민
이 문제를 해결하기 위해 두 가지 핵심 질문에 집중했습니다.
1. 리다이렉션 단계를 어떻게 줄일 수 있을까?
현재 4단계로 나뉜 리다이렉션을 줄이기 위해 다음과 같은 방법들을 검토했습니다.
- 불필요한 중간 단계 제거 - 꼭 필요한 단계인지 재검토
- 병렬 처리 가능한 작업 분리 - 인증 확인과 다른 작업을 동시에 처리
- 클라이언트 사이드에서 직접 처리 - 서버 리다이렉트를 클라이언트 라우팅으로 대체
2. 스플래시를 끄는 시점을 어떻게 최적화할까?
스플래시를 너무 일찍 끄면 사용자는 빈 화면이나 로딩 인디케이터를 보게 되고, 너무 늦게 끄면 불필요하게 대기 시간이 길어집니다. 따라서 다음과 같은 조건을 만족하는 시점을 찾아야 했습니다.
- 최소 요구사항: 웹뷰가 초기화되고 HTML 문서 로딩이 완료된 시점
- 이상적인 시점: 사용자가 상호작용 가능한 핵심 UI가 렌더링된 시점
- 실용적인 타협점: 첫 화면의 레이아웃이 그려지고 주요 콘텐츠가 보이기 시작하는 시점
개선 방안 적용하기
1. 네이티브 스플래시 구성 추가
앱 실행 직후 즉시 스플래시를 표시하기 위해 네이티브 레벨에서 스플래시 화면을 구성했습니다.
네이티브 스플래시 설정 예시
// 설정 파일에서 스플래시 옵션 지정
{
splash: {
image: '스플래시_이미지_경로',
resizeMode: 'contain',
backgroundColor: '#ffffff'
}
}
앱 시작 시 즉시 네이티브 스플래시가 표시되도록 설정합니다.
2. 네이티브 레이어: 스플래시 제어 로직
네이티브 레이어 스플래시 제어 로직
// 스플래시 설정
스플래시옵션설정({
페이드시간: 1000, // 1초 페이드 아웃
페이드효과: true
})
스플래시.자동숨김방지() // 수동 제어
const 최소_노출_시간 = 4000 // 4초
function 루트레이아웃() {
const [최소시간충족, set최소시간충족] = useState(false)
// 웹에서 전달받은 상태
const { 앱로드완료, 앱시작시간 } = 브리지상태구독()
// ✅ 조건 1: 최소 시간 보장
useEffect(() => {
const 경과시간 = 현재시간() - 앱시작시간
const 남은시간 = Math.max(0, 최소_노출_시간 - 경과시간)
타이머(남은시간).then(() => set최소시간충족(true))
}, [])
// ✅ 조건 2: 두 조건 모두 충족 시 스플래시 해제
useEffect(() => {
if (앱로드완료 && 최소시간충족) {
스플래시.숨기기()
}
}, [앱로드완료, 최소시간충족])
return <앱화면 />
}
브리지 상태 정의
// 네이티브 ↔ 웹 간 공유할 상태 정의
type 브리지상태 = {
앱시작시간: number
앱로드완료시간: number
앱로드완료: boolean
// ...
}
type 브리지액션 = {
앱로드완료설정: () => void
// ...
}
// 브리지 초기화
브리지생성({
// 초기값
앱시작시간: 현재시간(),
앱로드완료: false,
// 웹에서 호출할 수 있는 액션
앱로드완료설정: () => {
상태업데이트({ 앱로드완료: true, 앱로드완료시간: 현재시간() })
}
})
작동 원리
스플래시 해제는 두 가지 조건을 모두 만족해야 실행됩니다:
- 시간 조건: 앱 시작 후 최소 시간 경과 (예: 4초)
- 로딩 조건: 웹 앱에서 로드 완료 신호 전송

핵심 장점:
- 앱이 빠르게 로드되어도 최소 시간은 스플래시를 유지 → 깜빡임 방지
- 앱 로딩이 오래 걸려도 로드 완료를 기다림 → 빈 화면 방지
- 페이드 효과로 부드러운 전환 제공
3. 웹 레이어: 브리지를 통한 로드 완료 신호 전송
웹에서 브리지 사용
// 웹 앱에서 네이티브 브리지 연결
function 웹브리지프로바이더({ children }) {
const 브리지 = 브리지연결()
const 네이티브상태 = {
// 네이티브 상태 구독
앱로드완료: 브리지.앱로드완료,
// 네이티브 액션 호출
앱로드완료알림: () => 브리지.앱로드완료설정()
}
return (
<Context.Provider value={네이티브상태}>
{children}
</Context.Provider>
)
}
실제 사용 지점
// 웹 앱의 초기화 로직
function 앱초기화훅() {
const { 앱로드완료알림 } = 네이티브브리지사용()
useEffect(() => {
async function 초기화() {
try {
// 필요한 초기화 작업 (인증, 데이터 로드 등)
await 인증처리()
await 초기데이터로드()
// ✅ 모든 준비 완료 후 네이티브에 알림
앱로드완료알림()
} catch (error) {
// ⚠️ 에러 발생 시에도 스플래시는 해제
앱로드완료알림()
에러처리(error)
}
}
초기화()
}, [])
}
전체 흐름

핵심 포인트:
- 웹 앱이 실제로 사용 가능한 시점에 로드 완료 신호 전송
- 필요한 초기화 작업(인증, 데이터 로드 등)이 완료된 후 알림
- 에러 발생 시에도 스플래시가 영구적으로 남지 않도록 에러 처리 블록에서도 신호 전송
4. 초기 페인트 안정성 향상
안전 영역 배경색 동기화 (선택적)
// 브리지에 UI 상태 추가 (필요시)
type 브리지상태 = {
배경색: string
// ...
}
// 웹에서 페이지별 배경색 제어
function 페이지() {
const { 배경색설정 } = 브리지사용()
useEffect(() => {
배경색설정('#F5F5F5') // 페이지 진입 시
return () => 배경색설정('#FFFFFF') // 페이지 이탈 시
}, [])
return <div>페이지 내용</div>
}
브리지를 통해 네이티브와 웹의 배경색을 동기화하여 전환 시 깜빡임을 방지할 수 있습니다.
5. 스플래시 애니메이션 품질 개선 (선택적)
애니메이션 루프 설정
// Lottie 등 애니메이션 라이브러리 사용
<애니메이션뷰
소스={애니메이션파일}
자동재생
반복={true} // 스플래시가 숨겨질 때까지 반복 재생
스타일={전체화면}
/>
반복 재생으로 스플래시가 해제될 때까지 끊김 없는 애니메이션을 제공할 수 있습니다.
6. 기타 고려사항
- 네이티브 라이브러리 버전 호환성 확인
- 빌드 시 네이티브 리소스 동기화 스크립트 실행
- 각 플랫폼(iOS/Android)별 스플래시 설정 차이 확인
개선 결과
Before vs After
Before

문제점:
- 네이티브 스플래시가 빠르게 사라짐
- WebView 로딩 중 흰 화면 노출
- 앱 로드 상태 체크 없음
After


개선점:
- 네이티브 스플래시가 자동으로 숨겨지지 않음 (
preventAutoHide) - 최소 4초 보장 + 앱 로드 완료 확인
- 브리지를 통한 정확한 상태 전달
- 1초 페이드 아웃으로 부드러운 전환
체감 효과 요약
시각적 개선
- 앱 실행 직후 즉시 스플래시 노출 - 초기 흰 화면 완전 제거
- 부드러운 전환 - 네이티브 스플래시 → 오버레이 → 실제 화면으로 자연스러운 전환
- 깜빡임 최소화 - 안전 영역 배경색 일관화로 레이아웃 점프 감소
로딩 경험 개선
- 최소 노출 시간 보장 - 너무 짧은 스플래시로 인한 깜빡임 방지
- 이중 조건 체크 - 시간 조건과 로딩 조건을 모두 만족해야 해제
- 부드러운 전환 - 페이드 아웃 효과로 자연스러운 전환
- 브리지 기반 제어 - 웹 앱이 실제로 사용 가능한 시점에 네이티브에 알림
안정성 향상
- 이중 조건 체크 - 시간과 로딩 상태 모두 확인하여 안정적인 스플래시 해제
- 브리지 기반 통신 - 네이티브 ↔ 웹 간 명확한 상태 공유
- 네이티브 리소스 동기화 - 빌드 프로세스 안정성 확보
실제 측정 결과
| 지표 | Before | After | 개선 효과 |
|---|---|---|---|
| 초기 흰 화면 노출 | 발생 | 제거 | 네이티브 스플래시 즉시 표시 |
| 스플래시 최소 시간 | 불안정 | 보장 | 깜빡임 방지 |
| 페이드 전환 | 없음 | 적용 | 부드러운 전환 |
| 앱 로드 체크 | 없음 | 브리지로 확인 | 정확한 타이밍 |
| UI 일관성 | 불안정 | 통합 관리 | 시각적 안정성 |
배운 점
1. 네이티브와 웹의 경계를 이해하기
하이브리드 앱에서는 네이티브 스플래시와 웹뷰 로딩 사이의 간극이 사용자 경험을 크게 좌우합니다. 단순히 웹뷰의 로드 이벤트에 의존하면 HTML 문서가 로드된 시점만 알 수 있지, 실제 앱이 사용 가능한 시점은 알 수 없습니다. 브리지를 통한 명시적인 신호 전달이 핵심입니다.
2. 타이밍이 전부다
스플래시를 언제 보여주고 언제 숨길지에 대한 섬세한 제어가 체감 속도를 결정합니다.
- 너무 이르면: 깜빡임 발생, 불완전한 화면 노출
- 너무 늦으면: 답답함, "앱이 느리다"는 인식
이중 조건 체크(최소 노출 시간 + 앱 로드 완료)로 이 균형을 맞출 수 있습니다.
3. 방어적 프로그래밍의 중요성
try {
await 초기화작업()
앱로드완료알림()
} catch (error) {
// ⚠️ 에러 발생 시에도 스플래시는 해제해야 함
앱로드완료알림()
에러처리(error)
}
네트워크 지연, 인증 실패 등 예상치 못한 상황에서도 사용자가 영구적인 로딩 화면에 갇히지 않도록 에러 처리 블록에서도 스플래시 해제 신호를 보내는 것이 중요합니다.
4. 브리지 아키텍처의 위력
네이티브와 웹 간의 브리지 패턴을 통해:
- 양방향 통신 - 네이티브에서 상태 구독, 웹에서 액션 호출
- 타입 안전성 - 상태와 액션의 명확한 인터페이스 정의
- 명확한 책임 분리 - 네이티브는 UI 제어, 웹은 비즈니스 로직
이 구조로 스플래시 타이밍을 정확히 제어하고 일관된 사용자 경험을 제공할 수 있었습니다.
5. 작은 개선의 누적 효과
- 네이티브 스플래시 즉시 표시
- 자동 숨김 방지 및 수동 제어
- 최소 노출 시간 보장 (예: 4초)
- 부드러운 전환 효과 (페이드 아웃)
- 브리지 기반 정확한 타이밍 제어
- UI 일관성 유지 (배경색 동기화 등)
하나하나는 작은 개선이지만, 이들이 모여 전체적인 사용자 경험을 크게 향상시킵니다.
더 물어볼 만한 주제
- 스플래시 화면의 최적 지속 시간은? - 너무 짧으면 깜빡임, 너무 길면 답답함
- 웹뷰 프리로딩 vs 지연 로딩 - 어느 전략이 체감 속도를 개선할까?
- 스플래시 디자인이 UX에 미치는 영향 - 단순 로고 vs 프로그레스 바 vs 애니메이션
- 네이티브 vs 웹 기반 스플래시의 차이점 - 각각의 장단점과 구현 방식
- 초기 로딩 성능 측정 방법 - Time to Interactive, First Contentful Paint 등의 메트릭
- 콜드 스타트 vs 웜 스타트 최적화 - 앱 재실행 시 다른 전략이 필요할까?
- 스플래시 이후의 화면 전환 전략 - Fade out, 슬라이드, 즉시 전환 중 무엇이 자연스러울까?
마치며
"앱이 느리다"는 단순한 피드백 하나가, 스플래시 화면부터 초기 로딩 플로우 전체를 돌아보는 계기가 되었습니다. 개발자로서 익숙해진 환경에서는 놓치기 쉬운 사용자 경험의 세부 사항들을 다시 한번 점검할 수 있었고, 작은 개선들이 모여 큰 차이를 만든다는 것을 체감할 수 있었습니다.
여러분의 앱도 혹시 초기 로딩에서 사용자가 불편을 느끼고 있지는 않나요? 한번 천천히 들여다보시길 추천드립니다.