Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions 11장-리액트 계층 구조 애플리케이션/박선화.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# 11장 리액트 계층 구조 애플리케이션

계층 구조(Layered Architecture)

애플리케이션을 계층화하면 핵심 문제들을 해결할 수 있습니다.

- 관심사 분리 : 서로 다른 계층은 각기 다른 책임을 다루게 되므로 코드베이스를 쉽게 이해하고 찾아볼 수 있습니다.
- 높은 재사용성: 비즈니스 로직과 데이터 모델을 애플리케이션 전반에서 재사용하기 쉬워집니다.
- 테스트 용이성 : 계층 구조는 단위 테스트와 통합 테:스트를 작성하기 수월하게 하므로 더욱 탄탄한 애플리케이션을 만들 수 있습니다.
- 유지보수성 : 계층 구조로 설계하면 애플리케이션이 커지더라도 기능을 추가하기가 수월합니다.

## 리액트 애플리케이션의 진화

1. 단일 컴포넌트 애플리케이션

- 하나의 폼을 다루거나 다른 프레임워크에서 리액트로 전환하는 과정을 설명하고 이해시키고자 하는 경우에 적합합니다.
- 모든 기능을 하나의 컴포넌트 안에 구현하면 코드를 이해하고 관리하기가 어려워집니다.

2. 복합 컴포넌트 애플리케이션

- 컴포넌트의 역할이 분명해진 것은 좋지만, 애플리케이션이 커질수록 역할이 뷰 계층을 넘어 확장됩니다.

3. 훅을 이용한 상태 관리

4. 비즈니스 모델 분리

- 분리를 통해 로직의 결합도를 높이고 뷰와 독립하여 구성할 수 있습니다.

5. 계층화된 프런트엔드 애플리케이션

- 애플리케이션이 커지게 되면, 어떤 특정한 패턴을 찾을 수 있습니다. 사용자 인터페이스와도
관련 없고, 원격 서버, 로컬 스토리지, 캐시 등의 데이터 출처에 영향을 받지 않는 객체 집합을
찾을 수 있습니다. 이 러한 객체들을 모아 별도 계층으로 분리할 수 있습니다.

## 클래스 기반의 모델의 장점

단순 타입에서 클래스 기반 모델로 바꾸면 다음과 같은 장점들이 있습니다.

- 캡슐화: 클래스는 관련된 속성과 메서드를 한 곳에 위치하게 하여 깔끔한 구조화가 가능합니다. 그리고 데이터 접근을 제한할 수 있어 제어하기 쉽고, 데이터 무결성을 유지할 수 있습니다.
- 메서드: 메뉴 아이템과 관련된 복잡한 작업이 있을 때, 클래스가 제공하는 구조를 활용하여 데이터 조작이든 별도의 비즈니스 로직이든 상관없이 메서드를 정의할 수 있습니다.
- 상속과 다형성 : 메뉴 아이템에 상속이나 다형성 개념이 필요할 때 클래스 구조는 필수적입니다. 각각 다른 메뉴 아이템 타입을 공통의 기본 클래스에서 상속받아 필요한 행동을 재정의할 수 있습니다.
- 일관된 인터페이스: 클래스는 특히 여러 애플리케이션 영역에서 메뉴 아이템을 다룰 때 데이터의 일관된 인터페이스를 보장합니다.
- 읽기 전용 속성 : 클래스는 읽기 전용 속성을 정의할 수 있으므로 데이터 수정을 제한할 수 있습니다. 데이터 무결성을 유지하고 불변 데이터 구조에서 %하기 위한 필수적인 요소입니다.

### 전략 패턴(Strategy pattern)

전략 패턴은 코드의 런타임에 필요한 알고리즘 구현을 선택할 수 있는 행동 디자인 패턴입니다.
알고리즘 그룹을 캡슐화하고 서로 갈아 끼우기 쉽게 하여, 시용자가 코드 수정 없이도 적합한 알고리즘을 선택할 수 있도록 합니다.

```tsx
const handleAddMenuItem = (item: IMenuItem) => {
if (isTodayFridayO) {
item.discountstrategy = new SpecialDiscountStrategyO()
}
if (item.type === 'pizza') {
item.discountstrategy = new PizzaDiscountStrategyO()
}
onAddMenuItem(item)
}
```

### 계층 구조 알아보기

사용자 정의 훅으로 옮겨보겠습니다.

```tsx
export const useShoppingCart = (items: IMenuItem[]) => {
const totalPrice = useMemo(
() => items.reduce((acc, item) => (acc += item.price), 0),
[items]
)
const totalDiscount = useMemo(
() => items.reduce((acc, item) => (acc += item.calculateDiscountO), 0),
[items]
)
return {
totalPrice,
totalDiscount,
}
}
```

useShoppingCart 혹은 IMenuItem 객체 바]열을 붇으0]' totalPrice와 totalDiscount 2가]
의 값을 계산합니다.

- totalPrice는 아이템 배열을 reduce 연산하여 price 속성을 더합니다.
- totalDiscount는 아이템 배열을 red니ce 연산하여 item.calculateDiscountQ 함수 호출로 얻은
아이템의 할인 가격을 더하여 계산합니다.

두 연산은 모두 useMemo 함수로 감싸 아이템 배열이 바뀔 때에만 다시 계산되도록 하였습니다. 이 수정을 통해 ShoppingCart는 깔끔하고 단순해졌고, 값들을 쉽게 활용할 수 있습니다.

```
src
|-----App.tsx
|-------hooks
| |---- useMenultems.ts
| |---- useShoppingCart.ts
|----- models
| |---- BaseMenultem.ts
| |---- IMenuItem.ts
| |---- Pastaltem.ts
| |---- PizzaMenultem.ts
| |---- RemoteMenultem.ts
| |---- strategy
| |---- IDiscountStrategy.ts
| |---- NoDiscountStrategy.ts
| |---- SpecialDiscountStrategy.ts
| |---- TenPercentageDiscountStrategy.ts
|-------views
|------ MenuList.tsx
|------ ShoppingCart.tsx
```

### 계층 구조의 유리한 점

- 높은 유지보수성: 컴포넌트를 여러 세그먼트로 나누면 특정 코드 영역의 결함을 쉽게 식별하고 수정할 수 있으므로, 소요 시간을 최소화하고 수정하는 동안 새로운 버그가 발생할 가능성을 줄여줍니다.
- 모듈성 향상: 이 구조는 모듈화되어 코드 재사용을 촉진하고 새로운 기능을 간단히 추가할 수 있게 합니다. 뷰와 같은 각 계층 안에서도 코드를 더 쉽게 합성할 수 있습니다.
- 가독성 개선: 코드 안에서 로작을 이해하기 편하고 찾기 쉽습니다. 이는 처음 코드를 작성한 개발자 본인뿐만 아니라 같은 코드베이스에서 작업하는 다른 이들에게도 큰 장점입니다.
- 발전된 확장성 : 모듈 간의 복잡도를 줄여 애플리케이션의 확장성이 향상되고 전체 시스템에 영향을 주지 않으면서도 새로운 기능이나 변경 사항을 쉽게 도입할 수 있습니다. 시간이 지나면서 크고 복잡하게 발전할 것으로 예상되는 애플리케이션 개발에는 매우 중요한 장점입니다.
- 쉬운 기술 스택 이전: 대부분의 프로젝트는 그럴 가능성이 낮지만, 뷰의 존재를 인식하지 못하는 순수 자바스크립트 (또는 타입스크립트) 코드로 도메인 로직을 캡슐화한 덕분에 기본 모델과 로직을 변경하지 않고 뷰 계층을 교체할 수 있습니다.