본문 바로가기

코딩 개발일지

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

3주차 숙제 - 꿀팁 찜 페이지 만들기

1. 먼저 어바웃 페이지를 만들자

 제공된 이미지를 보면 온도 아래에 소개버튼이 있다.

 

우선 메인페이지에서 오늘의 날씨 텍스트 아래에 소개 페이지 버튼을 만든다

터쳐블오퍼서티로 해서 클릭시 {navigation.navigate('AboutPage')} 설정해 준다.

 <TouchableOpacity style={styles.aboutButton01} onPress={()=>{navigation.navigate('AboutPage')}}><Text style={styles.middleButtonText}>소개 페이지</Text></TouchableOpacity>

텍스트 스타일은 비슷한거 같으니 그대로 쓰고 버튼스타일은 살짝 다르니 aboutButton01 만들어 줬다

  aboutButton01: {

    width:100,

    height:40,

    padding:15,

    alignSelf:"flex-end",

    backgroundColor:"#fdc453",

    borderColor:"deeppink",

    borderRadius:15,

    margin:7

}

위에 미들버튼 정보 그대로 복사한 후에 alignSelf: “flex-end” 넣어서 우측정렬로 바꿧다.

그런데 버튼에 글씨가소개 페이지이렇게 다섯글자가 나와야 하는데 소개 출력 되는지 모르겠다.

잘 해결이 안되는데 일단 넘어가고 나중에 생각해보자.

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

어바웃페이지도 페이지화 시켜야 하므로 

스택네비게이터.js에서 어바웃페이지도 다른페이지와 마찬가지로 페이지화 시켜준다.

<Stack.Screen name="AboutPage" component={AboutPage}/>    

아참 위에 import AboutPage 잊어선 안된다. 

먼저 import 해두지 않으면 밑에서 컴포넌트로 aboutpage 봤자 색깔이 다르기때문에 뭐가 잘못됬는지 눈치챌수 있다.

 

2. AboutPage에서 링크 만들기

DetailPage에서 썻던거 그대로 복사해 오면 될거같다.

import * as Linking from 'expo-linking';

먼저 링킹 기능을 쓸거니까 임포트 해주고

const link = () => {

        Linking.openURL("https://spartacodingclub.kr")

    }

Link를 선언해준다. const할 때 이건 리턴문 안에다가 선언해준다. 주소는 안하지만 존재하긴 하는 인스타 계정 주소로 바꿔줄거다.

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

. 이대로 놓고 돌려보니까 문제가 있었다.

 

상단 헤더 부분이 하얀 배경에 AboutPage라고 떠있어서 매우 거슬린다. 이래서 useState useEffect로 헤더 제목을 바꿔줬던걸까? 디테일페이지에서 썻던걸 그대로 가져와야할거같다 어바웃페이지도 페이지니까

