도서 요약 / / 2022. 12. 19. 10:16

[클린 코드] 11. 시스템

클린 코드 도서 요약 내용입니다.

11장 시스템

"복잡성은 죽음이다. 개발자에게서 생기를 앗아가며, 제품을 계획하고 제작하고 테스트하기 어렵게 만든다."
레이 오지, 마이크로소프트 최고 기술 책임자

도시를 세운다면?

여러분이 도시를 세운다면? 온갖 세세한 사항을 혼자서 직접 관리할 수 있을까?

  • 이미 세워진 도시라도 한 사람의 힘으로는 무리다. 그럼에도 불구하고 도시는 잘 돌아간다.
  • 왜? 수도 관리팀, 전력 관리팀, 교통 관리 팀, 치안 관리 팀, 건축물 관리 팀 등 각 분야를 관리하는 팀이 있기 때문이다.
  • 도시에는 큰 그림을 그리는 사람들도 있으며 작은 사항에 집중하는 사람들도 있다.
  • 도시가 돌아가는 또 다른 이유는 적절한 추상화와 모듈화 때문이다.
시스템 제작과 시스템 사용을 분리하라

우선 제작(construction)은 사용(use)과 아주 다르다는 사실을 명심한다.

소프트웨어 시스템은 (애플리케이션 객체를 제작하고 의존성을 서로 '연결'하는) 준비 과정과 (준비 과정 이후에 이어지는) 런타임 로직을 분리해야 한다.

관심사 분리는 우리 분야에서 가장 오래되고 가장 중요한 설계 기법 중 하나다.

대부분 애플리케이션은 시작 단계라는 관심사를 분리하지 않는다. 다음이 전형적인 예다.

public Service getService() {
    if (service == null) {
        service = new MyServiceImpl(...); // 모든 상황에 적합한 기본값일까?
        return service;
    }
}

이것이 초기화 지연(Lazy Initialization) 혹은 계산 지연(Lazy Evaluation)이라는 기법이다.

장점은 여러 가지다.

  1. 실제로 필요할 때까지 객체를 생성하지 않으므로 불필요한 부하가 걸리지 않는다. 따라서 애플리케이션을 시작하는 시간이 그만큼 빨라진다.
  2. 어떤 경우에도 null 포인터를 반환하지 않는다.

하지만 getService가 MyServiceImpl과 생성자 인수에 명시적으로 의존한다. 런타임 로직에서 MyServiceImpl 객체를 전혀 사용하지 않더라도 의존성을 해결하지 않으면 컴파일이 안 된다.

테스트도 문제다. getService 메서드 호출하기 전에 적절한 테스트 전용 객체(TEST DOUBLE이나 MOCK OBJECT)를 service 필드에 할당해야 한다. service가 null인 경로와 null이 아닌 경로 모두 테스트해야 한다. 작게나마 단일 책임 원칙을 깬다는 말이다.

초기화 지연 기법을 한 번 정도 사용한다면 별로 심각한 문제가 아니다. 하지만 수시로 사용한다.

Main 분리

시스템 생성과 시스템 사용을 분리하는 한 가지 방법으로, 생성과 관련한 코드는 모두 main이나 main이 호출하는 모듈로 옮기고, 나머지 시스템은 모든 객체가 생성되었고 모든 의존성이 연결되었다고 가정한다.

애플리케이션은 main이나 객체가 생성되는 과정을 전혀 모른다는 뜻이다.

팩토리

때로는 객체가 생성되는 시점을 애플리케이션이 결정할 필요도 생긴다.
예를 들어, 주문처리 시스템에서 애플리케이션은 LineItem 인스턴스를 생성해 Order에 추가한다. 이때는 Abstract Factory 패턴을 사용한다.

의존성 주입

사용과 제작을 분리하는 강력한 매커니즘 하나가 의존성 주입이다. 의존성 주입은 제어역전(IoC) 기법을 의존성 관리에 적용한 매커니즘이다.

스프링 프레임워크는 가장 널리 알려진 자바 DI 컨테이너를 제공한다.
대다수 컨테이너는 필요할 때까지는 객체를 생성하지 않고, 대부분은 계산 지연이나 비슷한 최적화에 쓸 수 있도록 팩토리를 호출하거나 프록시를 생성하는 방법을 제공한다.

확장

군락은 마을로, 마을은 도시로 성장한다. 처음에는 좁거나 사실상 없던 길이 포장되며 점차 넓어진다. 작은 건물과 공터는 큰 건물로 채워지고 결국 곳곳에 고층 건물이 들어선다.

그렇지만 성장에는 고통이 따른다. '확장'공사로 꽉 막힌 도로에서 "왜 처음부터 넓게 만들지 않았지?"라고 자문한 적이 얼마나 많던가?

성장할지 모른다는 기대로 자그만 마을에 6차선을 뚫는데 들어가는 비용을 정당화할 수 있을까? 아니, 어느 조그만 마을이 6차선을 반길까?

'처음부터 올바르게' 시스템을 만들 수 있다는 믿음은 미신이다. 대신에 우리는 오늘 주어진 사용자 스토리에 맞춰 시스템을 구현해야 한다. 내일은 새로운 스토리에 맞춰 시스템을 조정하고 확장하면 된다. 이것이 반복적이고 점전적인 애자일 방식의 핵심이다. 테스트 주도 개발, 리팩터링, 깨끗한 코드는 코드 수준에서 시스템을 조정하고 확장하기 쉽게 만든다.

하지만 시스템 수준에서는 어떨까? 시스템 아키텍처는 사전 계획이 필요하지 않을까? 단순한 아키텍처를 복잡한 아키텍처로 조금씩 키울 수 없다는 현실은 정확하다. 맞는 말 아닌가?

소프트웨어 시스템은 물리적인 시스템과는 다르다. 관심사를 적절히 분리해 관리한다면 소프트웨어 아키텍처는 점진적으로 발전할 수 있다.

횡단(cross-cutting) 관심사
자바 프록시

프록시를 사용하면 깨끗한 코드를 작성하기 어렵다!

순수 자바 AOP 프레임워크
AspectJ 관점
테스트 주도 시스템 아키텍처 구축
의사 결정을 최적화하라
명백한 가치가 있을 때 표준을 현명하게 사용하라
시스템은 도메인 특화 언어가 필요하다
결론
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유