소개
클린아키텍처: 소프트웨어 구조와 설계의 원칙 책을 읽고 정리하며 소감을 적는 포스트입니다.
함수형 프로그래밍
이 패러다임에서 핵심이 되는 기반은 람다(lambda) 계산법으로 알론조 처치(alonzo Church)가 1930년대에 발명했다.
정수를 제곱하기
함수형 프로그래밍이 무엇인지 설명하기 위해 아래의 예제를 보자
1
2
3
4
5
6
public class Squint {
public static void main(String args[]){
for(int i=0; i<25; i++)
System.out.println(i*i);
}
}
리스프에서 파생한 클로저(Clojure)는 함수형 언어로 아래와 같이 구현 할 수 있다.
1
2
3
4
5
6
7
(println(take 25(map(fn[x](* x x)) (range))))
(println :___ 출력한다.
(take 25 :___ 처음부터 25까지
(map(fn[x](* x x)) :___ 제곱을
(range)))) :___ 정수의
자바는 가변 변수(mutable variable)를 사용하는데, 가변 변수는 프로그램 실행 중에 상태가 변할 수 있다.
반복문을 제어하는 변수인 i가 가변 변수다.
하지만 클로저에서는 x와 같은 변수가 한 번 초기화 되면 절대로 변하지 않는다.
불변성와 아키텍처
왜 아키텍트는 변수의 가변성을 염려해야 하는가?? 단순한 이유이지만 경합(race)조건, 교착 상태(deadlock) 조건, 동시 업데이트(concurrent update) 문제가 모두 가변 변수로 인해 발생하기 때문이다.
락(lock)이 가변적이지 않다면 교착상태도 일어나지 않는다.
아키텍트라면 동시성(concurrency)문제에 지대한 관심을 가져야만 한다.
가변성의 분리
불변성과 관련하여 가장 주요한 타협 중 하나는 애플리케이션, 또는 애플리케이션 내부의 서비스를 가변 컴포넌트와 불변 컴포넌트로 분리하는 일이다.
상태 변경과 트랜잭션 메모리(transactional memory)
상태 변경은 컴포넌트를 갖가지 동시성 문제에 노출하는 꼴이므로, 흔히 트랜잭션 메모리(transactional memory)와 같은 실천법을 사용하여 동시 업데이트와 경합 조건 문제로부터 가변 변수를 보호한다.
트랜잭션 메모리는 데이터베이스가 디스크의 레코드를 다루는 방식과 동일한 방식으로 메모리의 변수를 처리한다. 즉, 트랜잭션을 사용하거나 또는 재시도 기법을 통해 이들 변수를 보호한다.
하지만 안타깝게도 여러 변수가 상호 의존하는 상황에서는 동시 업데이트와 교착상태 문제로 부터 보호해 주지 못한다.
애플리케이션을 제대로 구조화하려면 변수를 변경하는 컴포넌트와 변경하지 않는 컴포넌트를 분리해야 한다.
현명한 아키텍트라면 가능한 한 많은 처리를 불변 컴포넌트로 옮겨야 하고, 가변 컴포넌트에서는 가능한 한 많은 코드를 빼내야 한다.
이벤트 소싱
간단한 예로, 고객의 계좌 잔고를 관리하는 은행 애플리케이션에서 입근 트랜잭션과 출금 트랜잭셩이 실행되면 잔고를 변경해야 한다.
이제 계좌 잔고를 변경하는 대신 트랜잭션 자체를 저장한다고 상상해 보다.
터무니 없는 접근법이다. 시간이 지날수록 트랜잭션 수는 끝없이 증가하고, 컴퓨팅 자원은 걷잡을 수 없이 커진다.
하지만 이 전략이 영원히 동작할 필요는 없다. 애플리케이션의 수명주기 동안만 문제 없이 동작할 정도의 저장 공간과 처리 능력만 있으면 충분할 것이다.
이것이 이벤트 소싱(event sourcing)의 기본 발상이다.
이벤스 소싱은 상태가 아닌 트랜잭션을 저장하자는 전략이다. 상태가 필요해지면 단순히 상태의 시작점부터 모든 트랜잭션을 처리한다.
결과적으로 애플리케이션은 CRUD가 아니라 CR만 수행한다. 저장공간과 처리능력이 충분하면 애플리케이션이 완전한 불변성을 갖도록 만들 수 있고, 따라서 완전한 함수형으로 만들수 있다.
결론
- 구조적 프로그래밍은 제어흐름의 직접적인 전환에 부과되는 규율이다.
- 객체 지향 프로그래밍은 제어흐름의 간접적인 전환에 부과되는 규율이다.
- 함수형 프로그래밍은 변수 항당에 부과되는 규율이다.
도구는 달라졌고 하드웨어도 변했지만, 소프트웨어의 핵심은 여전히 그대로다.
순차(sequence), 분기(selection), 반복(iteration), 참조(indirection)으로 구성된다.
그 이상도 이하도 아니다.