[kotlin][MVC] kotlin에서 MVC를 적용

Boknami·2023년 9월 17일
0

디자인 패턴

목록 보기
1/2

개발을 하는데 있어서 이미 검증이 된 여러 가지 디자인 패턴들이 존재한다. MVC, MVP, MVVM 등..최근에는 MVVM을 많이 사용하고 있다고 한다.

그 중 MVC패턴은 Model - View - Controller를 이용하여 데이터 간의 이동이나 사용자에게 어떻게 화면을 보여주고 그것들을 컨트롤하는 방법에 대해 영역을 분할하고 각 구성 요소에게 역할을 부여하는 개발 방식이다.

🙄 Kotlin에서는 이미 MVC가 적용?

처음 생각하기로 안드로이드 개발 체계는 이미 MVC를 적용한 것 아닌가라는 생각을 했다.

  • Activity > Model, Controller
  • xml > View

그런데 이건 내가 MVC를 적절하게 적용하지 못한 것이다.

  • Model > DB같은 데이터
  • Controller > View에서 입력 받은 사용자의 요구(이벤트)를 컨트롤(처리)하고 model값을 갱신
  • xml > View

이 자체를 이해하긴 하였으나, Model같은 경우 일반적으로 안드로이드 개발환경에서 모두 만드는 경우는 잘 없을 것이다. 보통 API통신을 통해서 백엔드에 저장된 정보를 가져오는 것이 일반적일텐데 이러한 부분들에 대해서 자세히 살펴보았다.

💻 직접 코드로 만들어서 이해하자!

가정 상황 : 사용자에 대한 정보를 서버와 통신해서 사용자에게 보여주는

Model

package com.example.pra_mvc

data class User(
    val id: Int, val name: String
)

모델에 대한 데이터 클래스를 하나 만들었다.
통신을 통해 데이터를 받은 후 이 클래스를 이용해 값을 저장하고 View에 보여줄 것이다.
추가적인 모델 부분은 Controller에서도 있다.

🚨 모델을 뷰를 몰라야한다 이 점을 주의하며 작성

View

`<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/txt_title"
        android:text="MVC TEST"
        android:textSize="44dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.276" />

    <TextView
        android:id="@+id/txt_ID"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="유저 ID : "
        android:textSize="36dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.298"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.435" />

    <TextView
        android:id="@+id/txt_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="유저 이름 : "
        android:textSize="36dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.338"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.579" />
</androidx.constraintlayout.widget.ConstraintLayout>`

xml은 크게 어려운 것 없이 데이터 클래스가 가진 값들을 잘 보여주기 위해서 id값만 잘 조정해주었다.

Controller

MVC에서 가장 많은 역할을 하는 컨트롤러!


class MainActivity : AppCompatActivity() {
    private lateinit var usernameTextView: TextView
    private lateinit var userIDTextView: TextView

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

        // View에 선언되있는 값이랑 매칭(초기화해두기)
        usernameTextView = findViewById(R.id.txt_name)
        userIDTextView = findViewById(R.id.txt_ID)


        // Model 생성
        val user = User(id = 1, name = "근재")

        // Model이 가진 데이터를 View에 설정해주자
        usernameTextView.text = "유저의 이름 :  ${user.name}!"
        userIDTextView.text = "유저의 ID : ${user.id}}"
    }
}

Controller에서 Model을 만들어도 돼?

컨트롤러는 Model, View를 다 아는 것이 맞지만, 이렇게 만들고 나니 조금 걸렸던 부분은 컨트롤러에서 직접 Model을 만드는 부분이었다. 뭔가 분리가 더 되어서 결합도는 조금 더 낮출 수 있다면 좋겠다는 느낌을 받았고 개선을 해보았다.

🔎 MVC 업그레이드

View나 모델 클래스가 존재하는 것은 그대로이나 여기서 더욱 커플링을 낮추기 위해서 노력해보자!

목표는 컨트롤러에서 직접적으로 API를 부르는 코드가 들어가있는 것은 애초에 보기에도 안 좋거니와 커플링은 증가시키는 행동이다. 우리는 이를 분리할 필요가 있다!

#Controller와 Model 사이의 결합도를 낮추는 것의 이점.

  • 코드를 이해하기 쉽고 테스트하기 쉬워진다
  • 코드 중복을 피하고 재사용성을 높일 수 있음
  • 코드의 유지 및 확장성을 개선하는 데 도움

이러한 이유들에 있어 충분히 코드를 개선할 이유는 존재!
우리는 의존성 주입(Dependency Injection)을 이용하여 개선할 것이다!

🏸 업그레이드 과정

  1. 기존 모델 클래스는 유지
data class User(
    val id: Int, val name: String
)

  1. UserModel이라는 인터페이스를 생성!
interface UserModel {
    fun getUser(): User
}

이 곳에서는 Model관련 데이터를 받고, 처리할 수 있는 함수들을 넣어두면 된다.
굳이 인터페이스를 만드는 이유는 UserModel 인터페이스가 Model을 추상화할 수 있다는 점이다!
이 점이 이해가 잘 안될 수 있는데 컴공을 전공한 사람이라면

class 도형{
	fun 치수제기
}

class 사각형 : 도형 {

}

이런 식으로 가장 일반적이고 틀이 될 수 있는 클래스를 하나 만들고 나머지는 이를 상속받아서 기본적인 특성을 유지하면서 확장?하는 형태로 한다는 것을 자바나 C++에서 배울 것이다. 이를 생각하면 이러한 행동을 하는 것이 납득이 조금 더 잘 갈 것 같다. 아 물론 클래스를 이용하여 다중 상속을 할 경우 메소드 출처의 모호성을 문제로 자바(코틀린)에서는 클래스를 통한 다중 상속은 지원하지 않기에 해당 코드에서는 인터페이스로 두었다!


  1. 의존성 주입을 위한 UserModelImpl
class UserModelImpl : UserModel {
    override fun getUser(): User {
        // 데이터를 생성하거나 가져오는 로직을 구현
        // 보통은 백엔드와 통신을 해서 데이터를 가져오는 부분을 구현할 것이다!
        return User(id = 1, name = "근자")
    }
}

의존성 주입을 위한 클래스로 우리가 만들어둔 인터페이스를 상속 받아서 만들어진다!
이 부분에서 인터페이스에서 만들어둔 getUser를 실제로 구현하면 된다!


  1. 컨트롤러에서 직접 Model 생성하지 않도록!
class MainActivity : AppCompatActivity() {
    private lateinit var usernameTextView: TextView
    private lateinit var userIDTextView: TextView

    private lateinit var userModel: UserModel//유저 모델 

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

        // View에 선언되있는 값이랑 매칭(초기화해두기)
        usernameTextView = findViewById(R.id.txt_name)
        userIDTextView = findViewById(R.id.txt_ID)

        // UserModel 구현체를 생성 또는 !주입!받을거다
        userModel = UserModelImpl()

        // Model에 있는 데이터를 가져와서
        val user = userModel.getUser()

        // Model이 가진 데이터를 View에 설정해주자
        usernameTextView.text = "유저의 이름 :  ${user.name}!"
        userIDTextView.text = "유저의 ID : ${user.id}"
    }
}

이렇게 하면 의존성 주입을 정상적으로 진행할 수 있다!!!

👍 결과

0개의 댓글