Syw.Frontend

✅ React 기초 강좌

6단계. 보너스 챕터

B-2-과제. B-2-과제

B-2 과제 ①번: API 주소를 바꿔서 다른 데이터 불러오기


✅ 목표

  • https://jsonplaceholder.typicode.com/users 주소에서 사용자 목록을 불러오기
  • 로딩 중 / 에러 / 정상 표시 모두 구현
  • 기본 구조는 PostList와 동일

📄 UserList.jsx – 사용자 목록 컴포넌트

import { useEffect, useState } from 'react';

function UserList() {
  const [users, setUsers] = useState([]);          // 사용자 데이터
  const [loading, setLoading] = useState(true);     // 로딩 상태
  const [error, setError] = useState(null);         // 에러 상태

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then((res) => {
        if (!res.ok) throw new Error('사용자 데이터를 불러오는 데 실패했습니다');
        return res.json();
      })
      .then((data) => setUsers(data))
      .catch((err) => setError(err.message))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <p>👀 사용자 정보를 불러오는 중...</p>;
  if (error) return <p>❌ 에러: {error}</p>;

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          <strong>{user.name}</strong> ({user.email})<br />
          📍 {user.address.city}
        </li>
      ))}
    </ul>
  );
}

export default UserList;

📄 App.jsx에서 사용하기 (PostList 대신)

import UserList from './components/UserList';

function App() {
  return (
    <div style={{ padding: '2rem' }}>
      <h1>👤 사용자 목록 불러오기</h1>
      <UserList />
    </div>
  );
}

export default App;

🧪 실행 결과

항목 설명
https://jsonplaceholder.typicode.com/users 사용자 10명의 정보 가져옴
표시 내용 이름, 이메일, 도시명(city)

B-2 과제 ②번: useFetch() 커스텀 훅 만들기

✅ 목표

항목 설명
공통 로직 fetch, loading, error, data 상태 처리
재사용성 어떤 URL이든 쉽게 사용 가능
적용 예시 사용자 목록, 게시글 목록 등에서 사용 가능

📁 폴더 구조

src/
├── hooks/
│   └── useFetch.js
├── components/
│   └── UserList.jsx
│   └── PostList.jsx

📄 1. hooks/useFetch.js

import { useEffect, useState } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);      // 받아온 데이터
  const [loading, setLoading] = useState(true); // 로딩 여부
  const [error, setError] = useState(null);     // 에러 여부

  useEffect(() => {
    let ignore = false; // 언마운트 방지용 플래그

    fetch(url)
      .then((res) => {
        if (!res.ok) throw new Error('데이터 불러오기 실패');
        return res.json();
      })
      .then((data) => {
        if (!ignore) setData(data);
      })
      .catch((err) => {
        if (!ignore) setError(err.message);
      })
      .finally(() => {
        if (!ignore) setLoading(false);
      });

    return () => {
      ignore = true;
    };
  }, [url]);

  return { data, loading, error };
}

export default useFetch;

📄 2. UserList.jsx – useFetch 적용 예시

import useFetch from '../hooks/useFetch';

function UserList() {
  const { data: users, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');

  if (loading) return <p>로딩 중...</p>;
  if (error) return <p>에러 발생: {error}</p>;

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          <strong>{user.name}</strong> ({user.email})<br />
          📍 {user.address.city}
        </li>
      ))}
    </ul>
  );
}

export default UserList;

📄 3. PostList.jsx – 게시글 API에도 재사용 가능

import useFetch from '../hooks/useFetch';

function PostList() {
  const { data: posts, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts?_limit=5');

  if (loading) return <p>로딩 중...</p>;
  if (error) return <p>에러 발생: {error}</p>;

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <strong>{post.title}</strong>
          <p>{post.body}</p>
        </li>
      ))}
    </ul>
  );
}

export default PostList;

✅ 장점 요약

항목 설명
재사용성 URL만 바꾸면 여러 곳에서 동일한 fetch 처리 가능
유지보수 공통 로직이 한 곳에 집중되어 관리 편리
테스트 용이 훅 단위로 테스트 가능

GitHub - heroyooi/react-app at bs2_resolve

B-2 과제 ③번: 버튼 클릭 시 API 호출


🎯 목표

항목 설명
트리거 방식 useEffect 자동 호출 → ❌ / 버튼 클릭 시 수동 호출 → ✅
적용 대상 게시글 목록 또는 사용자 목록
구현 방식 버튼 클릭 시 fetch() 실행, 결과 표시

📁 예시 파일 구성

src/
├── components/
│   └── PostFetcher.jsx

📄 PostFetcher.jsx – 버튼 클릭으로 fetch 실행

import { useState } from 'react';

function PostFetcher() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleFetchPosts = async () => {
    setLoading(true);
    setError(null);

    try {
      const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
      if (!res.ok) throw new Error('게시글을 불러오지 못했습니다.');
      const data = await res.json();
      setPosts(data);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <h2>📰 게시글 불러오기</h2>
      <button onClick={handleFetchPosts}>게시글 가져오기</button>

      {loading && <p>로딩 중...</p>}
      {error && <p style={{ color: 'red' }}>에러: {error}</p>}

      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <strong>{post.title}</strong>
            <p>{post.body}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default PostFetcher;

📄 App.jsx에서 사용 예시

import PostFetcher from './components/PostFetcher';

function App() {
  return (
    <div style={{ padding: '2rem' }}>
      <h1>🖱 버튼 클릭으로 API 호출</h1>
      <PostFetcher />
    </div>
  );
}

export default App;

✅ 작동 흐름

  1. 초기에는 아무것도 불러오지 않음
  2. 사용자가 "게시글 가져오기" 버튼 클릭 시 fetch 실행
  3. 성공 시 목록 표시, 실패 시 에러 표시

GitHub - heroyooi/react-app at bs2_resolve2

💬 댓글

    ※ 로그인 후 댓글을 작성할 수 있습니다.