본문 바로가기
대외활동/Draconist-프론트

REACT NATIVE 스터디. 1주차 3단원. 컴포넌트 DRACONIST

by 피스타0204 2025. 1. 16.

1. JSX

컴포넌트는 가상 DOM을 구성하는 조립블록입니다. 재사용이 가능한 화면 UI요소이므로 잘 이용하면 사용자 편의를 상승시키고 개발도 편하게 할 수 있습니다.

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

const App = () => {
    return (
        <View style={styles.container}>
            <Text style={styles.title}>My First React Native</Text>
            <StatusBar style="auto"/>
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        flex:1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: "ffffff",
    },
    title: {
        fontSize: 30,
    }
})

export default App;

 

1) 하나의 부모

컴포넌트는 객체 생성과 함수 호출을 위한 문법적 편의를 제공하기 위해 만들어진 확장 기능으로 리액트 프로젝트에서 사용됩니다.

JSX에서는 여러 개의 요소를 표현할 경우 반드시 하나의 부모로 감싸야 한다는 특징이 있습니다.

 

\

 

태그 설명)

더보기

+) View

view는 UI를 구성하는 가장 기본적인 요소로 div와 비슷한 역할을 하는 컴포넌트입니다.

 

+) Fragment

컴포넌트를 반환할 때 View 컴포넌트처럼 특정 역할을 하는 컴포넌트로 감싸지 않고, 여러개의 컴포넌트를 반환하고 싶을때 사용합니다.

Fragment 컴포넌트는 import해서 사용할 수도 있고,

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

const App = () => {
  return (
    <Fragment>
      <Text style={styles.text}>Hello,</Text>
      <Text style={styles.text}>World!</Text>
    </Fragment>
  );
};

const styles = StyleSheet.create({
  text: {
    fontSize: 24,
    textAlign: 'center',
    margin: 10,
  },
});

export default App;
더보기

 

단축 문법으로 사용할 수도 있습니다.

import React from 'react';
import { Text } from 'react-native';

const App = () => {
  return (
    <>
      <Text>Hello,</Text>
      <Text>World!</Text>
    </>
  );
};

export default App;
더보기

 

 

+) statusBar

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

const App = () => {
    return (
        <View style={styles.container}>
            <Text style={styles.title}>My First React Native</Text>
            <StatusBar backgroundColor="#001234" barStyle="light-content" animated={true} />
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        flex:1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: "ffffff",
    },
    title: {
        fontSize: 30,
    }
})

export default App;

 

2) 자바스크립트 변수

JSX는 내부에서 자바스크립트 변수를 전달하여 사용할 수 있습니다.

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

const App = () => {
    const name ='warigari';
    return (
        <View style={styles.container}>
            <Text style={styles.title}>My Name is {name}</Text>
            <StatusBar style="auto"/>
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        flex:1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: "ffffff",
    },
    title: {
        fontSize: 30,
    }
})

export default App;

3) 자바스크립트 조건문

-if문

JSX내부에서 if문을 사용할 수 있지만, 반드시 즉시 실행함수(임시함수)로 작성해야 합니다. 뒤에 () 완전 주의!!!

        <View style={styles.container}>
            <Text style={styles.title}>
                {(()=>{
                    if(name==='tlqkf') return 'it\'s illegal name';
                    else if(name==='warigari') return 'it is ohuek song';
                    else return `that is your name, ${name}`;
                })()}
            </Text>
            <StatusBar style="auto"/>
        </View>

 

-삼항연산자

JSX는 내부에서 삼항 연산자를 사용할 수 있습니다.

        <View style={styles.container}>
            <Text style={styles.title}>
            { name === 'warigari' 
                    ? 'it is ohuek song' 
                    : `that is your name, ${name}`}
            </Text>
            <StatusBar style="auto"/>
        </View>

 

-AND OR 연산자

중괄호는 JSX에서 JavaScript 표현식을 삽입할 때 사용하고, 소괄호는 여러 JSX 요소를 묶어야 할때 사용합니다.

 

&& 연산자는 왼쪽 조건이 참 (true)일 때만 오른쪽 값을 평가하고 반환합니다.

JSX에서 false는 렌더링 되지 않기 때문에 AND 연산자 앞의 조건이 참일 떄 뒤의 내용이 나타나고 거짓인 경우는 나타나지 않습니다.

