김동형수 개발기

이펙티브 코틀린 - 2부 4장 본문

책 스터디/[진행] 이펙티브 코틀린

이펙티브 코틀린 - 2부 4장

김동형수 2023. 11. 14. 10:31

4장 추상화 설계

추상화는 프로그래밍 세계에서 가장 중요한 개념 중 하나
컴퓨터 과학에서 추상화는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것을 말한다.
추상화를 하려면 객체에서 무엇을 감추고 무엇을 노출해야 하는지 결정해야 한다.
 
프로그래밍에서 추상화
추상화를 설계한다는 것은 단순하게 모듈 또는 라이브러리로 분리한다는 의미가 아니다. 함수를 정의할 때는 그 구현을 함수 시그니처 뒤에 숨기게 되는데, 이것이 바로 추상화다.
강력한 프로그래밍 언어들이 당연히 갖고 있는 기능 중 하나는 공통 패턴에 이름을 붙여서 추상화를 만드는 기능이다. 예를 들어 함수, 델리게이트, 클래스 등이 대표적인 예다.
 
추상화와 자동차
운전자는 자동차를 조종하는 인터페이스(핸들과 패달 등)를 사용하는 방법만 알면 된다.
추상화는 이처럼 내부적으로 일어나는 모든 것을 마법처럼 숨겨 준다.
추상화를 사용하는 목적

  • 복잡성을 숨기기 위해
  • 코드를 체계화 하기 위해
  • 만든느 사람에게 변화의 자유를 주기 위해

아이템 26: 함수 내부의 추상화 레벨을 통일하라

추상화 레벨
높은 레벨로 갈수록 물리 장치로부터 점점 멀어진다.
높은 레벨일수록 단순함을 얻지만, 제어력을 잃는다.
 
추상화 레벨 통일
함수도 높은 레벨과 낮은 레벨을 구분해서 사용해야 한다는 원칙, 이를 추상화 레벨 통일(SLA) 원칙이라 부른다.
오래된 프로그램들은 한 함수에 수 많은 로직을 때려 넣어서 개발된 경우가 많다. 이해하는 것이 거의 불가능에 가깝다.
최근에는 함수를 계층처럼 나누어서 사용한다.
함수는 작아야하며, 최소한의 책임만을 가져야 한다.
어떤 함수가 다른 함수보다 좀 복잡하다면, 일부 부분을 추출해서 추상화하는 것이 좋다.
 
프로그램 아키텍처의 추상 레벨
계층이 잘 분리된 프로젝트를 계층화가 잘 되어있다고 부른다.

아이템 27: 변화로부터 코드를 보호하려면 추상화를 사용하라

함수와 클래스 등의 추상화로 실질적인 코들르 숨기면, 사용자가 세부 사항ㅇ르 알지 못해도 괜찮다는 장점이 있다
 
상수
상수를 추상화하면 장점

  • 이름을 붙일 수 있다
  • 나중에 해당 값을 쉽게 변경할 수 있다.

함수
내부적으로만 사용하더라도 함수의 이름을 직접 바꾸는 것은 위험할 수 있다.
이름 변경은 그냥 레이블을 붙이는 방식의 변화이므로, 큰 차이가 없다고 생각하기도 한다. 이러한 관점은 컴파일러 관점이다.
사람의 관점에서는 이름을 바꾸면 큰 변화가 일어난 것이다.
함수는 매우 단순한 추상화지만, 제한이 많다. 예를 들어 함수는 상태를 유지하지 않는다. 또한 함수 시그니처를 변경하면 프로그램 전체에 큰 영향을 줄 수 있다.
 
클래스
클래스는 (함수보다)훨씬 더 많은 자유를 보장해준다.
open 클래스는 서비스클래스를 대신 제공할 수 있기 때문에 더 많은 자유를 보장한다.
많은 자유를 얻으려면, 더 추상적이게 만들면 된다. 인터페이스 뒤에 클래스를 숨기는 방법이다.
 
인터페이스
라이브러리를 만드는 사람은 내부 클래스의 가시성을 제한하고, 인터페이스를 통해 이를 노출하는 코드를 많이 사용한다.
인터페이스 뒤에 객체를 숨김으로써 실질적인 구현을 추상화하고, 사용자가 추상화된 것에만 의존하게 만들 수 있다. 즉, 결합을 줄일 수 있다.
인터페이스를 변경하면, 이를 구현하는 모든 클래스를 변경해야 한다.
 
ID만들기
더 많은 추상화는 더 많은 자유를 주지만, 이를 정의하고 사용하고 이해하는 것이 조금 어려워진다.
추상화는 자유를 주지만, 코드를 이해하고 수정하기 어렵게 만든다.
 
추상화의 문제
큰 프로젝트에서는 잘 모듈화 해야한다. 추상화도 비용이 발생한다. 극단적으로 모든 것을 추상화해서는 안된다.
추상화는 거의 무한하게 할 수 있지만, 어느 순간부터 득보단 실이 많아질 것이다.
추상화가 너무 많으면 코드를 이해하기 어렵다. 추상화가 많으 코드를 보면, 이해하기 어렵다는 생각 때문에 코드를 제대로 읽기 전에 두려움에 사로잡힐 수 있다.
 
어떻게 균형을 맞춰야 할까?

  • 추상화의 균형 맞추는 요소
  • 팀의 크기
  • 팀의 경험
  • 프로젝트의 크기
  • 특징 세트
  • 도메인 지식

항상 무언가 변화할 수 있다고 생각하는 것이 좋다.
 

아이템 28: API 안정성을 확인하라

