[App] React Native/Study

React Native Component

유진진 2025. 6. 11. 01:47

https://reactnative.directory/ 에서는 네이티브 기능(android, ios)들을 어떻게 쓸 수 있는지 소개하고 있다. 

https://reactnative.dev/docs/components-and-apis#basic-components 에서는 리액트 네이티브의 컴포넌트에 대해 소개한다. 

 

리액트 네이티브 프로젝트 구조 

https://github.com/sophia22001/React-Native

 

GitHub - sophia22001/React-Native

Contribute to sophia22001/React-Native development by creating an account on GitHub.

github.com

리액트 네이티브 프로젝트를 생성했으면, React Native 앱의 진입점이 되는 `index.js` 를 보자. 

 

index.js

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);
  • `AppRegistry` : 앱의 루트 컴포넌트를 등록하는 모듈로, 어떤 컴포넌트를 렌더링할지를 담당한다.
  • App 컴포넌트를 앱 이름에 등록하여 "MyApp"이라는 이름의 앱이 실행될 때 App 컴포넌트를 화면에 보여준다. 
    • 네이티브 코드(ios 또는  android)가 앱 이름을 기준으로 JS측의 루트 컴포넌트를 찾기 때문이다. 

 

 

App.tsx 

도 간단하게 바꿔줄 것이다. 

 

기존 App.tsx 코드 

더보기
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 */

import React from 'react';
import type {PropsWithChildren} from 'react';
import {
  ScrollView,
  StatusBar,
  StyleSheet,
  Text,
  useColorScheme,
  View,
} from 'react-native';

import {
  Colors,
  DebugInstructions,
  Header,
  LearnMoreLinks,
  ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';

type SectionProps = PropsWithChildren<{
  title: string;
}>;

function Section({children, title}: SectionProps): React.JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';
  return (
    <View style={styles.sectionContainer}>
      <Text
        style={[
          styles.sectionTitle,
          {
            color: isDarkMode ? Colors.white : Colors.black,
          },
        ]}>
        {title}
      </Text>
      <Text
        style={[
          styles.sectionDescription,
          {
            color: isDarkMode ? Colors.light : Colors.dark,
          },
        ]}>
        {children}
      </Text>
    </View>
  );
}

function App(): React.JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  /*
   * To keep the template simple and small we're adding padding to prevent view
   * from rendering under the System UI.
   * For bigger apps the recommendation is to use `react-native-safe-area-context`:
   * https://github.com/AppAndFlow/react-native-safe-area-context
   *
   * You can read more about it here:
   * https://github.com/react-native-community/discussions-and-proposals/discussions/827
   */
  const safePadding = '5%';

  return (
    <View style={backgroundStyle}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={backgroundStyle.backgroundColor}
      />
      <ScrollView style={backgroundStyle}>
        <View style={{paddingRight: safePadding}}>
          <Header />
        </View>
        <View
          style={{
            backgroundColor: isDarkMode ? Colors.black : Colors.white,
            paddingHorizontal: safePadding,
            paddingBottom: safePadding,
          }}>
          <Section title="Step One">
            Edit <Text style={styles.highlight}>App.tsx</Text> to change this
            screen and then come back to see your edits.
          </Section>
          <Section title="See Your Changes">
            <ReloadInstructions />
          </Section>
          <Section title="Debug">
            <DebugInstructions />
          </Section>
          <Section title="Learn More">
            Read the docs to discover what to do next:
          </Section>
          <LearnMoreLinks />
        </View>
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
  },
  highlight: {
    fontWeight: '700',
  },
});

export default App;

기본적인 코드 

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

const App = () => {
  return (
    <View>
      <Text>hello world</Text>
    </View>
  );
};

export default App;

 

 


 

이제부터 리액트 네이티브의 기본적인 컴포넌트와 작동을 알아보자. 

 

 

View 와 Text 

View와 Text는 모든 UI를 구성하는 가장 기본적인 빌딩 블록이다. 

