앱개발 튜터링_8회차

jyooooooung·2023년 5월 15일
0

오늘의 목표

  • 리액트 필수 지식
    컴포넌트, 속성, 상태와 useEffect
  • 로딩페이지 만들기
    컴포넌트, 상태 이용
  • state를 이용한 카테고리 기능
  • 네비게이터란?
    네비게이터 설치, 페이지 이동, 페이지 헤더 스타일 수정

시작 전에 어바웃 페이지 만들기

  1. pages 폴더 만들기

2.pages 폴더에 MainPage.js와 AboutPage.js 파일을 만들어주세요

  1. App.js 파일 코드 모두 복사한 후, MainPage.js에 붙여넣기 한 다음 함수 이름을 MainPage 로 바꿔주세요
  2. 그리고 MainPage.js의 두 코드 줄은 다음과 같이 변경해주세요
import main from './assets/main.png';
import data from './data.json';

에서

import main from '../assets/main.png';
import data from '../data.json';
  1. App.js 코드는 다음 코드로 변경해주세요
import React from 'react'
import MainPage from './pages/MainPage';

export default function App(){
  return (<MainPage/>)
}
import React from 'react'
import {View,Text,StyleSheet,Image, TouchableOpacity} from 'react-native'


export default function AboutPage(){
    const aboutImage = "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2FaboutImage.png?alt=media&token=13e1c4f6-b802-4975-9773-e305fc7475c4"
  return (
    <View style={styles.container}>
        <Text style={styles.title}>HI! 스파르타코딩 앱개발 반에 오신것을 환영합니다</Text>
       
        
        <View style={styles.textContainer}>
            <Image style={styles.aboutImage} source={{uri:aboutImage}} resizeMode={"cover"}/>
            <Text style={styles.desc01}>많은 내용을 간결하게 담아내려 노력했습니다!</Text>
            <Text style={styles.desc02}>꼭 완주 하셔서 꼭 여러분것으로 만들어가시길 바랍니다</Text>
            <TouchableOpacity style={styles.button}>
                <Text style={styles.buttonText}>여러분의 인스타계정</Text>
            </TouchableOpacity>
        </View>
    </View>)
}

const styles = StyleSheet.create({
    container: {
        flex:1,
        backgroundColor:"#1F266A",
        alignItems:"center"
      
    },
    title: {
        fontSize:30,
        fontWeight:"700",
        color:"#fff",
        paddingLeft:30,
        paddingTop:100,
        paddingRight:30
    },
    textContainer: {
        width:300,
        height:500,
        backgroundColor:"#fff",
        marginTop:50,
        borderRadius:30,
        justifyContent:"center",
        alignItems:"center"
    },
    aboutImage:{
        width:150,
        height:150,
        borderRadius:30
    },
    desc01: {
        textAlign:"center",
        fontSize:20,
        fontWeight:"700",
        paddingLeft:22,
        paddingRight:22

    },
    desc02: {
        textAlign:"center",
        fontSize:15,
        fontWeight:"700",
        padding:22
    },
    button:{
        backgroundColor:"orange",
        padding:20,
        borderRadius:15
    },
    buttonText: {
        color:"#fff",
        fontSize:15,
        fontWeight:"700"
    }
})

어때요? 그래도 화면은 오늘 우리가 마지막 까지 배우고 적용한 화면과 동일하죠?

지금 숙제를 위해 간단히 가이드 드리고 따라해본건 3주차때 배울 컴포넌트 및 페이지화 개념입니다!
어렵지 않죠? 이렇게 앱 파일을 쉽게 분할 할 수 있습니다.

우리가 배운 import, export를 개념을 통해서말이죠!
그럼 AboutPage 즉, 소개 페이지를 보려면 어떻게 해야할까요?

일단은 App.js return에서 내보내어 화면을 그리고 있는 MainPage를 주석치고
상단에서 AboutPage를 불러온 다음 주석친 MainPage 대신 넣어줘야 합니다. 이렇게 말이죠

  • AboutPage를 return에 담기
import React from 'react'
import MainPage from './pages/MainPage';
import AboutPage from './pages/AboutPage';

export default function App(){
  // return (<MainPage/>)
  return (<AboutPage/>)
}

현재 코드 상황

  • App.js
import React from 'react'
import MainPage from './pages/MainPage';
import AboutPage from './pages/AboutPage';

export default function App(){
  // return (<MainPage/>)
  return (<AboutPage/>)
}
  • MainPage.js
import React from 'react';
import main from '../assets/main.png';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
import data from '../data.json';


