- [BOOK - 클린코드] Chapter 12. 창발성(Emergence)2025년 03월 16일 21시 01분 51초에 업로드 된 글입니다.작성자: nickhealthy
이 장에서는 어떻게 단순한 규칙들이 모여 복잡하고 우아한 설계를 '창발'시키는지에 대해 설명합니다.
💡창발성의 개념
창발성이란 간단한 규칙들이 모여 복잡한 시스템을 만들어내는 현상을 말합니다. 마치 개미 한 마리는 단순한 규칙을 따르지만, 개미 군집은 놀랍도록 복잡하고 효율적인 시스템을 형성하는 것과 같습니다. 소프트웨어 설계에서도 몇 가지 간단한 설계 원칙들을 따르면 '좋은 설계'가 자연스럽게 창발된다는 것이 이 장의 핵심입니다.
켄트 벡의 단순한 설계 규칙
켄트 벡(Kent Beck)이 제시한 '단순한 설계 규칙'을 중심으로 설명합니다. 이 규칙들은 다음과 같은 우선순위를 가집니다:
- 모든 테스트를 실행한다: 무엇보다 먼저, 설계는 의도한 대로 동작해야 합니다. 모든 테스트가 통과한다는 것은 시스템이 의도한 대로 동작한다는 것을 의미합니다.
- 중복을 없앤다: 코드에서 중복은 추가 작업, 추가 위험, 불필요한 복잡성을 의미합니다. 이를 제거하면 코드가 더 명확해지고 유지보수가 쉬워집니다.
- 프로그래머의 의도를 표현한다: 코드는 읽는 사람이 코드의 의도를 쉽게 이해할 수 있어야 합니다. 변수명, 함수명, 클래스명 등을 통해 코드가 무엇을 하려는지 명확히 표현해야 합니다.
- 클래스와 메서드 수를 최소화한다: 불필요한 클래스와 메서드는 코드를 복잡하게 만듭니다. 필요한 추상화 수준을 유지하면서 클래스와 메서드의 수를 최소화해야 합니다.
규칙 1: 모든 테스트 실행
테스트는 시스템이 의도한 대로 동작하는지 확인하는 중요한 수단입니다. 테스트가 가능한 시스템을 만들려면:
- 결합도를 낮춰야 합니다.
- 모듈을 작게 만들어야 합니다.
- 명확한 인터페이스를 정의해야 합니다.
- 단일 책임 원칙(SRP)을 지켜야 합니다.
이런 속성들은 모두 좋은 설계의 특징이기도 합니다. 즉, 테스트 가능한 시스템을 만들기 위한 노력이 자연스럽게 좋은 설계로 이어집니다.
✅ 테스트하기 어려운/쉬운 코드 비교
// 테스트하기 어려운 코드 public class WeatherService { private static final WeatherService instance = new WeatherService(); private WeatherDatabase database; private WeatherService() { database = new WeatherDatabase(); } public static WeatherService getInstance() { return instance; } public String getWeatherInfo(String city) { return database.queryWeather(city); } } // 테스트하기 쉬운 코드 public class WeatherService { private WeatherDatabase database; public WeatherService(WeatherDatabase database) { this.database = database; } public String getWeatherInfo(String city) { return database.queryWeather(city); } } // 테스트 코드 public class WeatherServiceTest { @Test public void testGetWeatherInfo() { // Mock 데이터베이스 생성 WeatherDatabase mockDB = mock(WeatherDatabase.class); when(mockDB.queryWeather("Seoul")).thenReturn("Sunny, 25°C"); // 테스트할 서비스 객체 생성 WeatherService service = new WeatherService(mockDB); // 테스트 실행 및 검증 assertEquals("Sunny, 25°C", service.getWeatherInfo("Seoul")); } }
두 번째 코드는 의존성 주입을 통해 테스트하기 쉬운 구조로 변경되었습니다. 이처럼 테스트 가능한 코드를 작성하면 자연스럽게 좋은 설계가 됩니다.
규칙 2~4: 리팩터링
나머지 규칙들은 리팩터링 과정에서 적용됩니다.
중복 제거, 의도 표현, 클래스 수 최소화는 모두 코드를 더 깔끔하고 이해하기 쉽게 만드는 과정입니다.
중복 제거
중복은 코드의 품질을 저하시키는 주요 요인입니다.
중복을 찾아내고 제거하는 방법:- 동일한 코드 블록의 중복
- 유사한 알고리즘의 중복
- 동일한 기능을 수행하는 다른 구현의 중복
이런 중복을 제거하면 코드의 가독성이 높아지고, 유지보수가 쉬워지며, 버그 발생 가능성이 줄어듭니다.
의도 표현
코드는 단순히 컴퓨터를 위한 명령어가 아니라, 다른 개발자들이 읽고 이해해야 하는 문서이기도 합니다.
코드의 의도를 명확히 표현하는 방법:- 좋은 이름 선택
- 작은 함수와 클래스
- 표준 명명법 사용
- 단위 테스트 작성
이런 방법들을 통해 코드의 의도를 명확히 표현하면, 다른 개발자들이 코드를 더 쉽게 이해하고 유지보수할 수 있습니다.
클래스와 메서드 수 최소화
마지막 규칙은 앞의 규칙들과 균형을 이루는 역할을 합니다. 중복을 제거하고 의도를 표현하다 보면 클래스와 메서드가 많아질 수 있습니다. 하지만 불필요한 추상화는 코드를 복잡하게 만들 수 있으므로, 적절한 수준을 유지해야 합니다.
결론
'창발성'의 핵심은 간단한 규칙들을 따르면 복잡하고 좋은 설계가 자연스럽게 '창발'된다는 것입니다.
테스트 가능성을 높이고, 중복을 제거하고, 의도를 명확히 표현하고, 클래스와 메서드 수를 적절히 유지하는 이 네 가지 규칙은 소프트웨어 설계의 기본 원칙입니다.
다음글이 없습니다.이전글이 없습니다.댓글