앞에서 프론트 앤드(앱의 화면)을 만들었기 때문에, 이제는 백앤드 부분, 사용자들에게 보여줄 데이터와 사용하면서 발생하는 데이터들을 관리하는 방법에 대해 배운다.
앱과 서버: 데이터가 담겨있는 곳, 즉 서버와 앱과의 관계를 살펴본다.
서버리스: 서브를 직접 구축하지 않고 서버를 사용하는 방법을 알아본다. (필요한 기능들만 가져와서 사용할 수 있다.)
파이어베이스: 서버리스의 한 종류인 파이어베이스 사용방법을 익힌다.
API
API의 응답 방식: JSON (키와 밸류)
데이터를 요청하는 시점?
앱 화면이 그려지면 가장 먼저 실행되는 함수? useEffect 그렇기에, 항상 useEffect 함수 안에서 API를 요청한다.
외부 날씨 API를 가져와서 앱 화면에 출력해주기
날씨를 API를 사용하기 위해서는 현재 위치의 좌표가 필요하다.
expo install expo-location import * as Location from "expo-location"; // mainPage 상단에 작성하는 코드
const getLocation = async () => { //수많은 로직중에 에러가 발생하면 //해당 에러를 포착하여 로직을 멈추고,에러를 해결하기 위한 catch 영역 로직이 실행 try { //자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await await Location.requestPermissionsAsync(); const locationData= await Location.getCurrentPositionAsync(); console.log(locationData) } catch (error) { //혹시나 위치를 못가져올 경우를 대비해서, 안내를 준비합니다 Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?"); } }
try-catch문을 사용하여 에러 발생 상황을 잡는다.
location 정보를 가져가도 되는지 허락을 구하는 명령어
await location.requestPermissionsAsync()
현재 위치를 받는 명령어
getCurrentPositionAsync
자바스크립트는 실행결과가 나오는대로 결과값을 먼저 던져주기 때문에 순서가 뒤죽박죽이 될 수 있다. 코드에 작성된 순서대로 함수가 실행되게 하려면, 함수 앞에 async 를 붙이고, 함수 내부에 await를 두어 순차적으로 함수가 실행되게 한다.
const func = async function(){ await func01() await func02() }
const func = async () => { await func01() await func02() }
화살표 함수로도 async를 사용할 수 있다.
JSON 방식으로 response가 오기 때문에 key값을기준으로 원하는 value를 찾아나간다.
const temp = result.data.main.temp;
result 밑에 있는 data라는 키 값 밑에 main 밑에 temp라는 밸류 값을 찾아 가서 temp라는 변수에 넣어 앱 화면에 출력 시킨다.
API 응답 값에서 필요한 정보를 찾아 나가는 방법은 이미 해봐서 알고 있어서 이해하는데 쉬웠다!! 하지만,, 앱 화면에서 위치를 요구하는 permission이 뜨지 않아서 앱 자체에서 API가 불러와지지 않았다.. 왜 그런지 몰라서 google에 찾아보고 여러가지 방법을 시도해본 결과 "새로고침"이 정답이었다... ㅋㅋㅋㅋㅋㅋㅋ
이 방법을 알게 된 계기가 expo에서는 앱 화면을 웹(web)에서도 띄워 볼 수 있게 하는 서비스를 제공하는데, 핸드폰에서 작동하지 않으니 웹에서라도 확인해 봐야겠다 하고 웹으로 띄웠는데 API가 너무 작동이 잘되어서, 그럼 핸드폰에 문제가 있다!하고 생각하여 expo 앱을 새로고침 해보니 permission 창도 뜨고, 너무 잘 작동되는 것을 볼 수 있었다.
역시 등잔밑이 어둡다는 말이 정말이었다...
왜 새로고침해볼 생각을 안했을까..
⠀
⠀
날씨라는 상태를 또 만들어주어 데이터를 관리해준다!
const [weather, setWeather] = useState({
temp : 0,
condition : ''
})
setweather(상태 변경함수)를 통하여 API로 받아온 날씨를 weather 상태에 넣어 상태변수를 변경한다.그래서 새로운 데이터가 들어오면 새로운 데이터로 상태가 변경되어 변경된 데이터로 화면을 다시 그려준다.
⠀
서버를 직접 만들 필요가 없다!! 서버를 직접 구성할 필요 없이 필요한 서버 기능만을 가져다 서비를 제공하는 서비스이다.
구글에서 만든 서버리스 서비스이다.
한 서비스를 만드는데 충분하고 많은 서버적 기능들을 제공해준다. (로그인, 이미지, 푸시 알람 등등)
데이터를 자율적으로 저장하고 구현할 수 있다!!
사용방법
1) 파이어베이스에 가입한다.
2) 파이어베이스 프로젝트 생성
3) 사용 할 파이어베이스 서비스 활성화
파이어베이스를 앱과 연결시켜주기
주소를 연결해주는 방법
파이어베이스에서 프로젝트를 웹앱으로 설정하면, firebaseConfig라는 딕셔너리를 제공해준다.
expo에서 firebase를 사용할 수 있는 모듈이 있다.
expo install firebase
를 입력하여 모듈을 설치해준다.
파이어베이스에서 만들어야 하는 것들을 순차적으로 프로젝트를 만든다.
storage라는 곳에서 프로젝트를 하나 생성하면, 올리는 파일을 주소로 관리할 수 있게 된다.
폴더로도 관리할 수 있어서 폴더 생성 후 이미지를 업로드 하면, 이미지 고유의 주소가 생겨 어디에서든지 접근할 수 있게 되어 접근성이 높아지게 된다!
파이어베이스 안에 있는 서비스로, JSON 형태로 데티터가 저장/관리되는 데이터베이스 서비스이다.
이 서비스를 사용할 때, 파이어베이스에서 제공하는 함수를 이용하기만 하면 데이터 저장, 수정, 삭제가 가능하다고 한다!!
useEffect에서 setTimeout 함수를 사용하는 것보다 사용자들마다 네트워크 상태가 다르기 때문에 무조건 몇초 뒤에 실행시키는 setTimeout 함수보다 파이어베이스 API의 실행 완료 시간에 맡겨두는 것이 더 좋다.
useEffect(()=>{ //헤더의 타이틀 변경 navigation.setOptions({ title:'나만의 꿀팁' }) firebase_db.ref('/tip').once('value').then((snapshot) => { console.log("파이어베이스에서 데이터 가져왔습니다!!") let tip = snapshot.val(); setState(tip) setCateState(tip) getLocation() setReady(false) });
=> 즉, 원격으로 데이터를 요청하고 요청한 데이터를 앱 화면에 보여주는 것이다!
⠀
여기서 꿀팁!
만들어 놓은 데이터들을 잘 관리하고 잘 불러오려면 각 데이터별로 고유의 index 번호가 존재하는 것이 좋다!(사실 거의 필수)
인덱스 번호를 서버에 넘겨주는 이유는 실시간 데이터(RealTime Database)를 보여주기 위함이다.
실시간으로 변경되는 데이터를 보여주려면, 변경된 데이터를 서버로부터 바로 가져와야 하기 때문에 인덱스로 데이터를 가져오는 것이다.
firebase_db.ref('/tip').once('value').then((snapshot) => {})
firebase_db: firebase파일에서 압축해서 export시킨 변수이름, firebase API에 접속하는 마스터키 역할을 한다.
firebase_db.ref(''): reference = ' ' 안에 들어간 데이터 조회
snapshot: 조회한 데이터가 담기는 곳, { } 내부에서 조회한 데이터를 사용할 수 있고, 필요한 데이터는 snapshot.val()로 가져와 변수에 담아서 사용할 수 있다. ( ex. let tip = snapshot.val(); )
파이어베이스로 데이터를 보내 저장하는 상황에서 쓰이는 명령어들!
어떤 구조로 데이터를 보내 저장해야 할지 알아보자
사용자가 원하는 정보를 저장하고 싶다면, 각각의 사용자의 정보도 따로 고유한 번호가 지정되어야 할 것이다. 사용자들을 관리하는 것도 expo에서 모듈로 제공한다.
expo install expo-constants
import Constants from 'expo-constants'; console.log(Constants.installationId)
installationId: 사용자 디바이스의 고유한 아이디를 의미한다.
firebase_db.ref('/like/'+user_id+'/'+ tip.idx).set(tip,function(error){ console.log(error) Alert.alert("찜 완료!") });
ref로 저장될 장소를 지정하고, set 함수를 이용한다. set 함수의 첫번째 인자는 상태 데이터를 보내준다.(현재 클릭된 tip)
++ 원래 firebase database에는 like 폴더가 없었는데, 위에 코드를 작성함으로써 폴더를 생성하고, tip까지 저장하게 되는 것이다.
like 폴더 / 사용자 id / tip의 인덱스 라는 주소에 set 함수로 저장해준 tip이 저장되게 되는 것이다.
과제를 제출하였다!
4주차 강의를 듣고 일주일만에 과제를 하는 것이어서 내용을 좀 많이 까먹었었다. 그래서 복습도 하면서 과제를 하여 시간이 조금 오래 걸리게 되었지만, 내 힘으로 왠만한 부분들을 구현해 낼 수 있어서 다행이었다!! (많이 안까먹은 것 같다.. ㅋㅋㅋ)
꿀팁 찜 페이지를 만들고, 찜한 카드들을 가지고 와서 보여주고, 삭제까지 하는 기능을 구현했는데, 다른 부분들보다 삭제 후 로딩하는 기능이 가장 어려웠었다. 공식문서를 찾아서 삭제까지 가능했는데, 삭제를 하고는 다시 likePage로 돌아오는 것이 안되어서 이 문제를 가지고 한참을 씨름했던 것 같다.
결국,
let tip = snapshot.val();
let tip_list = Object.values(tip)
setTip(tip_list)
이 방법으로 서버에서 넘어오는 데이터를 다시 다른 변수로 파싱하여 상태를 설정하니 잘 작동되었다.
추가적으로,
메인 화면에서 카드들을 클릭하면, 데이터가 없어서 오류가 날 수 있는 상황을 방지하기 위해 설정해 놓은 기본 카드 화면이 먼저 보이고, 방금 클릭한 카드가 그 위로 보이게 되어, 카드 화면이 2번 열리는 것처럼 보이는 상황이었다. 강의에서 강사님의 상황도 동일해 보였는데, 나는 이게 너무 거슬려서 어떻게하면 기본 데이터는 살려 놓지만, 다른 카드를 눌렀을 때 카드가 2번 뜨는 것처럼 보이지 않게 할 수 있을까 생각해 보았다.
생각을 좀 해본 결과로, 페이지가 뜰 때, 이미지가 링크로 연결되어 있어서 이미지를 불러오고 화면에 띄워주는 시간 때문에 default 카드가 확실하게 보이고, 다른 카드가 엎쳐지는 것처럼 보인다는 것을 알게 되었다. 이미지 때문인 것을 알게 되어, default 카드의 이미지 링크를 지워보았다.
지우니, 아주 빠른 속도로 로딩되었다가 사라지면서 어어어엄청 예민하지 않으면 변경되는 화면을 눈치채지 못할 정도가 되었다. 그래서 만족스럽게 과제를 마무리할 수 있었다!!