1012 TIL-U

Lilac00xx·2024년 10월 12일
post-thumbnail

Section 18 연습 프로젝트 : 음식 주문 앱에 Http 및 양식 추가

오호 배민?! (배민 아니고 그냥 응, 실습)

이번 Section 18의 음식 주문 앱 프로젝트는 프론트엔드 개발자로서 HTTP 통신과 상태 관리를 실습하며, 실무적인 경험을 쌓을 수 있는 중요한 단계였다. 특히 음식 데이터를 서버에서 페칭하고, 사용자가 장바구니에 추가하는 로직을 context와 reducer를 통해 처리하면서, 데이터 흐름과 전역 상태 관리의 중요성을 다시 한번 깨달았다.

TIL1) 음식 데이터 페칭 (GET)

음식 데이터를 페칭할 때, fetch 사용하여 서버에서 메뉴 목록을 가져옵니다.
상태로 관리하여 렌더링하고, 로딩 중에는 스피너를 보여줄 수 있습니다.

useEffect(() => {
  const fetchMeals = async () => {
    const response = await fetch('https://example.com/meals.json');
    const data = await response.json();
    setMeals(data);
  };
  fetchMeals();
}, []);

서버에서 음식 데이터를 가져오는 방식에서 fetch API와 useEffect 훅을 사용했다. 평소에는 이 과정이 매우 단순하게 느껴졌지만, 실제로 서버가 응답을 보낼 때까지 로딩 상태를 관리하는 것이 중요한 이유를 이해하게 됐다. 데이터를 성공적으로 받아오지 못할 때 발생하는 에러 처리 또한 사용자 경험에서 큰 영향을 미친다는 점이 특히 기억에 남는다. 배달 앱에서 메뉴를 로딩 중인 동안 스피너가 보이는 이유가 이런 백엔드 통신 때문이라는 점을 실감할 수 있었다.

Cart(장바구니) 컨텍스트와 Reducer로 시작하기

장바구니 기능을 위해 useReducer를 활용하여 상태와 로직을 분리합니다. context로 데이터를 전역에서 쉽게 접근할 수 있도록 한다

const cartReducer = (state, action) => {
  if (action.type === 'ADD') {
    const updatedTotalAmount = state.totalAmount + action.item.price * action.item.amount;
    return { items: updatedItems, totalAmount: updatedTotalAmount };
  }
  return defaultCartState;
};

const CartProvider = (props) => {
  const [cartState, dispatchCartAction] = useReducer(cartReducer, defaultCartState);
  return <CartContext.Provider value={{ cartState, dispatchCartAction }}>{props.children}</CartContext.Provider>;
};

장바구니 기능 구현에서는 useReducer와 context의 결합이 핵심이었다. 처음에는 단순히 상태를 useState로 관리하는 것보다 복잡하게 느껴졌지만, 다양한 컴포넌트에서 동일한 상태를 쉽게 접근하고 관리할 수 있어 매우 효율적임을 알게 됐다. useReducer를 사용해 액션에 따라 장바구니 상태를 업데이트하면서, 여러 아이템이 추가되거나 삭제될 때 데이터 흐름이 더 명확해졌다. 특히, 전역 상태 관리의 필요성을 몸소 체험하며 대규모 애플리케이션에서 필수적인 패턴임을 깨달았다.

장바구니 컴포 >contextprovider

바구니 컴포넌트에서 CartContext를 사용하여 장바구니 상태를 업데이트

const Cart = () => {
  const cartCtx = useContext(CartContext);
  return <div>{/* 장바구니 아이템 목록과 결제 버튼 */}</div>;
};

order 데이터로 post 요청 전송 (결제, order.JSON)

결제 정보를 JSON 형식으로 서버에 POST 요청으로 전송

const submitOrder = async (userData) => {
  await fetch('https://example.com/orders.json', {
    method: 'POST',
    body: JSON.stringify({
      user: userData,
      orderedItems: cartCtx.items,
    }),
  });
};

결제와 주문 데이터를 서버로 보내는 POST 요청 역시 실제 배달 앱에서 자주 사용되는 방식이다. 사용자가 결제 버튼을 누르면 그 순간 서버에 주문 정보가 전달되고, 서버가 이를 저장하고 처리하게 된다. 이 과정에서 JSON 형식의 데이터를 주고받으며, 서버 통신이 어떻게 이루어지는지 깊이 이해할 수 있었다. 실제로 작성한 코드가 서버에 데이터를 전달하고, 결과를 받는다는 점에서 서버와의 상호작용에 대한 자신감을 키울 수 있었다.

HTTP 로딩과 에러 상태

로딩 상태와 에러 처리 상태를 추가하여 사용자 경험을 개선

const [isLoading, setIsLoading] = useState(false);
const [httpError, setHttpError] = useState(null);

useEffect(() => {
  const fetchMeals = async () => {
    setIsLoading(true);
    try {
      const response = await fetch('https://example.com/meals.json');
      if (!response.ok) throw new Error('Something went wrong!');
      const data = await response.json();
      setMeals(data);
    } catch (error) {
      setHttpError(error.message);
    }
    setIsLoading(false);
  };
  fetchMeals();
}, []);

데이터를 페칭하거나 전송할 때 로딩과 에러 상태 관리는 필수적이다. 사용자가 버튼을 클릭했을 때, 무작정 화면이 멈춰버리면 큰 불편을 겪을 수 있다. 따라서 로딩 중에는 스피너나 "로딩 중" 메시지를 표시해 사용자에게 기다리는 상황임을 알리고, 문제가 발생했을 때는 명확한 에러 메시지를 통해 문제 해결 방법을 제시하는 것이 중요하다. 이 과정에서 에러 처리를 세심하게 다루면서, 실제 배달앱에서 왜 에러 메시지가 표시되는지 그 이면의 로직을 이해할 수 있었다.

** 유효성

주문 양식을 통해 사용자로부터 정보를 받아오는 부분에서는 input의 값이 올바른지 검증하는 과정을 추가했다. 이름, 주소, 전화번호 같은 필수 정보가 정확하게 입력되었는지 확인하는 유효성 검사는 사용자 경험을 크게 좌우한다. 배달앱에서 실수로 주소를 잘못 입력하면 큰 문제가 발생할 수 있으므로, 이를 미리 차단하는 유효성 검증 로직이 얼마나 중요한지 체감하게 됐다.

** 느낀점

실습을 진행하면서 실제로 내가 평소 사용하는 배달앱이 이런 식으로 작동한다는 것을 알게 되어 흥미로웠다. 특히 배달앱에서 장바구니 기능이 어떻게 구현되는지, 결제 버튼을 누르면 서버와 어떻게 데이터를 주고받는지 이해하면서, 매일 사용하는 앱을 개발자의 시각으로 보게 되었다. 이 과정에서 사용자의 편의를 위해 다양한 에러 처리와 로딩 관리를 세심하게 다루는 것이 중요하다는 것을 다시 한번 느끼며, 이러한 경험들이 향후 다른 프로젝트에서도 큰 도움이 될 것이라 확신한다.

** 앞으로

지금 하는 프로젝트에서, 결제 과정이나 장바구니와 같은 경우가 있다면, 데이터페칭 활용하고 싶다.
지금 상태관리는 React-query로 하고 있고, 이를 더 고도화? 음 딥다이브하게 사용해볼것.


ㅇㅋ 오늘은 그럼 사요나라!

profile
Challenge & Change

0개의 댓글