본문 바로가기

Reading Record/이펙티브 코틀린

[이펙티브 코틀린] Item 28 ~30. API 안정성 및 가시성

Item 28. API 안정성을 확보하라


프로그래밍에서는 안정적이고 표준 API를 선호하는데 그 이유는 아래와 같음

  • API가 변경되고 개발자가 이를 업데이트 했다면 여러 코드를 수동으로 업데이트 해야함
    • 많은 곳에서 api에 의존적이면 변경사항이 많을 수 있음
  • 사용자가 새로운 API를 배워야함
    • 변경된 api를 쓰는 쪽에서는 변경사실을 알아야 하고 변경 부분에 대한 이해가 필요함

한번에 안정적인 API가 나왔으면 하지만 좋은 API 설계는 어렵기 때문에 우선 만들고 지속적으로 발전시켜 나가야 한다. 그래서 API 안정성을 지정해서 정보를 제공함으로써 api를 사용하는 곳에서 안정성을 확인할 수 있음

  • 안정성 제공에 가장 간단한 방법은 문서에서 API 일부가 불안정한지 명확하게 지정
  • 버전을 사용해 전체 라이브러리 또는 모듈의 안정성을 지정
    • Semantic Versioning : MAJOR.MINOR.PATCH의 3개 부분으로 버전 번홀르 구성
      • 호환되지 않는 API 변경을 수행하는 경우 MAJOR 버전
      • 이전 버전과 호환되는 방식으로 기능을 추가하는 경우 MINOR 버전
      • 이전 버전과 호환되는 버그를 수정을 할 때 PATCH 버전
  • 추가로 안정적인 API에 새로운 요소를 추가할 때, 아직 해당 요소가 안정적이지 않다면 먼저 다른 브랜치에 해당 요소를 두고 일부 사용자가 이를 사용하다록 허용하려면 일단 Experimental 메타 어노테이션을 사용해 알려주는 것이 좋음
@Experimental(level = Experimental.Level.WARNING)
annotaion class ExperimentalNewApi

@ExperimentalNewApi
suspend fun getUsers(): List<User> {
   //... 
}
  • 안정적인 API의 일부를 변경해야 한다면, 전환하는 데 시간을 두고 Deprecated 어노테이션을 활용해 사용자에게 미리 알려주는 것이 좋음
@Deprecated("User suspending getUsers instead")
fun getUsers(): List<User> {
   //... 
}
  • 또한 직접적인 대안이 있는 ReplaceWith 경우 IDE에서 자동 전환을 허용하도록 지정
@Deprecated("User suspending getUsers instead", ReplaceWith("getUsers()"))
fun getUsers(callback: (List<User> -> Unit) {
   //... 
}

정리

  • 사용자는 API 안정성을 알고 써야함
  • 모듈 또는 라이브러리 작성자와 해당 사용자 간의 올바른 통신이 중요
  • 또한 안정적인 API의 변경사항을 적용하려면 충분한 시간을 가져야 함

 

Item29. 외부 API를 wrap해서 사용하라

잠재적으로 불안정하다고 판단되는 외부 라이브러리 API를 Wrap해서 사용하면 자유와 안정성을 얻을 수 있음

Wrapper의 장점

  • 문제가 있다면 Wrapper만 변경하면 되므로 API 변경에 쉽게 대응 가능
  • 프로젝트의 스타일에 맞춰서 API의 형태를 조정할 수 있음
  • 특정 라이브러리에서 문제가 발생하면, 래퍼를 수정해서 쉽게 변경 가능
  • 필요한 경우 쉽게 동작을 추가하거나 수정할 수 있음

Wrapper의 단점

  • Wrapper를 따로 정의해야함
  • 다른 개발자가 프로젝트를 다룰 때, 어떤 Wrapper들이 있는지 따로 확인해야함
  • Wrapper는 프로젝트 내부에만 유효하므로 문제가 생겨도 질문하기 어려움

정리

  • 이외의 장단점들을 비교해 Wrap할 API를 적절하게 결정해야함.
  • 라이브러리가 얼마나 안정적인지 알려주는 좋은 방법은 version 번호와 사용자 수이기 때문에 이를 참고해 사용할 라이브러리 및 wrap할 API를 결정하면 좋음

 

Item 30. 요소의 가시성을 최소화하라

API를 설계할 때 가능한 한 간결한 API를 선호하는데 그 이유는 아래와 같음

  • 작은 인터페이스는 배우기 쉽고 유지하기 쉬움
    • 눈에 보이는 요소가 적을 수록 유지하고 테스트 해야할 것이 적음
  • 변경을 가할 때 기존의 것을 숨기는 것보단 새로운 것을 노출하는 것이 쉬움
    • public 으로 공개해놓은 API는 외부에서 사용되는데 요소를 변경하는 경우에 모든 곳에서 업데이트 해야하기 때문에 가기성을 제한하는 것이 훨씬 더 어려워 각 용도를 신중하게 고려해 대안을 제공하는 것이 좋음
  • 클래스의 상태를 외부에서 직접 변경할 수 있다면 클래스는 자산의 상태를 보장할 수 없음
    • 불변성이 깨질 수 있는 가능성이 있기 때문에 위험함
  • 가시성이 제한될수록 클래스의 변경을 쉽게 추적할 수 있음
    • 프로퍼티의 상태를 더 쉽게 이해할 수 있고 동시성 처리에 용이함

가시성 한정자 사용하기

접근제어자

  • public : 모든 곳에서 사용 가능
  • private : 클래스 내에서만 사용 가능
  • protected : 해당 클래스와 하위 클래스에서만 사용 가능
  • internal : 모듈 내부에서만 사용 가능

Top-level 요소

  • public : 모든 곳에서 사용 가능
  • private : 같은 파일 내부에서만 사용 가능
  • internal : 모듈 내부에서만 사용 가능

정리

  • 인터페이스가 작을수록 학습과 유지보수에 쉬움
  • 최대한 제한이 되어야 변경하기 쉬움
  • 클래스의 상태를 나타내는 프로퍼티가 노출되어 있다면 클래스가 자신의 상태를 책일질 수 없음
  • 가시성이 제한되면 변경을 쉽게 추적할 수 있음