React

 

 

https://macaronics-react-udemy-ex21-2.netlify.app/

 

 

 

 

 

 

1.App

동적라우트 설정

- 1)객체 배열 방식

import { createBrowserRouter , RouterProvider} from 'react-router-dom';
import HomePage from './pages/HomePage';
import EventsPage , {loader as eventLoader }from './pages/events/Events';
import EventDetailPage , 
{ loader as eventDetailLoader ,
  action as deleteEventAction
} from './pages/events/EventDetailPage';
import NewEventPage from './pages/events/NewEventPage';
import EditEventPage from './pages/events/EditEventPage';
import RootLayout from './pages/RootLayout';
import ErrorPage from './pages/Error';
import EventsRootLayout from './pages/events/EventsRootLayout';
import  { action as manipulateEventAction } from './components/EventForm';
import NewsletterPage , {action as newsletterAction} from './pages/newsletter/Newsletter';



// 라우트 정보를 담은 객체 배열
const router =createBrowserRouter( [
  {
    path: '/',
    element: <RootLayout />,
    errorElement: <ErrorPage/>,
    children: [
      { index: true, element: <HomePage /> },
      {
        path: 'events',      
        element: <EventsRootLayout />,
        children: [
          { 
            index: true, 
            element: <EventsPage />,        
            loader: eventLoader
          },
          { 
            path: 'new', 
            element: <NewEventPage /> ,            
            action: manipulateEventAction,
          },
          { 
            path: ':eventId',
            id:'event-detail',
            loader: eventDetailLoader,
            children:[
              {
                index: true,
                element: <EventDetailPage />,  
                action:deleteEventAction,             
              },
              {
                path: 'edit', 
                element: <EditEventPage /> ,
                action: manipulateEventAction,
              }
            ]          
          },
        
        ]
      }
      ,
      {
        path: 'newsletter',
        element: <NewsletterPage />,
        action: newsletterAction,
      },
    ]
  },
]);

function App() {
  return <RouterProvider router={router} />     ;  
}

export default App;

 

 

 

2)

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import HomePage from './pages/HomePage';
import EventsPage from './pages/events/Events';
import EventDetailPage from './pages/events/EventDetailPage';
import NewEventPage from './pages/events/NewEventPage';
import EditEventPage from './pages/events/EditEventPage';
import RootLayout from './pages/RootLayout';
import ErrorPage from './pages/Error';
import EventsRootLayout from './pages/events/EventsRootLayout'; 

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path='/' element={<RootLayout />} >
          <Route index element={<HomePage />} />
        
          <Route path="events" element={<EventsRootLayout />} >
            <Route index element={<EventsPage />} />
            <Route path="new" element={<NewEventPage />} />
            <Route path=":eventId" element={<EventDetailPage />} />
            <Route path=":eventId/edit" element={<EditEventPage />} />
          </Route>
       
        </Route>
        <Route path="*" element={<ErrorPage />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;


 

 

 

 

 

 

 

2. RootLayout

1)전체 페이지 레이아웃 설정

import React from 'react'
import MainNavigation from '../components/MainNavigation'
import { Outlet } from 'react-router-dom'

function RootLayout() {
  return (
    <>

        <MainNavigation />
        <main>
            <Outlet />
        </main>    
    </>
  )
}

export default RootLayout

 

 

 

 

2) 서브페이지 레이웃 EventsRootLayout.js

import React from 'react'
import EventsNavigation from '../../components/EventsNavigation';
import { Outlet } from 'react-router-dom';

function EventsRootLayout() {
  return (
    <>
        <EventsNavigation />
        <Outlet />
    </>
  )
}

export default EventsRootLayout

 

 

 

 

 

 

 

 

 

3.ErrorPage

import React from 'react'
import PageContent from './../components/PageContent';
import { useRouteError } from 'react-router-dom';
import MainNavigation from '../components/MainNavigation';

function ErrorPage() {
  const error =useRouteError();
  
  let title="에러 발생됨!";
  let message="에러가 발생했습니다.";

  if(error.status === 500){
    message=error.data.message;
  } 

  if(error.status === 404){
    title="404 Error";
    message='페이지를 찾을 수 없습니다.';
  } 

  return (
    <>
    <MainNavigation />
    <PageContent title={title}>
      <p>{message}</p>
    </PageContent>
    </>
  )
}

export default ErrorPage

 

 

 

 


 

 

4. Events  && EventDetailPage 

동적라우트  파라미터 받기, 뒤로가기 

 

