From 84a79a542f34955f28f36c903b85ff32af6440ba Mon Sep 17 00:00:00 2001 From: Orchemi <86189596+Orchemi@users.noreply.github.com> Date: Sun, 25 Aug 2024 16:11:54 +0900 Subject: [PATCH] =?UTF-8?q?Docs:=20=EB=B0=95=EC=8A=B9=ED=9B=88=205?= =?UTF-8?q?=EC=9E=A5=20(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\353\260\225\354\212\271\355\233\210.md" | 363 ++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 "5\354\236\245/\353\260\225\354\212\271\355\233\210.md" diff --git "a/5\354\236\245/\353\260\225\354\212\271\355\233\210.md" "b/5\354\236\245/\353\260\225\354\212\271\355\233\210.md" new file mode 100644 index 0000000..63536a0 --- /dev/null +++ "b/5\354\236\245/\353\260\225\354\212\271\355\233\210.md" @@ -0,0 +1,363 @@ +## 들어가며 + +> 이번 장에서 기대할 수 있는 내용들 + +- 되돌릴 수 있는 의사 결정을 내리는 구체적인 방법 +- 관계 없는 개념들을 분리하여 결합도를 낮추는 방법 +- 이벤트를 관리하고, 이벤트에 반응하는 전략 +- 결합도가 낮은 함수 파이프라인 기법 +- 유연하고 바꾸기 쉬운 코드를 만들 수 있는 더 나은 대안 +- 세부 사항을 완전히 코드 밖으로 옮기는 방법 + +
+ +## Topic 28 : 결합도 줄이기 + +소프트웨어의 구조는 유연해야 한다. 그리고 유연하려면 각각의 부품이 다른 부품에 가능한 한 조금만 연결되어야 한다. + +
+ +### 결합도가 낮은 코드가 바꾸기 쉽다 + +- 결합은 두 개의 코드 조각이 무언가를 공유하면 언제나 일어날 수 있다. +- 다음과 같은 결합의 증상을 놓치지 않도록 주의한다. + + 관계 없는 모듈이나 라이브러리 간의 희한한 의존관계 + + 한 모듈의 '간단한' 수정이 이와 관계없는 모듈을 통해 시스템 전역으로 퍼져 나가거나 시스템의 다른 곳에서 무언가를 깨뜨리는 경우 + + 개발자가 수정하는 부분이 시스템에 어떤 영향을 미칠지 몰라 코드의 수정을 두려워하는 경우 + + 변경 사항에 누가 영향을 받는지 파악하고 있는 사람이 없어서 결국 모든 사람이 참석해야 하는 회의 + +
+ +### 묻지 말고 답하라(TDA; Tell, Don't Ask) + +- 다른 객체의 내부 상태에 따라 판단을 내리고 그 객체를 갱신해서는 안 된다. +- 객체의 내부 상태를 묻는 것 + + 캡슐화의 장점이 사라진다. + + 구현에 대한 지식이 코드 여기저기로 퍼져 버린다. + +
+ +### 메서드 호출을 엮지 말라 + +- 애플리케이션에 있는 것은 모두 바뀌리라 생각해라. +- 외부의 라이브러리 역시 불안정하다고 여겨라. + +
+ +### 파이프라인 + +- 파이프라인은 구현 세부사항을 숨기는 객체지향과 달리 구현과정을 공개한다. +- 파이프라인이 결합을 하나도 만들지 않는 것은 아니다. +- 파이프라인의 함수에서 반환하는 데이터는 반드시 다음 함수가 처리할 수 있는 형식이어야 한다. + +
+ +> 파이프라인에 대한 나의 생각 + +파이프라인의 예시로는 Promise가 생각났다. `.then()`, `.catch()`, `.finally()` Promise 객체의 정적 메서드는 또다른 Promise 객체를 반환하여 같은 동작을 이어나갈 수 있게 한다. 또는 Builder Pattern을 사용한 pipeline 방식도 최근에 접했는데 코드 구현은 다음과 같다. + +
+ +```java +// 출처 : https://inpa.tistory.com/entry/GOF-💠-빌더Builder-패턴-끝판왕-정리 + +Student student = new StudentBuilder(2016120091) + .setName("홍길동") + .setGrade("freshman") + .setPhoneNumber("010-5555-5555") + .build(); + + +class StudentBuilder { + // 초기화 필수 멤버 + private int id; + + // 초기화 선택적 멤버 + private String name; + private String grade; + private String phoneNumber; + + // 필수 멤버는 빌더의 생성자를 통해 설정 + public StudentBuilder(int id) { + this.id = id; + } + + // 나머지 선택 멤버는 메서드로 설정 + public StudentBuilder name(String name) { + this.name = name; + return this; + } + + ... + + public Student build() { + return new Student(id, name, grade, phoneNumber); + } +} +``` + +이 빌더 패턴 역시 class 인스턴스의 메서드들이 this를 반환하며 계속 인스턴스 스스로를 이어갈 수 있도록 하는 방식인데, 다음 함수가 처리할 수 있는 형식이라는 점에서 내용의 맥락과 비슷하다고 느꼈다. + +
+ +### 글로벌화의 해악 + +- 어디서나 접근할 수 있는 데이터는 교묘하게 애플리케이션 컴포넌트 간의 결합을 만들어 낸다. +- 전역 데이터의 구현을 변경할 때 시스템 코드 전체에 영향을 줄 수 있음음 분명하다. +- 문제는 바꿔야 하는 곳을 모두 바꿨는지 확인하기 힘들다는 데 있다. + +
+ +### 코드 재사용과 전역 데이터 + +- 코드를 처음 작성하는 시점의 제 1 관심사가 코드 재사용이어서는 안 될 것 +- 코드를 재사용할 수 있게 하려면 깨끗한 인터페이스를 만들고 나머지 코드와의 결합을 없애야 한다. +- 코드가 전역 데이터를 사용한다면 나머지로부터 떼어 내기 힘들어질 것이다. + +
+ +> 전역 데이터에 대한 나의 생각 + +프론트엔드 개발자의 입장에서 생각했을 때 전역 데이터를 쓰지 않는 것이 좋은 이유는 리렌더링과 관련된 성능 저하 위주로만 생각했다. 그리고 컴포넌트 트리를 따라 내려가면서 props drilling이 너무 심해질 때 전역 데이터는 이것을 해결할 수 있는 좋은 대안이라고만 생각했다. + +
+ +하지만 생각해보면 전역 데이터를 피하는 것이 좋다는 이야기를 많이 들었는데 그 근거를 제대로 알지 못했다가, 어디서든 접근하고 변경할 수 있다는 것은 양날의 검이라는 사실, 특히 재사용과 유지보수에서 어려움이 있을 것이라는 것을 다시 느끼게 되었다. 앞으로는 보수적, 지역적으로 사용할 필요를 느꼈다. + +
+ +### 외부 리소스도 전역 데이터다 + +- 수정 가능한 외부 리소스는 모두 전역 데이터다. +- 해법은 반드시 이 리소스들을 여러분이 작성하는 코드로 모두 감싸는 것이다. +- **전역적이어야 할 만큼 중요하다면 API로 감싸라.** + +
+ +### 결국은 모두 ETC + +- 결합된 코드는 바꾸기 힘들다. +- 코드의 한 곳을 바꾸면 다른 곳에 여파가 미칠 수 있다. +- 직접적으로 아는 것만 다루는 부끄럼쟁이 코드를 계속 유지하라. + +
+ +## Topic 29 : 실세계를 갖고 저글링하기 + +- 우리가 작성하는 애플리케이션은 맡은 일을 어떻게든 수행해야 한다. +- 비록 우리의 세계는 엉망이고 사람들은 생각을 자주 바꾸지만 말이다. + +
+ +### 이벤트 + +- 이벤트는 무언가 정보가 있다는 것을 의미한다. +- 어떻게 이벤트에 잘 반응하는 애플리케이션을 만들 수 있을까? + +
+ +### 감시자 패턴 + +- 이벤트를 발생시키는 '감시대상(observable)'과 이벤트에 관심이 있는 클라이언트인 '감시자(observer)'로 구성된다. +- 동작 방식 + + 감시자는 자신이 관심 있는 이벤트를 감시 대상에 등록한다. 호출될 함수의 참조도 등록할 때 함께 넘긴다. + + 해당 이벤트가 발생하면 감시 대상은 등록된 감시자 목록을 보며 함수들을 일일이 호출한다. 이때, 발생한 이벤트를 감시자 함수의 인자로 넘긴다. + +
+ +### 감시자 패턴의 문제 + +- 모든 감시자가 감시 대상에 등록을 해야 하기 때문에 결합이 생긴다. +- 일반적으로 감시 대상이 콜백을 직접 호출하도록 구현하기 때문에 성능 병목이 될 수 있다. (동기 처리 특성상 콜백 실행을 마칠 때까지 감시 대상이 계속 기다려야 함) + +
+ +### 게시-구독 모델 + +- 게시-구독(Publish-Subscribe) 혹은 발행-구독 모델, 줄여서 pubsub이라고 부른다. +- 감시자 패턴을 일반화한 것 +- 감시자 모델의 결합도를 높이는 문제와 성능 문제도 해결 +- 동작 방식 + + 구독자는 관심사를 하나 이상의 채널에 등록하고 게시자는 채널에 이벤트를 보낸다. + + 감시자 패턴과는 다르게 게시자와 구독자 사이의 통신은 코드 밖에서 일어난다. + + (아마 비동기적으로) + +
+ +### 게시-구독 모델의 장점 + +- 추가적인 결합 없이 비동기 이벤트 처리를 구현하기에 좋다. +- 다른 기존 코드를 수정하지 않고 이벤트 처리 코드를 추가하거나 교체할 수 있다. +- 애플리케이션이 작동하고 있는 도중에 작업이 가능할 수도 있다. + +
+ +### 게시-구독 모델의 단점 + +- 게시-구독 모델을 아주 많이 사용하는 시스템에서는 현재 어떤 일이 벌어지고 있는 지 파악하기가 힘들다. +- 게시자가 메시지를 보내는 것을 확인했더라도 어떤 구독자가 메시지를 처리하는지 바로 이어서 볼 수 없다. + +
+ +### 스트림 모델 + +- 이벤트를 이리저리 연결하는 것은 쉽지 않다. +- 스트림은 이벤트를 일반적인 자료 구조처럼 다룰 수 있게 해준다. +- 익숙한 방식으로 스트림을 다룰 수 있어서 좋다. 우리가 아는 온갖 작업을 일반적인 자료 구조와 마찬가지 방법으로 할 수 있다. +- 이벤트 스트림과 일반 자료 구조를 조합할 수 있다. +- Rxjs가 그 예시 +- 이벤트 스트림은 동기적 처리와 비동기적 처리를 하나의 편리한 공통 API로 감싸서 통합한다. + +
+ +### 이벤트를 다룰 때 + +- 이벤트는 모든 곳에 있다. +- 이벤트가 어디서 발생하든 이벤트를 중심으로 공들여 만든 코드는 일직선으로 수행되는 코드보다 더 잘 반응하고 결합도가 더 낮다. + +
+ +## Topic 30 : 변환 프로그래밍 + +- 프로그램이란 입력을 출력으로 바꾸는 것이라는 사고방식으로 돌아갈 필요가 있다. +- 구조는 명확해지고 더 일관적으로 오류를 처리하게 되어 결합도 대폭 줄어들 것이다. + +
+ +### 변환 찾기 + +- 요구사항에서 시작하는 게 변환을 찾는 가장 쉬운 방법 +- 요구사항의 입력과 출력이 무엇인지 찾아라. +- 그러면 전체 프로그램을 나타내는 함수가 정해진다. +- 이제 더 작은 단계로 나누어 각각을 찾아간다. +- (일종의 top-down 접근 방식) +- (위의 파이프라인과 연결) + +
+ +### 객체 지향 프로그래밍과의 비교 + +- 객체 지향 프로그래밍 + + 객체 지향 프로그래밍 경험이 많다면 반사적으로 데이터를 숨기고, 객체 안에 캡슐화해야 한다고 느낄 것이다. + + 이런 객체들은 서로 이리저리 이야기하며 서로의 상태를 변경 + + 결합을 많이 만들어 내고, 결국 객체 지향 시스템이 바꾸기 어려워지는 큰 요인 + +- 변환 모델 + + 요구 사항을 달성하기 위해 필요한 것은 하나로 연결된 변환들 뿐 + + 데이터를 전체 시스템 여기저기의 작은 웅덩이에 흩어 놓는 대신, 데이터를 거대한 강으로, 흐름으로 생각 + + 데이터는 기능과 동등해진다. + + 데이터는 더 이상 특정한 함수들과 묶이지 않는다.(클래스와 달리) + + 어떤 함수든 매개 변수가 다른 함수의 출력 결과와 맞기만 하면 어디서나 사용하고 또 재사용할 수 있다. + +
+ +### 오류 처리는 어떻게 하나? + +- 연쇄 변환이 일직선으로만 이어진다면 어떻게 오류 검사에 필요한 조건부 논리를 추가할 수 있을까? +- 공통적으로 사용하는 기본적인 관례가 있다. 바로 변환 사이에 값을 절대 날것으로 넘기지 않는 것 + + 래퍼(wrapper) 역할을 하는 자료 구조나 타입으로 값을 싸서 넘긴다. + + 이런 자료 구조나 타입은 안에 들어 있는 값이 유효한지를 추가로 알려 준다. + + 파이프라인의 각 함수들은 내부에서 특정 오류에 대해 확인하는 코드들을 내재하고, 파이프 진행 중 오류를 맞이하면 파이프라인 전체를 중단하고 오류를 반환한다. + +
+ +### 변환은 프로그래밍을 변환한다 + +- 코드를 일련의 (중첩된) 변환으로 생각하는 접근 방식은 프로그래밍을 해방시킨다. +- 습관을 들이면 코드가 더 명확해지고, 함수는 짧아지며, 설계는 단순해진다. + +
+ +## Topic 31 : 상속세 + +> 상속을 사용하고 있다면 아마 여러분에게 필요한 것은 상속이 아닐 것이다. + +
+ +### 상속의 문제 + +- 상위 클래스를 수정했을 때 이를 상속하는 하위 클래스는 함께 망가질 수 있다.(결합이 많다.) +- 계층 위에 계층을 덧붙이다 보면, 클래스 계층도는 엄청나게 복잡해진다. 이는 애플리케이션을 취약하게 만든다. +- 더 나쁜 것은 다중 상속이다. 특정 클래스가 여러 다른 도메인의 상위 클래스가 될 수 있다면 추후 변경을 할 수 없는 지경이 된다. +- (다행히도 이제는 많은 객체 지향 언어에서 다중 상속을 지원하지 않는다.) + +
+ +### 상속을 쓸 필요가 없게 해주는 기법 + +- 인터페이스와 프로토콜 + + 선언들은 아무런 코드를 만들지 않는다. + + 타입으로 사용할 수 있다. + + 해당 인터페이스를 구현하는 클래스라면 무엇이든 그 타입과 호환된다. + + 다형성은 인터페이스로 표현하는 것이 좋다. +- 위임 +- 믹스인과 트레이트 + +
+ +> 상속에 대한 나의 생각 + +프론트엔드 진영에서 상속이라 할만한 것은 interface가 interface를 상속하는 정도일 것 같다. 그런데 상속의 부모 인터페이스의 변경으로 인해 자식 인터페이스가 함께 영향을 받는 일도 비일비재했다. 하지만 이를 더 쪼개서 더 작은 인터페이스로 만들고 이를 상속하는 것은 조금 애매한 것 같다. + +
+ +그리고 위임과 믹스인, 트레이트는 도대체 뭘 말하는건지 모르겠다. 자바같은 객체 지향 언어는 조금 더 와닿을 수도 있을 것 같은데 아직까지는 상속을 그리 사용해보지는 않았다. 다만 다음에 클래스형으로 개발할 일이 있을 때 참고해 봐야겠다. 물론 그럴 일이 있을까 싶지만... + +
+ +## Topic 32 : 설정 + +나중에 바뀌리라 알고 있는 것, 소스 코드 본체 바깥에 표현할 수 있는 것을 찾아라. 그리고 설정 더미에 던져 넣어라. + +
+ +### 애플리케이션 외부에서 설정/관리해야 하는 값들 + +- 출시된 이후 바뀔 수도 있는 값 +- 여러 환경에서 실행될 때 특정 환경에 한정된 값 +- 여러 고객을 위해 실행될 때 특정 고객에 한정된 값 + +
+ +### 정적 설정 + +- 정보를 일반 파일로 관리할 때는 일반 텍스트 형식을 사용하는 추세(2021년 기준 YAML과 JSON이 가장 많이 사용) +- 어떤 형태를 사용하든 애플리케이션에서는 설정을 자료 구조 형태로 불러온다. +- 보통 처음 애플리케이션을 시작할 때 읽어올 것이다. +- 흔히 이 자료 구조를 전역에서 접근할 수 있도록 하는데, 코드의 어느 부분에서든 설정 정보에 쉽게 접근할 수 있도록 하기 위해서일 것이다. +- 이것은 추천하지 않으며, 대신 **설정 정보를 (얇은) API 뒤로 숨기는 걸 권한다.** +- 그러면 설정을 표현하는 세부 사항으로부터 코드를 떼어 놓을 수 있다. + +
+ +### 서비스형 설정 + +- 우리는 일반 파일이나 데이터베이스가 아니라, 서비스 API 뒤에서 관리하는 것을 선호한다. +- 장점 + + 여러 애플리케이션이 설정 정보를 공유할 수 있다. + + 인증과 접근 제어를 붙여서 애플리케이션마다 보이는 정보가 다르게 만들 수 있다. + + 여러 인스턴스에 걸쳐서 전체 설정을 한번에 바꿀 수 있다. + + 설정 데이터를 전용 UI로 관리할 수 있다. + + 설정 데이터를 동적으로 계속 바꿀 수 있다. +- 설정 데이터를 동적으로 바꿀 수 있다는 점은 고가용성 애플리케이션을 만들 때 매우 중요하다. + +
+ +### 설정에 대한 당부 + +- 너무 지나치게 하지는 말라. +- 게으름 때문에 결정을 내리지 않고 설정을 추가하여 사용자에게 미루지 말라. + +
+ +## 총평 + +전체적으로 이번 장에는 루비나 엘릭서 코드가 많아서 코드가 한눈에 들어오지는 않았다... 차라리 잘 써보지도 않은 자바 코드가 더 나을 것 같다고 느끼기도 했다. 한편 루비 코드는 github Page로 블로그를 만들 때 사용했던 jekyll 엔진 기반의 liquid 언어의 템플릿 코드와 비슷한 것 같다고 느끼긴 했다. (그래도 싫은 건 싫은 거다.) + +
+ +현재는 함수형으로 굳어진 프론트엔드 패러다임에 객체지향과 상속, 파이프라인과 관련된 내용들이 많아 조금은 백엔드나 더 low-level 개발에 적합한 조언들이 아니었나 싶기도 하다. + +
+ +그렇지만 특정한 설정을 외부로 옮겨서 설정을 용이하게 한다는 등 몇몇 팁들은 개인 개발에서라도 써먹어볼 수 있을 것 같다고 느꼈다. 예를 들면 현재 기획/개발이 진행중인 블로그 개발 프로젝트에서 특정 카테고리 포스트들을 모두 숨기고 LNB에 표시하지 않게 하려는 등의 기획을 하고 있는데 이걸 사용할 때 설정값들을 별도의 정적파일이나 데이터베이스에 따로 담아서 API로 호출할 수 있도록 하려고 생각했는데, 이 생각의 연장으로 사용해볼 수 있을 것 같다.