▶︎ script \ FirendlyEats.js \ FriendlyEats.prototype.data
필요한 데이터가 key : value [배열] 형태로 들어있다.
앞으로 다른 소스에서 종종 보일 'this.data' 또는 'data'는 이 data가 될 것이며,
firebase에 들어가는 'data' 또한 될 것이다.
FriendlyEats.prototype.data = {
words: [
'Bar', 'Fire', 'Grill', 'Drive Thru',
'Place', 'Best', 'Spot', 'Prime', 'Eatin'
],
cities: [
'Albuquerque', 'Arlington', 'Atlanta', 'Austin',
'Baltimore', 'Boston', 'Charlotte', 'Chicago', 'Cleveland', 'Colorado Springs',
'Columbus', 'Dallas', 'Denver', 'Detroit', 'El Paso',
'Fort Worth', 'Fresno', 'Houston', 'Indianapolis', 'Jacksonville',
'Kansas City', 'Las Vegas', 'Long Island', 'Los Angeles', 'Louisville',
'Memphis', 'Mesa', 'Miami', 'Milwaukee', 'Nashville', 'New York',
'Oakland', 'Oklahoma', 'Omaha', 'Philadelphia', 'Phoenix', 'Portland', 'Raleigh',
'Sacramento', 'San Antonio', 'San Diego', 'San Francisco', 'San Jose',
'Tucson', 'Tulsa', 'Virginia Beach', 'Washington'
],
categories: [
'Brunch', 'Burgers', 'Coffee', 'Deli', 'Dim Sum', 'Indian',
'Italian', 'Mediterranean', 'Mexican', 'Pizza', 'Ramen', 'Sushi'
],
ratings: [
{ rating: 1, text: 'Would never eat here again!' },
{ rating: 2, text: 'Not my cup of tea.' },
{ rating: 3, text: 'Exactly okay' },
{ rating: 4, text: 'Actually pretty good, would recommend!' },
{ rating: 5, text: 'This is my favorite place. Literally.' }
]
};
▶︎ script \ FirendlyEats.js \ new FriendlyEats() 객체 생성
윈도우 onload 하면 함수 발동.
윈도우.app에 새롭게 FriendlyEat 함수를? 호출? 생성? 한다.
그럼 FriendlyEats() 는 class, instance, function 뭔가겠지? 찾아본다.
window.onload = function() {
window.app = new FriendlyEats();
};
▶︎ script \ FirendlyEats.js \ function FriendlyEats() 생성
FriendlyEats()는 함수였다.
window의 location의 hostname 값이 저 값들과 동일하면
localhost에 true or false 를 담음.
this.filters = { sort : 'Rating' } 레이팅으로 소트한다...
function FriendlyEats() {
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
if(isLocalhost) {
/*
TODO: Set up local debug token
*/
}
this.filters = {
city: '',
price: '',
category: '',
sort: 'Rating'
};
this.dialogs = {};
var that = this;
that.initAppCheck();
firebase.auth().signInAnonymously().then(function() {
that.initTemplates();
that.initRouter();
that.initReviewDialog();
that.initFilterDialog();
}).catch(function(err) {
console.log(err);
});
}
this.initAppCheck(); < FriendlyEats.View.js 에 있음
ㄴ> 얜 아무 소스도 없음
초기 템플릿 생성 initTemplates() < FriendlyEats.View.js 에 있음
초기 라우터 생성 initRouter() < FriendlyEats.js 에 있음
초기 리뷰다이로그 생성 initReviewDialog() < FriendlyEats.View.js 에 있음
초기 필터다이로그 생성 initFilterDialog() < FriendlyEats.View.js 에 있음
대부분 화면을 구성하며 렌더링하는 함수라 엄청 자세히 안봐도 될 것 같지만
그 구성하면서 Data.js의 함수를 호출할테니
순차적으로 각각 찾아가서 분석해보자.
꽤 길다...
▶︎ script \ FirendlyEats.View.js \ function initTemplates() 생성
FriendlyEats.prototype.initTemplates = function() {
this.templates = {};
var that = this;
document.querySelectorAll('.template').forEach(function(el) {
that.templates[el.getAttribute('id')] = el;
});
};
일단 templates 빈 객체로 하나 만들고,
forEach로
key : value
el.id el
담고 있다.
console.log로 el(element)와 el.getAttribute('id')를 출력해본다.
화면에 뿌려주기 위해서 필요한 id들을 나타낸 듯 하다.
내려보면
restaurant-card
star-icon
item-list 등 다양한 el(element 요소들)의 id들을 확인할 수 있다.
id를 key 로 갖고
그 id가 속한 el(element)를 value로 가진다.
그것이, function initTemplates() 에서 하는 일.
▶︎ script \ FirendlyEats.js \ function initRouter() 생성
와 Navigo는 또 뭐지...
Navigo란 것을 객체생성 or 인스턴싱 or 함수 호출하면
on( { key : func() }) 형태로 담나보다. 흠..?
FriendlyEats.prototype.initRouter = function() {
this.router = new Navigo();
var that = this;
this.router
.on({
'/': function() {
that.updateQuery(that.filters);
}
})
.on({
'/setup': function() {
that.viewSetup();
}
})
.on({
'/restaurants/*': function() {
var path = that.getCleanPath(document.location.pathname);
var id = path.split('/')[2];
that.viewRestaurant(id);
}
})
.resolve();
firebase
.firestore()
.collection('restaurants')
.limit(1)
.onSnapshot(function(snapshot) {
if (snapshot.empty) {
that.router.navigate('/setup');
}
});
};
다른 파일에는 아무리 검색해도 안 뜨고
index.html 에 스크립트 추가 된 소스만 볼 수 있다.
<script src="//unpkg.com/navigo@6"></script>
그래서 뭔데 이게?
(사실 제일 좋은 건 무조건 공식 사이트나 공식 문서가 짱이지만..)
구글링 'router navigo on'
감사합니다... 한국어 velog 하나가 떴다.
참고 문헌
<JS> Router? (Navigo Router 사용법)
"Navigo Router"는 JS 환경에서 쉽게 SPA를 구현하게 도와주는 라우터 라이브러리!
그러니까
path = / 이면 updateQuery 함수 호출
path = /setup 이면 viewSetup 함수 호출
path = /restaurants/* 이면 viewRestaurant 함수 호출하도록 세팅하는 거였다.
그리고나서
firebase 특정 컬렉션에서
1개만 가져오고..
snapshot이 비어있으면
자동으로 /setup으로 가게한다?
그것이, function initRouter() 에서 하는 일. (일단 가봐)
▶︎ script \ FirendlyEats.View.js \ function initReviewDialog() 생성
초기 리뷰 대화란 뭘까..!?
index.html에서 id="dialog-add-review" 인 요소를 찾아오고
mdc 기능의 dialog 기능의 MDCDialog 기능에 쓴단다... 흠...
accept...?
[6. Cloud Firestore에 데이터 쓰기] 게시글에서 hosting하고
브라우저에서 Filter로 선택하고 'ACCEPT' 버튼으로 눌렀던
그 accept 인 것 같다.
리뷰를 작성하고 렌더링하는 함수인 듯하다.
FriendlyEats.prototype.initReviewDialog = function() {
var dialog = document.querySelector('#dialog-add-review');
this.dialogs.add_review = new mdc.dialog.MDCDialog(dialog);
var that = this;
this.dialogs.add_review.listen('MDCDialog:accept', function() {
var pathname = that.getCleanPath(document.location.pathname);
var id = pathname.split('/')[2];
that.addRating(id, {
rating: rating,
text: dialog.querySelector('#text').value,
userName: 'Anonymous (Web)',
timestamp: new Date(),
userId: firebase.auth().currentUser.uid
}).then(function() {
that.rerender();
});
});
var rating = 0;
dialog.querySelectorAll('.star-input i').forEach(function(el) {
var rate = function() {
var after = false;
rating = 0;
[].slice.call(el.parentNode.children).forEach(function(child) {
if (!after) {
rating++;
child.innerText = 'star';
} else {
child.innerText = 'star_border';
}
after = after || child.isSameNode(el);
});
};
el.addEventListener('mouseover', rate);
});
};
그러고 나서
index.html에서 class="start-input i" 인 요소들을 싹 다 긁어서 forEach 돌린다.
소스를 쭉 훑어보니 리뷰 별점(1~5개) 줄 때 화면에서 마우스오버하면
별 1개 씩을 child로 slice해서 또 foreach로 돌리는데
if( ) {
점수 ++ 하고
star(꽉찬 별)
} else {
star_border(빈 별)
}
하란 것 같다.
마우스 올리면 별점 몇개 줄까 호로록호로록 왔다 갔다 그건 듯.
그것이, function initReviewDialog() 에서 하는 일.
▶︎ script \ FirendlyEats.View.js \ function initFilterDialog() 생성
와 너무 길다. 이건 Codelab 마저 진행하다 해봐야겠다 ㅋ
아무튼 function FriendlyEats() 에서 동작하는
대표적인 화면 렌더링 함수들은
저정도로만 봤다.
그렇다면
[6. Cloud Firestore에 데이터 쓰기] 게시글에서 사용했던
addRestaurant 함수 (data)
[7. Cloud Firestore의 데이터 표시] 게시글에서 사용했던
getAllRestaurants 함수 (renderer)
getDocumentsInQuery 함수 (query, renderer)
이 Firebase에서 문서를 가져오는 함수들.
어디서 호출되고 뭔 data, renderer, query를 가져오고
가져오는 데이터 형태는 어떤 것인가?
▶︎ script \ FirendlyEats.Data.js \ function addRestaurant
script \ FirendlyEats.Mock.js \ addMockRestaurants 에서 호출된다.
VS Code에서 Mock.js 와 Data.js 소스를 반반 두고 함께 보자.
사진 좌측 Mock.js \ for문 안의
(그림 속 노랑색) var 변수들을 this.data나 랜덤 값으로 초기화하고 있다.
저 this.data들은
이 게시글 맨 처음에 봤던 FirendlyEats.js \ FriendlyEats.prototype.data들이다.
초기화하고 나선
var promise 에 addRestaurant 함수를 호출하는데
아까 세팅된 변수들을 보낸다. (그림 속 핑크색)
그러면 우측 Data.js \ addRestaurant에선
parameter 인자로 data를 받는다.
firebase의 최상위 restaurants 컬렉션을 호출하고
그 컬렉션에 data을 add한다. (이 글 맨 처음에 있던 많은 텍스트 data를 firebase에 넣는 것)
add를 수행하고 난 뒤의 결과값을 = 리턴값으로 보낸다.
그러면 좌측 Mock.js \ promise에는 add 성공 여부에 따른
리턴값 true or false 가 담겨있겠지?
보통 성공하면 true 실패하면 false 아니겠습니까
if (!promise면) {
뭔가 안됐다 그러고
} else면 {
push하라고 한다.
}
* 여기서 promise는 눈치챘겠지만
js 의 여러가지 다양한 진보된 기능 중 짱인 '비동기' 기능.
참고 문헌 (MDN 공식 문서)
Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.
async await promise 다 배웠눈데 까묵.
아무튼 비동기 기능이다. 그렇다.
이것이, function addRestaurant()이 어디서 호출되고, 어디서 어떻게 담긴 데이터를 가져와서, 일을 하는지 알았다.
▶︎ script \ FirendlyEats.Data.js \ function getAllRestaurants
script \ FirendlyEats.View.js \ getAllRestaurants 에서 2번 사용한다.
다만
FriendlyEats.prototype.viewHome = function(...
ㄴ> getAllRestaurants(parameter 인자로 넘기는 renderer)가 없다.
FriendlyEats.prototype.viewList = function(
ㄴ> getAllRestaurants(parameter 인자로 넘기는 renderer)가 있다.
java나 swift에선
함수 이름이 같더라도 인자parameter 여부, 순서에 따라서 다른 함수로 보는데
js는 잘 모르겠다. 일단 pass.
Data.js \ getAllRestuarants 에서
마지막에 getDocumentsInQuery(query, renderer)로 넘겨주긴하지만
getDocumentsInQuery 가보면
renderer로 받냐 마냐에 따라서 크게 영향을 받진 않는 듯하다?
다시 View.js로 돌아가서
renderer에 담기는 값은
{ key(remove, display, empty) : function } 이다.
화면상에 rendering하기 위해서 특정 class, id를 가진 요소들을 가져오는 듯하다.
db에서 조회하는 query는 Data.js에서 생성된다.
sql에서 굳이 대충 만들자면
SELECT * FROM restaurants ORDER BY avgRating, desc limit 50; -- desc는 예약어지만 일단 무시
뭐 이런걸까.
console.log(query)로 찍어봤지만 이 영역까지는 아직 안해도 될 것 같다.
돌아가는 Cycle만 알면 되겠지...
java의 JPA도 체이닝해서 사용하는 함수가 어떤 기능인지만 파악해도 충분..
본 함수의 마지막이 getDocumentsInQuery이니 그거까지만 살펴보고
Codelab 9번으로 간다.
▶︎ script \ FirendlyEats.Data.js \ function getDocumentsInQuery
이제서야 Codelab [7. Cloud Firestore의 데이터 표시]에 적혀있던 외계어(!)가 조금이나마 이해된다.
두 가지 주요 단계는 쿼리 생성과 스냅샷 리스너 추가입니다.
이 리스너는 쿼리와 일치하는 모든 기존 데이터에 대한 알림을 받으며 실시간으로 업데이트를 수신합니다.
...
query.onSnapshot는 쿼리 결과가 변경될 때마다 콜백을 트리거합니다.
처음에는 쿼리의 전체 결과 집합, 즉 Cloud Firestore의 전체 restaurants 컬렉션과 함께 콜백이 트리거됩니다.
그런 다음 모든 개별 문서를 renderer.display 함수에 전달합니다.
문서가 삭제되면 change.type가 removed와 같습니다. 따라서 이 경우 UI에서 레스토랑을 삭제하는 함수를 호출합니다.
FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) {
/*
TODO: Render all documents in the provided query
*/
query.onSnapshot(function(snapshot) {
if (!snapshot.size) return renderer.empty(); // "There are no restaurants"
snapshot.docChanges().forEach(function(change){
if (change.type === 'removed') {
renderer.remove(change.doc);
} else {
renderer.display(change.doc);
}
});
});
};
java나 nodejs의 SELECT query라면 return 해서 받아온 결과 row(s)를 (재가공하거나) 사용하는데
여기선 바로 콜백함수 function(change)에서
바로 renderer.display(firebase에서 가져온 문서) 보내고 있었다.
그래서 firebase 데이터가 어디 있는지 못 찾고 헤매고 있었다.
찾았으니 console.log(change.doc) 해봤는데
뭔 구조인진 모르겠지만 firebase 문서는 맞는 듯하다.
그리고 아까
var renderer =
{ key(remove, display, empty) : function } 라고 했으니
renderer.display() = 함수가 firebase 문서에서 필요한 데이터를 추출해서 화면에 렌더링한다.
display: function(doc) {
var data = doc.data();
data['.id'] = doc.id;
data['go_to_restaurant'] = function() {
that.router.navigate('/restaurants/' + doc.id);
};
var el = that.renderTemplate('restaurant-card', data);
el.querySelector('.rating').append(that.renderRating(data.avgRating));
el.querySelector('.price').append(that.renderPrice(data.price));
// Setting the id allows to locating the individual restaurant card
el.querySelector('.location-card').id = 'doc-' + doc.id;
var existingLocationCard = mainEl.querySelector('#doc-' + doc.id);
if (existingLocationCard) {
// modify
existingLocationCard.parentNode.before(el);
mainEl.querySelector('#cards').removeChild(existingLocationCard.parentNode);
} else {
// add
mainEl.querySelector('#cards').append(el);
}
},
아하. doc.data() 란 함수가 아까 보기 힘들었던 firebase 문서를 보기 좋게 data화 해준다.
그 아래는 index.html의 id, class를 가져와서 element 요소에 넣어줌.
그러면 data 선언 밑에
console.log(data)하기.
됐다. 아까 Data.js \ function(change) 에서도 data화해서 출력해보면 위와 동일하게 출력된다.
.data() 함수 최고.
이제 Codelab 9 로 넘어간다.
'Firebase - FriendlyEats' 카테고리의 다른 글
10. 색인 배포 (0) | 2024.10.11 |
---|---|
9. 데이터 정렬 및 필터링 (0) | 2024.10.11 |
8. Get() 데이터 (+ source 분석) (0) | 2024.09.27 |
7. Cloud Firestore의 데이터 표시 (0) | 2024.09.26 |
6. Cloud Firestore에 데이터 쓰기 (0) | 2024.09.26 |