export default function MainPage() {
  console.disableYellowBox = true;
  //return 구문 밖에서는 슬래시 두개 방식으로 주석

  let tip = data.tip;
  let todayWeather = 10 + 17;
  let todayCondition = "흐림"
  return (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
			 <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {/* 하나의 카드 영역을 나타내는 View */}
         {
          tip.map((content,i)=>{
            return (<View style={styles.card} key={i}>
              <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>)
          })
        }
        
      </View>
   
    </ScrollView>
  );
}

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


});
  • AboutPage.js
import React from 'react'
import {View,Text,StyleSheet,Image, TouchableOpacity} from 'react-native'


export default function AboutPage(){
    const aboutImage = "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2FaboutImage.png?alt=media&token=13e1c4f6-b802-4975-9773-e305fc7475c4"
  return (
    <View style={styles.container}>
        <Text style={styles.title}>HI! 스파르타코딩 앱개발 반에 오신것을 환영합니다</Text>
       
        
        <View style={styles.textContainer}>
            <Image style={styles.aboutImage} source={{uri:aboutImage}} resizeMode={"cover"}/>
            <Text style={styles.desc01}>많은 내용을 간결하게 담아내려 노력했습니다!</Text>
            <Text style={styles.desc02}>꼭 완주 하셔서 꼭 여러분것으로 만들어가시길 바랍니다</Text>
            <TouchableOpacity style={styles.button}>
                <Text style={styles.buttonText}>여러분의 인스타계정</Text>
            </TouchableOpacity>
        </View>
    </View>)
}

const styles = StyleSheet.create({
    container: {
        flex:1,
        backgroundColor:"#1F266A",
        alignItems:"center"
      
    },
    title: {
        fontSize:30,
        fontWeight:"700",
        color:"#fff",
        paddingLeft:30,
        paddingTop:100,
        paddingRight:30
    },
    textContainer: {
        width:300,
        height:500,
        backgroundColor:"#fff",
        marginTop:50,
        borderRadius:30,
        justifyContent:"center",
        alignItems:"center"
    },
    aboutImage:{
        width:150,
        height:150,
        borderRadius:30
    },
    desc01: {
        textAlign:"center",
        fontSize:20,
        fontWeight:"700",
        paddingLeft:22,
        paddingRight:22

    },
    desc02: {
        textAlign:"center",
        fontSize:15,
        fontWeight:"700",
        padding:22
    },
    button:{
        backgroundColor:"orange",
        padding:20,
        borderRadius:15
    },
    buttonText: {
        color:"#fff",
        fontSize:15,
        fontWeight:"700"
    }
})
  • 폴더구조

디테일페이지 만들기

  1. DetailPage.js를 pages 폴더에 만들어 넣기

  2. 저번 1주차 마무리 시간에, pages 폴더를 만들고 AboutPage.js 파일을 만들어 넣었던 기억이 나시나요? 그때랑 동일하게 pages 폴더안에 DetailPage.js 파일을 만들고 App.js에서 return 부분에 DetailPage.js를 넣어주세요 다음과 같이요!

  3. 디테일 화면을 위한 데이터

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 값을 적절히 잘 이용해야 합니다. 
        <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>
                <TouchableOpacity style={styles.button} onPress={()=>popup()}><Text style={styles.buttonText}>팁 찜하기</Text></TouchableOpacity>
            </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"
    },
    button:{
        width:100,
        marginTop:20,
        padding:10,
        borderWidth:1,
        borderColor:'deeppink',
        borderRadius:7
    },
    buttonText:{
        color:'#fff',
        textAlign:'center'
    }
})

리액트 필수 지식

컴포넌트, 속성, 상태와 useEffect

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

  • 컴포넌트(Component)
  • 상태(State,useState)
  • 속성(Props)
  • useEffect

이렇게 네 가지입니다. 단어가 조금 어려워 보이지만, 재미있고 간단한 개념들입니다.
이 개념들을 통해 앱 코드를 체계적으로 관리할 수 있고
화면안에서 데이터를 자유자재로 관리할 수도 있습니다.

최소한의 리액트 개념을 배우는 이유는

리액트 네이티브(React Native)는 리액트(React.js) 기반으로 만들어진 앱 개발 기술이기 때문이에요!

  • 앱다운 앱을 위해 사용할 리액트 네이티브와 Expo 기능들

앱 필수 기초지식

공부 할 리액트 필수 지식

다시 떠올리자면, 리액트 네이티브는 리액트(React.js) 라이브러리 기반으로 만들어진 프레임워크 입니다. 그렇기 때문에 기본적인 구조는 리액트를 닯아 있습니다.

대표적으로

