앱개발 종합반 3주차

홍영훈·2021년 7월 31일
0

component

- 코드의 재사용이 가능하도록
- 버튼, App.js 등도 컴포넌트
- App.js에서 return (<MainPage />); ==> MainPage.js도 컴포넌트화

Props

- 컴포넌트에 데이터를 전달하는 것
- key, value의 형태 ==> content = { content }
- numberOfLines, resizeMode ...
- 컴포넌트를 반복문으로 돌릴 때에는 컴포넌트마다 고유하다는 것을 표현하기 위해 map에서의 index를 key = { i } 속성 전달 형태로 꼭 넣어야 함.
- 비구조 할당 방식으로 내려받음.

State, useState ... Loading.js

- 컴포넌트마다 데이터를 보유하고 관리할 수 있음.
- 컴포넌트에서 보유/관리되는 데이터를 상태(State)라고 부름 ==> UI = component(state)
- 상태(State)는 useState 함수로 생성하고, setState 함수로 정의/변경할 수 있음.

useEffect

- 화면이 그려진 다음 가장 먼저 실행되는 함수
- useEffect( ()=>{ 실행되는 코드 } , [])
- 보통 데이터를 준비할 때 사용. 데이터를 받은 후 상태(State)에 반영 ==> 
  화면이 그려짐 > useEffect가 데이터를 준비 > State update... > 화면이 다시 그려짐.

Category (state)

StatusBar (Expo SDK)

- 앱에 따라 화면 맨 위 상태 바의 모습을 지정
- 해당 라이브러리를 설치해주어야 한다. 
- 공식 문서 :  https://docs.expo.io/versions/latest/sdk/status-bar/
- 페이지 개념, 컴포넌트 들을 페이지화 시키고, 해당 페이지끼리 이동할 수 있도록 한다.
- 외부 라이브러리를 사용해야 한다.
- React-Navigation 공식 문서 : 바로가기
- 페이지화(Main.js 와 같은 컴포넌트를 페이지로) : Stack.Screen
- 책갈피(등록하여 페이지 이동 가능하게) : Stack.Navigator

Stack.Screen 에 등록된 모든 페이지 컴포넌트는 navigation과 route 라는 딕셔너리(객체)를 속성을 넘겨 받아 사용할 수 있음.
navigation 객체가 가지고 있는 두 함수 : setOptions, navigate

- 해당 페이지의 제목 설정 : navigation.setOptions({ title: '페이지 제목' })
- 데이터 전달 없이 name 속성으로 해당 페이지로 이동 : 
  navigation.navigate("DetailPage")
- name 속성 전달, 두번째 인잘 데이터를 전달. route 딕셔너리로 받을 수 있음. : 
  navigation.navigate( "DetailPage", { title: title } )

Share

- React Native 에서 기본적으로 제공해주는 공유 기능

Linking

- 페이지에 링크 버튼을 추가하고자 한다면,  Expo에서 도구를 가져와야 한다. 즉 설치해야 한다.

설치
- expo install expo-linking

Component, Props

MainPage.js에서 일부를 Card.js로...
MainPage.js 에서,

...

import data from '../data.json';  
import Card from '../components/Card';

...

<View style={styles.cardContainer}>  
    {/* 하나의 카드 영역을 나타내는 View */}  
    {  
        tip.map((content,i)=>{  
            return (<Card content={content} key={i}/>)  
        })  
    }  
</View>

MainPage.js 에서 Card.js로 Props(속성)를 넘긴다(데이터를 전달한다).
Card.js 에서, 비구조 할당 방식으로 간단하게 키 값만 꺼내서 사용하고자 한다.

...

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

useState, useEffect 그리고 Loading.js

데이터 준비 중 잠시 지나가는 Loading.js가 있다고 하자.
MainPage.js에서,

...

import React, { useState, useEffect } from 'react';
import data from '../data.json';  
import Card from '../components/Card';  
import Loading from '../components/Loading';

...

