Expo Status Bar 사용법: Safe Area와 화면별 설정

2026.04.22·수정 2026.09.13·약 16분·작성: 해비·블로그 소개

학습 목표·선수 지식: 상태바 글자색과 안전 영역을 구분하고 현재 활성 화면에 맞춰 색상을 바꿉니다. Expo Router 및 Safe Area를 먼저 익혀야 합니다.

이 글에서 정리하는 내용

이 글에서는 expo-status-bar가 무엇을 제어하는지부터 시작해서, 루트 레이아웃에 기본값을 두는 방법, 화면별로 상태바 스타일을 바꾸는 방법과 Safe Area를 왜 분리해서 이해해야 하는지까지 한 번에 정리합니다. 읽고 나면 상태바 스타일과 상단 여백 문제를 같은 문제로 섞어 처리하는 실수를 줄일 수 있습니다.

expo-status-bar가 하는 일

Expo Status Bar 사용법: Safe Area와 화면별 설정 핵심 개념을 설명하는 첫 번째 본문 이미지

이 글에서 먼저 분리해서 보려는 것은 상태바의 역할과 화면 레이아웃의 역할입니다. expo-status-bar는 시간, 배터리, 네트워크 아이콘이 보이는 상단 시스템 영역의 텍스트 색상, 숨김 여부, 애니메이션 같은 표시 상태를 제어할 때 사용하는 도구입니다. 즉 상단에 무엇이 보일지를 제어하는 쪽에 가깝고, 콘텐츠를 얼마나 아래로 밀어야 하는지는 별도 문제입니다.

상태바 스타일과 Safe Area는 서로 다른 역할입니다. 상태바가 밝게 보여야 해서 StatusBar를 만졌는데, 정작 버튼이 노치 아래에 깔리는 문제는 그대로 남아 있는 식입니다. 반대로 상단 padding만 늘렸는데 아이콘 색상은 여전히 배경과 겹쳐 안 보이는 경우도 많습니다. 그래서 expo-status-bar는 표시 스타일, Safe Area는 안전한 배치라는 식으로 먼저 나눠 이해하는 편이 훨씬 덜 헷갈립니다.

React Native StatusBar와의 관계

코드 위치: src/app/detail.tsx. 앞에서 전체 파일로 명시한 경우를 제외하면 해당 구조를 설명하는 부분 예제입니다. 서로 다른 예제의 export default를 한 파일에 합치지 않습니다.

// expo-status-bar는 Expo 환경에 맞춘 기본값을 가진 StatusBar 컴포넌트를 사용합니다.
import { StatusBar } from 'expo-status-bar';
export default function App() {
  return <StatusBar style="auto" />;
}

expo-status-bar는 React Native StatusBar와 거의 같은 인터페이스를 사용하지만, Expo 프로젝트에서 바로 쓰기 좋게 정리된 패키지입니다. 그래서 Expo에서는 기본적으로 이 패키지부터 익히는 편이 자연스럽습니다. 이 패키지는 컴포넌트 방식과 imperative API를 모두 제공하지만, 일반적인 화면 구성에서는 컴포넌트 방식이 흐름을 읽기 더 쉽습니다.

가장 많이 쓰는 기본 사용법

가장 먼저 익혀둘 것은 루트 레이아웃에 기본 상태바 스타일을 두는 방식입니다. 앱 전체에 공통으로 적용할 기본값을 하나 두고, 특별한 화면에서만 덮어쓰는 구조가 관리하기 좋습니다. Expo Router를 쓴다면 보통 app/_layout.tsx에서 시작하는 편이 깔끔합니다.

루트 레이아웃에 기본값 두기

코드 위치: src/app/_layout.tsx. 앞에서 전체 파일로 명시한 경우를 제외하면 해당 구조를 설명하는 부분 예제입니다. 서로 다른 예제의 export default를 한 파일에 합치지 않습니다.

// 앱 전체 기본 상태바 스타일을 루트에 둡니다.
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
export default function RootLayout() {
  return (
    <>
      
      <Stack>
        
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      </Stack>
      <StatusBar style="dark" />
    </>
  );
}

