이번 포스팅에서는 간단한 계산기 어플리케이션을 구현해보자. 다음 내용을 다루게 된다.
- 스타일 : StyleSheet로 스타일 변경하기 / 화면 정렬과 방향
- 컴포넌트 : props와 상태 / 커스텀 컴포넌트
- Hooks: useState로 상태 관리하기
먼저 "rn-calc"이라는 프로젝트를 생성 후 프로젝트 파일로 이동, ESLint 파일을 생성한다.
실행하면 프로젝트 유형을 선택하는 옵션이 나타난다. 여기서 "blank"(빈 프로젝트)를 선택한 후 진행한다.
맥북에서 파일/폴더 삭제하는 명령어 정리
1. 일반 파일 삭제 : rm 파일경로
2. 폴더 삭제 (-r 옵션 추가) : rm -r 폴더경로
3. 강제 삭제 (-rf 옵션 추가) : rm -rf 파일경로 또는 폴더경로
4. 권한 문제 발생 시 (sudo 사용) : sudo rm -rf 파일경로 또는 폴더경로
5. 삭제 대신 휴지통으로 이동 : mv 파일경로 ~/.Trash/
1. 프로젝트 준비하기
이때 먼저 개발 환경으로 이동해야함을 잊지 않도록 하자 : cd /Users/apple/Desktop/RN
expo init rn-calc
cd rn-calc
npx eslint --init
이때 eslint 패키지 설치를 진행하겠냐는 질문에 그렇다는 답변을 한 후 다음과 같은 질문에 선택지를 고른다.
...
npx eslint --init
You can also run this command directly using 'npm init @eslint/config@latest'.
> rn-calc@1.0.0 npx
> create-config
@eslint/create-config: v1.4.0
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · javascript
✔ Where does your code run? · node
The config that you've selected requires the following dependencies:
eslint, globals, @eslint/js, eslint-plugin-react
✔ Would you like to install them now? · No / Yes
✔ Which package manager do you want to use? · npm
☕️Installing...
added 157 packages, changed 1 package, and audited 1015 packages in 5s
162 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Successfully created /Users/apple/Desktop/RN/rn-calc/eslint.config.mjs file.
① How would you like to use ESLint? → problems : ESLint를 어떤 용도로 사용할지 선택
- problems → 코드에서 문제(버그, 오류 가능성)를 찾는 용도
- problems + style → 코드 스타일도 함께 검사
- problems + style + formatting → 코드 포매팅까지 관리
✅ ESLint를 코드 문제 탐지용으로만 사용하도록 설정함.
② What type of modules does your project use? → esm : 프로젝트에서 사용하는 모듈 시스템 선택
- esm (ECMAScript Modules) → import/export 구문을 사용
- commonjs → require/module.exports 사용
- none → 모듈 시스템 없음
✅ React 프로젝트이므로 최신 ES 모듈(import/export) 방식을 사용하도록 설정.
③ Which framework does your project use? → react (사용하는 프레임워크 선택)
- react → React 프로젝트
- vue → Vue.js 프로젝트
- none → 특정 프레임워크 없이 사용
✅ React 프로젝트이므로 react 선택.
④ Does your project use TypeScript? → javascript (TypeScript 사용 여부 선택)
- typescript → TypeScript 사용
- javascript → 순수 JavaScript 사용
✅ 현재 프로젝트는 JavaScript 기반이므로 javascript 선택.
⑤ Where does your code run? → node
(코드 실행 환경 선택)
- browser → 브라우저에서 실행
- node → Node.js 환경에서 실행
- 둘 다 선택 가능
✅ React Native 프로젝트는 JavaScript 코드가 Node.js에서도 실행되므로 node 선택.
⑥ 필요한 패키지 설치 여부 → Yes : ESLint가 정상적으로 작동하려면 다음 패키지가 필요.
eslint, globals, @eslint/js, eslint-plugin-react
- eslint → 기본 ESLint 라이브러리
- globals → 전역 변수 정의
- @eslint/js → JavaScript용 ESLint 기본 규칙
- eslint-plugin-react → React 전용 ESLint 플러그인
✅ 이 패키지들을 즉시 설치하도록 Yes 선택.
⑦ 사용할 패키지 매니저 선택 → npm
- npm → npm install을 사용하여 패키지 설치
- yarn → yarn add를 사용하여 패키지 설치
- pnpm → pnpm install 사용
✅ 기본적으로 npm을 사용하도록 설정.
위와 같은 ESLint를 설치했다면 eslint.config.mjs 파일을 수정해야한다.
이때 유의할 점은, ESLint v9.0.0 이상 버전에서는 기본 설정 파일이 eslint.config.js로 변경되었으며, 기존의. eslintrc.json을 그대로 사용할 수 없다. 따라서,. eslintrc.json을 새 포맷인 eslint.config.js로 변환해야 한다!
변경 방법은 아래 링크의 맨 밑을 참고한다.
ESLint 설정
다음과 같이 eslint.config.mjs 파일을 수정하자.
이제 ESLint가 React 17의 JSX 변환 방식에 맞게 동작하며, console.log 사용 시 경고를 표시하지 않는다. 🚀
1. React 17+에서는 import React from 'react' 없이 JSX를 사용할 수 있도록 설정
2. console.log 사용을 확인하기 위해 rules에 no-console을 추가
import globals from 'globals';
import pluginJs from '@eslint/js';
import pluginReact from 'eslint-plugin-react';
/** @type {import('eslint').Linter.Config[]} */
export default [
{ files: ['**/*.{js,mjs,cjs,jsx}'] },
{ languageOptions: { globals: globals.node } },
{
rules: {
'no-console': 'off', // console.log 허용
},
},
pluginJs.configs.recommended,
pluginReact.configs.flat.recommended,
pluginReact.configs['jsx-runtime'], // React 17 버전 호환
];
Prettier 설정
ESLint의 설정이 끝났더면 Prettier 설정을 진행하자.
1. settings > Format On Save 체크
2. Default Formatter를 Prettier - Code formatter로 변경
3. 설정이 완료되면 프로젝트 폴더에. prettierrc라는 파일을 만들고 다음과 같이 작성한다.
{
"singleQuote": true,
"arrowParens": "always",
"tabWidth": 2,
"printWidth": 80
}
App.js에 console.log 코드를 입력했을때 에러 표시(물결 표시)가 없다면 제대로 적용된 것이다.
그 다음으로 자동 완성 기능 적용을 위한 패키지를 설치하자.
개발 의존성 설치
React Native 프로젝트에서 타입스크립트를 사용할 경우, React와 React Native의 타입 정의 파일을 설치하는 것이 필수적이다. 이를 위해 다음 명령어를 실행한다.
npm install -D @types/react @types/react-native
이 명령어는 개발 의존성(devDependencies)으로 @types/react와 @types/react-native를 설치하는 역할을 한다.
1) @types/react
- React의 타입 정보를 포함하는 패키지
- React.FC, useState, useEffect 등의 함수 및 훅에 대한 타입 정의 제공
- JSX를 사용할 때 필요한 JSX.Element 타입을 지원
2) @types/react-native
- React Native의 기본 컴포넌트(Button, View, Text 등)에 대한 타입 정의 제공
- StyleSheet와 같은 React Native 전용 API의 타입 정보를 포함
- 터치 이벤트, 애니메이션 관련 API 등에 대한 정확한 타입 지원
이 패키지들을 설치하지 않으면 TypeScript 환경에서 React 및 React Native의 타입 정보를 알 수 없어, 컴파일 오류가 발생하거나 자동완성 기능이 정상적으로 작동하지 않을 수 있다.
또한 명령어에서 -D(--save-dev) 옵션을 사용하는 이유는 이 패키지들이 런타임이 아니라 개발 단계에서만 필요한 의존성이기 때문이다.
@types/react와 @types/react-native는 실행 파일에 포함되지 않으며, 오직 코딩 시 타입 검사 및 코드 완성을 돕는 용도로 사용된다. 따라서 dependencies가 아닌 devDependencies에 추가하여 불필요한 의존성 증가를 방지한다.
불필요한 패키지 삭제
React Native 프로젝트에서 react-native-web 패키지는 React Native 컴포넌트를 웹 환경에서도 사용할 수 있도록 지원하는 라이브러리다. 이를 통해 View, Text, Image 등의 React Native 컴포넌트를 웹 브라우저에서도 렌더링할 수 있으며, 단일 코드베이스로 iOS, Android, Web을 모두 지원할 수 있는 장점이 있다.
react-native-web는 react-dom을 사용하여 네이티브 컴포넌트를 HTML 및 CSS로 변환한다. 이를 통해 React Native 스타일 시스템과 레이아웃을 웹에서도 동일하게 유지할 수 있다. 또한, react-native-gesture-handler와 같은 네이티브 종속성이 있는 패키지도 웹에서 동작할 수 있도록 지원하는 경우가 많다.
그러나 React Native 프로젝트에서 react-native-web이 반드시 필요한 것은 아니다. React Native는 기본적으로 모바일 환경을 대상으로 개발되는 프레임워크이며, 웹 환경까지 지원할 필요가 없다면 해당 패키지를 유지할 이유가 없다. 또한, React Native에서 제공하는 일부 네이티브 API는 react-native-web에서 완전히 지원되지 않거나, 추가적인 설정이 필요할 수 있다.
웹 환경을 고려하지 않는 프로젝트라면 react-native-web을 제거하는 것이 불필요한 의존성을 줄이고 프로젝트를 보다 가볍게 유지하는 데 도움이 된다. 다음 명령어를 실행하면 패키지를 제거할 수 있다.
npm uninstall react-native-web
패키지를 제거한 후 package.json과 node_modules에서 react-native-web 관련 항목이 삭제되었는지 확인하고, 만약 react-native-web을 사용했던 코드가 있다면 적절히 수정해야 한다.
프로젝트 폴더 생성
프로젝트에 src라는 폴더를 생성한다. 앞으로 우리가 생성한 파일은 src 폴더에서 관리한다.
아래 코드는 /Users/apple/Desktop/RN/rn-calc/src/App.js에 생성한다.
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text>Calculate App</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default App;
다음으로 루트 경로에 자동으로 생성된 App.js파일을 수정한다 : /Users/apple/Desktop/RN/rn-calc/App.js
import App from './src/App';
export default App;
다음 명령어를 통해 expo go에서 실행해보자 : npm start
npm start > PC 가상환경 실행
터미널에서 i 버튼을 누르면 ios가 실행되고 a를 누르면 안드로이드가 실행되며 자동으로 EXPO 앱이 설치된다.
React Native에서 앱을 실행한 후 실물 기기를 흔들면 화면 왼쪽에 메뉴가 나타난다. 가상 기기를 사용할 경우 iOS에서는 Command + D, 안드로이드에서는 Command + M을 누르면 동일한 메뉴를 호출할 수 있다. Windows 환경에서는 Command 대신 Ctrl을 사용하면 된다. 실행 중인 터미널에서 키보드의 M 키를 눌러도 동일하게 메뉴가 표시된다.
React Native는 코드가 변경될 때 자동으로 새로고침되지만, 강제로 새로고침을 원한다면 메뉴를 열고 Reload 버튼을 클릭하면 된다. 가상 기기에서는 메뉴를 열지 않고도 iOS에서는 A 키를 한 번, 안드로이드에서는 R 키를 두 번 누르면 즉시 새로고침이 실행된다. 또한, 실행 중인 터미널에서 R 키를 입력하면 현재 실행 중인 모든 기기가 동시에 새로고침된다.
기기에서 React Native가 정상적으로 실행되는지 확인하고, 메뉴 호출 및 새로고침 기능이 제대로 작동하는지 테스트해 보면 개발 과정에서 더욱 효율적으로 앱을 수정하고 반영할 수 있다.
이때 유의할 점은 import App from '.src/App';에서 '.src/App'는 잘못된 경로다. 올바른 경로는 ./src/App 또는 'src/App'이어야 한다.
이는 상대경로로 접근하는 주소이며, 컴마 뒤에 슬레쉬를 붙이도록 하자.
> ./src/App → 현재 파일 기준으로 src/App.js를 가져옴
> src/App → 프로젝트 루트에서 src/App.js를 가져옴 (Babel/Webpack 설정에 따라 다를 수 있음)
2. 컴포넌트와 스타일
2-1. 컴포넌트
React Native에서 UI를 구성하는 기본 단위는 컴포넌트(Component)다. 컴포넌트는 재사용이 가능하며, 코드의 유지보수를 쉽게 만들어주는 중요한 개념이다. 쉽게 말해, 레고 블럭을 떠올리면 된다.
컴포넌트는 앱의 UI를 구성하는 최소 단위로, 하나 이상의 UI 요소를 포함할 수 있다. 웹 개발에서 HTML 태그(<div>, <button> 등)를 사용하듯이, React Native에서는 View, Text, Image 같은 컴포넌트를 사용하여 화면을 구성한다.
컴포넌트는 재사용성과 모듈성을 높이는 역할을 한다. 동일한 UI 요소를 여러 화면에서 사용할 경우, 하나의 컴포넌트로 만들어 관리하면 유지보수가 쉬워진다. 또한, 컴포넌트 내부에서 상태(state)를 관리할 수도 있어 동적인 UI를 구현하는 데 유용하다.
컴포넌트는 역할에 따라 크게 두 가지로 나뉜다.
- 프레젠테이셔널 컴포넌트 (Presentational Component)
- UI를 담당하는 컴포넌트
- props를 통해 데이터를 받아 화면에 출력
- 로직이 아닌 스타일과 레이아웃에 집중
- 예: Button, Card, Avatar 등
- 컨테이너 컴포넌트 (Container Component)
- 상태(state)와 로직을 관리하는 컴포넌트
- API 요청, 데이터 가공 등의 비즈니스 로직 처리
- 프레젠테이셔널 컴포넌트를 감싸고, 필요한 데이터를 전달
- 예: UserListContainer, PostContainer 등
주로 사용되는 컴포넌트
다음은 React Native에서 주로 사용되는 컴포넌트를 표로 정리한 내용이다:
컴포넌트 | 설명 | 예제 코드 |
View | 레이아웃을 구성하는 기본 컨테이너. 다른 컴포넌트를 감쌀 때 사용. | tsx<br> <View style={{ padding: 10, backgroundColor: 'lightgray' }}><Text>Hello, React Native!</Text></View><br> |
Text | 텍스트를 렌더링하는 컴포넌트. HTML의 <p>, <span>과 유사. | tsx<br> <Text style={{ fontSize: 20 }}>React Native Text</Text><br> |
Image | 이미지를 표시하는 컴포넌트. 로컬 또는 원격 이미지를 로드. | tsx https://example.com/image.jpg' }} style={{ width: 100, height: 100 }} /> |
StatusBar | 상태 바의 스타일을 제어하는 컴포넌트. 화면 상단의 배경색 및 텍스트 색상 등을 설정할 수 있다. | tsx<br> import { StatusBar } from 'react-native';<br> <StatusBar barStyle="dark-content" backgroundColor="lightblue" /><br> |
ScrollView | 스크롤 가능한 뷰를 제공. 화면 크기를 초과하는 콘텐츠를 포함할 때 사용. | tsx<br> <ScrollView><Text>Item 1</Text><Text>Item 2</Text><Text>Item 3</Text></ScrollView><br> |
TouchableOpacity | 터치 가능한 영역을 만들며, 주로 버튼이나 상호작용 요소에 사용. | tsx<br> <TouchableOpacity onPress={() => alert('Button Pressed!')}><Text>Click Me</Text></TouchableOpacity><br> |
src/App.js에서 App 컴포넌트는 여러 개의 컴포넌트가 조합된 컴포넌트로, 시작 파일에 있어 더 이상 상위 컴포넌트가 존재하지 않으므로 App 컴포넌트를 최상위 컴포넌트라고 부르기도 한다.
2-2. 인라인 스타일 적용하기
React Native에서 컴포넌트에 스타일을 전달하는 방법은 크게 두 가지가 있다.
첫 번째는 인라인 스타일을 사용하는 방법이고, 두 번째는 StyleSheet를 사용하는 방법이다. 인라인 스타일은 JavaScript 객체로 스타일을 직접 정의해 컴포넌트의 style 속성에 전달하는 방식이다.
인라인 스타일을 사용하는 방법을 아래 예제 코드로 설명해보겠다.
import { StatusBar } from 'expo-status-bar';
import { Text, View } from 'react-native';
const App = () => {
return (
<View style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center' }}>
<StatusBar style="auto" />
<Text style={{ fontSize: 20, fontWeight: 'bold', color: 'blue', paddingHorizontal: 20, paddingVertical: 10 }}>
Calculate App
</Text>
</View>
);
};
export default App;
위의 코드에서 인라인 스타일은 style={{...}} 형태로 작성된다. 각 속성은 JavaScript 객체 내에서 속성 이름과 값을 쌍으로 정의하는 방식으로 전달된다. 다시 말해, 컴포넌트의 style에 직접 스타일 코드를 작성하는 것을 인라인 스타일이라고 한다.
...생략
const App = () => {
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text
style = {{
}}
>
Calculate App
</Text>
</View>
);
};
다음은 코드에서 사용된 스타일 속성과 그 의미를 정리한 표이다.
스타일 속성 | 의미 |
flex | 요소의 크기를 동적으로 조정하는 속성. 1은 부모 요소에서 가능한 모든 공간을 차지함. |
backgroundColor | 배경 색상을 설정. #fff는 흰색을 의미함. |
alignItems | 자식 컴포넌트를 수평으로 정렬하는 속성. center는 수평 중앙 정렬. |
justifyContent | 자식 컴포넌트를 수직으로 정렬하는 속성. center는 수직 중앙 정렬. |
fontSize | 텍스트의 글자 크기를 설정. 20은 20픽셀 크기. |
fontWeight | 텍스트의 글자 두께를 설정. bold는 두꺼운 글씨. |
color | 텍스트의 색상을 설정. blue는 파란색. |
paddingHorizontal | 수평 방향의 여백을 설정. 20은 좌우 각각 20픽셀의 여백. |
paddingVertical | 수직 방향의 여백을 설정. 10은 상하 각각 10픽셀의 여백. |
React Native에서 자주 사용하는 스타일 속성을 포함한 예제 코드와 설명이다.
import { StatusBar } from 'expo-status-bar';
import { Text, View } from 'react-native';
const App = () => {
return (
<View style={{
// flex: 1은 화면 전체를 차지하도록 설정
flex: 1,
// 배경 색상을 연한 회색으로 설정
backgroundColor: '#f5f5f5',
// 수평으로 중앙 정렬
alignItems: 'center',
// 수직으로 중앙 정렬
justifyContent: 'center',
}}>
<StatusBar style="auto" />
<Text style={{
// 텍스트 크기를 24px로 설정
fontSize: 24,
// 글씨를 두껍게 설정
fontWeight: 'bold',
// 텍스트 색상을 어두운 회색으로 설정
color: '#333',
// 텍스트 배경을 연한 회색으로 설정
backgroundColor: '#e0e0e0',
// 좌우 여백을 30px로 설정
paddingHorizontal: 30,
// 상하 여백을 15px로 설정
paddingVertical: 15,
// 텍스트와 아래 요소 간의 여백을 20px로 설정
marginBottom: 20,
}}>
Calculate App
</Text>
<Text style={{
// 텍스트 크기를 16px로 설정
fontSize: 16,
// 텍스트 색상을 연한 회색으로 설정
color: '#888',
// 좌우 여백을 20px로 설정
paddingHorizontal: 20,
}}>
Start calculating your results now.
</Text>
</View>
);
};
export default App;
다음과 같이 화면이 변하는 것을 확인할 수 있다.
React Native의 스타일과 CSS는 기본적으로 목적이 같지만, 그 구현 방식에서 몇 가지 중요한 차이점이 있다. 아래는 React Native 스타일링의 주요 특징과 CSS와의 차이를 정리한 내용이다.
1. 속성은 카멜 케이스로 작성
CSS에서는 스타일 속성의 이름이 하이픈(-)으로 연결된 소문자로 작성된다. 예를 들어, font-size나 background-color와 같이 하이픈을 사용한다.
하지만 React Native에서는 스타일 속성을 카멜 케이스(camelCase)로 작성해야 한다. 이때 카멜 케이스(Camel Case)는 여러 단어를 하나로 합칠 때, 단어 사이에 공백이나 하이픈(-)을 사용하지 않고, 첫 번째 단어는 소문자로 시작하고, 두 번째 단어부터는 첫 글자를 대문자로 시작하는 방식이다. 이 방식이 마치 낙타의 등처럼 보인다고 해서 "카멜 케이스"라는 이름이 붙여졌다.
정리하면, React Native에서는 단어 사이에 하이픈 대신 첫 글자를 대문자로 작성하여 fontSize, backgroundColor와 같이 작성한다. 예를 들어 font-size는 fontSize로 변경된다.
CSS: font-size, background-color
React Native: fontSize, backgroundColor
2. px, em 단위 사용하지 않음
CSS에서는 px, em, % 등 다양한 단위를 사용하여 스타일을 지정할 수 있다. 예를 들어 font-size: 16px; 또는 padding: 1em;과 같은 방식으로 스타일을 정의한다.
반면 React Native에서는 단위 없이 숫자만 사용한다. 숫자는 기본적으로 dp(density-independent pixels) 단위로 처리되며, 실제 디바이스의 화면 크기에 따라 자동으로 조정된다. 따라서 px, em 등 단위를 명시할 필요가 없다.
CSS: font-size: 16px;, padding: 1em;
React Native: fontSize: 16, padding: 10 (단위 없이 숫자만 사용)
3. fontWeight와 같은 지정 타입이 다를 수 있음
CSS에서는 font-weight 속성에 숫자나 키워드를 사용하여 글자의 굵기를 설정할 수 있다. 예를 들어 font-weight: 700; 또는 font-weight: bold;와 같이 지정할 수 있다.
React Native에서는 fontWeight를 문자열로 지정한다. React Native에서 fontWeight는 "normal", "bold", "100", "200" 등과 같이 문자열로 표현된다. 또한, 일부 값은 CSS에서 사용하는 것과 달리 다르게 처리될 수 있다.
CSS: font-weight: 700; 또는 font-weight: bold;
React Native: fontWeight: 'bold' 또는 fontWeight: '700'
4. borderColor 등 스타일을 각각 지정해야함
CSS에서는 border 속성을 한 번에 설정할 수 있다. 예를 들어, border: 1px solid black;와 같이 하나의 속성으로 모든 테두리 스타일을 정의할 수 있다.
그러나 React Native에서는 borderWidth, borderColor, borderStyle와 같이 각각의 속성을 개별적으로 지정해야 한다. 한 번에 여러 속성을 지정하는 방식이 아니라, 각 속성을 별도로 설정해야 하므로 보다 세부적인 제어가 가능하다.
CSS: border: 1px solid black;
React Native:
borderWidth: 1,
borderColor: 'black',
borderStyle: 'solid',
이렇게 React Native에서는 각 스타일 속성을 개별적으로 설정해야 하므로, CSS에 비해 조금 더 많은 코드가 필요할 수 있다. 하지만 이런 방식은 각 속성에 대해 더 세밀한 제어가 가능하다는 장점도 있다.
2-3. StyleSheet로 스타일 적용하기
React Native에서 스타일을 적용할 때, 기본적으로는 인라인 스타일을 사용할 수 있지만, StyleSheet를 사용하면 성능이 향상되고 코드가 더욱 깔끔해지며, 유지보수가 용이해진다. StyleSheet는 React Native에서 스타일을 관리하는 권장되는 방식으로, 스타일 객체를 생성하고 이를 컴포넌트에 적용하는 방법을 제공한다.
StyleSheet란?
StyleSheet는 React Native에서 스타일을 정의하고 관리하는 방법이다. StyleSheet.create 메서드를 사용하여 스타일을 정의하면, 해당 스타일은 성능 상 최적화된 형태로 저장되며, 애플리케이션이 실행될 때 스타일 객체가 한 번만 생성된다. 이는 인라인 스타일에 비해 렌더링 성능을 크게 향상시킨다.
- 성능 최적화: 스타일 객체가 한 번만 생성되어 성능이 향상된다.
- 코드 관리 용이: 스타일 코드와 컴포넌트 코드가 분리되어 가독성이 높아진다.
- 유지보수: 스타일을 중앙에서 관리할 수 있어, 코드 수정 및 업데이트가 쉬워진다.
아래 예시는 React Native에서 StyleSheet를 사용하는 방법을 보여준다.
.. 생략
// StyleSheet.create로 스타일 객체를 생성
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 20,
fontWeight: 'bold',
color: '#333',
},
});
- StyleSheet.create: StyleSheet.create 메서드를 사용하여 스타일 객체를 생성한다. 이를 통해 React Native는 스타일을 최적화하여 성능을 향상시킨다.
- container 스타일: flex: 1로 화면 전체를 차지하도록 하고, alignItems: 'center'와 justifyContent: 'center'로 자식 요소를 화면의 중앙에 배치한다. 배경색은 흰색으로 설정된다.
- text 스타일: fontSize로 텍스트 크기를 설정하고, fontWeight를 'bold'로 설정하여 텍스트를 굵게 만든다. color는 텍스트의 색을 설정한다
스타일 객체 적용
스타일 객체는 컴포넌트의 style 속성에 객체 형태로 전달된다. 예시에서는 styles.container와 styles.text가 각각 View와 Text 컴포넌트에 적용된다. StyleSheet.create로 만든 스타일은 항상 객체로 관리되며, 해당 객체의 속성에 대해 스타일을 적용할 수 있다. 이때 StyleSheet.create는 함수로, 객체를 전달하며 키-값 형태로 스타일 이름과 스타일 코드를 작성한다.
스타일을 적용 할때에는 스타일 이름을 사용해 컴포넌트의style에 적용한다. StyleSheet를 사용해 스타일을 만들고 Text 컴포넌트에 적용하는 다음 코드를 작성해보자.
const App = () => {
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text style={styles.text}>Calculate App</Text>
<Text style={styles.text}>StyleSheet</Text>
<Text></Text>
</View>
);
};
// StyleSheet.create로 스타일 객체를 생성
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 30,
fontWeight: '700',
color: 'green',
},
});
export default App;
App 컴포넌트는 View와 Text 컴포넌트로 구성된다. View는 컨테이너 역할을 하며, 그 안에 여러 개의 Text 요소가 포함된다. 각 Text 요소는 "Calculate App"과 "StyleSheet"라는 텍스트를 출력하며, 두 번째 Text 요소는 텍스트가 비어있는 상태로 존재한다.
스타일 설명
스타일은 StyleSheet.create를 사용하여 정의되며, 두 가지 주요 스타일이 있다.
- container 스타일:
- flex: 1: View가 화면을 가득 채우도록 설정한다. flex 속성은 레이아웃을 설정할 때 사용된다. 1로 설정하면 View가 가능한 모든 공간을 차지한다.
- backgroundColor: '#fff': 배경색을 흰색으로 설정한다.
- alignItems: 'center': 자식 요소들이 수평으로 중앙 정렬되도록 설정한다.
- justifyContent: 'center': 자식 요소들이 수직으로 중앙 정렬되도록 설정한다. alignItems와 justifyContent는 각각 수평 및 수직 정렬을 담당한다.
- text 스타일:
- fontSize: 70: 텍스트의 크기를 70으로 설정한다. 매우 큰 글씨 크기이다.
- fontWeight: '700': 텍스트의 두께를 굵게 설정한다. 700은 보통 'bold'와 같은 역할을 한다.
- color: 'green': 텍스트 색상을 초록색으로 설정한다.
StatusBar 컴포넌트
StatusBar는 앱의 상태 표시줄을 다루는 컴포넌트로, 이 코드에서는 style="auto"로 설정되어 있어, 상태 표시줄의 스타일이 자동으로 설정된다. 이를 통해 시스템에 맞는 스타일을 자동으로 적용할 수 있다.
View는 flex 스타일을 사용하여 화면에 가득 차게 되고, alignItems와 justifyContent 속성으로 그 안에 포함된 텍스트들이 화면의 중앙에 위치한다. 첫 번째 Text는 큰 초록색 글씨로 "Calculate App"을 출력하며, 두 번째 Text도 같은 스타일로 "StyleSheet"라는 텍스트를 출력한다. 세 번째 Text는 비어 있어 화면에 아무것도 출력되지 않는다.
그 결과 아래와 같은 화면으로 출력된다.
StyleSheet에서 스타일 이름을 통해 스타일의 의도를 명확히 할 수 있다.
예를 들어 일반 텍스트로 에러를 나타내는 텍스트가 있다면 다음과 같이 코딩이 가능하다.
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text style={styles.text}>Calculate App</Text>
<Text style={styles.text}>StyleSheet</Text>
<Text style={styles.error}>error</Text>
<Text></Text>
</View>
);
};
// StyleSheet.create로 스타일 객체를 생성
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 30,
fontWeight: '700',
color: 'green',
},
error: {
fontSize: 30,
fontWeight: '700',
color: 'red',
},
});
export default App;
2-4. 여러개의 스타일 적용하기
React Native에서는 한 컴포넌트에 여러 개의 스타일을 적용할 수 있다. 여러 개의 스타일을 배열로 묶어 style 속성에 전달하는 방식은 스타일을 효율적으로 관리하고, 조건에 따라 스타일을 동적으로 변경할 때 유용하다.
<Component style = {스타일1, 스타일2, 스타일3}}></Component>
이때 배열에 있는 스타일 순서에 유의하자. <Component style={스타일1, 스타일2, 스타일3}>와 같이 스타일을 순서대로 배열로 전달할 때, 스타일의 적용 순서에 유의해야 하는 이유는 나열된 스타일들이 위에서 아래로 순차적으로 적용되기 때문이다. 만약 여러 스타일이 같은 속성을 지정하고 있다면, 마지막에 정의된 스타일의 속성이 우선적으로 적용된다.
따라서 일반적인 스타일을 앞쪽에 두고 해당 컴포넌트에 적용되어야하는 특정 스타일을 뒤로 두어야한다.
<Component style={[styles.style1, styles.style2, styles.style3]} />
이 경우, styles.style1, styles.style2, styles.style3가 순차적으로 적용되며, 만약 style1, style2, style3이 같은 속성(예: color, fontSize)을 포함하고 있다면, style3의 속성이 최종적으로 적용된다.
const styles = StyleSheet.create({
style1: {
color: 'red', // 첫 번째 스타일
},
style2: {
color: 'blue', // 두 번째 스타일
},
style3: {
color: 'green', // 세 번째 스타일
},
});
<Component style={[styles.style1, styles.style2, styles.style3]} />
위의 코드에서는 style1, style2, style3 모두 color 속성을 정의하고 있다. 하지만 style3가 마지막으로 적용되므로, 최종적으로 color: green이 적용된다.
- 스타일 중복 처리: 여러 스타일이 같은 속성을 정의할 때, 나중에 나오는 스타일이 먼저 나오는 스타일을 덮어쓴다. 이 점을 고려하지 않고 스타일을 작성하면 의도치 않게 스타일이 적용되지 않을 수 있다.
- 동적 스타일링: 조건부로 스타일을 적용할 때, 스타일을 배열로 전달하면 조건에 따라 다른 스타일을 마지막에 추가할 수 있다. 이때, 조건에 따라 스타일을 올바르게 적용하려면 스타일의 순서가 중요하다.
- 우선순위 설정: 스타일을 적용할 때 우선순위를 정확히 설정하고 싶다면, 어떤 스타일이 마지막에 적용될지 명확하게 알 필요가 있다.
위 특징을 잘 이용하면 중복된 스타일 코드를 줄일 수 있다.
유의점은 꼭 스타일을 배열(대괄호 , [])로 전달해야 한다는 것이다. 배열을 사용하여 여러 스타일을 적용하면, 각 스타일이 차례대로 적용된다.
const App = () => {
return (
<View style={styles.container}>
<StatusBar style="auto" />
{
/* <Text style={(styles.error, styles.text)}>Calculate App</Text>
<Text style={(styles.error, styles.text)}>StyleSheet</Text>
<Text style={(styles.text, styles.error)}>error</Text>
이 방식은 JavaScript에서 **쉼표 연산자(comma operator)**를 사용한 것이기 때문에 마지막 스타일만 적용
*/
}
<Text style={[styles.error, styles.text]}>Calculate App</Text>
<Text style={[styles.error, styles.text]}>StyleSheet</Text>
<Text style={[styles.text, styles.error]}>error</Text>
<Text></Text>
</View>
);
};
이렇게 스타일을 배열로 전달하면, 첫 번째 스타일과 두 번째 스타일이 순차적으로 적용된다. 만약 두 스타일이 같은 속성을 가지고 있다면, 두 번째 스타일의 속성이 우선적으로 적용된다. styles.error에서 color: red가 정의되어 있고, styles.text에서 color: green이 정의되어 있으므로, 마지막에 나오는 스타일에 따라 color 속성이 결정된다.
실행해보면 기존 출력과 동일함을 알수있다.
이외에도 특정 상황에서 원하는 스타일을 적용하기 위한 용도로 사용하며, 특정 변수의 값에 따라 스타일이 다르게 적용되는 상황에서는 다음과 같다.
const App = () => {
const isError = true;
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text style={[styles.error, styles.text]}>Calculate App</Text>
<Text style={[styles.error, styles.text]}>StyleSheet</Text>
<Text style={[styles.text, isError && styles.error]}>error</Text>
<Text></Text>
</View>
);
};
이때 논리 연산에서 자주 사용되는 값들(예: null, 0, NaN, '', undefined 등)은 자바스크립트에서 falsy 값(falsy value)으로 간주되며, 그 외의 값들은 truthy 값(truthy value)으로 간주된다. 이는 논리 연산자(AND, OR 등)와 결합될 때 어떻게 처리되는지에 중요한 영향을 미친다.
구분 | 값 | 설명 |
Falsy | false | 불리언 값 false 자체 |
Falsy | 0 | 숫자 0 |
Falsy | -0 | 음수 0 |
Falsy | NaN | 숫자가 아닌 값 (Not-a-Number) |
Falsy | '' (빈 문자열) | 빈 문자열 |
Falsy | null | 값이 없음 (null) |
Falsy | undefined | 정의되지 않은 값 (undefined) |
Truthy | true | 불리언 값 true |
Truthy | 1, -1, Infinity, -Infinity | 모든 숫자 값, Infinity 및 -Infinity는 truthy |
Truthy | 'hello', 'false', '0' | 빈 문자열을 제외한 모든 문자열 |
Truthy | {} (빈 객체) | 빈 객체 |
Truthy | [] (빈 배열) | 빈 배열 |
Truthy | function() {} | 함수 |
3. Button 컴포넌트와 props
props의 주요 특징은 다음과 같다:
- 읽기 전용: props는 부모 컴포넌트에서 자식 컴포넌트로 전달되는 데이터이며, 자식 컴포넌트는 해당 props를 수정할 수 없다. props는 한 번 설정되면 자식 컴포넌트에서 수정할 수 없고, 상태를 변경해야 할 경우에는 다른 방법(예: state)을 사용해야 한다.
- 컴포넌트 간 데이터 전달: props는 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방법으로 사용된다. 자식 컴포넌트는 부모 컴포넌트로부터 받은 데이터를 렌더링하거나 다른 로직에 활용할 수 있다.
- 동적 데이터 전달: props는 부모 컴포넌트에서 동적으로 설정할 수 있으므로, 컴포넌트 간에 서로 다른 데이터나 상태를 전달하는 데 유용하다.
모든 컴포넌트가 같은 props를 사용할 수 없는 데, props는 부모 컴포넌트에서 자식 컴포넌트로 전달되는 데이터이기 때문이다.
각 컴포넌트는 고유한 props를 받을 수 있으며, 부모 컴포넌트가 자식 컴포넌트에 전달하는 값이 다르면 자식 컴포넌트는 각각 다른 props를 사용할 수밖에 없다. 또한, 컴포넌트의 재사용성과 유연성을 고려하면, 각 컴포넌트는 다른 props를 받는 것이 일반적이다.
즉, 부모와 자식의 관계에 따라 props가 다르게 전달되며, 이를 통해 각 컴포넌트는 독립적이고 재사용 가능한 상태를 유지할 수 있다.
3-1. Button 컴포넌트 사용하기
//..생략
const App = () => {
const isError = true;
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text style={[styles.error, styles.text]}>Calculate App</Text>
<Text style={[styles.error, styles.text]}>StyleSheet</Text>
<Text style={[styles.text, isError && styles.error]}>error</Text>
<Button />
<Text></Text>
</View>
);
};
어떤 props도 선언하지 않고 Button 컴포넌트를 사용하면 다음과 같이 에러가 뜬다.
따라서 필수로 전달되어야하는 props에 유의하자.
React Native의 Button 컴포넌트는 기본적인 사용자 인터페이스(UI) 요소로, 버튼을 클릭했을 때 특정 작업을 처리하는 데 사용된다. 이 컴포넌트는 여러 가지 props를 받아들인다. 주요 props와 그 규칙은 다음과 같다:
Prop | 설명 | 타입 | 필수 여부 |
title | 버튼에 표시될 텍스트를 지정 | string | 예 |
onPress | 버튼 클릭 시 호출되는 함수 | function | 예 |
color | 버튼의 텍스트 색상 지정 | string | 아니오 |
disabled | 버튼을 비활성화할 때 사용, true일 경우 버튼이 비활성화됨 | boolean | 아니오 |
accessibilityLabel | 버튼에 대한 접근성 레이블을 설정 | string | 아니오 |
testID | 자동화된 테스트에서 해당 버튼을 식별하는 ID | string | 아니오 |
style | 버튼의 스타일을 설정, Button 컴포넌트에서는 직접 사용되지 않음 | style object | 아니오 |
Button 컴포넌트에 onPress와 title을 전달해보자:
const App = () => {
const isError = true;
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text style={[styles.error, styles.text]}>Calculate App</Text>
<Text style={[styles.error, styles.text]}>StyleSheet</Text>
{/* <Text style={[styles.text, isError && styles.error]}>error</Text> */}
<Button title="button" onPress={() => console.log('Click!')}></Button>
</View>
);
};
3-2. 플렛폼(OS)마다 다른 props
리액트 네이티브에서 버튼 컴포넌트는 Android와 iOS 플랫폼마다 스타일과 동작이 다를 수 있다. 예를 들어, Button 컴포넌트는 iOS에서 기본적으로 약간의 여백이 추가되고, Android에서는 더욱 강조된 버튼 효과가 있을 수 있다. 이는 각 플랫폼의 네이티브 요소에 맞추어 기본적으로 스타일링을 다르게 적용하기 때문이다.
플랫폼에 관계없이 동일한 디자인을 적용하려면 Button 컴포넌트에서 제공하는 color prop을 사용하여 버튼의 색을 변경할 수 있다. 또한, 버튼에 스타일을 적용할 때 Button 컴포넌트가 기본적으로 style prop을 지원하지 않기 때문에, 버튼을 감싸는 View에 스타일을 적용하여 동일한 모양을 유지할 수 있다.
하지만 Button 컴포넌트는 기본적으로 스타일을 style prop으로 받지 않기 때문에, 버튼의 색상이나 크기 등의 스타일을 직접적으로 변경하는 것이 불가능하다. 대신, 버튼의 색상 변경은 color prop을 통해 가능하다. 추가적인 스타일 변경이 필요하다면 Button 컴포넌트를 감싸는 View나 다른 컴포넌트를 활용하여 스타일링을 해야 한다.
React에서 props는 컴포넌트가 부모로부터 데이터를 전달받을 수 있는 방법이다. props는 "properties(속성)"의 줄임말로, 컴포넌트의 외부에서 데이터를 전달받을 때 사용된다. 이 데이터는 주로 렌더링에 영향을 주거나, 컴포넌트 간의 상호작용을 돕는 역할을 한다.
이때 "Button 컴포넌트는 기본적으로 스타일을 style prop으로 받지 않"의 의미는 Button 컴포넌트가 리액트 네이티브에서 제공하는 기본적인 스타일링 방식인 style prop을 직접적으로 받지 않는다는 것이다. 즉, Button 컴포넌트에서는 스타일을 변경할 때 style prop을 통해 스타일을 전달할 수 없다는 의미이다. 일반적으로 리액트 네이티브의 대부분 컴포넌트는 style prop을 통해 스타일을 적용할 수 있다. 예를 들어, View, Text, Image와 같은 컴포넌트는 style prop을 통해 색상, 크기, 정렬 등을 설정할 수 있다. 그러나 Button 컴포넌트는 이러한 방식으로 스타일을 직접 설정할 수 없다.
Button 컴포넌트에서 스타일을 설정하는 대신, color prop을 사용해 버튼의 색을 설정하거나, 버튼을 감싸는 View에 스타일을 적용하는 방식으로 간접적으로 스타일을 조정해야 한다. 예를 들어, Button 컴포넌트의 텍스트 색상은 color prop을 통해 변경할 수 있지만, 크기나 배경색 등은 style prop을 통해 설정할 수 없기 때문에 다른 방법을 사용해야 한다.
공식 문서에서는 color porps도 플렛폼에 따라 다르게 움직이고 기본 설정 색도 다르다는 것을 알 수 있다.
따라서 이번에는 Button 컴포넌틍 style 대신 color를 사용해 보자.
const App = () => {
const isError = true;
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text style={[styles.error, styles.text]}>Calculate App</Text>
{/* <Text style={[styles.error, styles.text]}>StyleSheet</Text> */}
{/* <Text style={[styles.text, isError && styles.error]}>error</Text> */}
<Button
title="button"
onPress={() => console.log('Click!')}
color="purple"
>
{/* style = {{ backgroundColor: 'black', color: 'white' }} */}
</Button>
</View>
);
};
리액트 네이티브에서 Button 컴포넌트의 color prop은 iOS와 Android에서 다르게 적용된다. 두 플랫폼에서 버튼 색상이 적용되는 방식에 대해 아래와 같이 정리할 수 있다.
iOS와 Android에서 Button 컴포넌트의 color prop 차이점
Android
Android에서는 Button 컴포넌트의 color prop을 통해 버튼의 배경색을 변경할 수 있다.
color는 버튼 텍스트 색상에만 적용된다. 실제 버튼 배경색을 변경하려면 추가적인 스타일링이 필요하다.
<Button title="Press Me" color="blue" onPress={() => console.log('Pressed')} />
위의 예제에서 color prop은 버튼의 텍스트 색을 'blue'로 변경한다.
iOS
iOS에서는 Button 컴포넌트의 color prop이 버튼의 배경색에 적용된다.
color 값은 텍스트 색상뿐만 아니라 버튼의 배경색에도 영향을 준다. 이로 인해 iOS에서는 color prop을 사용하여 버튼 색상을 일관되게 관리할 수 있다.
<Button title="Press Me" color="blue" onPress={() => console.log('Pressed')} />
위의 예제에서 color prop은 버튼의 배경색과 텍스트 색상을 모두 'blue'로 변경한다.
동일한 스타일을 두 플랫폼에서 적용하는 방법
iOS와 Android에서 동일한 스타일을 적용하려면 Button 컴포넌트의 color prop만으로는 한계가 있다. 따라서, TouchableOpacity나 TouchableHighlight와 같은 다른 컴포넌트를 사용하고, 스타일을 style prop을 통해 명시적으로 지정하는 방식으로 일관된 디자인을 구현할 수 있다.
import { StatusBar } from 'expo-status-bar';
import { TouchableOpacity, Text, StyleSheet, View } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<StatusBar style="auto" />
<TouchableOpacity
style={styles.button}
onPress={() => console.log('Button Pressed')}
>
<Text style={styles.buttonText}>Press Me</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
button: {
backgroundColor: 'blue', // 버튼 배경색
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 5,
},
buttonText: {
color: 'white', // 버튼 텍스트 색상
fontSize: 16,
fontWeight: 'bold',
},
});
export default App;
iOS에서 Button 컴포넌트의 스타일을 직접 변경하는 데는 제한이 있다. Button 컴포넌트는 iOS에서 기본적인 스타일 속성만을 제공하며, 색상(color prop)만 변경할 수 있는 제한적인 방식으로 동작한다. 특히, iOS에서는 color prop을 통해 버튼의 텍스트 색만 변경할 수 있고, 버튼 배경색과 같은 다른 스타일을 직접 수정하는 것이 불가능하다.
따라서 iOS에서 버튼의 스타일을 더 정밀하게 제어하려면, Button 컴포넌트 대신 TouchableOpacity, TouchableHighlight, Pressable 등의 다른 컴포넌트를 사용하는 것이 필요하다. 이러한 컴포넌트들은 스타일링에 훨씬 더 많은 유연성을 제공하며, 버튼의 색상, 배경색, 크기 등을 자유롭게 커스터마이즈할 수 있다.
iOS에서 버튼의 스타일을 더 정밀하게 제어 필요시,
TouchableOpacity, TouchableHighlight, Pressable 등의 다른 컴포넌트를 사용하기 🚀
TouchableOpacity 컴포넌트를 사용하여 버튼의 색이 클릭 시 변경되도록 하려면, 상태를 관리하는 방법이 필요하다. iOS 환경에서 버튼을 클릭했을 때 버튼 색상이 변경되도록 하기 위해서는 useState 훅을 사용해 버튼의 색상을 동적으로 변경할 수 있다.
아래는 버튼 클릭 시 색상이 바뀌는 예시 코드이다. useState를 사용하여 버튼 색상을 동적으로 변경하고, 상태에 따라 색을 바꾸는 방식으로 작성되었다.
import { StatusBar } from 'expo-status-bar';
import { Button, StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import { useState } from 'react';
const App = () => {
// 버튼 색상을 관리하기 위한 상태
const [buttonColor, setButtonColor] = useState('blue'); // 초기 색상은 blue로 설정
// 버튼 클릭 시 색상 변경
const handlePress = () => {
setButtonColor(buttonColor === 'blue' ? 'red' : 'blue'); // 색상을 토글
};
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text style={[styles.error, styles.text]}>Calculate App</Text>
<Text style={[styles.error, styles.text]}>StyleSheet</Text>
{/* TouchableOpacity로 버튼을 감싸고 클릭 시 색상 변경 */}
<TouchableOpacity
style={[styles.button, { backgroundColor: buttonColor }]} // buttonColor 상태를 배경색으로 적용
onPress={handlePress}
>
<Text style={styles.buttonText}>BUTTON</Text>
</TouchableOpacity>
</View>
);
};
// 스타일 객체
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 30,
fontWeight: '700',
color: 'green',
},
button: {
padding: 15,
borderRadius: 5,
alignItems: 'center',
justifyContent: 'center',
},
buttonText: {
color: 'white',
fontSize: 20,
},
});
export default App;
이 코드는 iOS에서 버튼을 클릭할 때마다 버튼의 색상이 파란색과 빨간색으로 변경된다.
3-3. 커스텀 Button 컴포넌트 만들기
리액트 네이티브의 기본 Button 컴포넌트는 간단한 버튼을 만들 때 유용하지만, 스타일을 자유롭게 커스터마이징하는 데 한계가 있다. 특히 style prop을 지원하지 않아 버튼의 크기나 여백 등을 직접 설정할 수 없기 때문에 TouchableOpacity와 같은 다른 컴포넌트를 활용해 커스텀 버튼을 만들어야 한다.
스타일 제한 : 기본 Button 컴포넌트는 color prop을 제외하고 배경색, 크기, 여백 등을 직접 조정할 수 없음.
일관된 디자인 적용 : 앱에서 여러 곳에서 같은 스타일의 버튼을 사용할 경우, 하나의 커스텀 버튼 컴포넌트를 만들어 재사용 가능.
추가 기능 구현 가능 : 애니메이션 적용, 로딩 상태 표시, 아이콘 추가 등 기능을 쉽게 추가할 수 있음.
리액트 네이티브에서 컴포넌트를 만들 때는 함수 컴포넌트를 사용한다. 함수 컴포넌트는 JavaScript 함수로 작성되며 JSX를 반환해야 한다.
함수 컨포넌트 작성 규칙
- 컴포넌트 이름은 대문자로 시작해야 한다.
- 리액트에서는 컴포넌트와 HTML 태그를 구분하기 위해 컴포넌트의 이름을 대문자로 작성하는 것이 규칙이다.
- JSX를 반환해야 한다.
- return 문을 통해 JSX를 반환해야 화면에 렌더링할 수 있다.
아래와 같이 Button.js 파일을 생성하고, TouchableOpacity를 이용한 커스텀 버튼을 구현할 수 있다.
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
// 커스텀 버튼 컴포넌트
const Button = () => {
return <Text>My Button</Text>;
};
export default Button;
export default를 이용해 Button 함수를 다른 파일에서 사용할 수 있도록 한다.
이를 App 컴포넌트에서 사용해보자.
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import Button from '../components/Button';
...생략
이때 유의할 점은 src 폴더의 App.js는 components 폴더와 상대경로로 2개 차이나므로 App.js에서 접근하기 위해서는 App.js이 속한 src폴더에서 나오는 (1)번과 components로 들어가는 (2)과정을 수행해야한다.
현재 Button.js는 /Users/apple/Desktop/RN/rn-calc/components/Button.js에 위치하지만, App.js가 /Users/apple/Desktop/RN/rn-calc/src/App.js에 위치하고 있다.
따라서 import Button from './components/Button';는 src/components/Button.js를 찾게 되는데, 해당 경로에는 Button.js 파일이 존재하지 않는다.
App.js에서 Button.js를 가져오려면 ../components/Button을 사용해야 한다.
props 파라미터로 데이터 전달받기
컴포넌트에 전달한 props는 함수의 파라미터로 전달된다. 함수의 인자에 props를 추가하면 다음과 같은 터미널 출력을 확인할 수 있다.
이를 통해 우리가 Button 컴포넌트를 사용하면서 전달한 데이터가 모두 들어오는 것을 볼 수 있다.
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
// 커스텀 버튼 컴포넌트
const Button = (props) => {
console.log(props);
return <Text>My Button</Text>;
};
export default Button;
(NOBRIDGE) LOG {"color": "purple", "onPress": [Function onPress], "title": "button"}
이제 props로 전달된 데이터 중 title을 사용해 텍스트를 변경해보자.
이때 props로 전달된 title은 src/App.js에서 선언한 버튼의 타이틀을 의미한다.
//..src/App.js
const App = () => {
const isError = true;
return (
<View style={styles.container}>
<StatusBar style="auto" />
<Text style={[styles.error, 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"
onPress={() => console.log('Click!')}
color="purple"
>
{/* style = {{ backgroundColor: 'black', color: 'white' }} */}
</Button>
</View>
);
};
Button.js를 다음과 같이 수정해보자.
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
// 커스텀 버튼 컴포넌트
const Button = (props) => {
console.log(props);
// return <Text>My Button</Text>;
return <Text>{props.title}</Text>;
};
export default Button;
객체 구조 분해 할당
객체 구조 분해 할당(Object Destructuring)은 객체에서 특정 속성만 추출하여 변수에 할당하는 문법이다.
예를 들어, 아래와 같이 props를 직접 사용하면 props.title과 props.onPress처럼 매번 props를 참조해야 한다.
하지만 구조 분해 할당을 사용하면 코드가 간결해지고 가독성이 좋아진다.
const Button = ({ title, onPress }) => {
console.log(title, onPress);
return <Text>{title}</Text>;
};
객체 구조 분해를 통해 props에서 title만 추출해서 원하는 데이터만 사용할 수 있다. 예를 들어 다음과 같다.
const obj = { a: 1, b : 2, c: 3 } const { a , b , c } console.log(a) //1 console.log(b) //2 console.log(d) //undifined |
a와 b는 obj 객체 안에 같은 이름의 프로퍼티가 존재하므로 정상적으로 출력되고 d는 존재하지 않으므로 undifinded가 된다.
'App' 카테고리의 다른 글
[React Native] #1-2. 리액트 네이티브 프로젝트 생성하기 (2) | 2025.02.02 |
---|---|
[React Native] #1-1. 리액트 네이티브 시작하기 (1) | 2025.02.01 |