export default function MainPage() {

...

    const [state, setState] = useState([])
    // setState = [] 와 같은 의미, 아무런 데이터도 준비되어 있지 않다...
    // 데이터가 없기 때문에 오류 발생할 수 있다.

    const [ready, setReady] = useState(true)
    // 위 함수에서 오류가 발생할 수 있기 때문에 데이터 준비를 위한 setReady 함수가 준비
    // setReady = true

    useEffect(() => {  

        setTimeout(() => {  
            setState(data)  
            setReady(false)  
        }, 1000)  
        // 1000 숫자는 1초를 뜻함. 1초 뒤에 실행되는 코드들이 담겨 있다는 의미임.

    }, [])

...

    return ready ? <Loading /> : (  
    // ready ? true : false 와 같은 삼항 연산자
    // setReady = true 가 우선 준비되었으므로, true에 해당하는(전항) Loading 컴포넌트가 실행
    // 1초(1000) 뒤에 useEffect가 가장 먼저 실행되면서 
    // setState 함수의 변수인 state에 data를 준비하면서 setReady = false로 대입
    // 이후 다시 만난 삼항 연산자에서는 ready = false가 되므로, 
    // false에 대항하는(후항) 엘리먼트들(MainPage 컴포넌트)이 실행됨.

...

            <View style={styles.cardContainer}>  
                {/* 하나의 카드 영역을 나타내는 View */}  
                {  
                    tip.map((content, i) => {  
                        return (<Card content={content} key={i} />)  
                    })  
                }  
            </View>

...

Category

MainPage.js에서

...

    const [cateState, setCateState] = useState([])
    // cateState 변수에 카테고리에 따라 다른 꿀팁 데이터를 그때그때 저장하기 위한 상태로 만들었다.
    // useState에 의해 빈 리스트([])를 생성해 놓았다.

...

    useEffect(() => {  
        setTimeout(() => {  
            // 꿀팁 데이터로 모두 초기화 준비  
            let tip = data.tip;  
            setState(tip)  
            setCateState(tip)  
            setReady(false)  
            // setState(tip)을 통해 전체 데이터를 넣어놓는다. 
            // 이는 "전체보기"를 눌렀을 때 전체 꿀팁이 나타나도록 하기 위함.
            // setCateState(tip)은 카테고리에 따라 달라지는 tip 데이터를 
            // setState(tip)에서 꺼내서 사용하기 위함
        }, 1000)  
    }, [])

...

    const category = (cate) => {  
        if (cate == "전체보기") {  
            //전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화  
            setCateState(state)  
        } else {  
            setCateState(state.filter((d) => {  
                return d.category == cate  
            }))  
        }  
    }
    // category 함수 정의
    // cate == "전체보기" 가 참이 되면, state에 들어가 있는 tip(= data.tip, 즉 전체 데이터)로 접근
    // 즉, 초기화 상태와 같다.
    // cate 에 다른 조건이 들어가면, 그 조건에 맞는 항목을 찾아냄. (state.filter 에 의해...)

...

            <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>
    {/* onPress = {} 로 key, value 형태의 Props...
        그 안에 함수가 정의되면서 category 함수가 실행될 코드로 들어간다.
        () => { category(d) }
        d 값에 해당하는 항목(카테고리)를 tip (= data.tip)에서 찾아 화면에 그려준다. */}

...

StatusBar

MainPage.js에서

...

import { StatusBar } from 'expo-status-bar';

...

        <ScrollView style={styles.container}>  
            <StatusBar style="black" />  
            {/* StatusBar의 style : auto, inverted, light, dark ... */}
            <Text style={styles.title}>나만의 꿀팁</Text>

...

StackNavigator.js를 생성한다.
StackNavigator.js에서,

import React from 'react';  

import { createStackNavigator } from '@react-navigation/stack';  
// 성치한 스택 네비게이션 라이브러리를 가져옴.

import DetailPage from '../pages/DetailPage';  
import MainPage from '../pages/MainPage';  
// 페이지화 한 컴포넌트를 불러옴.
// 페이지 : DetailPage, 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 삭제 (또는, title 부분 주석 처리 : {/ 나만의 꿀팁 /} )

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.js에서,

...

export default function MainPage({navigation,route}) {

...

  useEffect(()=>{   
	      
    setTimeout(()=>{   
        //헤더의 타이틀 변경   
        navigation.setOptions({   
            title:'나만의 꿀팁'   
        })   
        //꿀팁 데이터로 모두 초기화 준비   
        let tip = data.tip;   
        setState(tip)   
        setCateState(tip)   
        setReady(false)   
    },1000)   
       
  },[])

...

        <View style={styles.cardContainer}>  
            {/* 하나의 카드 영역을 나타내는 View */}  
            {  
            cateState.map((content,i)=>{  
                return (<Card content={content} key={i} navigation={navigation}/>)  
            })  
            }  
              
        </View>

...

비구조 할당 방식으로 수정되었으며, useEffect 부위에 navigation.setOptions를 통해 제목을 강제로 바꾸었다. cateState.map 부위에도 navigation = { navigation} 으로 Props를 주어 이동이 가능해진다.

Card.js도 수정되어야 한다. Card.js에서 태그로 감싸져 있던 부분을 로 바꾸었다. 해당 부분을 버튼처럼 사용하기 위해서이다. 그러면서 onPress = { } 로 Props를 주면서 그 안에 함수를 정의하여 navigation.navigate('DetailPage')} 함수를 실행 코드로 넣었다. 이 코드로 DetailPage로 이동한다.
즉, Card.js에서,

...

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

...


데이터를 가지고 페이지 이동하기

위의 수정된 Card.js에서
DetailPage로 이동할 때, MainPage로부터 넘겨받은 content도 넘겨서 보려면 Card.js를 일부 수정해야 한다.
위 Card.js에서,

...

<TouchableOpacity style={styles.card} onPress={()=>{navigation.navigate('DetailPage',content)}}>

...

위 Card.js와 비교하면, navigation.navigate('DetailPage') 부분이 navigation.navigate('DetailPage', content)로 바뀌어 있다.

DetailPage.js도 수정되어야 한다.
DetailPage.js에서

...

export default function DetailPage({navigation,route}) {

...

    const [tip, setTip] = useState({  
        "idx":9,  
        "category":"재테크",  
        "title":"렌탈 서비스 금액 비교해보기",
    // 초기 데이터를 setTip 함수이 tip 변수에 넣어둔다.
    // 초기 컴포넌트의 상태값을 설정(의미 없는 값이라도...)해 줘야 오류가 없다.

...

    useEffect(()=>{  
        console.log(route)  
        navigation.setOptions({  
            title:route.params.title,  
            // Card.js에서 navigation.navigate 함수를 쓸 때 content를 넘겼다.
            // content는 딕셔너리 그 자체이므로 route.params에 그대로 남겨온다.
            // 즉, route.params 는 content 다.
            headerStyle: {  
                backgroundColor: '#000',  
                shadowColor: "#000",  
            },  
            headerTintColor: "#fff",  
        })  
        setTip(route.params)  
    },[])  
    const popup = () => {  
        Alert.alert("팝업!!")  
    }

...

                <TouchableOpacity style={styles.button} onPress={()=>popup()}><Text style={styles.buttonText}>팁 찜하기</Text></TouchableOpacity>

...

Share

페이지 공유 기능을 위해서는 import { Share } from "react-native"; 와 같이 Share를 import 해야 한다.
DetailPage에서 공유 버튼을 추가하기 위해서는 DetailPage.js를 수정해야 한다.
DetailPage.js에서

...

import { StyleSheet, Text, View, Image, ScrollView, TouchableOpacity, Alert, Share } from 'react-native';

...

    const share = () => {  
        Share.share({  
            message:`${tip.title} \n\n ${tip.desc} \n\n ${tip.image}`,  
        });  
    }

...

            <TouchableOpacity style={styles.button} onPress={()=>share()}><Text style={styles.buttonText}>팁 공유하기</Text></TouchableOpacity>

...

Linking

DetailPage에 링크 버튼을 추가하기 위해서는 DetailPage.js를 수정해야 한다.
DetailPage.js에서,

...

import * as Linking from 'expo-linking';

...

    const link = () => {   
        Linking.openURL("https://spartacodingclub.kr")   
    }

...

            <TouchableOpacity style={styles.button} onPress={()=>link()}><Text style={styles.buttonText}>외부 링크</Text></TouchableOpacity>

...
profile
코딩을 처음 배워가는 코린이?

0개의 댓글