--> 멀리 있는 파일 저장소에 이미지, 사용할 파일 등등을 넣어놓고 필요할 때마다 꺼내쓰는 용도로 사용
구글 클라우드의 경우 파일에 접근하기 위해 일일이 구글 클라우드에 접속해야 함
파이어베이스가 제공하는 파일 저장소의 경우 파일 하나하나에 주소를 제공하여 주소를 입력하는 것 만으로 그 파일에 접근할 수 있다.
--> 파이어베이스에 접속 --> 내가 만든 서버에 접속 --> 서버의 서비스들 중 Storage를 선택 및 활성화(좌측 메뉴바에 있음)
--> 파일 저장소 스토리지
--> Storage에 넣는 것 만으로 파이어베이스가 해당 이미지에 고유 주소를 부여해주기 때문에 쉽게 외부에서 접근할 수 있다.
--> JSON형태로 저장/관리되는 데이터베이스 서비스
--> 데이터베이스 주소는 파이어베이스를 앱에 연결할 때 주어지는 (SDK) JSON데이터에 제공되어있음 --> 따로 기입하지 않아도 됨
--> 데이터 사용에 대한 공식문서 : https://firebase.google.com/docs/reference/js/v8/firebase.database.Reference?authuser=2#once
--> 파이어 베이스가 제공하는 서비스 중 Realtime Database 클릭
--> 규칙 탭을 통해 제 3자가 데이터베이스의 데이터에 접근, 수정, 삭제 할 수 있는지 설정할 수 있음
공식문서 : https://firebase.google.com/docs/database/web/read-and-write?hl=ko
//import * as firebase from 'firebase/app';
import firebase from 'firebase/app';
// 사용할 파이어베이스 서비스 주석을 해제합니다
//import "firebase/auth";
import "firebase/database";
//import "firebase/firestore";
//import "firebase/functions";
import "firebase/storage";
// Initialize Firebase
//파이어베이스 사이트에서 봤던 연결정보를 여기에 가져옵니다
const firebaseConfig = {
apiKey: "AIzaSyBKG2xY91x23W8PF1231k5OUJ5o9kHSKYQeNWUw",
authDomain: "sparta-psytest-gun.firebaseapp.com",
databaseURL: "https://sparta-psytest-gun.firebaseio.com",
projectId: "sparta-psytest-gun",
storageBucket: "sparta-psytest-gun.appspot.com",
messagingSenderId: "781790378482",
appId: "1:78179037128482:web:ddbca5330779f67b947136b",
measurementId: "G-3F5L9F3340Q3"
};
//사용 방법입니다.
//파이어베이스 연결에 혹시 오류가 있을 경우를 대비한 코드로 알아두면 됩니다.
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
export const firebase_db = firebase.database()
import {firebase_db} from "../firebaseConfig"
위 코드의 export된 부분을 가져오고 있음
firebaseConfig는 파이어베이스 SDK 코드를 담고 있는 파일
firebase_db.ref('/tip').once('value').then((snapshot) => {
let tip = snapshot.val();
})
--> 위의 코드의 방식으로 데이터 조회
--> 위 코드의 의미는 해당 파이어베이스 서버(SDK에 기입된)의 리얼타임 DB에 tip에 해당하는 이름(즉 리얼타임 DB에서 /tip이라는 주소에 존재하는)의 JSON데이터를 가져와서 tip이라는 변수에 할당한 것이다
--> firebase_db.ref('/tip').once('value').then((snapshot)
ref('/tip') --> 이부분에서 데이터베이스의 어느 JSON데이터에 접근할지 설정 (여기서는 tip이라는 이름의 JSON에 접근)
--> 리얼타임 DB의 모든 주소를 적지 않아도 되는 이유는 파이어베이스 연결시의 SDK에 주소가 이미 기입되어 있기 떄문
firebase_db.ref('/tip').once('value').then((snapshot) => {})
-->서버리스를 이용하여 데이터베이스를 조회하기 위해 파이어베이스 측에서 정해놓은 API 사용방법
-->조회한 데이터는 snapshot 부분에 담겨서 {} 내부에서 사용할 수 있는데, 그 중 실제 우리에게 필요한 데이터는 snapshot.val()로 가져와 변수에 담아 사용할 수 있음
snapshot.val()
--> 위의 함수 내에서 이 함수를 사용하여 데이터를 조회해 가져올 수 있음
......
import {firebase_db} from "../firebaseConfig"
......
......
useEffect(()=>{
//뒤의 1000 숫자는 1초를 뜻함
//1초 뒤에 실행되는 코드들이 담겨 있는 함수
setTimeout(()=>{
//헤더의 타이틀 변경
navigation.setOptions({
title:'나만의 꿀팁'
})
firebase_db.ref('/tip').once('value').then((snapshot) => {
console.log("파이어베이스에서 데이터 가져왔습니다!!")
let tip = snapshot.val();
setState(tip)
setCateState(tip)
getLocation()
setReady(false)
});
// setTimeout(()=>{
// let tip = data.tip;
// setState(tip)
// setCateState(tip)
// getLocation()
// setReady(false)
// },500)
},1000)
},[])
......
{
"tip":[
{
"idx":0,
"category":"생활",
"title":"먹다 남은 피자를 촉촉하게!",
"image":"https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fpizza.png?alt=media&token=1a099927-d818-45d4-b48a-7906fd0d2ad3",
"desc":"먹다 남은 피자는 수분이 날라가기 때문에 처음처럼 맛있게 먹을 수 없는데요. 이럴 경우 그릇에 물을 받아 전자레인지 안에서 1분 30초에서 2분 정도 함께 돌려주면 촉촉하게 먹을 수 있습니다. 물이 전자레인지 안에서 수증기를 일으키고, 피자에 촉촉함을 더해줍니다.",
"date":"2020.09.09"
},
{
"idx":1,
"category":"생활",
"title":"바나나를 싱싱하게 보관하기",
"image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fbanana.png?alt=media&token=886aeb87-7ff8-4498-8674-7e4f878b8845",
"desc":"바나나에 날파리가 꼬이거나 금방 익어버리는 것을 예방하기 위한 방법인데요. 바나나 양쪽 끝을 자른 후, 보관용 케이스나 비닐봉지에 묶어 밀봉합니다. 그리고 냉장고에 넣어주면 되는데요. 하루에 1~2개씩 꺼내서 싱싱하게 먹을 수 있습니다.",
"date":"2020.09.09"
},
{
"idx":2,
"category":"생활",
"title":"셔츠에 묻은 볼펜 자국 없애기",
--> 이 idx를 통해 딕셔너리를 식별하여 특정 데이터만 가져오는 것이다.
Card.js
......
export default function Card({content,navigation}){
return(
//카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
<TouchableOpacity style={styles.card} onPress={()=>{navigation.navigate('DetailPage',{idx:content.idx})}}>
<Image style={styles.cardImage} source={{uri:content.image}}/>
<View style={styles.cardText}>
......
DetailPage.js
......
//넘어온 데이터는 route.params에 들어 있습니다.
const { idx } = route.params;
firebase_db.ref('/tip/'+idx).once('value').then((snapshot) => {
let tip = snapshot.val();
setTip(tip)
});
},[])
......
1) Card.js 에서 버튼을 클릭하면 DatailPage로 이동하면서, 해당 버튼을 이루는 딕셔너리의 idx넘버를 같이 전달한다.
2) DetailPage에서 idx넘버를 바탕으로 서버에 해당 딕셔너리 데이터를 요청하여 그 데이터로 DatailPage를 채운다.
DetailPage 코드의
firebase_db.ref('/tip/'+idx) 부분을 보면
파이어베이스 서버의 tip이라는 딕셔너리의 해당 idx넘버를 가진 딕셔너리를 선별한다는 것을 볼 수 있다.
--> 이처럼 idx번호와 리얼타임DB에 저장되는 딕셔너리의 주소 번호가 일치하기 때문에 위의 방식처럼 처리가 가능했던 것
위의 데이터들은
sparta-myhoneytip-hyeon-default-rtdb/tip/0,
sparta-myhoneytip-hyeon-default-rtdb/tip/1,
sparta-myhoneytip-hyeon-default-rtdb/tip/2
의 저장주소를 가짐
but DB주소인 sparta-myhoneytip-hyeon-default-rtdb부분은
이미 firebaseConfig.js에 딕셔너리 형태로 저장되어 있기 떄문에 굳이 써주지 않는다(상단의 코드 참고)
-->firebaseConfig.js를 담는 변수 firebase_db가 SDK(파이어베이스의 정보가 담긴 딕셔너리)를
압축하고 있음(expo도구를 사용하여) (상단 코드 참고)
--> 즉, 마스터키에 이미 정보가 있으니까 일일이 써줄 필요가 없는것
firebase_db.ref('/like/'+user_idx+'/'+ tip.idx).set(tip,function(error){
console.log(error)
Alert.alert("찜 완료!")
});
--> ref의 매개변수로 데이터를 입력할 주소를 넣음 --> DB에 미리 만들어 놓지 않더라도 저장하는 과정에서 만듬
set함수에 저장할 데이터를 매개변수로 넣음,
이후 function함수는 에러가 발생할 경우 에러코드 받는 함수인 듯 함
expo가 제공하는 expo-constants 도구
expo install expo-constants
--> 도구 받는 코드
도구 불러오기 (import)
import Constants from 'expo-constants';
해당 스마트폰을 고유한 번호로 가져오기
let user_idx = Constants.installationId
--> 위 코드는 이 코드를 실행한 스마트폰에 대해 고유한 식별번호를 리턴한다.
--> 즉, 이것 자체로 유니크한 ID로 사용할 수 있다.
--> 이는 스마트폰을 기반으로 정보를 저장할 경우(따로 로그인이 필요 없는 경우),
이 데이터를 DB에 정리하여 저장하는 것을 용이하게 한다.
--> 하지만 스마트폰에 대한 고유번호이므로 유저에 대해 데이터를 다르게 할 경우
(즉 다른 기기로도 해당 유저의 정보에 접속하는 등의 작업이 필요하다면)
따로 userData를 수집하는 것이 맞다.
--> 즉 이런 경우는 로그인 기능을 구현하는 것이 맞다.
import React,{useState,useEffect} from "react";
import {View, Image,Text,StyleSheet,ScrollView} from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler";
import Loading from "../components/Loading";
import LikeCard from "../components/LikeCard";
import Constants from "expo-constants";
import { firebase_db } from "../firebaseConfig";
export default function LikePage({navigation,route}){
const [tip, setTip] = useState([])
const [ready,setReady] = useState(true)
useEffect(()=>{
navigation.setOptions({
title:"꿀팁 찜"
})
const userIdx = Constants.installationId
firebase_db.ref('/like/'+userIdx).once('value').then((snapshot)=>{
let tip = snapshot.val();
console.log(tip)
let tip_list = Object.values(tip)
if(tip_list.length > 0){
setTip(tip_list)
setReady(false)
}
// if(tip.length > 0){
// setTip(tip);
// setReady(false)
// }
})
},[])
const reload = () =>{
const user_id = Constants.installationId;
firebase_db.ref('/like/'+user_id).once('value').then((snapshot) => {
//snapshot에 값이 있는지 없는지 체크하는 exists 함수 사용
if(snapshot.exists()){
let tip = snapshot.val();
let tip_list = Object.values(tip)
setTip(tip_list)
}else{
setReady(true)
setTip([])
}
})
}
return ready ? <Loading/> : (
<ScrollView style={styles.container}>
<View style={styles.cardContainer}>
{
tip.map((content,i)=>{
return (<LikeCard content={content} key={i} reload={reload} navigation={navigation}/>)
})
}
</View>
</ScrollView>
)
}
const styles = StyleSheet.create({
container:{
backgroundColor:"#fff"
},
cardContainer:{
marginTop:20
}
})
가져온 data의 object를 불어서 문제 해결한 부분
firebase_db.ref('/like/'+userIdx).once('value').then((snapshot)=>{
let tip = snapshot.val();
console.log(tip)
let tip_list = Object.values(tip)
if(tip_list.length > 0){
setTip(tip_list)
setReady(false)
}
// if(tip.length > 0){
// setTip(tip);
// setReady(false)
// }
})
(즉 A컴포넌트(LikeCard)가 A컴포넌트를 일부로써 사용하는 B컴포넌트(LikePage)의 다른 부분에 영향을 미칠 수 있게 함 )
reload 부분
const reload = () =>{
const user_id = Constants.installationId;
firebase_db.ref('/like/'+user_id).once('value').then((snapshot) => {
//snapshot에 값이 있는지 없는지 체크하는 exists 함수 사용
if(snapshot.exists()){
let tip = snapshot.val();
let tip_list = Object.values(tip)
setTip(tip_list)
}else{
setReady(true)
setTip([])
}
})
}
reload함수를 LikeCard의 매개변수(속성, props)에 넣어 전달한 부분
<View style={styles.cardContainer}>
{
tip.map((content,i)=>{
return (<LikeCard content={content} key={i} reload={reload} navigation={navigation}/>)
})
}
</View>
그렇게 전달받은 reload함수를 LikeCard컴포넌트가 사용하여 LikePage에 영향을 끼치는 부분 --> 아래 부분의 코드
export default function LikeCard({content,reload,navigation}){
const remove = (idx)=>{
const user_id = Constants.installationId;
firebase_db.ref('/like/'+user_id+'/'+idx).remove().then(()=>{
Alert.alert("찜 해제 완료!");
reload()
})
}
import React from 'react';
import {View, Image, Text, StyleSheet,TouchableOpacity,Alert} from 'react-native'
import Constants from 'expo-constants';
import {firebase_db} from '../firebaseConfig'
//MainPage로 부터 navigation 속성을 전달받아 Card 컴포넌트 안에서 사용
export default function LikeCard({content,reload,navigation}){
const remove = (idx)=>{
const user_id = Constants.installationId;
firebase_db.ref('/like/'+user_id+'/'+idx).remove().then(()=>{
Alert.alert("찜 해제 완료!");
reload()
})
}
return(
//카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
<View style={styles.container}>
<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>
<View style={styles.buttonContainer} >
<TouchableOpacity style={styles.detailButton} onPress={()=>navigation.navigate('DetailPage',{idx:content.idx})} >
<Text style={styles.detailText}>자세히보기</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.deleteButton} onPress={()=>remove(content.idx)} >
<Text style={styles.deleteText}>찜 해제</Text>
</TouchableOpacity>
</View>
</View>
)
}
const styles = StyleSheet.create({
container:{
flex:1
},
card:{
flex:1,
flexDirection:"row",
margin:10,
borderBottomWidth:0.5,
borderBottomColor:"#eee",
paddingBottom:10
},
cardImage: {
flex:1,
width:100,
height:100,
borderRadius:10,
},
cardText: {
flex:2,
flexDirection:"column",
marginLeft:10,
},
cardTitle: {
fontSize:20,
fontWeight:"700"
},
cardDesc: {
fontSize:15
},
cardDate: {
fontSize:10,
color:"#A6A6A6",
},
buttonContainer:{
flex:1,
flexDirection:"row",
alignSelf:"center"
},
detailButton:{
borderWidth:2,
borderColor:"#c09",
marginLeft:120,
padding:10,
width:100,
borderRadius:10
},
deleteButton:{
borderWidth:2,
borderColor:"#c09",
marginLeft:20,
padding:10,
width:100,
borderRadius:10
},
detailText:{
textAlign:"center",
color:"#c09"
},
deleteText:{
textAlign:"center",
color:"#c09"
}
});
데이터 삭제 부분
const remove = (idx)=>{
const user_id = Constants.installationId;
firebase_db.ref('/like/'+user_id+'/'+idx).remove().then(()=>{
Alert.alert("찜 해제 완료!");
reload()
})
}