표준적인 API를 선호하는 이유
1. API가 변경되고, 개발자가 이를 업데이트했다면, 여러 코드를 수동으로 업데이트 해야한다.
   많은 요소가 이 API에 의존하고 있다면 이는 큰 문제가 된다.
   개발자가 안정적인 라이브러리로 업데이트 하는 것을 두려워한다는 것은 매우 좋지 않은 상황이다.
2. 사용자가 새로운 API를 배워야 한다.
    처음부터 안정적이지 않은 모듈을 많이 공부하는 것보다는 안정적인 모듈부터 공부해두는 것이 좋다.
일반적으로 버전을 활용해서 라이브러리와 모듈의 안정성을 나타낸다.
시멘틱 버저닝을 사용한다.
MAJOR, MINOR, PATCH로 나눠서 구성

  • MAJOR : 호환되지 않는 수준의 API 변경
  • MINOR : 이전 변경과 호환되는 기능을 추가
  • PATCH : 간단한 버그 수정

Experimental 메타 어노테이션을 사용해서 사용자들에게 아직 해당 요소가 안전하지 않다는 것을 알려 주는 것이 좋다.
안정적인 API의 일부를 변경해야 한다면, 전환하는데 시간을 두고 Deprecated 어노테이션을 활용해서 사용자에게 미리 알려줘야한다.
대안은 ReplaceWith을 사용한다.

아이템 29: 외부 API를 랩해서 사용하라

불안정한 API를 과도하게 사용하는 것은 굉장히 위험하다.
잠재적으로 불안정하다고 판단되는 외부 라이브러리 API를 랩해서 사용한다.
 

아이템 30: 요소의 가시성을 최소화하라

보이는 요소가 적다면, 유지보수하고 테스트할 것이 적다.
가시성과 관련된 제한을 변경하는 것은 더 어렵다. 이러한 변경은 시중하게 고려해야 하며, 변경할 경우 대체재를 제공해야 한다.
클래스의 상태를 나타내는 프로퍼티를 외부에서 변경할 수 있다면 클래스는 자신의 상태를 보장할 수 없다.
일반적으로 코틀린에서는 이처럼 구체 접근자의 가시성을 제한해서 모든 프로퍼티를 캡슐화하는 것이 좋다.
 
가시성 한정자 활용하기
클래스 멤버

  • public
  • private
  • protected
  • internal

 
톱레벨 요소

  • public
  • private
  • internal

코틀린은 지역적으로만 사용되는 있는 요소는 private로 만든느 것이 좋다는 컨벤션이 있는데, DTO에는 적용하지 않는 것이 좋다.
숨길 이유가 없기 때문이다.
프로퍼티를 사용할 수 있게 눈에 띄게 만드는 것이 좋으며, 필요하지 않은 경우 그냥 프로퍼티를 제거하는 것이 좋다.
한 가지 큰 제한은 API를 상속할 때 오버라이드해서 가시성을 제한할 수는 없다는 것이다.

아이템 31: 문서로 규약을 정의하라

KDoc
 
일반적으로 문제는 행위가 문서화되지 않고, 요소의 이름이 명확하지 않다면 이를 사용하는 사용자는 우리가 만들려고 했던 추상화 목표가 아닌, 현재 구현에만 의존하게 된다는 것이다. 이러한 문제는 예상되는 행위르 문서로 설명함으로써 해결한다.
 
규약
어떤 행위를 설명하면 사용자는 이를 일종의 약속으로 취급하며, 이를 기반으로 스스로 자윱롭게 생각하던 예측을 조정한다.
서로가 규약을 존중한다면, 독립적으로 작업해도 모든 것이 정상적으로 기능할 것이다.
만약 규약을 설정하지 않는다면 구현의 세부적인 정보에 의존하게 된다. 클래스를 만든 사람은 사용자가 대체 무엇을 할지 알 수 가 없으므로 사용자의 구현을 망칠 위험이 있다.
규약을 설정하는 것은 중요하다.
 
규약 정의하기

  • 이름
  • 주석과 문서
  • 타입

주석을 써야할까?
초기에는 문학적 프로그래밍 - 주석으로 모든 것을 설명하는 프로그래밍 방식
클린코드로 인해 주석없이도 읽을 수 있는 코드를 작성해야 하는 프로그래밍 방식으로 바뀌었다.
주석을 다는 것보다 함수로서 추출하는 것이 훨씬 좋다는 것에 동의한다.
주석은 굉장히 유용하고 중요하다. 표준라이브러리에 주석으로 규약을 잘 정리해뒀다.
 
타입 시스템과 예측
클래스가 어떤 동작을 할 것이라 예측되면, 그 서브클래스도 이를 보장해야 한다. 이를 리스코프 치환 원칙이라 부른다.
인스턴스를 활용하는 사람에게 모두 문서를 통해 전달할 수 있다.
 
조금씩 달라지는 세부사항
언어는 어떤 식으로 작동해도 괜찮지만, 좋은 방식을 기억하고 이를 적용해서 사용하는 것이 좋다.
구현의 세부 사항은 항상 달라질 수 있지만, 최대한 많이 보호하는 것이 좋다.
캡슐화를 통해서 이를 보호한다.
캡슐화가 많이 적용될수록, 사용자가 구현에 신경을 많이 쓸 필요가 없어지므로, 더 많은 자유를 갖게 된다.

아이템 32: 추상화 규약을 지켜라

규약을 위반하면 코드가 작동을 멈췄을 때 문제가 된다.
 
상속된 규약
equals, hashCode 예시
 
프로그램을 안정적으로 유지하고 싶다면, 규약을 지켜라, 잘 문서화 해라
 

Comments