본문 바로가기

App

[React Native] #2-2. 계산기 인터페이스 기초

728x90
반응형
25.02.10

 

이전 포스팅에서는 기본적인 리엑트 네이티브의 프로젝트 초기 설정을 진행했고, props의 객체 구조 분해할당에 대해 언급하고 마무리했다. 잠깐 다시 복습해 보자!

 

[React Native] #2-1. 계산기 만들기 초기 설정

이번 포스팅에서는 간단한 계산기 어플리케이션을 구현해보자. 다음 내용을 다루게 된다.스타일 : StyleSheet로 스타일 변경하기 / 화면 정렬과 방향컴포넌트 : props와 상태 / 커스텀 컴포넌트Hooks: u

udangtangtang-cording-oldcast1e.tistory.com

이전 포스팅 복습

객체 구조 분해 할당(Object Destructuring)은 객체에서 특정 속성만 추출하여 변수에 할당하는 방법이다. 이를 통해 불필요한 객체 참조를 줄이고, 코드의 가독성을 높일 수 있다.

예를 들어, props를 직접 사용하면 다음과 같이 매번 props를 참조해야 한다.

const Button = (props) => {
  console.log(props.title, props.onPress);
  return <Text>{props.title}</Text>;
};

 

그러나 구조 분해 할당을 사용하면 props.title 대신 title, props.onPress 대신 onPress로 직접 접근할 수 있어 코드가 간결해진다.

const Button = ({ title, onPress }) => {
  console.log(title, onPress);
  return <Text>{title}</Text>;
};

 

객체 구조 분해 할당을 사용할 때, 해당 속성이 존재하면 정상적으로 추출되지만 존재하지 않으면 undefined가 된다.

const obj = { a: 1, b: 2, c: 3 };
const { a, b, d } = obj;

console.log(a); // 1
console.log(b); // 2
console.log(d); // undefined (obj 안에 d가 없기 때문)

 

객체에 존재하지 않는 프로퍼티를 추출하려 하면 오류는 발생하지 않지만, 값은 undefined가 된다.

이를 방지하기 위해 기본값을 설정할 수 있다.

const { a, b, d = 4 } = obj;
console.log(d); // 4 (d가 없으므로 기본값 사용)

 

구조 분해 할당이 중요한 이유

 
  1. 코드 가독성 향상 : props.title 대신 title을 바로 사용하여 가독성을 높일 수 있다.
  2. 불필요한 코드 감소 : 객체의 특정 속성을 여러 번 참조할 필요 없이 변수로 추출하여 사용할 수 있다.
  3. 기본값 설정 가능 : 존재하지 않는 속성에 대한 기본값을 제공하여 undefined를 방지할 수 있다.
  4. 매개변수에서 직접 분해 가능 : 함수의 매개변수에서 바로 구조 분해 할당을 적용할 수 있어 더욱 간결한 코드 작성이 가능하다.

3-3. 커스텀 Button 컴포넌트 만들기

propTypes로 타입 설정하기

React에서 propTypes를 설정하는 것은 컴포넌트가 예상하는 props의 타입을 명확히 정의하여 코드의 안정성을 높이는 중요한 과정이다. 특히, 예상과 다른 데이터 타입이 전달될 경우 이를 사전에 감지할 수 있어 디버깅이 용이해진다.

 

이는 여러 명이서 작업을 할 때 더욱 중요한데, 한 사람이 만든 컴포넌트를 다른 팀원이 재사용할 경우 잘못된 타입을 전달하는 문제가 생길 수 있다. 이를 해결하기 위해 propTypes를 사용해야 한다.

 

  • 코드 안정성 보장 : 예기치 않은 데이터 타입이 전달될 경우 경고 메시지를 출력하여 버그를 사전에 방지할 수 있다.
  • 가독성 및 유지보수성 향상 : 다른 개발자가 컴포넌트의 props 타입을 쉽게 이해할 수 있어 협업에 유리하다.
  • 디버깅 및 오류 예방 : propTypes를 통해 잘못된 타입의 props를 감지하면, 애플리케이션 실행 전에 경고 메시지를 받을 수 있다.

