리액트 네이티브는 리액트 (React.js) 기반으로 만들어진 앱 개발 기술
Expo
리액트 네이티브를 사용하는 상황과 Expo를 사용하여 앱을 만드는 상황은 다르다.
React Native vs Expo
초기 앱 기획 시 어떤 기능이 필요할지 나열 후 Expo에 해당 기능 지원 여부 확인 후 진행
Node 버전 관리 필요성
Node 버전 관리 툴 nvm
nvm 설치 및 사용
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash
- Node version 12
```
nvm install 12
nvm use 12.20.1
```
Expo 명령어 도구 설치
sudo npm install -g expo-cli
Expo 가입 및 로컬에 계정 세팅
expo login --username "Expo 사이트 가입당시 입력한 name"
...
expo 패스워드 입력란 > 차례대로 입력하면 로그인
Expo 프로젝트 생성
expo init 프로젝트명
// blank template 선택
Expo 프로젝트 실행
expo start
Prettier 설치
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true
}
1. mac (shift + command + p) / window (ctrl + ,)
2. user settings (JSON)
// 추가
{
...
"javascript.format.enable": false,
"prettier.jsxBracketSameLine": true
}
3. Code -> Preferences -> Settings
4. Format On Save 설정 체크
5. Default Formatter 설정 esbenp.prettier-vscode로 변경
기본적으로 .js 확장자 (자바스크립트) 기준으로 규칙 적용하기 때문에 발생.
따라서 <> 태그 문법과 자바스크립트 문법 동시 사용 JSX 문법 코드 파일은 .jsx 확장자로 변경.
1. 모든 태그는 가져와서 사용
import { StyleSheet, Text, View } from 'react-native';
2. 태그는 항상 닫는 태그와 자체적으로 닫는 태그 구분 사용
3. 모든 엘리먼트는 감싸는 최상위 엘리먼트 존재 (엘리먼트 == 태그)
<View>
<Text>Open up App.js to start working on your app!</Text>
<StatusBar style="auto" />
</View>
<>
<View>
<Text>Open up App.js to start working on your app!</Text>
</View>
<StatusBar style="auto" />
</>
4. return에 의해 렌더링 시 항상 소괄호로 감싸져야 한다
5. JSX 문법 밖과 안의 주석은 다르다
export default function App() {
//JSX밖에서의 주석
return (
//JSX 밖에서의 주석
<View style={styles.container}>
{/*
JSX 문법 안에서의 주석
*/}
<Text>Open up App.js to start working on your app!</Text>
<StatusBar style="auto" />
</View>
);
}
< View > </ View >
import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<View style={styles.subContainerOne}></View>
<View style={styles.subContainerTwo}></View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
},
subContainerOne: {
flex: 1,
backgroundColor: "yellow",
},
subContainerTwo: {
flex: 1,
backgroundColor: "green",
},
});
< Text >
<View style={styles.container}>
<Text>
그날이 오면 그날이 오며는 삼각산(三角山)이 일어나 더덩실 춤이라도 추고,
한강(漢江)물이 뒤집혀 용솟음칠 그날이, 이 목숨이 끊기기 전에 와주기만 할
양이면 나는 밤하늘에 날으는 까마귀와 같이 종로(鐘路)의 인경을 머리로
들이받아 울리오리다. 두개골은 깨어져 산산조각이 나도 기뻐서 죽사오매 오히려
무슨 한(恨)이 남으오리까.
</Text>
</View>
줄바꿈
<View style={styles.container}>
<Text>
{`그날이 오면 그날이 오며는
삼각산(三角山)이 일어나 더덩실 춤이라도 추고,
한강(漢江)물이 뒤집혀 용솟음칠 그날이,
이 목숨이 끊기기 전에 와주기만 할 양이면
나는 밤하늘에 날으는 까마귀와 같이
종로(鐘路)의 인경을 머리로 들이받아 울리오리다.
두개골은 깨어져 산산조각이 나도
기뻐서 죽사오매 오히려 무슨 한(恨)이 남으오리까. `}
</Text>
</View>
<View style={styles.container}>
<Text>
그날이 오면 그날이 오며는{"\n"}
삼각산(三角山)이 일어나 더덩실 춤이라도 추고,{"\n"}
한강(漢江)물이 뒤집혀 용솟음칠 그날이,{"\n"}이 목숨이 끊기기 전에 와주기만
할 양이면{"\n"}
나는 밤하늘에 날으는 까마귀와 같이{"\n"}
종로(鐘路)의 인경을 머리로 들이받아 울리오리다.{"\n"}
두개골은 깨어져 산산조각이 나도{"\n"}
기뻐서 죽사오매 오히려 무슨 한(恨)이 남으오리까.{"\n"}
</Text>
</View>
말줄임표
<View style={styles.container}>
<Text numberOfLines={3}>
그날이 오면 그날이 오며는{"\n"}
삼각산(三角山)이 일어나 더덩실 춤이라도 추고,{"\n"}
한강(漢江)물이 뒤집혀 용솟음칠 그날이,{"\n"}이 목숨이 끊기기 전에 와주기만
할 양이면{"\n"}
나는 밤하늘에 날으는 까마귀와 같이{"\n"}
종로(鐘路)의 인경을 머리로 들이받아 울리오리다.{"\n"}
두개골은 깨어져 산산조각이 나도{"\n"}
기뻐서 죽사오매 오히려 무슨 한(恨)이 남으오리까.{"\n"}
</Text>
</View>
< TouchableOpacity />
export default function App() {
const customAlert = (title = "기본값") => {
console.log(title);
if (title) {
Alert.alert(title);
} else {
Alert.alert("TouchableOpacity에도 onPress 속성이 있습니다");
}
};
const customAlert2 = () => {
Alert.alert(
"함수이름을 onPress에 바로 사용할땐 아무것도 넘겨도 함수에서 받는 파라미터에도 아무것도 있으면 안되요!"
);
};
return (
<ScrollView style={styles.container}>
<TouchableOpacity
style={styles.textContainer}
onPress={() => {
customAlert("값을 함수로 전달");
}}>
<Text style={styles.textStyle}>텍스트 버튼</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.textContainer}
onPress={() => customAlert()}>
<Text style={styles.textStyle}>텍스트 버튼</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.textContainer} onPress={customAlert2}>
<Text style={styles.textStyle}>텍스트 버튼</Text>
</TouchableOpacity>
</ScrollView>
);
}
// 정의한 함수에 데이터를 넘길 경우
onPress={() => { customAlert('값을 함수로 넘겨줄 수도 있습니다.'); }}
onPress={() => customAlert()}
// 데이터를 넘기지 않고 함수만 실행시킬 경우
onPress={customAlert2}
< Image >
import favicon from "./assets/favicon.png";
export default function App() {
return (
<View style={styles.container}>
<Image source={favicon} resizeMode={"repeat"} style={styles.imageStyle} />
</View>
);
}
export default function App() {
return (
<View style={styles.container}>
<Image
source={{
uri: "https://images.unsplash.com/photo-1424819827928-55f0c8497861?fit=crop&w=600&h=600%27",
}}
resizeMode={"cover"}
style={styles.imageStyle}
/>
</View>
);
}
다양한 태그 사용법 - Expo 공식 문서
<View style={styles.container}>
const styles = StyleSheet.create({
container: {
//영역을 잡는 속성입니다. 따로 자세히 다룹니다.
//flex: 1은 전체 화면을 가져간다는 뜻입니다
flex: 1,
//영역의 배경 색을 결정합니다
backgroundColor: "#fff",
//아래 두 속성은 영역 안의 컨텐츠들의 배치를 결정합니다.
//flex를 자세히 다룰때 같이 자세히 다룹니다
justifyContent: "center",
alignContent: "center",
},
textContainer: {
//영역의 바깥 공간 이격을 뜻합니다(하단 이미지 참조)
margin: 10,
//영역 안의 컨텐츠 이격 공간을 뜻합니다(하단 이미지 참조)
padding: 10,
//테두리의 구부러짐을 결정합니다. 지금 보면 조금 둥글죠?
borderRadius: 10,
//테두리의 두께를 결정합니다
borderWidth: 2,
//테두리 색을 결정합니다
borderColor: "#000",
//테두리 스타일을 결정합니다. 실선은 solid 입니다
borderStyle: "dotted",
},
textStyle: {
//글자 색을 결정합니다. rgb, 값 이름, 색상코드 모두 가능합니다
color: "red",
//글자의 크기를 결정합니다
fontSize: 20,
//글자의 두께를 결정합니다
fontWeight: "700",
//가로기준으로 글자의 위치를 결정합니다
textAlign: "center",
},
});
한 태그에 여러 개의 스타일 부여
<Text style={[styles.BlueText, styles.BigText, styles.CenterText]}>이다영</Text>
flex
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerOne: {
flex: 1,
backgroundColor: "white",
},
containerTwo: {
flex: 2,
backgroundColor: "lightgray",
},
});
flexDirection
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerOne: {
flex: 1,
backgroundColor: "white",
},
containerTwo: {
flex: 2,
flexDirection: "row",
backgroundColor: "black",
padding: 5,
},
innerOne: {
flex: 1,
backgroundColor: "yellow",
},
innerTwo: {
flex: 4,
backgroundColor: "orange",
},
});
justifyContent
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerOne: {
flex: 1,
backgroundColor: "white",
},
containerTwo: {
flex: 2,
flexDirection: "row",
backgroundColor: "black",
padding: 5,
},
innerOne: {
flex: 1,
backgroundColor: "yellow",
},
innerTwo: {
flex: 4,
justifyContent: "flex-start",
backgroundColor: "orange",
},
});
flex flexDirection justifyContent
alignItems
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerOne: {
flex: 1,
backgroundColor: "white",
},
containerTwo: {
flex: 2,
flexDirection: "row",
backgroundColor: "black",
},
innerOne: {
flex: 1,
backgroundColor: "yellow",
},
innerTwo: {
flex: 4,
backgroundColor: "orange",
alignItems: "flex-end",
},
content: {
width: 50,
height: 50,
backgroundColor: "lightblue",
},
});
alignSelf
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerOne: {
flex: 1,
backgroundColor: "white",
},
containerTwo: {
flex: 2,
flexDirection: "row",
backgroundColor: "black",
},
innerOne: {
flex: 1,
backgroundColor: "yellow",
},
innerTwo: {
flex: 4,
backgroundColor: "orange",
alignItems: "flex-end",
},
content: {
width: 50,
height: 50,
backgroundColor: "lightblue",
alignSelf: "center",
},
});
컴포넌트 (Component)
App.js == App 컴포넌트
상태 (State)
속성 (Props)
useEffect
App.jsx
import React from "react";
import { StyleSheet, View, Text, Alert } from "react-native";
import PopupButton from "./components/PopupButton";
export default function App() {
const CustomAlert = () => {
Alert.alert("Main page pop up!");
};
return (
<View style={styles.contianer}>
<Text> Main Page </Text>
<PopupButton CustomAlert={CustomAlert} title={"Main Page"} />
</View>
);
}
const styles = StyleSheet.create({
contianer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
PopupButton.jsx
import React from "react";
import { StyleSheet, View, Text, TouchableOpacity } from "react-native";
export default function PopupButton({ CustomAlert, title }) {
return (
<View>
<TouchableOpacity onPress={CustomAlert}>
<Text>{title} Pop Up Button</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({});
App.jsx
import React from "react";
import { StyleSheet, ScrollView, View, Text } from "react-native";
import data from "./data.json";
export default function App() {
return (
<ScrollView contentContainerStyle={styles.contianer}>
{data.diary.map((content, i) => {
return (
<View>
<Text>{content.title}</Text>
</View>
);
})}
</ScrollView>
);
}
const styles = StyleSheet.create({
contianer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
key = {i}
로 key 속성으로 연결상태 (State, useState)
상태
라고 일컫음 UI = component(state)
App.jsx
import React, { useState } from "react";
import { StyleSheet, ScrollView, View, Text, Alert } from "react-native";
import data from "./data.json";
export default function App() {
const [state, setState] = useState(data.diary);
return (
<ScrollView contentContainerStyle={styles.contianer}>
{state.map((content, i) => {
return (
<View key={i}>
<Text>{content.title}</Text>
</View>
);
})}
</ScrollView>
);
}
const styles = StyleSheet.create({
contianer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
useState(data.diary)
useEffect(()=>{
...화면이 그려진 다음 가장 먼저 실행되야 할 코드 작성 공간
},[])
import React, { useEffect, useState } from "react";
import { StyleSheet, ScrollView, View, Text, Alert } from "react-native";
import data from "./data.json";
const tempList = [
{ title: 1 },
{ title: 2 },
{ title: 3 },
{ title: 4 },
{ title: 5 },
];
export default function App() {
const [state, setState] = useState(tempList);
useEffect(() => {
setTimeout(() => {
setState(data.diary);
}, 5000);
}, []);
return (
<ScrollView contentContainerStyle={styles.contianer}>
{state.map((content, i) => {
return (
<View key={i}>
<Text>{content.title}</Text>
</View>
);
})}
</ScrollView>
);
}
const styles = StyleSheet.create({
contianer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});