1) 컴포넌트(Component) : 정해진 엘리먼트들(요소)을 사용하여 만든 화면의 일부분
2) 상태(State) : 컴포넌트에서 데이터를 유지하고 관리하기 위한 유일한 방법 == 그냥 사용할 데이터!
3) 속성(Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식 == 그냥 데이터 전달!
4) useEffect : 화면에 컴포넌트가 그려지면 처음 실행해야 하는 함수들을 모아두는 곳

네 가지가 있습니다.
워워 어렵지 않아요. 이것도 정해진 규칙과 개념일 뿐!
사용방법 숙지!만 하면 그만이랍니다!

컴포넌트(Component)

컴포넌트는 UI의 요소, 요소를 재사용 가능한 부분으로 조각내서 운영하는 기법입니다.
리액트 기반으로 만들어진 페이스북 웹사이트는 운영되는 컴포넌트가 수 만가지라고 합니다

  • 컴포넌트는 즉, 화면의 모든 부분입니다.
    그리고 컴포넌트란 App.js의 큰 App 함수처럼, 코드 전체를 감싸고 있는 함수를 뜻하기도 합니다.

  • 따라서 App.js를 App 컴포넌트라고도 부를 수 있습니다.

  • 버튼 하나가 컴포넌트가 될 수 있고, 버튼을 모아둔 영역이 컴포넌트가 될 수 있습니다.

  • 이렇게 컴포넌트로 생각하고 앱을 개발해 나아간다면, 코드 재사용이 용이 해집니다.

  • 코드 재사용이란 어려운 용어는 아니고, 버튼 하나를 만들었을 때 이 버튼 코드를 여러 페이지에서 사용 가능하다는 뜻입니다.

  • 실제 우리 예제에 어떻게 적용할 수 있는지. 확인해보겠습니다.

메인화면 컴포넌트화 해보기

Components 폴더 안에 Card.js라는 파일을 만듭니다. 보통 컴포넌트들은 components 폴더에 모아두면서 관리하곤 합니다.

그럼 다음 코드를 Card.js에 넣어주세요! 어디서 많이 본 코드입니다 그쵸?

  • 마찬가지로 MainPage.js 화면을 확인하려면 App.js를 바꿔야 겠죠?
    return () 으로요!

  • 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에서 이미지위에 텍스트가 있던, 한 줄에 세 개씩 나열 되었던 그 카드 버튼입니다!.

그럼 MainPage.js는 다음과 같이 변경해주세요

  • MainPage.js
import React from 'react';
import main from '../assets/main.png';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
import data from '../data.json';
import Card from '../components/Card';


export default function MainPage() {
  console.disableYellowBox = true;
  //return 구문 밖에서는 슬래시 두개 방식으로 주석

  let tip = data.tip;
	let todayWeather = 10 + 17;
  let todayCondition = "흐림"
  return (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
			 <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {/* 하나의 카드 영역을 나타내는 View */}
         {
          tip.map((content,i)=>{
            return (<Card content={content} key={i}/>)
          })
        }
        
      </View>
   
    </ScrollView>
  );
}

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


});

MainPage.js에 있던 코드들을 Card.js로 옮겼습니다. 이렇게 옮기게 되면, 추후에 MainPage.js가 아닌 다른 페이지(페이지 또한 컴포넌트로 볼 수 있습니다) 또는 컴포넌트에서 Card.js를 사용할 수 있습니다.

그런데 약간 생소한 부분이 있습니다 다음 두 부분인데요!

  • MainPage.js
  • Card.js
    미리 말씀드리자면 MainPage.js에서 Card.js로 데이터를 넘기고 있는 모습입니다!
    이를 속성을 넘긴다! 라고 보통 일컫는데 이는 다음 챕터에서 더 자세히 다루도록 하겠습니다!

속성(Props)

  • 속성은 쉽게 생각해서 컴포넌트에 데이터를 전달한다는 것이다.
    그 전달 모습은 키와 벨류의 형태입니다.

태그엔 numberOfLines이란 속성이 있었습니다. 말줌임표 효과를 주었죠!
태그엔 resizeMode란 속성이 있었습니다. 이미지가 영역을 차지하는 방식을 나타냈죠.

이 모든 속성들은 공식 문서에 나와 있고, 해당 엘리먼트들이 태어날 때부터 가지고 있는
속성이였습니다.

  • 우리가 만든 컴포넌트에도 속성이 존재합니다.
    우리가 부여 하면 부여하는 대로 속성이 되는 겁니다.