prop-types 라이브러리 설치

 

prop-types

Runtime type checking for React props and similar objects.. Latest version: 15.8.1, last published: 3 years ago. Start using prop-types in your project by running `npm i prop-types`. There are 59117 other projects in the npm registry using prop-types.

www.npmjs.com

React에서는 prop-types 패키지를 사용하여 props의 타입을 정의한다. 기본적으로 React에 포함되지 않으므로 별도로 설치해야 한다.

터미널에서 다음 명령어를 실행한다.

npm install prop-types

 

prop-types 라이브러리는 여러 가지 타입 검사 기능을 제공한다. 이를 통해 예상치 못한 데이터 타입이 전달될 경우 경고 메시지를 출력하여 디버깅을 쉽게 하고 코드의 안정성을 높일 수 있다. 설치 후, prop-types를 불러와서 컴포넌트의 propTypes 속성을 설정하면 된다.

import PropTypes from 'prop-types';

const Button = ({ title, onPress }) => {
  return <Button onClick={onPress}>{title}</Button>;
};

// prop-types 설정
Button.propTypes = {
  title: PropTypes.string.isRequired, // 필수 문자열
  onPress: PropTypes.func.isRequired, // 필수 함수
};

export default Button;

prop-types 주요 타입

  • PropTypes.string → 문자열
  • PropTypes.number → 숫자
  • PropTypes.bool → 불리언
  • PropTypes.func → 함수
  • PropTypes.array → 배열
  • PropTypes.object → 객체
  • PropTypes.node → JSX 요소
  • PropTypes.element → React 요소
  • PropTypes.oneOf(['값 1', '값 2']) → 특정 값 중 하나
  • PropTypes.shape({ key: PropTypes.string }) → 특정 객체 형태
상단에 다음과 같은 코드를 추가한다:
import PropTypes from 'prop-types';

 

설치가 완료되면 Button 컴포넌트에 propTypes를 사용해 title의 타입을 지정하자.

title은 "제목"을 일컬으므로 문자열 자료형인 <string>으로 지정해야 한다.

