본문 바로가기

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

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

백엔드 !

 

서버리스: 서버를 직접 구축하지 않고 서버를 사용하는 방법(파이어베이스 등)

 

👉 앱에서 서버에 데이터를 요청하거나 데이터를 보내는 대화를 하려면 서버가 정한 규칙에 따라

대화 요청(Request)을 해야합니다.

정한 규칙에 따라 요청을 하지 않으면 응답(Response)이 오지 않습니다.

서버 쪽에서 정한 규칙을 우린 보통 API(Application Programming Interface)라고 부릅니다.

그 규칙의 형태는 다음과 같이

👉 서버가 제공하는 도메인일수도 있고

www.sparta.com/getdata ←- 데이터 조회 API
www.sparta.com/setData ←- 데이터 저장 API

 

👉 서버가 만들어놓은 함수를 그냥 이용해서 사용하는 규칙일 수도 있습니다.

db.ref('/like/').on('value') ←- 데이터 조회 API
db.ref('/like/').set(new_like); <-- 데이터 저장 API

 

  • 서버가 앱에 데이터를 줄 땐 JSON 형태로 데이터를 전달해줌. JSON 형태는 리스트와 딕셔너리의 복합 구조

 

  • 리액트 네이티브에 주로 데이터를 준비하는 시점

 

1. 앱 화면이 그려진 다음 데이터를 준비 ← useEffect

 

  • 그래서 우리는 보통 앱 화면이 그려진 다음 useEffect 안에서 서버측에서 제공해주는 API를 이용해 필요한 데이터들을 준비합니다. 
  •  
  • useEffect(()=>{ //서버 API 사용 //이 화면에서 사용 할 데이터 준비 등... },[])
  •  

2. 앱에서 사용자가 저장 버튼을 눌렀을때

  • 👉 예를 들어 나만의 꿀팁 앱에서 데이터 저장 시점은, 사용자가 꿀팁을 보고 팁 찜하기 버튼을 눌렀을 때 일겁니다.

 


  • 날씨 앱에 날씨 API 사용해보기

1) 현재 위치(좌표) 데이터 필요, 가져오기 (어디의 날씨인지 알아야 하니까요!)

2) 위치 데이터를 이용해 현재 위치 날씨 데이터 가져오기

 

(1) 앱 위치 정보 권한 설정

expo install expo-location

(2) try / catch(에러 방지 코드)

  • 외부 API 요청 작업은 try /catch로 감싸기
  • try{ } 부분엔 API요청 같은 작업 코드를, catch{ } 부분엔 에러가 발생 했을 때 실행 할 코드를 작성합니다.
  • 👉 try/ catch는 API 사용 할 땐 주로 사용하는 에러 처리 방지 코드입니다. 여러분들은 API 호출을 제대로 호출 했을 수도 있지만 서버 측에서 혹은 휴대폰 자체에서 등, 앱 외적으로 오류가 발생 할 수 있습니다. 이런 상황들을 처리하는코드입니다.

ex)

const getLocation = async () => {
    //수많은 로직중에 에러가 발생하면
    //해당 에러를 포착하여 로직을 멈추고,에러를 해결하기 위한 catch 영역 로직이 실행
    try {
      //자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await
      await Location.requestForegroundPermissionsAsync();                 // Expo 공식문서 
      const locationData= await Location.getCurrentPositionAsync();       예시 문장 그대로사용
      console.log(locationData)

    } catch (error) {
      //혹시나 위치를 못가져올 경우를 대비해서, 안내를 준비합니다
      Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껐다 켜볼까요?");
    }
  }

 

(3) 함수 실행 순서를 정해주는 async / await

 

👉 코드 상에 async / await 키워드를 볼 수 있습니다.

이는 단순히 외부 API 호출 및 휴대폰 기기에 대한 정보/파일 등에 접근할 때 사용하는 키워드라고 생각하면 쉬운데요!

이걸 이때 쓰는 이유는 함수의 실행 순서를 딱! 고정하기 위해서입니다. 외부 네트워크 작업(API 호출)이나 휴대폰의 파일 시스템 또는 위치정보 가져오기 같이 무거운 작업을들 하는 경우 어떤 작업부터 할지 알수가 없습니다.

자바스크립트의 특징인 "비동기"(준비된 함수부터 먼저실행 하게하는 기능?) 라는 특징 때문인데,

  1. 외부 API 작업과
  2. 앱이 아닌 휴대폰 자체 기능(위치 정보 권한 물어보기 등)을

사용할 땐 async / await를 사용한다 생각하면 편합니다

사용 할 땐 함수들을 감싸는 함수 선언 부 앞에 async, 사용하는 함수들 앞엔 await 입니다

다음 예시를 보시면 금방 이해가 됩니다

  • 3가지 사용방법
const func = async function(){
	await func01()
  await func02()
}
const func = async () => {
   await func01()
   await func02()
}
async function func(){
	 await func01()
	 await func02()
}

MainPage.js

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

