본문 바로가기

Study/React

HTTP Request

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

  1. http 통신은 전형적인 side effect 코드이고, 해당 코드 안에서 state를 업데이트 하고 있기 때문에 무한 루프에 빠질 수 있다. => useEffect를 사용한다. 
  2. 모든 비동기 함수를 처리할 때에는 에러핸들링이 필수이다. 

 

  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>
   </>
  */

 

  1. fetch API는 error status code 응답을 실제 에러로 간주하지 않는다. => ex) 서버로부터 404 코드를 받더라도 에러가 발생하지 않는다. 
  2. 코드를 추가적으로 작성하면 실제 에러로 간주하게 만들 수 있다. => setError 
  3. 비동기 작업이 성공적으로 끝나든지 아니든지 간에, 마지막에는 loading 상태를 false로 만들어야 한다.
  4. fetchMovies는 다음과 같이 호출한다.  

 

// X
useEffect(fetchMovies, []);

// O
useEffect(() => {  
  fetchMovies();
}, [fetchMovies]);
  
useEffect(() => {  
  const sendRequest = async() => {
    /*...*/
  }
  sendRequest(); 
}, []);
   
   // 콜백함수가 외부에서 정의된 fetchMovies를 호출하고 있다는 것은, 
   // 이 함수와, 함수가 사용하고 있는 데이터가 변경되었을 때, 콜백함수를 재호출해야 한다는 것을 의미한다.
   // => fetchMovies를 dependency로 전달(closure)

 

  1. useEffect의 콜백 함수가 fetchMovies 자체를 가리키면 return 키워드는 클린업 함수에서만 사용하라는 에러 메세지가 출력된다. ??? 그런데 화면에 데이터 렌더링하는 부분은 에러 없이 잘 동작하는 것 같다...?  
    • useEffect의 콜백함수는 비동기함수이면 안된다. 
    • 비동기함수는 내부적으로 promise를 반환하기 때문에, return 키워드는 클린업 함수에서만 사용해야 한다는 경고 메세지를 띄운다. 
  2. 콜백 자체는 동기함수로 두고, 그 안에서 비동기 함수인 fetchMovies를 실행하면 에러가 발생하지 않는다.
  3. 매번 fetchMovies 함수를 재생성하고 싶지 않다면, useCallback을 이용할 수 있다. 이 때 useCallback의 의존성으로 함수가 사용하고 있는 외부 데이터를 전달해 주어야 한다. 

using Database 

  1. 브라우저에서 실행되는 어플리케이션(JS codes in browser)에서는, 직접적으로(directly) DB와 통신해서는 안된다. 브라우저에서 실행되는 모든 js 코드는 웹 사이트의 사용자에게도 노출되고(chrome dev tools), 이는 곧 데이터베이스에 접근하기 위해 필요한 credential을 브라우저에 노출한다는 것을 의미하기 때문이다.
  2. 백엔드 코드는 웹 사이트의 사용자에게 노출되지 않기 때문에, 백엔드 서버에서는 데이터베이스와 직접 통신하는 것이 가능하다. 
  3. 따라서 브라우저 앱은 백엔드 서버(API, different URLs)를 통해 credential의 노출 없이 데이터를 받아온다. 
  4. 백엔드 서버에 데이터를 요청하는 방법에는 => fetch와 같은 브라우저 내장 함수 / 라이브러리(axios) 이용하기 등이 있다. 

firebase

  1. firebase의 realtime database 기능을 이용하면 실제 데이터 베이스를 이용할 수 있다.물론, 제공받는 백엔드 url(firebase API)을 통해서 이 데이터베이스와 통신한다. 
  2. 기본 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);
      });
  }

 

  1. 데이터베이스에 데이터를 추가하기 위해서, firebase 백엔드 어플리케이션에 post request를 보낸다. 
  2. fetch 함수는 이름과 달리, 데이터를 받아올 때 뿐만 아니라 보낼 때에도 사용할 수 있다. 
  3. 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