1)Events

 

import React from "react";
import { Link } from "react-router-dom";
import classes from "./Event.module.css";

const DUMMYDATA = [
  {
    id: 1,
    title: "Event 1",
    description: "This is the first event",
    date: "2021-10-10T17:00:00.000Z",
  },
  {
    id: 2,
    title: "Event 2",
    description: "This is the second event",
    date: "2021-10-10T17:00:00.000Z",
  },

  {
    id: 3,
    title: "Event 3",
    description: "This is the third event",
    date: "2021-10-10T17:00:00.000Z",
  },
];

const EventsPage = () => {
  return (
    <div>
      <h1>EventsPage</h1>
      <p>
        <ul>
          {DUMMYDATA &&
            DUMMYDATA.map((event) => {
              return (
                <li>
                  <Link key={event.id} to={"/events/" + event.id}>
                    {event.title}
                  </Link>
                </li>
              );
            })}
        </ul>
      </p>
    </div>
  );
};

export default EventsPage;

 

 

 

 

2)EventDetailPage 

import React from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'

function EventDetailPage() {
  let {eventId} =useParams();
  const navigate=useNavigate();

  return (
    <div>      
      <h1>EventDetailPage  : {eventId} </h1>
    <button onClick={()=>navigate(-1)}>뒤로가기</button>
      <br/> <br/> <br/>
    
    <Link to=".."> 점두개로 뒤로가기</Link>

    </div>
  )
}

export default EventDetailPage

 

 

 

=========================================================================================================================


react-router-dom의 loader() 함수와 서버 사이드 렌더링(SSR)은 모두 웹 애플리케이션에서 데이터를 로드하고 
렌더링하는 방법에 대한 다른 접근 방식입니다. 


둘의 공통점과 차이점은 다음과 같습니다.

1.공통점:
둘 다 웹 애플리케이션에서 데이터를 로드하고 페이지를 렌더링하는 데 사용됩니다.
둘 다 사용자 경험을 향상시키기 위해 설계되었습니다.


2.차이점:
1)데이터 로딩 위치: SSR은 서버에서 데이터를 로드하고 HTML을 생성한 후 클라이언트에 전송합니다. 
반면에 loader() 함수는 클라이언트 측에서 데이터를 로드합니다.

2)렌더링 시점: SSR은 서버에서 페이지를 렌더링하고, 이를 클라이언트에 전송합니다. 
loader() 함수는 데이터를 로드한 후 클라이언트에서 페이지를 렌더링합니다.

loa

der() 함수가 SEO에 친화적인지 여부는 다양한 요인에 따라 달라집니다.
일반적으로, 클라이언트 측 렌더링은 검색 엔진 최적화(SEO)에 덜 유리할 수 있습니다. 
이는 검색 엔진 크롤러가 JavaScript를 실행하지 않거나 제한적으로 실행하기 때문입니다.
그러나 최근에는 많은 검색 엔진들이 JavaScript를 더 잘 처리하고 있으며, 
이로 인해 클라이언트 측 렌더링의 SEO 문제가 점점 줄어들고 있습니다.

 

그러나, 웹에서 중요한것이 seo 검색엔진 적용이다. 
따라서, nextjs 프레임워크를 통해서만 ssr 적용이 가능하므로
react-router-dom 버전 6 이상 나오는 다음과 같은  loader() , action(), useFetcher(), defer()  등의 함수들은 
그냥 이런것이 있다 정도 만 생각하면 될것 같다.

 

=========================================================================================================================
 

 

 

 

 

 

5. loader() 함수사용하기

 

react-router-dom 버전 6 이상에서 loader() 함수는 서버에서 데이터를 가져온 후 컴포넌트를 렌더링할 수 있게 도와주는 기능입니다.
다음은 loader() 함수의 사용 방법에 대한 간략한 설명입니다.


1.데이터를 가져오는 페이지(컴포넌트)의 route definition에 loader property를 추가합니다.

{
  path: 'url',
  element: <컴포넌트 />,
  loader: () => {
    // 데이터 가져오기
    return data;
  }
}

 

 loader는 함수를 값으로 가집니다.
 loader의 return 값은 element 컴포넌트와 해당 컴포넌트를 필요로 하는 모든 컴포넌트들(children)에 전달됩니다.

 

2.원하는 컴포넌트에서 loader에서 return한 data를 사용합니다.
 

const data명 = useLoaderData();

 

아래는 loader() 함수를 사용하는 예시입니다.

