참고 - https://github.com/bradtraversy/vanillawebprojects
사용한 API 사이트
API 호출 개념 참고
https://www.daleseo.com/js-window-fetch/
결과 화면
코드
API 사이트에서
Lookup full meal details by id
www.themealdb.com/api/json/v1/1/lookup.php?i=52772
Lookup a single random meal
www.themealdb.com/api/json/v1/1/random.php
List all meal categories
www.themealdb.com/api/json/v1/1/categories.php
Filter by Category
www.themealdb.com/api/json/v1/1/filter.php?c=Seafood
이러한 API들을 사용했다.
차례대로 아이디에 따라 음식 보여주고
랜덤한 음식 하나를 보여주고
모든 음식 카테고리를 보여주고
카테고리에 따른 음식을 보여주는 API이다.
특정 음식을 검색하고
fetch를 통해 받아온 특정 음식의 데이터를 콘솔창에 출력해보면 이렇게 오브젝트가 나온다.
오브젝트의 value는 음식으로 이루어진 배열이고 각각의 요소는 음식의 특징들이 담긴 오브젝트이다.
여기서 음식의 이름, 이미지, 아이디 등의 값들을 가져와 innerHTML로 html에 추가해서 화면에 띄운다.
음식을 클릭했을 때 event.path를 사용해 클릭한 노드의 이벤트 흐름을 따라 ID를 찾아
그 ID에 맞는 음식 정보 화면을 띄운다.
음식 종류에 따라 띄우는 것, 랜덤한 음식 하나를 띄우는 것 모두 같은 로직으로 작성한다.
const search = document.getElementById("search"),
form = document.getElementById("form"),
random = document.getElementById("random"),
meals = document.getElementById("meals"),
result = document.getElementById("result"),
single_meal = document.getElementById("single-meal"),
categoryBtn = document.getElementById("categories"),
meals_by_category = document.getElementById("meals-by-category"),
search_result = document.getElementById("result"),
area = document.getElementById("area");
function searchMeal(e) {
e.preventDefault(); // submit 되어서 화면이 reload 되는 것을 방지
meals_by_category.innerHTML = "";
single_meal.innerHTML = "";
const searchItem = search.value;
if (searchItem.trim()) {
// 입력 내용이 있으면
fetch(`https://www.themealdb.com/api/json/v1/1/search.php?s=${searchItem}`)
//아니 대문자 S였는데 소문자로 바꾸니까 됨 뭐임
.then((response) => response.json()) // json으로 변환
// 성공 했을 경우에는 응답(response)객체를 resolve
// 대부분의 REST API들은 JSON 형태의 데이터를 응답하기 때문에 응답 객체는 JSON()메서드를 제공
// 응답 객체로부터 JSON 포멧의 응답 전문을 자바스크립트 객체로 변환하여 얻을 수 있음
.then((data) => {
console.log(data);
// data는 {meals: Array(음식의개수)} 오브젝트.
// 그 Array의 요소들은 오브젝트다.
if (data.meals === null) {
meals.innerHTML = "";
result.innerHTML = `<p class="search-result">찾는 결과가 없습니다.</p>`;
} else {
result.innerHTML = `<p class="search-result">'${searchItem}' 검색결과 ${data.meals.length}건</p>`;
const mealArray = data.meals.map(
// 음식 이미지, 이름을 넣은 새로운 array 만듬
(meal) =>
`
<div class="meal">
<img src="${meal.strMealThumb}" alt="${meal.strMeal}" />
<div class="meal-info" data-mealID="${meal.idMeal}">
<h3>${meal.strMeal}</h3>
</div>
</div>
`
);
meals.innerHTML = mealArray.join("");
// join 안하면 각각 배열의 인덱스가 0, 1, 2..로 저장이 되는데
// join을 해줘 하나의 문자열로 반환하여 html에 넣어 줌
}
});
search.value = "";
} else {
alert("찾는 음식을 입력하세요.");
}
}
function getRandomMeal() {
meals.innerHTML = "";
result.innerHTML = "";
meals_by_category.innerHTML = "";
fetch(`https://www.themealdb.com/api/json/v1/1/random.php`)
.then((response) => response.json())
.then((data) => {
//console.log(data); // data는 랜덤으로 선택된 음식의 정보가 담긴 길이가 1인 array 이다.
const meal = data.meals[0];
addMealToDOM(meal);
});
}
function addMealToDOM(meal) {
const ingredients = [];
for (let i = 1; i <= 20; i++) {
if (meal[`strIngredient${i}`]) {
// 있으면 넣어줌 (조건문 없으면 빈칸 출력됨)
ingredients.push(
`${meal[`strIngredient${i}`]} - ${meal[`strMeasure${i}`]}`
);
} else {
break;
}
}
single_meal.innerHTML = `
<div class="single-meal">
<h1>${meal.strMeal}</h1>
<img src="${meal.strMealThumb}" alt="${meal.strMeal}"/>
<div class="single-meal-info">
<p> ${meal.strCategory ? `${meal.strCategory}` : ""} /
${meal.strArea ? `${meal.strArea}` : ""}</p>
</div>
<div class="main">
<ul>
${ingredients.map((item) => `<li>${item}</li>`).join("")}
</ul>
<p>${meal.strInstructions}</p>
<p><i class="fab fa-youtube"></i> <a href=${
meal.strYoutube ? `${meal.strYoutube}` : ""
} target='_blank'>만드는 법 YOUTUBE로 보기</a></p>
</div>
</div>
`;
}
function getMealById(mealID) {
// ID에 따라 정보들을 DOM에 추가한다.
fetch(`https://www.themealdb.com/api/json/v1/1/lookup.php?i=${mealID}`)
.then((response) => response.json())
.then((data) => {
const meal = data.meals[0];
addMealToDOM(meal);
single_meal.scrollIntoView({
behavior: "smooth",
block: "start",
inline: "nearest",
});
});
}
function showCategories() {
single_meal.innerHTML = "";
meals.innerHTML = "";
search_result.innerHTML = "";
fetch(`https://www.themealdb.com/api/json/v1/1/categories.php`)
.then((res) => res.json())
.then((data) => {
meals_by_category.innerHTML = data.categories
.map(
(categories) =>
`
<div class = "mealByCategory" data-categoryName="${categories.strCategory}">
<h2>${categories.strCategory}</h2>
<img src="${categories.strCategoryThumb}" alt="${categories.strCategory}"/>
</div>
`
)
.join("");
});
}
function filterByCategory(name) {
search_result.innerHTML = `<p class="search-result">카테고리: '${name}'</p>`;
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?c=${name}`)
.then((res) => res.json())
.then((data) => {
meals_by_category.innerHTML = data.meals
.map(
(meal) =>
`
<div class="meal">
<img src="${meal.strMealThumb}" alt="${meal.strMeal}" />
<div class="meal-info" data-mealID="${meal.idMeal}">
<h3>${meal.strMeal}</h3>
</div>
</div>
`
)
.join("");
});
}
function getMealInfo(e) {
//Event.composedPath는 이벤트의 Path를 반환해준다. 이벤트가 어떤 흐름으로 도달했는지 알 수 있다.
//console.log(e.path) 했을 때
//이미지를 클릭했을 시에 [img, div.meal, div#meals.meals, body, html, document, window] 가 출력
const mealInfo = e.path.find((item) => {
//console.log(item.classList);
if (item.classList) {
return item.classList.contains("meal-info"); //contains함수라는게 없다는데??
// meal-info class가 있으면 true반환
} else {
return false;
}
});
if (mealInfo) {
const mealID = mealInfo.getAttribute("data-mealid");
getMealById(mealID);
}
}
form.addEventListener("submit", searchMeal); //form 안의 input type이 submit
random.addEventListener("click", getRandomMeal);
meals.addEventListener("click", getMealInfo);
meals_by_category.addEventListener("click", getMealInfo);
meals_by_category.addEventListener("click", (e) => {
const mealCategory = e.path.find((item) => {
if (item.classList) {
return item.classList.contains("mealByCategory");
} else {
return false;
}
});
if (mealCategory) {
const categoryName = mealCategory.getAttribute("data-categoryName");
filterByCategory(categoryName);
}
});
categoryBtn.addEventListener("click", showCategories);
노마드코더, 드림코딩에서도 잠깐 fetch를 사용해봤지만 이번에야말로 제대로 활용하여 연습할 수 있었다.
가져온 데이터가 어떤 타입이고 어떤 요소들이 있는지 잘 분석해서 적절하게 사용하는게 중요한듯 하다.
'FRONT > JAVASCRIPT' 카테고리의 다른 글
[JS] 바닐라 자바스크립트 타이핑 게임 (0) | 2021.08.09 |
---|---|
[JS] 자바스크립트 이벤트 위임( Event Delegation ) (0) | 2021.07.25 |
[JS] 바닐라 자바스크립트로 행맨 게임 만들기 (0) | 2021.07.25 |
[JS]바닐라 자바스크립트 투두리스트 (노드 추가, 삭제, 키보드 입력받기) (0) | 2021.07.24 |
[JS] 클릭하면 특정 위치로 화면 스크롤 하기: scrollIntoView (0) | 2021.07.24 |