React Native의 스타일링은 CSS와 비슷하지만, Flexbox 기본값이 다르고 단위 체계도 다릅니다.

웹 CSS를 알고 있으면 React Native 스타일링에 빠르게 적응할 수 있습니다. 하지만 미묘한 기본값 차이 때문에 의도하지 않은 레이아웃이 나오는 경우가 많습니다.


StyleSheet.create()

TSX
import { View, Text, StyleSheet } from 'react-native';

function StyledComponent() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>스타일 적용</Text>
      {/* 여러 스타일 합성 */}
      <Text style={[styles.text, styles.bold]}>합성된 스타일</Text>
      {/* 인라인 스타일 혼합 (뒤의 스타일이 우선) */}
      <Text style={[styles.text, { color: 'red' }]}>인라인 혼합</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
  },
  text: {
    fontSize: 16,
    color: '#333',
  },
  bold: {
    fontWeight: 'bold',
  },
});

StyleSheet.create()를 쓰는 이유

  • **유효성 검사 **: 잘못된 스타일 속성을 빌드 타임에 잡아줌
  • ** 성능 **: 스타일 객체가 한 번만 생성되어 참조로 전달
  • ** 가독성 **: 스타일을 컴포넌트 로직과 분리
TSX
// 조건부 스타일 적용 패턴
function ConditionalStyle({ isActive }: { isActive: boolean }) {
  return (
    <View style={[
      styles.button,
      isActive && styles.activeButton,
    ]}>
      <Text style={[
        styles.buttonText,
        isActive && styles.activeText,
      ]}>
        버튼
      </Text>
    </View>
  );
}

웹 CSS와의 차이점

단위 체계

TSX
const styles = StyleSheet.create({
  box: {
    // 단위 없이 숫자만 사용 — dp(density-independent pixels)
    width: 100,        // 100dp
    height: 50,        // 50dp
    fontSize: 16,      // 16dp

    // 퍼센트도 사용 가능
    width: '80%',

    // px, rem, em, vh, vw 등은 사용 불가!
  },
});

지원하지 않는 CSS 속성

  • float — 사용 불가, Flexbox로 대체
  • display: grid — 사용 불가
  • display: inline — 사용 불가 (flex만 지원)
  • CSS 애니메이션 — Animated API 사용
  • 의사 클래스 (:hover, :focus) — 사용 불가
  • 미디어 쿼리 — Dimensions API 또는 useWindowDimensions 사용

속성명 차이

TSX
const styles = StyleSheet.create({
  example: {
    // 웹: background-color → React Native: backgroundColor
    backgroundColor: '#fff',

    // 웹: border-radius → React Native: borderRadius
    borderRadius: 8,

    // 웹: box-shadow → React Native: 플랫폼별 다름
    // iOS
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
    // Android
    elevation: 5,
  },
});

Flexbox 레이아웃

기본값 차이 (가장 중요!)

속성웹 기본값React Native 기본값
flexDirectionrowcolumn
alignContentstretchflex-start
flexShrink10

이 기본값 차이를 모르면 레이아웃이 예상과 다르게 나옵니다. 특히 flexDirection: 'column'이 기본이라는 점, flexShrink: 0이라 콘텐츠가 넘쳐도 자동으로 줄어들지 않는다는 점을 기억하세요.

flex 속성

TSX
function FlexExample() {
  return (
    <View style={{ flex: 1 }}>
      {/* 1:2:1 비율로 공간 분배 */}
      <View style={{ flex: 1, backgroundColor: 'red' }} />
      <View style={{ flex: 2, backgroundColor: 'blue' }} />
      <View style={{ flex: 1, backgroundColor: 'green' }} />
    </View>
  );
}

justifyContent (주축 정렬)