import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import RootLayout from './pages/Root';
import Homepage from './pages/Home';
import EventsPage, { loader as eventsLoader } from './pages/Events';
import EventDetailPage from './pages/EventDetail';
import EditEventPage from './pages/EditEvent';
import NewEventPage from './pages/NewEvent';
import EventsRootLayout from './pages/EventsRoot';

const router = createBrowserRouter([
  {
    path: '/',
    element: <RootLayout />,
    children: [
      { index: true, element: <Homepage /> },
      {
        path: 'events',
        element: <EventsRootLayout />,
        children: [
          { index: true, element: <EventsPage />, loader: eventsLoader },
          { path: ':eventId', element: <EventDetailPage /> },
          { path: 'new', element: <NewEventPage /> },
          { path: ':eventId/edit', element: <EditEventPage /> },
        ],
      },
    ],
  },
]);

function App() {
  return <RouterProvider router={router} />;
}

export default App;

 

그리고 EventsPage 컴포넌트에서 useLoaderData()를 사용하여 loader에서 return한 데이터를 사용하는 예시입니다.

 

import React from 'react';
import EventsList from '../components/EventsList';
import { useLoaderData } from 'react-router-dom';
import { getEvents } from '../plugins/eventAxios';

function EventsPage() {
  const events = useLoaderData();
  return <>{<EventsList events={events} />}</>;
}

export default EventsPage;

export async function loader() {
  const data = await getEvents();
  return data.result;
}

 

이렇게 loader() 함수를 사용하면 비동기 데이터를 더 효과적으로 처리할 수 있습니다. 
이 기능을 활용하면 사용자 경험을 향상시키는 데 도움이 될 것입니다.

 

 

 

 

 

6. loader() 함수사용하기

 

react-router-dom 버전 6에서는 json() 유틸리티 함수를 사용하여 데이터를 쉽게 처리할 수 있습니다.
이 함수는 주로 loader에서 사용되며, 컴포넌트가 렌더링 될 때 자동으로 response.json()을 호출하므로, 컴포넌트에서 데이터를 파싱할 필요가 없습니다.
 

json() 함수 사용법

import { json } from "react-router-dom";

const loader = async () => {
  const data = getSomeData();
  return json(data);
};

 

 

위의 코드에서 getSomeData()는 데이터를 가져오는 함수를 나타냅니다. 
이 함수는 실제 애플리케이션에서 필요한 데이터를 가져오는 함수로 대체해야 합니다. 
이렇게 하면 loader 함수는 json(data)를 반환하게 되며, 이는 react-router-dom이 자동으로 response.json()을 호출하게 됩니다.

 

async function loadEvents() {
  const response = await fetch("http://localhost:8080/events");
  if (!response.ok) {
    // throw new Response(JSON.stringify({message:'이벤트를 가져올 수 없습니다.'}),
    //   { status:505}
    // );
    return json({ message: "데이터를 가져올 수 없습니다." }, { status: 500 });
  } else {
    // 다음과 같이 response 로 직접 반환 처리를 해도 다음과정이 생략되어 전달된다. 그러나, defer 사용시에는 원래데로 작성해 야 한다.
    // const resData = await response.json();
    // return {events: resData.events, isError: false};
    const resData = await response.json();
    return resData.events;
  }
}

 

 

 

 

7. useRouteLoaderData 사용하기

react-router-dom 버전 6에서는 useRouteLoaderData 훅을 사용하여 현재 렌더링된 라우트의 데이터를 트리의 어디에서나 사용할 수 있습니다

import { useRouteLoaderData } from "react-router-dom";

function SomeComp() {
  const user = useRouteLoaderData("root");
  // ...
}

위의 코드에서 root는 라우트의 ID를 나타냅니다. 
이 ID는 라우트를 생성할 때 정의됩니다. 
이렇게 하면 useRouteLoaderData 훅은 root 라우트의 데이터를 반환하게 됩니다1. 이 데이터는 앱의 어디에서나 사용할 수 있습니다.


라우트 객체를 생성할 때 id 속성을 사용하여 라우트에 ID를 할당할 수 있습니다. 
이 ID는 문자열이어야 하며, 라우트를 고유하게 식별하는 데 사용됩니다. 
이 ID는 useRouteLoaderData 훅에서 사용되어 특정 라우트의 데이터에 액세스할 수 있게 합니다.

다음은 라우트에 ID를 할당하는 예시입니다.