웹 개발에 비유하자면 View는 `div`와 같고, Text는 `p`나 `span`과 같다. 

 

View 의 특징

UI 레이아웃을 위한 컨테이너 역할을 하는 컴포넌트이다.

다른 컴포넌트를 감싸고 그룹화하며, 스타일을 적용해 시각적인 요소를 만들 수 있다.

 

Text 의 특징 

텍스트 내용을 표시하는 데 사용되는 컴포넌트이다.

텍스트를 표시하려면 반드시 Text 컴포넌트 안에 내용을 넣어야 하고, View 안에 직접 텍스트를 넣을 수 없다.

 

 

 

Style 적용하기 

 

- 인라인 스타일

<View style={{width: "100%"}}></View>

 

 

- StyleSheet 스타일: 리액트에서 styled-components와 비슷한 느낌이다. `StyleSheet.create` 로 스타일들을 만든다. 

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

const App = () => {
  return (
    <View style={styles.mainView}>
      <View style={styles.subView}>
        <Text style={styles.mainText}>hello world</Text>
      </View>
      <View style={styles.subView}>
        <Text>hello world</Text>
      </View>
      <View style={styles.anotherSubView}>
        <Text style={styles.mainText}>hello world</Text>
      </View>
    </View>
  );
};

export default App;

const styles = StyleSheet.create({
  mainView: {
    flex: 1,
    // ..
  },
  subView: {
    flex: 1,
    // ..
  },
  anotherSubView: {
    flex: 2,
    // .. 
  },
  mainText: {
    // .. 
  },
});

StyleSheet를 리액트 네이티브에서 import 하고,

아래에 따로 배열 형식의 스타일을 만든다. 여기서 스타일은 카멜케이스로 작성한다. 

 

Flex: 1 ?

flex: 숫자 형식으로 작성하면 이는 화면을 차지하는 비율을 이야기하는 것이다. 

첫번째 View와 두번째 View가 `flex: 1` 이고, 세번째 View는 `flex: 2`라면, 화면에서 1 : 1 : 2 의 비율을 차지하게 된다. 

 

 

 

 

 

TouchEvent

https://reactnative.dev/docs/touchableOpacity

View는 touch에 반응한다. 

 

 

View의 터치이벤트 : TouchableOpacity

View에서 터치 이벤트를 쓰려면 TouchableOpacity의 `onPress`라는 프롭스를 사용해 터치할 때의 이벤트를 나타낸다. 

- `onPress` : 클릭하면 반응한다. 
- `onLongPress` : 클릭을 오래하고 있어야 반응한다.
- `onPressIn` : 클릭한 뒤 바로 반응한다.
- `onPressOut` : 클릭하고 손을 땔 때 반응한다.
<TouchableOpacity onPress={() => Alert.alert('helloworld')}>
	<View>
  		<Text>{props.name}</Text>
	</View>
</TouchableOpacity>

- `TouchableWithoutFeedback` : `TouchableOpacity`는 클릭을 하는 순간 잠깐 투명하게 보이는데, 이건 클릭하는 순간에 투명하게 되지 않게 그대로 보여준다. 

 

Text의 터치이벤트 

Text에서는 다른 것 필요없이 바로 `onPress`를 적용하면 된다. 

<View>
  <Text onPress={() => Alert.alert('helloworld')}>Hello World</Text>
</View>

 

 

 

 

 

Button

https://reactnative.dev/docs/button

버튼은 필수적으로 `title` 프롭스를 갖으며, 이는 버튼의 이름이 된다. 

<View>
  <Button title="Add Number" />
</View>

 

아래 사진처럼 같은 코드인데 ios와 android인지에 따라 기본 버튼 스타일이 다르다. 

 

 

Button에서 터치이벤트

이것도 마찬가지로 `onPress`로 바로 적용하면 된다. 

<Button title="Add Number" onPress={() => add()} />

 

 

 

 

 

ScrollView

https://reactnative.dev/docs/scrollview

