[Front-End] 한 눈에 보이는 에러 처리 (with 프론트엔드주니어 에러회고)

2023. 10. 17. 19:11b:develop

 

프론트엔드로 전향 후 

기존 UI 리뉴얼 및 개선 이라는 담당 업무가 생겼다.

약 세달의 시간 동안 이쁘지도 않은 회색 화면을 깔끔하고 이쁘게 변경하였다.

 

"나비효과" 의 시작

영화제목인줄 알았지만

나의 작은(?) 세달의 코딩이 예상치 못한 엄청난 결과를 불러일으킨 

최근이었다.

 

[우당탕탕]

이슈1. 기능과 상관없이 화면에서 무한대로 polling 되는 API 호출을 막았더니 검색이 안된다

이슈2. 컴포넌트의 통합이라는 명목하에 버튼의 위치를 통일 시켰더니 active panel / 잘되던 Save기능 등이 안된다

이슈3. 기존에 되던 기능이 안된다

 

[원인분석]

문제1. 앵귤러를 사용하면서도 의존성을 파악하지 않고 컴포넌트를 살피지 않았다

문제2. 버튼이 이쁘지 않아 왜 그렇게 만들었는지 이해하지 않았다

문제3. 아직도 부족하다

 

-

이렇게 발견한 이슈를 jira에 리스트업해가면서 고치고 있다.
충분히 막을 수 있었던 문제들이 반복적으로 일어나 다시 기초부터, 구조부터 알아야겠다는 생각이 든다.
에러를 처리하려면 누구보다 에러를 잘 아는 개발자가 되어야겠단 생각에

에러 처리에 관해 학습하면 좋을 거 같단 생각에 자료를 만들었다.

 

 

[ 한눈에 보이는 에러 처리 ]

에러는 어디서부터 시작되는 걸까?

나의 부족함이 9할이겠지만 가끔 예상치 못한 이슈들이 터져 나온다.

스스로 많은 이슈를 불러일으키고 에러에 대한 지식이 부족하니 외부에서 발생한 에러인지 판단 못한 적도 있었다. (부끄 ._.)

이런 경험을 바탕으로 한번 자세히 알아보고자 조사했다.

 

 

1. 내부로부터 시작되는 에러: 외부 영향과 관계없이 개발자가 작업한 코드에서 발생한 에러

  • 개발자의 실수 (대문자 소문자 실수, 타입 실수 등)
     -> Syntax Error / Logical Error / Runtime Error
  • 기획을 잘못 이해함
  • 커뮤니케이션 오류
  • 특수한 상황에 대한 처리가 미흡함
    -> 최신 브라우저에서 기능이 바뀜, Safari 특정 버전의 Indexed DB API 버그, 특이 케이스에서만 발생하는 런타임 오류 등

 

2. 외부로부터 시작되는 에러: 외부 영향 때문에 발생하는 에러

  • 인터넷 상태가 좋지 않음
  • 디바이스 상태 오류 (베터리, 메모리 등)
  • 사용자의 잘못된 입력 (ex. URL 입력오류)
  • 서버로부터 발생한 에러 (4xx에러, 5xx에러)

 

에러 핸들링

어떻게 에러가 어떻게 발생하는지 알게 되었지만, 사실 실무에서 100퍼센트 응대하기란 어렵다.

좀 더 유연한 처리 방법이 필요하다.

 

특히 UI를 개발하는 개발자로서

1. 사용자에게 에러에 대해 인지시키고 다른 행동을 할 수 있도록 유도하여 서비스에 대한 부정적인 경험 방지

2. 데이터베이스에 정보를 넣는 작업일 경우 서비스의 트랜잭션에 영향을줘 서비스 장애를 일으키는 현상 발생 방지

를 해야 한다고 생각한다.


그래서 필요한 게 바로 '에러핸들링' 이다.

 

에러 핸들링이란? 

- 코드 실행 -> 에러 발생 -> 예외처리 -> 실행 흐름 복구

코드가 실행되고 예상하거나 예상치 못했던 상황에서 에러가 발생했을 때 자바스크립트에서 지원하는 기본적인

기능들로 예외 처리를 하여 애플리케이션이 중단되는 것을 막고 실행 흐름이 이어 갈 수 있도록 복구 하는 것

(오류가 발생할 수도 있고 아닐 수도 있는 것을 '예외'구문으로 처리)

 

 

에러 정리

현실적으로 모든 에러를 처리하고 끊기에는 어렵지만 그렇다고 손 놓고 있을 수도 없는 일!

 

1. 에러가 발생한 곳에서 처리 (에러전파를 최대한 막기)

// 에러가 최초 발생한 함수 - API호출 (Service)
// console.error() 정도의 역할 후 에러 2번으로 전달