const router = createBrowserRouter([
  {
    id: "root", // 라우트 ID 설정
    element: <Team />,
    path: "/teams/:teamId",
    loader: async ({ request, params }) => {
      return fetch(`/fake/api/teams/${params.teamId}.json`, {
        signal: request.signal,
      });
    },
    action: async ({ request }) => {
      return updateFakeTeam(await request.formData());
    },
    errorElement: <ErrorBoundary />,
  },
]);

 

위의 코드에서 root는 라우트의 ID를 나타냅니다. 
이 ID는 라우트를 생성할 때 정의됩니다. 
이렇게 하면 useRouteLoaderData 훅은 root 라우트의 데이터를 반환하게 됩니다. 
이 데이터는 앱의 어디에서나 사용할 수 있습니다.

 

 

 

 

 

8. action() 사용하기

 

react-router-dom 버전 6에서 action() 함수는 라우트 로더의 "읽기"에 대한 "쓰기"를 제공합니다. 
이 함수는 앱이 간단한 HTML 및 HTTP 의미론을 사용하여 데이터 변형을 수행할 수 있게 해주며, React Router는 비동기 UI와 재검증의 복잡성을 추상화합니다.

action() 함수는 앱이 라우트로 비-GET 제출(“post”, “put”, “patch”, “delete”)을 보낼 때마다 호출됩니다. 
이는 몇 가지 방법으로 발생할 수 있습니다.

// 폼
<Form method="post" action="/songs" />;
<fetcher.Form method="put" action="/songs/123/edit" />;

// 명령형 제출
let submit = useSubmit();
submit(data, { method: "delete", action: "/songs/123" });
fetcher.submit(data, { method: "patch", action: "/songs/123/edit" });

 


action() 함수는 라우트로 보내는 Fetch Request 인스턴스를 받습니다. 
가장 일반적인 사용 사례는 요청에서 FormData를 파싱하는 것입니다.

 

<Route action={async ({ request }) => {
  let formData = await request.formData();
  // ...
}} />

 

action() 함수에서는 웹 Response를 반환할 수도 있습니다.
useActionData에서 액세스할 수 있습니다.

 

EventForm.js

import { Form, useNavigate, useNavigation , useActionData, json, redirect } from 'react-router-dom';

import classes from './EventForm.module.css';

function EventForm({ method, event }) {
   /**
   *1.useActionData 사용자 입력 검증하고 검증 오류 출력하기
   * 
   *1)폼 검증은 백엔드에서 실행한다.
   *2)백엔드에서  반환상태값 예를 들어 422 status 로 반환처리한다.
   *3)action 에서  다음과 같이 422 응답처리를 받으면 리턴한다.
   *  if(response.status===422){
   *   return response;
   *  } 
   *4)useActionData 에서 감지하여 백엔드에서 받은 오류데이터를 받는다
   *
   */

   const checkeData=useActionData();
   const navigate = useNavigate();
   const navigation=useNavigation();

  //2.useNavigation
  // react-router-dom 의 navigation 에서 실시간 상태값을 반환된다.
  //따라서 다음과 같이 이용할수 있다.
  const isSubmitting=navigation.state==='submitting';
  function cancelHandler() {
    navigate('..');
  }


 /**
  * 3.action
  * action='/any-other-path'
  * action 값이 없다면 자동으로 해당 경로 /events/new 의 action 에서 데이터를 처리하게 된다.
  */


  return (
    <Form  method={method}    className={classes.form}>
      {/* 1.useActionData 사용자 입력 검증하고 검증 오류 출력하기 */}
      {checkeData&& checkeData.errors && 
        <ul>
          {Object.values(checkeData.errors).map(err=><li key={err}>{err}</li>)}
        </ul>      
      }
      <p>
        <label htmlFor="title">제목</label>
        <input id="title" type="text" name="title" required 
              defaultValue={event ? event.title : ''}
         />
      </p>
      <p>
        <label htmlFor="image">이미지 주소</label>
        <input id="image" type="url" name="image" required   defaultValue={event ? event.image : ''}  />
      </p>
      <p>
        <label htmlFor="date">날짜</label>
        <input id="date" type="date" name="date" required  defaultValue={event ? event.date : ''}    />
      </p>
      <p>
        <label htmlFor="description">내용</label>
        <textarea id="description" name="description" rows="5" required  defaultValue={event ? event.description : ''}  />
      </p>
      <div className={classes.actions}>
        <button type="button" onClick={cancelHandler} disabled={isSubmitting} >
          취소
        </button>
        <button disabled={isSubmitting}>{isSubmitting ? '전송중...' :'저장'}</button>
      </div>
    </Form>
  );
}

