React 및 GraphQL - 14를 사용하여 Reddit 클론 만들기

이 블로그 게시물은 원래 내 블로그 사이트에 게시되었으며 여기에서 찾을 수 있습니다.


middleware라는 폴더를 만들고 isAuth.ts 파일을 추가하고 블로우 코드를 넣어봅시다.

import { RedditDbContext } from "../types";
import { MiddlewareFn } from "type-graphql";

export const isAuth: MiddlewareFn<RedditDbContext> = ({ context }, next) => {
  if (!context.req.session.userId) {
    throw new Error("not authenticated");
  }
  return next();
};


그런 다음 post 리졸버에서 이 미들웨어를 사용할 수 있습니다. 이쪽@UseMiddlewaretype-graphql

@Mutation(() => Post)
@UseMiddleware(isAuth)
async createPost(
// ... remaining code



이제 프론트 엔드 앱으로 이동하여 Post 양식을 추가합니다. 페이지 폴더에 create-post.tsx 파일을 생성해 봅시다.

이 코드를 추가해 보겠습니다.

const CreatePost: React.FC<{}> = ({}) => {
  return (
    <Wrapper variant="small">
      <Formik
        initialValues={{ title: "", text: "" }}
        onSubmit={async (values) => {}}
      >
        {({ isSubmitting }) => (
          <Form>
            <InputField name="title" placeholder="title" label="Title" />
            <Box mt={4}>
              <InputField name="text" placeholder="text..." label="Body" />
            </Box>
            <Button
              isLoading={isSubmitting}
              mt={4}
              type="submit"
              colorScheme="teal"
            >
              Create Post
            </Button>
          </Form>
        )}
      </Formik>
    </Wrapper>
  );
};

export default CreatePost;


바디 필드용textarea이 있으면 더 좋습니다. InputField 구성 요소를 변경할 수 있습니다. prop라고 불리는 textarea를 받아 부울로 정의할 수 있습니다.

... // InputFieldProps
textarea?: boolean



그런 다음 InputField 구성 요소에서 확인하십시오.


let InputOrTextarea = Input
if (textarea) {
  InputOrTextarea = Textarea
}
...
<InputOrTextarea
{...props}
{...field}
id={field.name}
placeholder={props.placeholder}
/>



이제 돌연변이를 추가해야 합니다. createPost.graphql라는 파일을 만들고 아래 코드를 추가합니다. 그런 다음 실행yarn gen

mutation CreatePost($input: PostInput!) {
  createPost(input: $input){
    title
    id
    createdAt
    creatorId
    updatedAt
    text
    points
  }
}



그런 다음 useCreatePostMutation를 사용하고 게시물 생성 요청을 처리할 수 있습니다. 또한 이 게시물 작성 페이지에서 이 페이지에 NavBar를 추가해야 합니다. 이를 위한 공통 구성 요소를 만들어 보겠습니다. Layout.tsx라는 구성 요소를 만들고 이 코드 블록을 추가합니다.

import { NavBar } from "./NavBar";
import { Wrapper, WrapperVariant } from "./Wrapper";

interface LayoutProps {
  // added WrapperVariant type
  variant?: WrapperVariant;
}

export const Layout: React.FC<LayoutProps> = ({ children, variant }) => {
  return (
    <>
      <NavBar />
      <Wrapper variant={variant}>{children}</Wrapper>
    </>
  );
};


여기에서 Wrapper를 사용했기 때문입니다. 따라서 레이아웃에서 변형을 설정해야 합니다. 코드를 복제하지 않고 거기에 유형을 생성할 수 있습니다.

// Wrapper.tsx
export type WrapperVariant = "small" | "regular";
// change inside the Wrapper.tsx
interface WrapperProps {
  variant?: WrapperVariant;
}


그런 다음 변형을 사용한 모든 곳에서 교체하십시오. 그런 다음 Wrapper 페이지에서 레이아웃으로 create-post.tsx를 바꿀 수 있습니다.

<Layout variant="small">...</Layout>


이제 이 navbar를 끈적하게 만들 수 있습니다. 아래 코드를 추가하여 끈끈하게 만듭니다.

// NavBar.tsx
<Flex zIndex={1} position="sticky" top={0} bg="tomato" p={4}>
...



이제 이 시점에서 사용자가 시스템에 로그인하지 않고 게시물을 작성하면 인증되지 않은 오류가 발생합니다. onSubmit 함수에서 간단히 관리할 수 있습니다.


// create-post.tsx
onSubmit={async (values) => {
  const { error } = await creatPost({ input: values });

  if (error?.message.includes("not authenticated")) {

   router.push("/login")
  } else {
   router.push("/");
  }
}}



이 접근 방식의 문제는 사용자가 인증되었는지 여부에 대한 모든 graphql 쿼리를 확인해야 합니다. 이를 위해 전역 오류 처리기를 만들 수 있습니다. createUrqlClient.ts 에 아래 코드를 추가하고 exchanges 를 추가하십시오.

import { pipe, tap } from "wonka";
const errorExchange: Exchange = ({ forward }) => (ops$) => {
  return pipe(
    forward(ops$),
    tap(({ error }) => {
      if (error?.message.includes("not authenticated")) {
        Router.replace("/login");
      }
    })
  );
};
// ...
// below in this file
exchanges: [
  // ...
  errorExchange,
  ssrExchange,
  fetchExchange,
];


사용자가 로그인 없이 작업을 시도하면 로그인 페이지로 이동합니다.
me 쿼리를 확인하여 사용자가 로그인했는지 확인하여 이를 개선할 수 있습니다. 이를 위해 우리는 후크를 생성할 수 있습니다. utils 폴더 안에 useIsAuth.ts라는 파일을 추가하고 아래 코드를 추가합니다.

import { useRouter } from "next/router";
import { useEffect } from "react";
import { useMeQuery } from "../generated/graphql";

export const useIsAuth = () => {
  const [{ data, fetching }] = useMeQuery();
  const router = useRouter();
  useEffect(() => {
    if (!fetching && !data?.me) {
      router.replace("/login?next=" + router.pathname);
    }
  }, [fetching, data, router]);
};


여기에서 쿼리 매개변수로 router.pathname를 추가합니다. 이것은 사용자가 이전에 있었던 이전 페이지로 다시 라우팅됩니다. create-post.tsx 페이지에서 이것을 사용합시다.

const CreatePost: React.FC<{}> = ({}) => {
const router = useRouter();
useIsAuth();
// ...



그런 다음 login 페이지에서 다음 쿼리 매개변수로 로그인에 성공하면 해당 페이지로 이동합니다.

// ...
// inside the onSubmit method
else if (response.data?.login.user) {
  if (typeof router.query.next === "string") {
    router.push(router.query.next);
  } else {
    router.push("/");
  }
}



오류가 없으면 홈 페이지로 이동합니다.

// inside the onSubmit method
const { error } = await creatPost({ input: values });
if (!error) {
  router.push("/");
}


또한 토큰도 쿼리 값으로 액세스할 수 있으므로 [token].tsx 페이지에서 토큰 전달 메커니즘을 수정할 수 있습니다. 따라서 해당 코드도 변경해 보겠습니다.


const ChangePassword: NextPage<{ token: string }> = () => {
...
// previously we took token as initial prop.
// const ChangePassword: NextPage<{ token: string }> = ({ token })
// ...
// get the token from the query parameter
const response = await changePassword({
newPassword: values.newPassword,
token:
typeof router.query.token === "string" ? router.query.token : "",
});
// then remove getInitialProps
// delete below code from the file
ChangePassword.getInitialProps = ({ query }) => {
return {
token: query.token as string,
};
};


getInitialProps 를 제거했으므로 Next.JS 이 페이지를 정적 페이지로 렌더링하도록 최적화합니다.


읽어주셔서 감사합니다. 이와 관련하여 질문할 사항이 있으면 여기에 댓글을 남겨주세요. 또한 내 이해에 따라 이것을 썼습니다. 따라서 잘못된 점이 있으면 주저하지 말고 저를 수정하십시오. 정말 감사합니다.
오늘의 친구들을 위한 것입니다. 곧 봐요. 고맙습니다.

참조:

이 기사 시리즈는 . 이것은 놀라운 자습서이며 확인하는 것이 좋습니다.

기본 이미지credit

좋은 웹페이지 즐겨찾기