Fin→FinResponse 브릿지
기존 Fin<T> 기반 코드와 새로운 FinResponse<T>를 어떻게 연결할까요? Repository 계층은 Fin<T>를 반환하고, Usecase 계층은 FinResponse<T>를 반환합니다. 이 두 계층을 연결하는 브릿지가 ToFinResponse() 확장 메서드입니다. 이 장에서는 다양한 변환 오버로드와 사용 시나리오를 학습합니다.
계층 간 타입 흐름:
Repository Layer Usecase Layer───────────────── ─────────────────Fin<Product> ──→ FinResponse<Product> 직접 변환Fin<Product> ──→ FinResponse<ProductDto> 매퍼 변환Fin<Unit> ──→ FinResponse<string> 팩토리 변환Fin<T> (Fail) ──→ FinResponse<T> (Fail) 실패 전파Fin<int> ──→ FinResponse<string> 커스텀 변환학습 목표
섹션 제목: “학습 목표”이 장을 완료하면 다음을 할 수 있습니다:
- Repository(
Fin<T>)와 Usecase(FinResponse<T>) 계층 간 변환이 필요한 이유를 설명할 수 있습니다 - 상황에 맞는
ToFinResponse()오버로드를 선택할 수 있습니다 - 실패 상태가 변환 시 자동으로 전파되는 메커니즘을 이해할 수 있습니다
핵심 개념
섹션 제목: “핵심 개념”1. 왜 브릿지가 필요한가?
섹션 제목: “1. 왜 브릿지가 필요한가?”Fin<T>: LanguageExt의 Result 타입. sealed struct이므로 제약 조건으로 사용 불가.FinResponse<T>: IFinResponse 인터페이스 계층을 구현한 Discriminated Union. Pipeline 제약에 사용 가능.
Repository가 반환하는 Fin<T>를 Usecase의 FinResponse<T>로 변환해야 Pipeline 체인에서 타입 안전하게 처리할 수 있습니다.
2. 직접 변환: Fin<A> -> FinResponse<A>
섹션 제목: “2. 직접 변환: Fin<A> -> FinResponse<A>”가장 단순한 변환입니다. 성공 값의 타입이 동일할 때 사용합니다.
Fin<string> fin = Fin<string>.Succ("Hello");FinResponse<string> response = fin.ToFinResponse();3. 매퍼 변환: Fin<A> -> FinResponse<B>
섹션 제목: “3. 매퍼 변환: Fin<A> -> FinResponse<B>”성공 값의 타입을 변환할 때 사용합니다. 예: Entity -> DTO
Fin<string> fin = Fin<string>.Succ("Hello");FinResponse<int> response = fin.ToFinResponse(s => s.Length);4. 팩토리 변환: Fin<A> -> FinResponse<B>
섹션 제목: “4. 팩토리 변환: Fin<A> -> FinResponse<B>”원본 성공 값을 무시하고 새로운 값을 생성할 때 사용합니다. 예: Fin<Unit> -> FinResponse<string>
Fin<Unit> fin = Fin<Unit>.Succ(Unit.Default);FinResponse<string> response = fin.ToFinResponse(() => "Deleted successfully");5. 실패 전파
섹션 제목: “5. 실패 전파”Fin이 실패 상태이면, 변환 방식에 관계없이 Error가 그대로 FinResponse의 Fail로 전파됩니다.
Fin<string> fin = Fin<string>.Fail(Error.New("not found"));FinResponse<string> response = fin.ToFinResponse();// response.IsFail == true6. 커스텀 변환: Fin<A> -> FinResponse<B> (onSucc/onFail)
섹션 제목: “6. 커스텀 변환: Fin<A> -> FinResponse<B> (onSucc/onFail)”성공과 실패 모두에 대해 커스텀 처리가 필요한 경우 사용합니다. 예: 성공 값을 다른 타입으로 변환하면서, 실패 시에도 별도 에러 처리를 적용하는 경우.
Fin<int> fin = Fin.Succ(42);FinResponse<string> response = fin.ToFinResponse( onSucc: value => FinResponse.Succ($"Value is {value}"), onFail: error => FinResponse.Fail<string>(error));이 오버로드는 Fin의 Match와 동일한 구조이므로 가장 유연하지만, 대부분의 경우 직접/매퍼/팩토리 변환으로 충분합니다.
7. 변환 오버로드 정리
섹션 제목: “7. 변환 오버로드 정리”ToFinResponse()가 제공하는 변환 오버로드를 정리하면 다음과 같습니다.
| 오버로드 | 시그니처 | 용도 |
|---|---|---|
| 직접 변환 | Fin<A>.ToFinResponse() | 동일 타입 변환 |
| 매퍼 변환 | Fin<A>.ToFinResponse(Func<A, B>) | Entity -> DTO |
| 팩토리 변환 | Fin<A>.ToFinResponse(Func<B>) | Unit -> Response |
| 커스텀 변환 | Fin<A>.ToFinResponse(Func<A, FinResponse<B>>, Func<Error, FinResponse<B>>) | 완전 제어 |
FAQ
섹션 제목: “FAQ”Q1: Repository가 Fin<T>를 반환하고 Usecase가 FinResponse<T>를 반환하는 이유는 무엇인가요?
섹션 제목: “Q1: Repository가 Fin<T>를 반환하고 Usecase가 FinResponse<T>를 반환하는 이유는 무엇인가요?”A: Repository는 LanguageExt의 순수 함수형 타입인 Fin<T>를 사용하여 외부 라이브러리 의존 없이 성공/실패를 표현합니다. Usecase는 Pipeline 제약에 사용 가능한 FinResponse<T>를 반환해야 합니다. ToFinResponse()가 이 두 계층을 연결합니다.
Q2: 매퍼 변환과 팩토리 변환은 각각 어떤 상황에서 사용하나요?
섹션 제목: “Q2: 매퍼 변환과 팩토리 변환은 각각 어떤 상황에서 사용하나요?”A: 매퍼 변환(Func<A, B>)은 Entity를 DTO로 변환할 때 사용합니다. 예: fin.ToFinResponse(product => new ProductDto(product)). 팩토리 변환(Func<B>)은 원본 값을 무시하고 새로운 값을 생성할 때 사용합니다. 예: Fin<Unit> 반환을 FinResponse<string>의 “삭제 성공” 메시지로 변환.
Q3: ToFinResponse()에서 실패가 자동 전파되는 원리는 무엇인가요?
섹션 제목: “Q3: ToFinResponse()에서 실패가 자동 전파되는 원리는 무엇인가요?”A: ToFinResponse()는 내부적으로 Fin<T>의 Match를 호출합니다. Succ이면 변환 함수를 적용하고, Fail이면 변환 함수를 호출하지 않고 Error를 그대로 FinResponse.Fail로 전달합니다. 어떤 오버로드를 사용하든 실패 시 동일하게 동작합니다.
프로젝트 구조
섹션 제목: “프로젝트 구조”04-Fin-To-FinResponse-Bridge/├── FinToFinResponseBridge/│ ├── FinToFinResponseBridge.csproj│ ├── BridgeExamples.cs│ └── Program.cs├── FinToFinResponseBridge.Tests.Unit/│ ├── FinToFinResponseBridge.Tests.Unit.csproj│ ├── xunit.runner.json│ └── FinToFinResponseBridgeTests.cs└── README.md실행 방법
섹션 제목: “실행 방법”# 프로그램 실행dotnet run --project FinToFinResponseBridge
# 테스트 실행dotnet test --project FinToFinResponseBridge.Tests.UnitPipeline 제약 패턴이 완성되었습니다. Nested class 패턴으로 Request/Response/Validator/Handler를 구성하는 Command Usecase의 완전한 구현 예제를 작성합니다.