export default EventForm;



export async function action({request, params}){
  const method = request.method;
  
  const data =await request.formData();

  const eventData={
    title:data.get('title'),
    image:data.get('image'),
    date:data.get('date'),
    description:data.get('description'),
  }


  let url ="http://localhost:8080/events";
  //업데이트 할경우
  if(method.toUpperCase()==="PATCH"){
    const eventId=params.eventId;
    console.log("업데이트 처리 아이디 :",  eventId);
    url="http://localhost:8080/events/"+eventId;
  }

 const response= await fetch(url, {
    method:method,
    headers:{
      'Content-Type':'application/json'
    },
    body:JSON.stringify(eventData)  
  });

  //사용자 입력 검증하고 검증 오류 출력하기
  if(response.status===422){
    return response;
  }
  
  if(!response.ok){
    throw json({message :'이벤트를 저장할 수 없습니다.'}, {status:500});
  }

  return redirect('/events');

}

 

 

 

 

 

 

 

9. useFetcher() 사용하기

 


react-router-dom 버전 6에서 useFetcher() 훅은 데이터 변형과 로드를 모델링하는 데 사용됩니다. 
이 훅은 네비게이션 외부에서 로더를 호출하거나, URL을 변경하지 않고 액션을 호출하고 페이지의 
데이터를 재검증하거나, 동시에 여러 변형을 비행 상태로 유지하는 등의 작업을 수행할 때 유용합니다.

 

import { useFetcher } from "react-router-dom";

function SomeComponent() {
  const fetcher = useFetcher();

  // call submit or load in a useEffect
  React.useEffect(() => {
    fetcher.submit(data, options);
    fetcher.load(href);
  }, [fetcher]);

  // build your UI with these properties
  fetcher.state;
  fetcher.formData;
  fetcher.json;
  fetcher.text;
  fetcher.formMethod;
  fetcher.formAction;
  fetcher.data;

  // render a form that doesn't cause navigation
  return <fetcher.Form />;
}

 

위의 코드에서 data와 options는 submit() 함수에 전달되는 데이터와 옵션을 나타내며, href는 load() 함수에 전달되는 URL을 나타냅니다1fetcher 객체는 다양한 속성을 가지고 있으며, 이를 사용하여 UI를 구성할 수 있습니다1fetcher.Form 컴포넌트를 사용하면 네비게이션을 발생시키지 않는 폼을 렌더링할 수 있습니다
 

 

NewsletterSignup

import { useFetcher } from 'react-router-dom';
import classes from './NewsletterSignup.module.css';
import { useEffect } from 'react';

function NewsletterSignup() {
  /**
   * https://reactrouter.com/en/main/hooks/use-fetcher
   * useFetcher\
   *1.공통되게 처리
   *2.다른 페이지로 이동되지 않게 처리
    3.loader 나 액션이 속한 페이지 또는 라우트를 로딩하지 않고, 그것을 트리거하고 싶을 때 사용해야 한다.

    useFetcher는 React Router에서 제공하는 훅 중 하나로, 
    UI를 액션과 로더에 연결할 수 있게 해주는 기능입니다.
    이 훅은 네비게이션 없이 로더를 호출하거나, URL을 변경하지 않고 액션을 호출하고 페이지의 데이터를 재검증하고 싶을 때 유용합니다.
    또한, 여러 개의 변형을 동시에 처리해야 하는 경우에도 사용할 수 있습니다.

    useFetcher를 사용하면, 다음과 같은 작업을 수행할 수 있습니다.

    UI 경로와 관련이 없는 데이터를 가져오기 (팝오버, 동적 폼 등)
    네비게이션 없이 액션에 데이터를 제출하기 (뉴스레터 가입과 같은 공유 컴포넌트)
    목록에서 여러 개의 제출을 동시에 처리하기 (여러 버튼을 클릭하고 모두 동시에 대기 상태가 되어야 하는 “할 일” 앱 목록)
    무한 스크롤 컨테이너 등
   */
  const fetcher=useFetcher();
  const {data, state}=fetcher;

  useEffect(()=>{
    //console.log("useFetcher  data  :", data);
    if(state==='idle' && data && data.message) {
        window.alert(data.message);
    }
  }, [data, state]);



  return (
    <fetcher.Form  
      method="post"
      action='/newsletter'
      className={classes.newsletter}>
      <input
        type="email"
        placeholder="뉴스레터를 신청하세요..."
        aria-label="뉴스레터 신청"
      />
      <button>가입하기</button>
    </fetcher.Form>
  );
}