화면에 내용이 많아지면 아래로 스크롤 할 수 있도록 만들 수 있다. 

- `onMomentumScrollBegin` : 스크롤이 움직이기 시작했을 때 트리거 
- `onMomentumScrollEnd` : 스크롤의 움직임이 멈췄을 때(즉, 모두 내리거나 모두 올렸을 때) 트리거
- `onScroll` : 스크롤에 움직임이 발생했을 때(조금이라도 움직였으면) 트리거 
- `onContentSizeChange(width, height)` : 스크롤 사이즈가 바뀌면 트리거 
- `bounces` : 스크롤이 끝나면 통통 튀는 효과를 트리거. 기본값은 `true`이다. 
<ScrollView style={{width: '100%'}} onScroll={() => Alert.alert('Scrolling')}>
  <NumList num={state.random} onDelete={onNumDelete} />
</ScrollView>

Simulator Screen Recording - iPhone 16 Pro - 2025-06-10 at 00.04.21.mp4
0.96MB

 

 

 

 

 

 

TextInput

https://reactnative.dev/docs/textinput

앱 화면에서 키보드로 텍스트 값을 입력할 수 있다. 

- `onChangeText` : 입력 값을 받아서 어떤 동작을 수행할 수 있게 해주는 함수 
- `multiline` : `true`이면 입력값이 화면보다 늘어나면 자연스럽게 밑으로 확장시켜준다.(개행)
- `maxLength` : 입력 글자 수 제한 
- `autoCapitalize` : `none` 이면 첫글자가 대문자로 나오지 못하게 한다. 
- `editable` : `false` 이면 입력이 불가능해진다. 
<TextInput
  value={state.myTextInput}
  onChangeText={onChangeInput}
  multiline={true}
/>

 

 

 

 

 

Picker

https://github.com/react-native-picker/picker 을 참고해서 picker을 설치해준다. 

여러가지 옵셥 선택지가 있고 그중 원하는 값을 고를 수 있는 것이다. 

 

설치하기 

npm install @react-native-picker/picker --save

 

ios는 pod 설치도 해줘야한다. 

cd ios
pod install

 

 

Picker 사용하기 

- `onSelectedValue` : 현재 선택된 값 보여주기 
- `onValueChange` : 선택하면 그 `value` 가 적용되게 하기 
import {Picker} from '@react-native-picker/picker';

// ..
<Picker
  selectedValue={country}
  onValueChange={(val, index) => setCountry(val)}>
  <Picker.Item label="Korea" value="korea" />
  <Picker.Item label="Canada" value="canada" />
  <Picker.Item label="Canada2" value="canada2" />
</Picker>

안드로이드에서는 잘 되는데 ios에서 글씨가 안보이는 문제는 찾아봐도 너무 해결이 안되서 일단 냅두기로 했다.. ㅜㅜ 

근데 요즘은 `react-native-picker-select`가 UI 를 바꾸기 쉬워서 더 잘 쓰이는 것 같다. https://github.com/lawnstarter/react-native-picker-select

 

 

 

 

 

Slider

https://github.com/callstack/react-native-slider 를 보고 설치해준다. 

 

설치하기 

npm install @react-native-community/slider --save

 

 

Slider 사용하기 

- `minimumValue` : 최소 값
- `onValueChange` :  변경되는 Slider 값 
- `minimumTrackTintColor` : 앞쪽 slider 선의 색 
- `step`: 숫자 값 

 

import Slider from '@react-native-community/slider';

// ...
<Slider
  value={value}
  minimumValue={0}
  maximumValue={100}
  onValueChange={val => setValue(val)}
  maximumTrackTintColor="red"
  minimumTrackTintColor="blue"
  step={10}
/>

 

 

 

 

 

 

ActivityIndicator

화면을 전환하거나 다운로드를 받을 때, 서버에서 데이터를 받는 중일 때 로딩된다는 빙글빙글 표시를 만들 수 있다. 