{ name === 'warigari' && (<Text style={styles.title}>My Name is {name}</Text>) }

 

|| 연산자는 왼쪽 조건이 거짓(falsy)일 때만 오른쪽 값을 평가하고 반환합니다. JSX에서 false, null, undefined, 0, 빈 문자열('') 등은 화면에 렌더링되지 않기 때문에, || 연산자는 왼쪽 값이 거짓일 경우 오른쪽 내용을 나타내고, 참(truthy)일 경우 왼쪽 값을 그대로 반환합니다.

<Text style={styles.title}>{name || "이름이 설정되지 않았습니다."}</Text>

 

 

4) null과 undefined

JSX는 null은 허용하지만 undefined는 오류가 발생합니다.

//화면 안 보임
export default function App(){
	return null;
}

//오류가 발생
export default function App(){
	return undefined;
}

 

 

5) JSX의 주석

JSX의 주석은 /* */를 이용해서 작성합니다.

하지만 주석도 태그 안에서는 {/*  */} 이렇게 작성해야 한다는 것을 알아야 합니다.

        <View style={styles.container}>
            {/*주석 */}
            { name === 'warigari' && (<Text style={styles.title}>My Name is {name}</Text>)}
            <StatusBar style="auto"/>
        </View>

 

6) 인라인 스타일링

JSX에서는 style에 문자열이 아닌 객체 형태로 입력해야 합니다.

        <View style={{
            flex:1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: "ffffff",
        }}>
            { name === 'warigari' && (<Text style={styles.title}>My Name is {name}</Text>)}
            <StatusBar style="auto"/>
        </View>

 

아래와 같이 작성하면 오류가 발생합니다.

<Text style="color: red; font-size: 20px;">Hello</Text> // 오류!

2. 컴포넌트

컴포넌트란 재사용이 가능한 조립블록으로 화면에 나타나는 UI요소입니다. App.js도 App이라는 컴포넌트입니다. 

하지만 컴포넌트는 단순 UI역할(레이아웃)만 하는 것이 아니라 데이터에 영향을 받습니다. 부모로부터 받은 속성(props)이나 자신의 상태(state)에 따라 표현이 달라지고 다양한 기능을 수행합니다. 

 

1) 내장 컴포넌트

리액트 네이티브에는 다양한 내장 컴포넌트(core component)들이 제공됩니다. View컴포넌트나 Text 컴포넌트가 그렇습니다.

 

-Button

Button 컴포넌트의 color속성은 iOS에서는 텍스트 색을 나타내는 값이지만 안드로이드에서는 바탕색을 나타냅니다. 따라서 여러 플랫폼에서 테스트를 하는 습관을 가지는 것이 중요합니다.

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

const App = () => {
    const name ='warigari';
    return (
        <View style={{
            flex:1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: "ffffff",
        }}>
            <Text style={{fontSize: 30, marginBottom: 10}}>Button component</Text>
            <Button title="Button" onPress={()=>Alert('click !!!')}/>
            <StatusBar style="auto"/>
        </View>
    )
}

export default App;

 

 

2) 커스텀 컴포넌트 만들기

아까 Button컴포넌트는 iOS와 안드로이드에서 다른 모습으로 겐더링된다는 단점이 있었습니다. 이 단점을 보완하기 위해 TouchableOpacity컴포넌트(리액트 네이티브 0.63이상에서는 Pressable로 대체) 와 Text컴포넌트를 이용해 MyButton 컴포넌트를 만들어봅시다.

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

const MyButton = () => {
  return (
    <TouchableOpacity>
        <Text style={{fontSize: 24}}>My Button</Text>
    </TouchableOpacity>
  )
}

export default MyButton

 

import React from 'react'
import { Alert, Text, TouchableOpacity } from 'react-native'

const MyButton = () => {
  return (
    <TouchableOpacity
    style={{
        backgroundColor: '#3498db',
        padding:16,
        margin: 10,
        borderRadius: 8,
    }}
        onPress={()=>Alert.alert('click!!!')}
    >
        <Text style={{fontSize: 24, color: 'white'}}>My Button</Text>
    </TouchableOpacity>
  )
}

export default MyButton

 

 

 


3. props & states

props는 properties를 줄인 표현으로 부모컴포넌트로부터 상속받은 속성값을 뜻합니다. 

 

