본문 바로가기
웹개발/리액트

리액트 error 핸들링

by 어컴띵 2021. 3. 18.

오늘은 리액트에 오류가 발생했을때 어떻게 처리할 지 한번 알아보자

여기서 오류는 api서버에서 받은 오류 메세지 일수도 있고, 리액트에서 캐치한 오류일수도 있다.

 

처리방식을 간단히 설명하면 오류 발생시 오류발생여부를 체크하여 오류발생여부가 true이면 정의한 에러 컴포넌트에 에러정보를 셋팅해서 보내주면 에러 컴포넌트에서는 모달창으로 오류발생여부를 보여준다.

 

여기서는 함수형 컴포넌트에서 처리하는 방식이다. 클래스형 함수에서는 ErrorBoundary를 사용해서

componentDidCatch and getDerivedStateFromError 를 이용한 에러 처리방식이 있다. 하지만 hook에서는 아직 제공하지 않고 있다고 한다.

ko.reactjs.org/docs/error-boundaries.html

 

에러 경계(Error Boundaries) – React

A JavaScript library for building user interfaces

ko.reactjs.org

stackoverflow.com/questions/60537645/how-to-implement-error-boundary-with-react-hooks-component

 

How to implement Error Boundary with React Hooks Component

How can we implement error Boundary in React Hooks. Is it even supported with react Hooks?

stackoverflow.com

다음 순서로 작업을 해보자

 

1. 모달창 오류 컴포넌트를 작성

2. 오류 발생시 오류처리를 위한 useState를 이용한 셋팅

3. 오류 발생하는 부분에 error를 캐치하거나 서버의 오류 응답을 확인

4. return함수에 오류 컴포넌트 추가

5. 오류발생을 시켜서 제대로 처리되는지 확인

 

1. 모달창 오류 컴포넌트를 작성

import {useEffect, useState} from "react";
import ServerErrorModal from "./ServerErrorModal";
import LoginErrorModal from "./LoginErrorModal";

function ErrorComponent({show,setError,message,type}){
    const [close, setClose] = useState(show);
    useEffect(()=>{
        setClose(show)
    },[show])

    const handleClose = () => {
        setClose(false);
        setError(false);
        return 0;
    }

    const errorModal = {
        server:<ServerErrorModal close={close} handleClose={handleClose} message={message} ></ServerErrorModal>,
        login:<LoginErrorModal close={close} handleClose={handleClose} message={message} ></LoginErrorModal>
    }

    const modal = errorModal[type];

    return (
        <>
            {modal}
        </>
    )
}
export default ErrorComponent;

 

LoginErrorModal.js

import {Button, Modal} from "react-bootstrap";

const LoginErrorModal = ({close,handleClose,message}) => {
    return (
        <Modal show={close} onHide={handleClose}>
            <Modal.Header>
                <Modal.Title>Login Error</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <p>로그인 중 오류가 발생하였습니다.</p>
                <p>잠시후 다시 접속해주시기 바랍니다.</p>
                <p>{message}</p>
            </Modal.Body>
            <Modal.Footer>
                <Button variant={"primary"} onClick={handleClose}>Close</Button>
            </Modal.Footer>
        </Modal>
    )
}

export default LoginErrorModal;

ServerErrorModal.js

import {Button, Modal} from "react-bootstrap";

const ServerErrorModal = ({close,handleClose,message}) => {
    return (
        <Modal show={close} onHide={handleClose}>
            <Modal.Header>
                <Modal.Title>Server Error</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <p>서버에 응답이 없거나, 오류가 발생하였습니다.</p>
                <p>잠시후 다시 접속해주시기 바랍니다.</p>
                <p>{message}</p>
            </Modal.Body>
            <Modal.Footer>
                <Button variant={"primary"} onClick={handleClose}>Close</Button>
            </Modal.Footer>
        </Modal>
    )
}

export default ServerErrorModal;

두개의 소스가 거의 같아서 하나의 모달만 이용하고 메세지 부분만 별도로 만들어도 될거 같다.

 

2. 오류 발생시 오류처리를 위한 useState를 이용한 셋팅

const [err, setError] = useState({hasError:false,message:"",type:"server"});

3. 오류 발생하는 부분에 error를 캐치하거나 서버의 오류 응답을 확인

async function handleSubmit(e){
        setLoading(true);
        await signIn(user).then(response=>{
            if(response.status==200){
                console.log(response.data);
                localStorage.setItem("token",response.data);
                setIsRedirect(true);
            }else{
                setError({
                    ...err,
                    hasError: true,
                    message: response.message,
                    type:"login"});
            }
        }).catch(ex=>{
            console.log(ex)
            setError({
                ...err,
                hasError: true,
                message: "",
                type: "server"})
        })
        setLoading(false);
    }

4. return함수에 오류 컴포넌트 추가

return(
            <Container className={"text-center"} fluid>
                <ErrorComponent show={err.hasError}
                                setError={(flag)=>setError(flag)}
                                message={err.message}
                                type={err.type}/>

전체 소스

import {Button, Container, Form, NavLink, Spinner} from "react-bootstrap";
import {useState} from "react";
import {signIn} from "../api";
import ErrorComponent from "../error/ErrorComponent";
import {Link, Route} from "react-router-dom";
import {Redirect} from "react-router";

function Signin(){

    const [user, setUser] = useState({username:'',password:''})
    const [err, setError] = useState({hasError:false,message:"",type:"server"});
    const [loading,setLoading] = useState(false);
    const [isRedirect, setIsRedirect] = useState(false);

    function handleEmail(e){
        setUser({...user,username:e.target.value})
    }
    function handlePassword(e){
        setUser({...user,password:e.target.value})

    }
    async function handleSubmit(e){
        setLoading(true);
        await signIn(user).then(response=>{
            if(response.status==200){
                console.log(response.data); 
                setIsRedirect(true);
            }else{
                setError({
                    ...err,
                    hasError: true,
                    message: response.message,
                    type:"login"});
            }
        }).catch(ex=>{
            console.log(ex)
            setError({
                ...err,
                hasError: true,
                message: "",
                type: "server"})
        })
        setLoading(false);
    }

    return(
            <Container className={"text-center"} fluid>
                <ErrorComponent show={err.hasError}
                                setError={(flag)=>setError(flag)}
                                message={err.message}
                                type={err.type}/>
                <Form className="form-signin">
                    <h1 className="h3 mb-3 font-weight-normal">Please sign in</h1>
                    <Form.Group controlId={"formBasicEmail"}>
                        <Form.Label className="sr-only">Email address</Form.Label>
                        <Form.Control onChange={handleEmail} type={"email"} placeholder={"Enter email"} required />
                        <Form.Label className="sr-only">Password</Form.Label>
                        <Form.Control onChange={handlePassword} type={"password"} placeholder={"Password"} required />
                    </Form.Group>
                    <Form.Group controlId={"formBasicCheckbox"}>
                        <Form.Check type={"checkbox"} label={"Remember me"}/>
                    </Form.Group>
                    <Button onClick={handleSubmit} className="btn btn-lg btn-primary btn-block" variant={"primary"} type={"button"} disabled={loading}>
                        {loading && <span><Spinner
                            as="span"
                            animation="border"
                            size="sm"
                            role="status"
                            aria-hidden="true"
                        /> Sign in...</span>}
                        {!loading && "Sign in"}
                    </Button>
                    <p className={"float-left"}><Link to={"/signup"}>Sign up</Link></p>
                    <p className="mt-5 mb-3 text-muted">&copy; 2017-2020</p>
                </Form>
                <Route>
                    {isRedirect && <Redirect to="/"/>}
                </Route>
            </Container>
    )
}
export default Signin;

 

5. 오류발생을 시켜서 제대로 처리되는지 확인