Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

Joon's Space

[React] authentication front-end (log-in 페이지 ) (1) 본문

Web/React

[React] authentication front-end (log-in 페이지 ) (1)

Happy Joon 2021. 8. 1. 14:27

Local State ? 

local state 는 local storage ( HTML5에서 브라우저에 추가된 저장소로, 브라우저가 종료되어도 그대로 남아있음. ) 에 state 값을 저장해서 사용 할 수 있는 것.

 

LogIn & LogOut Authentication 

// Apollo.ts

import { ApolloClient, InMemoryCache } from "@apollo/client";

export const client = new ApolloClient({
    uri: 'http://localhost:4000/graphql',
    cache: new InMemoryCache({
        typePolicies: {
            Query: {
                fields: {
                    isLoggedIn: {
                        read() {
                            return false;
                        },
                    },
                },
            },
        },
    }),
});

Apollo.ts 부분에서 cache 에서 다음과 같이 Query 를 작성해주고, 

 

// App.tsx

import React from 'react';
import { LoggedOutRouter } from './routers/logged-out-router';
import { gql, useQuery } from '@apollo/client';

const IS_LOGGED_IN = gql`
  query isLoggedIn {
    isLoggedIn @client
  }
`

function App() {
  const { data } = useQuery(IS_LOGGED_IN);
  console.log(data);
  return <LoggedOutRouter />;
}

export default App;

IS_LOGGED_IN 에서 query를 client cache에 요청하여 (요청할 field 뒤에 @client를 붙인다.), useQuery를 이용하여 값을 { data } 에 저장하여 콘솔에 출력한다.

 

 

 

작성한것 과 같이 false가 나온 것 을 확인한다. 

 

이제 isLoggedIn 필드 값이 true, false 인지에 따라 LoggedOutRouter, LoggedInRouter를 조건에 맞게 리턴해줄 수 있게 해준다. 

function App() {
  const { 
    data: { isLoggedIn } 
  } = useQuery(IS_LOGGED_IN);

  return isLoggedIn? <LoggedInRouter /> : <LoggedOutRouter />;
}

 

log-out-router.tsx 수정 (local state를 apollo client 로 변경)

 

query에서 항상 isLoggedIn은 false 이기 때문에, react variables 이라는 것을 사용한다. (Apollo Client 의 새 기능)

 

React variables 는 application 어디에서나 읽고 수정할 수 있다. GraphQL operation 을 사용할 필요가 없다. 이 variables의 저장은 apollo client에 된다. 

 

Reactive variables의 값이 변경 되면, 그 필드를 갖는 쿼리들은 자동으로 새로고침 된다. 

 

makeVar를 이용해서 react variables을 다음과 같이 생성한다.

export const isLoggedInVar = makeVar(false);

 

isLoggedInVar 변수를 다른 파일에서도 사용할 것 이기 때문에 export 해준다.

 

// logged-out-router.tsx

import React from "react";
import { isLoggedInVar } from "../apollo";

export const LoggedOutRouter = () => {
    const onClick = () => {
        isLoggedInVar(true);
    };
    return (
        <div>
            <h1>Logged Out</h1>
            <button onClick={onClick}>Click to login</button>
        </div>
    );
};

 

다음과 같이 Click to login 버튼을 누르면 isLoggedInVar 의 값이 true로 바뀌고, App.tsx 는 LoggedInRouter를 반환하게 될 것이다.

 

버튼을 누르기 전

 

버튼을 누른 후

그리고 query 요청해여 받아서 저장하지 않고, hook을 이용해서 reactive variables 의 값을 받아 사용할 수 있다. (useReactiveVar 함수 사용)

 

const isLoggedIn = useReactiveVar(isLoggedInVar);

 

그렇다면 App.tsx 파일 코드는 훨씬 더 간결해 진 것을 볼 수 있다.

// App.tsx

import React from 'react';
import { LoggedOutRouter } from './routers/logged-out-router';
import { gql, useQuery, useReactiveVar } from '@apollo/client';
import { LoggedInRouter } from './routers/logged-in-router';
import { isLoggedInVar } from './apollo';


function App() {
  
  const isLoggedIn = useReactiveVar(isLoggedInVar);

  return isLoggedIn? <LoggedInRouter /> : <LoggedOutRouter />;
}