1) title로 props 전달하고 사용하기

title속성에 상속할 부모를 부여하면 props가 전달됩니다.

import React from "react";
import {View, StyleSheet, Text, StatusBar, Button, Alert} from 'react-native';
import MyButton from "./components/MyButton";

const App = () => {
    const name ='warigari';
    return (
        <View style={{
            flex:1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: "ffffff",
        }}>
            <Text style={{fontSize: 30, marginBottom: 10}}>Button component</Text>
            <MyButton title="Button"/>
            <StatusBar style="auto"/>
        </View>
    )
}

export default App;

 

ctrl + m 을 눌러 디버그 콘솔을 키고 내용을 확인해보자

 

 

App 컴포넌트에서 MyButton컴포넌트로 지정된 타이틀이 전달되었습니다.


import React from "react";
import {View, StyleSheet, Text, StatusBar, Button, Alert} from 'react-native';
import MyButton from "./components/MyButton";

const App = () => {
    const name ='warigari';
    return (
        <View style={{
            flex:1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: "ffffff",
        }}>
            <Text style={{fontSize: 30, marginBottom: 10}}>Button component</Text>
            <MyButton title="Button"/>
            <MyButton title="Click Me" /> 
            <StatusBar style="auto"/>
        </View>
    )
}

export default App;

 

React에서 컴포넌트를 렌더링할 때마다, 컴포넌트는 새로운 객체로 생성됩니다. 컴포넌트 자체는 함수이기 때문에, 이를 호출할 때마다 새로운 실행 컨텍스트가 만들어지고, 그에 따라 새로운 React 요소 객체가 반환됩니다.

따라서 상속을 한다고 그 자식 컴포넌트 자체의 속성을 바꾸지 않고, 매번 부를 때마다 만들어지는 각 객체의 속성을 바꿉니다.

 

2) 태그 안의 값도 props로 전달된다.

태그 안에 입력되는 값도 전달할 수 있습니다.

 

그렇기 때문에 상속받은 속성들을 자식 컴포넌트에서 사용할 수 있습니다. 

 

3) defaultprops

만약 아무것도 props로 전달하지 아무것도 전달하지 않으면 다음과 같이 뜹니다.

 

컴포넌트를 잘못 파악해 꼭 전달되어야 하는 중요한 값이 전달되지 않는 경우가 발생하지 않도록, defaultProps 를 지정해줄 수 있습니다. 

import React from 'react'
import { Alert, Text, TouchableOpacity } from 'react-native'

const MyButton = props => {

    console.log(props);
  return (
    <TouchableOpacity
    style={{
        backgroundColor: '#3498db',
        padding:16,
        margin: 10,
        borderRadius: 8,
    }}
        onPress={()=>Alert.alert('click!!!')}
    >
        <Text style={{fontSize: 24, color: 'white'}}>{props.children || props.title}</Text>
    </TouchableOpacity>
  )
};

MyButton.defaultProps  = {
    title:"Button"
}

export default MyButton

 

하지만 앞으로의 주요 버전에서 defaultProps의 사용이 제거될 예정이므로, defaultProps를 사용하는 대신, 함수형 컴포넌트의 기본 매개변수 기능을 사용하는 방식으로 변경하는 것을 추천합니다.

 

위 코드에서는 props를 구조 분해 할당 방식으로 받아오고, 기본 값을 **title = "Button"**으로 설정했습니다.

 

4) propTypes

프로젝트 크기가 커지면 컴포넌트끼리 props를 전달할 때 잘못된 타입으로 전달하거나 값을 전달하지 않아서 문제가 생길 수 있습니다. 이런 상황에 잘못된  props가 전달되었다는 것을 경고 메시지를 통해 알리는 방법으로 PropTypes를 사용하는 방법이 있습니다. 터미널에 다음 명령어를 쳐서 라이브러리를 추가해봅시다.

#안뜨면 npm run android로 확인해야 한다는 단점이 있다

pm install prop-types

 

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

const MyButton = ({ title = "Button", children })  => {
  return (
    <TouchableOpacity
    style={{
        backgroundColor: '#3498db',
        padding:16,
        margin: 10,
        borderRadius: 8,
    }}
        onPress={()=>Alert.alert('click!!!')}
    >
        <Text style={{fontSize: 24, color: 'white'}}>{children || title}</Text>
    </TouchableOpacity>
  )
};


