포스트

실용주의 프로그래머 5

구부러지거나 부러지거나

현대의 빠른 변화 속도를 따라가기 위해서는 느슨하고 유연한 코드를 작성해야 한다.

결합도 줄이기

좋은 설계 원칙을 따르면 바꾸기 쉬운 코드(ETC)를 만들 수 있다. 높은 결합도는 당연하게도 변경을 어렵게 만든다. 소프트웨어를 설계할 때는 형태를 유연하게 바꿀 수 있도록 설계해야 한다.

결합도가 낮은 코드가 바꾸기 쉽다.

묻지 말고 말하라는 원칙이 존재한다. 외부에서 다른 객체의 내부 상태에 따라 판단을 내리고 그 객체를 갱신해서는 안된다는 것이다. 이는 캡슐화의 장점을 위반한다.

1
2
3
customer.orders.find(order_id).getTotals().applyDiscount(discount);

이런 코드를 주의해야 한다. 고객의 주문 컬렉션을 가져오고, 주문을 찾아서는 안된다. 주문에 할인은 적용하기 위해 주문의 합을 가져올 필요는 없다.

1
2
3
customer.findOrder(order_id).applyDiscount(discount);

그저 적절한 주문을 찾고, 해당 주문에 할인을 적용하는 편이 좋은 캡슐화의 예시이다. 결국 메서드 호출을 엮는 것을 경계해야 한다. 이처럼 함수를 조합하여 파이프라인을 만들고, 파이프라인은 함수에서 함수로 데이터를 변환하며 넘긴다. 파이프라인은 메서드 호출로 이루어진 것과는 다르게 숨겨진 세부 사항에 의존하지 않는다.
전역 데이터는 애플리케이션 컴포넌트 간의 결합을 만들어낸다. 전역 데이터 하나하나는 애플리케이션의 모든 메서드에 갑자기 매개 변수가 추가된 것과 같은 효과를 낸다.
만약 우리의 코드가 전역 데이터를 사용한다면, 이 코드는 나머지로부터 그만큼 뗴어내기 힘든 코드이다. 즉 재사용성이 낮다. 전역 데이터를 쓰는 코드에 단위 테스트를 만들면 이런 문제를 확인할 수 있다. 내부적인 변수나 싱글턴 객체의 값 뿐만 아니라 외부 리소스도 전역 데이터다. 이런 전역 데이터의 취급에 주의하며, API 로 감싸서 안전장치를 만들어라.

실세계를 갖고 저글링하기

오늘날 우리는 엉망진창인 세계를 반영한 애플리케이션을 만들어야 한다. responsive 한 애플리케이션을 만들기 위해서는 이벤트를 활용해야 한다.

이벤트

이벤트는 무언가 정보가 있다는 것을 의미한다. 이런 이벤트를 활용한 4가지 전략이 있다.

유한 상태 기계

FSM (유한 상태 기계)은 이벤트를 어떻게 처리할지 정의한 명세이다. 정해진 상태들이 있고, 그중 하나가 ‘현재 상태’다. 이벤트에 의해서 상태가 변하면서 다른 작업을 수행한다. 각 상태별로 정해진 행동과, 행동에 따라 상태를 변화시키면서 움직이는 것이 FSM 이다.

감시자 패턴

감시자 패턴은 이벤트를 발생시키는 감시 대상과, 이벤트에 관심이 있는 클라이언트인 감시자로 이루어진다. 이벤트가 발생하면 감시 대상은 등록된 감시자 목록의 함수를 호출한다. 감시자 패턴에는 문제가 하나 있는데, 모든 감시자가 감시 대상에 등록을 하므로 결합이 생기며, 감시 대상이 콜백을 직접 호출하기에 성능 병목이 될 수 있다.

게시-구독

게시-구독은 펍섭이라고 부른다. 이 방법을 통해서 감시자 모델의 결합도를 높이는 무넺와 성능 문제를 해결할 수 있다. 구독자는 채널을 구독하고, 감시자는 채널에 이벤트를 보낸다. 즉 비동기적으로 채널을 통해서 구독자와 게시자 사이의 통신이 발생한다.

반응형 프로그래밍과 스트림 그리고 이벤트

리액트같은 프레임워크는 특정 값에 대한 반응을 통해서 새로운 컴포넌트를 렌더링한다. 즉, 이벤트에 대해서 적절한 처리를 하는 일종의 스트림을 구축함으로서 이벤트에 대해 처리를 할 수 있다.