여기서 가장 자주 쓰는 prop은 style, hidden, animated입니다. style은 상태바 텍스트와 아이콘을 밝게 혹은 어둡게 보이게 하는 역할이고, hidden은 상태바 자체를 숨길지 결정합니다. animated는 스타일 변화나 숨김 전환이 바뀔 때 화면이 덜 튀게 해줍니다. 처음에는 prop을 많이 외우기보다 이 세 개만 정확히 익혀도 대부분의 화면은 대응할 수 있습니다.

추가로 Android에서는 backgroundColor나 translucent 같은 속성이 떠오르기 쉽지만, 최근 edge-to-edge 레이아웃 기준에서는 이 값이 항상 기대한 대로 동작하지 않을 수 있습니다. 특히 Android 15 이상에서는 상태바 배경색과 translucent 설정이 더 이상 효과가 없거나 폐기 대상으로 안내되고 있어서, expo-status-bar를 사용할 때도 이 부분을 예전 방식 그대로 믿으면 화면이 다르게 보일 수 있습니다. 그래서 요즘은 상단 배경을 화면 쪽에서 설계하고, 이 도구는 스타일과 가시성 중심으로 다루는 감각이 더 중요합니다.

필요할 때만 imperative API 쓰기

코드 위치: src/app/detail.tsx. 앞에서 전체 파일로 명시한 경우를 제외하면 해당 구조를 설명하는 부분 예제입니다. 서로 다른 예제의 export default를 한 파일에 합치지 않습니다.

// expo-status-bar는 명령형 함수도 제공합니다.
// 다만 일반 화면에서는 컴포넌트 방식이 더 읽기 쉽습니다.
import { setStatusBarHidden } from 'expo-status-bar';

function openFullscreen() {
  setStatusBarHidden(true, 'fade');
}

function closeFullscreen() {
  setStatusBarHidden(false, 'fade');
}

expo-status-bar는 setStatusBarStyle, setStatusBarHidden 같은 명령형 함수도 제공합니다. 영상 전체화면 전환처럼 이벤트 한 번에 맞춰 바로 상태를 바꾸고 싶을 때는 이런 방식이 편할 수 있습니다. 다만 화면 렌더링 흐름 안에서 스타일을 관리하는 일반 화면이라면 컴포넌트 방식이 더 예측 가능하고, 유지보수할 때도 읽기 쉽습니다.

그리고 style의 기본값이 auto라는 점도 알아두면 좋습니다. expo-status-bar에서 auto는 현재 색상 테마에 맞춰 light 또는 dark를 자동으로 고르는 값입니다. 즉 다크 모드와 라이트 모드를 모두 고려한 기본값이 필요할 때 이 auto 값은 꽤 실용적입니다.

자주 쓰는 prop 핵심 역할
style, hidden, animated 텍스트 색상, 표시 여부, 전환 애니메이션
backgroundColor, translucent Android 관련 속성이지만 최신 환경에서는 제약을 함께 봐야 함

화면별 상태바 제어 방법

src/app/detail.tsx 전체 예제입니다. 헤더가 없는 Router 화면을 기준으로 합니다. 탭·스택에서는 보이지 않는 화면도 마운트되어 있을 수 있으므로 화면 전용 StatusBar는 포커스된 동안만 렌더링합니다. 루트 기본값은 유지하고 다른 화면도 각자의 활성 상태를 기준으로 처리하세요.

코드 위치: src/app/detail.tsx. 앞에서 전체 파일로 명시한 경우를 제외하면 해당 구조를 설명하는 부분 예제입니다. 서로 다른 예제의 export default를 한 파일에 합치지 않습니다.

import { useIsFocused } from '@react-navigation/native';
import { Text } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { SafeAreaView } from 'react-native-safe-area-context';

export default function DetailScreen() {
  const focused = useIsFocused();
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: '#111827' }}>
      {focused && <StatusBar style="light" animated />}
      <Text style={{ color: '#fff' }}>상세 화면</Text>
    </SafeAreaView>
  );
}

상세 화면처럼 배경이 어두운 화면에서는 루트의 스타일을 그대로 쓰면 아이콘이 묻혀 보일 수 있습니다. 이럴 때는 그 화면 안에 StatusBar를 한 번 더 두고 style을 light로 덮어쓰면 됩니다. 저는 이 패턴을 목록 화면과 상세 화면의 분위기가 확실히 다를 때 자주 사용합니다.