MyButton.propTypes = {
    title: PropTypes.number,
}

export default MyButton

이렇게 props의 타입이 잘못되었다는 오류가 뜹니다.


또 이렇게 필수로 제공되어야 하는  속성값을 지정할 수도 있습니다. 아래 코드에서는 name이 전달되지 않아 오류가 발생합니다.

title은 기본값을 전달했기 때문에 오류가 발생하지 않습니다.

MyButton.propTypes = {
    title: PropTypes.string.isRequired,
    name: PropTypes.string.isRequired,
}

 


이외에도 func(함수), object(객체), array(배열)등 다양한 타입을 지정할 수 있습니다.

//App.js
//...중략
<MyButton title="Button"  onPress={()=>Alert.alert('click!!!')}></MyButton>


//MyButton.js
//...중략
MyButton.propTypes = {
    title: PropTypes.string.isRequired,
 onPress: PropTypes.func.isRequired,
}

5) state

props는 부모 컴포넌트에서 받은 값으로 변경할 수 없는 반면, state는 컴포넌트 내부에서 생성되고 값을 변경할 수 있습니다. 이를 토해 컴포넌트의 상태를 관리합니다. 이 상태값이 변하면 해당 컴포넌트는 리렌더링(re-rendering)됩니다.

 

과거에는 함수형 컴포넌트(function components)로 상태를 관리할 수 없었기 때문에 반드시 클래스형 컴포넌트를 사용해야 했지만, 리액트 16.8 버전(리액트 네이티브 0.59) 이후로는 Hook을 통해 함수형 컴포넌트에서도 상태를 관리할 수 있게 되었습니다. 

왼쪽이 클래스형, 오른쪽이 함수형입니다. 누가봐도 왼쪽이 불편하죠

 

리액트 네이티브 개발팀에서 클래스형 컴포넌트를 삭제할 계획은 없지만 장기적으로 함수형 컴포넌트를 이용하여 hooks를 사용하는 것이 주된 개발이 될 것이라고 했으니 우리도 hook을 위주로 개발하는 것을 목표로 해봅시다.

 

-useState

리액트 Hooks 중 useState 는 함수현 컴포넌트에서 상태를 관리할 수 있도록 해줍니다. 물론 useState을 여러 개 사용하는 것도 가능합니다.

//MyButton
import React from 'react'
import { Alert, Text, TouchableOpacity } from 'react-native'
import PropTypes from 'prop-types';

const MyButton = ({ title = "Button",onPress})  => {
   console.log({ title, onPress });
  return (
    <TouchableOpacity
    style={{
        backgroundColor: '#3498db',
        padding:16,
        margin: 10,
        borderRadius: 8,
    }}
        onPress={onPress}  //alert로 하면 내용이 고정됩니다!
    >
        <Text style={{fontSize: 24, color: 'white'}}>{ title}</Text>
    </TouchableOpacity>
  )
};


MyButton.propTypes = {
    title: PropTypes.string.isRequired,
 onPress: PropTypes.func.isRequired,
}

export default MyButton

 

import React, { useState } from 'react'
import { Text, View } from 'react-native'
import MyButton from './MyButton';

const Counter = () => {
const [count, setCount] = useState(0);
const [double, setDouble] = useState(0);
    return (
    <View style={{alignItems: 'center'}}>
        <Text style={{fontSize: 30, margin: 10}}>count: {count}</Text>
        <Text style={{fontSize: 30, margin: 10}}>double: {double}</Text>
        <MyButton title="+1"  onPress={()=>{setDouble(double+2);setCount(count+1)}}></MyButton>
        <MyButton title="-1" onPress={()=>{setDouble(double-2);setCount(count-1)}}></MyButton>
    </View>
  )
}

export default Counter
import React from "react";
import {View, StyleSheet, Text, StatusBar, Button, Alert} from 'react-native';
import MyButton from "./components/MyButton";
import Counter from "./components/Counter";

const App = () => {
    const name ='warigari';
    return (
        <View style={{
            flex:1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: "ffffff",
        }}>
            <Counter/>
        </View>
    )
}

export default App;

 


4. 이벤트

리액트 네이티브는 사용자의 행동에 따라 상호 작용하는 이벤트를 다양하게 제공합니다. 