fetchData() {
	return this.http.get('/api/data').pipe(
    	catchError(error => {
        	console.error('API Error:', error);
            return throwError(error);
        })
   );
}
// 1번 함수의 반환값을 표현하는 컴포넌트
// 이 컴포넌트는 서비스의 결과를 받아 화면에 표시
// 만약 에러가 발생하면, 에러를 처리하거나 에러 핸들러에 전달

@Component ({
selector: 'app-data', 
template: `<div *ngIf="error">{{ error }}</div><div *ngIf="data">{{ data }}</div>`
})
…

ngOnInit() {
	this.dataService.fetchData().subscribe(
    	response => {
        	this.data = response;  
			// observable 성공. this.data에 fetchData데이터 있음
         }, error => {
        	this.error = `Failed to fetch data: ${error.message}`;
            // 여기서 에러를 다루거나, 다음 단계인 에러 핸들러로 전달
         } 
    ); 
}
// 에러 핸들러는 전체 애플리케이션의 에러를 관리하며, 미처 처리되지 않은 에러를 캐치하고 처리

import { ErrorHandler, Injectable } from '@angular/core';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
	handleError(error: any): void {
    // 모든 에러가 여기로 도착합니다. 원하는 로직을 추가하여 에러를 처리
    console.error('Global error handler caught:', error);
}}

// AppModule에서 사용하려면 다음과 같이 추가해야 합니다.
// providers: [{ provide: ErrorHandler, useClass: GlobalErrorHandler }]

 

2. 에러를 정리 후 다음 레이어에서 처리

  • 서비스 발생한 에러 컴포넌트에 내려줘서 컴포넌트서 처리
  • 에러를 정리해서 보기 쉽거나 처리하기 쉽게 다른 에러로 치환해서 전달
  • 빈 화면 alert등

** 서버로부터 온 401 에러를 UI 에서 필요한 정보만 추출해서  UI 쪽에 전달

    - 로그인 하도록 유도 -> 로그인 화면 보여주거나 로그인 필요하다는 메시지 UI 노출

 

 

3. 비동기 API통신에서의 에러 핸들링

  • API를 통해 통신할 때 상태 코드를 통해서 예외 상황 구분 (하나의 상태 코드에 다양한 상황이 있을 수 있음)
  • API 응답에 관해 에러코드, 메시지를 활용하여 사용자에게 가이드 또는 안내를 줄 수 있음
  • 빈 화면 alert등

** 잘못된 페이지나 없는 페이지 접근

    - 리다이렉트로 다른 페이지 이동

    - 사용자에게 접근할 수 없다는 의미 전달

 

 

보너스 체크포인트

자바스크립트와 타입스크립트를 쓰고 있는 지금 체크해보면 좋을 포인트 같아 메모

 

JS/TS 컴파일 환경 에러

1. 내부로부터 시작되는 에러

  • 컴파일 오류 : 문법을 잘못 작성하는 듯 코드가 컴파일 될 때 컴파일러가 해석하지 못해서 발생
  • 런타임 오류 : 프로그램이 동작할 때 발견할 수 있는 에러 쉽게 try catch 에서 잡히는 에러

** 자바스크립트에서는 런타임 에러를 예외라고 부릅니다.

  • 자바스크립트 언어는 동적 프로그래밍 언어이기 때문에 프로그램이 동작할 때(런타임)
    실시간으로 Type이 결정되어서 모든 에러가 컴파일 단계가 아닌 런타임 환경에서 발생
  • 최악의 경우 사용자가 어플리케이션을 사용할 때 에러가 발생
  • 따라서 컴파일 환경에서 타입에 관련된 에러를 잡을 수 있도록 도와주는 정적타입 언어인 TypeScript 사용
  • 타입스크립트만 쓰면 문제가 없어 라기보단 타입스크립트를 잘 활용하면 런타임 전에 오류 발견 가능

 

2. 런타임 에러 핸들링

// 에러 처리 코드를 미리 등록해 두고 에러가 발생하면 에러 처리 코드로 점프하도록 하는 방법

try { 
	console.log (‘try’) // 실행할 코드(에러가 발생할 가능성이 있는 코드) 
} catch(err) {
	 // try 코드 블록에서 에러가 발생하면 이 코드 블록의 코드가 실행된다. 
	console.error (‘error’) // err에는 try 코드 블록에서 발생한 Error 객체가 전달된다. 
} finally {
	console.log (‘finally’) // 에러 발생과 상관없이 반드시 한 번 실행된다.
}

 

try...catch...finally 조심하기
catch에 넘겨지는 error 객체의 타입을 보장할 수 없고, 
오류를 해결하는 것이 아닌 숨기는 것이라

코드의 가독성이 떨어지고 유지보수가 어렵다. 의미 있는 결과를 내보낼 수 없다면, 에러를 터트려 

추후에 모니터링 하는 것이 더 좋을 수도 있다.