2021.05.24

김승우·2021년 5월 24일
0

TIL

목록 보기
62/68

2021.05.24

🎉 TIL

1. 페이지 에러 컴포넌트 적용 및 예외 처리

  • 내용
    : 몇일동안 고민한 내용이다. API 요청 중에 에러가 발생할 경우를 대비한 예외 처리가 메인 페이지에 하나도 적용이 안되어있어서 공통적으로 에러 컴포넌트를 적용하기 위한 방법을 찾아봤다.
  • 에러를 처리하는 시점? 어디가 더 좋은가
    : API 공통화한 모듈에서 처리하는 것 보다는 컴포넌트 단에서 에러를 처리하고, 사용자에게 알려주는 것이 더 좋은 방법이라는 것을 알게되었다.(캡틴판교님 강의 질문 중에서)

  • API 상태를 저장하는 데이터
    : fetches, processes라는 객체를 생성, API 함수 별로 해당하는 프로퍼티를 만들고 해당 프로퍼티의 상태를 변경해서 API의 성공, 로딩, 에러에 대한 상태를 처리했다.

		// API함수의 상태를 담는 객체
		data() {
			return {
				fetches: {
					goods: SUCCESS_CODE,
				},
			}
		},

		methods: {
			// API 요청 함수
			async fetchGoods() {
				try {
					// 요청 시작
					this.fetches.goods = LOADING_CODE;

					// 요청 성공 시
					this.fetches.goods = SUCCESS_CODE;
				} catch (error) {
					// 에러 발생 시
					this.fetches.goods = ERROR_CODE;
				}
			},
-	API 상태에 따라 보여줄 화면 지정
	<!-- goods/index.vue -->
	<!-- 로딩 상태일 때 보여지는 영역 -->
	<div v-if="fetches.goods === LOADING_CODE">loading...</div>

	<!-- 요청 성공 상태일 때 보여지는 영역 -->
	<div v-else-if="fetches.goods === SUCCESS_CODE">{{ goods }}</div>

	<!-- 요청 실패 상태일 때 보여지는 영역 -->
	<div v-else>Error!</div>

2. 여러개의 API의 상태를 관리하는 방법 적용하기.

: 1번의 경우에 하나의 API당 한 개의 에러 컴포넌트가 필요하다. 따라서, 여러개의 API 함수의 상태를 공통으로 관리하는 방법이 필요했다.
우선, 페이지의 구조를 다음과 같이 바꿨다.

// goods/index.vue
data() {
	return {
		pageErrors: [],

		// 페이지 새로고침 중 여부
		isReloading: false,

		fetches: {
			// 기본 상태는 성공 상태
			layouts: SUCCESS_CODE,
			goods: SUCCESS_CODE,
		},
	}
},

computed: {
	// processes 중에서 상태값이 에러인 프로퍼티가 있는지 체크
	isProcessesError() {
		if( !this.processes ) return false;

		const valuesOfProcesses = Object.values(this.processes);

		if( !valuesOfProcesses.length ) return;

		return valuesOfProcesses.findIndex(value => value === ERROR_CODE) > -1;
	},

	// fetches 중에서 상태값이 에러인 프로퍼티가 있는지 체크
	isFetchesError() {
		if( !this.fetches ) return false;

		const valuesOfFetches = Object.values(this.fetches);

		return !!valuesOfFetches.length && valuesOfFetches.findIndex(value => value === ERROR_CODE) > -1;
	},

	// isProcessesError, isFetchesError중에서 하나라도 true인지 체크
	isPageError() {
		return this.isProcessesError || this.isFetchesError;
	},

	// Process, Fetches 중에 에러가 발생한 프로퍼티들을 배열로 리턴
	getErrorPropertyName() {
		if( !this.isPageError ) return [];
		
		let names = [];

		if( this.isProcessesError ) {
			for( const key in this.processes ) {
				if( this.processes[key] === ERROR_CODE ) {
					names.push(key);
				}
			}
		}

		if( this.isFetchesError ) {
			for( const key in this.fetches ) {
				if( this.fetches[key] === ERROR_CODE ) {
					names.push(key);
				}
			}
		}

		return names;
	},
},

methods: {
	async fetchLayouts() {
		// ...
	},

	async fetchGoods() {
		// ...
	},

	// pageErrors 배열에서 name에 해당하는 객체가 이미 존재하는지 체크
	getErrorMethodIndex(name) {
		return this.pageErrors.findIndex(o => o.methodName === name);
	},


	// pageErrors에 에러 객체 생성해서 푸시
	pushErrorMethod(name, args) {
		const _index = this.getErrorMethodIndex(name);

		if( _index > -1 ) {
			this.pageErrors.splice(_index, 1);
		}

		this.pageErrors.push({
			methodName: name,
			args,
		});
	},

	// 에러가 있는 API 함수들 재 실행
	async executeErrorMethods() {
		const self = this;

		if( !this.pageErrors.length ) return;

		// this.pageErrors.forEach(o => {
		//     if( o.methodName && self[o.methodName] ) {
		//         var args = o.args || [];

		//         self[o.methodName].apply(self, args);
		//     }
		// });

		const pageErrors = this.pageErrors.filter(o => !!o.methodName && !!self[o.methodName]);

		if( !pageErrors.length ) return;
		
		// 페이지 새로고침 중 여부 true로 변경
		this.isReloading = true;

		// 참고: https://ko.javascript.info/promise-api
		const requests = pageErrors.map(o => {

			// FIXME 기본 값을 []로 할지 null로 할지
			var args = o.args || [];

			// .apply()를 통해서 context 지정 및 arguments 전달
			// NOTE return을 하지 않을 경우 Promise.all()에 적용할 수 없다.
			return self[o.methodName].apply(self, args);
		});

		// Promise.all을 통해 모든 요청이 끝날때까지 대기
		await Promise.all(requests);

		this.isReloading = false;
	},

	// 에러 발생 시 > 페이지 새로고침 버튼 클릭
	reloadPage() {
		this.executeErrorMethods();
	},
},
<!-- goods/index.vue -->
<!-- 에러가 있을 경우 보여지는 컴포넌트 영역 -->
<error-component v-if="isPageError"></error-component>

<!-- 페이지 레이아웃 데이터를 가져오는 API 로딩 시 보여지는 영역 -->
<loading-component v-if="fetches.layouts === LOADING_CODE"></loading-component>

<!-- 페이지 레이아웃 데이터를 가져오는 API 성공 시 보여지는 영역 -->
<main-component v-else-if="fetches.layouts === SUCCESS_CODE"></main-component>
  • isReloading 적용한 경우
<!-- goods/index.vue -->
<!-- 에러가 있고, 새로고침 중이 아닐 경우 보여지는 컴포넌트 영역 -->
<error-component v-if="isPageError && !isReloading"></error-component>

<!-- 페이지 레이아웃 데이터를 가져오는 API 로딩 또는 새로고침 중일 경우 보여지는 영역 -->
<loading-component v-if="fetches.layouts === LOADING_CODE || isReloading"></loading-component>

<!-- 페이지 레이아웃 데이터를 가져오는 API 성공 시 보여지는 영역 -->
<main-component v-else-if="fetches.layouts === SUCCESS_CODE"></main-component>
profile
사람들에게 좋은 경험을 선사하고 싶은 주니어 프론트엔드 개발자

0개의 댓글