dependency-injection

2 개의 포스트

코드 품질 개선 기법 27편: 티끌이 모여 태산이 되듯 의존성도 쌓이면 (새 탭에서 열림)

의존성 주입(Dependency Injection)은 코드의 유연성을 높이는 강력한 도구이지만, 명확한 목적 없이 모든 요소를 주입 대상으로 삼는 것은 오히려 코드 복잡도를 높이고 유지보수를 어렵게 만듭니다. 참조 투명한 유틸리티나 단순한 모델 클래스까지 외부에서 주입받기보다는, 복잡도가 낮거나 변경 가능성이 희박한 객체는 내부에서 직접 생성하는 것이 더 효율적일 수 있습니다. 따라서 의존성을 주입할 때는 객체의 라이프사이클 관리, 구현체 전환, 테스트 용이성 등 구체적인 목적이 있는지 먼저 검토해야 합니다. **불필요한 의존성 주입의 사례와 개선** * **과도한 주입의 예시**: 뉴스 기사 스니펫을 생성하는 `LatestNewsSnippetUseCase` 클래스에서 데이터 모델인 `NewsSnippet`의 팩토리 함수나, 단순한 문자열 포매터인 `StringTruncator`까지 생성자로 주입받는 경우입니다. * **개선 방향**: 상태를 갖지 않는 단순한 유틸리티 구현체나 데이터 모델의 생성자는 클래스 내부에서 직접 호출하도록 변경합니다. * **단순화 결과**: 환경에 따라 달라지는 값(Locale)이나 네트워크 통신 등 복잡한 로직을 가진 리포지터리만 주입 대상으로 남겨 코드를 더 간결하게 유지할 수 있습니다. **의존성 주입이 필요한 명확한 목적** * **라이프사이클 및 범위 관리**: 객체의 상태를 공유해야 하거나, 사용하는 객체보다 더 긴 수명을 가진 객체를 활용해야 할 때 주입을 사용합니다. * **의존성 역전(DIP)**: 모듈 간의 순환 의존성을 해결하거나 아키텍처에서 정의한 계층 간 의존 방향을 준수하기 위해 필요합니다. * **구현체 전환 및 분리**: 테스트나 디버깅을 위해 Mock 객체로 교체해야 하는 경우, 혹은 빌드 속도 향상을 위해 독점 라이브러리를 분리해야 할 때 유용합니다. **무분별한 주입이 초래하는 문제점** * **추적의 어려움**: 인터페이스와 주입이 남발되면 특정 로직의 실제 동작을 확인하기 위해 생성자의 호출자를 거꾸로 추적해야 하는 수고가 발생합니다. * **호출자 책임 전가**: 하위 클래스의 모든 의존성을 상위 호출자가 해결해야 하므로, 의존성 해결이 연쇄적으로 전달되어 메인 클래스에 과도한 책임이 집중됩니다. * **연관 데이터의 일관성 파괴**: 예를 들어 동일한 `Locale` 값을 사용해야 하는 여러 객체를 각각 주입받을 경우, 실수로 서로 다른 로케일이 전달되어도 컴파일 타임에 이를 감지하기 어렵고 테스트 작성이 까다로워집니다. 의존성 주입은 '할 수 있기 때문'이 아니라 '필요하기 때문'에 수행해야 합니다. 복잡한 비즈니스 로직이나 외부 시스템 의존성은 주입을 통해 유연성을 확보하되, 단순한 값 객체(Value Object)나 유틸리티는 직접 인스턴스화하여 코드의 명확성을 높이는 것을 권장합니다.

Flutter Riverpod 200% 활용하기 (새 탭에서 열림)

Riverpod은 기존 Provider 라이브러리의 한계를 극복하고 개발자가 더욱 직관적이고 유연하게 상태를 관리할 수 있도록 설계된 Flutter 상태 관리 라이브러리입니다. 서버 데이터 처리 최적화, 자동 생명 주기 관리, 의존성 주입 등 강력한 기능을 기본으로 제공하여 코드의 복잡성을 대폭 낮춰줍니다. 결과적으로 개발 생산성을 높이고, 성능 저하나 디버깅 오류와 같은 상태 관리의 고질적인 문제들을 효과적으로 해결해 줍니다. **서버 데이터 처리 및 상태 관리 최적화** * 서버에서 데이터를 가져올 때 필수적인 로딩, 에러, 데이터 유무 상태를 별도 로직 없이 기본적으로 제공합니다. * API 호출 도중 해당 데이터가 더 이상 필요하지 않게 되면 요청을 자동으로 취소하거나, 데이터의 유효 기간 및 재사용 설정을 손쉽게 관리할 수 있습니다. * '당겨서 새로 고침(pull to refresh)'과 같은 빈번한 UI 패턴을 `ref.refresh` 기능을 통해 간결하게 구현할 수 있어 반복적인 코드 작성을 줄여줍니다. **자유로운 의존성 주입과 자동 생명 주기 관리** * 위젯 계층 구조에 묶이지 않고 어디서든 Provider에 접근할 수 있어, 복잡한 순서나 구조를 신경 쓰지 않고 데이터를 참조할 수 있습니다. * Riverpod이 Provider의 생성과 소멸 시점을 자동으로 관리하므로 메모리 누수 방지와 같은 자원 관리가 용이합니다. * 개발자는 상태를 정의하는 Model과 이를 관리하는 Provider, 데이터를 소비하는 View를 명확히 분리하여 깔끔한 아키텍처를 유지할 수 있습니다. **효율적인 데이터 연동 및 캐시 활용 기법** * **Provider 간 상태 구독:** 필터 조건이 변경되면 이를 구독 중인 목록 Provider가 자동으로 데이터를 새로 불러오도록 설정할 수 있어 수동 리빌드 로직이 필요 없습니다. * **즉각적인 사용자 경험 제공:** 상세 화면 이동 시 목록에서 미리 불러온 데이터를 즉시 노출하고, 동시에 서버에서 추가 정보를 가져오는 방식으로 로딩 지연을 최소화합니다. * **오프라인 데이터 결합:** 로컬 DB 데이터와 서버 데이터를 유연하게 결합하여 네트워크가 불안정한 환경에서도 사용자에게 끊김 없는 인터페이스를 제공할 수 있습니다. * **화면 간 동기화:** 상세 화면에서 수정된 즐겨찾기 상태나 업데이트 내역이 목록 화면에도 즉각 반영되도록 구현하여 앱 전반의 데이터 일관성을 보장합니다. Riverpod은 단순히 상태를 저장하는 도구를 넘어, 비동기 프로그래밍과 의존성 관리를 우아하게 해결해 주는 솔루션입니다. 특히 서버 통신이 많고 화면 간 데이터 동기화가 복잡한 앱을 개발할 때, Riverpod의 선언적인 코드 스타일과 리액티브한 특징을 활용하면 유지 보수성이 뛰어난 코드를 작성할 수 있습니다.