카테고리 없음

TDD로 MVP 개발하기

growdai1y 2025. 1. 21. 23:22

회사에서 MVP 패턴을 적용하려고 하면 많은 개발자들이 어려움을 겪는 경우를 자주 봤습니다. 특히, UI와 비즈니스 로직을 분리하는 것이 처음엔 복잡하고 부담스럽게 느껴지곤 했죠. 저도 처음엔 막막했지만, 페어 프로그래밍을 통해 Presenter를 먼저 테스트하면서 개발하는 TDD 방식을 도입해 큰 변화를 경험했습니다. Presenter를 먼저 설계하고 검증하는 과정이 반복되면서 UI 개발은 단순한 작업으로 바뀌었고, 전체적인 유지보수도 훨씬 수월해졌습니다.

이 경험을 통해 TDD가 MVP 개발에서 얼마나 효율적인 방법인지 깨닫게 되었고, 오늘은 여러분께 그 구체적인 접근 방법과 장단점을 소개하려 합니다.

TDD로 Presenter 퍼스트
TDD로 Presenter 퍼스트


TDD와 MVP의 결합

MVP 패턴의 핵심: Presenter

MVP(Model-View-Presenter) 패턴은 애플리케이션 로직과 UI를 분리하여 유지보수성과 확장성을 높이는 데 중점을 둡니다. 이 패턴에서 Presenter는 핵심적인 역할을 맡습니다. Presenter는 데이터를 처리하여 UI(View)에 전달하고, UI에서 발생하는 사용자 입력을 모델(Model)에 전달하는 중재자 역할을 합니다.

TDD(Test-Driven Development)를 적용하면 Presenter를 먼저 설계하고 검증하는 과정이 가능해집니다. 이렇게 하면 UI를 개발하기 전에도 주요 기능을 완성할 수 있습니다. Presenter가 로직을 책임지고 있으니, UI 개발자는 화면을 꾸미는 작업에만 집중할 수 있죠.

TDD로 Presenter 개발하기

Presenter를 TDD로 개발하려면 몇 가지 단계를 거쳐야 합니다. 첫째, Presenter가 수행해야 할 주요 작업을 테스트로 정의합니다. 예를 들어, "사용자 데이터를 로드하고 필터링한다" 같은 구체적인 목표를 설정합니다. 그런 다음, 이 테스트를 통과하도록 Presenter 로직을 구현합니다. 마지막으로, 추가적인 테스트를 작성하고 Presenter를 점진적으로 개선해 나갑니다.


사례: 쇼핑몰 앱의 상품 목록 페이지 개발

테스트 작성

Presenter가 상품 데이터를 로드하고, 카테고리별로 필터링하는 기능을 담당한다고 가정합니다. 이를 테스트 코드로 작성해 보겠습니다.

// 테스트 예제
class ProductListPresenterTest {

    private lateinit var presenter: ProductListPresenter
    private val mockView: ProductListView = mockk(relaxed = true)
    private val mockRepository: ProductRepository = mockk()

    @Before
    fun setUp() {
        presenter = ProductListPresenter(mockView, mockRepository)
    }

    @Test
    fun `should load products and show them on view`() {
        // Arrange
        val products = listOf(Product("Product1"), Product("Product2"))
        every { mockRepository.getProducts() } returns products

        // Act
        presenter.loadProducts()

        // Assert
        verify { mockView.displayProducts(products) }
    }

    @Test
    fun `should filter products by category`() {
        // Arrange
        val products = listOf(
            Product("Product1", category = "Electronics"),
            Product("Product2", category = "Books")
        )
        every { mockRepository.getProducts() } returns products

        // Act
        presenter.filterProducts("Electronics")

        // Assert
        verify { mockView.displayProducts(listOf(products[0])) }
    }
}

Presenter 구현

테스트를 기반으로 Presenter를 구현합니다. Presenter는 모델에서 데이터를 가져와 필터링하고, 이를 View에 전달하는 역할을 합니다.

// Presenter 구현 예제
class ProductListPresenter(
    private val view: ProductListView,
    private val repository: ProductRepository
) {

    fun loadProducts() {
        val products = repository.getProducts()
        view.displayProducts(products)
    }

    fun filterProducts(category: String) {
        val filteredProducts = repository.getProducts().filter {
            it.category == category
        }
        view.displayProducts(filteredProducts)
    }
}

UI 개발

Presenter에서 데이터를 받아 UI를 구성합니다. RecyclerView와 같은 컴포넌트를 활용해 데이터를 화면에 표시합니다.

// View 및 Activity 예제
class ProductListActivity : AppCompatActivity(), ProductListView {

    private lateinit var presenter: ProductListPresenter
    private lateinit var adapter: ProductAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_product_list)

        val repository = ProductRepositoryImpl()
        presenter = ProductListPresenter(this, repository)

        adapter = ProductAdapter()
        recyclerView.adapter = adapter

        presenter.loadProducts()
    }

    override fun displayProducts(products: List<Product>) {
        adapter.submitList(products)
    }
}

interface ProductListView {
    fun displayProducts(products: List<Product>)
}

TDD와 Presenter 개발의 장점

Presenter를 먼저 개발하면 로직과 UI를 철저히 분리할 수 있습니다. UI는 오직 데이터를 화면에 표시하는 데 집중할 수 있고, Presenter는 비즈니스 로직을 책임지기 때문에 구조가 단순하고 유지보수도 쉬워집니다. 더불어, TDD로 개발된 Presenter는 독립적으로 테스트 가능하기 때문에 기능의 정확성을 보장할 수 있습니다. UI가 변경되더라도 Presenter는 그대로 재사용할 수 있어 개발 속도와 효율성이 높아집니다.


TDD와 Presenter 개발의 단점

하지만 모든 것이 완벽하지는 않습니다. 처음 TDD와 MVP 패턴을 도입하려면 설정 과정이 복잡하고, 테스트 작성에 익숙하지 않다면 시간이 더 걸릴 수 있습니다. 작은 프로젝트에서는 이런 구조가 오히려 불필요하게 복잡하게 느껴질 수도 있죠. 또한, 팀원들이 TDD나 MVP에 익숙하지 않다면 학습이 필요하고, 그동안 생산성이 일시적으로 낮아질 수 있습니다.


이론적 근거와 참고 자료

로버트 C. 마틴의 "클린 아키텍처"와 켄트 벡의 "Test-Driven Development: By Example"은 TDD와 구조적 개발의 중요성을 강조합니다. 이 책들은 TDD가 코드를 더 간결하고 효율적으로 만드는 방법을 실질적으로 알려줍니다.


결론

TDD와 MVP 패턴의 결합은 복잡한 UI 개발을 단순화하고, 유지보수성과 테스트 가능성을 극대화하는 방법입니다. Presenter를 먼저 설계하고 검증하는 방식은 UI 개발의 부담을 줄이고, 팀 전체의 생산성을 높입니다. 여러분도 이 방식을 프로젝트에 적용해 보면 개발의 새로운 차원을 경험할 수 있을 것입니다.

반응형