본문 바로가기

코딩수강일지/팀스파르타 앱개발 종합반 수업일지

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

코드의 작동 원리에대해서 아는것도 중요하지만

자전거를 탈때 자전거 작동원리를 몰라도 탈 수 있는것 처럼

일단 먼저 실행하고 작동시켜본 다음 천천히 알아가는것도 중요하다.

 

  • 리액트 네이티브는 리액트(React.js) 라이브러리 기반으로 만들어진 프레임워크 입니다
  • 리액트(React.js) 기본적인 구조

- 상단에 내가 사용할 도구들(자바스크립트 파일)을 Import 해서 준비시켜놓음

자바스크립트 파일 안에는 항상 큰 기본이 되는 함수가 있고 그 함수를 export해서 사용하는 구조로 되어있음

ex)

//리액트의 모~든 파일은 컴포넌트라 생각하고
//페이지 기능을 해주는 모든 기능이 담겨 있는 컴포넌트를 만든다 생각하세요!
const StackNavigator = () =>{
    return (
    )
}

export default StackNavigator;

1) 컴포넌트(Component) : 정해진 엘리먼트들(요소)을 사용하여 만든 화면의 일부분

2) 상태(State) : 컴포넌트에서 데이터를 유지하고 관리하기 위한 유일한 방법 == 그냥 사용할 데이터!

3) 속성(Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식 == 그냥 데이터 전달!(ex.numberOfLines에 3이라는 데이터를 전달 - 3줄까지만 나옴)

4) useEffect : 화면에 컴포넌트가 그려지면 처음 실행해야 하는 함수들을 모아두는 곳

 

  • 1) 컴포넌트(Component)

- 컴포넌트는 UI의 요소, 요소를 재사용 가능한 부분으로 조각내서 운영하는 기법

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

- 이렇게 컴포넌트를 많이 만들어 놓을 수록, 다른 앱개발시 용이 하게 재사용 가능

 

  • 2) 속성(Props)

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

- <Text> 태그엔 numberOfLines이란 속성이 있었습니다. 말줌임표 효과를 주었죠!

<Image> 태그엔 resizeMode란 속성이 있었습니다. 이미지가 영역을 차지하는 방식을 나타냈죠.

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

 

Props 추가설명

- Props 란 쉽게 말해 부모의 Component 혹은 Container 안의

값, 변수, 함수 등을 자식 Component들 에게 넘겨주려 할 때 사용함.

혹은 자식의 값을 부모의 값으로 가져 올 수도 있음.

 

- 하지만 직접 컴포넌트에 속성을 부여하는것도 가능!

1. 컴포넌트에 속성(데이터)을 부여해줘서 전달할땐, 키와 벨류(content={content}) 형태로 전달해줘야 할 것

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

전달 형태로 꼭 넣을것! 입니다.

{content}라는 Props를 만들어(이름 변경가능) content안에 저장된 데이터 값을 자식 component인

Card.js 컴포넌트에 넘겨준다(넘겨줄때 사용하는 형식이 Props)

 

  • 3) 상태(State) = 컴포넌트 안의 동적인 값

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

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

- UI = component(state)

- 상태가 업데이트 되면 화면을 자동으로 다시 그려줌

 

* useState : 컴포넌트안의 상태데이터를 초기화하거나 변경하기위해 사용하는 도구

useState 사용법

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

useState 뒤 () 괄호 안에는 useState의 초기값을 지정해줌(비워둘 수 있고, 리스트형태기때문에 대괄호 사용)

첫번째 state 자리는 useState의 초기값을 받아옴(이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수)

두번째 setState자리는 state에 담긴 데이터를 수정할때 사용하는 함수

 

 

* useEffect : 화면이 그려지고 데이터를 준비한 다음 실행되는 함수(상태가 업데이트되면 보여줌)

형식
useEffect(()=>{

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

},[])


useEffect(function, deps)
function : 실행하고자 하는 함수
deps : 배열 형태. function을 실행시킬 조건.
deps에 특정값을 넣게 되면 컴포넌트가 mount 될 때, 지정한 값이 업데이트될 때 useEffect를 실행합니다.

 

  const [state,setState] = useState([])
	
    
  useEffect(()=>{
    setState(data)    //내부에서 data.json으로 부터 가져온 데이터를 state 상태에 담고 있음
  },[])
  
  
보통 useEffect는 데이터를 준비할 때 사용합니다
데이터를 준비한다는 것은, 데이터를 서버로부터 혹은 어디선가로부터 받은 후 
상태(state)에 반영한다는 것을 뜻합니다. 이런순서로 말이죠

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

 

