Kotlin

코틀린 디자인 패턴과 아키텍처

code2772 2024. 11. 26. 08:16
728x90
반응형

1. 디자인 패턴이란? 🤔

1.1 쉬운 비유로 이해하기

디자인 패턴은 집을 지을 때 사용하는 설계도와 같습니다:

  • 검증된 방법으로 문제 해결
  • 유지보수가 쉬워짐
  • 다른 개발자들과 소통이 쉬워짐

예를 들어, 아파트를 지을 때 주방, 화장실, 거실의 위치를 계획하는 것처럼, 앱을 만들 때도 각 부분의 역할과 위치를 계획합니다.

2. MVVM 패턴 🏗️

2.1 MVVM이란?

쉽게 설명하면, 앱을 세 부분으로 나누는 방법입니다:

  • Model: 데이터
  • View: 화면
  • ViewModel: 데이터를 화면에 맞게 가공

2.2 실생활 비유

레스토랑으로 비유하면:

  • Model: 주방에서 요리하는 셰프 (데이터 처리)
  • View: 손님이 보는 메뉴판과 음식 (화면)
  • ViewModel: 주문을 받고 전달하는 웨이터 (중간 역할)

2.3 간단한 예제

// Model: 데이터 클래스
data class User(
    val name: String,
    val age: Int
)

// ViewModel: 데이터 처리
class UserViewModel : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user

    fun loadUser() {
// 사용자 정보 가져오기
        _user.value = User("홍길동", 25)
    }
}

// View: 화면 표시
class MainActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

// 데이터 변경 관찰
        viewModel.user.observe(this) { user ->
            nameTextView.text = user.name
            ageTextView.text = "${user.age}세"
        }
    }
}

 

3. Repository 패턴 📚

3.1 Repository란?

데이터를 저장하고 가져오는 방법을 한 곳에서 관리하는 패턴입니다.

3.2 실생활 비유

도서관으로 비유하면:

  • Repository: 도서관 사서
  • 데이터: 책
  • 앱: 책을 빌리는 사람

3.3 간단한 예제

// Repository 인터페이스
interface UserRepository {
    suspend fun getUser(id: String): User
    suspend fun saveUser(user: User)
}

// Repository 구현
class UserRepositoryImpl(
    private val api: UserApi,
    private val database: UserDatabase
) : UserRepository {
    override suspend fun getUser(id: String): User {
// 1. 먼저 로컬 데이터베이스 확인
        val localUser = database.getUser(id)
        if (localUser != null) {
            return localUser
        }

// 2. 없으면 서버에서 가져오기
        val remoteUser = api.fetchUser(id)

// 3. 데이터베이스에 저장
        database.saveUser(remoteUser)

        return remoteUser
    }
}

 

4. Factory 패턴 🏭

4.1 Factory란?

객체를 만드는 공장이라고 생각하면 됩니다.

4.2 실생활 비유

피자 가게로 비유하면:

  • Factory: 피자를 만드는 주방
  • 제품: 다양한 종류의 피자
  • 주문: 객체 생성 요청

4.3 간단한 예제

// 피자 인터페이스
interface Pizza {
    fun prepare()
    fun bake()
    fun cut()
}

// 구체적인 피자 클래스들
class CheesePizza : Pizza {
    override fun prepare() = println("치즈 피자 준비")
    override fun bake() = println("치즈 피자 굽기")
    override fun cut() = println("치즈 피자 자르기")
}

class PepperoniPizza : Pizza {
    override fun prepare() = println("페퍼로니 피자 준비")
    override fun bake() = println("페퍼로니 피자 굽기")
    override fun cut() = println("페퍼로니 피자 자르기")
}

// 피자 공장
class PizzaFactory {
    fun createPizza(type: String): Pizza {
        return when (type) {
            "cheese" -> CheesePizza()
            "pepperoni" -> PepperoniPizza()
            else -> throw IllegalArgumentException("알 수 없는 피자 종류")
        }
    }
}

 

5. Builder 패턴 🔨

5.1 Builder란?

복잡한 객체를 단계별로 만드는 방법입니다.

5.2 실생활 비유

레고 조립으로 비유하면:

  • Builder: 레고 조립 설명서
  • 부품: 객체의 속성들
  • 완성품: 만들어진 객체

5.3 간단한 예제

// 일반적인 방법
class User(
    val name: String,
    val age: Int,
    val email: String,
    val address: String,
    val phone: String
)

// Builder 패턴 사용
class UserBuilder {
    private var name: String = ""
    private var age: Int = 0
    private var email: String = ""
    private var address: String = ""
    private var phone: String = ""

    fun setName(name: String) = apply { this.name = name }
    fun setAge(age: Int) = apply { this.age = age }
    fun setEmail(email: String) = apply { this.email = email }
    fun setAddress(address: String) = apply { this.address = address }
    fun setPhone(phone: String) = apply { this.phone = phone }

    fun build() = User(name, age, email, address, phone)
}

// 사용 예시
val user = UserBuilder()
    .setName("홍길동")
    .setAge(25)
    .setEmail("hong@example.com")
    .build()

 

6. Clean Architecture 🏛️

6.1 Clean Architecture란?

앱을 여러 층으로 나누어 관리하는 방법입니다.

6.2 실생활 비유

백화점으로 비유하면:

  • UI 층: 진열대 (고객이 보는 부분)
  • Domain 층: 매장 관리자 (비즈니스 로직)
  • Data 층: 창고 (데이터 저장소)

6.3 간단한 예제

// Data Layer (데이터 층)
class UserRepositoryImpl : UserRepository {
    override suspend fun getUser(id: String): User {
        return api.getUser(id)
    }
}

// Domain Layer (비즈니스 로직 층)
class GetUserUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(id: String): User {
        return repository.getUser(id)
    }
}

// Presentation Layer (UI 층)
class UserViewModel(
    private val getUserUseCase: GetUserUseCase
) : ViewModel() {
    private val _user = MutableStateFlow<User?>(null)
    val user: StateFlow<User?> = _user.asStateFlow()

    fun loadUser(id: String) {
        viewModelScope.launch {
            _user.value = getUserUseCase(id)
        }
    }
}

디자인 패턴 선택 가이드 💡

언제 어떤 패턴을 사용할까?

  1. MVVM 패턴
    • 화면이 많은 앱
    • 데이터와 화면을 분리하고 싶을 때
    • Android 앱 개발시 권장
  2. Repository 패턴
    • 여러 데이터 소스를 사용할 때
    • 데이터 캐싱이 필요할 때
    • 오프라인 모드 지원시
  3. Factory 패턴
    • 비슷한 객체를 여러 개 만들 때
    • 객체 생성 로직을 분리하고 싶을 때
  4. Builder 패턴
    • 복잡한 객체를 만들 때
    • 선택적 매개변수가 많을 때
  5. Clean Architecture
    • 큰 규모의 앱
    • 테스트가 중요한 프로젝트
    • 유지보수가 중요한 경우

결론 ✨

디자인 패턴과 아키텍처는 앱을 더 잘 만들기 위한 도구입니다:

  1. 코드 구조화
    • 체계적인 코드 관리
    • 쉬운 유지보수
    • 명확한 책임 분리
  2. 팀 협업
    • 공통된 용어
    • 이해하기 쉬운 구조
    • 효율적인 소통
  3. 품질 향상
    • 버그 감소
    • 테스트 용이성
    • 확장성 개선
반응형