여기서 기억할 점은 StatusBar를 여러 개 마운트할 수 있다는 점입니다. 전역 하나, 특정 화면 하나가 동시에 존재해도 이상한 구조는 아닙니다. 다만 컴포넌트 기반 설정과 명령형 함수 기반 설정을 한 흐름에 섞어서 쓰면 다음 렌더 때 값이 덮이면서 흐름을 추적하기 어려워질 수 있으니, 한 화면 안에서는 되도록 컴포넌트 방식으로 통일하는 편이 안정적입니다.

전역 기본값과 화면 전용 값을 같이 쓰는 기준

코드 위치: src/app/detail.tsx. 앞에서 전체 파일로 명시한 경우를 제외하면 해당 구조를 설명하는 부분 예제입니다. 서로 다른 예제의 export default를 한 파일에 합치지 않습니다.

// 밝은 배경 화면은 루트 기본값을 그대로 쓰고,
// 어두운 배경 화면만 개별 StatusBar를 추가합니다.
function ListScreen() {
  return <View style={{ flex: 1, backgroundColor: '#ffffff' }} />;
}
function PhotoScreen() {
  return (
    <View style={{ flex: 1, backgroundColor: '#000000' }}>
      
      <StatusBar style="light" />
    </View>
  );
}

기준은 단순합니다. 앱 전체에서 대부분 같은 톤이라면 루트에 하나만 두고 끝내면 됩니다. 반대로 특정 화면만 배경색이나 몰입감이 크게 다르다면 그 화면 안에서만 따로 선언하면 됩니다. 모든 화면마다 StatusBar를 습관처럼 넣기보다, 기본값에서 벗어나는 화면에만 추가하는 편이 구조를 읽기 쉽습니다.

Safe Area와 플랫폼 차이

설치는 npx expo install expo-status-bar react-native-safe-area-context입니다. app/profile.tsx의 아래 예제는 SafeAreaView가 bottom을 이미 적용하므로 안쪽 View에 insets.bottom을 다시 더하지 않습니다. Android edge-to-edge에서는 상태바 배경색·translucent 속성으로 공간을 확보하는 방식에 의존하지 말고 화면 배경과 inset을 조합합니다. 웹은 이 네이티브 상태바 색 검증 대상이 아닙니다.

Expo Status Bar 사용법: Safe Area와 화면별 설정 적용 흐름을 설명하는 두 번째 본문 이미지

상태바가 예쁘게 보이는 것과 콘텐츠가 안전하게 배치되는 것은 다른 문제입니다. Safe Area는 노치, 상태바, 홈 인디케이터 같은 시스템 영역을 피해 콘텐츠를 배치하기 위한 개념입니다. 그래서 상단 아이콘 색을 바꾸려면 StatusBar를 보고, 헤더나 버튼이 가려지지 않게 하려면 SafeAreaView나 useSafeAreaInsets를 봐야 합니다.

상태바 스타일은 StatusBar로, 여백은 Safe Area로 분리하기

코드 위치: src/app/profile.tsx. 앞에서 전체 파일로 명시한 경우를 제외하면 해당 구조를 설명하는 부분 예제입니다. 서로 다른 예제의 export default를 한 파일에 합치지 않습니다.

import { Text, View } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { SafeAreaView } from 'react-native-safe-area-context';

export default function ProfileScreen() {
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: '#f9fafb' }}>
      <StatusBar style="dark" />
      <View style={{ paddingTop: 12, paddingHorizontal: 20, paddingBottom: 16 }}>
        <Text>프로필 화면</Text>
      </View>
    </SafeAreaView>
  );
}

이런 식으로 분리하면 책임이 선명해집니다. StatusBar는 상단 시스템 텍스트가 밝을지 어두울지 정하고, SafeAreaView는 화면 바깥쪽 위험 구간을 피하게 해줍니다. 하단 고정 버튼처럼 섬세한 보정이 필요할 때는 useSafeAreaInsets로 값을 직접 더하는 편이 더 유연합니다.