export default function AboutPage(navigation,route){

괄호 안에 똑같이 넣어주고 useState를 똑같이 만들어 주려다가 다시 생각해보니 지금 만들려는 거에선 useState가 쓸곳이 없다. 어떻게 하지 고민하다가 정답을 봐보니까 그냥 안써도 되는거였다.

배경색은 밑에서 따오고

useEffect(()=>{

        navigation.setOptions({

        title:"소개 페이지",

        headerStyle: {

            backgroundColor: '#1F266A',

            shadowColor: "#000",

        },

        headerTintColor: "#fff",

    })

},[])

이러고 돌리니까 에러가 뜬다....아!

import React,{useEffect} from 'react';

위에 useEffect를 임포트 했어야 한다! ...응? 그래도 에러가 뜬다.

정답코드를 보고 아무리 비교해도 잘못한게 없는거같은데 골치 아프다. 이래서 포기하는걸까?

정답코드를 넣고 돌리면 오류가 안뜨고, 내 코드를 넣고 돌리면 빨간 에러가 뜬다...진짜 뭐니

한줄한줄 비교하면서 결국 찾았다

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

괄호 안에 {}중괄호를 빼먹었었다.. 좋은교훈이라 생각하자

 

3. 찜 페이지 만들기

일단 페이지부터 만들고 페이지화 시키는걸 해야겠지? 친절하게 페이지 이름도 정해줬다. Pages 폴더안에 다른 페이지와 마찬가지로 먼저 만들어 준다.

그리고 페이지화 해야 하는데, Stacknavigator.js 파일로 들어가보면 다른 페이지들 만들어둔게 있어서 그대로 복사해서 주소만 바꿔주면 된다.

 <Stack.Screen name="LikePage" component={LikePage}/>

 

메인페이지에서 꿀팁찜 부분에 onPress시 꿀팁찜이라는 category라는게 애초에 필요 없으므로 Card.js 에서 디테일페이지로 이동했던방식으로 똑같이 페이지 이름만 바꿔서

<TouchableOpacity style={styles.middleButton04} onPress={()=>{navigation.navigate('LikePage',content)}}><Text style={styles.middleButtonText}>꿀팁 </Text></

이렇게 만들어 주면 되겠지?

 

그리고 LikePage를 만들어준다.

주어짐 찜 상태 데이터를 보니 이번에도 useState 이름을 tip으로 지어야 할것 같다.

이번엔 초기값으로 주어진게 있으니 그리 복잡한게 없을것 같다.

메인페이지에서 썻던 맵 함수를 그대로 쓰면 될것같다

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

여기서 View를 ScrollView로 바꿔주고 cateState로 썻던 부분도 tip으로 바꿔준다. 아참 꿀팁 찝 카드도 새로 만들라고 했으니 Card를 LikeCard로 바꿔준다

 

4. LikeCard만들기

Card 꺼를 그대로 복사해서 함수 이름 바꿔주고 터쳐블오페시티를 View로 바꿔주고, View태그니까 onPress부분을 지워준다

 

....이렇게 하고 돌려보면.. 에러가 뜬다... 온갖시도를 다 해봐도 도저히 이유를 알수가 없었다.

 정답을 복사해서 돌렸는데도 똑같이 에러가 떠서 일단 포기하고, 다음날 정답을 다시 처음부터 다 복사해서 돌리니까 에러가 안뜬다..

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

대체 원인이 뭔지 답답해서 코드 하나하나 비교분석 해보고나서 결국 원인을 찾았다.

MainPage에서 꿀팁찜 버튼을 눌렀을때 동작을 Card.js에서 그대로 복사해서 썻더니 문제가 생겼었다.

onPress={()=>{navigation.navigate('LikePage',content)}

이걸 페이지 이름만 바꿔서 그대로 썻는데 지금 하는 숙제는 화면만 띄울뿐 같이 보내지는 데이터가 없었던 것 같다. content부분을 지우니까 문제없이 돌아간다.

<TouchableOpacity style={styles.middleButton04} onPress={()=>{navigation.navigate('LikePage')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>

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

에러 원인을 찾아서 그래도 잠을잘수 있을거 같다.

..

아, 소개 페이지 이름이 왜 '소개'만 짤려서 나오는거지?

나중에 찾아보자

//메인페이지 코드 저장

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';
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(()=>{
    navigation.setOptions({
      title:'로딩창',
      headerStyle: {
          backgroundColor: '#fdc453',
          shadowColor: "#fdc453",
      },
      headerTintColor: "#fff",
  })
    //뒤의 1000 숫자는 1초를 뜻함
    //1초 뒤에 실행되는 코드들이 담겨 있는 함수
    setTimeout(()=>{
        //헤더의 타이틀 변경
        navigation.setOptions({
          title:'나만의 꿀팁',
          headerStyle: {
            backgroundColor: 'white',
            shadowColor: 'white',
        },
        headerTintColor: "#000",
        })  
        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>
       <TouchableOpacity style={styles.aboutButton01} onPress={()=>{navigation.navigate('AboutPage')}}><Text style={styles.middleButtonText}>소개 페이지</Text></TouchableOpacity>

      <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={()=>{navigation.navigate('LikePage')}}><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>)
}

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
  },
  aboutButton01: {
    width:100,
    height:50,
    padding:15,
    alignSelf:"flex-end",
    backgroundColor:"#fdc453",
    borderColor:"deeppink",
    borderRadius:15,
    margin:7
  }

});