코딩/NextJs

당근마켓 :6 Authorization

코딩쪼렙 2023. 4. 11. 12:45
728x90

프론트엔드에서 인증된 유저를 확인하여 
페이지 접근을 제한하는 비공개 , 공개페이지를 구분 할거임

  • 비공개,공개 핸들러 
  • swr fetching data


인증되지 않는 요청으로부터 핸들러 보호 
e.g) login 하지 않은 상태에서 api/user/ 접속하지 못하게 

인자가 많아지면 객체 형식으로 표현해주는것이 가독성에 좋다
`fn('GET', handler, true) -> fn({method:'GET', handler, isPrivate:true})`

withHandler.tsx
interface ConfigType {
    method: "GET" | "POST" | "DELETE";
    handler: (req: NextApiRequest, res: NextApiResponse) => void;
    isPrivate?: Boolean;
}
export default function withHandler({
    method,
    isPrivate = true,//기본값 설정 비공개인 화면이 더 많을 거라서 true로 설정 
    handler,
}: ConfigType) {
    return async function (
        req: NextApiRequest,
        res: NextApiResponse
    ): Promise<any> {
        if (req.method !== method) {
            return res.status(405).end();
        }
        if (isPrivate && !req.session.user) {
            return res.status(401).json({ ok: false, error: "plz Login" });
        }
        try {
            await handler(req, res);
        } catch (error) {
            console.log(error);
            return res.status(500).json({ error });
        }
    };
}
다른 api 
api/user/users confirm, enter, me .tsx
export default withApiSession(withHandler("GET", handler, false));
-->config type을 지정하므로 코드를 공유하는 입장에서 어떤의민지 더 잘 파악 할 수 있음 
export default withApiSession(withHandler({
    method:"GET",
    handler
}));
isPrivate를 선언해 준 이유가 접근하는 API마다 private인지 public인지 구분해주기 위함인가요?

사실 로그인 체크같은 경우에도 isPrivate 없이 if(!req.session.user){...} 로도 체크 할 수 있지만, isPrivate를 추가하여 같이 검사함으로써 해당 API에 접근할때만 로그인이 되어있는지 안되어있는지 체크하기 위해 isPrivate랑 같이 사용하는것 맞죠? isPrivate 없이 if문으로만 체크한다면 어떤 API든지 요청을 보내면 로그인 검사를 하게되어 문제가 생길 수 있기 때문에 isPrivate로 구분해주는거죠?!


백엔드
-----
프엔 
w정보를 api페이지에서 프론트페이지로 가져와보여주는것 
libs>clients> useUser.ts유저 데이터에 접근 할 수 있는 훅을 만들고 각 페이지에서 데이터 불러오는게 편함 

import { useRouter } from "next/router";
import { useEffect, useState } from "react";
export default function useUser() {
    const [user, setUser] = useState();
    const router = useRouter();
    useEffect(() => {
        fetch("/api/users/me")
            .then((response) => response.json())
            .then((data) => {
                if (!data.ok) {  //data.ok가 아닐 때  api/users/me --> withHandler 로그인된게 없을 때 
                    return router.replace("/enter");
                }
                setUser(data.profile);
            });
    }, [router]);
    return user;
}

pages.index.tsx