export default NewsletterSignup;

 

 

 

 

 

10. defer() 사용하기

react-router-dom 버전 6에서 defer() 함수는 데이터를 로드하는 동안 페이지를 즉시 렌더링하도록 합니다. 
이는 라우트 로더에서 데이터를 가져오는 데 시간이 오래 걸리는 경우에 유용합니다12. defer() 함수를 사용하면 페이지가 Response 객체가 반환되기 전에 이미 로드되므로, 
응답이 fetch(url)을 반환한 것처럼 자동으로 처리되지 않습니다.

다음은 defer() 함수를 사용하는 방법에 대한 예시입니다

import { Await, defer, useLoaderData } from "react-router-dom";
import { getPackageLocation } from "./api/packages";

async function loader({ params }) {
  const packageLocationPromise = getPackageLocation(params.packageId);
  return defer({
    packageLocation: packageLocationPromise,
  });
}

function PackageRoute() {
  const data = useLoaderData();
  const { packageLocation } = data;

  return (
    <main>
      <h1>Let's locate your package</h1>
      <p>
        Your package is at {packageLocation.latitude} lat and{" "}
        {packageLocation.longitude} long.
      </p>
    </main>
  );
}

 

 


위의 코드에서 getPackageLocation()는 패키지 위치를 가져오는 함수를 나타냅니다. 
이 함수는 실제 애플리케이션에서 필요한 데이터를 가져오는 함수로 대체해야 합니다. 
defer() 함수를 사용하면 loader 함수는 defer({ packageLocation: packageLocationPromise })를 반환하게 되며, 
이는 react-router-dom이 페이지를 즉시 렌더링하게 됩니다12. 이렇게 하면 컴포넌트에서 데이터를 파싱할 필요가 없어집니다

 

 

EventDetailPage.jsx

import React, { Suspense } from 'react'
import { json,  useRouteLoaderData, redirect, defer, Await } from 'react-router-dom'
import EventItem from './../../components/EventItem';
import EventsList from '../../components/EventsList';

function EventDetailPage() {
  const {event, events}=useRouteLoaderData('event-detail');
  //const data=useLoaderData();
  console.log("events  :", event);

  return (
    <>
     <Suspense fallback={<p className='center'>로딩중...</p> } > 
        <Await resolve={event}>
            { (loadEvent) =><EventItem event={loadEvent} />}
        </Await>
     </Suspense>

     <Suspense fallback={<p className='center'>로딩중...</p> } > 
        <Await resolve={events}>
        {(loadEvents) => <EventsList events={loadEvents} />}
        </Await>
    </Suspense>
    </>   
  )
}

export default EventDetailPage;



async function loadEvents() {
  const response = await fetch("http://localhost:8080/events");
  if (!response.ok) {

    return json({ message: "데이터를 가져올 수 없습니다." }, { status: 500 });
  } else {    

    const resData = await response.json();
    return resData.events;
  }
}



async function loadEvent(params) {
  const response=await fetch('http://localhost:8080/events/' +params.eventId);

  if(!response.ok){     
    return json({message:'이벤트 상세 데이터를 가져올 수 없습니다.'}, {status:500});
  }

  const resData = await response.json();
  return resData.event;
}


//로더
export async function loader ( {request, params}  ){    
  return defer({
    event: await loadEvent(params),
    events: loadEvents(),  
  });
}


export async function action({request, params}){
    const eventId=params.eventId;
    const response=await fetch('http://localhost:8080/events/'+eventId ,{
        method:request.method,
        headers:{
          'Content-Type':'application/json'
        }
    });

    if(!response.ok){ 
       throw json({message:'이벤트 삭제에 실패 했습니다'}, {status:500});
    }

    return redirect('/events');
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1번 기본 소스를 참조로 해서 2번 소스를 볼것

소스 

1) https://github.dev/braverokmc79/macaronics-react-udemy-ex21-1

2) https://github.dev/braverokmc79/macaronics-react-udemy-ex21-2

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

사랑하는 여자와 갈등이 생겼을 때 여자를 이치로 따져 설득할 수는 없다. 남자가 위로해 주면 된다. 침묵을 지키면 된다. 참으면 된다. -모로아

댓글 ( 0)

댓글 남기기

작성