React Native의 컴포넌트 정리 (2. Button, Pressible, Touchable)

eeensu·2026년 2월 14일

React Native

목록 보기
7/38

Button

React Native가 기본으로 제공하는, 시스템 표준 스타일의 버튼이다.

특징

  • Android : 꽉 찬 배경색(Filled) 버튼으로 렌더링 된다. (Material Design)
  • iOS : 배경색 없이 파란색 글자만 있는 버튼으로 렌더링 된다. (Apple Design)
  • 장점 : 코드가 엄청 짧다. title, onPress만 넣으면 끝이다.
  • 단점 : 스타일 수정이 거의 불가능하다. 글자 크기, 패딩, 배경색 등 전혀 불가능하다.

실무에선 잘 사용하지 않으며, 기능 테스트용으로만 권장한다.

<Button
  title="나를 눌러줘"
  color="#841584"
  onPress={() => console.log('눌림')}
/>



Touchable 계열

Pressable이 나오기 전까지(RN 0.62 이전) RN의 표준이었다. 뷰(View)를 감싸서 터치 이벤트를 처리한다.

종류

  1. TouchableOpacity (가장 많이 씀)
    누르면 투명도(opacity)가 살짝 낮아지며 "눌렸다"는 피드백을 준다. iOS/Android 둘 다 무난하다.

  2. TouchableHighlight
    누르면 배경색이 어두워지거나 바뀐다. 지저분해서 잘 쓰진 않는다.

  3. TouchableNativeFeedback
    Android 전용. 물결 효과(Ripple)가 생긴다. (iOS 에러 방지 처리 귀찮음)

  • 장점 : activeOpacity 속성 하나로 누르는 느낌을 아주 쉽게 낼 수 있다.
  • 단점 : 여러 컴포넌트로 나뉘어 있어 파편화가 심하며 현재 RN 아키텍처(Fabric)에서 Pressable보다 성능 최적화가 덜 되어 있다.
import { TouchableOpacity, Text } from 'react-native';

<TouchableOpacity
  activeOpacity={0.8} // 누를 때 투명도 80%
  onPress={() => console.log('터치됨')}
  style={{ backgroundColor: 'blue', padding: 10 }}
>
  <Text style={{ color: 'white' }}>커스텀 버튼</Text>
</TouchableOpacity>



Pressable

React Native 0.63부터 도입된 차세대 터치 핸들러다. 기존의 Touchable 시리즈를 통합하고 대체하기 위해 나왔다.

특징

  • 상태 기반 스타일링 : pressed라는 상태 값을 실시간으로 제공한다. 이를 통해 "눌렸을 때만 빨간색", "눌렸을 때만 글자 커짐" 같은 로직을 CSS(Style) 안에서 바로 짤 수 있다.
  • 플랫폼 통합 : Android의 물결 효과(android_ripple)를 속성 한 줄로 지원한다.
  • 정밀한 제어 : onLongPress(길게 누르기), delayLongPress(시간 조절), hitSlop(터치 영역 확장) 등의 기능이 있다.
  • 장점 : 하나의 컴포넌트로 모든 터치 인터랙션을 구현할 수 있다.
  • 단점 : TouchableOpacity처럼 기본 투명도 효과가 없어서, 직접 스타일 코드를 짜야 한다. (초기 설정이 조금 더 길다)
import React from 'react';
import { Pressable, Text, ActivityIndicator, StyleSheet, Platform } from 'react-native';

// 1. 디자이너가 준 스펙대로 타입 정의
type ButtonVariant = 'primary' | 'secondary' | 'outline';

interface CustomButtonProps {
  title: string;
  onPress: () => void;
  variant?: ButtonVariant; // 버튼 스타일 (기본값 primary)
  disabled?: boolean;      // 비활성화 여부
  isLoading?: boolean;     // API 호출 중 로딩 표시
}

export default function CustomButton({
  title,
  onPress,
  variant = 'primary',
  disabled = false,
  isLoading = false,
}: CustomButtonProps) {
  
  return (
    <Pressable
      // 2. 로딩 중이거나 비활성화면 클릭 막기
      onPress={disabled || isLoading ? undefined : onPress}
      
      // 3. 안드로이드 물결 효과 (디자이너가 좋아함)
      android_ripple={{ color: 'rgba(255, 255, 255, 0.3)' }}
      
      // 4. 상태에 따른 스타일링 (Pressable의 핵심 기능!)
      style={({ pressed }) => [
        styles.container,
        styles[variant], // primary, secondary 등 스타일 적용
        pressed && styles.pressed, // 눌렸을 때 효과 (iOS용)
        disabled && styles.disabled, // 비활성화 스타일
      ]}
    >
      {/* 5. 로딩 중이면 텍스트 대신 스피너 돌리기 */}
      {isLoading ? (
        <ActivityIndicator color={variant === 'outline' ? '#007AFF' : 'white'} />
      ) : (
        <Text style={[
          styles.text, 
          variant === 'outline' && styles.textOutline
        ]}>
          {title}
        </Text>
      )}
    </Pressable>
  );
}

// 6. 스타일 정의 (피그마 값 그대로 옮김)
const styles = StyleSheet.create({
  container: {
    paddingVertical: 14,
    paddingHorizontal: 24,
    borderRadius: 8,
    alignItems: 'center',
    justifyContent: 'center',
  },
  // 변형(Variant) 스타일
  primary: { backgroundColor: '#007AFF' },
  secondary: { backgroundColor: '#5856D6' },
  outline: { 
    backgroundColor: 'transparent', 
    borderWidth: 1, 
    borderColor: '#007AFF' 
  },
  // 상태(State) 스타일
  pressed: {
    opacity: Platform.OS === 'ios' ? 0.7 : 1, // iOS는 투명도, 안드는 ripple
  },
  disabled: {
    backgroundColor: '#A0A0A0', // 회색 처리
  },
  // 텍스트 스타일
  text: {
    color: 'white',
    fontWeight: '600',
    fontSize: 16,
  },
  textOutline: {
    color: '#007AFF',
  },
});
profile
안녕하세요! 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글