onst Home: NextPage = () => {
    const user = useUser(); //불러오기 
    return (
        <Layout title="" hasTabBar>
            <Head>
                <title>Home</title>
            </Head>
            <div className="flex flex-col space-y-5 divide-y">
                {[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1].map((_, i) => (
                    <Item
                        id={i}
                        key={i}
                        title="iPhone 14"


useLocation().push --> home에서 login 되어 있지 않으면 enter으로 가게되는데   home이라는 history가 남음(뒤로가기) -->필요없는 기능-->.replace로 바꾸기
https://nextjs.org/docs/api-reference/next/router#routerpush

 

해당요청을 상품페이지, 상세페이지,유저페이지에서 매번 api요청을 보내는건 좋지않으므로 cacheing이용
훅을 한번만 사용하고 메모리에저장 -->swr

 

 

 

 

 

SWR


SWR은 먼저 캐시로부터 데이터를 반환한 후, fetch 요청(재검증)을 하고, 최종적으로 최신화된 데이터를 가져오는 전략입니다. SWR을 사용하면 컴포넌트는 지속적이며 자동으로 데이터 업데이트 스트림을 받게 됩니다. 그리고 UI는 항상 빠르고 반응적입니다.
SWR은 React 프레임워크인 Next.js를 만든 동일한 팀이 만들었습니다.

npm i swr 또는
https://swr.vercel.app/ko/docs/getting-started

useSWR사용하기
1. JSON 데이터를 사용하는 일반적인 RESTful API라면 먼저 네이티브 fetch의 단순한 래퍼인 fetcher 함수를 생성해야 합니다.
ex) const fetcher = (...args) => fetch(...args).then(res => res.json())

2. 그 다음, useSWR을 import하고, 함수 컴포넌트 내에서 사용하여 시작하면 됩니다.
ex) const { data, error } = useSWR('/api/user/123', fetcher)

useSWR 옵션
const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
https://swr.vercel.app/docs/options

 

userUser.tsx

useSWR사용으로 코드가 아주가볍게 바뀜

import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((response) => response.json());//fetcher fn = fetch & return data
export default function useUser() {
    const { data, error } = useSWR("/api/users/me", fetcher); //fetcher("/api/users/me")이렇게 실행됌
    const router = useRouter();
  //         return router.replace("/enter");

    return data;
}

pages/index.ts

const Home: NextPage = () => {
    const { user, isLoading } = useUser();
    return (
 

 

기존: undefined로 시작 --> fetch해서데이터가져오기 

현재: 기존데이터로 fetch됨

 

VS react-query `https://goongoguma.github.io/2021/11/04/React-Query-vs-SWR/`

- React Query가 더 많은 기능을 제공하지만 그만큼 더 많은 용량을 차지(3배)
- 간단한 용도로 사용하기에는 유용하게 사용가능

데이터가 변경되면 자동으로 데이터를 변경해줌=  서버api 는 매번 호출

swr
stale while revalidate

 

 

"/api/users/me"는 url만이아니라
fetcher가 캐시에서 데이터를 가져올 때 사용하는 ID이다.


다시 fetch 하지 않고 캐시된 data를 로드하여 가져옴 
super_cache:{
"/api/users/me":{

 

}

api로 키가 분류되기 때문에 
같은 url이면 같은 캐시데이터를 가져오면되는구나하고 useSWR가 인지함. 
}


전체적으로 React Query가 더 좋지만 패키지 크기가 SWR의 3배가 넘기 때문에 간단한 어플리케이션 만들기에는 SWR가 좋고, 많은 커스텀 기능들이 필요하면 React Query가 사용하기 좋은거 같습니다

 


/procduct에 접근하기위해서 또 "/api/users/proudcts"쓰고 fetcher 도 계속 import해서 쓰고싶지않아


global swr configuration 

모든페이지에 적용되는 fetcher같은 것의 기본값을 지정 할 수 있는 프로바이더
Global Configuration
컨텍스트 SWRConfig는 모든 SWR 훅에 대한 Global Configuration(옵션)을 제공할 수 있습니다.
```
< SWRConfig
value={{
refreshInterval: 3000,
fetcher: (resource, init) => fetch(resource, init).then(res => res.json())
}}
>
< Dashboard />
< /SWRConfig>
```
https://swr.vercel.app/docs/global-configuration


 

 

_app.tsx

import { SWRConfig } from "swr";
import "../styles/globals.css";
import type { AppProps } from "next/app";

function MyApp({ Component, pageProps }: AppProps) {
    return (
        <SWRConfig
            value={{
                fetcher: (url: string) =>
                    fetch(url).then((response) => response.json()),
            }}
        >
            <div className="w-full max-w-xl mx-auto">
                <Component {...pageProps} />
            </div>
        </SWRConfig>
    );
}

export default MyApp;

 

useUser.tsx


export default function useUser() {
    const { data, error } = useSWR("/api/users/me"); //fetcher fn사라짐