Skip to content

Commit

Permalink
Docs: 박승훈 5장 (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
Orchemi authored Aug 25, 2024
1 parent 7cc2e8c commit 84a79a5
Showing 1 changed file with 363 additions and 0 deletions.
363 changes: 363 additions & 0 deletions 5장/박승훈.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,363 @@
## 들어가며

> 이번 장에서 기대할 수 있는 내용들
- 되돌릴 수 있는 의사 결정을 내리는 구체적인 방법
- 관계 없는 개념들을 분리하여 결합도를 낮추는 방법
- 이벤트를 관리하고, 이벤트에 반응하는 전략
- 결합도가 낮은 함수 파이프라인 기법
- 유연하고 바꾸기 쉬운 코드를 만들 수 있는 더 나은 대안
- 세부 사항을 완전히 코드 밖으로 옮기는 방법

<br />

## Topic 28 : 결합도 줄이기

소프트웨어의 구조는 유연해야 한다. 그리고 유연하려면 각각의 부품이 다른 부품에 가능한 한 조금만 연결되어야 한다.

<br />

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

- 결합은 두 개의 코드 조각이 무언가를 공유하면 언제나 일어날 수 있다.
- 다음과 같은 결합의 증상을 놓치지 않도록 주의한다.
+ 관계 없는 모듈이나 라이브러리 간의 희한한 의존관계
+ 한 모듈의 '간단한' 수정이 이와 관계없는 모듈을 통해 시스템 전역으로 퍼져 나가거나 시스템의 다른 곳에서 무언가를 깨뜨리는 경우
+ 개발자가 수정하는 부분이 시스템에 어떤 영향을 미칠지 몰라 코드의 수정을 두려워하는 경우
+ 변경 사항에 누가 영향을 받는지 파악하고 있는 사람이 없어서 결국 모든 사람이 참석해야 하는 회의

<br />

### 묻지 말고 답하라(TDA; Tell, Don't Ask)

- 다른 객체의 내부 상태에 따라 판단을 내리고 그 객체를 갱신해서는 안 된다.
- 객체의 내부 상태를 묻는 것
+ 캡슐화의 장점이 사라진다.
+ 구현에 대한 지식이 코드 여기저기로 퍼져 버린다.

<br />

### 메서드 호출을 엮지 말라

- 애플리케이션에 있는 것은 모두 바뀌리라 생각해라.
- 외부의 라이브러리 역시 불안정하다고 여겨라.

<br />

### 파이프라인

- 파이프라인은 구현 세부사항을 숨기는 객체지향과 달리 구현과정을 공개한다.
- 파이프라인이 결합을 하나도 만들지 않는 것은 아니다.
- 파이프라인의 함수에서 반환하는 데이터는 반드시 다음 함수가 처리할 수 있는 형식이어야 한다.

<br />

> 파이프라인에 대한 나의 생각
파이프라인의 예시로는 Promise가 생각났다. `.then()`, `.catch()`, `.finally()` Promise 객체의 정적 메서드는 또다른 Promise 객체를 반환하여 같은 동작을 이어나갈 수 있게 한다. 또는 Builder Pattern을 사용한 pipeline 방식도 최근에 접했는데 코드 구현은 다음과 같다.

<br />

```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를 반환하며 계속 인스턴스 스스로를 이어갈 수 있도록 하는 방식인데, 다음 함수가 처리할 수 있는 형식이라는 점에서 내용의 맥락과 비슷하다고 느꼈다.

<br />

### 글로벌화의 해악

- 어디서나 접근할 수 있는 데이터는 교묘하게 애플리케이션 컴포넌트 간의 결합을 만들어 낸다.
- 전역 데이터의 구현을 변경할 때 시스템 코드 전체에 영향을 줄 수 있음음 분명하다.
- 문제는 바꿔야 하는 곳을 모두 바꿨는지 확인하기 힘들다는 데 있다.

<br />

### 코드 재사용과 전역 데이터

- 코드를 처음 작성하는 시점의 제 1 관심사가 코드 재사용이어서는 안 될 것
- 코드를 재사용할 수 있게 하려면 깨끗한 인터페이스를 만들고 나머지 코드와의 결합을 없애야 한다.
- 코드가 전역 데이터를 사용한다면 나머지로부터 떼어 내기 힘들어질 것이다.

<br />

> 전역 데이터에 대한 나의 생각
프론트엔드 개발자의 입장에서 생각했을 때 전역 데이터를 쓰지 않는 것이 좋은 이유는 리렌더링과 관련된 성능 저하 위주로만 생각했다. 그리고 컴포넌트 트리를 따라 내려가면서 props drilling이 너무 심해질 때 전역 데이터는 이것을 해결할 수 있는 좋은 대안이라고만 생각했다.

<br />

하지만 생각해보면 전역 데이터를 피하는 것이 좋다는 이야기를 많이 들었는데 그 근거를 제대로 알지 못했다가, 어디서든 접근하고 변경할 수 있다는 것은 양날의 검이라는 사실, 특히 재사용과 유지보수에서 어려움이 있을 것이라는 것을 다시 느끼게 되었다. 앞으로는 보수적, 지역적으로 사용할 필요를 느꼈다.

<br />

### 외부 리소스도 전역 데이터다

- 수정 가능한 외부 리소스는 모두 전역 데이터다.
- 해법은 반드시 이 리소스들을 여러분이 작성하는 코드로 모두 감싸는 것이다.
- **전역적이어야 할 만큼 중요하다면 API로 감싸라.**

<br />

### 결국은 모두 ETC

- 결합된 코드는 바꾸기 힘들다.
- 코드의 한 곳을 바꾸면 다른 곳에 여파가 미칠 수 있다.
- 직접적으로 아는 것만 다루는 부끄럼쟁이 코드를 계속 유지하라.

<br />

## Topic 29 : 실세계를 갖고 저글링하기

- 우리가 작성하는 애플리케이션은 맡은 일을 어떻게든 수행해야 한다.
- 비록 우리의 세계는 엉망이고 사람들은 생각을 자주 바꾸지만 말이다.

<br />

### 이벤트

- 이벤트는 무언가 정보가 있다는 것을 의미한다.
- 어떻게 이벤트에 잘 반응하는 애플리케이션을 만들 수 있을까?

<br />

### 감시자 패턴

- 이벤트를 발생시키는 '감시대상(observable)'과 이벤트에 관심이 있는 클라이언트인 '감시자(observer)'로 구성된다.
- 동작 방식
+ 감시자는 자신이 관심 있는 이벤트를 감시 대상에 등록한다. 호출될 함수의 참조도 등록할 때 함께 넘긴다.
+ 해당 이벤트가 발생하면 감시 대상은 등록된 감시자 목록을 보며 함수들을 일일이 호출한다. 이때, 발생한 이벤트를 감시자 함수의 인자로 넘긴다.

<br />

### 감시자 패턴의 문제

- 모든 감시자가 감시 대상에 등록을 해야 하기 때문에 결합이 생긴다.
- 일반적으로 감시 대상이 콜백을 직접 호출하도록 구현하기 때문에 성능 병목이 될 수 있다. (동기 처리 특성상 콜백 실행을 마칠 때까지 감시 대상이 계속 기다려야 함)

<br />

### 게시-구독 모델

- 게시-구독(Publish-Subscribe) 혹은 발행-구독 모델, 줄여서 pubsub이라고 부른다.
- 감시자 패턴을 일반화한 것
- 감시자 모델의 결합도를 높이는 문제와 성능 문제도 해결
- 동작 방식
+ 구독자는 관심사를 하나 이상의 채널에 등록하고 게시자는 채널에 이벤트를 보낸다.
+ 감시자 패턴과는 다르게 게시자와 구독자 사이의 통신은 코드 밖에서 일어난다.
+ (아마 비동기적으로)

<br />

### 게시-구독 모델의 장점

- 추가적인 결합 없이 비동기 이벤트 처리를 구현하기에 좋다.
- 다른 기존 코드를 수정하지 않고 이벤트 처리 코드를 추가하거나 교체할 수 있다.
- 애플리케이션이 작동하고 있는 도중에 작업이 가능할 수도 있다.

<br />

### 게시-구독 모델의 단점

- 게시-구독 모델을 아주 많이 사용하는 시스템에서는 현재 어떤 일이 벌어지고 있는 지 파악하기가 힘들다.
- 게시자가 메시지를 보내는 것을 확인했더라도 어떤 구독자가 메시지를 처리하는지 바로 이어서 볼 수 없다.

<br />

### 스트림 모델

- 이벤트를 이리저리 연결하는 것은 쉽지 않다.
- 스트림은 이벤트를 일반적인 자료 구조처럼 다룰 수 있게 해준다.
- 익숙한 방식으로 스트림을 다룰 수 있어서 좋다. 우리가 아는 온갖 작업을 일반적인 자료 구조와 마찬가지 방법으로 할 수 있다.
- 이벤트 스트림과 일반 자료 구조를 조합할 수 있다.
- Rxjs가 그 예시
- 이벤트 스트림은 동기적 처리와 비동기적 처리를 하나의 편리한 공통 API로 감싸서 통합한다.

<br />

### 이벤트를 다룰 때

- 이벤트는 모든 곳에 있다.
- 이벤트가 어디서 발생하든 이벤트를 중심으로 공들여 만든 코드는 일직선으로 수행되는 코드보다 더 잘 반응하고 결합도가 더 낮다.

<br />

## Topic 30 : 변환 프로그래밍

- 프로그램이란 입력을 출력으로 바꾸는 것이라는 사고방식으로 돌아갈 필요가 있다.
- 구조는 명확해지고 더 일관적으로 오류를 처리하게 되어 결합도 대폭 줄어들 것이다.

<br />

### 변환 찾기

- 요구사항에서 시작하는 게 변환을 찾는 가장 쉬운 방법
- 요구사항의 입력과 출력이 무엇인지 찾아라.
- 그러면 전체 프로그램을 나타내는 함수가 정해진다.
- 이제 더 작은 단계로 나누어 각각을 찾아간다.
- (일종의 top-down 접근 방식)
- (위의 파이프라인과 연결)

<br />

### 객체 지향 프로그래밍과의 비교

- 객체 지향 프로그래밍
+ 객체 지향 프로그래밍 경험이 많다면 반사적으로 데이터를 숨기고, 객체 안에 캡슐화해야 한다고 느낄 것이다.
+ 이런 객체들은 서로 이리저리 이야기하며 서로의 상태를 변경
+ 결합을 많이 만들어 내고, 결국 객체 지향 시스템이 바꾸기 어려워지는 큰 요인

- 변환 모델
+ 요구 사항을 달성하기 위해 필요한 것은 하나로 연결된 변환들 뿐
+ 데이터를 전체 시스템 여기저기의 작은 웅덩이에 흩어 놓는 대신, 데이터를 거대한 강으로, 흐름으로 생각
+ 데이터는 기능과 동등해진다.
+ 데이터는 더 이상 특정한 함수들과 묶이지 않는다.(클래스와 달리)
+ 어떤 함수든 매개 변수가 다른 함수의 출력 결과와 맞기만 하면 어디서나 사용하고 또 재사용할 수 있다.

<br />

### 오류 처리는 어떻게 하나?

- 연쇄 변환이 일직선으로만 이어진다면 어떻게 오류 검사에 필요한 조건부 논리를 추가할 수 있을까?
- 공통적으로 사용하는 기본적인 관례가 있다. 바로 변환 사이에 값을 절대 날것으로 넘기지 않는 것
+ 래퍼(wrapper) 역할을 하는 자료 구조나 타입으로 값을 싸서 넘긴다.
+ 이런 자료 구조나 타입은 안에 들어 있는 값이 유효한지를 추가로 알려 준다.
+ 파이프라인의 각 함수들은 내부에서 특정 오류에 대해 확인하는 코드들을 내재하고, 파이프 진행 중 오류를 맞이하면 파이프라인 전체를 중단하고 오류를 반환한다.

<br />

### 변환은 프로그래밍을 변환한다

- 코드를 일련의 (중첩된) 변환으로 생각하는 접근 방식은 프로그래밍을 해방시킨다.
- 습관을 들이면 코드가 더 명확해지고, 함수는 짧아지며, 설계는 단순해진다.

<br />

## Topic 31 : 상속세

> 상속을 사용하고 있다면 아마 여러분에게 필요한 것은 상속이 아닐 것이다.
<br />

### 상속의 문제

- 상위 클래스를 수정했을 때 이를 상속하는 하위 클래스는 함께 망가질 수 있다.(결합이 많다.)
- 계층 위에 계층을 덧붙이다 보면, 클래스 계층도는 엄청나게 복잡해진다. 이는 애플리케이션을 취약하게 만든다.
- 더 나쁜 것은 다중 상속이다. 특정 클래스가 여러 다른 도메인의 상위 클래스가 될 수 있다면 추후 변경을 할 수 없는 지경이 된다.
- (다행히도 이제는 많은 객체 지향 언어에서 다중 상속을 지원하지 않는다.)

<br />

### 상속을 쓸 필요가 없게 해주는 기법

- 인터페이스와 프로토콜
+ 선언들은 아무런 코드를 만들지 않는다.
+ 타입으로 사용할 수 있다.
+ 해당 인터페이스를 구현하는 클래스라면 무엇이든 그 타입과 호환된다.
+ 다형성은 인터페이스로 표현하는 것이 좋다.
- 위임
- 믹스인과 트레이트

<br />

> 상속에 대한 나의 생각
프론트엔드 진영에서 상속이라 할만한 것은 interface가 interface를 상속하는 정도일 것 같다. 그런데 상속의 부모 인터페이스의 변경으로 인해 자식 인터페이스가 함께 영향을 받는 일도 비일비재했다. 하지만 이를 더 쪼개서 더 작은 인터페이스로 만들고 이를 상속하는 것은 조금 애매한 것 같다.

<br />

그리고 위임과 믹스인, 트레이트는 도대체 뭘 말하는건지 모르겠다. 자바같은 객체 지향 언어는 조금 더 와닿을 수도 있을 것 같은데 아직까지는 상속을 그리 사용해보지는 않았다. 다만 다음에 클래스형으로 개발할 일이 있을 때 참고해 봐야겠다. 물론 그럴 일이 있을까 싶지만...

<br />

## Topic 32 : 설정

나중에 바뀌리라 알고 있는 것, 소스 코드 본체 바깥에 표현할 수 있는 것을 찾아라. 그리고 설정 더미에 던져 넣어라.

<br />

### 애플리케이션 외부에서 설정/관리해야 하는 값들

- 출시된 이후 바뀔 수도 있는 값
- 여러 환경에서 실행될 때 특정 환경에 한정된 값
- 여러 고객을 위해 실행될 때 특정 고객에 한정된 값

<br />

### 정적 설정

- 정보를 일반 파일로 관리할 때는 일반 텍스트 형식을 사용하는 추세(2021년 기준 YAML과 JSON이 가장 많이 사용)
- 어떤 형태를 사용하든 애플리케이션에서는 설정을 자료 구조 형태로 불러온다.
- 보통 처음 애플리케이션을 시작할 때 읽어올 것이다.
- 흔히 이 자료 구조를 전역에서 접근할 수 있도록 하는데, 코드의 어느 부분에서든 설정 정보에 쉽게 접근할 수 있도록 하기 위해서일 것이다.
- 이것은 추천하지 않으며, 대신 **설정 정보를 (얇은) API 뒤로 숨기는 걸 권한다.**
- 그러면 설정을 표현하는 세부 사항으로부터 코드를 떼어 놓을 수 있다.

<br />

### 서비스형 설정

- 우리는 일반 파일이나 데이터베이스가 아니라, 서비스 API 뒤에서 관리하는 것을 선호한다.
- 장점
+ 여러 애플리케이션이 설정 정보를 공유할 수 있다.
+ 인증과 접근 제어를 붙여서 애플리케이션마다 보이는 정보가 다르게 만들 수 있다.
+ 여러 인스턴스에 걸쳐서 전체 설정을 한번에 바꿀 수 있다.
+ 설정 데이터를 전용 UI로 관리할 수 있다.
+ 설정 데이터를 동적으로 계속 바꿀 수 있다.
- 설정 데이터를 동적으로 바꿀 수 있다는 점은 고가용성 애플리케이션을 만들 때 매우 중요하다.

<br />

### 설정에 대한 당부

- 너무 지나치게 하지는 말라.
- 게으름 때문에 결정을 내리지 않고 설정을 추가하여 사용자에게 미루지 말라.

<br />

## 총평

전체적으로 이번 장에는 루비나 엘릭서 코드가 많아서 코드가 한눈에 들어오지는 않았다... 차라리 잘 써보지도 않은 자바 코드가 더 나을 것 같다고 느끼기도 했다. 한편 루비 코드는 github Page로 블로그를 만들 때 사용했던 jekyll 엔진 기반의 liquid 언어의 템플릿 코드와 비슷한 것 같다고 느끼긴 했다. (그래도 싫은 건 싫은 거다.)

<br />

현재는 함수형으로 굳어진 프론트엔드 패러다임에 객체지향과 상속, 파이프라인과 관련된 내용들이 많아 조금은 백엔드나 더 low-level 개발에 적합한 조언들이 아니었나 싶기도 하다.

<br />

그렇지만 특정한 설정을 외부로 옮겨서 설정을 용이하게 한다는 등 몇몇 팁들은 개인 개발에서라도 써먹어볼 수 있을 것 같다고 느꼈다. 예를 들면 현재 기획/개발이 진행중인 블로그 개발 프로젝트에서 특정 카테고리 포스트들을 모두 숨기고 LNB에 표시하지 않게 하려는 등의 기획을 하고 있는데 이걸 사용할 때 설정값들을 별도의 정적파일이나 데이터베이스에 따로 담아서 API로 호출할 수 있도록 하려고 생각했는데, 이 생각의 연장으로 사용해볼 수 있을 것 같다.

0 comments on commit 84a79a5

Please sign in to comment.