** 로딩화면 만들기 실습 **(불필요한 코드 제거함)

 

Loding.JS ( 로딩화면)

import React from 'react';
import {View,Text,StyleSheet} from 'react-native';

export default function Loading(){
    return(<View style={styles.container}><Text style={styles.title}>준비중입니다...</Text></View>)
}

Mainpage.JS(메인화면)

import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';

const main = 'https://storage.googleapis.com/sparta-image.appspot.com/lecture/main.png'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
export default function MainPage() {

  const [state,setState] = useState([])
	
  const [ready,setReady] = useState(true)

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

  //data.json 데이터는 state에 담기므로 상태에서 꺼내옴
  let tip = state.tip;
  let todayWeather = 10 + 17;
  let todayCondition = "흐림"
  return ready ? <Loading/> :  (

      <View style={styles.cardContainer}>
         {
         tip.map((content,i)=>{
            return (<Card content={content} key={i}/>)
          })
        }
      </View>
   
    </ScrollView>)
}

화면이 그려지는 순서

 

1) ready 값이 true이므로 return 구문에서 ? 물음표 바로 뒤의 Loading 컴포넌트가 화면에 그려짐

2) 화면이 그려지고 난다음, 1초 이따가 상태값들이 채워지고 변경됨

3) ready 상태 값이 false가 됨

4) 상태값이 변경되었으므로 화면이 다시 그려짐

5) ready 값이 false 이므로 return 구문에서 : 콜론 뒤의 MainPage 컴포넌트가 화면에 그려짐

 

 

** 카테고리 버튼(전체보기, 생활, 꿀팁 찜 등) 만들기 실습 **

Mainpage.js

import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';

