without using Database
loading state
function App() {
const [movies, setMovies] = useState([]);
const [loading, setLoading] = useState(false);
async function getMovies() {
setLoading(true)
const res = await fetch('https://swapi.dev/api/films')
// 에러 핸들링
const data = await res.json()
const fetchedMovies = data.results.map((item) => {
return {
id: item.episode_id,
title: item.title,
openingText: item.opening_crawl,
releaseDate: item.release_date,
};
})
setLoading(false)
setMovies(fetchedMovies)
}
return (
<>
<section>
<button onClick={getMovies}>Fetch Movies</button>
</section>
<section>
{!loading && movies.length > 0 && <MoviesList movies={movies} />}
{!loading && movies.length === 0 && <p>No movies</p>}
{loading && <p>loading...</p>}
</section>
</>
);
}
error handling & useEffect
- http 통신은 전형적인 side effect 코드이고, 해당 코드 안에서 state를 업데이트 하고 있기 때문에 무한 루프에 빠질 수 있다. => useEffect를 사용한다.
- 모든 비동기 함수를 처리할 때에는 에러핸들링이 필수이다.
const [error, setError] = useState(null);
const fetchMovies = async () => {
setLoading(true);
setError(null);
try {
const res = await fetch('https://swapi.dev/api/films');
if (!res.ok) throw new Error('something wrong'); // res.status를 확인할 수도 있다
const data = await res.json();
const movies = await data.results.map((item) => ({
id: item.episode_id,
title: item.title,
openingText: item.opening_crawl,
releaseDate: item.release_date,
}));
setMovies(movies);
} catch (e) {
setError(e.message);
}
setLoading(false);
};
/*
<>
<section>
<button>Fetch Movies</button>
</section>
<section>
{loading && !error && <p>loading...</p>}
{!loading && error && <p>Error! {error}</p>}
{!loading && movies.length > 0 && <MoviesList movies={movies} />}
</section>
</>
*/
- fetch API는 error status code 응답을 실제 에러로 간주하지 않는다. => ex) 서버로부터 404 코드를 받더라도 에러가 발생하지 않는다.
- 코드를 추가적으로 작성하면 실제 에러로 간주하게 만들 수 있다. => setError
- 비동기 작업이 성공적으로 끝나든지 아니든지 간에, 마지막에는 loading 상태를 false로 만들어야 한다.
- fetchMovies는 다음과 같이 호출한다.
// X
useEffect(fetchMovies, []);
// O
useEffect(() => {
fetchMovies();
}, [fetchMovies]);
useEffect(() => {
const sendRequest = async() => {
/*...*/
}
sendRequest();
}, []);
// 콜백함수가 외부에서 정의된 fetchMovies를 호출하고 있다는 것은,
// 이 함수와, 함수가 사용하고 있는 데이터가 변경되었을 때, 콜백함수를 재호출해야 한다는 것을 의미한다.
// => fetchMovies를 dependency로 전달(closure)
- useEffect의 콜백 함수가 fetchMovies 자체를 가리키면 return 키워드는 클린업 함수에서만 사용하라는 에러 메세지가 출력된다. ??? 그런데 화면에 데이터 렌더링하는 부분은 에러 없이 잘 동작하는 것 같다...?
- useEffect의 콜백함수는 비동기함수이면 안된다.
- 비동기함수는 내부적으로 promise를 반환하기 때문에, return 키워드는 클린업 함수에서만 사용해야 한다는 경고 메세지를 띄운다.
- 콜백 자체는 동기함수로 두고, 그 안에서 비동기 함수인 fetchMovies를 실행하면 에러가 발생하지 않는다.
- 매번 fetchMovies 함수를 재생성하고 싶지 않다면, useCallback을 이용할 수 있다. 이 때 useCallback의 의존성으로 함수가 사용하고 있는 외부 데이터를 전달해 주어야 한다.
using Database
- 브라우저에서 실행되는 어플리케이션(JS codes in browser)에서는, 직접적으로(directly) DB와 통신해서는 안된다. 브라우저에서 실행되는 모든 js 코드는 웹 사이트의 사용자에게도 노출되고(chrome dev tools), 이는 곧 데이터베이스에 접근하기 위해 필요한 credential을 브라우저에 노출한다는 것을 의미하기 때문이다.
- 백엔드 코드는 웹 사이트의 사용자에게 노출되지 않기 때문에, 백엔드 서버에서는 데이터베이스와 직접 통신하는 것이 가능하다.
- 따라서 브라우저 앱은 백엔드 서버(API, different URLs)를 통해 credential의 노출 없이 데이터를 받아온다.
- 백엔드 서버에 데이터를 요청하는 방법에는 => fetch와 같은 브라우저 내장 함수 / 라이브러리(axios) 이용하기 등이 있다.
firebase
- firebase의 realtime database 기능을 이용하면 실제 데이터 베이스를 이용할 수 있다.물론, 제공받는 백엔드 url(firebase API)을 통해서 이 데이터베이스와 통신한다.
- 기본 url/ node이름.json(확장자)를 이용해서 백엔드에 원하는 요청을 할 수 있다.
- ex) url/movies.json
- 해당 요청을 보내면, firebase(백엔드)는 데이터베이스에 "movies"라는 새로운 노드를 만들고, 이미 있는 노드라면 저장되어 있는 데이터를 받을 수 있다.
- firebase는 반드시 마지막에 json 확장자를 붙일 것을 요구한다.
// input
const titleRef = useRef('');
const openingTextRef = useRef('');
const releaseDateRef = useRef('');
function submitHandler(event) {
event.preventDefault();
const movie = {
title: titleRef.current.value,
openingText: openingTextRef.current.value,
releaseDate: releaseDateRef.current.value,
};
fetch(
// not fetch, but send data
'baseurl/movies.json',
{
method: 'POST',
body: JSON.stringify(movie), // js object => json
headers: {
'Content-Type': 'application/json',
},
}
).then(() => props.onAddMovie());
// app
function addMovieHandler() {
fetch('baseurl/movies.json') // 아무런 옵션을 전달 안하면 get 요청이 기본값이다
.then((res) => res.json())
.then((data) => {
let allMovies = [];
for (const id in data) { // data는 js object로 변환된 것임
allMovies.push({
id: data.name, // firebase에서 자동으로 만드는 id를 name으로 접근 가능
openingText: data[id].openingText,
releaseDate: data[id].releaseDate,
title: data[id].title,
});
}
setMovies(allMovies);
});
}
- 데이터베이스에 데이터를 추가하기 위해서, firebase 백엔드 어플리케이션에 post request를 보낸다.
- fetch 함수는 이름과 달리, 데이터를 받아올 때 뿐만 아니라 보낼 때에도 사용할 수 있다.
- post 요청이 정상적으로 이루어지면, 업데이트된 영화 리스트를 보여주기 위해 get request를 보낸다.
'Study > React' 카테고리의 다른 글
Validation (0) | 2022.06.27 |
---|---|
custom Hooks (0) | 2022.06.24 |
Optimization | how does react work? (0) | 2022.06.22 |
etc | Image, Icon, Input (0) | 2022.06.21 |
Rules of Hooks (0) | 2022.06.20 |