앱개발 종합반 3주차 개발일지
[앱 필수 기초지식] 리액트 필수지식
리액트 네이티브 앱을 만들기 위해서 알아야 하는 최소한의 리액트 개념을 배웁니다.
- 컴포넌트(Component)
- 상태(State,useState)
- 속성(Props)
- useEffect
나만의 꿀팁 앱 상세 화면 만들어보기
폴더구조
DetailPage.js를 pages 폴더에 만들어 넣기
상세 화면에서 사용될 데이터
const tip = {
"idx":9,
"category":"재테크",
"title":"렌탈 서비스 금액 비교해보기",
"desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ",
"date":"2020.09.09" }
DetailPage.js 화면
import React from 'react';
import { StyleSheet, Text, View, Image, ScrollView,TouchableOpacity,Alert } from 'react-native';
export default function DetailPage() {
const tip = {
"idx":9,
"category":"재테크",
"title":"렌탈 서비스 금액 비교해보기",
"desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ",
"date":"2020.09.09" }
const popup = () => {
Alert.alert("팝업!!")
}
return (
// ScrollView에서의 flex 숫자는 의미가 없습니다. 정확히 보여지는 화면을 몇등분 하지 않고
// 화면에 넣은 컨텐츠를 모두 보여주려 스크롤 기능이 존재하기 때문입니다.
// 여기선 내부의 컨텐츠들 영역을 결정짓기 위해서 height 값과 margin,padding 값을 적절히 잘 이용해야 합니다.
{tip.title} {tip.desc} popup()}>팁 찜하기 ) }
const styles = StyleSheet.create({
container:{
backgroundColor:"#000"
},
image:{
height:400,
margin:10,
marginTop:40,
borderRadius:20
},
textContainer:{
padding:20,
justifyContent:'center',
alignItems:'center'
},
title: {
fontSize:20,
fontWeight:'700',
color:"#eee"
},
desc:{
marginTop:10,
color:"#eee"
},
button:{
width:100,
marginTop:20,
padding:10,
borderWidth:1,
borderColor:'deeppink',
borderRadius:7
},
buttonText:{
color:'#fff',
textAlign:'center'
}
})
[앱 필수 기초지식01] 컴포넌트
1) 컴포넌트(Component) : 정해진 엘리먼트들(요소)을 사용하여 만든 화면의 일부분
2) 상태(State) : 컴포넌트에서 데이터를 유지하고 관리하기 위한 유일한 방법 == 그냥 사용할 데이터!
3) 속성(Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식 == 그냥 데이터 전달!
4) useEffect : 화면에 컴포넌트가 그려지면 처음 실행해야 하는 함수들을 모아두는 곳
컴포넌트(Component)
메인화면 컴포넌트화 해보기
Components 폴더 안에 Card.js라는 파일을 만듭니다.
Card.js
import React from "react"
import {View,Text,Image,StyleSheet} from "react-native";
//비구조 할당 방식으로 넘긴 속성 데이터를 꺼내 사용함
export default function Card({content}) {
return (<View style={styles.card}>
<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>
</View>)
}
const styles = StyleSheet.create({
card:{
flex:1,
//컨텐츠들을 가로로 나열
//세로로 나열은 column <- 디폴트 값임
flexDirection:"row",
margin:10,
borderBottomWidth:0.5,
borderBottomColor:"#eee",
paddingBottom:10
},
cardImage: {
flex:1,
width:100,
height:100,
borderRadius:10,
},
cardText: {
flex:2,
flexDirection:"column",
marginLeft:10,
},
cardTitle: {
fontSize:20,
fontWeight:"700"
},
cardDesc: {
fontSize:15
},
cardDate: {
fontSize:10,
color:"#A6A6A6",
}
})
MainPage.js
import React from 'react';
import { StyleSheet, Text, View,
Image, TouchableOpacity, ScrollView} from 'react-native';
import data from '../data.json';
import Card from '../components/Card';
export default function MainPage() {
let tip = data.tip;
let todayWeather = 10 + 17;
let todayCondition = "흐림"
//return 구문 밖에서는 슬래시 두개 방식으로 주석
return ( /* return 구문 안에서는 {슬래시 + * 방식으로 주석 */
나만의 꿀팁 오늘의 날씨: {todayWeather + '°C ' + todayCondition}
생활 재테크 반려견 꿀팁 찜 {/* 하나의 카드 영역을 나타내는 View */}
{ tip.map((content,i)=>{ //
return ( //
// // {
content.title} // {
content.desc} // {
content.date} // // )
return () }) } ); }
const styles = StyleSheet.create({
container: {
//앱의 배경 색 backgroundColor: '#fff', },
title: {
//폰트 사이즈
fontSize: 20,
//폰트 두께
fontWeight: '700',
//위 공간으로 부터 이격
marginTop:50,
//왼쪽 공간으로 부터 이격
marginLeft:20
},
weather:{
alignSelf:"flex-end",
paddingRight:20
},
mainImage: {
//컨텐츠의 넓이 값 width:'90%',
//컨텐츠의 높이 값
height:200,
//컨텐츠의 모서리 구부리기
borderRadius:10,
marginTop:20,
//컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
//각 속성의 값들은 공식문서에 고대로~ 나와 있음
alignSelf:"center"
},
middleContainer:{
marginTop:20,
marginLeft:10,
height:60
},
middleButton01: {
width:100,
height:50,
padding:15,
backgroundColor:"#fdc453",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton02: {
width:100,
height:50,
padding:15,
backgroundColor:"#fe8d6f",
borderRadius:15,
margin:7
},
middleButton03: {
width:100,
height:50,
padding:15,
backgroundColor:"#9adbc5",
borderRadius:15,
margin:7
},
middleButtonText: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
middleButton04: {
width:100,
height:50,
padding:15,
backgroundColor:"#f886a8",
borderRadius:15,
margin:7
},
cardContainer: {
marginTop:10,
marginLeft:10
},
card:{
flex:1,
//컨텐츠들을 가로로 나열
//세로로 나열은 column <- 디폴트 값임
flexDirection:"row",
margin:10,
borderBottomWidth:0.5,
borderBottomColor:"#eee",
paddingBottom:10
},
cardImage: {
flex:1,
width:100,
height:100,
borderRadius:10,
},
cardText: {
flex:2,
flexDirection:"column",
marginLeft:10, },
cardTitle: {
fontSize:20,
fontWeight:"700"
},
cardDesc: {
fontSize:15
},
cardDate: {
fontSize:10,
color:"#A6A6A6",
},
});
MainPage.js에 있던 코드들을 Card.js로 옮겼습니다. 이렇게 옮기게 되면, 추후에 MainPage.js가 아닌 다른 페이지(페이지 또한 컴포넌트로 볼 수 있습니다) 또는 컴포넌트에서 Card.js를 사용할 수 있습니다.
MainPage.js
Card.js
[앱 필수 기초지식02] 속성(Props)
속성은 쉽게 생각해서 컴포넌트에 데이터를 전달한다는 것이다.
MainPage.js 에서의 Card.js 컴포넌트
1. 컴포넌트에 속성(데이터)을 부여해줘서 전달할땐, 키와 벨류(content={content}) 형태로 전달해줘야 할 것
2. 컴포넌트를 반복문 돌릴땐, 컴포넌트마다 고유하다는 것을 표현하기 위해, map에서 나오는 인덱스(i)를 key = {i} 속성 전달 형태로 꼭 넣을것!
Card.js 에서의 속성 값 내려 받기
MainPage.js에서 넘겨준 속성은 실제 받게되는 컴포넌트에서 정말 딕셔너리 데이터를 받았다! 라고 생각하면 됩니다.
[앱 필수 기초지식03] 상태(useState)와 useEffect
화면이 그려진다음 가장 먼저 실행되는 함수, useEffect
useEffect(()=>{
...화면이 그려진 다음 가장 먼저 실행되야 할 코드 작성 공간
},[])
이 안에서, 화면이 그려진다음 실행시키고 싶은 함수를 작성한다면 가장먼저 실행이 됩니다.
보통 useEffect는 데이터를 준비할 때 사용합니다.
데이터를 준비한다는 것은, 데이터를 서버로부터 혹은 어디선가로부터 받은 후 상태(state)에 반영한다는 것을 뜻합니다.
이런순서로 말이죠
1) 화면이 그려진다
2) useEffect가 데이터를 준비한다
3) 상태 데이터가 업데이트 되었으니 화면이 다시 그려진다
[앱 필수 기초지식 응용] 컴포넌트와 상태를 이용한 로딩화면 만들기
컴포넌트 폴더에 Loading.js 파일을 만들어준다.
Loading.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>)
}
const styles = StyleSheet.create({
container: {
//앱의 배경 색
flex:1,
justifyContent:'center',
alignItems:'center',
backgroundColor: '#fdc453',
},
title: {
fontSize:20,
fontWeight:'700'
}
})
MainPage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
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([])
//하단의 return 문이 실행되어 화면이 그려진다음 실행되는 useEffect 함수
//내부에서 data.json으로 부터 가져온 데이터를 state 상태에 담고 있음
const [ready,setReady] = useState(true) useEffect(()=>{
//뒤의 1000 숫자는 1초를 뜻함
//1초 뒤에 실행되는 코드들이 담겨 있는 함수
setTimeout(()=>{ setState(data) setReady(false) },1000) },[])
//data.json 데이터는 state에 담기므로 상태에서 꺼내옴
let tip = state.tip;
let todayWeather = 10 + 17; let todayCondition = "흐림"
//return 구문 밖에서는 슬래시 두개 방식으로 주석
return ready ? : (
/* return 구문 안에서는 {슬래시 + * 방식으로 주석 */
나만의 꿀팁 오늘의 날씨: {todayWeather + '°C ' + todayCondition}
생활 재테크 반려견 꿀팁 찜
{/* 하나의 카드 영역을 나타내는 View */}
{ tip.map((content,i)=>{ return () }) } ) }
const styles = StyleSheet.create({
container: {
//앱의 배경 색
backgroundColor: '#fff',
},
title: {
//폰트 사이즈
fontSize: 20,
//폰트 두께
fontWeight: '700',
//위 공간으로 부터 이격
marginTop:50,
//왼쪽 공간으로 부터 이격
marginLeft:20
},
weather:{
alignSelf:"flex-end",
paddingRight:20
},
mainImage: {
//컨텐츠의 넓이 값 width:'90%',
//컨텐츠의 높이 값 height:200,
//컨텐츠의 모서리 구부리기
borderRadius:10,
marginTop:20,
//컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
//각 속성의 값들은 공식문서에 고대로~ 나와 있음
alignSelf:"center" },
middleContainer:{
marginTop:20,
marginLeft:10,
height:60
},
middleButton01: {
width:100,
height:50,
padding:15,
backgroundColor:"#fdc453",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton02: {
width:100,
height:50,
padding:15,
backgroundColor:"#fe8d6f",
borderRadius:15,
margin:7
},
middleButton03: {
width:100,
height:50,
padding:15,
backgroundColor:"#9adbc5",
borderRadius:15,
margin:7
},
middleButtonText: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
middleButton04: {
width:100,
height:50,
padding:15,
backgroundColor:"#f886a8",
borderRadius:15,
margin:7
},
cardContainer: {
marginTop:10,
marginLeft:10
},
card:{
flex:1,
//컨텐츠들을 가로로 나열
//세로로 나열은 column <- 디폴트 값임
flexDirection:"row",
margin:10,
borderBottomWidth:0.5,
borderBottomColor:"#eee",
paddingBottom:10
},
cardImage: {
flex:1,
width:100,
height:100,
borderRadius:10,
},
cardText: {
flex:2,
flexDirection:"column",
marginLeft:10,
},
cardTitle: {
fontSize:20,
fontWeight:"700"
},
cardDesc: {
fontSize:15
},
cardDate: {
fontSize:10,
color:"#A6A6A6",
},
});
ready 라는 새로운 상태값이 추가되었습니다.
useEffect안에 setTimeout이라는 함수가 존재합니다.
, 뒤에 있는 숫자 (1000 === 1초) 만큼, 지연됐다가 안에 있는 코드가 실행되는 지연 함수입니다.
즉, 이런 순서로 진행된거에요!
1) ready 값이 true이므로 return 구문에서 ? 물음표 바로 뒤의 Loading 컴포넌트가 화면에 그려짐
2) 화면이 그려지고 난다음, 1초 이따가 상태값들이 채워지고 변경됨
3) ready 상태 값이 false가 됨
4) 상태값이 변경되었으므로 화면이 다시 그려짐
5) ready 값이 false 이므로 return 구문에서 : 콜론 뒤의 MainPage 컴포넌트가 화면에 그려짐
[앱 필수 기초지식 응용] state를 이용한 카테고리 기능 넣기
MainPage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
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 ? : (
/* return 구문 안에서는 {슬래시 + * 방식으로 주석 */
나만의 꿀팁 오늘의 날씨: {todayWeather + '°C ' + todayCondition}
{category('전체보기')}}>전체보기
{category('생활')}}>생활
{category('재테크')}}>재테크
{category('반려견')}}>반려견
{category('꿀팁 찜')}}>꿀팁 찜 {/* 하나의 카드 영역을 나타내는
View */} { cateState.map((content,i)=>{
return () }) } ) }
const styles = StyleSheet.create({
container: { //앱의 배경 색 backgroundColor: '#fff', }, title: {
//폰트 사이즈
fontSize: 20,
//폰트 두께
fontWeight: '700',
//위 공간으로 부터 이격
marginTop:50,
//왼쪽 공간으로 부터 이격
marginLeft:20
},
weather:{
alignSelf:"flex-end",
paddingRight:20
},
mainImage: {
//컨텐츠의 넓이 값
width:'90%',
//컨텐츠의 높이 값
height:200,
//컨텐츠의 모서리 구부리기
borderRadius:10,
marginTop:20,
//컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
//각 속성의 값들은 공식문서에 고대로~ 나와 있음
alignSelf:"center"
},
middleContainer:{
marginTop:20,
marginLeft:10,
height:60
},
middleButtonAll: {
width:100,
height:50,
padding:15,
backgroundColor:"#20b2aa",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton01: {
width:100, height:50,
padding:15,
backgroundColor:"#fdc453",
borderColor:"deeppink",
borderRadius:15, margin:7
},
middleButton02: {
width:100,
height:50,
padding:15,
backgroundColor:"#fe8d6f",
borderRadius:15,
margin:7
},
middleButton03: {
width:100,
height:50,
padding:15,
backgroundColor:"#9adbc5",
borderRadius:15,
margin:7
},
middleButton04: {
width:100,
height:50,
padding:15,
backgroundColor:"#f886a8",
borderRadius:15,
margin:7
},
middleButtonText: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center" },
middleButtonTextAll: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
cardContainer: {
marginTop:10,
marginLeft:10
},
});
제태크를 눌렀을때의 모습
[Expo 앱다운 앱기능] 앱 상태 바(Status Bar) 관리
상태 바란?
앱이 앱에 따라 모바일 맨 위 상태 바가 변하는 앱이 있습니다.
StatusBar
라이브러리 설치
터미널 우측 버튼중에 분할이란 버튼이 있습니다.
Expo 상태 바 설치
expo install expo-status-bar
* 공식 문서:
https://docs.expo.io/versions/latest/sdk/status-bar/
StatusBar - Expo Documentation
Expo is an open-source platform for making universal native apps for Android, iOS, and the web with JavaScript and React.
docs.expo.dev
MainPage에 StatusBar 설치
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
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() {
//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 ? : (
/* return 구문 안에서는 {슬래시 + * 방식으로 주석 */ 나만의 꿀팁 오늘의 날씨: {todayWeather + '°C ' + todayCondition}
{category('전체보기')}}>전체보기
{category('생활')}}>생활
{category('재테크')}}>재테크
{category('반려견')}}>반려견
{category('꿀팁 찜')}}>꿀팁 찜
{/* 하나의 카드 영역을 나타내는 View */}
{ cateState.map((content,i)=>{ return () }) } ) }
const styles = StyleSheet.create({
container: {
//앱의 배경 색
backgroundColor: '#fff', },
title: {
//폰트 사이즈 fontSize: 20,
//폰트 두께 fontWeight: '700', //위 공간으로 부터 이격 marginTop:50,
//왼쪽 공간으로 부터 이격
marginLeft:20
},
weather:{
alignSelf:"flex-end",
paddingRight:20
},
mainImage: {
//컨텐츠의 넓이 값
width:'90%',
//컨텐츠의 높이 값
height:200,
//컨텐츠의 모서리 구부리기
borderRadius:10,
marginTop:20,
//컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
//각 속성의 값들은 공식문서에 고대로~ 나와 있음
alignSelf:"center"
},
middleContainer:{
marginTop:20,
marginLeft:10,
height:60
},
middleButtonAll: {
width:100,
height:50,
padding:15,
backgroundColor:"#20b2aa",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton01: {
width:100,
height:50,
padding:15,
backgroundColor:"#fdc453",
borderColor:"deeppink",
borderRadius:15, margin:7 }, middleButton02: { width:100, height:50, padding:15, backgroundColor:"#fe8d6f", borderRadius:15, margin:7 }, middleButton03: { width:100, height:50, padding:15, backgroundColor:"#9adbc5", borderRadius:15, margin:7 }, middleButton04: { width:100, height:50, padding:15, backgroundColor:"#f886a8", borderRadius:15, margin:7 },
middleButtonText: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
middleButtonTextAll: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
cardContainer: {
marginTop:10,
marginLeft:10
},
});
style이 light일 때와, black일 때가 다릅니다. 우린 앱 화면 배경색을 검은색으로 설정했기 떄문에, 상태바를 black으로 하면 보이지 않습니다. 상태 바 속성은 공식문서에 다양하게 존재하니, 살펴보면서 앱에 적합한 상태 바를 적용해보세요! Status Bar공식문서
https://docs.expo.io/versions/latest/sdk/status-bar/
StatusBar - Expo Documentation
Expo is an open-source platform for making universal native apps for Android, iOS, and the web with JavaScript and React.
docs.expo.dev
black 일때
light 일때
[앱 페이지 적용] 네비게이터란?
react-navigation 공식문서 보러가기 —> [(링크)]
기본 설치 코드
네비게이션 설치 코드
yarn add @react-navigation/native
네비게이션 추가 설치코드
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view
[앱 페이지 적용] 설치 - 스택네비게이션 01
스택 네비게이션이란?
스택 네비게이션은 컴포넌트에 페이지 기능을 부여해주고 컴포넌트에서 컴포넌트로 이동, 즉 페이지 이동을 가능하게 해줍니다.
createStackNavigator 사용해보기
yarn add @react-navigation/stack
navigation 폴더 하나를 만들고 StackNavigator.js 파일을 만들어주세요
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;
스택 네비게이터 코드 분석
https://reactnavigation.org/docs/stack-navigator/
https://reactnavigation.org/docs/stack-navigator/
reactnavigation.org
적용 순서 1) 사용 준비
import React from 'react';
//설치한 스택 네비게이션 라이브러리를 가져옵니다
import { createStackNavigator } from '@react-navigation/stack';
//페이지로 만든 컴포넌트들을 불러옵니다.
import DetailPage from '../pages/DetailPage';
import MainPage from '../pages/MainPage';
//스택 네비게이션 라이브러리가 제공해주는 여러 기능이 담겨있는 객체를 사용합니다
//그래서 이렇게 항상 상단에 선언하고 시작하는게 규칙입니다!
const Stack = createStackNavigator();
적용 순서 2) 기본 틀
//리액트의 모~든 파일은 컴포넌트라 생각하고
//페이지 기능을 해주는 모든 기능이 담겨 있는 컴포넌트를 만든다 생각하세요!
const StackNavigator = () =>{
return (
/// 페이지 기능이 들어갈 곳
)
}
export default StackNavigator;
적용 순서 3) 스크린 옵션
//컴포넌트들을 페이지처럼 여기게끔 해주는 기능을 하는 네비게이터 태그를 선언합니다.
//위에서 선언한 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>
적용 순서 4) 페이지 연결
{/* 컴포넌트를 페이지로 만들어주는 엘리먼트에 끼워 넣습니다. 이 자체로 이제 페이지 기능을 합니다*/}
<Stack.Screen name="MainPage" component={MainPage}/>
<Stack.Screen name="DetailPage" component={DetailPage}/>
컴포넌트를 페이지화 했고, 페이지를 이동할 수 있는 네이게이션도 준비가 됐다면, 우리는 최상단 컴포넌트 즉 App.js에 네비게이션 기능을 달아야 합니다. 즉, 앱 가장 최상위 코드에 네비게이션을 다는겁니다
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>
<StatusBar style="black" />
<StackNavigator/>
</NavigationContainer>
);
}
스택 네비게이터 적용 후 MainPage 모습
[앱 페이지 적용] 페이지 헤더 스타일 수정 - 스택네비게이션 02
페이지 헤더 수정
스택 네비게이터의 헤더 스타일 부분 코드
<Stack.Navigator screenOptions={{
headerStyle: {
backgroundColor: "black",
borderBottomColor: "black",
shadowColor: "black",
height:100 },
//헤더의 텍스트를 왼쪾에 둘지 가운데에 둘지를 결정
headerTintColor: "#fff",
headerBackTitleVisible: false }} >
{/* name에 해당 하는 부분이 페이지의 타이틀이 됩니다.*/}
<Stack.Screen name="MainPage" component={MainPage}/>
<Stack.Screen name="DetailPage" component={DetailPage}/>
</Stack.Navigator>
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: "white",
borderBottomColor: "white",
shadowColor: "white",
height:100
},
//헤더의 텍스트를 왼쪾에 둘지 가운데에 둘지를 결정
headerTitleAlign:'left', headerTintColor: "#000", headerBackTitleVisible: false }} >
{/* 컴포넌트를 페이지로 만들어주는 엘리먼트에 끼워 넣습니다. 이 자체로 이제 페이지 기능을 합니다*/}
<Stack.Screen name="MainPage" component={MainPage}/>
<Stack.Screen name="DetailPage" component={DetailPage}/>
</Stack.Navigator> )
}
export default StackNavigator;
MainPage.js 에서 title 삭제!
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
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() {
//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 ? : (
/* return 구문 안에서는 {슬래시 + * 방식으로 주석 */ {/* 나만의 꿀팁 */}
오늘의 날씨: {todayWeather + '°C ' + todayCondition}
{category('전체보기')}}>전체보기
{category('생활')}}>생활
{category('재테크')}}>재테크
{category('반려견')}}>반려견
{category('꿀팁 찜')}}>꿀팁 찜 {
/* 하나의 카드 영역을 나타내는 View */
}
{ cateState.map((content,i)=>{ return () }) } ) } const styles = StyleSheet.create({
container: {
//앱의 배경 색 backgroundColor: '#fff', },
title: {
//폰트 사이즈
fontSize: 20,
//폰트 두께
fontWeight: '700',
//위 공간으로 부터 이격
marginTop:50,
//왼쪽 공간으로 부터 이격
marginLeft:20 },
weather:{
alignSelf:"flex-end",
paddingRight:20
},
mainImage: {
//컨텐츠의 넓이 값
width:'90%',
//컨텐츠의 높이 값
height:200,
//컨텐츠의 모서리 구부리기
borderRadius:10,
marginTop:20,
//컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
//각 속성의 값들은 공식문서에 고대로~ 나와 있음 alignSelf:"center" },
middleContainer:{
marginTop:20,
marginLeft:10,
height:60 },
middleButtonAll: {
width:100,
height:50,
padding:15,
backgroundColor:"#20b2aa",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton01: {
width:100,
height:50,
padding:15,
backgroundColor:"#fdc453",
borderColor:"deeppink",
borderRadius:15, margin:7 },
middleButton02: {
width:100,
height:50,
padding:15,
backgroundColor:"#fe8d6f",
borderRadius:15, margin:7
},
middleButton03: {
width:100,
height:50,
padding:15,
backgroundColor:"#9adbc5",
borderRadius:15,
margin:7
},
middleButton04: {
width:100,
height:50,
padding:15,
backgroundColor:"#f886a8",
borderRadius:15,
margin:7
},
middleButtonText: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
middleButtonTextAll: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬 textAlign:"center" },
cardContainer: {
marginTop:10,
marginLeft:10 },
});
StackNavigator 옵션 코드 수정 후 모습
[앱 페이지 적용] 페이지 이동하기 - 스택 네비게이션 03
Main 페이지(MainPage.js)에서 카드 버튼을 누르면(Card.js) 꿀팁 상세 페이지(DetailPage.js)로 이동합니다
일단 페이지를 이동시키려면, 책갈피가 페이지들에게 부여해준 페이지 이동 기능을 사용해야 합니다.
Stack.screen에 등록된 모든 페이지 컴포넌트들은 navigation 와 route 라는 딕셔너리(객체)를 속성으로 넘겨받아 사용할 수 있습니다. 이 두 딕셔너리는 다음과 같은 기능을 갖습니다.
//navigation 객체가 가지고 있는 두 함수(setOptions와 navigate)
//해당 페이지의 제목을 설정할 수 있음
navigation.setOptions({
title:'나만의 꿀팁'
})
//Stack.screen에서 name 속성으로 정해준 이름을 지정해주면 해당 페이지로 이동하는 함수 navigation.navigate("DetailPage")
//name 속성을 전달해주고, 두 번째 인자로 딕셔너리 데이터를 전달해주면, Detail 페이지에서
//두번째 인자로 전달된 딕셔너리 데이터를 route 딕셔너리로 로 받을 수 있음
navigation.navigate("DetailPage",{
title: title
})
//전달받은 데이터를 받는 route 딕셔너리
//비구조 할당 방식으로 route에 params 객체 키로 연결되어 전달되는 데이터를 꺼내 사용
//navigate 함수로 전달되는 딕셔너리 데이터는 다음과 같은 모습이기 때문입니다.
/*
{
route : {
params :{
title:title
}
}
}
*/
const { title} = route.params;
데이터 없이 페이지 이동하기
navigation.navigate("DetailPage")
MainPage.js
import React,{useState,useEffect} from 'react'; import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native'; const main = 'https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fmain.png?alt=media&token=8e5eb78d-19ee-4359-9209-347d125b322c' 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 ? : ( /* return 구문 안에서는 {슬래시 + * 방식으로 주석 */ {/* 나만의 꿀팁 */} 오늘의 날씨: {todayWeather + '°C ' + todayCondition}
{category('전체보기')}}>전체보기 {category('생활')}}>생활 {category('재테크')}}>재테크 {category('반려견')}}>반려견 {category('꿀팁 찜')}}>꿀팁 찜 {/* 하나의 카드 영역을 나타내는 View */} { cateState.map((content,i)=>{ return () }) } ) } const styles = StyleSheet.create({ container: { //앱의 배경 색 backgroundColor: '#fff', }, title: { //폰트 사이즈 fontSize: 20, //폰트 두께 fontWeight: '700', //위 공간으로 부터 이격 marginTop:50, //왼쪽 공간으로 부터 이격 marginLeft:20 }, weather:{ alignSelf:"flex-end", paddingRight:20 }, mainImage: { //컨텐츠의 넓이 값 width:'90%', //컨텐츠의 높이 값 height:200, //컨텐츠의 모서리 구부리기 borderRadius:10, marginTop:20, //컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능) //각 속성의 값들은 공식문서에 고대로~ 나와 있음 alignSelf:"center" }, middleContainer:{ marginTop:20, marginLeft:10, height:60 }, middleButtonAll: { width:100, height:50, padding:15, backgroundColor:"#20b2aa", borderColor:"deeppink", borderRadius:15, margin:7 }, middleButton01: { width:100, height:50, padding:15, backgroundColor:"#fdc453", borderColor:"deeppink", borderRadius:15, margin:7 }, middleButton02: { width:100, height:50, padding:15, backgroundColor:"#fe8d6f", borderRadius:15, margin:7 }, middleButton03: { width:100, height:50, padding:15, backgroundColor:"#9adbc5", borderRadius:15, margin:7 }, middleButton04: { width:100, height:50, padding:15, backgroundColor:"#f886a8", borderRadius:15, margin:7 }, middleButtonText: { color:"#fff", fontWeight:"700", //텍스트의 현재 위치에서의 정렬 textAlign:"center" }, middleButtonTextAll: { color:"#fff", fontWeight:"700", //텍스트의 현재 위치에서의 정렬 textAlign:"center" }, cardContainer: { marginTop:10, marginLeft:10 }, });
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')}}> <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> ) } const styles = StyleSheet.create({ card:{ flex:1, flexDirection:"row", margin:10, borderBottomWidth:0.5, borderBottomColor:"#eee", paddingBottom:10 }, cardImage: { flex:1, width:100, height:100, borderRadius:10, }, cardText: { flex:2, flexDirection:"column", marginLeft:10, }, cardTitle: { fontSize:20, fontWeight:"700" }, cardDesc: { fontSize:15 }, cardDate: { fontSize:10, color:"#A6A6A6", } });
데이터 가지고 페이지 이동하기
navigation.navigate("Detail",{
title: title
})
그럼 Card에서 DetailPage로 이동할 때, MainPage로 부터 넘겨받은 content도 넘겨볼까요?
왜냐하면 지금은 어떠한 카드를 눌러도 상세 페이지에서 동일한 데이터를 보게 되잖아요? tip 데이터를 고정해놨으니까요!
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> ) } const styles = StyleSheet.create({ card:{ flex:1, flexDirection:"row", margin:10, borderBottomWidth:0.5, borderBottomColor:"#eee", paddingBottom:10 }, cardImage: { flex:1, width:100, height:100, borderRadius:10, }, cardText: { flex:2, flexDirection:"column", marginLeft:10, }, cardTitle: { fontSize:20, fontWeight:"700" }, cardDesc: { fontSize:15 }, cardDate: { fontSize:10, color:"#A6A6A6", } });
DetailPage.js
import React,{useState,useEffect} from 'react'; import { StyleSheet, Text, View, Image, ScrollView,TouchableOpacity,Alert } from 'react-native'; export default function DetailPage({navigation,route}) { //초기 컴포넌트의 상태값을 설정 //state, setState 뿐 아니라 이름을 마음대로 지정할 수 있음! const [tip, setTip] = useState({ "idx":9, "category":"재테크", "title":"렌탈 서비스 금액 비교해보기", "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Frental.png?alt=media&token=97a55844-f077-4aeb-8402-e0a27221570b", "desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ", "date":"2020.09.09" }) useEffect(()=>{ console.log(route) //Card.js에서 navigation.navigate 함수를 쓸때 두번째 인자로 content를 넘겨줬죠? //content는 딕셔너리 그 자체였으므로 route.params에 고대~로 남겨옵니다. //즉, route.params 는 content죠! navigation.setOptions({ //setOptions로 페이지 타이틀도 지정 가능하고 title:route.params.title, //StackNavigator에서 작성했던 옵션을 다시 수정할 수도 있습니다. headerStyle: { backgroundColor: '#000', shadowColor: "#000", }, headerTintColor: "#fff", }) setTip(route.params) },[]) const popup = () => { Alert.alert("팝업!!") } return ( // ScrollView에서의 flex 숫자는 의미가 없습니다. 정확히 보여지는 화면을 몇등분 하지 않고 // 화면에 넣은 컨텐츠를 모두 보여주려 스크롤 기능이 존재하기 때문입니다. // 여기선 내부의 컨텐츠들 영역을 결정짓기 위해서 height 값과 margin,padding 값을 적절히 잘 이용해야 합니다.
{tip.title} {tip.desc} popup()}>팁 찜하기 ) } const styles = StyleSheet.create({ container:{ backgroundColor:"#000" }, image:{ height:400, margin:10, marginTop:40, borderRadius:20 }, textContainer:{ padding:20, justifyContent:'center', alignItems:'center' }, title: { fontSize:20, fontWeight:'700', color:"#eee" }, desc:{ marginTop:10, color:"#eee" }, button:{ width:100, marginTop:20, padding:10, borderWidth:1, borderColor:'deeppink', borderRadius:7 }, buttonText:{ color:'#fff', textAlign:'center' } })
건네 받은 값을 꺼낼 땐, 다음과 같이 책갈피가 navigation과 추가적으로 건네준 route에서 꺼내 확인 할 수 있습니다. route.params 객체에 건네준 딕셔너리가 넘겨 있습니다!
route에 담겨져 오는 데이터 콘솔에서 직접 확인
DetailPage에서 상태값을 초기에 설정한 이유:
DetailPage 초반에 우린 이렇게 상태값을 설정해놨었습니다. 그 이유가 뭘까요? 심지어 이 상태값을 초기에 설정안하면 오류가 발생합니다. tip엔 아무것도 없다며... //초기 컴포넌트의 상태값을 설정 const [tip, setTip] = useState({ "idx":9, " category":"재테크", "title":"렌탈 서비스 금액 비교해보기", "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Frental.png?alt=media&token=97a55844-f077-4aeb-8402-e0a27221570b", "desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ", "date":"2020.09.09" }) |
1. DetailPage 컴포넌트가 useState에 들어 있는 데이터 가지고 화면에 그려짐(return 함수실행)
2. 화면에 다 그려진후, useEffect 함수 실행
3. useEffect에서 상태값 변경 이벤트가 실행되면 변경된 데이터 가지고 다시 return 실행
4. 변경된 데이터를 가지고 화면에 DetailPage가 다시 그려짐.
[Expo 앱다운 앱기능02] 페이지 내용 공유하기
Share 적용해보기
import { Share } from "react-native";
버튼 추가 디테일 페이지 모습
DetailPage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View,
Image, ScrollView,TouchableOpacity,Alert,Share } from 'react-native';
export default function DetailPage({navigation,route}) {
const [tip, setTip] = useState({
"idx":9,
"category":"재테크",
"title":"렌탈 서비스 금액 비교해보기",
"desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ",
"date":"2020.09.09"
})
useEffect(()=>{
console.log(route) navigation.setOptions({
title:route.params.title, headerStyle: {
backgroundColor: '#000',
shadowColor: "#000",
},
headerTintColor: "#fff",
})
setTip(route.params) },[])
const popup = () => { Alert.alert("팝업!!") }
const share = () => { Share.share({
message:`${tip.title} \n\n ${tip.desc} \n\n ${tip.image}`,
});
}
return (
// ScrollView에서의 flex 숫자는 의미가 없습니다. 정확히 보여지는 화면을 몇등분 하지 않고
// 화면에 넣은 컨텐츠를 모두 보여주려 스크롤 기능이 존재하기 때문입니다.
// 여기선 내부의 컨텐츠들 영역을 결정짓기 위해서 height 값과 margin,padding 값을 적절히 잘 이용해야 합니다. <ScrollView style={styles.container}>
<Image style={styles.image}
source={{uri:tip.image}}/>
<View style={styles.textContainer}>
<Text style={styles.title}>{tip.title}</Text>
<Text style={styles.desc}>{tip.desc}</Text>
<View style={styles.buttonGroup}>
<TouchableOpacity style={styles.button} onPress={()=>popup()}>
<Text style={styles.buttonText}>팁 찜하기</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={()=>share()}>
<Text style={styles.buttonText}>팁 공유하기</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView> ) }
const styles = StyleSheet.create({
container:{
backgroundColor:"#000"
},
image:{
height:400,
margin:10,
marginTop:40,
borderRadius:20
},
textContainer:{
padding:20,
justifyContent:'center',
alignItems:'center'
},
title: {
fontSize:20,
fontWeight:'700',
color:"#eee"
},
desc:{
marginTop:10,
color:"#eee"
},
buttonGroup: {
flexDirection:"row",
},
button:{ width:100,
marginTop:20,
marginRight:10,
marginLeft:10,
padding:10,
borderWidth:1,
borderColor:'deeppink',
borderRadius:7
},
buttonText:{
color:'#fff',
textAlign:'center'
}
})
공유 모습
[Expo 앱다운 앱기능02] 외부 링크 클릭 이벤트!
Linking 적용해보기
링크 버튼 추가 된 DetailPage 모습
expo 에서 제공해주는 도구를 설치 한다음, 해당 도구를 상단에 가져와 준비해야 합니다
expo install expo-linking
import * as Linking from 'expo-linking';
적용후:
DetailPage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, ScrollView,TouchableOpacity,Alert,Share } from 'react-native';
import * as Linking from 'expo-linking';
export default function DetailPage({navigation,route}) {
const [tip, setTip] = useState({
"idx":9,
"category":"재테크",
"title":"렌탈 서비스 금액 비교해보기",
"desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ",
"date":"2020.09.09"
})
useEffect(()=>{
console.log(route)
navigation.setOptions({
title:route.params.title,
headerStyle: {
backgroundColor: '#000',
shadowColor: "#000",
},
headerTintColor: "#fff", })
setTip(route.params) },[])
const popup = () => {
Alert.alert("팝업!!")
}
const share = () => { Share.share({
message:`${tip.title} \n\n ${tip.desc} \n\n ${tip.image}`,
});
}
const link = () => {
Linking.openURL("https://spartacodingclub.kr")
}
return (
// ScrollView에서의 flex 숫자는 의미가 없습니다. 정확히 보여지는 화면을 몇등분 하지 않고
// 화면에 넣은 컨텐츠를 모두 보여주려 스크롤 기능이 존재하기 때문입니다.
// 여기선 내부의 컨텐츠들 영역을 결정짓기 위해서 height 값과 margin,padding 값을 적절히 잘 이용해야 합니다.
{tip.title} {tip.desc} popup()}>팁 찜하기 share()}>팁 공유하기 link()}>외부 링크 ) }
const styles = StyleSheet.create({
container:{
backgroundColor:"#000"
},
image:{
height:400,
margin:10,
marginTop:40,
borderRadius:20
},
textContainer:{
padding:20,
justifyContent:'center',
alignItems:'center'
},
title: {
fontSize:20,
fontWeight:'700',
color:"#eee"
},
desc:{
marginTop:10,
color:"#eee"
},
buttonGroup: {
flexDirection:"row",
},
button:{
width:90,
marginTop:20,
marginRight:10,
marginLeft:10,
padding:10,
borderWidth:1,
borderColor:'deeppink',
borderRadius:7
},
buttonText:{
color:'#fff',
textAlign:'center'
}
})