본문 바로가기
Kotlin

[Effective Kotlin] 26 - 함수 내부의 추상화 레벨을 통일하라

by 매운돌 2023. 7. 22.

계층이 잘 분리되면 어떤 계층을 작업할 때 그 아래의 계층은 이미 완성되어 있으므로, 해당 계층만 생각하면 됩니다. 즉 전체를 이해할 필요가 없어지는 것입니다. 예를 들어 어셈블리 언어, JVM 바이트 코드가 무엇인지 몰라도 프로그래밍할 수 있습니다. 

 

추상화 레벨

일반적으로 컴퓨터 과학자들은 어떤 계층이 높은 레벨인지 낮은 레벨인지를 구분합니다. 높은 레벨로 갈수록 물리 장치로부터 점점 멀어집니다. 프로그래밍에서는 일반적으로 높은 레벨일수록 프로세서로부터 멀어진다고 표현합니다. 높은 레벨일수록 걱정해야 하는 세부적인 내용들이 적습니다. 단 높은 레벨일수록 단순함을 얻지만, 제어력을 잃습니다.

예를들어 C언어는 메모리 관리를 직접할 수 있습지만, Java는 가비지 컬렉터가 자동으로 메모리를 관리해 줍니다. 따라서 메모리 사용을 최적화하는 것이 굉장히 힘듭니다.

 

추상화 레벨 통일

컴퓨터 과학과 마찬가지로 코드도 추상화를 계층처럼 만들어서 사용할 수 있습니다. 이를 위한 기본적인 도구가 바로 함수입니다. 컴퓨터 과학이 높은 레벨과 낮은 레벨을 확실하게 구분하고 있는 것처럼, 함수도 높은 레벨과 낮은 레벨을 구분해서 사용해야 한다는 원칙이 있습니다. 이를 추상화 레벨 통일(Single Level of Abstraction, SLA)원칙이라고 부릅니다.

class CoffeeMachine {
	
    fun makeCoffee() {
		boilWater()
        brewCoffee()
        pourCoffee()
        pourMilk()
    }
    
    private fun boilWater() {
    	// ...
    }
    
    private fun brewCoffee() {
    	// ...
    }
    
    private fun pourCoffee() {
    	// ...
    }
    
    private fun pourMilk() {
    	// ...
    }
}

위와 같은 코드는 makeCoffee함수를 읽고 이해하기 쉬우며, 누군가 낮은 레벨(boilWater, brewCoffee 등)을 이해해야 한다면, 해당 부분의 코드만 살펴보면 됩니다. 매우 간단한 추상화를 추출해서 가독성을 크게 향상시킨 것입니다.

이처럼 "함수는 작아야 하며, 최소한의 책임만을 가져야 한다"라는 일반적인 규칙입니다. 또 어떤 함수가 다른 함수보다 좀 복잡하다면, 일부 부분을 추출해서 추상화하는 것이 좋습니다. 

추가적으로 이런 형태로 함수를 추출하면, 재사용과 테스트가 쉬워집니다.

 

프로그래 아키텍처의 추상 레벨

추상화 계층이라는 개념은 함수보다 높은 레벨에서도 적용할 수 있습니다. 추상화를 구분하는 이유는 서브시스템의 세부 사항을 숨김으로써 상호 운영성과 플랫폼 독립성을 얻기 위함입니다. 이는 문제 중심으로 프로그래밍한다는 의미입니다. 이러한 개념은 모듈 시스템을 설계할 떄도 중요합니다. 모듈을 분리하면 계층 고유의 요소를 숨길 수 있습니다.

계층이 잘 분리된 프로젝트를 계층화가 잘되어 있다고 부릅니다. 계층화가 잘된 프로젝트를 좋은 프로젝트라고 부립니다. 계층화가 잘 된 프로젝트는 어떤 계층 위치에서 코드를 보아도 일관적인 관점을 얻을 수 있습니다.

 

정리

별도의 추상화 계층을 만드는 것은 프로그래밍에서 일반적으로 사용되는 개념입니다. 이는 knowledge를 체계화하고, 서브시스템에 세부 사항을 숨김으로써 상호 운영성과 플랫폼 독립성을  얻게 합니다. 함수, 클래스, 모듈 등의 다양한 방식을 통해서 추상화를 분리합니다. 이때 각각의 레이어가 너무 커지는 것은 좋지 않습니다. 작고 최소한의 책임만 갖는 함수가 이해하기 쉽습니다. 추상화 레벨은 구체적인 동작, 프로세서, 입출력과 가까울수록 낮은 레벨이라고 표현합니다. 낮은 추상화 계층에서 높은 계층에서 사용하는 요소 API를 만듭니다.