순수 자바 관점을 구현하는 스프링 AOP, JBoss AOP 등과 같은 여러 자바 프레임워크는 내부적으로 프록시를 사용한다. * '순수 자바' 란 AspectJ를 사용하지 않는다는 뜻이다.
스프링은 비즈니스 논리를 POJO로 구현한다.
POJO는 순수하게 도메인에 초점이 맞춘다.
POJO는 엔터프라이즈 프레임워크에 (그리고 다른 도메인에도) 의존하지 않는다. 따라서 테스트가 개념적으로 더 쉽고 간단하다.
상대적으로 단순하기 때문에 사용자 스토리를 올바로 구현하기 쉬우며 미래 스토리에 맞춰 코드를 보수하고 개선하기 편하다.
애플리케이션 도메인 논리를 POJO로 작성할 수 있다면, 즉 코드 수준에서 아키텍처 관심사를 분리할 수 있다면, 진정한 테스트 주도 아키텍처 구축이 가능해진다.
초창기 EJB 아키텍처는 기술을 너무 많이 넣느라 관심사를 제대로 분리하지 못했던 유명한 API 중 하나다.
좋은 API는 걸리적거리지 않아야 한다. 그래야 팀이 창의적인 노력을 사용자 스토리에 집중한다. 그리하지 않으면 아키텍처에 발이 묶여 고객에게 최적의 가치를 효율적으로 제공하지 못한다.
도메인 논리가 흐려지면 제품 품질이 떨어진다.
버그가 숨어들기 쉬워지고, 스토리를 구현하기 어려워지는 탓이다.
기민성이 떨어지면 생산성이 낮아져 TDD가 제공하는 장점이 사라진다.
모든 추상화 단계에서 의도는 명확히 표현해야 한다.
그러려면 POJO를 작성하고 관점 혹은 관점과 유사한 메커니즘을 사용해 각 구현 관심사를 분리해야 한다.
시스템을 설계하든 개별 모듈을 설계하든, 실제로 돌아가는 가장 단순한 수단을 사용해야 한다는 사실을 명심하자.
🤔오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요.
도메인 설계를 왜 POJO로 하는지 개념이 확실하게 잡히지 않았었습니다. => 이번 챕터를 읽으면서 그 이유에 대해서 알 수 있었습니다.
설계가 잘못되면 테스트가 힘들다는 것은 알고 있었는데 정확한 이유를 몰랐었습니다. => 도메인 논리를 POJO로 작성하면서 관심사 분리할 수 있다는 이점이 있었습니다.
🔎궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.
횡단 관심사란?
핵심적인 기능이 아닌 중간중간 삽입되어야 할 기능들 관심들을 횡단관심사라고 한다. ex) 로깅, 보안, 트랜잭션 처리 등 비즈니스 핵심적인 기능이 아닌 다양한 횡단 관심이 발생할 수 있다.
다른 관심사에 영향을 미치는 프로그램의 Aspect이다. 이 관심사들은 디자인과 구현 면에서 시스템의 나머지 부분으로부터 깨끗이 분해되지 못하는 경우가 있을 수 있으며 분산(코드 중복)되거나 얽히는(시스템 간의 상단한 의존성 존재) 일이 일어날 수 있다. - 위키백과 -
여러 비즈니스 로직이 수행될 때 서로 중복되는 로직이 존재하는지에 대한 관심사.
영속성(persistence) 정보란?
영속성은 데이터를 생성한 프로그램의 실행이 종료되더라도 사라지지 않는 데이터의 특성을 의미한다.
영속성은 파일 시스템, 관계형 데이터베이스 혹은 객체 데이터베이스 등을 활용하여 구현한다.
영속성을 갖지 않는 데이터는 단지 메모리에서만 존재하기 때문에 프로그램을 종료하면 모두 잃어버리게 된다.
영속성은 특정 데이터 구조를 이전 상태로 복원할 수 있게 해주어 프로그램의 종료와 재개를 자유롭게 해준다.
👀소감 3줄 요약
도메인 논리를 POJO로 작성하고, 코드 수준에서 아키텍처 관심사를 분리할 수 있다면, 진정한 테스트 주도 아키텍처 구축이 가능해진다.
이상적인 시스템이라면 새 기능을 추가할 때 시스템을 확장할 뿐 기존 코드를 변경하지는 않는다.
변경으로부터 격리
요구사항은 변하기 마련이다. 따라서 코드도 변하기 마련이다.
상세한 구현에 의존하는 클라이언트 클래스는 구현이 바뀌면 위험에 빠진다. 그래서 우리는 인터페이스와 추상 클래스를 사용해 구현이 미치는 영향을 격리한다.
상세한 구현에 의존하는 코드는 테스트가 어렵다.
시스템의 결합도를 낮추면 유연성과 재사용성도 더욱 높아진다.
결합도가 낮다는 소리는 각 시스템 요소가 다른 요소로부터 그리고 변경으로부터 잘 격리되어 있다는 의미다.
시스템 요소가 서로 잘 격리되어 있으면 각 요소를 이해하기도 더 쉬워진다.
DIP(Dependency Inversion Principle)
클래스가 상세한 구현이 아니라 추상화에 의존해야 한다는 원칙이다.
🤔오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요.
OCP, DIP 등 객체지향 선계의 핵심 원칙을 단순히 개념만 공부하는 정도로 했다는 걸 느꼈습니다. => "OCP는 이거다!"라는 개념이 아니라 빌드업 형식으로 책에서 설명해 이해에 도움되었습니다.
강한 결합도의 코드를 짜서 프로젝트의 방향성이 조금만 바뀌어도 프로젝트가 흔들린 경험이 있습니다. => '위와 같은 개념을 알고 있었다면 프로젝트에서 어떻게 적용했을까?'라는 생각을 했습니다. => 당장 적용하는 것은 힘들겠지만, 코드를 작성하고 리팩터링 하면서 배워야겠다고 느꼈습니다.
함수는 물리적인 행 수로 크기를 측정했다. 클래스는 다른 척도로 사용한다. 클래스가 맡은 책임을 센다.
클래스 이름은 해당 클래스 책임을 기술해야 한다. 실제로 작명은 클래스 크기를 줄이는 첫 번째 관문이다.
클래스 설명은 만일("if"), 그리고("and"), -(하)며("or"), 하지만("but")을 사용하지 않고서 25 단어 내외로 가능해야 한다.
단일 책임 원칙(Single Responsibility Principle, SRP)
클래스나 모듈을 변경할 이유가 하나, 단 하나뿐이어야 한다는 원칙이다.
SRP는 '책임'이라는 개념을 정의하며 적절한 클래스 크기를 제시한다. => 클래스는 책임, 즉 변경할 이유가 하나여야 한다는 의미다.
큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더 바람직하다.
응집도(Cohesion)
클래스는 인스턴스 변수 수가 작아야 한다.
각 클래스 메서드는 클래스 인스턴스 변수를 하나 이상 사용해야 한다.
모든 인스턴스 변수를 메서드마다 사용하는 클래스는 응집도가 가장 높다.
클래스가 응집력을 잃는다면 쪼개라!
🤔오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요.
SRP에 대한 개념은 책으로 한번 봤었는데 이해가 전혀 안되었습니다. => 이번에 클린코드를 읽으면서 좀 더 명확하게 이해를 하게 되었습니다. => 예시가 너무 잘 나와있어서 이해에 큰 도움이 되었습니다. => 아직 제가 진행한 프로젝트가 규모가 너무 작아서 클래스가 복잡해지는 경우가 적은 것 같습니다. => 이론으로 공부 하는 것보다는 실질적으로 코드를 보고 타이핑 해보는게 도움이 될 것 같습니다.
🔎궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.
응집도에 대한 부분이 전반적으로 이해가 되지 않았습니다.
클래스는 적은 인스턴스 변수를 가져야 한다.
응집도가 높다는 것은 클래스의 모든 메소드들에 대하여 인스턴스 변수를 사용하는 비율이 높다는 것이다.
응집도가 낮다는 것은 메소드와 변수의 논리적 연결이 약하므로 클래스를 더욱 분리 할 수 있음을 암시한다.
실제 환경에서는 절대로 안 되지만 테스트 환경에서는 전혀 문제없는 방식이 있다. => 대게 메모리나 CPU 효율과 관련 있는 경우다. => 코드의 깨끗함과는 철저히 무관하다.
테스트 당 assert 하나
assert 문이 단 하나인 함수는 결론이 하나라서 코드를 이해하기 쉽고 빠르다.
독자적인 테스트 클래스를 만들어 @Before 함수에 given/when 부분을 넣고 @Test 함수에 then 부분을 넣어도 된다.
assert 문 개수는 최대한 줄여야 좋다.
테스트 당 개념 하나
테스트 함수마다 한 개념만 테스트하라
이것저것 잡다한 개념을 연속으로 테스트하는 긴 함수는 피한다.
F.I.R.S.T
빠르게(Fast) => 테스트가 느리면 자주 돌릴 엄두를 못 낸다. => 자주 돌리지 않으면 초반에 문제를 찾아내 고치지 못한다.
독립적으로(Independent) => 각 테스트는 서로 의존하면 안 된다. => 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안 된다. => 각 테스트는 독립적으로 그리고 어떤 순서로 실행해도 괜찮아야 한다. => 테스트가 서로 의존하면 하나가 실패할 때 나머지도 잇달아 실패해 원인을 진단하기 어렵다.
반복 가능하게(Repeatable) => 테스트는 어떤 환경에서도 반복 가능해야 한다.(실제 환경, QA 환경, 집에 있는 노트북 등) => 테스트가 돌아가지 않는 환경이 하나라도 있다면 테스트가 실패한 이유를 둘러댈 변경이 생긴다.
자가 검증하는(Self-Validating) => 테스트는 부울(bool) 값으로 결과를 내야 한다.(성공 아니면 실패) => 통과 여부를 알려고 로그 파일을 읽게 만들어서는 안 된다.
적시에(Timely) => 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다. => 테스트가 불가능하도록 실제 코드를 설계할지도 모른다.
🤔오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요.
given-when-then이라는 관례를 함수명에 넣은 것은 처음 봤습니다. => 제가 작성했던 코드에는 주석을 달아서 명시해줬었습니다. => 보통 서비스 로직에 있는 메서드를 가져와서 테스트를 했기 때문에 이 방법을 적용하기 힘들 것 같습니다.
단위 테스트를 먼저 구현하고 실제 코드를 작성한다는 부분이 흥미로웠습니다. => 프로젝트를 하면서 한번 해보고 싶었지만, 같은 팀원이 테스트에 부정적이라 적용이 힘들었습니다. => 테스트를 먼저 작성한다면 그것을 어떻게 구상할지 생각해봐야겠다고 생각했습니다. => 확실히 이미 구현된 실제 코드를 단위 테스트로 구현하는건 제한 사항이 있었습니다. * 코드 커버리지를 올리기 정말 힘들었습니다.
🔎궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.
테스트를 빨리 돌리는 부분에서 어떻게 해야 할지 감이 안 잡힙니다. => 제가 진행했던 프로젝트에서는 규모가 커질수록 테스트가 엄청 오래 걸렸습니다. => 좋은 단위 테스트는 속도가 빠르다는 게 이 부분을 개선하도록 해봐야겠습니다.