import { TouchableOpacity, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';

// 커스텀 버튼 컴포넌트
const Button = ({ title }) => {
  // console.log(props);
  // return <Text>My Button</Text>;
  return <Text>{title}</Text>;
};
export default Button;

Button.propTypes = {
  title: PropTypes.string,
};

 

여기서 유의할 점은 Button.js에서 propTypes를 설정할 때 오타를 조심해야 한다는 것이다. prototype이 아니라 올바른 설정 방법은 propTypes이다. 대문자를 유의하자!

prop-types 타입 지정

Button.propTypes = {
  title: PropTypes.string,
};

 

  • Button.propTypes 속성을 설정하여 title prop의 타입을 string으로 지정함.
  • 이를 통해 title이 문자열이 아닐 경우 경고 메시지가 출력.
  • propTypes를 사용하면 예상치 못한 데이터 타입이 전달되는 것을 방지하여 코드 안정성을 높일 수 있음.

 

잘못된 데이터 타입이 전달될 경우 콘솔 경고 발생 - 예를 들어, title이 숫자로 전달될 경우: <Button title={123} />

React 개발 모드에서는 다음과 같은 경고 메시지가 출력됨:

Warning: Failed prop type: Invalid prop `title` of type `number` supplied to `Button`, expected `string`.

 

올바른 사용 예시: <Button title="Click me" />

 

React는 propTypes라는 특정 속성을 인식하여 props의 타입을 검사하지만, prototype을 사용하면 이를 인식하지 못한다.

올바르게 설정하였다면 src/App.js를 수정해 보자.

//...생략
const App = () => {
  const isError = true;
  return (
    <View style={styles.container}>
      <StatusBar style="auto" />
      <Text style={styles.text}>Calculate App</Text>
      {/* <Text style={[styles.error, styles.text]}>StyleSheet</Text> */}
      {/* <Text style={[styles.text, isError && styles.error]}>error</Text> */}
      <Button
        // title="My button"
        title={1234}
        onPress={() => console.log('Click!')}
        color="purple"
      >
        {/* style = {{ backgroundColor: 'black', color: 'white' }} */}
      </Button>
    </View>
  );
};
//...생략

 

 

[cmd + s]를 누르지 마자 바로 터미널에 경고메시지가 출력되는 것을 확인할 수 있다!

INFO  
 💡 JavaScript logs will be removed from Metro in React Native 0.77! Please use React Native DevTools as your default tool. Tip: Type j in the terminal to open (requires Google Chrome or Microsoft Edge).
 (NOBRIDGE) ERROR  Warning: Failed prop type: Invalid prop `title` of type `number` supplied to `Button`, expected `string`.
    at Button (http://172.30.1.21:8081/components/Button.bundle//&platform=ios&hot=false&transform.engine=hermes&transform.bytecode=1&transform.routerRoot=app&unstable_transformProfile=hermes-stable&dev=true&minify=false&modulesOnly=true&runModule=false&shallow=true:12:21)
    in App (created by withDevTools(App))
    in withDevTools(App)
    in RCTView (created by View)
    in View (created by AppContainer)
    in RCTView (created by View)
    in View (created by AppContainer)
    in AppContainer
    in main(RootComponent)

 

기존 리액트 네이티브에서 제공하는 Button 컴포넌트는 title props가 필수였다.

이렇게 특정 props가 필수로 전달되어야 한다는 것도 propTypes를 통해 지정이 가능하다. 이때 propTypes에 타입을 지정하고 isRequired를 붙여주면 필수로 전달되어야 하는 props가 된다.

 

다음과 같이 title을 필수로 전달되어야하는 props로 설정해 보자.

/Users/apple/Desktop/RN/rn-calc/components/Button.js
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';

// 커스텀 버튼 컴포넌트
const Button = ({ title }) => {
  // console.log(props);
  // return <Text>My Button</Text>;
  return <Text>{title}</Text>;
};
export default Button;

Button.propTypes = {
  title: PropTypes.string.isRequired,
};

 

코드 저장 후 App 컴포넌트에서 Button 컴포넌트에 전달하고 있는 title을 삭제하면, 'title'은 필수이지만 undifinded가 전달되었다는 메시지가 출력된다. 이를 통해 컴포넌트의 누락을 방지할 수 있다.

defaultProps로 기본값 설정하기

React Native에서 컴포넌트를 만들다 보면 특정 props(속성) 값이 주어지지 않을 경우 기본값을 설정해야 하는 상황이 있다.
이때 defaultProps 를 사용하면, props 값이 없을 때도 컴포넌트가 정상적으로 동작하도록 기본값을 지정할 수 있다.

 

먼저 defaultProps 컴포넌트에서 props 값이 없을 경우, 기본값을 제공하는 기능이다.
예를 들어, 버튼 컴포넌트를 만들었을 때 title 값이 전달되지 않아도 기본 제목을 표시하고 싶다면 defaultProps를 사용하면 된다.

아래와 같이 Button.js 파일에 defaultProps를 설정한다.

/Users/apple/Desktop/RN/rn-calc/components/Button.js
//... 생략
//defaultProps 추가
Button.defaultProps = {
  title: 'button title',
};

 

title props 값이 없을 경우, 기본값으로 "button title"이 자동으로 적용되며, defaultProps 덕분에 컴포넌트가 항상 예상된 값으로 렌더링 된다. 다음으로는 App 컴포넌트에서 Button 컴포넌트를 추가해 title이 전달된 컴포넌트와 아닌 컴포넌트를 비교해 보자.

 

 

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import Button from '../components/Button';

const App = () => {
  const isError = true;
  return (
    <View style={styles.container}>
      <StatusBar style="auto" />
      <Text style={styles.text}>Calculate App</Text>
      {/* <Text style={[styles.error, styles.text]}>StyleSheet</Text> */}
      {/* <Text style={[styles.text, isError && styles.error]}>error</Text> */}
      <Button onPress={() => console.log('Click!')} color="purple">
        {/* style = {{ backgroundColor: 'black', color: 'white' }} */}
      </Button>
      <Button title="title" />
    </View>
  );
}

 

첫 번째 버튼 (title 없음) → "button title" (기본값 적용) : 버튼의 이름을 명시하지 않았음
두 번째 버튼 (title 있음) → "title" (전달된 값 적용) : 버튼 이름을 "title"로 명시

 

👉 즉, title 값을 전달하지 않은 경우 defaultProps에 설정된 "button title"이 출력되는 것을 확인할 수 있다.

 

propTypes를 사용하면 props의 데이터 타입을 검사하고, 필수 여부를 설정할 수 있다. 하지만 defaultProps를 사용하면 propTypes에서 필수 조건을 통과할 수 있다. 그리고 title을 전달하지 않았음에도 불가하고 propTypes의 경고메시지가 나타나지 않는 이유는 defaultProps 때문이다. title을 전달하지 않더라도 defaultProps에서 지정한 문자열이고 필수이라는 조건을 통과하기 때문이다.

 

필수로 전달되지 않아도 되는 props인데 컴포넌트에서 항상 사용하는 props라면 기본값을 설정하는 것이 좋다. 기본값을 설정하면 컴포넌트를 사용하는 사람은 매번 props를 전달하지 않아도 되고 간단하게 사용할 수 있다. 더불어 컴포넌트를 만드는 사람은 props가 전달되지 않아도 문제가 발생하지 않도록 만들 수 있다는 장점이 있다.

터치 컴포넌트로 감싸기

React Native에서는 버튼을 직접 만들 때 Touchable 계열 컴포넌트를 사용한다.
기본적으로 네이티브 버튼 대신 TouchableOpacity, TouchableWithoutFeedback 등을 활용하여 터치 효과를 적용할 수 있다.

 

✅ 주요 Touchable 컴포넌트 종류

컴포넌트 설명
TouchableOpacity 터치 시 투명도가 조절됨
TouchableWithoutFeedback 터치 효과 없음 (이벤트 감지용)
TouchableHighlight 터치 시 배경색 변경
TouchableNativeFeedback 터치 시 물결 효과 (안드로이드 전용)

 

iOS에서는 TouchableOpacity를 주로 사용

Android에서는 TouchableNativeFeedback을 사용할 수도 있음 (iOS에서는 지원하지 않음)

React Native의 기본 Button 컴포넌트도 내부적으로 TouchableOpacity나 TouchableNativeFeedback을 사용한다.


TouchableOpacity

TouchableOpacityReact Native에서 터치(클릭) 이벤트를 감지하고, 터치 시 투명도를 조절하는 컴포넌트이다.

터치 이벤트 감지 버튼이나 링크 같은 역할을 수행할 때 사용됨
투명도 조절 (activeOpacity) 터치 시 투명도가 낮아졌다가 원래대로 돌아옴
클릭 이벤트 (onPress) 지원 버튼 클릭 이벤트 등을 쉽게 설정 가능
다양한 스타일 적용 가능 style을 사용해 자유롭게 디자인 가능

 

기본 사용 코드는 아래와 같다 (예시):

import { TouchableOpacity, Text } from 'react-native';

const MyButton = () => {
  return (
    <TouchableOpacity 
      style={{ backgroundColor: 'blue', padding: 10, borderRadius: 5 }} 
      onPress={() => console.log('Button Pressed')}
      activeOpacity={0.5} // 터치할 때 투명도 설정
    >
      <Text style={{ color: 'white' }}>Click Me</Text>
    </TouchableOpacity>
  );
};

export default MyButton;

 

activeOpacity는 터치 시 투명도를 조절하는 속성이다.
기본값은 0.2이며, 값이 작을수록 더 투명해지고 클수록 덜 투명해진다.

<TouchableOpacity activeOpacity={0.3}> {/* 터치하면 투명도 0.3 */}
<TouchableOpacity activeOpacity={0.8}> {/* 터치하면 투명도 0.8 */}
activeOpacity 값 터치 효과
0.1 매우 투명
0.5 중간 투명
1.0 투명도 변화 없음

 

터치 이벤트는 다음과 같다.

onPress 짧게 터치할 때 실행됨 (일반 클릭)
onLongPress 길게 누를 때 실행됨
onPressIn 터치를 시작할 때 실행됨
onPressOut 터치를 끝낼 때 실행됨

 

버튼을 터치했을 때 투명도가 변경되는 효과를 추가해 보자.
기본적으로 터치하면 투명도가 0.2로 바뀌었다가 손을 떼면 원래 상태로 돌아간다.

import { Text, TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';

const Button = ({ title, onPress }) => {
  return (
    <TouchableOpacity
      style={{ backgroundColor: 'red', padding: 20 }}
      onPress={onPress}
      activeOpacity={0.5} // 터치할 때 투명도 설정
    >
      <Text style={{ color: 'white' }}>{title}</Text>
    </TouchableOpacity>
  );
};

// 기본값 설정
Button.defaultProps = {
  title: 'Click Me!',
  onPress: () => console.log('Button Pressed'), // 기본 클릭 이벤트
};

// PropTypes 정의
Button.propTypes = {
  title: PropTypes.string,
  onPress: PropTypes.func,
};

export default Button;

 

  • TouchableOpacity로 버튼 감싸기
    • 터치하면 투명도가 0.5로 변했다가 원래대로 복귀
    • activeOpacity 값을 조절하여 터치할 때의 투명도를 변경할 수 있음
  • 기본값(defaultProps) 설정
    • title이 없으면 기본값 "Click Me!" 적용
    • onPress가 없으면 기본적으로 "Button Pressed" 로그 출력
  • PropTypes 추가
    • title은 문자열이어야 함 (string)
    • onPress는 함수여야 함 (func)

TouchableHighlight

TouchableHighlightReact Native에서 터치 시 배경색이 변경되는 버튼 역할을 하는 컴포넌트이다.
이 컴포넌트는 기본적으로 버튼을 강조하는 역할을 하며, 터치할 때 시각적인 피드백을 제공한다.

 

주요 기능은 아래와 같다.

터치 이벤트 감지 (onPress) 버튼을 터치할 때 실행되는 이벤트
배경색 변경 (underlayColor) 터치 시 변경될 배경색을 설정
다양한 터치 이벤트 지원 onLongPress, onPressIn, onPressOut 사용 가능

 

📌 TouchableOpacity vs TouchableHighlight 차이점

비교 항목 TouchableOpacity TouchableHighlight
터치 효과 투명도 조절 배경색 변경
사용 용도 부드러운 UI 효과 명확한 버튼 피드백 제공
스타일 자유롭게 적용 가능 underlayColor 사용 가능

 

/Users/apple/Desktop/RN/rn-calc/components/Button.js
import {
  TouchableHighlight, // 터치 시 배경색이 변하는 버튼 역할
  TouchableOpacity, // 터치 시 투명도가 변경되는 버튼 역할 (현재 사용 X)
  Text, // 텍스트 컴포넌트
  StyleSheet, // 스타일을 정의하기 위한 API
  View,
} from 'react-native';
import PropTypes from 'prop-types'; // PropTypes를 사용하여 props의 타입을 검증

// Button 컴포넌트 정의
const Button = ({ title, onPress }) => {
  // 이전 코드: TouchableOpacity 사용 (현재 주석 처리됨)
  // return (
  //   <TouchableOpacity
  //     style={{ backgroundColor: 'red', padding: 20 }}
  //     onPress={onPress}
  //     activeOpacity={0.5} // 터치할 때 투명도 설정
  //   >
  //     <Text style={{ color: 'white' }}>{title}</Text>
  //   </TouchableOpacity>
  // );

  return (
    <TouchableHighlight
      style={styles.button}
      underlayColor="orange"
      onPress={onPress} // props로 전달된 함수 실행
    >
      <Text style={styles.text}>{title}</Text>
    </TouchableHighlight>
  );
};

// 기본값 설정 (defaultProps)
// 부모 컴포넌트에서 props를 전달하지 않았을 때 적용되는 기본값
Button.defaultProps = {
  title: 'Click Me!', // 기본 버튼 제목
  onPress: () => console.log('Button Pressed'), // 기본 클릭 이벤트 (콘솔에 로그 출력)
};

// PropTypes 정의 (props의 데이터 타입을 검증하여 오류 방지)
Button.propTypes = {
  title: PropTypes.string, // title은 문자열 타입이어야 함
  onPress: PropTypes.func, // onPress는 함수 타입이어야 함
};

// 스타일 정의 (StyleSheet 사용)
const styles = StyleSheet.create({
  button: {
    backgroundColor: 'red', // 기본 배경색 빨강
    padding: 20, // 내부 여백
    borderRadius: 5, // 모서리를 둥글게 만듦
  },
  text: {
    color: 'white', // 텍스트 색상: 흰색
    textAlign: 'center', // 텍스트 가운데 정렬
  },
});

// Button 컴포넌트 내보내기 (다른 파일에서 import 가능)
export default Button;

 

  • 버튼을 터치하면 배경색이 orange로 변경되었다가 손을 떼면 원래 색(red)으로 복귀
  • underlayColor 속성을 사용하여 터치 시 배경색을 커스텀 가능
  • TouchableOpacity보다 더 명확한 터치 피드백을 제공하는 방식

React Native에서 TouchableHighlight를 사용하려 할 때, ReferenceError가 발생하는 문제를 겪을 수 있다. 이 글에서는 해당 오류가 발생하는 원인과 해결 방법을 정리하고, 주의해야 할 점을 함께 살펴보겠다.

 

📌 발생한 문제: TouchableHighlight가 존재하지 않는 오류

(NOBRIDGE) ERROR Warning: ReferenceError: Property 'TouchableHighlight' doesn't exist

 

이 오류는 TouchableHighlight가 react-native에서 인식되지 않는 경우 발생한다.

TouchableHighlight는 react-native에서 제공하는 컴포넌트이므로, 반드시 import 해야 한다.

 

 

React Native의 업데이트로 인해 특정 컴포넌트가 변경되었거나, 일부 환경에서 정상적으로 동작하지 않을 수 있다.

사용 중인 React Native 버전이 최신인지 확인하려면 다음 명령어를 실행한다. : npm list react-native

 

 


🔍 문제 발생

주석 처리 전에는 정상적으로 실행되었는데, 주석을 추가한 후 React.Children.only expected to receive a single React element child. 오류가 발생한 이유는 JSX 내부에서 잘못된 위치에 주석을 추가했거나, JSX 요소가 예상과 다르게 해석되었기 때문이다.

 

주석 전 (정상 코드): 이 코드에서는 TouchableHighlight 내부에 Text가 하나만 존재하므로 문제가 발생하지 않았다.

<TouchableHighlight
  style={styles.button}
  underlayColor="orange"
  onPress={onPress} // props로 전달된 함수 실행
>
  <Text style={styles.text}>{title}</Text>
</TouchableHighlight>

 

주석 추가 후 (문제 발생 코드) :

<TouchableHighlight
  style={styles.button}
  underlayColor="orange"
  onPress={onPress} // props로 전달된 함수 실행
>
  {/* 터치 가능한 버튼 내부 텍스트 */}
  <Text style={styles.text}>{title}</Text>
</TouchableHighlight>

위 코드에서는 TouchableHighlight의 자식 요소로 주석이 들어가게 되었고, 이를 React가 새로운 요소로 해석할 수 있다.
즉, TouchableHighlight 내부에 Text 한 개가 아니라 주석 + Text 두 개가 있는 것처럼 해석될 수 있다.

 

불필요한 View와 주석을 제거하고, 다음과 같이 수정하면 해결할 수 있다. :

// React Native에서 터치 가능한 컴포넌트 및 스타일 관련 모듈 import
import {
  TouchableHighlight, // 터치 시 배경색이 변하는 버튼 역할
  TouchableOpacity, // 터치 시 투명도가 변경되는 버튼 역할 (현재 사용 X)
  Text, // 텍스트 컴포넌트
  StyleSheet, // 스타일을 정의하기 위한 API
  View,
} from 'react-native';
import PropTypes from 'prop-types'; // PropTypes를 사용하여 props의 타입을 검증

  return (
    <TouchableHighlight
      style={styles.button}
      underlayColor="orange"
      onPress={onPress} // props로 전달된 함수 실행
    >
      <Text style={styles.text}>{title}</Text>
    </TouchableHighlight>
  );
};

Pressable

 

Pressable · React Native

Pressable is a Core Component wrapper that can detect various stages of press interactions on any of its defined children.

reactnative.dev

 

Pressable은 React Native에서 제공하는 새로운 터치 이벤트 핸들링 컴포넌트다.
이전의 TouchableHighlight 및 TouchableOpacity와 달리, 더 세부적인 터치 이벤트 핸들링이 가능하다는 장점이 있다.

하지만 터치와 관련되어 정해진 효과가 없고 style props에서 터치 여부를 전달받을 수 있다.

 

이를 이용해 style에서 터치 여부에 따라 원하는 스타일을 변경할 수 있다.

단, Pressable 컴포넌트의 style에는 코드가 아닌 함수를 전달 시 파라미터로 터치 여부가 전달되며 전달한 함수에서 스타일 코드를 반환해야 한다.

  • 터치 시작(pressIn), 유지(longPress), 해제(pressOut) 같은 다양한 이벤트 감지
  • onPress뿐만 아니라 터치에 대한 상세한 제어가 가능
  • Touchable* 계열보다 더 가볍고 성능이 우수
컴포넌트 동작 방식 장점 단점
TouchableHighlight 터치 시 배경색이 변경됨 시각적 피드백 제공 스타일 조정이 필요
TouchableOpacity 터치 시 투명도가 낮아짐 자연스러운 애니메이션 효과 배경색 변경 불가
Pressable pressIn, pressOut, longPress 등
세부 제어 가능
유연한 이벤트 처리 가능,
성능이 좋음
단순 스타일 적용 가능

 

Pressable을 이용하면 터치 이벤트를 더 세밀하게 제어할 수 있다. 아래 코드는 인라인 스타일을 적용했다.

import Button from '../components/Button_ Pressable';을 이용해 임포트를 변경했다.

/Users/apple/Desktop/RN/rn-calc/components/Button_ Pressable.js
import { Pressable, Text } from 'react-native';
import PropTypes from 'prop-types';

const Button = ({ title, onPress }) => {
  return (
    <Pressable
      style={({ pressed }) => ({
        backgroundColor: pressed ? 'darkred' : 'red', // 터치 시 색 변경
        padding: 20,
        borderRadius: 5,
      })}
      onPress={onPress} // 기본 클릭 이벤트
      onPressIn={() => console.log('Press In')} // 터치 시작
      onPressOut={() => console.log('Press Out')} // 터치 해제
      onLongPress={() => console.log('Long Press!')} // 길게 누르기
    >
      <Text
        style={{
          color: 'white',
          textAlign: 'center',
        }}
      >
        {title}
      </Text>
    </Pressable>
  );
};

// 기본값 설정
Button.defaultProps = {
  title: 'Press Me!',
  onPress: () => console.log('Button Pressed'),
};

// PropTypes 정의
Button.propTypes = {
  title: PropTypes.string,
  onPress: PropTypes.func,
};

export default Button;

 

터미널에서 버튼의 클릭 상태를 아래와 같이 구분하는 것을 확인 가능하다.

  • onPressIn={() => console.log('Press In')} // 터치 시작     
  • onPressOut={() => console.log('Press Out')} // 터치 해제
  • onLongPress={() => console.log('Long Press!')} // 길게 누르기

press 이벤트

앞선 과정에서 Button 컴포넌트 사용 시, 버튼을 터치 혹은 터치 유지 시에 onPress에 전달한 함수가 호출됨을 확인했다.

onPress 이외에 여러 상황에 맞춰 사용할 수 있는 props가 더 있다. 사실 이 언급을 하기 전에 바로 위 코드에 적용했다.

 

  • onPress: 버튼을 눌렀다가 뗄 때 실행되는 기본 이벤트로, 사용자가 클릭하면 등록된 함수가 실행된다.
  • onPressIn: 사용자가 버튼을 누르는 순간 실행되는 이벤트로, 터치가 시작될 때 발생하며 UI 피드백을 제공하는 데 유용하다.
  • onPressOut: 사용자가 버튼을 눌렀다가 손을 뗄 때 실행되는 이벤트로, onPress와 유사하지만 터치가 해제되는 시점에 발생한다.
  • onLongPress: 버튼을 일정 시간 이상 길게 누르면 실행되는 이벤트로, 일반 클릭과 구별되는 추가적인 동작을 설정할 수 있다.

위 4가지 함수 이외에도 React Native의 Pressable 컴포넌트는 다양한 이벤트를 감지하고 조작할 수 있도록 여러 속성을 제공한다. 이 속성을 활용하면 사용자 경험을 향상하고 터치 인터랙션을 보다 세밀하게 제어할 수 있다. 

elayLongPress – 길게 누르는 시간 조절

onLongPress 이벤트가 실행되기까지의 시간을 조절하는 속성이다. 기본값은 500ms(0.5초)이며, 예를 들어 delayLongPress={1000}으로 설정하면 1초 이상 길게 눌러야 실행된다.

<Pressable 
  onLongPress={() => console.log('Long Press!')}
  delayLongPress={1000} // 1초 이상 길게 눌러야 실행됨
>
  <Text>Long Press Test</Text>
</Pressable>

 

hitSlop – 터치 영역 확장

터치 가능한 영역을 확장할 때 사용되는 속성으로, top, bottom, left, right 값을 각각 지정할 수 있으며, 숫자 하나를 사용하여 모든 방향을 균일하게 설정할 수도 있다.
이 속성을 사용하면 작은 버튼이라도 터치 영역을 넓혀 사용성을 개선할 수 있다.

<Pressable 
  onPress={() => console.log('Button Pressed!')} 
  hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} // 터치 영역 확대
>
  <Text>Press Me</Text>
</Pressable>

disabled – 버튼 비활성화

disabled={true}로 설정하면 버튼이 비활성화되며, 터치 이벤트가 발생하지 않는다. 이를 활용하면 특정 조건에서 버튼을 일시적으로 비활성화할 수 있다.

<Pressable 
  onPress={() => console.log('Button Pressed!')} 
  disabled={true} // 버튼 비활성화
>
  <Text>Disabled Button</Text>
</Pressable>

android_ripple – 안드로이드에서 물결 효과 추가

안드로이드에서는 android_ripple 속성을 사용해 버튼 터치 시 물결 효과를 줄 수 있다. (iOS에서는 이 속성이 적용되지 않음)
이 속성은 color를 설정하여 물결의 색상을 지정할 수 있으며, borderless를 true로 설정하면 둥근 버튼에도 적용된다.

<Pressable 
  onPress={() => console.log('Ripple Effect!')} 
  android_ripple={{ color: 'blue', borderless: false }}
>
  <Text>Android Ripple</Text>
</Pressable>

style – 터치 상태에 따른 동적 스타일 적용

터치 상태에 따라 스타일을 동적으로 변경할 수 있다.
({ pressed }) => (style) 형태의 함수를 사용하면 pressed 상태(터치 중인지 여부)에 따라 버튼의 스타일을 다르게 설정할 수 있다.

<Pressable 
  onPress={() => console.log('Button Pressed!')}
  style={({ pressed }) => ({
    backgroundColor: pressed ? 'darkred' : 'red',
    padding: 20,
    borderRadius: 5,
  })}
>
  <Text style={{ color: 'white', textAlign: 'center' }}>Dynamic Button</Text>
</Pressable>
728x90
반응형
댓글