const main = 'https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fmain.png?alt=media&token=8e5eb78d-19ee-4359-9209-347d125b322c'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
import { StatusBar } from 'expo-status-bar';
import * as Location from "expo-location";
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:'나만의 꿀팁'
    })  
		//뒤의 1000 숫자는 1초를 뜻함
    //1초 뒤에 실행되는 코드들이 담겨 있는 함수
    setTimeout(()=>{
        //헤더의 타이틀 변경
        getLocation()
        setState(data.tip)
        setCateState(data.tip)
        setReady(false)
    },1000)
 
    
  },[])

  const getLocation = async () => {
    //수많은 로직중에 에러가 발생하면
    //해당 에러를 포착하여 로직을 멈추고,에러를 해결하기 위한 catch 영역 로직이 실행
    try {
      //자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await
      await Location.requestForegroundPermissionsAsync();
      const locationData= await Location.getCurrentPositionAsync();
      console.log(locationData['coords']['latitude'])
      console.log(locationData['coords']['longitude'])

    } catch (error) {
      //혹시나 위치를 못가져올 경우를 대비해서, 안내를 준비합니다
      Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?");
    }
  }

  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.aboutButton} onPress={()=>{navigation.navigate('AboutPage')}}>
          <Text style={styles.aboutButtonText}>소개 페이지</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
  },
  aboutButton: {
    backgroundColor:"pink",
    width:100,
    height:40,
    borderRadius:10,
    alignSelf:"flex-end",
    marginRight:20,
    marginTop:10
  },
  aboutButtonText: {
    color:"#fff",
    textAlign:"center",
    marginTop:10
  }


});

 

  • 날씨 서버 외부 API 적용

서버가 제공하는 도메인 형식의 API를 사용하려면, 사용을 위한 도구가 필요합니다.

axios 라는 주소사용도구 설치해서 사용

 

명령어 입력해서 설치

yarn add axios

 

MainPage.js(axios 적용해서 openweathermap.org 사이트의 날씨데이터 가져옴)

(API가 주는 데이터에서 필요한 데이터를 추출하는 방법은 사이트 API 공식문서

https://openweathermap.org/current 참고)

 

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';
import * as Location from "expo-location";
import axios from "axios"                                 //axios import

export default function MainPage({navigation,route}) {
  //useState 사용법
	//[state,setState] 에서 state는 이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
  //setState는 state를 변경시킬때 사용해야하는 함수

  //모두 다 useState가 선물해줌
  //useState()안에 전달되는 값은 state 초기값
  const [state,setState] = useState([])
  const [cateState,setCateState] = useState([])
  //날씨 데이터 상태관리 상태 생성!
  const [weather, setWeather] = useState({                 //날씨데이터 상태 초기값 설정
    temp : 0,
    condition : ''
  })

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

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

  const getLocation = async () => {
    //수많은 로직중에 에러가 발생하면
    //해당 에러를 포착하여 로직을 멈추고,에러를 해결하기 위한 catch 영역 로직이 실행
    try {
      //자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await
      await Location.requestForegroundPermissionsAsync();
      const locationData= await Location.getCurrentPositionAsync();
      console.log(locationData)
      console.log(locationData['coords']['latitude'])
      console.log(locationData['coords']['longitude'])
      const latitude = locationData['coords']['latitude']            //위도, 경도값 가져와서
      const longitude = locationData['coords']['longitude']            각 변수에 저장
      const API_KEY = "cfc258c75e1da2149c33daffd07a911d";         //API제공 사이트가 요구하는 KEY값 입력(가입후 받을수 있음)
      const result = await axios.get(
        `http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
      );                                    // axios를 사용해 도메인으로 API를 이용하여 데이터를 제공 받는 모습

      console.log(result)
      const temp = result.data.main.temp; 
      const condition = result.data.weather[0].main
      
      console.log(temp)
      console.log(condition)

      //오랜만에 복습해보는 객체 리터럴 방식으로 딕셔너리 구성하기!!
      //잘 기억이 안난다면 1주차 강의 6-5를 다시 복습해보세요!
      setWeather({
        temp,condition                                  // 상태 데이터 업데이트
      })

    } catch (error) {
      //혹시나 위치를 못가져올 경우를 대비해서, 안내를 준비합니다
      Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?");
    }
  }

  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}>오늘의 날씨: {weather.temp + '°C   ' + weather.condition} </Text>
       <TouchableOpacity style={styles.aboutButton} onPress={()=>{navigation.navigate('AboutPage')}}>
          <Text style={styles.aboutButtonText}>소개 페이지</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>)
}

- style 생략 -

 

서버리스(serverless)

  • 서버리스(serverless)란?  서버를 직접 구현, 구성 할 필요없이 필요한 서버 기능을 제공하는 곳에서, 서비스를 사용하는 것

 

  • 파이어베이스 이용

1. 파이어베이스 프로젝트 생성

2. 파이어베이스를 앱에 연결

(파이어베이스를 이용할 수 있게 도와주는 expo 도구 설치

expo install firebase

firebaseConfig.js 파일 생성 후 프로젝트 만들고 나온 SDK 데이터를 붙여넣어준다.)