diff --git "a/11\354\236\245/\355\231\251\353\217\231\354\244\200.md" "b/11\354\236\245/\355\231\251\353\217\231\354\244\200.md" new file mode 100644 index 0000000..8b57fa2 --- /dev/null +++ "b/11\354\236\245/\355\231\251\353\217\231\354\244\200.md" @@ -0,0 +1,43 @@ +## 11장 시스템 + +<196p>
+체계적이고 탄탄한 시스템을 만들고 싶다면 흔히 쓰는 좀스럽고 손쉬운 기번으로 모듈성을 깨서는 절대로 안 된다. 객체를 생성하거나 의존성을 연결할 때도 마찬가지다. 설정 논리는 일반 실행 논리와 분리해야 모듈성이 높아진다. 또한 주요 의존성을 해소하기 위한 방식, 즉 전반적이며 일관적인 방식도 필요하다. + +<199p>
+‘처음부터 올바르게’ 시스템을 만들 수 있다는 믿음은 미신이다. 대신에 우리는 오늘 주어진 사용자 스토리에 맞춰 시스템을 구현해야 한다. 내일은 새로운 스토리에 맞춰 시스템을 조정하고 확장하면 된다. 이것은 반복적이고 점진적인 애자일 방식의 핵심이다. 테스트 주도 개발, 리펙터링, 깨끗한 코드는 코드 수준에서 시스템을 조정하고 확장하기 쉽게 만든다.
-> 확장성을 보장하기 위해서 깨끗한 코드를 짜는 것은 필수적이라고 생각합니다. 이를 위해 필요한 방법론이 TDD와 리펙터링이고요. 다른 분들은 깨끗한 코드, 즉 확장성을 보장하는 코드를 작성하기 위해 어떤 노력을 하는지 궁금합니다. + +하지만 시스템 수준에서는 어떨까? 시스템 아키텍처는 사전 계획이 필요하지 않을까? 단순한 아키텍처를 복잡한 아키텍처로 조금씩 키울 수 없다는 현실은 정확하다. 맞는 말 아닌가?
+-> ㅇㅈ 해당 답변이 아래에 바로 나오네요 + +소프트웨어 시스템은 물리적인 시스템과 다른다. 관심사를 적절히 분리해 관리한다면 소프트웨어 아키텍처는 점진적으로 발전할 수 있다. + +<203p>
+사실 EJB 아키텍처가 영속성, 보안, 트랜잭션을 처리하는 방식은 관점 지향 프로그래밍을 예견했다고 보인다. AOP는 횡단 관심사에 대처해 모듈성을 확보하는 일반적인 방법론이다.
+-> 좋은 방법론인거 같은데 횡단 관심사가 무엇일까요? 횡단 관심사는 핵심적인 기능이 아닌 중간중간 삽입되어야 할 기능에 대한 관심을 말합니다. 은행 시스템으로 가정한다면 다음과 같습니다. + +핵심 관심사 : 입금, 출금, 이체 +횡단 관심사 : 임금, 출금, 이체 과정에서 동작하는 보안 처리 혹은 예외 처리 + +image + +위 그림을 통해 한번에 이해됐습니다. + +AOP에서 관점(aspect)이라는 모듈 구성 개념은 “특정 관심사를 지원하려면 시스템에서 특정 지점들이 동작하는 방식을 일관성 있게 바꿔야 한다”라고 명시한다. 명시는 간결한 선언이나 프로그래밍 메커니즘으로 수행한다. + +<210p>
+애플리케이션에서 도메인 논리를 POJO로 작성할 수 있다면, 즉 코드 수준에서 아키텍처 관심사를 분리할 수 있다면, 진정한 테스트 주도 아키텍처 구축이 가능해진다. 그때그때 새로운 기술을 채택해 단순한 아키텍처를 복잡한 아키텍처로 키워갈 수도 있다. BDUF(Big Design Up Front)를 추구할 필요가 없다. 실제로 BDUF는 해롭기까지 하다. 처음에 쏟아 부은 노력을 버리지 않으려는 심리적 저항으로 인해, 그리고 처음 선택한 아키텍처가 향후 사고 방식에 미치는 영향으로 인해, 변경을 쉽사리 수용하지 못하는 탓이다.
+-> POJO(Plain Old Java Object)는 아주 단순한 자바 객체(오래된 방식의 간단한 자바 객체)로 특정 기술에 종속되어 있지 않은 상태의 객체를 뜻합니다. 위와 같은 방식을 프론트엔드에 적용하기 위해선 어떤 방식으로 적용하면 좋을지 고민하고 좋은 방법을 구상해 봐야겠다는 생각이 들었습니다. + +그렇다고 ‘아무 방향 없이’ 프로젝트에 뛰어들어도 좋다는 소리는 아니다. 프로젝트를 시작할 때는 일반적인 범위, 목표, 일정은 물론이고 결과로 내놓을 시스템의 일반적인 구조도 생각해야 한다. 하지만 변하는 환겨에 대처해 진로를 변경할 능력도 반드시 유지해야 한다. + +<211p>
+설계가 아주 멋진 API조차도 정말 필요하지 않으면 과유불급이다. + +모듈을 나누고 관심사를 분리하면 지엽적인 관리와 결정이 가능해진다. + +가장 적합한 사람에게 책임을 맡기면 가장 좋다. 우리는 때때로 가능한 마지막 순간까지 결정을 미루는 방법이 최선이라는 사실을 까먹곤 한다. 게으르거나 무책임해서가 아니다. 최대한 정보를 모아 최선의 결정을 내리기 위해서다. 성급한 결정은 불충분한 지식으로 내린 결정이다. 너무 일찍 결정하면 고객 피드백을 더 모으고, 프로젝트를 더 고민하고, 구현 방안을 더 탐험할 기회가 사라진다.
+-> 여기서 말하는 가능한 마지막 순간까지 결정을 미루는 방법은 사용자의 피드백, 프로젝트에 대한 고민, 좋은 구현 방안을 더 탐험하는 시간을 말합니다. 아무것도 하지 않은 채로 결정을 미루는 것이 아니라 최선의 결정을 할 수 있도록 많은 고민과 연구를 거치고 가능한 마지막에 결정을 하는 것을 말한다는 생각이 드네요 + +<212p>
+EJB2는 단지 표준이라는 이유만으로 많은 팀이 사용했다, 가볍고 간단한 설계로 충분했을 프로젝트에서도 EJB2를 채택했다. 나는 업계에서 여러 형태로 아주 과장되게 포장된 표준에 집착하는 바람에 고객 가치가 뒷전으로 밀려난 사례를 많이 봤다.
+-> 기술을 선택함에 있어 가장 중요한 부분이라고 생각합니다. 표준이 되는 것이 중요한 것이 아니라 사용자 니즈를 충족하고, 주어진 상황을 해결할 수 있는 기술 선택이 중요하다고 생각합니다. 백엔드에서는 무작정 spring을 선택하는 방식, 프론트에서는 무작정 Next.js를 선택하는 방식을 예로 들 수 있다고 생각해요. 두 프레임워크 모두 상당히 몸집이 거대합니다. 간단한 설계가 필요한 상황에 사용되면, 오히려 프로젝트의 복잡도를 높일 수 있다고 생각해요. 그렇기에 기술을 선택함에 있어 항상 신중하고, 상황에 맞아야 한다고 생각합니다! diff --git "a/12\354\236\245/\355\231\251\353\217\231\354\244\200.md" "b/12\354\236\245/\355\231\251\353\217\231\354\244\200.md" new file mode 100644 index 0000000..5f10885 --- /dev/null +++ "b/12\354\236\245/\355\231\251\353\217\231\354\244\200.md" @@ -0,0 +1,45 @@ +## 12장 창발성 + +<216p> + +SRP(Single Responsibility Principle) - 단일 책임 원칙 +DIP(Dependency Inversion Principle) - 의존 관계 역전 원칙 + +우리들 대다수는 켄트 백이 제시한 단순한 설계 규칙 네 가지가 소프트웨어 설계 품질을 크게 높여준다고 믿는다. + +- 모든 테스트를 실행한다. +- 중복을 없앤다. +- 프로그래머 의도를 표현한다. +- 클래스와 메서드 수를 최소로 줄인다. + +시스템이 의도한 대로 돌아가느지 검증할 간단한 방법이 없다면, 문서 작성을 위해 투자한 노력에 대한 가치는 인정받기 힘들다. + +다행스럽게도, 테스트가 가능한 시스템을 만들려고 애쓰면 설계 품질이 더불어 높아진다.
+-> 공감되는 말입니다. 프론트엔드 에서도 테스트를 하기 위해 코드를 리팩토링 하다보면, 코드의 단일 책임 원칙이 잘 지켜지고, 가독성도 올라가는 것을 경험한 적인 있습니다. + +<217p> + +단순한 설계 규칙 1: 모든 테스트를 실행하라 +테스트 케이스를 많이 작성할수록 개발자는 DIP와 같은 원칙을 적용하고, 의존성 주입, 인터페이스, 추상화 등과 같은 도구를 사용해 결합도를 낮춘다 따라서 설계 품질은 더욱 높아진다. + +단순한 설계 규칙 2~4: 리팩터링 +코드를 정리하면서 시스템이 꺠질까 걱정할 필요가 없다. 테스트 케이스가 있으니까! 리팩터링 단계에서는 소프트웨어 설계 품지릉ㄹ 높이는 기법이라면 무엇이든 적용해도 괜찮다. 응집도를 높이고, 결합도를 낮추고, 관심사를 분리하고, 시스템 관심사를 모듈로 나누고, 함수와 클래스 크기를 줄이고, 더. 나은 이름을 선택하는 등 다양한 기법을 동원한다. +또한 이 단계는 단순한 설계 규칙 중 나머지 3개를 적용해 중복을 제거하고, 프로그래머 의도를 표현하고, 클래스와 메서드 수를 최소로 줄이는 단계이기도 하다. + +<221~222p> + +자신이 이해하는 코드를 짜기는 쉽다. 코드를 짜는 동안에는 문제에 푹 빠져 코드를 구석구석 이해하니까. 하지만 나중에 코드를 유지보수할 사람이 코드를 짜는 사람만큼이나 문제를 깊이 이해할 가능성은 희박하다. +소프트웨어 프로젝트 비용 중 대다수는 장기적은 유지보수에 들어간다. 코드를 변경하면서 버그의 싹을 심지 않으려면 유지보수 개발자가 시스템을 제대로 이해해야 한다. +~ +그러므로 코드는 개발자의 의도를 분명히 표현해야 한다. 개발자가 코드를 명백하게 짤수록 다른 사람이 그 코드를 이해하기 쉬워진다, 그래야 결함이 줄어들고 유지보수 비용이 적게 든다. + +1. 우선, 좋은 이름을 선택한다. +2. 둘째, 함수와 클래스 크기를 가능한 줄인다. +3. 세째, 표준 명칭을 사용한다. 예를 들어 디자인 패턴은 의사소통과 표현력 강화가 주요 목적이다. 클래스가 COMMAND나 VISTOR와 같은 표준 패턴을 사용해 구현된다면 클래스 이름에 패턴 이름을 넣어준다. 그러면 다른 개발자가 클래스 설계 의도를 이해하기 쉬워진다. +4. 네째, 단위 테스트 케이스를 꼼꼼히 작성한다. + +**하지만 표현력을 제일 높이는 가장 중요한 방법은 노력이다.** + +흔히 코드만 돌린 후, 다음 문제로 직행하는 사례가 너무도 흔하다. 나중에 읽을 사람을 고려해 조금이라도 읽기 쉽게 만들려는 충분한 고민은 거의 찾기 어렵다. **하지만 나중에 코드를 읽을 사람은 바로 자신일 가능성이 높다는 사실을 명심하자.** **그러므로 자신의 작품을 조금 더 자랑하자** + +클래스와 메서드 수를 최소로 줄여라 - 목표는 함수와 클래스 크기를 작게 유지하면서 동시에 시스템 크기도 작게 유지하는 데 있다. 하지만 이 규칙은 간단한 걸계 규칙 네 개 중 우선순위가 가장 낮다 다시 말해, 클래스와 함수 수를 줄이는 작업도 중요하지만, 테스트 케이스를 만들고 중복을 제거하고 의도를 표현하는 작업이 더 중요하다는 뜻이다. diff --git "a/13\354\236\245/\355\231\251\353\217\231\354\244\200.md" "b/13\354\236\245/\355\231\251\353\217\231\354\244\200.md" new file mode 100644 index 0000000..2334425 --- /dev/null +++ "b/13\354\236\245/\355\231\251\353\217\231\354\244\200.md" @@ -0,0 +1,31 @@ +## 13장 동시성 + +<226p> + +동시성은 결합을 없애는 전략이다. 즉 무엇과 언제를 분리하는 전략이다. +~ 무엇과 언제를 분리하면 애클리케이션 구조와 효율이 극적으로 나아진다. + +<230p> + +동시성 방어 원칙 + +단일 책임 원칙
+SRP는 주어진 메서드/클래스/컴포넌트를 변경할 이유가 하나여야 한다는 원칙이다. 동시성은 복잡성 하나만으로도 따로 분리할 이유가 충분하다. 즉, 동시성 관련 코드는 다른 코드와 분리해야 한다는 뜻이다. 그런데 불행히도 동시성과 간련이 없는 코드에 동시성을 곧바로 구현하는 사례가 너무도 흔하다. 동시성을 구현할 때는 다음 몇 가지를 고려한다. + +- 동시성 코드는 독자적인 개발, 변경, 조율 주기가 있다. +- 동시성 코드에는 독자적인 난관이 있다. 다른 코드에서 겪는 난관과 다르면 훨씬 어렵다. +- 잘못 구현한 동시설 코드는 별의별 방식으로 실패한다. 주변에 있는 다른 코드가 발목을 잡지 않더라도 동시성 하나만으로도 충분히 어렵다. + +따름 정리: 자료 범위를 제한하라 +앞서 봤듯이, 객체 하나를 공유한 후. 동일 필드를 수정하던 두. 스레드 가 서로 간섭하므로 예상치 못한 결과를 내놓는다. 이런 문제를 해결하는 방안으로 공유 객체를 사용하는 코드 내 임계영역을 synchroniaed 키워드로 보호하라고 권장한다. + +<243p> + +다중 스레드 코드는 올바로 구현하기 어렵다. 간단했던 코드가 여러 스레드와 공유 자료를 추가하면서 악몽으로 변한다. 다중 스레드 코드를 작성한다면 각별히 깨끗하게 코드를 짜야 한다. + +무엇보다 먼저, SRP를 준수한다. POJO를 사용해 스레드를 아는 코드와 스레드를 모르는 코드를 분리한다. 그레드 코드를 테스트할 때는 전적으로 스레드만 테스트한다. 즉, 그레드 코드는 최대한 집약되고 작아야 한다는 의미다. + +**사용하는 라이브러리와 기본 알고리즘을 이해한다.** 특정 라이브러리 기능이 기본 알고리즘과 유사한 어떤 문제를 어떻게 해결하는지 파악한다.
+-> 매우 중요한 소양이라고 생각합니다. + +어떻게든 문제는 생긴다. 초반에 드러나지 않는 문제는 일회성으로 치부해 무시하기 십상이다. 소위 일회성 문제는 대개 시스템에 부하가 걸리 때나 아니면 뜬금없이 발생한다. 그러므로 스레드 코드는 많은 플랫폼에서 많은 설정으로 반복해서 계속 테스트해야 한다. **테스트 용이성은 TDD 3대 규칙을 따르면 자연히 얻어진다.**