이벤트 중심으로 작동하는 코드는 일직선으로 수행되는 코드보다 더 잘 반응하며, 결합도가 더 낮다.

변환 프로그래밍

모든 프로그램은 데이터를 변환한다. 입력 -> 출력이 프로그램이 하는 일이다. 만약에 줄 수 기준으로 가장 긴 파일 5개를 찾는 프로그램을 만든다면 어떻게 해야할까. 해당 요구사항을 단계별로 나누면 다음과 같다.

  • 디렉터리 이름에서
  • 파일명 목록을 확인
  • 줄 수와 파일명 목록을 확인
  • 줄 수를 기준으로 정렬
  • 가장 긴 다섯개 추출

모든 코드를 이런 방식으로 생각하면 훨씬 문제가 간단할 것이다.

프로그래밍은 코드에 관한 것이지만, 프로그램은 데이터에 관한 것이다.

서로 간의 상태를 전달할 수 있는 여러 함수 파이프라인의 집합을 만드는 형식으로 프로그램을 설계하라. 데이터를 거대한 강이자 흐름으로 생각하고, 각 데이터를 순차적으로 처리해서 목표로 하는 결과를 얻어내자.
이를 통해서 결합도를 줄이고 재사용성을 높일 수 있다. 어떤 함수든 매개 변수가 다른 함수의 출력이나 입력 결과와 맞기만 하다면 대체 및 재사용이 가능하다.
이런 방법에 있어서 오류 처리를 하기 위해서는 1가지 유념해야 하는 사실이 있다. 바로 변환 사이의 값을 날것으로 넘기지 않는 것이다. 래퍼 역할을 하는 자료 구조나 타입으로 값을 싸서 넘김으로서 단계별 값이 유효한 값인지를 확인할 수 있다.

상속세

상속에는 2가지 방법이 있다. 타입을 조합하는 방법과, 동작을 다양하게 구성하는 방법이다. 전자는 java 에서 사용되는 형식이고 후자는 자바스크립트에서 사용되는 형식이다. 그러나 2가지 상속 모두 문제가 존재한다.

코드를 공유하기 위해 상속을 쓰는 문제

상속은 결합이다. 자식 클라스는 모든 조상과 얽히게 된다. 상위 클래스의 변경 사항은 모든 자식에 영향을 미친다.

타입을 정의하기 위해 상속을 쓰는 문제

클래스의 계층 구조를 표현하기 위해 상속을 사용하다보면, 이런 복잡한 계층 구조로 인해 애플리케이션이 취약하게 된다. 변경 사항이 위나 아래로 영향을 미칠 수 있기 때문이다. 다중 상속은 더 위험한데, 계층도의 복잡도를 매우 올리기 때문이다.

더 나은 대안

상속을 쓸 필요가 없는 3가지 기법을 소개한다.

  1. 인터페이스와 프로토콜

객체 지향 언어는 클래스가 특정한 동작을 구현하기로 지정할 수 있고 이때 사용하는 것이 인터페이스다.

1
2
3
4
5
6
public interface Drivable {
    doulbe getSpeed();
    void stop();
}

이 선언 자체가 코드를 만들지 않지만, 이 인터페이스의 구현체에는 우리가 기대하는 동작을 할 것이고 우리는 그를 활용할 수 있을 것이다

  1. 위임

필요로 하는 기능에 대해서 상속하기보다 해당 기능에 위임하는게 낫다.

서비스에 위임해라. Has-A 가 Is-A 보다 낫다.

설정

애플리케이션 출시 이후 바뀔 수 있는 값에 코드가 의존하고 있다면 그 값을 애플리케이션 외부에서 관리하라.

외부 설정으로 애플리케이션을 조정할 수 있게 하라.

대부분의 프레임워크에서 설정은 일반 파일아니 db 테이블로 관리된다. yaml 이나 json 과 같은 파일 형식으로 설정을 관리하거나, 데이터베이스 테이블에 저장해서 구조화하기도 한다. 설정 정보를 전역에서 접근할 수 있도록 하기보다, api 뒤로 숨겨라.
이런 방식을 서비스형 설정이라고 한다. 설정 데이터를 동적으로 바꿀 수 있기 때문에 고가용성 애플리케이션을 만들 때 매우 유용하다. 설정값 하나를 바꾸기 위해 전체 애플리케이션을 멈추고 다시 시작하는 것은 어려운 일이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.