예를 들어 Card를 사용하는 곳에서<Card image={'이미지 주소'}/> 라고 속성을 써 넣으면,
이 는 image라는 속성이름에 {'이미지 주소'} 값을 을 갖게 됩니다. 즉 키와 벨류의 값을 갖게 됩니다
근데 이게 무슨 의미가 있을까요?

의미가 있습니다.
속성을 부여받은 컴포넌트에서 해당 속성 값을 받아서 사용 할 수 있거든요! 다음과 같이 말이죠!

확인해보기

  • MainPage.js에서 방금전 컴포넌트화! 했던 Card.js를 우린 이렇게 사용했습니다.
  • MainPage.js 에서의 Card.js 컴포넌트
  • 어렵지 않은 규칙이 숨어 있습니다!
  1. 컴포넌트에 속성(데이터)을 부여해줘서 전달할땐, 키와 벨류(content={content}) 형태로 전달해줘야 할 것

  2. 컴포넌트를 반복문 돌릴땐, 컴포넌트마다 고유하다는 것을 표현하기 위해, map에서 나오는 인덱스(i)를 key = {i} 속성 전달 형태로 꼭 넣을것! 입니다

  • Card.js 에서의 속성 값 내려 받기

    - 오랜만에 복습겸 나온 비구조 할당 방식 의 모습입니다.

    MainPage.js에서 넘겨준 속성은 실제 받게되는 컴포넌트에서 정말 딕셔너리 데이터를 받았다! 라고 생각하면 됩니다.

때문에, 비구조 할당 방식이 뭐였죠?
딕셔너리에서 키값을 바로 취해서 변수로써 함수안에서 즉시 사용할 수 있는 방식이였죠?

이 방식으로 넘겨준 키값을 {키,키,키} 비구조 할당 방식으로 바로 꺼내서 사용하면 됩니다!

상태(useState)와 useEffect

상태(State, useState)

컴포넌트마다 데이터를 보유하고 관리 할 수 있습니다. 데이터라고 불러도 되지만, 리액트에서는 컴포넌트에서 보유/관리 되는 데이터를 상태라 부릅니다.

리액트에서 상태(state)는 리액트 라이브러리에서 제공해주는 useState로 생성하고 setState 함수로 정/변경 할 수 있습니다.

사용방법은 실습을 통해 알아보도록 하겠습니다!

화면이 그려진다음 가장 먼저 실행되는 함수, useEffect

useEffect는 너무 간단하고 유용하게 쓰이는 리액트 기본 제공 함수입니다.

화면이 그려진 다음 가장 먼저 실행되는 함수로 사용방법이 간단합니다
형식만 알고 있으면 충분합니다

useEffect(()=>{

	...화면이 그려진 다음 가장 먼저 실행되야 할 코드 작성 공간

},[])

useState, 그렇다면 왜 중요할까?

리액트는 특이한 점이 있습니다.

여러분이 만드는 화면은 데이터에 따라 변경됩니다
또 아무 데이터가 아니라 이 상태(state)로 관리되는 데이터가 변경되면 화면이 바뀝니다.

모르셔도 되지만 꽤 유의미한 공식이 있습니다

  • UI = component(state)

저 공식은 즉, 사용자 화면(UI)는 컴포넌트(component)에 어떤 데이터(state)가 주입되고 변경되냐에 따라 변화된다를 뜻합니다

무슨말인지 아직 잘 와닿지 않으시죠?
다른건 잘 와닿지 않아도 state는 컴포넌트에서 관리되는 상태만 기억하시면 충분합니다
이 개념은 이번 강의에서 개념을 다 배운 뒤, 실습 하며 직접 확인할 수 있습니다

[실습✍️] data.json을 상태(state)에 넣어서 관리해보기

지금 꿀팁을 data.json 파일에서 꺼내 바로 JSX 문법에서 사용하고 있습니다. 이를 다음과 같이 상태에 저장한 다음 사용하도록 변경해보도록 하겠습니다.

복습하실때 주석도 자세히 살펴보세요!

  • MainPage.js에 복붙하기
  import React,{useState,useEffect} from 'react';
import main from '../assets/main.png';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
import data from '../data.json';
import Card from '../components/Card';


