코드의 작동 원리에대해서 아는것도 중요하지만
자전거를 탈때 자전거 작동원리를 몰라도 탈 수 있는것 처럼
일단 먼저 실행하고 작동시켜본 다음 천천히 알아가는것도 중요하다.
- 리액트 네이티브는 리액트(React.js) 라이브러리 기반으로 만들어진 프레임워크 입니다
- 리액트(React.js) 기본적인 구조
- 상단에 내가 사용할 도구들(자바스크립트 파일)을 Import 해서 준비시켜놓음
자바스크립트 파일 안에는 항상 큰 기본이 되는 함수가 있고 그 함수를 export해서 사용하는 구조로 되어있음
ex)
//리액트의 모~든 파일은 컴포넌트라 생각하고
//페이지 기능을 해주는 모든 기능이 담겨 있는 컴포넌트를 만든다 생각하세요!
const StackNavigator = () =>{
return (
)
}
export default StackNavigator;
1) 컴포넌트(Component) : 정해진 엘리먼트들(요소)을 사용하여 만든 화면의 일부분
2) 상태(State) : 컴포넌트에서 데이터를 유지하고 관리하기 위한 유일한 방법 == 그냥 사용할 데이터!
3) 속성(Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식 == 그냥 데이터 전달!(ex.numberOfLines에 3이라는 데이터를 전달 - 3줄까지만 나옴)
4) useEffect : 화면에 컴포넌트가 그려지면 처음 실행해야 하는 함수들을 모아두는 곳
- 1) 컴포넌트(Component)
- 컴포넌트는 UI의 요소, 요소를 재사용 가능한 부분으로 조각내서 운영하는 기법
- 컴포넌트는 즉, 화면의 모든 부분입니다. 그리고 컴포넌트란 App.js의 큰 App 함수처럼, 코드 전체를 감싸고 있는 함수를 뜻하기도 합니다. 따라서 App.js를 App 컴포넌트라고도 부를 수 있습니다.
- 이렇게 컴포넌트를 많이 만들어 놓을 수록, 다른 앱개발시 용이 하게 재사용 가능
- 2) 속성(Props)
- 속성은 쉽게 생각해서 컴포넌트에 데이터를 전달한다는 것. 그 전달 모습은 키와 벨류의 형태입니다.
- <Text> 태그엔 numberOfLines이란 속성이 있었습니다. 말줌임표 효과를 주었죠!
<Image> 태그엔 resizeMode란 속성이 있었습니다. 이미지가 영역을 차지하는 방식을 나타냈죠.
이 모든 속성들은 공식 문서에 나와 있고, 해당 엘리먼트들이 태어날 때부터 가지고 있는 속성이였습니다.
Props 추가설명
- Props 란 쉽게 말해 부모의 Component 혹은 Container 안의
값, 변수, 함수 등을 자식 Component들 에게 넘겨주려 할 때 사용함.
혹은 자식의 값을 부모의 값으로 가져 올 수도 있음.
- 하지만 직접 컴포넌트에 속성을 부여하는것도 가능!
1. 컴포넌트에 속성(데이터)을 부여해줘서 전달할땐, 키와 벨류(content={content}) 형태로 전달해줘야 할 것
2. 컴포넌트를 반복문 돌릴땐, 컴포넌트마다 고유하다는 것을 표현하기 위해, map에서 나오는 인덱스(i)를 key = {i} 속성
전달 형태로 꼭 넣을것! 입니다.
{content}라는 Props를 만들어(이름 변경가능) content안에 저장된 데이터 값을 자식 component인
Card.js 컴포넌트에 넘겨준다(넘겨줄때 사용하는 형식이 Props)
- 3) 상태(State) = 컴포넌트 안의 동적인 값
- 컴포넌트마다 데이터를 보유하고 관리 할 수 있습니다. 데이터라고 불러도 되지만, 리액트에서는 컴포넌트에서 보유/관리 되는 데이터를 "상태"라 부릅니다.
- 리액트에서 상태(state)는 리액트 라이브러리에서 제공해주는 useState로 생성하고 setState 함수로 변경 할 수 있습니다.
- UI = component(state)
- 상태가 업데이트 되면 화면을 자동으로 다시 그려줌
* useState : 컴포넌트안의 상태데이터를 초기화하거나 변경하기위해 사용하는 도구
useState 사용법
const [state,setState] = useState([])
useState 뒤 () 괄호 안에는 useState의 초기값을 지정해줌(비워둘 수 있고, 리스트형태기때문에 대괄호 사용)
첫번째 state 자리는 useState의 초기값을 받아옴(이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수)
두번째 setState자리는 state에 담긴 데이터를 수정할때 사용하는 함수
* useEffect : 화면이 그려지고 데이터를 준비한 다음 실행되는 함수(상태가 업데이트되면 보여줌)
형식
useEffect(()=>{
...화면이 그려진 다음 가장 먼저 실행되야 할 코드 작성 공간
},[])
useEffect(function, deps)
function : 실행하고자 하는 함수
deps : 배열 형태. function을 실행시킬 조건.
deps에 특정값을 넣게 되면 컴포넌트가 mount 될 때, 지정한 값이 업데이트될 때 useEffect를 실행합니다.
const [state,setState] = useState([])
useEffect(()=>{
setState(data) //내부에서 data.json으로 부터 가져온 데이터를 state 상태에 담고 있음
},[])
보통 useEffect는 데이터를 준비할 때 사용합니다
데이터를 준비한다는 것은, 데이터를 서버로부터 혹은 어디선가로부터 받은 후
상태(state)에 반영한다는 것을 뜻합니다. 이런순서로 말이죠
1) 화면이 그려진다
2) useEffect가 데이터를 state에 useState를 이용하여 업데이트한다
3) 상태(state)가 변경되었으니 화면이 다시 그려진다
** 로딩화면 만들기 실습 **(불필요한 코드 제거함)
Loding.JS ( 로딩화면)
import React from 'react';
import {View,Text,StyleSheet} from 'react-native';
export default function Loading(){
return(<View style={styles.container}><Text style={styles.title}>준비중입니다...</Text></View>)
}
Mainpage.JS(메인화면)
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
const main = 'https://storage.googleapis.com/sparta-image.appspot.com/lecture/main.png'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
export default function MainPage() {
const [state,setState] = useState([])
const [ready,setReady] = useState(true)
useEffect(()=>{
setTimeout(()=>{
setState(data)
setReady(false)
},1000) //뒤의 1000 숫자는 1초를 뜻함, 1초 뒤에 실행되는 코드들이 담겨 있는 함수
},[])
//data.json 데이터는 state에 담기므로 상태에서 꺼내옴
let tip = state.tip;
let todayWeather = 10 + 17;
let todayCondition = "흐림"
return ready ? <Loading/> : (
<View style={styles.cardContainer}>
{
tip.map((content,i)=>{
return (<Card content={content} key={i}/>)
})
}
</View>
</ScrollView>)
}
화면이 그려지는 순서
1) ready 값이 true이므로 return 구문에서 ? 물음표 바로 뒤의 Loading 컴포넌트가 화면에 그려짐
2) 화면이 그려지고 난다음, 1초 이따가 상태값들이 채워지고 변경됨
3) ready 상태 값이 false가 됨
4) 상태값이 변경되었으므로 화면이 다시 그려짐
5) ready 값이 false 이므로 return 구문에서 : 콜론 뒤의 MainPage 컴포넌트가 화면에 그려짐
** 카테고리 버튼(전체보기, 생활, 꿀팁 찜 등) 만들기 실습 **
Mainpage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
const main = 'https://storage.googleapis.com/sparta-image.appspot.com/lecture/main.png'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
export default function MainPage() {
//useState 사용법
//[state,setState] 에서 state는 이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
//setState는 state를 변경시킬때 사용해야하는 함수
//모두 다 useState가 선물해줌
//useState()안에 전달되는 값은 state 초기값
const [state,setState] = useState([])
const [cateState,setCateState] = useState([])
//하단의 return 문이 실행되어 화면이 그려진다음 실행되는 useEffect 함수
//내부에서 data.json으로 부터 가져온 데이터를 state 상태에 담고 있음
const [ready,setReady] = useState(true)
useEffect(()=>{
//뒤의 1000 숫자는 1초를 뜻함
//1초 뒤에 실행되는 코드들이 담겨 있는 함수
setTimeout(()=>{
setState(data.tip)
setCateState(data.tip)
setReady(false)
},1000)
},[])
const category = (cate) => {
if(cate == "전체보기"){
//전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
setCateState(state)
}else{
setCateState(state.filter((d)=>{
return d.category == cate
}))
}
}
//data.json 데이터는 state에 담기므로 상태에서 꺼내옴
// let tip = state.tip;
let todayWeather = 10 + 17;
let todayCondition = "흐림"
//return 구문 밖에서는 슬래시 두개 방식으로 주석
return ready ? <Loading/> : (
/*
return 구문 안에서는 {슬래시 + * 방식으로 주석
*/
<ScrollView style={styles.container}>
<Text style={styles.title}>나만의 꿀팁</Text>
<Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
<Image style={styles.mainImage} source={{uri:main}}/>
<ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
<TouchableOpacity style={styles.middleButtonAll} onPress={()=>{category('전체보기')}}><Text style={styles.middleButtonTextAll}>전체보기</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton01} onPress={()=>{category('생활')}}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton03} onPress={()=>{category('반려견')}}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton04} onPress={()=>{category('꿀팁 찜')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
</ScrollView>
<View style={styles.cardContainer}>
{/* 하나의 카드 영역을 나타내는 View */}
{
cateState.map((content,i)=>{
return (<Card content={content} key={i}/>)
})
}
</View>
</ScrollView>)
}
버튼 각각의 touchableOpacity에 onPress에서 category 함수가 실행되도록 만들어져있고 '전체보기, 생활 '등의
인자(Argument)를 넘겨주고 있다.
category 함수에서는 넘겨받은 인자를 cate라는 매개변수(parameter)에 저장 하고
그 매개변수가 '전체보기' 일경우 state(전체데이터)로 상태를 변경하고
필터함수를 써서 d에저장된 전체 리스트(필터 함수 사용법 참고)의 category 키값이 cate(넘겨받은 인자) 일경우
그 리스트로 상태를 업데이트 시켜준다.
* 정리
1. 화면이 그려짐
2. useEffect가 실행
3. 데이터가 준비됨
4. 상태변경이 일어나면 화면이 다시 그려짐
- 앱 기능추가
Expo SDK 활용
- Expo 공홈 API reference 참고하여 명령어로 설치 후 사용가능(VS code terminal에서 설치시 split terminal 누르면 창 하나 더 열려서 쉽게 설치가능)
예시 ; StatusBar 색깔바꾸기(사용 방법은 공홈 참고)
명령어 expo install expo-status-bar로 설치 후 <StatusBar style="light" /> 추가
- Navigator
앱안에서 웹 사이트를 이용하듯, 앱에서 내가 만든 컴포넌트들을 페이지화 시켜주고, 해당 페이지끼리 이동을 가능하게 해주는 기술 또는 라이브러리
- Stack Navigator(컴포넌트를 페이지화 시켜주는 도구)
- React Navigation이라는 큰 도구상자 안에 들어있는 도구 중 하나
- 컴포넌트에 페이지 기능을 부여해주고 컴포넌트에서 컴포넌트로 이동, 즉 페이지 이동을 가능하게 해줍니다
- 열어본 페이지들이 하나씩 쌓여있는 상태에서 뒤로가기를 했을 때, 쌓여진 스택이 차례차례 열리는 구조
- 책갈피 같은 느낌으로 책갈피를 열었을때 바로 그 페이지로 이동됨
원리
StackNavigator.js
import React from 'react';
//설치한 스택 네비게이션 라이브러리를 가져옵니다
import { createStackNavigator } from '@react-navigation/stack';
//페이지로 만든 컴포넌트들을 불러옵니다
import DetailPage from '../pages/DetailPage';
import MainPage from '../pages/MainPage';
//스택 네비게이션 라이브러리가 제공해주는 여러 기능이 담겨있는 객체를 사용합니다
//그래서 이렇게 항상 상단에 선언하고 시작하는게 규칙입니다!
const Stack = createStackNavigator();
const StackNavigator = () =>{
return (
//컴포넌트들을 페이지처럼 여기게끔 해주는 기능을 하는 네비게이터 태그를 선언합니다.
//위에서 선언한 const Stack = createStackNavigator(); Stack 변수에 들어있는 태그를 꺼내 사용합니다.
//Stack.Navigator 태그 내부엔 페이지(화면)를 스타일링 할 수 있는 다양한 옵션들이 담겨 있습니다.
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: "black",
borderBottomColor: "black",
shadowColor: "black",
height:100
},
headerTintColor: "#FFFFFF",
headerBackTitleVisible: false
}}
>
{/* 컴포넌트를 페이지로 만들어주는 엘리먼트에 끼워 넣습니다. 이 자체로 이제 페이지 기능을 합니다*/}
<Stack.Screen name="MainPage" component={MainPage}/>
<Stack.Screen name="DetailPage" component={DetailPage}/>
</Stack.Navigator>
)
}
export default StackNavigator;
App.js
import React from 'react';
//이제 모든 페이지 컴포넌트들이 끼워져있는 책갈피를 메인에 둘예정이므로
//컴포넌트를 더이상 불러오지 않아도 됩니다.
// import MainPage from './pages/MainPage';
// import DetailPage from './pages/DetailPage';
import { StatusBar } from 'expo-status-bar';
//메인에 세팅할 네비게이션 도구들을 가져옵니다.
import {NavigationContainer} from '@react-navigation/native';
import StackNavigator from './navigation/StackNavigator'
export default function App() {
console.disableYellowBox = true;
return (
<NavigationContainer> //NavigationContainer 부분은 하나의 책이라고 생각하면됨
<StatusBar style="black" /> App.js에서 책을 보여주고 세부 책갈피/내용은
<StackNavigator/> StackNavigator.js에서 수정
</NavigationContainer>);
}
* 정리
StackNavigator.js 에서는 createStackNavigator 도구를 impot해서 사용할수있게 만들어주고,
StackNavigator라는 함수를 사용해 책갈피 같은 기능을 만들어준다.(책갈피의 색깔, 모양 등 수정가능)그 StackNavigator 함수 안 Stack.screen부분을 통해 우리가 만든 컴포넌트들을 페이지화 시킬 수 있다.
(위에있는 페이지가 먼저 열림)
그 페이지들은 StackNavigator라는 책갈피가 달려있는 구조
App.js 에서는 return문 안에 StackNavigator.js 책갈피를 활용할 수 있는 container(책)를 만들어줌
- Stack Navigation 적용 후 페이지간 이동시키는 방법
Stack.screen에 등록된 모든 페이지 컴포넌트들은 navigation 와 route 라는 딕셔너리(객체)를 속성으로 넘겨받아
사용할 수 있음. 이 두 딕셔너리는 다음과 같은 기능을 갖습니다.
//navigation 객체가 가지고 있는 함수(setOptions와 navigate, route)
navigation.setOptions({ //navigation 딕셔너리안에 setOptions라는 키값을가진 함수가
title:'나만의 꿀팁' 들어있음.(특정 값(나만의 꿀팁)을 전달해주면 제목을 바꿀수있음)
})
//Stack.screen에서 name 속성으로 정해준 이름을 지정해주면 해당 페이지로 이동하는 함수
navigation.navigate("DetailPage")
//navigation 딕셔너리안에 navigate라는 키값을 가진 함수가 있음
(특정 값(DetailPage)을 전달해주면 그 페이지로 이동가능)
navigation.navigate("DetailPage",{
title: title //navigate사용시 두번째인자(title: title)부분에
}) 데이터를 입력해 페이지 이동시 그 데이터를 같이 넘겨줄수 있음
전달된 데이터는 route 딕셔너리로 로 받을 수 있음
//전달받은 데이터를 받는 route 딕셔너리의 형식
//navigate 함수로 전달되는 딕셔너리 데이터는 다음과 같은 모습으로 전달됨
{
route : {
params :{
title:title
}
}
}
//그렇기 때문에 사용시에는 비구조 할당 방식으로 route에 params 객체 키로 연결되어 전달되는 데이터를 꺼내 사용
const {title} = route.params;
실습
MainPage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
const main = 'https://storage.googleapis.com/sparta-image.appspot.com/lecture/main.png'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
import { StatusBar } from 'expo-status-bar';
export default function MainPage({navigation,route}) {
//useState 사용법
//[state,setState] 에서 state는 이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
//setState는 state를 변경시킬때 사용해야하는 함수
//모두 다 useState가 선물해줌
//useState()안에 전달되는 값은 state 초기값
const [state,setState] = useState([])
const [cateState,setCateState] = useState([])
//하단의 return 문이 실행되어 화면이 그려진다음 실행되는 useEffect 함수
//내부에서 data.json으로 부터 가져온 데이터를 state 상태에 담고 있음
const [ready,setReady] = useState(true)
useEffect(()=>{
//뒤의 1000 숫자는 1초를 뜻함
//1초 뒤에 실행되는 코드들이 담겨 있는 함수
setTimeout(()=>{
//헤더의 타이틀 변경
navigation.setOptions({
title:'나만의 꿀팁'
})
setState(data.tip)
setCateState(data.tip)
setReady(false)
},1000)
},[])
const category = (cate) => {
if(cate == "전체보기"){
//전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
setCateState(state)
}else{
setCateState(state.filter((d)=>{
return d.category == cate
}))
}
}
//data.json 데이터는 state에 담기므로 상태에서 꺼내옴
// let tip = state.tip;
let todayWeather = 10 + 17;
let todayCondition = "흐림"
//return 구문 밖에서는 슬래시 두개 방식으로 주석
return ready ? <Loading/> : (
/*
return 구문 안에서는 {슬래시 + * 방식으로 주석
*/
<ScrollView style={styles.container}>
<StatusBar style="light" />
{/* <Text style={styles.title}>나만의 꿀팁</Text> */}
<Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
<Image style={styles.mainImage} source={{uri:main}}/>
<ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
<TouchableOpacity style={styles.middleButtonAll} onPress={()=>{category('전체보기')}}><Text style={styles.middleButtonTextAll}>전체보기</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton01} onPress={()=>{category('생활')}}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton03} onPress={()=>{category('반려견')}}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
<TouchableOpacity style={styles.middleButton04} onPress={()=>{category('꿀팁 찜')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
</ScrollView>
<View style={styles.cardContainer}>
{/* 하나의 카드 영역을 나타내는 View */}
{
cateState.map((content,i)=>{
return (<Card content={content} key={i} navigation={navigation}/>)
})
}
</View>
</ScrollView>)
}
- export default function MainPage({navigation,route}) 에서
navigation,route는 StackNavigator.js의 Stack.screen 부분에서 MainPage를 페이지화 시켰기때문에
페이지화 시킨 컴포넌트는 Stack.screen에서 navigation,route 딕셔너리를 넘겨받아 자동으로 사용 할 수 있다.
- return (<Card content={content} key={i} navigation={navigation}/>) 부분에서
navigation={navigation} 은 card 컴포넌트가 페이지화 되지 않았기때문에 navigation을 사용할 수 없지만
MainPage.js가 Stack.screen으로 페이지화 되었기때문에 Card 컴포넌트(자식 컴포넌트/내부 컴포넌트)에
navigation도구를 사용하게 전달해 줄 수 있다.
Card.js
import React from 'react';
import {View, Image, Text, StyleSheet,TouchableOpacity} from 'react-native'
//MainPage로 부터 navigation 속성을 전달받아 Card 컴포넌트 안에서 사용
export default function Card({content,navigation}){
return(
//카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
<TouchableOpacity style={styles.card} onPress={()=>{navigation.navigate('DetailPage',**content**)}}>
<Image style={styles.cardImage} source={{uri:content.image}}/>
<View style={styles.cardText}>
<Text style={styles.cardTitle} numberOfLines={1}>{content.title}</Text>
<Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text>
<Text style={styles.cardDate}>{content.date}</Text>
</View>
</TouchableOpacity>
)
}
숙제 하다가 막힌 것
1. 제목 스타일 변경
AboutPage.js
export default function AboutPage({navigation,route}){
const aboutImage = "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2FaboutImage.png?alt=media&token=13e1c4f6-b802-4975-9773-e305fc7475c4"
useEffect(()=>{
navigation.setOptions({
title:"소개 페이지",
headerStyle: {
backgroundColor: '#1F266A',
shadowColor: "#1F266A",
},
headerTintColor: "#fff",
})
},[])
StackNavigator.js 에서는 전체적인 컴포넌트 title style 설정 할 수 있고
개별적으로 useEffect에 setOptions 써서 headerStyle 및 name 변경 할 수 있음
2. 중괄호 안넣어서 에러 난 것. 개............
export default function LikePage({navigation,route}){ ◀ 속성 받아올땐 중괄호 넣기;;;;;;;;;;;
'코딩수강일지 > 팀스파르타 앱개발 종합반 수업일지' 카테고리의 다른 글
앱개발 종합반 4주차 수강일지 (0) | 2022.08.17 |
---|---|
앱개발 종합반 3주차 - useState 추가 정리 (0) | 2022.08.09 |
앱개발 종합반 2주차 수강일지 (0) | 2022.06.30 |
앱개발 종합반 1주차 수강일지 (0) | 2022.06.28 |