특히 Android에서는 edge-to-edge 기준이 강해지면서, 예전처럼 반투명 상태바만 켜 두고 여백 문제를 대충 넘기기 어려워졌습니다. 게다가 React Native 기본 SafeAreaView는 현재 deprecated로 안내되고 있어서, Expo에서는 react-native-safe-area-context 기준으로 정리하는 편이 더 안전합니다. 그래서 요즘은 Android와 iOS를 따로 외우기보다, 먼저 safe area로 겹침을 막고 그 위에서 status bar style만 조정하는 순서로 생각하면 실수가 줄어듭니다.

결론

expo-status-bar는 상단 시스템 바의 표시 상태를 제어하는 도구이고, 콘텐츠 배치 문제를 직접 해결하는 도구는 아닙니다. 앱 전체 기본값은 루트에 두고, 분위기가 다른 화면만 개별로 덮어쓰며, 겹침 문제는 Safe Area로 분리해서 처리하면 구조가 훨씬 단정해집니다. 이 흐름만 익혀도 상태바 때문에 화면이 어색해 보이거나 상단 여백이 꼬이는 문제를 훨씬 안정적으로 다룰 수 있습니다.

많이 받는 질문

Q. expo-status-bar와 react-native의 StatusBar 중 무엇부터 쓰는 편이 좋나요?
Expo 프로젝트라면 보통 expo-status-bar부터 보는 편이 자연스럽습니다. 인터페이스가 익숙하고 Expo 환경에 맞춘 기본값을 바로 활용할 수 있기 때문입니다.

Q. 상태바를 바꿨는데도 상단 콘텐츠가 노치와 겹칩니다. 왜 그런가요?
상태바 스타일 제어와 안전 영역 보정은 다른 문제이기 때문입니다. 이 경우는 StatusBar보다 SafeAreaView나 useSafeAreaInsets가 필요한 상황입니다.

Q. 모든 화면에 StatusBar를 각각 넣어야 하나요?
그럴 필요는 없습니다. 대부분의 화면이 같은 톤이라면 루트에 기본값 하나만 두고, 특정 화면만 예외적으로 다른 스타일이 필요할 때만 화면 안에 추가하는 편이 관리하기 쉽습니다.

같이 읽으면 좋은 글

문서·검증 기준 (2026-09-13): 해당 기능의 Expo 공식 문서와 코드 구조를 대조했습니다. 설치 버전은 프로젝트 Expo SDK에 맞는 npx expo install 결과와 lockfile을 기준으로 합니다. 이번 개정에서는 실제 Android·iOS 기기 실행, 원격 EAS 빌드, 외부 인증 서버 연동을 수행하지 않았습니다. 본문의 기기 동작은 독자가 확인할 기대 결과입니다.

이 글이 도움이 되었나요?

조회 중

Expo 학습 순서

필수 9개 · 전체 13개

읽음 기록 관리

전체 과정 목차 (13개)
  1. 필수 학습 · Expo 첫 앱 만들기: 설치·프로젝트 생성부터 첫 화면 실행까지
  2. 필수 학습 · Expo Safe Area 사용법: 화면 여백을 안전하게 잡는 방법
  3. 필수 학습 · Expo Status Bar 사용법: Safe Area와 화면별 설정 현재 글
  4. 필수 학습 · Expo vector icons 사용법: 탭바와 커스텀 아이콘 적용하기
  5. 필수 학습 · Expo KeyboardAvoidingView 사용법: 키보드가 화면을 가릴 때 해결하기
  6. 필수 학습 · Expo Router 사용법: app 폴더와 Stack Tabs 구조 잡기
  7. 필수 학습 · Expo WebBrowser 사용법: 외부 링크와 로그인 복귀 처리
  8. 필수 학습 · Expo SecureStore 사용법: 토큰 저장과 생체 인증 처리
  9. 선택 참고 · Expo Location 사용법: 현재 위치와 백그라운드 추적 처리
  10. 선택 참고 · Expo React Native Web 사용법: 앱을 웹으로 확장하기
  11. 선택 참고 · Expo Metro unable to resolve module 오류 해결: 경로와 캐시
  12. 필수 학습 · Expo EAS Build 시작하기: Android APK 빌드부터 설치 확인까지
  13. 선택 참고 · Expo EAS Build 오류 해결: 로컬은 되는데 원격 빌드만 실패할 때

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기