- `size` : 로딩 표시의 크기
- `color` : 로딩 표시의 색상
- `animating` : 이 프로퍼티가 true 여야 표시된다. 
<ActivityIndicator
  style={{paddingTop: 200}}
  size="large"
  color="green"
  animating={true}
/>

 

 

 

 

Image

이미지를 화면에 띄울 수 있다. 

- `source` : 표시할 이미지의 위치(URL 또는 로컬 파일 경로)를 지정한다.
- `resizeMode` : 이미지 원본의 크기가 Image 컴포넌트의 지정된 크기(style속성으로 설정한 `width`와 `height`)와 다를 때, 이미지를 어떻게 조정하여 표시할지 결정한다. 
- `onLoadedEnd` : 이미지 로딩이 끝나면 동작을 트리거

 

1. 로컬에서 이미지 가져오기 

import Train from './assets/images/train.jpg';

<Image 
  source={Train} 
  resizeMode="contain"         
  onLoadEnd={() => Alert.alert('Image Loaded!!')}
/>

 

 

resizeMode 

Cover과 Contain의 차이 

- `Cover` : Image 컴포넌트 영역에 이미지를 다 채운다. (기본값) 그래서 어쩔 수 없이 잘리는 부분이 생긴다. 

- `Contain` : Image 컴포넌트 영역 안에 완전히 들어오도록 원본 이미지 전체를 보여준다. 

 

 

2. 서버에서 이미지 가져오기 

서버: https://picsum.photos/ - 사진을 가져올 수 있는 웹주소 제공한다.

 

위 사이트에서 알려주는 주소로 사용해보자. source 에서 `uri`로 주소를 지정해주면 된다.

<Image
  source={{uri: 'https://picsum.photos/id/237/200/300'}}
  resizeMode="contain"
/>

여기서 헷갈릴 수도 있는게 웹사이트에서 사진보다 잘린 것처럼 보이지만,

이미지 주소 url을 보면 `../200/300`으로 되어있는데 이는 `width`와 `height`를 말한다. 즉, 원본이 200, 300 사이즈로 잘려있는 것이다.

 

 

 

 

Modal

화면 가장 위에 표시될 레이어이다. ex> 광고 띄우기, 경고창 띄우기 

Modal은 독립적인 렌더링 계층을 갖으며, 부모 View의 자식으로 존재하지만, 실제로는 화면의 최상단에 띄워지는 팝업과 같은 역할을 하는 것이다. 

- `visible` : `true`로 설정되면 Modal 내용이 보이게 된다.
- `animationType` : 어떤 방식으로 모달이 띄워질지를 결정한다. `slide`, `fade` 방식이 있으며 기본값은 `none`이다.
- `onShow` : 모달이 띄워졌을 때 트리거된다. 
<View>
  <Button title="Open Modal" onPress={handleModal} />

  <Modal
    visible={modal}
    animationType="slide"
    onShow={() => Alert.alert('Warning@')}>
    <View style={{marginTop: 60, backgroundColor: 'red'}}>
      <Text>This is modal content</Text>
    </View>
    <Button title="Go Back" onPress={handleModal} />
  </Modal>
</View>

 

 

버튼을 누르면 모달로 넘어가는데 여기서 버튼은 사라진 것 처럼 보인다. 

버튼 컴포넌트가 사라지는 것이 아니라, Modal 컴포넌트가 화면 전체를 덮어버려서 기존에 있던 Button 컴포넌트가 가려지는 것이다.

Simulator Screen Recording - iPhone 16 Pro - 2025-06-11 at 01.22.20.mp4
0.62MB

 

 

 

 

'[App] React Native > Study' 카테고리의 다른 글

React Navigation (3) - Tab  (1) 2025.06.20
React Navigation (2) - Drawer  (0) 2025.06.20
React Navigation (1) - Stack  (3) 2025.06.19
ios & Android Simulator 구동하기  (5) 2025.05.21
React Native 소개 및 개발 환경 구축  (1) 2025.05.21