1. 목적 : Likepage나 Playlist를 만들기 위해 구현하고자 한다.
2. 실험
시도 1. 실패 : 한 번 클릭하면 모든 좋아요버튼이 실행된다.
원인 : Flatlist 내 좋아요(Like=favorite) 버튼은 구현했으나, 단순 반복되버림.
때문에, 렌더링 된 것이라 각 카드(View)별로 구분이 없어 모든 return값에 실행되버리는 것.
return (
...
<FlatList
...
renderItem={({ item, index }) => (
...
<View style={styles.heartBotton}>
{favorite ?
<Pressable onPress={() => UnLike()}>
<AntDesign name="heart" size={30} color="#eb4b4b" />
</Pressable>
:
<Pressable onPress={() => Like()}>
<AntDesign name="hearto" size={30} color="#999" />
</Pressable>
</View>
...
)
시도 2. 실패 : 구분값으로 index값을 넣어 map함수로 반복함수를 돌렸으나, 한 카드 안에 무한 하트 구현...
<View style={styles.heartBotton}>
{state.map((item, idx) => {
if (setFavorite(false)) {
return (
<Pressable onPress={() => Like({ item })} key={idx}>
<AntDesign name="heart" size={30} color="#eb4b4b" />
</Pressable>)
} else {
return (
<Pressable onPress={() => UnLike({ item })} key={idx}>
<AntDesign name="hearto" size={30} color="#999" />
</Pressable>)
}
})}
시도 3. 실패 : Like와 UnLike함수를 하나로 통함시켰다. 한 함수에서 if문을 통해 두 가지 기능을 구동할 수 있도록 지정했다. 그리고 return문에 렌더링을 index값에 맞춰 돌리려 했으나 안된다.
좋아요도 수정이 안된다. 잘못된 방식이다.
function Like() {
const user_id = Constants.installationId;
if (setFavorite(false)) {
firebase_db.ref('/like/' + user_id + '/Drum/' + state.idx).set(state)
setFavorite(true);
}
else if (setFavorite(true)) {
const data_remove = firebase_db.ref(`/like/${user_id}/Drum/${state.idx}`).remove()
setFavorite(false);
}
}
return (
...
<View style={styles.heartBotton}>
<Pressable onPress={() => Like({ index })} key={index} >
{favorite ?
<AntDesign name="heart" size={30} color="#eb4b4b" />
:
<AntDesign name="hearto" size={30} color="#999" />
}
</Pressable>
</View>
시도 4. 실패 : firebase에 쓰기, 읽기, 삭제 명령어 조정해보기
이번엔 몇가지 명령어를 수정하는 방법을 시도해봤다. 그 과정에서 undefined라는 폴더도 지울 수 있었다.
ㄱ. 쓰기 조정해보기
: 우선 쓰기기능을 조정해보도록 했다. push사용시 firebase에서 자동으로 key값을 지정하기 때문에
읽는 단계에서 해당 키값에 대한 변수설정이 어렵다.
따라서 set이나 update함수로 user_id에 바로 value값을 추가하는 방식을 사용해보려 했다.
그러나 무슨짓을 해도 merge가 되지 않고 덮어쓰기만 되었다.
혹시나 해서 map함수를 통해 index를 넣는 방식을 구현해보려 했으나, 오류가 발생한다.
function Like({ item, index }) {
const user_id = Constants.installationId;
firebase_db.ref('/like/' + user_id + index).set(item)
.then(() => { Alert.alert('<찜 완료>'); })
setFavorite(true);
}
ㄴ. 읽기 조정해보기
: 그렇다면 firebase에서 지정해준 key값을 경로로 지정할 수 있도록 변수를 할당해보려 했다.
하지만, 해당 key값을 받을 변수가 없어 실패했다.
useState를 통해 hook을 이용해보거나, key, index 등 다양한 변수값을 시도해보았으나 작동하지않는다.
ㄷ. 삭제 조정해보기
: remove는 한번에 저장된 json데이터가 전부 지워지는 방식이라, 하나씩 지워질 수 있도록 경로를 지정해보려 했다.
마찬가지로 key값을 할당할 변수가 없어 실패했다.
ㄹ. 수정된 코드
: 알아보는 과정에서 child값을 지정하는 방법에 대해 알게되었다.
또한, 기존 firebase가 주는 기본 API에서 수정하는 방법도 공부하게 되었다.
이를 통해 useEffect에서 사용중인 코드를 일부 이해하게 되었다. (snapshop, once, on, Object 등)
아래 코드는 LikePage.js를 구성하기 위한 API 코드다.
아직까진 경로지정이 되지않아 return문을 통한 카드값 렌더링은 구현되지 않는다.(단, json값은 불러와진다)
useEffect(() => {
setLoading(true);
const user_id = Constants.installationId;
firebase_db
.ref("/like/" + user_id)
.once('child_added')
.then((snapshot) => {
let Likeitem = snapshot.val()
setState(Likeitem)
setTotalDataSource(Likeitem);
setLoading(false);
console.log(Likeitem)
})
// if (items_list.length > 0) {
// setState(items_list);
// }
// .catch(err => { setLoading(false); setError(err); });
}, []);
시도 5. getValue() 사용하기 : push로 임의지정되는 key값 벗겨주는 역할
ㄱ. 기존 firebase API에서 사용하던 val()를 getValue()로 바꿔주려한다.
getValue로 변경시, ()안에 공백이면 에러가 난다. 즉 인식을 못한다.
변수지정을 알아보자
firebase_db
.ref("/like/")
.once('child_added')
.then((snapshot) => {
let Likeitem = snapshot
.val()
// .getValue('child_added')
setState(Likeitem)
setTotalDataSource(Likeitem);
setLoading(false);
console.log(Likeitem)
})
시도 6. 일부 성공 : Likepage에 firebase API로 읽기방식을 수정했다.
ㄱ. 우선 ref할 최상위 경로는 그대로 좋아요한 페이지로 생성될 /like 이다.
ㄴ. once로 읽어올 json정보는 child_added로 자식노드를 받아온다.
ㄷ. then문으로 snapshot화 하여 불러올 정보를 Object.value를 사용하여 value값만 받아온다.
이를 통해, firebase내 자체생성되는 key값을 없애고 value값만 받아오는 것이다.
ㄹ. 여기서 Like_List이라는 변수로 받아온 json value값이 온전히 잘 들어왔는지 터미널로 확인한다. (딕셔너리값이 찍힘)
ㅁ. if문을 활용하여 데이터가 있을 때와 없을때의 상태값을 구분한다.
if문은 데이터가 없는 경우 (Like_List === null), Alert를 활용하여 없다는 것을 표시한다.
else문은 데이터가 하나라도 있을 경우, useState를 활용하여 Like_List으로 받아온 데이터를 할당해준다.
이후 console.log로 데이터개수를 체크해준다.
(이는 만약 if문을 Like_List.length > 1 형식으로 if조건을 추가하는 방식으로도 사용 가능하다.)
ㅂ. 단, 여기서 정보를 불러온 상태에서 새로 추가한 데이터는 표시되지 않고 있다.
once를 사용해서 그런것 같다.
(once는 하위 요소를 한번만 읽어온다. 반면 on은 바뀔때마다 읽어오지만, 현재 on 사용시 오류가 난다)
ㅅ. LikePage내에서 찜 해제를 누를 시, 삭제가 되지 않는다. 이를 해결할 필요가 있다. (경로문제인거같음)
ㅇ. like 데이터가 없으면, 안내 페이지 및 알람이 아닌 무한로딩이다.
( 참고링크 : https://kdinner.tistory.com/72 )
useEffect(() => {
setLoading(true);
firebase_db
.ref(`/like`)
.once('child_added')
.then((snapshot) => {
let Like_List = Object.values(snapshot.val())
console.log(Like_List);
if (Like_List === null) {
Alert.alert('<찜 없음>', '목록이 없습니다!')
}
else {
setState(Like_List)
setTotalDataSource(Like_List);
setLoading(false);
}
console.log(Like_List.length);
console.log(loading);
}, 1000)
.catch(err => { setLoading(false); setError(err); }, 1000);
}, []);
시도 7. 실패 : realtime database로부터 읽기 기능을 할 때, once() 메소드를 사용하여 최초 한번만 읽어오는 상태
on() 메소드를 이용하면 , 변경값이 있을 때마다 불러와진다고 하여 변경 시도.
on() 메소드의 경우, ref()뒤에 연결하여 사용하는 것은 once와 동일하나,
on('childe_added', fuction) 형식을 사용해야 했다.
그리고 function쪽에 snapshot을 넣어 사용해야 했으나 기존에 작성한 코드로는 callback 오류가 난다.
문제점을 찾기위해 코드를 간소화 하여 다시 작성해보고, on() 메소드를 적용시켜보았다.
무한 로딩상태인거 보니 경로지정을 새로해야 하나 싶다.
useEffect(() => {
setLoading(true);
const user_id = Constants.installationId;
firebase_db
.ref(`/like/${user_id}`)
.on('child_added', snapshot => {
const Like_List = (snapshot.val())
return (setState(Like_List))
})
시도 8. 실패 : on() 메소드를 사용하는 것은 성공했으나, once와 마찬가지로 실행할 때 한 번만 읽어온다...
if 조건을 아래의 코드를 붙임으로서 실행에는 성공했으나, once()메소드와 같은 기능이 발생한다.
(이때, if문이 아닌 return문을 이용하면 snapshot으로 array값은 불러와지나, 렌더링 인식이 안된다.)
on() 메소드를 이용한 코드
useEffect(() => {
setLoading(true);
firebase_db
.ref(`/like`)
.on('child_added', (snapshot) => {
const Like_List = Object.values(snapshot.val())
if (Like_List === null) {
Alert.alert('<찜 없음>', '목록이 없습니다!')
}
else {
setState(Like_List)
setTotalDataSource(Like_List);
setLoading(false);
}
console.log(Like_List.length);
console.log(loading);
})
...
}, []);
once() 메소드를 이용한 코드
useEffect(() => {
setLoading(true);
firebase_db
.ref(`/like`)
.once('child_added')
.then((snapshot) => {
let Like_List = Object.values(snapshot.val())
console.log(Like_List);
if (Like_List === null) {
Alert.alert('<찜 없음>', '목록이 없습니다!')
}
else {
setState(Like_List)
setTotalDataSource(Like_List);
setLoading(false);
}
console.log(Like_List.length);
console.log(loading);
}, 1000)
.catch(err => { setLoading(false); setError(err); }, 1000);
}, []);
시도 9. on() 메소드에서 'child_added'와 'chilc_changed'로 구현해보기
: 종전에 child_added를 사용했을 때, 컨텐츠가 추가 된 후 최초 한번만 데이터를 받아오는 상태가 되었다.
따라서 변화에 따른 값을 받아보기위해 child_change를 사용했으나,
이는 말 그대로 변화되는 로그에 대한 값을 불러온다.
즉, '변화되는 값'을 받아오는 것이지 변화된 결과값을 받아오는 것은 아니다.
다만, 기존에 있던 데이터도 함께 받아오기는 한다.
한편, 두 방법 다 remove 실행 시, 받아올 데이터가 없어 오류가 발생한다.
즉, 초음부터 child보단 value를 써야 할 것 같은데,
문제는 value사용시 realtime database에서 지정해주는 키값이 딸려온다,
useEffect(() => {
setLoading(true);
firebase_db
.ref(`/like`)
.on('child_added', (snapshot) => { // added 대신 changed를 넣으면 update되는 것과 유사한 방식이 구현되기는 하나, 온전히 update를 구현하진 못한다.
const Like_List = Object.values(snapshot.val())
if (Like_List === null) {
Alert.alert('<찜 없음>', '목록이 없습니다!')
return (<View><Text>공백</Text></View>)
//오류메세지 대신 뜨도록 View를 추가해보앗으나 먹히지 않는다.
}
else if (Like_List.length > 0) {
setState(Like_List)
setTotalDataSource(Like_List);
setLoading(false);
}
console.log(Like_List.length);
console.log(loading);
})
시도 10. SQL 조정 : ref를 child 명령어로 바꿔보기
( 참고링크 : https://cionman.tistory.com/72 )
결과적으로 실패다. 경로가 탐색이 안된다.
시도 11. 코드 단순화
일단 if문이 먹히질 않으므로 코드를 단순화 시켰다.
useEffect(() => {
setLoading(true);
firebase_db
.ref(`/like`)
.on('child_added', (snapshot) => {
const Like_List = Object.values(snapshot.val())
return (
setState(Like_List)
)
})
시도 12. 캐시문제인가 싶어서 수동 업데이트를 활용한 디버깅을 시도해보려 한다.
우선 해당 페이지에 손쉽게 업데이트를 실행시키기 위해 Pull down Refresh기능을 추가했다.
Refreshing은 정상적으로 동작하나, 데이터가 받아지질 않고, 기존 데이터만 남아있기에,
Pull Down에 반응하는 캐시삭제를 구현해해봐야겠다.
시도 12. 일부 성공 일부실패 : 우선 on() 명령어를 통한 실시간 refresh의 원인을 찾고 해결했다.
그러나, 이제 remove 명령어가 문제이다. 또한, Like데이터가 null일 경우 Object.values() 명령의 오류가 발생한다.
1) on() 명령어가 안먹힌 이유 : userId를 임의로 지정받는 것 때문에, push단계에서 지정한 userId를 reading단계에서
이미 한 번 인지 했기때문에 refresh가 되지 않는것 같다.
따라서, userId 생성 자체를 없앴고, 이를통해 realtime database에서 임의로 생성한 postId의 변화에 따른
실시간 인지가 됨으로서 refresh가 구현됬다.
## 추후에 userId항목을 넣고 싶다면, postId마다 userId를 개별적으로 할당하도록 설정해야겠다.
어차피 like라는 최상위 키값 안에 개별적으로 생성되는 것이기 때문이다.
2) remove명령어의 문제 : 경로 지정이 안된다. 각 item마다 경로가 지정되지 않는다.
3) Object.values() 문제 : like안에 데이터가 있을때는 상관없으나, null값일 경우 해당 명령어로 읽어올 데이터가
없기때문에 오류가 발생한다. 그래서 if문을 통해 null값일 경우, Alert와 대체화면을 지정해주었으나, 먹히질 않는다.
import Blank from '../data.json'
....
export default function Users() {
...
useEffect(() => {
setTimeout(() => {
setLoading(true);
const DB = firebase_db.ref(`/like/`).on('value', snapshot => (snapshot.val()));
if (DB === null) {
Alert.alert('<찜 없음>', '목록이 없습니다!')
setLoading(true);
console.log("비어있음!!")
setState(Blank)
} else {
firebase_db
.ref(`/like/`)
.on('value', (snapshot) => {
const Like_List = Object.values(snapshot.val());
setState(Like_List)
setTotalDataSource(Like_List);
setLoading(false);
console.log(Like_List);
console.log(Like_List.length);
})
}
}, 300)
}, []);
시도 13. 실패 : remove 코드 만들기
remove할 경로를 지정하기 위해 고군분투중이다.
현재, remove를 실행할 경우, like를 포함해 그 안의 모든데이터가 지워져버린다.
선택된 데이터만 지우고 싶기 때문에 코드를 새로 작성했다.
아래 코드는 실패다. (애당초 item에 대한 지정이 안됨)
function UnLike({ item }) {
const itemKey = firebase_db.ref(`/like`).on('value', (snapshot) => {
Object.keys(snapshot.val())
});
firebase_db.ref(`/like/${itemKey}`).remove()
.then(() => { Alert.alert('<찜 해제 완료>'); })
setFavorite(false);
}
아래코드도 실패다. 마찬가지로 itemfks props를 넣을 데가 없을 뿐더러 넣으면 경로를 찾질 못함.
여기서 사용한 방식은 like 아래의 key값을 변수로 할당하여 입력하는 방식응 채용했다.
문제는 해당 아이템의 키값을 찾질 못한다.
function UnLike({ item }) {
const LikeData = firebase_db.ref(`/like`)
const itemKey = Object.keys(LikeData);
firebase_db.ref(`/like/${itemKey}`).remove()
.then(() => { Alert.alert('<찜 해제 완료>'); })
setFavorite(false);
}
다시 코드를 수정해보았으나 실패
function UnLike({ item }) {
const data_remove = firebase_db.ref(`/like/${item.idx}/`).remove()
.then(() => { Alert.alert('<찜 해제 완료>'); })
// setFavorite(false);
}
다음은 전체삭제 코드이다.
function UnLike({ item }) {
const data_remove = firebase_db.ref(`/like/`)
.set(null)
.then(() => { Alert.alert('<찜 해제 완료>'); })
}
시도 14. 실패
1) like의 item.idx를 변수선언 => 이 JSON의 key값 추출 => 다시 '/like/추출한 키값'을 remove 시도. 실패.
function UnLike({ item }) {
const data_remove = firebase_db.ref(`/like/` + item.idx);
const dataKey = Object.keys(data_remove);
firebase_db.ref(`/like/${dataKey}`).remove()
.then(() => { Alert.alert('<찜 해제 완료>'); })
}
2) item.keys로 다이렉트로 연동해보았으나 경로가 맞지 않는다.
function UnLike({ item }) {
firebase_db.ref(`/like/${item.keys}`).remove()
.then(() => { Alert.alert('<찜 해제 완료>'); })
}
시도 15. realtime database에서 지정해주는임의의 키값들을 불러오는데 성공했다. 다만 특정 아이디만 지정이 안됨.
우선아래의 경로설정을 통해 임의로 받은 키값을 console.log로 받아오는데 성공했다.
즉, /like/user_id 에서 value값을 불러오면 임의의 키값부터 시작디는 밸류값이 지정된다.
이 Array데이터를 snapshot하고, 그 중 키값인 임의의 키값을 Object.keys를 이용해 추출한다.
function UnLike({ item }) {
const user_id = Constants.installationId;
let FBKey = firebase_db.ref(`/like/${user_id}`).on('value', (snapshot) => {
const keys = (snapshot.val());
let itemkeys = Object.keys(keys);
console.log(itemkeys)
})
}
그럼 다음과 같이 로그로 키값만 찍힌다. (현재 두개만 넣어놔서 두개 찍힘)
문제는 이 중 내가 선택한 컨텐츠의 변수값인 item에 맞는 키값만 선택해야 해서 remove를 실행시켜야 하는데,
문제점은 해당 키값에 매칭해줘야한다는 점과 remove를 실행시키기가 안된다는 것이다.
@@여기서 console.log로 찍어본 경로들 기준 정리
1. Unlike의 경로에서 확인되는 값
console.log(itemkeys) //realtime database에서 지정해준 임의의 키값
console.log(Object.values(state)) // Object라는 키값으로 지정 후 하위로 임의의 키값의 하위 Value값들
console.log(Object.values(item)) // item에 해당하는 data. 임의의 키값을 포함한 하나의 데이터
2. firebase를 대입하기위한 useEffect 부분에서 확인되는 값(on()메소드 안에서 console.log함)
console.log(Object.values(Like_List)) // 상단의 console.log(Object.values(state))와 동일
시도 16. 성공!!!!! 근데 에러창이 발생하긴 함.
며칠째 안되던 것의 원인을 찾고자 경로부터 파악하자는 생각이 들었다.
그래서 각각의 코드별로 경로를 파악해보자는 생각으로 consol.log 대잔치를 벌였다.
모든 코드를 console.log쳐보는 미친짓을 했다.(Object.keys와 Object.vlaues까지 다 쳐봄ㅎ)
그 결과 FlatList에선 애당초 realtime database에서 부여하는 key값들을 불러올 수가 없다.
당연한건데 노동하고 깨달았다. useState를 이용해 state라는 변수로 FlatList의 data에 넣는데,
firebase에서 가져올때 이미 Object.values를 이용해 밸류값만 받아와 렌더링했기 때문이다.
본인이 Object.valuse코드 짜놓고 인지못하고 있었다..ㅎㅎ....ㅎ...ㅎ..ㅎ.ㅎ.ㅎ.ㅎ..
그렇다면 임의의 key값을 받아올 방법은 없을까 찾아보기 시작했다.
UnLike라는 반응형 함수에서 firebase.ref.on 메소드를 이용해 겨우겨우 받아냈다. Unlike를 통해 FlatList에서 받아온
item라는 props를 병용하기 위해서였다.
다행이다 firebase.ref.on을 통해 임의의 key값은 받아졌다. 근데 모든 키값이 들어온다.
그 중 선택한 item에 대한 키값만 선택할 순 없을까 했다. 안먹힌다. item자체가 들어갈 구녕이 없다.
그렇다고 인덱스가 지정되어 있던건 아니고, 그렇다고 무한대에 가까운 데이터에 일일이 인덱스를 넣는 것은 너무 비효율적이다.
그렇게 시도와 고민만 보름이 지난듯하다.
...
그러다 문득 생각이 났다. 이것이 돈오인가...!
...
아! 그렇다면 item라는 props대신 FlatList에 index라는 props를 추가해주고, UnLike를 통해 item대신 index를 받아오자!
그리고 firebase에서 키값을 전부 받아와서, 해당 인덱스에 대응되는 키값만 추출하자!
그리고 받은 키값을 useState를 통해 Hook으로 지정해주고, 다시 ref()메소드를 통해 이어서 경로를 지정해주자!
거기에 .remove() 메소드로 지워보자!
구상은 완료했지만, 신중을 기하기 위해 경로마다 하나씩.. 하나씩 차근차근.. console.log를 찍어가며..
완성! 정상작동 한다!!(사용법은 따로 다시 정리하고자 한다)
단, 정상 작동도 하고, 데이터 변환도 바로바로 찍히는데, Uncaught Error로 index를 찾을 수 없다는 오류가 뜬다.
그것도 LikePage에서가 아닌 다른 탭의 Flatlist에서 뜬다.
Query오류라고 뜨는데, 그럴리는 없을것 같고, 자바스크립트 특성상 오류 원인은 못잡은것 같다만 오류는 오류다.
이 것만 해결하면 코드는 완성된다,
## 이는 빌드 중간에 문제가 발생했던 것 같다. reroad 후 정상 작동한다.
'React-native > 실패일지' 카테고리의 다른 글
[실패일지] 유튜브 API Search 연동하기 (0) | 2022.06.20 |
---|---|
[실패일지] iframe에서 연속재생 반복재생 구현하기 (0) | 2022.05.27 |
[실패일지] realtime database에 여러 파일 올려 관리하기 (0) | 2022.05.27 |
[실패일지] Search bar 연동 중 쿼리문제 (0) | 2022.05.24 |
[실패일지] FlatList에 iframe 적용하기 (성공완료) (0) | 2022.05.23 |