티스토리 뷰
왜 추상화 계층을 만드는가
HttpConnection connection = HttpConnection.connect("http://example.com/server");
conncetion.send("Hello Server");
connection.close();
예를 들어 사용자의 어떤 장치에서 실행되면서 서버에 메시지를 보내는 코드를 작성한다고 가정해보자.
이때, 최상위 계층에서는 HTTP 프로토콜이 어떻게 구현되는지 알 필요도 없이 서버에 메시지를 보내는 것에만 신경을 쓰면서 코드를 작성할 수 있다. 이와 비슷하게 HTTP 프로토콜을 구현하기 위한 코드를 작성한 엔지니어는 데이터가 무선 신호에 변조되는 방법에 대해 아무것도 몰라도 문제가 없을 것이다. HttpConnection 코드를 구현한 개발자는 물리적인 데이터 전송을 추상적인 개념으로 생각할 수 있고, 우리 역시 HTTP 연결을 추상적인 개념으로 생각할 수 있다. 이것을 추상화 계층이라고 한다.
추상화 계층 및 코드 품질의 핵심 요소
깨끗하고 뚜렷한 추상화 계층을 구축하면 이전에 살펴본 코드 품질의 네가지 핵심 요소를 달성할 수 있다.
그 이유는 아래와 같다.
가독성
코드베이스에 있는 모든 코드의 세부 사항을 이해하는 것은 쉽지 않지만, 몇 가지 높은 계층의 추상화를 이해하고 사용하기는 쉽다. 깨끗하고 뚜렷한 추상화 계층을 만드는 것은 개발자가 한 번에 한두 개 정도의 몇 개의 개념만 다루면 된다는 것을 의미한다. 떄문에 코드의 가독성이 크게 향상된다.
모듈화
추상화 계층이 하위 문제에 대한 해결책을 깔끔하게 나누고 구현 세부 사항이 외부로 노출되지 않도록 보장할 때, 다른 계층이나 코드의 일부에 영향을 미치지 않고 계층 내에서만 구현을 변경하기가 매우 쉬워진다.
재사용성 및 일반화성
하위 문제에 대한 해결책이 간결한 추상화 계층으로 제시되면 해당 하위 문제에 대한 해결책을 재사용하기가 쉬워진다. 그리고 문제가 적절하게 추상적인 하위 문제로 세분화된다면, 해결책은 여러 가지 다른 상황에서 유용하게 일반화될 가능성이 크다.
테스트 용이성
신뢰할 수 있는 코드를 작성하고자 한다면, 각 하위 문제에 대한 해결책이 견고하고 제대로 작동하는지 확인해야 한다. 코드가 추상화 계층으로 깨끗하게 분할되면 각 하위 문제에 대한 해결책을 완벽하게 테스트하는 것이 훨씬 쉬워진다.
코드의 계층
실제로 추상화 계층을 생성하는 방법은 코드를 서로 다른 단위로 분할하여 단위 간의 의존 관계를 보여주는 의존성 그래프를 생성하는 것이다. 대체적으로 그 요소는 아래와 같다.
- 함수
- 클래스
- 인터페이스
함수
함수를 작게 만들고 수행하는 작업을 명확하게 하면 코드의 가독성과 재사용성이 높아진다. 코드를 마구 작성하다 보면 너무 길어서 읽을 수 없는 함수가 되기 쉽다. 따라서 코드 작성을 일단 마치고 코드 검토를 요청하기 전에 자신이 작성한 코드를 비판적으로 다시 한번 살펴보는 것이 좋다. 함수를 한 문장으로 표현하기 어렵게 구현했다면 로직의 일부를 잘 명명된 헬퍼 함수로 분리하는 것을 고려해봐야 한다.
클래스
- 응집력: 한 클래스 내의 모든 요소들이 얼마나 잘 속해 있는지를 보여주는 척도로, 좋은 클래스는 매우 응집력이 강하다.
- 순차적 응집력: 한 요소의 출력이 다른 요소에 대한 입력으로 필요할 때 발생한다. 예를 들어, 커피 한 잔을 만드는 과정을 수행할 때 원두를 갈기 전에는 커피를 내릴 수 없다. 원두를 갈아내는 과정의 산출물은 커피를 추출하는 과정에 투입된다. 그러므로 우리는 갈고 추출하는 것 사이에 서로 응집력이 있다고 말할 수 있다.
- 기능적 응집력: 몇 가지 요소들이 모여서 하나의 일을 성취하는 데 기여할 때 발생한다. 하나의 일이 무엇인가에 대한 정의는 매우 주관적일 수 있지만, 예를 들어 빵을 만들기 위해 필요한 모든 장비를 부엌의 전용 서랍에 보관하는 것이 될 수 있다. 반죽을 섞을 그릇, 숟가락, 그리고 빵 틀은 빵을 만들기 위해 모두 필요하며 함께 있어야 한다.
- 관심사의 분리: 시스템이 각각 별개의 문제(또는 관심사)를 다루는 개별 구성 요소로 분리되어야 함을 의미하는 설계 원칙이다.
인터페이스
계층 사이를 뚜렷하게 구분하고 구현 세부 사항이 계층 사이에 유출되지 않도록 하기 위해 사용할 수 있는 한 가지 접근법은 어떤 함수를 외부에 노출할 것인지를 인터페이스를 통해 결정하는 것이다. 그 다음 이 인터페이스에 정의된 대로 클래스가 해당 계층에 대한 코드를 구현한다. 이보다 위에 있는 계층은 인터페이스에 의존할 뿐 로직을 구현하는 구체적인 클래스에 의존하지 않는다.
층이 너무 얇아질 때
코드를 별개의 계층으로 세분화하면 장점이 많지만, 아래와 같은 추가 비용이 발생한다.
- 클래스를 정의하거나 의존성을 새 파일로 import하려고 반복적으로 사용하는 코드로 인해 코드의 양이 늘어난다.
- 로직의 이해를 위해 파일이나 클래스를 따라갈 때 더 많은 노력이 필요한다.
- 인터페이스 뒤에 계층을 숨기게 되면 어떤 상황에서 어떤 구현이 사용되는지 파악하는 데 더 많은 노력이 필요하다. 이로 인해 로직을 이해허거나 디버깅하는 것이 더 어려워 질 수 있다.
요약
- 코드를 깨끗하고 뚜렷한 추상화 계층으로 세분화하면 가독성, 모듈화, 재사용, 일반화 및 테스트 용이성이 향상된다.
- 특정 언어에 국한된 기능뿐만 아니라 함수, 클래스 및 인터페이스를 사용하여 코드를 추상화 계층으로 나눌 수 있다.
- 코드를 추상화 계층으로 분류하는 방법을 결정하려면 해결 중인 문제에 대한 판단과 지식을 사용해야 한다.
- 너무 비대한 계층 때문에 발생하는 문제는 너무 얇은 계층때문에 발생하는 문제보다 더 심각하다. 확실하지 않은 경우에는 남용의 위험에도 불구하고 계층을 얇게 만드는 것이 좋다.
Reference
톰 롱. 『좋은 코드, 나쁜 코드』. 제이펍, 2022.
'Book > 좋은 코드, 나쁜 코드' 카테고리의 다른 글
[좋은 코드, 나쁜 코드] 코드를 오용하기 어렵게 만들라 (0) | 2023.01.26 |
---|---|
[좋은 코드, 나쁜 코드] 가독성 높은 코드를 작성하라 (2) | 2022.12.30 |
[좋은 코드, 나쁜 코드] 오류 (0) | 2022.12.25 |
[좋은 코드, 나쁜 코드] 다른 개발자와 코드 계약 (2) | 2022.12.21 |
[좋은 코드, 나쁜 코드] 코드 품질 (0) | 2022.12.18 |
- Total
- Today
- Yesterday
- 정렬
- 백준
- 데이터베이스
- spring boot
- 구현
- Algorithm
- 코테
- 알고리즘
- Real MySQL
- 노마드
- 릿코드
- 문자열
- 그리디
- 스프링부트
- 스프링
- 노마드코더
- leetcode
- 리팩토링
- kotlin
- 파이썬
- Spring
- mysql 8.0
- 코틀린
- 북클럽
- 자료구조
- 스프링 부트
- webflux
- MySQL
- 김영한
- 인프런
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |