본문 바로가기

영어쌤의 놀이방_ 수퍼.코드.파티/리액트와 장고로 회원가입 관리 만들어욤

(4.1) 예외처리와 알림 서비스_third-party package(써드파티 사용)_ 영어쌤이 만드는 회원가입, 인증[CRUD] 만들어욤! 리액트와 파이썬 장고

반응형

(4.1) 예외처리와 알림 서비스_ 
영어쌤이 만드는 회원가입, 인증[CRUD] 만들어욤! 리액트와 파이썬 장고

안녕하세요? 영어회화 전문 강사 영어쨈 죠니입니당~!!!

- 이번엔 지난 포스팅(3.4) 마지막에 언급했듯이 예외처리와 알림 서비스를 다룰 것 입니다.
- 시작하기에 앞서 Remind(상기) 시켜 볼까요?
- 정보 입력 이후 등록을 하려 해도 이미 동일 정보인 email이 list에 있으므로 등록이 안됩니다.
- 사용자 입장에선 안되는 이유를 알 수 없기에 그 다음 단계의 행동을 취하기 난해해집니다.
- 그렇기에 이미 다른사용자가 해당 email 계정을 가지고 있어 다른 이름의 계정을 생성하라는 메세지를 띄워 줘야 할 것 입니다.

- 이를 해결하기 위해 우리가 만들 것은!
1. erros reducers
2. alerts component
입니다.

바로 시작해 봐요 우리!

(1) 사전 답사하기:
1. 예외처리 시나리오 파악 
2. 작용 가능한 Third-Party Source 찾아 적용하는 방법은 무엇인가요?

- 아무 것도 입력하지 않은 상태에서 Submit 버튼을 눌렀을 경우
-- 아무런 정보가 없는데 서버로 POST 요청을 보내는 것 입니다. - 에러!


- 크롬 개발자모드에서 콘솔을 보면 다음과 같은 에러 메세지가 발생되어 있습니다.
- Bad request 메세지를 받았습니다.

- 양식을 채우지 않은 상태에서 Submit 즉, POST 요청을 하였기 때문입니니다.

- 그렇게 이번 시간에 진행할 것은 Error가 발생할때마다 우리 코드의 State에 던져주고 그것을 받아 컴포넌트에 넣어줍니다.
- 그렇게 잡아낸 에러들을 질제 UI 상에 Popup message로 사용자에게 전달해 주는 것 입니다.
- alert라는 컴포넌트를 만듭니다. 
- 이번엔 react-alert라는 third party package를 사용할 것 입니다.
- React-Alert_Third_Party_Github_Link(리액트-알림-써드파티-깃헙-링크)
- 데모페이지 입니다.https://codesandbox.io/s/l2mo430lzq

 

l2mo430lzq - CodeSandbox

The online code editor tailored for web applications

codesandbox.io

- 데모 화면을 띄운 Sandbox로 가 보면 오른쪽 화면에 결과를 볼 수 있습니다. 버튼 UI는 상당히 간단합니다. 여기서 관건은 버튼을 눌렀을 시 발생되는 메세지에 있는 것 입니다.
- 버튼은 다음과 같습니다.

- 왼쪽부터 차례대로 Show, Error, Success 를 클릭하면 다음과 같은 에러 메세지가 pop-up 됩니다.

- 아래의 코드의 내용 중 붉은 박스 안의 BOTTOM_CENTER 이라는 부분이 있는데, 해당 코드를 수정하여 다른 방향을 시작점으로 잡을 수 있습니다.

- 가령 TOP_LEFT 로 코드를 수정하면 다음과 같이 좌측 상단부에 에러 메세지가 발생됩니다. 참고해 주세요~!

!!!!!! 이 기능을 여러분의 Authentication(인증)에 적용시키길 원한다면 깃헙 내에서 안내하는 몇 가지 요구사항 및 절차를 따라 종속 패키지들을 설치해 주어야 합니다.
- Github의 내용을 조금 더 살펴보겠습니다.
- React-alert 와 react-alert-template-basic, 그리고 rat-transition-group을 설치해 주어야 합니다.

- Alert Provider 가지고 오기(사용하기 위해)

import { transitions, positions, Provider as AlertProvider } from 'react-alert'

- Redux provider때와 같은 형식으로 이번에는 alert-provider로 감싸주면 됩니다.

const Root = () => (
  <AlertProvider template={AlertTemplate} {...options}>
    <App />
  </AlertProvider>
)

- withAlert 가지고 오기(사용하기 위해)

import { withAlert } from 'react-alert'

- 코드 감싸주기!!! (요거로다가!)

export default withAlert()(App)

- 자 그러면 사전 답사를 모두 마쳤습니다. 그러면 다시 움직여야죠!! 
- We gotta move!!! BAY BAY!!

(2) React-Alert의 dependency(의존, 종속) 패키지들 설치하기(가상환경 인거 잊지 마세요!!!)

- 다음의 명령어로 Dependency들을 설치해 줍니다.

$ npm install --save react-alert react-alert-template-basic react-transition-group

- 그리고 다음의 명령어로 서버를 재실행해 주세요.

$ npm run dev

(3) provider 가져오기(import) || /src/components/App.js 파일에 코드 추가 작성

- 다음은 alert 관련 코드 추가 작성 전 입니다.

// App.js 코드 주가 전 
import React, { Component, Fragment } from "react";
import ReactDOM from "react-dom";

import Header from "./layout/Header";
import Dashboard from "./leads/Dashboard";

import { Provider } from "react-redux";
import store from "../store";

class App extends Component {
	render() {
    	return (
        	<Provider store={store}>
            	<Fragment>
                	<Header />
                    <div className="container">
                    	<Dashboard />
                    </div>
                </Fragment>
            </Provider>
        );
    }
}

- 이제 작성해 볼까요???
- 먼저 alert 기능들을 가져옵니다.

- Alert message의 화면 잔류 시간 및 발생 위치 결정

- 렌더링해줄 html 태그 코드 에 AlertProvider로 감싸주기 및 기타 코드 작성

- 다음은 위의 가이드를 따라 작성이 완료된 App.js 파일의 내용 입니다.(이전 내용 포함)

// App.js 코드 주가 전 
import React, { Component, Fragment } from "react";
import ReactDOM from "react-dom";

import { Provider as Alert Provider } from 'react-alert';
import AlertTemplate from 'react-alert-template-basic';

import Header from "./layout/Header";
import Dashboard from "./leads/Dashboard";

import { Provider } from "react-redux";
import store from "../store";

// Alert Options
const alertOptions = {
	timeout: 3000,
    position: 'top center'
}

class App extends Component {
	render() {
    	return (
        	<Provider store={store}>
            	<AlertProvider template={AlertTemplate} 
                {...alertOptions}>
            		<Fragment>
                		<Header />
                	    <div className="container">
                    		<Dashboard />
                   		</div>
                	</Fragment>
                </AlertProvider>
            </Provider>
        );
    }
}

(4) /src/components/layout/Alerts.js 파일 생성 및 작성

- 시작하기 전에!!! Alerts.js에 대한 업데이트 사항이 생겼습니다.
- 내용은 다음과 같습니다.

Alerts.js 업데이트 사항

- 파일 내에서 코드 마지막 줄에 작성된 export default withAlert(Alerts); 는 버전 업데이트가 되면서 수정을 조금 해줘야 한다고 합니다.
- 이렇게요!!

 export default withAlert()(Alerts); 

- 현 포스팅에서 지금 이 줄의 아랫부분의 코드들은 (캡쳐 이미지 제외) 수정완료 하였습니다.

- 먼저 이번에 사용할 Alert 기능이 제대로 작동하는지 여부를 알기 위한 테스트 코드 부터 작성합니다.

// Alerts.js
import React, { Component, Fragment } from 'react';
import { withAlert } from 'react-alert';

export class Alerts extends Component {
	// 다음은 Alert 기능이 작동에 대한 Test 코드 입니다.
    componentDidMount() {
    	this.props.alert.show("It Works");
    }

	render() {
    	return <Fragment />;
    }
}

export default withAlert()(Alerts);

(5) /src/components/App.js에서 /layout/Alerts.js 파일 가져오기

- 다음의 코드를 추가해 주어 방금 작성한 Alerts.js 파일을 호출합니다.

- html 태주에서는 Header 아래에 Alerts 호출 코드를 넣어주세요.

// App.js 코드 주가 전 
import React, { Component, Fragment } from "react";
import ReactDOM from "react-dom";

import { Provider as Alert Provider } from 'react-alert';
import AlertTemplate from 'react-alert-template-basic';

import Header from "./layout/Header";
import Dashboard from "./leads/Dashboard";
import Alerts from "./layout/Alerts";

import { Provider } from "react-redux";
import store from "../store";

// Alert Options
const alertOptions = {
	timeout: 3000,
    position: 'top center'
}

class App extends Component {
	render() {
    	return (
        	<Provider store={store}>
            	<AlertProvider template={AlertTemplate} 
                {...alertOptions}>
            		<Fragment>
                		<Header />
                        <Alerts />
                	    <div className="container">
                    		<Dashboard />
                   		</div>
                	</Fragment>
                </AlertProvider>
            </Provider>
        );
    }
}

결과화면
- 작성이 완료되었으니 테스트를 진행합니다.

- Alerts.js 에서 테스트 코드로 작성했던 것과 같이 "It Works"라는 Alert message가 출력됩니다.

(6) /src/reducers/errors.js 파일 생성

- add lead나 delete 또는 기타 에러가 발생했을 때 console에 발생되는 정보로는 특히 사용자에겐 크게 유용한 정보를 제공해 줄 수 없습니다. 그렇기 때문에 메세지들과 상태를 errors reducer로 보내줍니다.

(7) /src/reducers/index.js || 예외처리에 대한 코드 작성

- 여기서 index.js 는 reducers 의 root 파일인 셈입니다. 모든 동작을 가지고 있다고 보면 됩니다.

// /leadmanager/frontend/src/reducer/index.js
import { combineReducers } from 'redux';
import leads from './leads';
import errors from "./errors";

export default combineReducers({
	leads,
    errors
});

(8) /src/actions/type.js || 예외처리 관련 코드 작성

- type.js 파일에 다음의 코드를 추가 작성합니다.

// /src/actions/types.js
export const GET_LEADS = "GET_LEADS";
export const DELETE_LEAD = "DELETE_LEAD";
export const ADD_LEAD = "ADD_LEAD";
export const GET_ERRORS = "GET_ERRORS";

(9) /src/reducers/errors.js 작성

-다음의 코드를 작성합니다.

// errors.js
import { GET_ERRORS } from '../actions/types';

const initialState = {
	msg: {},
    status: null
};

export default function(state = initialState, action) {
	switch (action.type) {
    	case GET_ERRORS:
        	return {
            	msg: action.payload.msg,
                status: action.payload.status
            };
          default:
          	return state;
    }
}

(10) /src/actions/leads.js 수정

- 다음과 같이 actions 폴더 내의 leads.js 파일을 수정합니다.

- 위와 같이 코드를 수정하여 정확한 에러 메세지를 콘솔에 먼저 발생시킵니다.

// lead.js axios 활용예정
import axios from 'axios';

import { GET_LEADS, DELETE_LEAD } from './types';

// GET LEADS
export const getLeads = () => dispatch => {
	axios.get("/api/leads/")
    	.then(res => {
        	dispatch({
            	type: GET_LEADS,
                payload: res.data
            });           
        })
        .catch(err => console.log(err));
};

// DELETE LEAD
export const deleteLeads = (id) => dispatch => {
	axios.delete(`/api/leads/${id}/`)
    	.then(res => {
        	dispatch({
            	type: DELETE_LEADS,
                payload: id
            });           
        })
        .catch(err => console.log(err));
};

// ADD LEADS
export const addLeads = () => dispatch => {
	axios.post("/api/leads/") // 실제 데이터를 보내기 위한 명령어도 POST입니다. 그러니 post로!
    	.then(res => {
        	dispatch({
            	type: ADD_LEADS,
                payload: res.data
            });           
        })
        .catch(err => console.log(err.response.data));
};

(11) 콘솔창에서 오류 메세지 확인하기 // leads.js 파일 수정

- 브라우저 UI를 Reload(새로고침) 하여 아무것도 입력하지 않은 상태에서 Submit 버튼을 눌러 에러를 발생시킨 뒤 콘솔창에서 확인하면 다음과 같은 결과를 얻을 수 있습니다.

결과화면

- 이렇게 발생된 메세지들을 각각 객체화 시켰습니다.

- 그리고 이를 Redux에 적용시키기 위해 lead.js의 마지막 줄의 catch 부분을 수정해 줍니다.

// lead.js axios 활용예정
import axios from 'axios';

import { GET_LEADS, DELETE_LEAD } from './types';

// GET LEADS
export const getLeads = () => dispatch => {
	axios.get("/api/leads/")
    	.then(res => {
        	dispatch({
            	type: GET_LEADS,
                payload: res.data
            });           
        })
        .catch(err => console.log(err));
};

// DELETE LEAD
export const deleteLeads = (id) => dispatch => {
	axios.delete(`/api/leads/${id}/`)
    	.then(res => {
        	dispatch({
            	type: DELETE_LEADS,
                payload: id
            });           
        })
        .catch(err => console.log(err));
};

// ADD LEADS
export const addLeads = () => dispatch => {
	axios.post("/api/leads/") // 실제 데이터를 보내기 위한 명령어도 POST입니다. 그러니 post로!
    	.then(res => {
        	dispatch({
            	type: ADD_LEADS,
                payload: res.data
            });           
        })
        .catch(err => {
        	const errors = {
            	msg: err.response.data,
                status: err.response.status
            }
            dispatch({
            	type: GET_ERRORS,
                payload: errors
            });
        });
};

(12) 결과확인_ Redux tool 사용

- 인위적으로 Submit 버튼만 클릭(정보 입력 없이) 하면 미리 입력해준 코드에 의해 다음의 Redux툴에서 msg, status에 대한 값을 얻을 수 있습니다. 

- 이제 이 메세지를 UI상에서 실제로 pop-up message로 발생시켜주도록 합니다.

(13) /src/components/layout/Alerts.js 수정

- 다음코드를 추가 작성하여 'react-redux'(리액트와 리덕스를 서로 연결하는데 사용)를 가져옵니다(import).

- 기본적인 토대가 다음의 코드로 완성되었습니다. 이제 설명하겠습니다.

// Alerts.js
import React, { Component, Fragment } from 'react';
import { withAlert } from 'react-alert';
import { connect } from 'react-redux';
import PropTypes from "prop-types";

export class Alerts extends Component {
	static propTypes = {
    	error: PropTypes.object.isRequired
    }
}

export class Alerts extends Component {
	// 다음은 Alert 기능이 작동에 대한 Test 코드 입니다.
    componentDidMount() {}

	render() {
    	return <Fragment />;
    }
}

const mapStateToProps = state => ({
	error: state.errors
});

export default connect(mapStateToProps)(withAlert()(Alerts));
//export default withAlert()(Alerts);

...
componentDidUpdate() {}

/*메세지가 발생햇을 때 그 메세지를 UI베 반영시켜줘야 합니다.
그래서 위의 componentDidUpdate와 가장 아래의 export 연결해 줍니다.
발생된 메세지는 앞으로 componentDidUpdate 내에 넣어 줄 코드에 의해 
UI에 발생될 것 입니다.*/
export default connect(mapStateToProps)(withAlert(Alerts));

- 그러면 다시 Alert.js로 돌아가 마저 작성해 줍니다.

// Alerts.js
import React, { Component, Fragment } from 'react';
import { withAlert } from 'react-alert';
import { connect } from 'react-redux';
import PropTypes from "prop-types";

export class Alerts extends Component {
	static propTypes = {
    	error: PropTypes.object.isRequired
    }
}

export class Alerts extends Component {
    componentDidMount(prevProps) {
    	const { error, alert } = this.props;
        // 지금의 error가 이전의 Props 의 error와 동일하지 않다면...
        if(error !== prevProps.error) {
			// "There is an error" 라는 메세지를 발생시킵니다.
        	alert.error("There is an error");
        }
    }

	render() {
    	return <Fragment />;
    }
}

const mapStateToProps = state => ({
	error: state.errors
});

export default connect(mapStateToProps)(withAlert()(Alerts));
// export default withAlert()(Alerts);

결과확인
- 정확히 코드에서 의도한 대로 결과가 나옵니다.

(14) 각 메세지에 대한 디테일 핸들링 해주기, Alerts.js 수정

- 이제 커스텀 메세지를 작성해  발생 지점에 따른 오류 발생 경위에 대해 정확히 명시해 줍니다.

- Name, Email 정보 누락에 대한 메세지 발생

// Alerts.js
import React, { Component, Fragment } from 'react';
import { withAlert } from 'react-alert';
import { connect } from 'react-redux';
import PropTypes from "prop-types";

export class Alerts extends Component {
	static propTypes = {
    	error: PropTypes.object.isRequired
    }
}

export class Alerts extends Component {
    componentDidMount(prevProps) {
    	const { error, alert } = this.props;
        if(error !== prevProps.error) {
        	if(error.msg.name) alert.error("Name is Required");
            if(error.msg.email) alert.error("Email is Required");
        }
    }

	render() {
    	return <Fragment />;
    }
}

const mapStateToProps = state => ({
	error: state.errors
});

export default connect(mapStateToProps)(withAlert()(Alerts));
// export default withAlert()(Alerts);

 

여기까지 하고 다음 4.2 포스팅으로 넘깁니다.

내용은 다음과 같습니다.

1. 위와 같이 hard coding 되어 발생되는 메세지로는 한계가 있습니다.
2. 만약 이미 등록되어 있던 이메일과 동일한 이메일을 입력해 생성 시도할 경우 상태는 다르지만 같은 누락된 것과 동일한 메세지가 출력될 것 입니다. 그렇게 hard coding 된 코드들을 조금 더 정확하게 출력해 줄 수 있도록 수정해 줄 것 입니다.

잠시 뒤에 봐욤~!!!  

반응형