일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- coding
- Ros
- 전국국밥
- 머신러닝
- 딥러닝
- FirebaseV9
- 데이터분석
- pandas
- JavaScript
- 앱개발
- expo
- 정치인
- 리액트네이티브
- 강화학습
- Instagrame clone
- python
- selenium
- 강화학습 기초
- TeachagleMachine
- 클론코딩
- 사이드프로젝트
- 카트폴
- React
- 조코딩
- 크롤링
- App
- redux
- kaggle
- clone coding
- ReactNative
- Today
- Total
qcoding
8)[사이드프로젝트]App개발-전국국밥_realm를 사용한 list 저장 페이지 본문
1) 기본구성
-> 지역별 LIST에서 Detail 페이지로 이동한 뒤 하트 아이콘을 클릭하면 한줄평을 작성하는 모달이 뜨고, 모달을 작성하면 realm DB를 통해 휴대폰내에 저장이 되게 된다. 나만의 국밥집 List 페이지에 들어가면 저장된 list를 불러오고 해당 하트 버튼을 클릭 시 삭제가 된다.
2) 기능소개
◆ realm DB 구조 만들기 (scheme 만들기)
// scheme 구조를 만드는 것으로 이는 내가 사용할 DB를 만들어 놓는 것이다.
// primaryKey는 "_id" 로 설정하면 나중에 특정 객체를 선택할 때 primary key를 사용하여 선택할 수 있다.
import Realm from "realm";
const favoriteSchema = {
name: "favorite_kookbab",
properties: {
_id: "string",
diningCode_url: "string",
score: "int",
name: "string",
recommend_food: "string",
keyword:"string",
dong:"string",
address:"string",
lat:"int",
lon:"int",
img_url:"string",
comment:"string"
},
primaryKey: "_id",
};
◆ Apploading 시 realm DB생성
-> 내 경우 expo-apploaindg 시에 시작되는 startLoading에 realm을 생성하게 하였다.
realm의 경우 schmeVersion이 숫자로 있으며, 수정시에는 반드시 버전의 숫자가 올라가야 한다.
또한 여기서 deleteRealmIfMigrationNeeded: true, 로 하면 DB그 구조가 바뀔 시 기존의 DB가 삭제되고 새로 만든다는 의미이다. 만약 구조를 변경하거나 string되어야 되는 값을 int가 되어야한다고 위에서 스키마 변경 시 에러가 나서 동작이 되지 않을 것이다. 이때 true로 정하면 기존 DB는 삭제되고 새로운 DB가 생성되므로 에러가 뜨지 않지만 기존 DB는 다 삭제된다.
// realm 생성
const createRealm=async()=>{
const realm_favorite = await Realm.open({
path: "myrealm",
schema: [favoriteSchema],
schemaVersion: 1,
// deleteRealmIfMigrationNeeded: true,
});
setRealmFavorite(realm_favorite)
}
//Apploading이 시작될 때 실행되는 함수
const startLoading = () => {
createRealm();
myChannel();
}
if (!ready || !assets) {
return (
<>
<StatusBar style="auto" />
<AppLoading
onError={(e)=>console.log(e)}
startAsync={startLoading}
onFinish={onFinish}
/>
</>
);
}
◆ Detail 페이지에서 기존 DB에 있는 음식점이 있는 지 확인 // DB에 저장
-> 위에서 Schme에서 보면 id가 string으로 해놓은 것은 저장 시 id를 음식점이름으로 하기 위해서 였고, 코드에서도 아래의 DB저장을 살펴보면 _id : "item.name" 으로 저장을 하였다. DB내에 해당 음식점이 존재하는 지 를 확인하기 위해서는 id (음식점 이름) 으로 조회 후 해당 값이 있으면 favorite 상태를 변경한다.
* const IsFavorite=realmFavorite.objectForPrimaryKey("favorite_kookbab", item.name) 사용
// Context API를 통해 realm에서 불러옴
const {realmFavorite}= useContext(DBContext);
//기존 DB에 해당 음식점이 있으면 true , 없으면 false 이며 기본값은 false
const [favorite,setFavorite]=useState(false)
const {params:{item}} = useRoute();
useEffect(()=>{
// realm에 데이터가 들어 있는 것을 확인함
// realmFavorite.objects("favorite_kookbab")=[{},{} ...]
// DB안에 값이 있는 경우
if(realmFavorite.objects("favorite_kookbab").length!=0){
// realmFavorite를 그냥 쓰면 안되고 .object("favorite_kookbab")을 해줘야함
// 이렇게 하면 [{}.{}] 형태로 가져올 수 있다.
// 값이 있으면 favorite 값을 true로 변경
const IsFavorite=realmFavorite.objectForPrimaryKey("favorite_kookbab", item.name)
if(IsFavorite){
setFavorite(prev=>!prev)
}
}
},[])
// Favorite 버튼을 favorite 스테이트 상태에 따라서 이모티콘을 다르게 사용한다.
// Favorite button
const FavoriteButton=()=>{
return(
<TouchableOpacity
onPress={()=>
{ favorite?delteFavorite(item):toggleModal() }
}>
<FontAwesome name={favorite?"heart":"heart-o"} size={24} color="red" />
</TouchableOpacity>
)}
DB저장
// add Favorite
const addFavorite=async(item)=>{
let addData
if(!oneComment){
Alert.alert("한줄평을 입력하세요")
return;
}
// 실제 DB에 저장하는 코드이며, 구조는 Scheme와 동일하다
// 저장한 뒤에는 favorite 상태를 변경하여 UI를 변경해준다.
await realmFavorite.write(()=>{
addData=realmFavorite.create("favorite_kookbab", {
_id: item.name,
diningCode_url: item.diningCode_url,
score: item.score,
name: item.name,
recommend_food: item.recommend_food,
keyword:item.keyword,
dong:item.dong,
address:item.address,
lat:item.lat,
lon:item.lon,
img_url:item.img_url,
comment:oneComment
});
setFavorite(prev=>!prev);
toggleModal();
})
}
◆ 나만의 국밥집 List 탭 클릭 시 DB내용 불러오기
// Context API에서 realm에서 자료가져오기
const {realmFavorite:realmFavorite}= useContext(DBContext);
// name으로 가져온다.
useEffect(()=>{
const content_temp=realmFavorite.objects("favorite_kookbab");
// DB내의 score순으로 내림차순 정렬을 한다. false하면 오름차순
setContent(content_temp.sorted("score",true))
// 저장하거나 삭제할 때 마다 DB에 있는 값을 새롭게 불러오기 위하여
// eventListener를 연결하여 DB가 변경될 시 값을 계속 불러온다.
content_temp.addListener(()=>{
const content_temp=realmFavorite.objects("favorite_kookbab");
setContent(content_temp.sorted("score",true))
})
// useEffect의 return 함수는 clean up 함수로 위에서 생성한 eventLisener를
// 제거한다.
return () => {
content_temp.removeAllListeners();
};
},[])
◆ DB에서 삭제하기
// delete Favorite
// useCallback을 사용한 이유는 해당 함수를 하위 컴포넌트에 넘겨줘서 사용하기 때문에
// 리렌더링이 되는 것을 방지하기 위해서 사용하였다. 그러나 사용하지 않아도 크게
// 상관는 없는 듯 하다.
const removeFavorite=useCallback(async(item)=>{
// realm DB에서 objectForPrimaryKey 메서드를 사용하여 선택한 item을 불러오고
// delete를 통해서 삭제한다.
// 이때 objectForPrimaryKey는 "_id" 로 설정하였으므로 ,_id값과 동일하며 현재 DB에서는
// 음식점 이름이다.
await realmFavorite.write(() => {
const selectedItem = realmFavorite.objectForPrimaryKey("favorite_kookbab", item.name);
realmFavorite.delete(selectedItem);
});
},[])
◆ Favorite.js 전체코드
import React ,{useContext,useEffect,useState,useCallback}from 'react'
import { View, Text,FlatList,Dimensions } from 'react-native'
import styled from 'styled-components/native';
import { DBContext } from '../Context';
import CardComponent from '../components/CardComponent';
const TopNavigator=styled.View`
flex:1;
justify-content:center;
align-items:center;
background-color:white;
`;
const TypeBtnText=styled.Text`
color:black;
font-size:20px;
font-weight:bold;
`;
const Favorite = () => {
// realm에서 자료가져오기
const {realmFavorite:realmFavorite}= useContext(DBContext);
const [content,setContent]=useState({});
// delete Favorite
const removeFavorite=useCallback(async(item)=>{
await realmFavorite.write(() => {
const selectedItem = realmFavorite.objectForPrimaryKey("favorite_kookbab", item.name);
realmFavorite.delete(selectedItem);
});
},[])
// name으로 가져온다.
useEffect(()=>{
const content_temp=realmFavorite.objects("favorite_kookbab");
setContent(content_temp.sorted("score",true))
content_temp.addListener(()=>{
const content_temp=realmFavorite.objects("favorite_kookbab");
setContent(content_temp.sorted("score",true))
})
return () => {
content_temp.removeAllListeners();
};
},[])
// 여기서 content의 형태는 array
// [{},{},{}]
// console.log(`favorite 페이지 : ${content[2].name}`)
return (
<View style={{backgroundColor:"white",flex:1}}>
{content.length == 0?
<TopNavigator>
<TypeBtnText>리스트를 저장해주세요!</TypeBtnText>
</TopNavigator>:
<FlatList
contentContainerStyle={{paddingHorizontal:10,backgroundColor:"white"}}
data={content}
ItemSeparatorComponent={() => <View style={{ height: 15}} />}
keyExtractor={(item)=>item._id}
renderItem={({item,index})=>{
return(
<CardComponent item={item} index={index} removeFavorite={removeFavorite}></CardComponent>
)
}}
/>
}
</View>
)
}
export default Favorite
◆ CardComponent.js ( Favorite.js 하위 컴포넌트 )
--> 여기서 import * as WebBrowser from 'expo-web-browser'; 를 사용하여 해당 url 클릭 시 웹브라우저를 열어서 해당 사이트로 접속할 수 있다.
import React from 'react'
import styled from 'styled-components/native';
import { View,TouchableOpacity} from 'react-native'
import { useNavigation } from '@react-navigation/native';
import { FontAwesome } from '@expo/vector-icons';
import * as WebBrowser from 'expo-web-browser';
const CardContainer=styled.View`
height:80px;
justify-content:center;
flex-direction:row;
align-items:center;
/* border-color:black;
border-width:2px; */
`
const ColmnFirst=styled.View`
flex:1;
justify-content:center;
align-items:center;
`;
const ColmnSecond=styled.View`
flex:5;
/* border-color:black;
border-width:2px; */
`;
const RowFist=styled.View`
flex:1;
border-bottom-color:black;
border-bottom-width:2px;
justify-content:space-between;
align-items:center;
flex-direction:row;
`;
const RowSecond=styled.View`
flex:1;
justify-content:space-between;
align-items:center;
flex-direction:row;
`;
const IndexTitle=styled.Text`
font-size:30px;
font-weight:bold;
`;
const FoodName=styled.Text`
font-size:20px;
font-weight:bold;
`;
const Comment=styled.Text`
font-size:15px;
`;
const InfoBtn=styled.TouchableOpacity`
width:70px;
padding:5px 5px;
border-radius:5px;
background-color:#a29bfe;
justify-content:center;
align-items:center;
`
const InfoText=styled.Text`
color:white;
font-weight:bold;
`;
const CardComponent=({item,index,removeFavorite})=>{
// url 클릭 시 diningCode로 이동
const goDiningCodeUrl=async(diningCode_url)=>{
await WebBrowser.openBrowserAsync(diningCode_url);
};
return(
<CardContainer>
<ColmnFirst>
<IndexTitle>{index+1}</IndexTitle>
</ColmnFirst>
<ColmnSecond>
<RowFist>
<FoodName>{item.name}</FoodName>
<TouchableOpacity style={{justifyContent:"center",alignSelf:"center"}}
onPress={()=>{
removeFavorite(item);
}}>
<FontAwesome name="heart" size={24} color="red" />
</TouchableOpacity>
</RowFist>
<RowSecond>
<Comment>⭐{item.comment}</Comment>
<InfoBtn onPress={()=>{
goDiningCodeUrl(item.diningCode_url);
}}>
<InfoText>More</InfoText>
</InfoBtn>
</RowSecond>
</ColmnSecond>
</CardContainer>
)
}
export default CardComponent;
'[사이드 프로젝트]App개발 - 전국 국밥 찾기' 카테고리의 다른 글
10)[사이드프로젝트]App개발_Push Local Notification/ Splash Screen (0) | 2022.02.07 |
---|---|
9)[사이드프로젝트]App개발_전국국밥-내기할래 페이지 (0) | 2022.02.06 |
7)[사이드프로젝트]App개발-전국국밥_지역별 국밥찾기 페이지 (0) | 2022.02.06 |
6) [사이드 프로젝트]App개발-전국국밥찾기 -> Drawer Navigator (0) | 2022.02.06 |
5) ReactNative를 통한 App개발 진행 상황 (0) | 2022.01.15 |