[springboot+jwt+react#4] login api 연결

오늘 만들것

  • 로그인 처리를 API에 연결해보자

걸리는 시간

  • 15min

실습

저번에 만든 로그인 폼에 api을 연결해보자

블로그에 설명되어있진 않지만 api 서버에 로그인 컨트롤러가 준비되어있다.

B /controller/AuthController.java

package com.project.controller;

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
    private final AuthService authService;

    @PostMapping("/login")
    public ResponseEntity<TokenDto> login(@RequestBody MemberRequestDto memberRequestDto) {
        return ResponseEntity.ok(authService.login(memberRequestDto));
    }
}

meemgerRequestDto에 email / password로 되어있어서 프론트도 조금 변경

F /components/Auth/LoginForm.tsx

import {SubmitHandler, useForm} from "react-hook-form";

type Inputs = {
    email: string,
    password: string,
};

const LoginForm = () => {
    const { register, handleSubmit, watch, formState: { errors } } = useForm<Inputs>();
    const onSubmit: SubmitHandler<Inputs> = data => console.log(data);

    // watch console.
    console.log(watch(["email","password"]))

    return (
        <section>
            <h1>Login</h1>
            <form onSubmit={handleSubmit(onSubmit)}>
                <div>
                    <label htmlFor='userid'>Id</label>
                    <input className={"border-2"} {...register("email", { required: true })}/>
                    {errors.email && <span>This field is required</span>}
                </div>
                <div>
                    <label htmlFor="password">Password</label>
                    <input type='password' className={"border-2"} {...register("password", { required: true })}/>
                    {errors.email && <span>This field is required</span>}
                </div>
                <div>
                    <button type='submit'>Login</button>
                </div>
            </form>
        </section>
    )
}

export default LoginForm;

이메일 타입 벨리데이션도 필요하고, 실제로그인 테스트를 해보려면 데이터가 있어야 한다. 회원가입 기능이 먼저 만들어저야겠지만...
postman을 이용하여 일단 회원 가입을 해놓는다.

우선 fetch작업을 수월하게 하기 위해서 fetch관련 util을 하나 만들어보자

자 이제 프론트에셔 실제 로그인처리를 하고 응답을 받아오는지 확인하자
F /src/utils/fetch-action.ts

import axios, { AxiosError, AxiosResponse }  from 'axios';
type ServerError = { errorMessage: string };
type LoginFailType = { status: number, error: string,};

const URI = "http://localhost:8080"

interface FetchData {
    method: string,
    url: string,
    data? : {},
    header : {},
}

const uri = URI;

const fetchAuth = async (fetchData: FetchData) => {
    const method = fetchData.method;
    const url = fetchData.url;
    const data = fetchData.data;
    const header = fetchData.header;


    try {
        console.log(fetchData);
        const response:AxiosResponse<any, any> | false =
            (method === 'get' && (await axios.get(uri + url, header))) ||
            (method === 'post' && (await axios.post(uri + url, data, header))) ||
            (method === 'put' && (await axios.put(uri + url, data, header))) ||
            (method === 'delete' && (await axios.delete(uri + url, header))
            );

        if(response && response.data.error) {
            console.log((response.data as LoginFailType).error);
            console.log(response.data);
            return null;
        }

        if (!response) {
            alert("false!");
            return null;
        }

        return response;

    } catch(err) {

        if (axios.isAxiosError(err)) {
            const serverError = err as AxiosError<ServerError>;
            if (serverError && serverError.response) {
                console.log(serverError.response.data);
                alert("failed!");
                return null;
            }
        }

        console.log(err);
        alert("failed!");
        return null;
    }

}

const GET = ( url:string, header:{} ) => {
    const response = fetchAuth({ method: 'get', url, header });
    return response;
};

const POST = ( url:string, data: {}, header:{}) => {
    const response = fetchAuth({ method: 'post', url, data, header })
    return response;
};

const PUT = async ( url:string, data: {}, header:{}) => {
    const response = fetchAuth({ method: 'put', url, data, header });
    return response;
};

const DELETE = async ( url:string, header:{} ) => {
    const response = fetchAuth({ method: 'delete', url, header });
    return response;
};

export { GET, POST, PUT, DELETE }

/utils/auth-action.ts 를 생성해주고

import {POST} from "./fetch-action";

export const loginActionHandler = (email: string, password: string) => {
    const URL = '/auth/login';
    const loginObject = { email, password };
    const response = POST(URL, loginObject, {});
    return response;
}

로그인하는 함수를 하나 만들어주고 loginForm에서 호출해보자

/components/Auth/LoginForm.tsx

// import 해주고
import * as authAction from '../../utils/auth-actions'

...
 const onSubmit: SubmitHandler<Inputs> = data => {
        authAction.loginActionHandler(data.email, data.password).then(r => {
            console.log('response=>' + r);
        })
    }

onSumbit부분을 바꾸고 테스트

cors 오류 .. cors에대해서 자세히 알고싶으면 아래 사이트 클릭해서 확인

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

뭐 쉽게 말해서 같은 도메인이 아닌데 ajax 호출을 했다고 경고를 하는것이다.

front는 3000포트이고 backend는 8080포트라 생기는 문제

간단히 front package.json에 proxy 설정을 해주면 간단히 해결가능하다.

나중에는 backend 안으로 코드를 넣을거라 개발시에만 이렇게 써주면 된다.

프록시 서버에 대한 설명은 아래 문서에서 확인

https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9D%EC%8B%9C_%EC%84%9C%EB%B2%84

F package.json

"proxy": "http://localhost:8080"

제일 하단에 추가해주고 다시 실행

자 실제 로그인정보를 입력하고 로그인을 해보면 위와같이 accessToken과 refreshToken을 받아오는것을 확인할수 있다.

로그인 후에 어떻게 처리를 해야할지 생각해보자.

사용자는 로그인을 하고 가져온 accessToken을 이제 다음 api 요청에서부터 계속 서버에 함께 해보내야한다.

추가로 프론트에서는 해당 accessToken을 가지고 로그인여부를 체크해야한다.

로그인처리는 다음시간에...