export default App;

 

logged-in-router.tsx 도 logout 버튼을 만들어 isLoggedInVar 가 다시 false로 바뀌게 해준다.

 

// logged-in-router.tsx

import React from "react";
import { isLoggedInVar } from "../apollo";

export const LoggedInRouter = () => (
    <div>
        <h1>Logged In</h1>
        <button onClick={() => isLoggedInVar(false)}>Log Out</button>     
    </div>
);

 

Form

 

React Hook Form : 리액트에서 form을 다루는 것을 좀 더 쉽게 도와주는 라이브러리.

 

React Hook Form 패키지 설치

npm i react-hook-form

 

useForm 을 이용하여, unchaged, default event등등 잡다한 것들을 피해서 form을 생성할 수 있다. 

 

// logged-out-router.tsx

import React from "react";
import { isLoggedInVar } from "../apollo";
import { useForm } from "react-hook-form";

export const LoggedOutRouter = () => {
    const { register, watch } = useForm();
    console.log(watch());
    return (
        <div>
            <h1>Logged Out</h1>
            <form>
                <div>
                    <input {...register("email")} name="email" type="email" required placeholder= "email" />
                </div>
                <div>
                    <input {...register("password")} name="password" type="password" required placeholder="password" />
                </div>
                <button className="bg-yellow-300 text-white">Submit</button>
            </form>
        </div>
    );
};


input 안에 {...register("")} 만 넣어서 사용 할 수 있다. 

register는 input 안 value 값의 validation을 쉽게 할 수 있도록 도와주는데 그 validation 옵션(minLength, maxLength 등등)은 register 괄호 속에 넣어서 사용 할 수 있다. 

 

그리고 useForm 의 watch 함수를 console 창에 출력 해 보면, input 창에 입력 할 때마다 입력한 값이 출력 되는 것을 확인 할 수 있다.

 

그동안 react에서 form부분에서 구현해야 할 것들이 많았기 때문에 다음과 같이 useForm을 이용하여 코드를 간결하게 표현할 수 있다는 것에 react-hook-form 의 기능에대해 감탄하였다.!! 

 

그 외의 추가 내용은 아래 링크에서 확인

https://react-hook-form.com/api/useform/register/

 

useForm - register

Performant, flexible and extensible forms with easy-to-use validation.

react-hook-form.com

 

useForm 에서는 submit을 다루는 handleSubmit함수도 내장되어 있다. 이때, onSubmit 함수를 구현해주어야 한다. 

그리고 handleSubmit 안 인자에는 2개의 함수를 넣을 수 있는데, 유효성 검사를 통과했을 때 실행되는 함수(onSubmit), 통과하지 못했을 때 실행되는 함수(onInValid) 를 넣는다.

 

정규 표현식

register 안에 옵션에 pattern 을 추가 설정해서 정규 표현식을 사용 할 수 도 있다. 

 

{...register("email", {required: true, pattern: /^[A-Za-z0-9._%+-]+@gmail.com$/,})}

 

다음과 같이 react-hook-form 의 useForm 을 이용하여 validate가 훨씬 더 간결해 진 것을 볼 수 있다. 

 

Router

 

typescript 를 위한 react-router-dom 패키지 설치 (typescript 에 대한 react-router-dom 에는 type 이 없기 때문에 따로 declare 해주거나, type을 포함한 package를 새로 설치해야함)

 

npm install @types/react-router-dom

 

위에서 만든 log out 페이지는 다시 route를 이용해서 작성해주고

src에 pages 폴더를 만들어, login, create-account 페이지 작성을 위한 파일을 각각 생성해준다. 

 

// logged-out-router.tsx

import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { Login } from "../pages/login";
import { CreateAccount } from "../pages/create-account";
export const LoggedOutRouter = () => {
    return <Router>
        <Switch>
            <Route path="/create-account">
                <CreateAccount />
            </Route>
            <Route path="/">
                <Login />
            </Route>
        </Switch>
    </Router>
};
// login.tsx

import React from "react";

export const Login = () => {
    return <span>Login</span>;
};

// create-account.tsx

import React from "react";

export const CreateAccount = () => {
    return <span>Create Account</span>;
};

 

 

반응형