본문 바로가기

Kotlin/안전성, 가독성을 효과적인 향상시키는 사용법

👋Kotlin : 1️⃣가변성을 제한하라

728x90

코틀린은 2010년에 처음 개발되었지만 2016년 2월에 첫 번째 안정 버전(stable version)이 공식적으로 배포되었을 정도로 굉장히 오랜 시간 동안 만들어지고 있고, 처음부터 대규모 애플리케이션을 실용적으로 만들기 위한 프로그래밍 언어로 설계되었고 현재는 모바일 애플리케이션, 웹 애플리케이션의 백엔드, 웹 애플리케이션의 프론트엔드 등 다양한 영역에서 활용되고 있습니다.

 

1️⃣ 가변성 제한

var 프로퍼티 또는 mutable 객체 사용 시 상태(state)를 가질 수 있게 됩니다.

예를들어 계좌의 잔고처럼 변경될 수 있는 상태를 가지는 경우 상태의 변경으로 인한 단점이 생깁니다.

  • 프로그램을 이해하고 디버그하기 힘들다.
  • 코드의 실행을 예측하기 힘들다.
  • 멀티스레드 프로그램일 땐 충돌 방지를 위한 동기화가 필요하다.
  • 모든 상태에 대한 테스트가 필요하므로 테스트가 힘들어진다.
  • 정렬된 리스트에 가변 요소가 추가되면 요소에 변경이 일어날 때마다 리스트 전체를 재정렬 해야한다.

 

가변성을 제한하는 대표적인 예

  • 읽기 전용 프로퍼티(val)
  • 가변 컬렉션과 읽기 전용 컬렉션 구분
  • 데이터 클래스의 copy

val 프로퍼티의 값은 변경될 수 있긴 하지만, 프로퍼티 레퍼런스 자체를 변경할 순 없으므로 동기화 문제 등을 줄일 수 있어, var 보단 val가 더 많이 사용됩니다.

 

읽기 전용 프로퍼티를 사용하더라도, val 프로퍼티를 사용하더라도 mutable 객체를 담고 있다면, 내부적으로 변할 수 있고 / val 프로퍼티에서 사용자 정의 getter를 이용해 var 프로퍼티를 불러오도록 하는 경우 변할 수 있습니다.

 

val name : String? = "Gon"
val surname : String = "Lee"

val fullName : String? = name?.let { "$it $surname" }

fun main() {
	if (fullName != null) println(fullName.length)	// Gon Lee
}

*스마트 캐스트 : 위 코드처럼 if문으로 인해서 null이 아님을 검증한 경우 코틀린 컴파일러가 자동으로 String으로 변경해 줄 수 있는데, 이런 경우를 스마트 캐스트라 부르며, null 외에 타입 확인 등에도 활용됩니다.

 

val 프로퍼티가 완전히 변경할 필요가 없는 경우라면, getter를 사용하지말고 final로 직접 초기화하는 것이 좋습니다. 이유는 스마트 캐스트 등의 추가적인 기능의 활용 때문입니다.

 

Mutable 인터페이스는 Immutable 인터페이스를 상속 받아서, 변경을 위한 메서드를 추가한 것이고, 이를 통해 컬렉션들을 구현하기 때문에 Immutable 컬렉션을 다운캐스팅하는 것은 추상화를 무시하는 행위입니다.

 

immutable 컬렉션을 mutable 컬렉션으로 변경하기 원한다면, copy()를 통해 새로운 Mutable 컬렉션을 만드는 to***() 메서드를 활용해야 합니다. 또한 Immutable 객체를 변경하기 원한다면 자신을 수정한 새로운 객체를 만들어 내도록 하는게 좋습니다.

 

변경 가능한 리스트를 만드는 경우, Mutable 컬렉션과 var 프로퍼티를 만드는 방법이 존재하는데, 

val list = mutableListOf<Int>()
var lsit = listOf<Int>()

효율적인 방법
var lsit = listOf<Int>()
	private set
    
 최악의 방법 : 리스트 구현 내부의 변경 가능 지점과 프로퍼티 자체의 변경 가능 지점에 대한 동기화 구현이 필요
 var lsit = mutableListOf<Int>()

변경 가능 지점이 서로 다릅니다. 첫 번째 list는 구체적인 리스트 구현 내부에 변경 가능 지점이 존재하고, 두 번째 리스트는 프로퍼티 자체가 변경 가능 지점입니다. Mutable 컬렉션을 사용하는 것이 더 간단하게 느껴지겠지만, mutable 프로퍼티를 사용하여 객체 변경을 제어하는 것이 더 효율적 입니다. 즉, 프로퍼티의 변경 지점에서 동기화 처리를 하여 편리하게 멀티스레드 처리의 안전성을 가질 수 있습니다.

 

상태를 나타내는 mutable 객체를 외부에 노출하는 것은 굉장히 위험합니다. 불가능한 상태가 아니라면, mutable 객체는 data 한정자로 만들어지는 copy()를 활용하거나, 읽기 전용 슈퍼타입으로 업캐스트하여 가변성을 제한하여 반환하는 것이 좋습니다.

 

Immutable 객체를 사용하는 것이 좋다고 했지만, 효율성으로 인해서 Mutable 객체를 사용하는 것이 좋을 때가 있습니다. 해당 내용은 다음에 정리해보겠습니다.