728x90
반응형
1. 데이터 클래스 (Data Class) 📦
1.1 데이터 클래스란?
데이터 클래스는 데이터를 보관하고 전달하는 것이 주 목적인 클래스입니다. 일반적으로 다음과 같은 상황에서 사용됩니다:
- API 응답 데이터 모델링
// API 응답을 표현하는 데이터 클래스
data class UserResponse(
val id: Int,
val name: String,
val email: String,
val age: Int
)
- UI 상태 표현
// 화면에 표시할 사용자 정보를 담는 데이터 클래스
data class UserUiState(
val userName: String,
val userImage: String,
val followersCount: Int,
val isFollowing: Boolean
)
- 이벤트 전달
// 사용자 동작을 표현하는 데이터 클래스
data class UserEvent(
val eventType: String,
val timestamp: Long,
val metadata: Map<String, Any>
)
1.2 데이터 클래스의 특별한 기능
1) copy() 함수 활용
data class Product(
val id: Int,
val name: String,
val price: Double,
val isOnSale: Boolean = false
)
// 기존 상품의 세일 버전 생성
val originalProduct = Product(1, "노트북", 1000.0)
val saleProduct = originalProduct.copy(
price = 800.0,
isOnSale = true
)
2) 구조 분해 선언
data class Point(val x: Int, val y: Int, val z: Int)
val point = Point(10, 20, 30)
// 구조 분해로 각 값 추출
val (x, y, z) = poin
// 필요한 값만 추출
val (x, _, z) = point// y 값은 무시
1.3 실제 사용 예시
1) 네트워크 요청/응답
// 요청 데이터
data class LoginRequest(
val email: String,
val password: String,
val deviceId: String
)
// 응답 데이터
data class LoginResponse(
val token: String,
val user: UserInfo,
val expiresIn: Long
)
// 사용 예시
fun login(request: LoginRequest): LoginResponse {
return apiService.login(request)
}
2) 데이터베이스 엔티티
@Entity(tableName = "users")
data class UserEntity(
@PrimaryKey val id: Int,
val name: String,
val email: String,
val createdAt: Long = System.currentTimeMillis()
)
2. sealed 클래스 (Sealed Class) 🔒
2.1 sealed 클래스란?
sealed 클래스는 제한된 계층 구조를 만들 때 사용하는 클래스입니다. 주로 다음과 같은 상황에서 사용됩니다:
- 상태 관리
sealed class ScreenState {
object Loading : ScreenState()
data class Content(val data: List<Item>) : ScreenState()
data class Error(val message: String) : ScreenState()
object Empty : ScreenState()
}
class MainViewModel : ViewModel() {
private val _state = MutableStateFlow<ScreenState>(ScreenState.Loading)
fun loadData() {
viewModelScope.launch {
try {
val items = repository.getItems()
_state.value = if (items.isEmpty()) {
ScreenState.Empty
} else {
ScreenState.Content(items)
}
} catch (e: Exception) {
_state.value = ScreenState.Error(e.message ?: "Unknown error")
}
}
}
}
- 네트워크 결과 처리
sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(
val code: Int,
val message: String,
val throwable: Throwable? = null
) : NetworkResult<Nothing>()
object Loading : NetworkResult<Nothing>()
// 편의 함수 추가
fun isSuccess() = this is Success
fun isError() = this is Error
fun isLoading() = this is Loading
}
// 사용 예시
class UserRepository {
suspend fun getUser(id: String): NetworkResult<User> {
return try {
val response = api.getUser(id)
if (response.isSuccessful) {
NetworkResult.Success(response.body()!!)
} else {
NetworkResult.Error(response.code(), response.message())
}
} catch (e: Exception) {
NetworkResult.Error(500, "Network error", e)
}
}
}
- 이벤트 처리
sealed class NavigationEvent {
data class NavigateToDetail(val id: Int) : NavigationEvent()
object NavigateBack : NavigationEvent()
data class NavigateToWebView(val url: String) : NavigationEvent()
data class ShowDialog(val message: String) : NavigationEvent()
}
class NavigationManager {
fun handleNavigationEvent(event: NavigationEvent) {
when (event) {
is NavigationEvent.NavigateToDetail -> {
// 상세 화면으로 이동
}
is NavigationEvent.NavigateBack -> {
// 이전 화면으로 이동
}
is NavigationEvent.NavigateToWebView -> {
// 웹뷰로 이동
}
is NavigationEvent.ShowDialog -> {
// 다이얼로그 표시
}
}
}
}
2.2 실제 활용 사례
1) 결제 프로세스 상태 관리
sealed class PaymentState {
object Idle : PaymentState()
object Processing : PaymentState()
data class Success(
val transactionId: String,
val amount: Double
) : PaymentState()
sealed class Error : PaymentState() {
data class NetworkError(val message: String) : Error()
data class InsufficientFunds(val available: Double) : Error()
data class CardDeclined(val reason: String) : Error()
}
}
class PaymentViewModel : ViewModel() {
private val _paymentState = MutableStateFlow<PaymentState>(PaymentState.Idle)
fun processPayment(amount: Double) {
_paymentState.value = PaymentState.Processing
viewModelScope.launch {
try {
val result = paymentProcessor.process(amount)
_paymentState.value = PaymentState.Success(
result.transactionId,
amount
)
} catch (e: Exception) {
_paymentState.value = when (e) {
is NetworkException ->
PaymentState.Error.NetworkError(e.message)
is InsufficientFundsException ->
PaymentState.Error.InsufficientFunds(e.availableAmount)
is CardDeclinedException ->
PaymentState.Error.CardDeclined(e.reason)
else -> PaymentState.Error.NetworkError("Unknown error")
}
}
}
}
}
2) 폼 검증
sealed class ValidationResult {
object Valid : ValidationResult()
data class Invalid(val errors: List<ValidationError>) : ValidationResult()
}
sealed class ValidationError {
data class EmptyField(val fieldName: String) : ValidationError()
object InvalidEmail : ValidationError()
object PasswordTooShort : ValidationError()
object PasswordsDoNotMatch : ValidationError()
object TermsNotAccepted : ValidationError()
}
data class RegistrationForm(
val email: String,
val password: String,
val confirmPassword: String,
val termsAccepted: Boolean
)
fun validateRegistrationForm(form: RegistrationForm): ValidationResult {
val errors = mutableListOf<ValidationError>()
// 이메일 검증
if (form.email.isBlank()) {
errors.add(ValidationError.EmptyField("email"))
} else if (!form.email.contains("@")) {
errors.add(ValidationError.InvalidEmail)
}
// 비밀번호 검증
if (form.password.length < 8) {
errors.add(ValidationError.PasswordTooShort)
}
if (form.password != form.confirmPassword) {
errors.add(ValidationError.PasswordsDoNotMatch)
}
if (!form.termsAccepted) {
errors.add(ValidationError.TermsNotAccepted)
}
return if (errors.isEmpty()) {
ValidationResult.Valid
} else {
ValidationResult.Invalid(errors)
}
}
2.3 sealed 클래스 사용 시 장점
- 타입 안전성
- 모든 케이스를 컴파일 시점에 확인
- when 식에서 else 분기 불필요
- 유지보수성
- 새로운 케이스 추가 시 컴파일러가 누락된 처리 지점 알림
- 코드의 안정성 향상
- 코드 구조화
- 관련된 상태나 타입을 논리적으로 그룹화
- 코드의 가독성과 이해도 향상
- 데이터 무결성
- 제한된 상속 구조로 데이터 일관성 유지
- 예상치 못한 확장 방지

반응형
'Kotlin' 카테고리의 다른 글
코틀린 디자인 패턴과 아키텍처 (0) | 2024.11.26 |
---|---|
Kotlin(코틀린) FLow 란? (24) | 2024.11.21 |
코틀린이란? (Kotlin) (1) | 2024.11.19 |
QueryDSL 란, 주요 메서드 및 장단점 기본 설명 (0) | 2024.08.13 |
테스트 코드 작성 이유 및 방법 - SpringBoot, Kotlin (0) | 2024.08.09 |