Error State 사용해보기

조민호·2023년 1월 22일
0

예를 들어서 DB와 연결을 하고 유저정보를 저장하는 saveUser메소드가 repository 계층에 있다고 칩시다
async saveUser(user: User) {
	await  dataSource.manager.save(someUser);
}

그리고 service계층에서 repository계층의 saveUser메소드를 가져다 사용하는 경우

saveUser메서드의 리턴타입은 새로운 타입을 정의한뒤 해당 타입을 리턴하는 것이 좋습니다


type NetworkErrorState = {
  result: 'fail';
  reason: 'offline' | 'down' | 'timeout';
};

type SuccessState = {
  result: 'success';
};

type ResultState = SuccessState | NetworkErrorState; // Error State

												
async saveUser(user: User): ResultState {// Error State를 리턴

	 try {
    await dataSource.manager.save(user)

    return { result: 'success' } **// 성공한 상태 리턴**

	 } catch(err) {

    return { result: 'fail', msg: '..' } **// 실패한 상태 리턴**

}

// 외부 or 상위 레벨에서 saveUser()를 사용하는 부분에는 
// try catch를 사용 할 필요가 없다
// 그냥 ResultState만 리턴받아서 if문을 통해 로직을 진행


💡 saveUser()의 로직에서 try catch를 쓴 것은 한 단계 더 깊은 레벨인, dataSource.manager.save(user) 메소드의 로직에서 예상치 못한 에러를 throw하고 있기 때문입니다

로직을 실행할 때 그 결과는 3가지로 분류가 됩니다

  • Happy Path 성공한 경우
  • Expected Errors 혹은 Unhappy Path 예상하는 실패적인 케이스 (예: 사용자가 잘못된 비밀번호를 입력해서 로그인 실패 )
  • Exception, 혹은 Exceptional Errors 예상하지 않는, 대게 일어 나지 않는 케이스의 에러. 어플리케이션에서 예상하지 않는, 예외적으로 발생하는 에러 (예: 네트워크 오류, 메모리 부족, 서버 다운 등)

어플리케이션에서 예상할 수 있는 Expected Errors에 대해서는 가급적 exception (throw error) 에러를 던지기 보다는, 에러 상태를 나타낼 수 있는 타입을 사용하는것이 좋습니다



  1. 하위 레벨에서(Controller나 Repository) 단에서는 try-catch를 이용해서 exception 처리를 해주고
  2. 최대한 어플리케이션 레벨에서 사용하는 service 레벨에서는 ErrorState로 감싸주는 것입니다

async function saveUser(user: User)

여기서 실패했을때 갑작스럽게, 예외적으로 에러 throw해서 ,

service 레벨에서 사용하는 사람이 알아서 try-catch를 사용하게 만드는것 보다는

async function saveUser(user: User)
: SuccessState | NetworkErrorState

이렇게 성공할수 있고, 실패할 수 있다는 설명을 해주는게 좋습니다

ResultState와 같은 에러의 상태는 saveUser 같은 서비스를 사용하는

어플리케이션 service레벨에서 나의 서비스를 사용하는 대상이

적절하게 대응을 하고자 하는 에러들을 알 수 있도록, `어떤 에러 상태

인지 알 수 있게` 사용자 입맛에 맞게 만들어 주는게 좋습니다


async saveUser(user: User): User | undefined

단순히 성공했는지, 실패했는지만 궁금하다면, 별도의 ResultState 타입

을 만들 필요 없이, 성공했다면 저장된 user를 반환하고 그렇지 않다면

undefined을 반환하는것으로 함수의 인터페이스를 만들어도 됩니다

여기서 왜 저장하지 못했는지 이유를 알고 싶다면 위처럼 ResultState 같은 타입을 도입하면 되는 것입니다



여기서 중요한 것은,

무조건적으로 throw를 지양하자! (X)

예상하지 못한 Exceptional Errors에만 throw를 사용하자! (O)

Expected Errors에서는 무조건 State를 사용하자! (X)

사용하는자의 관심도와 필요에 따라서, State를 사용하면 된다 (O)
( 위처럼 단순히 성공했는지, 실패했는지만 궁금하다면
User | undefined를 리턴해도 됨 )

profile
웰시코기발바닥

0개의 댓글