const main = 'https://storage.googleapis.com/sparta-image.appspot.com/lecture/main.png'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
export default function MainPage() {
  //useState 사용법
	//[state,setState] 에서 state는 이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
  //setState는 state를 변경시킬때 사용해야하는 함수

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

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

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

  const category = (cate) => {
    if(cate == "전체보기"){
        //전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
        setCateState(state)
    }else{
        setCateState(state.filter((d)=>{
            return d.category == cate
        }))
    }
}

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

버튼 각각의 touchableOpacity에 onPress에서 category 함수가 실행되도록 만들어져있고 '전체보기, 생활 '등의

인자(Argument)를 넘겨주고 있다.

category 함수에서는 넘겨받은 인자를 cate라는 매개변수(parameter)에 저장 하고

그 매개변수가 '전체보기' 일경우 state(전체데이터)로 상태를 변경하고

필터함수를 써서 d에저장된 전체 리스트(필터 함수 사용법 참고)의 category 키값이 cate(넘겨받은 인자) 일경우

그 리스트로 상태를 업데이트 시켜준다.

 

* 정리

1. 화면이 그려짐

2. useEffect가 실행

3. 데이터가 준비됨

4. 상태변경이 일어나면 화면이 다시 그려짐


  • 앱 기능추가

Expo SDK 활용 

- Expo 공홈 API reference 참고하여 명령어로 설치 후 사용가능(VS code terminal에서 설치시 split terminal 누르면 창 하나 더 열려서 쉽게 설치가능)

 

예시 ; StatusBar 색깔바꾸기(사용 방법은 공홈 참고)

명령어 expo install expo-status-bar로 설치 후  <StatusBar style="light" /> 추가


  • Navigator

앱안에서 웹 사이트를 이용하듯, 앱에서 내가 만든 컴포넌트들을 페이지화 시켜주고, 해당 페이지끼리 이동을 가능하게 해주는 기술 또는 라이브러리

 

  • Stack Navigator(컴포넌트를 페이지화 시켜주는 도구)

- React Navigation이라는 큰 도구상자 안에 들어있는 도구 중 하나

- 컴포넌트에 페이지 기능을 부여해주고 컴포넌트에서 컴포넌트로 이동, 즉 페이지 이동을 가능하게 해줍니다

- 열어본 페이지들이 하나씩 쌓여있는 상태에서 뒤로가기를 했을 때, 쌓여진 스택이 차례차례 열리는 구조

- 책갈피 같은 느낌으로 책갈피를 열었을때 바로 그 페이지로 이동됨

원리
StackNavigator.js

import React from 'react';
//설치한 스택 네비게이션 라이브러리를 가져옵니다
import { createStackNavigator } from '@react-navigation/stack';

//페이지로 만든 컴포넌트들을 불러옵니다
import DetailPage from '../pages/DetailPage';
import MainPage from '../pages/MainPage';

//스택 네비게이션 라이브러리가 제공해주는 여러 기능이 담겨있는 객체를 사용합니다
//그래서 이렇게 항상 상단에 선언하고 시작하는게 규칙입니다!
const Stack = createStackNavigator();


const StackNavigator = () =>{
    return (

        //컴포넌트들을 페이지처럼 여기게끔 해주는 기능을 하는 네비게이터 태그를 선언합니다.
        //위에서 선언한 const Stack = createStackNavigator(); Stack 변수에 들어있는 태그를 꺼내 사용합니다.
        //Stack.Navigator 태그 내부엔 페이지(화면)를 스타일링 할 수 있는 다양한 옵션들이 담겨 있습니다.
        <Stack.Navigator
            screenOptions={{
                headerStyle: {
                    backgroundColor: "black",
                    borderBottomColor: "black",
                    shadowColor: "black",
                    height:100
                },
                headerTintColor: "#FFFFFF",
                headerBackTitleVisible: false
            }}
            
        >

            {/* 컴포넌트를 페이지로 만들어주는 엘리먼트에 끼워 넣습니다. 이 자체로 이제 페이지 기능을 합니다*/}
            <Stack.Screen name="MainPage" component={MainPage}/>
            <Stack.Screen name="DetailPage" component={DetailPage}/>
        </Stack.Navigator>
    )
}

export default StackNavigator;

App.js

import React from 'react';
//이제 모든 페이지 컴포넌트들이 끼워져있는 책갈피를 메인에 둘예정이므로
//컴포넌트를 더이상 불러오지 않아도 됩니다.
// import MainPage from './pages/MainPage';
// import DetailPage from './pages/DetailPage';
import { StatusBar } from 'expo-status-bar';

//메인에 세팅할 네비게이션 도구들을 가져옵니다.
import {NavigationContainer} from '@react-navigation/native';
import StackNavigator from './navigation/StackNavigator'

export default function App() {

  console.disableYellowBox = true;

  return ( 
  <NavigationContainer>                 //NavigationContainer 부분은 하나의 책이라고 생각하면됨
    <StatusBar style="black" />           App.js에서 책을 보여주고 세부 책갈피/내용은 
    <StackNavigator/>                     StackNavigator.js에서 수정
 </NavigationContainer>);                
}

* 정리

StackNavigator.js 에서는 createStackNavigator 도구를 impot해서 사용할수있게 만들어주고,

StackNavigator라는 함수를 사용해 책갈피 같은 기능을 만들어준다.(책갈피의 색깔, 모양 등 수정가능)StackNavigator 함수 안 Stack.screen부분을 통해 우리가 만든 컴포넌트들을 페이지화 시킬 수 있다.

(위에있는 페이지가 먼저 열림)

그 페이지들은 StackNavigator라는 책갈피가 달려있는 구조

App.js 에서는 return문 안에 StackNavigator.js 책갈피를 활용할 수 있는 container(책)를 만들어줌

 

 

  • Stack Navigation 적용 후 페이지간 이동시키는 방법

Stack.screen에 등록된 모든 페이지 컴포넌트들은 navigation 와 route 라는 딕셔너리(객체)를 속성으로 넘겨받아
사용할 수 있음. 이 두 딕셔너리는 다음과 같은 기능을 갖습니다.

//navigation 객체가 가지고 있는 함수(setOptions와 navigate, route)


navigation.setOptions({               //navigation 딕셔너리안에 setOptions라는 키값을가진 함수가
   title:'나만의 꿀팁'                  들어있음.(특정 값(나만의 꿀팁)을 전달해주면 제목을 바꿀수있음)
})


//Stack.screen에서 name 속성으로 정해준 이름을 지정해주면 해당 페이지로 이동하는 함수
navigation.navigate("DetailPage")    
                                    //navigation 딕셔너리안에 navigate라는 키값을 가진 함수가 있음
                                      (특정 값(DetailPage)을 전달해주면 그 페이지로 이동가능)



navigation.navigate("DetailPage",{
  title: title                            //navigate사용시 두번째인자(title: title)부분에
})                                         데이터를 입력해 페이지 이동시 그 데이터를 같이 넘겨줄수 있음 
                                           전달된 데이터는 route 딕셔너리로 로 받을 수 있음



//전달받은 데이터를 받는 route 딕셔너리의 형식
//navigate 함수로 전달되는 딕셔너리 데이터는 다음과 같은 모습으로 전달됨

  {
	route : {
		params :{
			title:title
			}
		}
	}


//그렇기 때문에 사용시에는 비구조 할당 방식으로 route에 params 객체 키로 연결되어 전달되는 데이터를 꺼내 사용
const {title} = route.params;

 

실습

MainPage.js

import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';

const main = 'https://storage.googleapis.com/sparta-image.appspot.com/lecture/main.png'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
import { StatusBar } from 'expo-status-bar';
export default function MainPage({navigation,route}) {
  //useState 사용법
	//[state,setState] 에서 state는 이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
  //setState는 state를 변경시킬때 사용해야하는 함수

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

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

  useEffect(()=>{
	   
		//뒤의 1000 숫자는 1초를 뜻함
    //1초 뒤에 실행되는 코드들이 담겨 있는 함수
    setTimeout(()=>{
        //헤더의 타이틀 변경
        navigation.setOptions({
          title:'나만의 꿀팁'
      })  
        setState(data.tip)
        setCateState(data.tip)
        setReady(false)
    },1000)
 
    
  },[])

  const category = (cate) => {
    if(cate == "전체보기"){
        //전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
        setCateState(state)
    }else{
        setCateState(state.filter((d)=>{
            return d.category == cate
        }))
    }
}

  //data.json 데이터는 state에 담기므로 상태에서 꺼내옴
  // let tip = state.tip;
  let todayWeather = 10 + 17;
  let todayCondition = "흐림"
  //return 구문 밖에서는 슬래시 두개 방식으로 주석
  return ready ? <Loading/> :  (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */

    <ScrollView style={styles.container}>
      <StatusBar style="light" />
      {/* <Text style={styles.title}>나만의 꿀팁</Text> */}
			 <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={{uri:main}}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
      <TouchableOpacity style={styles.middleButtonAll} onPress={()=>{category('전체보기')}}><Text style={styles.middleButtonTextAll}>전체보기</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton01} onPress={()=>{category('생활')}}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03} onPress={()=>{category('반려견')}}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04} onPress={()=>{category('꿀팁 찜')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {/* 하나의 카드 영역을 나타내는 View */}
         {
          cateState.map((content,i)=>{
            return (<Card content={content} key={i} navigation={navigation}/>)
          })
        }
        
      </View>
   
    </ScrollView>)
}

 - export default function MainPage({navigation,route}) 에서