TSX
// flexDirection: 'column'일 때 세로 방향 정렬
const styles = StyleSheet.create({
  // 위에서부터 배치
  start: { justifyContent: 'flex-start' },
  // 아래에서부터 배치
  end: { justifyContent: 'flex-end' },
  // 중앙 배치
  center: { justifyContent: 'center' },
  // 균등 분배 (양 끝 여백 없음)
  between: { justifyContent: 'space-between' },
  // 균등 분배 (양 끝 여백 있음)
  around: { justifyContent: 'space-around' },
  // 완전 균등 분배
  evenly: { justifyContent: 'space-evenly' },
});

alignItems (교차축 정렬)

TSX
// flexDirection: 'column'일 때 가로 방향 정렬
const styles = StyleSheet.create({
  stretch: { alignItems: 'stretch' },     // 늘려서 채움 (기본값)
  start: { alignItems: 'flex-start' },    // 왼쪽 정렬
  end: { alignItems: 'flex-end' },        // 오른쪽 정렬
  center: { alignItems: 'center' },       // 가운데 정렬
});

실전 레이아웃 패턴

TSX
// 패턴 1: 화면 중앙 배치
const centered = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

// 패턴 2: 헤더 - 본문 - 푸터
const layout = StyleSheet.create({
  container: { flex: 1 },
  header: { height: 60 },         // 고정 높이
  body: { flex: 1 },              // 나머지 공간 차지
  footer: { height: 80 },         // 고정 높이
});

// 패턴 3: 가로 배치 + 간격
const row = StyleSheet.create({
  container: {
    flexDirection: 'row',
    gap: 12,                       // RN 0.71+에서 gap 지원
  },
  item: {
    flex: 1,
  },
});

// 패턴 4: 절대 위치 (배지, 오버레이 등)
const badge = StyleSheet.create({
  container: {
    position: 'relative',
  },
  badge: {
    position: 'absolute',
    top: -5,
    right: -5,
    width: 20,
    height: 20,
    borderRadius: 10,
    backgroundColor: 'red',
  },
});

flexWrap과 gap

TSX
// 태그 목록처럼 줄바꿈이 필요한 레이아웃
function TagList({ tags }: { tags: string[] }) {
  return (
    <View style={styles.tagContainer}>
      {tags.map((tag) => (
        <View key={tag} style={styles.tag}>
          <Text style={styles.tagText}>{tag}</Text>
        </View>
      ))}
    </View>
  );
}

const styles = StyleSheet.create({
  tagContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap',        // 자동 줄바꿈
    gap: 8,                  // 아이템 간격
  },
  tag: {
    paddingHorizontal: 12,
    paddingVertical: 6,
    backgroundColor: '#e0e0e0',
    borderRadius: 16,
  },
  tagText: {
    fontSize: 14,
  },
});

반응형 스타일링

TSX
import { useWindowDimensions, StyleSheet, View } from 'react-native';

function ResponsiveLayout() {
  const { width } = useWindowDimensions();
  const isTablet = width >= 768;

  return (
    <View style={[
      styles.container,
      { flexDirection: isTablet ? 'row' : 'column' },
    ]}>
      <View style={[styles.sidebar, isTablet && { width: 250 }]} />
      <View style={styles.content} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  sidebar: { backgroundColor: '#f0f0f0' },
  content: { flex: 1 },
});

그림자 (플랫폼별 처리)

TSX
import { Platform, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  card: {
    backgroundColor: 'white',
    borderRadius: 8,
    padding: 16,
    // iOS 그림자
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.1,
        shadowRadius: 4,
      },
      // Android 그림자
      android: {
        elevation: 4,
      },
    }),
  },
});

정리

  • StyleSheet.create()로 스타일을 분리하면 성능과 가독성이 좋아집니다
  • React Native Flexbox의 기본값은 웹과 다릅니다: flexDirection: 'column', flexShrink: 0
  • 단위는 dp(숫자)와 %만 사용 가능합니다
  • 그림자는 iOS와 Android에서 다르게 처리해야 합니다
  • gap 속성은 RN 0.71+에서 지원됩니다
댓글 로딩 중...