React Native가 기본으로 제공하는, 시스템 표준 스타일의 버튼이다.
실무에선 잘 사용하지 않으며, 기능 테스트용으로만 권장한다.
<Button
title="나를 눌러줘"
color="#841584"
onPress={() => console.log('눌림')}
/>
Pressable이 나오기 전까지(RN 0.62 이전) RN의 표준이었다. 뷰(View)를 감싸서 터치 이벤트를 처리한다.
TouchableOpacity (가장 많이 씀)
누르면 투명도(opacity)가 살짝 낮아지며 "눌렸다"는 피드백을 준다. iOS/Android 둘 다 무난하다.
TouchableHighlight
누르면 배경색이 어두워지거나 바뀐다. 지저분해서 잘 쓰진 않는다.
TouchableNativeFeedback
Android 전용. 물결 효과(Ripple)가 생긴다. (iOS 에러 방지 처리 귀찮음)
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>
React Native 0.63부터 도입된 차세대 터치 핸들러다. 기존의 Touchable 시리즈를 통합하고 대체하기 위해 나왔다.
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',
},
});