sparta coding club .kr

앱개발 종합반 3주차 개발일지

김영수 2021. 11. 21. 16:31

 

[앱 필수 기초지식] 리액트 필수지식

리액트 네이티브 앱을 만들기 위해서 알아야 하는 최소한의 리액트 개념을 배웁니다.

- 컴포넌트(Component)

- 상태(State,useState)

- 속성(Props)

- useEffect

 

나만의 꿀팁 앱 상세 화면 만들어보기

폴더구조

DetailPage.jspages 폴더에 만들어 넣기

 

상세 화면에서 사용될 데이터

const tip = {

"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" }

 

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":"렌탈 서비스 금액 비교해보기",

"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" }

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';

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';

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';

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';

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';

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';

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';

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() {

//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

},

});

 

stylelight일 때와, 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 공식문서 보러가기 —> [(링크)]

https://reactnavigation.org/

기본 설치 코드

네비게이션 설치 코드

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';

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() {

//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에 등록된 모든 페이지 컴포넌트들은 navigationroute 라는 딕셔너리(객체)를 속성으로 넘겨받아 사용할 수 있습니다. 이 두 딕셔너리는 다음과 같은 기능을 갖습니다.

 

//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":"렌탈 서비스 금액 비교해보기",

"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) 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":"렌탈 서비스 금액 비교해보기",

"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)

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'

}

})