Result 패턴

허준호·2025년 6월 27일
0

dart 언어 공부

목록 보기
16/16
post-thumbnail

네트워크 요청 응답 상황

  • 성공
  • 실패(네트워크 연결 문제, 타임아웃, 요청 데이터 문제 등등)

try-catch 문

  • 기본적인 예외 처리이다
  • 런타임 에러뿐만 아니라 논리적인 오류나 예외 상황에 대한 처리를 하기에는 부족하다

Result 클래스

  • Result 클래스는 성공시에는 데이터를, 실패시에는 에러 정보(예: Exception, String)를 담는 객체를 정의한다
  • sealed 클래스는 타입 봉인 효과를 가진다 (enum 하고 비슷한 효과 + 다른 객체의 참조를 가질 수 있다)

사용법

Future<Result<List<Photo>>> fetch(String query);

기본 Result

sealed class Result<T> {
	factory Result.success(T data) = Success;
	factory Result.error(Exception e) = Error;
}

class Success<T> extends Result<T> {
	final T data;
	
	Success(this.data);
}

class Error<T> extends Result<T> {
	final Exception e;
	
	Error(this.e);
}

freezed를 사용한 Result

part 'result.freezed.dart'


sealed class Result<T> with _$Result<T> {
	const factory Result.success(T data) = Success;
	const factory Result.error(Exception e) = Error;
}

// 장점: 코드를 간단하게 작성할 수 있다

원하는 에러 타입 정의

part 'result.freezed.dart'


sealed class Result<D, E> with _$Result<D, E> {
	const factory Result.success(D data) = Success;
	const factory Result.error(E e) = Error;
}
// D는 데이터 타입, E는 에러 타입

enum을 사용하여 에러 타입 정의

enum NetworkError {
	requestTimeOut,
	unknown,
}

void main() async {
	final repository = PhotoRepositoryImpl(PhotoApiImpl())
	
	final result = await repository.getPhotos();
	
	switch(result) {
		case Success<List<Photo>, NetworkError>():
			print(result.data);
		case Error<List<Photo>, NetworkError>():
			switch(result.error) {
				case .requestTimeOut:
					print('타임아웃')
				case .unknown:
					print('알 수 없는 에러')
			}
	}
}

장점

  • enum과 동일하게 switch문과 조합하여 모든 처리를 강제할 수 있다
  • 타입 안정성이 향상된다
  • 에러 처리가 강제된다 (컴파일러가 모든 케이스 처리를 강제함)
  • 에러 타입의 명확한 문서화가 가능하다
  • try-catch문 사용을 줄일 수 있다
  • 옵셔널(Nullable) 타입 사용이 감소된다
  • 비즈니스 로직과 에러처리 로직을 분리할 수 있다
  • 케이스별 테스트가 용이하다
profile
후추랑 소금 좋아하는 개발자

0개의 댓글