navigation,route는 StackNavigator.js의 Stack.screen 부분에서 MainPage를 페이지화 시켰기때문에

페이지화 시킨 컴포넌트는 Stack.screen에서 navigation,route 딕셔너리를 넘겨받아 자동으로 사용 할 수 있다.

 

- return (<Card content={content} key={i} navigation={navigation}/>) 부분에서

navigation={navigation} 은 card 컴포넌트가 페이지화 되지 않았기때문에 navigation을 사용할 수 없지만

MainPage.js가 Stack.screen으로 페이지화 되었기때문에 Card 컴포넌트(자식 컴포넌트/내부 컴포넌트)에

navigation도구를 사용하게 전달해 줄 수 있다.

 

Card.js

import React from 'react';
import {View, Image, Text, StyleSheet,TouchableOpacity} from 'react-native'

//MainPage로 부터 navigation 속성을 전달받아 Card 컴포넌트 안에서 사용
export default function Card({content,navigation}){
    return(
        //카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
        <TouchableOpacity style={styles.card} onPress={()=>{navigation.navigate('DetailPage',**content**)}}>
            <Image style={styles.cardImage} source={{uri:content.image}}/>
            <View style={styles.cardText}>
                <Text style={styles.cardTitle} numberOfLines={1}>{content.title}</Text>
                <Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text>
                <Text style={styles.cardDate}>{content.date}</Text>
            </View>
        </TouchableOpacity>
    )
}

 

 

숙제 하다가 막힌 것

1. 제목 스타일 변경

AboutPage.js

export default function AboutPage({navigation,route}){
    const aboutImage = "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2FaboutImage.png?alt=media&token=13e1c4f6-b802-4975-9773-e305fc7475c4"
    
    useEffect(()=>{
        navigation.setOptions({
            title:"소개 페이지",
            headerStyle: {
                backgroundColor: '#1F266A',
                shadowColor: "#1F266A",
            },
            headerTintColor: "#fff",
        })
    },[])

StackNavigator.js 에서는 전체적인 컴포넌트 title style 설정 할 수 있고

개별적으로 useEffect에 setOptions 써서 headerStyle 및 name 변경 할 수 있음

 

2. 중괄호 안넣어서 에러 난 것. 개............

export default function LikePage({navigation,route}){        ◀ 속성 받아올땐 중괄호 넣기;;;;;;;;;;;