1) press이벤트

총 4가지 이벤트가 있습니다. 

 

  • onPress: 버튼이 눌렸을 때 실행되는 이벤트입니다.
  • onPressIn: 버튼이 눌리기 시작할 때 발생합니다.
  • onPressOut: 버튼에서 손을 뗄 때 발생합니다.
  • onLongPress: 터치가 일정시간 이상 지속되면 호출
import React from 'react'
import { TouchableOpacity,Text}from 'react-native';

const EventButton = () => {
    const _onPressIn = () => console.log('Press In !!!\n');
    const _onPressOut = () => console.log('Press Out !!!\n');
    const _onPress = () => console.log('Press !!!\n');
    const _onLongPress = () => console.log('Long Press!!!\n');
    return (
        <TouchableOpacity style={{ backgroundColor: '#f1c40f',
            padding:16,
            margin: 10,
            borderRadius: 8,}}
            onPressIn={_onPressIn}
            onLongPress={_onLongPress}
            onPressOut={_onPressOut}
        onPress={_onPress}
        delayLongPress={3000}
        >
            <Text style={{color: 'white', fontSize:24}}>
                Press
            </Text>
        </TouchableOpacity>
  )
}

export default EventButton

 

 

delayLongPress로 3초 동안 눌러야 console.log('Long Press!!!\n'); 가 출력되도록 할 수 있습니다.

 

2) change이벤트

change 이벤트는 사용자가 텍스트 입력을 변경할 때 발생합니다. 주로 TextInput 컴포넌트에서 사용되며, 사용자가 입력한 값을 실시간으로 추적하거나 처리할 수 있습니다.

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

const EventInput = () => {
    const [text, setText] = useState('');

    const _onChange = (newText) => {
        setText(newText);
    };
    const _onChangeText = text => setText(text);

    return (
        <View>
            <Text style={{ margin: 10, fontSize: 30 }}>
                text: {text}
            </Text>
            <TextInput
                style={{ borderWidth: 1, padding: 10, fontSize: 20 }}
                placeholder="Enter a text..."
                onChangeText={_onChangeText}
                value={text}
            />
        </View>
    );
};

export default EventInput;

import React from "react";
import {View, StyleSheet, Text, StatusBar, Button, Alert} from 'react-native';
import MyButton from "./components/MyButton";
import Counter from "./components/Counter";
import EventButton from "./components/EventButton";
import EventInput from "./components/EventInput";

const App = () => {
    const name ='warigari';
    return (
        <View style={{
            flex:1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: "ffffff",
        }}>
            <EventButton/>
            <EventInput/>
        </View>
    )
}

export default App;

 

3) pressable 컴포넌트

TouchableOpacity와 비슷하지만, Pressable은 더 유연하게 다양한 상호작용을 처리할 수 있도록 설계되었습니다.

기본적으로 activeOpacity와 같은 속성을 직접 설정할 필요 없이, pressed 상태를 사용하여 스타일을 변경할 수 있습니다.

기존 컴포넌트와 다른 특징은 HitRect와 PressRect입니다.

 

HitpRect를 통해 대부분의 사람들이 적당히 버튼을 눌러도 클릭이 되도록 설정할 수 있습니다.

PressRect는 버튼을 누른 상태에서 손가락을 이동시켰을 때 얼마나 벗어나야 버튼 누름 상태에서 벗어났는지 판단하는 범위를 표시합니다.

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

const Button = (props) => {
  return (
    <Pressable
      style={{ padding: 10, backgroundColor: '#1abc9c' }}
      onPressIn={() => console.log('Press In')}
      onPressOut={() => console.log('Press Out')}
      onPress={() => console.log('Press')}
      onLongPress={() => console.log('Long Press')}
      delayLongPress={3000}
      pressRetentionOffset={{ bottom: 50, left: 50, right: 50, top: 50 }}
      hitSlop={{ bottom: 50, left: 50, right: 50, top: 50 }}
    >
      <Text style={{ padding: 10, fontSize: 30 }}>{props.title}</Text>
    </Pressable>
  );
};

const App = () => {
  return (
    <View
      style={{
        flex: 1,
        justifyContent: 'center',
        backgroundColor: '#fff',
        alignItems: 'center',
      }}
    >
      <Button title="Pressable" />
    </View>
  );
};

export default App;