export default function MainPage() {
  console.disableYellowBox = true;
  //return 구문 밖에서는 슬래시 두개 방식으로 주석

	//useState 사용법
	//[state,setState] 에서 state는 이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
  //setState는 state를 변경시킬때 사용해야하는 함수

  //모두 다 useState가 선물해줌
  //useState()안에 전달되는 값은 state 초기값
  const [state,setState] = useState([])
	

	//하단의 return 문이 실행되어 화면이 그려진다음 실행되는 useEffect 함수
  //내부에서 data.json으로 부터 가져온 데이터를 state 상태에 담고 있음
  useEffect(()=>{
    setState(data)
  },[])

//   let tip = data.tip;
	//data.json 데이터는 state에 담기므로 상태에서 꺼내옴
  let tip = state.tip;
	let todayWeather = 10 + 17;
  let todayCondition = "흐림"
  return (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
			 <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {/* 하나의 카드 영역을 나타내는 View */}
         {
          tip.map((content,i)=>{
            return (<Card content={content} key={i}/>)
          })
        }
        
      </View>
   
    </ScrollView>
  );
}

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


});
  • 그러나 실행을 하면 다음과 같은 오류 화면이 나옵니다
  • 오류화면
  • 그 이유는 이런 절차로 발생됩니다.

1) 화면이 그려진다
2) useEffect가 데이터를 state에 useState를 이용하여 업데이트한다
3) 상태(state)가 변경되었으니 화면이 다시 그려진다

여기서 1번에서 오류가 난겁니다.
화면이 가장 먼저 그려질때, 우리가 Card에 데이터를 넘기기위해 어떤일을 했나요?

  • 상태(state)에서 tip 키 값에 접근하여 데이터를 꺼내 map 즉, 반복문을 돌리려했습니다.
    그런데 state에 데이터가 처음부터 있었나요? 없었죠?
    useEffect를 거쳐야 state에 데이터가 담기는데 아직 없는 상태라 오류가 나는 겁니다.

  • 처음부터 데이터가 없어서 , 꺼내올수 없는 오류

  • 이런 오류는 우리가 지금 배운 개념들, 상태, 컴포넌트, useEffect를 이용하여
    로딩화면을 만들어줌으로써 해결이 가능합니다!

    [앱 필수 기초지식 응용] 컴포넌트와 상태를 이용한 로딩화면 만들기

    앱 운영에 필요한 로딩화면

    • 5장에서 오류가 났던 것 처럼 화면이 그려질때 준비된 데이터가 없다!?
      이러한 상황은 아주 빈번합니다.
  • 이때 우린 로딩화면을 만들어줌으로써 쉽게 해결이 가능합니다.

  • 컴포넌트 폴더에 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 main from '../assets/main.png';
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() {
  console.disableYellowBox = true;
  //return 구문 밖에서는 슬래시 두개 방식으로 주석

  const [state,setState] = useState([])

	//컴포넌트에 상태를 여러개 만들어도 됨
  //관리할 상태이름과 함수는 자유자재로 정의할 수 있음
  //초기 상태값으로 리스트, 참거짓형, 딕셔너리, 숫자, 문자 등등 다양하게 들어갈 수 있음.
  const [ready,setReady] = useState(true)

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

//   let tip = data.tip;
  let tip = state.tip;
	let todayWeather = 10 + 17;
  let todayCondition = "흐림"

	//처음 ready 상태값은 true 이므로 ? 물음표 바로 뒤에 값이 반환(그려짐)됨
  //useEffect로 인해 데이터가 준비되고, ready 값이 변경되면 : 콜론 뒤의 값이 반환(그려짐)
  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={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {/* 하나의 카드 영역을 나타내는 View */}
         {
          tip.map((content,i)=>{
            return (<Card content={content} key={i}/>)
          })
        }
        
      </View>
   
    </ScrollView>
  );
}

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


});
  • 적용 모습
    ready 라는 새로운 상태값이 추가되었습니다.
    즉, 상태값은 컴포넌트안에 여러개가 될 수 있습니다.

useEffect안에 setTimeout이라는 함수가 존재합니다.
, 뒤에 있는 숫자 (1000 === 1초) 만큼, 지연됐다가 안에 있는 코드가 실행되는 지연 함수입니다.

즉, 이런 순서로 진행된거에요!

1) ready 값이 true이므로 return 구문에서 ? 물음표 바로 뒤의 Loading 컴포넌트가 화면에 그려짐
2) 화면이 그려지고 난다음, 1초 이따가 상태값들이 채워지고 변경됨
3) ready 상태 값이 false가 됨
4) 상태값이 변경되었으므로 화면이 다시 그려짐
5) ready 값이 false 이므로 return 구문에서 : 콜론 뒤의 MainPage 컴포넌트가 화면에 그려짐

차근차근 순서따라 반복해서 보면 별거아닙니다.
정해진 규칙에 따라 우린 코드를 적용했고
정해진 규칙에 따라 코드가 실행되고 있는 거에요!

profile
내가 공부하려고 시작한 코딩블로그 😏🔥

0개의 댓글