본문 바로가기

코딩 개발일지

[개발일지]스파르타코딩클럽_앱개발 종합반 3-3

이번 강의에서 가장 힘들었던건 useState 를 이해하는것

 

이번 강의에서 중요한것 네가지

 

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

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

3) 속성(Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식 == 그냥 데이터 전달!

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

 

그리고 몰라도 된다는 꽤 유의미한 공식

UI = component(state)

------------------------------------------------------------------------

처음 강의를 보고나서... 내 표정

-------------------------------------------------------

일단코드부터 보고 차근차근 따져봐야 할거같다.

MainPage.js 를 펼쳐놓고 이게 어떻게 돌아가는지부터가 이해가 안됬다.

강의노트 정독하고 강의 두번째 보니까 약간 이해가 가는거 같아서 맞는지 모르겠지만 일단 적어보자

-----------------------------------------------------------

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

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

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

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

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

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

  //data.json 데이터는 state에 담기므로 상태에서 꺼내옴
  // let tip = state.tip;
  let todayWeather = 10 + 17;
  let todayCondition = "흐림"
  //return 구문 밖에서는 슬래시 두개 방식으로 주석
  return ready ? <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>)
}

const styles = StyleSheet.create({
  container: {
    //앱의 배경 색
    backgroundColor: '#fff',
  },
  title: {
    //폰트 사이즈
    fontSize: 20,
    //폰트 두께
    fontWeight: '700',
//..이하 스타일 시트가 있으나 이해하는데는 의미 없으므로 ..지우고

---------------------------------------

//내 마음대로 이해한거라 맞는진 모르겠지만

 

일단 코드를 보기전에 이해할 필요가 있는건  useState 즉 상태라는 개념.

강의노트에 적혀있듯이 '컴포넌트에서 데이터를 유지하고 관리하기 위한 유일한 방법 == 그냥 사용할 데이터!' 라는말이 이해하고나면 맞는말인데 개념자체가 안잡혀 있으니까 그냥 그러거니 하고 넘어가게 되는거 같다

 

괜히 useState라고 적어두니까 이해가 잘 안되는거 같았는데 말 그대로 '상태' 라는것

 

상태가 뭐지? 말 그대로 상태를 말하는것이다 어떤 상태다 그런거.

뭐 이런걸로 생각해도 비슷할까? 전등이 켜져있거나 꺼져있는 상태, 혹은 뛰어가거나 걸어가는 상태, 책이 펼쳐져 있거나덮여 있거나 하는 상태, 바둑알이 흑돌인 상태 혹은 백돌인 상태, 1번버튼을 누른 상태 2번버튼을 누른 상태, 참인상태, 거짓인 상태. -> 그런 상태를 뭔가를 사용해서 컴퓨터에 코드로 표현을 해야 함 (매번 얘가 지금 어떤 상태라고 주절주절 적을수가 없으니까) -> 그래서 데이터로 표기해야함  -> 그 상태를 데이터로 넣어둔게 useState

 

갑자기 의문점? 근데 그런 상태를 변수를 선언해서 거기에 넣으면 되는게 아닌가? ..하고 생각이 들었다가 '상태' 라는게 워낙 많은 종류로 존재하니까 일일이 변수를 지정할수 없어서 몰아서 쓰는게 아닌가 싶기도 하다. 

 

아무튼

----------------------------------------------------------

위 코드를 보고 순서대로 생각해보자

일단 폰을 키면 컴퓨터는 가장 먼저 MainPage.js를 실행한다.

그리고 위에 import 이런것들을 지나서

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

이렇게 상태를 선언한걸 기억하고 코드를 타고 쭉 내려가겠지

useEffect(()=>{

중간에 useEffect를 보게 되는데 이친구는 '화면에 컴포넌트가 그려지고나서' 실행되는것이다. 다시 말하자면 return문이 실행되서 화면이 뜬 다음 실행된는 문구. 그러니까 이부분을 무시하고 쭉 내려갈거다

const category = (cate) => {

여기는 그냥 카테고리라 변수를 선언한거니까 더 밑으로

let todayWeather = 10 + 17;
let todayCondition = "흐림"

여기도 그냥 변수 선언이니까 넘어가고

 return ready ? <Loading/> :  (

자 여기 return문 뒤에 ready가 참이면 <Loading/>을 실행하고 아니면 : 이거 뒤에 함수를 실행할것이다. 위에 보면  const [ready,setReady] = useState(true) ready에 상태를 true로 준 부분이 있다

고로  Loading이 실행될거고

import Loading from '../components/Loading';

위에 주소에 따라서 Loading.js에 있는 함수

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

이게 실행된다. = 화면이 그려 진다

화면이 그려졌다면? 화면이 그려짐과 동시에 아까 위에서 봤던 useEffect가 실행될거다

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

setTimeout 해두고 밑에 1000이 적혀있으니, 1초 후에 밑에 넣어둔 코드가 실행된다.

고로 1초동안 로딩화면이 켜져있다는것이고 1초가 지나면 각각의 상태에 값이 부여가 된다

setState(data.tip)
setCateState(data.tip)

이 코드에서 setState와 setCateState라는 상태에 data.json에 있는 tip 데이터들이 들어간다

//왜 처음 선언한 const [state,setState]  에서 나오는 state가 아닌 두번째에 있는 setState에 값을 집어 넣는가?는 의문은 원래 useState를 쓸때 앞에있는 state를 변경하려고할땐 setState에 넣는거라고 한다.

그러니까 setState(data.tip) 이라고 되어 있으면 그냥 State에 data.tip가 들어갔다고 생각하면 될거같다. 그냥 외우자

//위에 상태 이름은 마음대로 정할수 있는것.(Cate가 뭔가 했더니 그냥 카테고리의 앞글자를 붙인거다. 아무렇게 붙여도됨)

setReady(false)

그리고 setReady가 ture에서 false가 됬다. 1초도 지났고 얼른 리턴문으로 가보자

 
return ready ? <Loading/> :  (
    <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>)
}

자 이제 setReady가 false 그러니까 ready가 false로 바꼇으니까 드디어 " : ( " 이후로 나오는 함수가 실행되면서 스크롤뷰... ok 텍스트...ok 이미지... ok  스크롤뷰 가로로...ok 터쳐블오퍼서티.... 5가지...ok 그리고 뷰

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

이부분 cateSate에 data.tip 전체 데이터가 들어가 있으니 data.json에 있는 전체 데이터가 map함수로 반복문을 돌리는데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>)
}

여기까지가 아무것도 안누른 메인화면 출력

-----------------------------------------

만약에 버튼중에 재테크 버튼을 누른다면 

<TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>

텍스트가 재테크인 버튼을 눌렀다면(onPress) '재테크' 라는 글씨가 category에 들어갈거고 위쪽에 선언했던

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

이부분에 cate라는 변수에 '재테크' 라는 글씨가 들어가게 된다 ( cate = '재테크') 그리고 if문을 돌리면서 cate가 "전체보기" 가 아니므로 else 부분이 실행되고 여기서 setCateState에 state가 필터된 데이터가 들어가게 된다

--------------------------------------------

이 부분을 다시 정리하자면 상태가 변화될 부분이 CateState니까 setCateState라는곳에 데이터를 집어넣는 것이고 (그냥 외우자) 집어넣는게 state가 필터된것(state.filter) 인데 state가 무슨 데이터를 가지고 있나?

처음에 앱을 켯을때는 state에 아무것도 없었지만 const [state,setState] = useState([])

로딩창이 뜨면서 useEffect가 실행되었고 1초가 지나면서 setState(data.tip)를 했었다.

useEffect(()=>{
    setTimeout(()=>{
        setState(data.tip)
        setCateState(data.tip)
        setReady(false)
    },1000)

고로 State 는 data.tip 즉 data.json에 있는 tip 전체 항목을 말한다.

----------------------------------------

다시 위에 이어서 하자면

 }else{
        setCateState(state.filter((d)=>{
            return d.category == cate

state.filter를 하는데 category값이 cate (= 재테크) 와 같은것들만 필터해서 꺼내놓게 된다

그래서 재테크에 해당하는것들로만 정렬이 된다.

여기서 만약 전체보기를 누른다면? 다시 위쪽으로 돌아가서 똑같이 반복으로 함수들이 실행될거고

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

이번엔 cate가 '전체보기' 이므로 if문에 걸려서 setCateState(state) 즉 전체 data.tip 데이터가 다 들어가게 된다.

-------------------------------------

내가 이해한게 대충 맞는거겠지?