Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# ch10-합성-패턴

생성자: 영훈
생성 일시: 2025년 8월 20일 오후 6:04
카테고리: 패턴으로익히고설계로완성하는리액트
최종 편집자:: 영훈
최종 업데이트 시간: 2025년 8월 20일 오후 8:49

# 개요

확장과 유지보수가 쉬운 UI를 만드는 일은 생각보다 까다롭다. 개발자가 가장 먼저 직면하게 되는 문제는 코드베이스가 커져도 모듈화된 구조를 유지하여 재사용성과 가독성을 확보하는 것이다. 컴포넌트가 서로 얽히고 결합된 구조 일수록, 유지보수와 테스트가 까다로울 뿐만 아니라 새 팀원이 프로젝트에 적응하는 데에도 시간이 걸린다.

합성은 이러한 문제를 해결하기 위해 체계적이고 확장 가능하며 깔끔한 코드베이스를 만들 수 있게 해주는 강력한 기술입니다. 하나의 거대한 컴포넌트에 여러 가지 기능을 만들지 않고 작고 다루기 편한 조각으로 분리하여 다양한 방법으로 이를 조합할 수 있습니다. 이러한 접근 방식은 로직을 간소화하고 재사용성을 향상시키며 관심사를 명확하게 분리할 수 있습니다.

이번 장에서는 합성을 이해합니다. 고차 컴포넌트의 훅을 다루기 전에 고차 함수와 같은 기반 기술에 대해 알아봅니다.

이 기술들이 어떻게 합성 원칙과 자연스럽게 이어지는지를 배우면서 더욱 강력한 리액트 애플리케이션을 구축할 수 있습니다. 이후 UI없이 로직만을 별도로 분리하여 압도적 유연성을 제공하는 헤드리스 컴포넌트에 대해 깊이있게 알아봅니다.

# 10.1 고차 컴포넌트를 통한 합성의 이해

합성은 소프트웨어 설계 전반에서 가장 중요한 기술 중 하나이며, 다양한 수준에서 적용할 수 있습니다.

고차 함수에 대해 알아보고, 이를 활용하여 리액트에서 합성을 구현하는 고차 컴포넌트에 대해 살펴봅니다.

## 10.1.1 고차 함수

고차 함수는 다른 함수를 인자로 받거나, 함수를 반환하거나 또는 2가지 특징을 모두 가진 함수를 의미합니다. 함수를 매개변수로 받을 수 있다는 것은 많은 장점이 있으며, 특히 합성을 할 때 유리합니다.

(고차 함수에 대한 예시)

기능을 추가하는 컴포넌트는 표준함수로만 구현할 수 있는 것은 아닙니다. 리액트에서는 고차 컴포넌트를 통해 이를 구현할 수 있다

## 10.1.2 고차 컴포넌트

HOC(High order components)는 컴포넌트를 전달받아 새로운 개선된 버전의 컴포넌트를 반환하는 함수입니다. HOC 의 원리는 단순하다. 기존 컴포넌트에 기능을 추가할 수 있도록 하는 것이다. 이 패턴은 특정 기능을 여러 컴포넌트에 재사용하고자 할 때 특히 유용합니다.

## 10.1.3 ExpandablePanel 컴포넌트 구현하기

# 10.2 리액트 훅

훅은 상태가 있는 로직을 컴포넌트에서 분리할 수 있게 하여 독립적인 테스팅과 재사용을 가능하게 해준다. 컴포넌트 구조를 바꾸지 않고도 상태가 있는 로직을 재사용할 수 있게 합니다. 기본적으로 훅을 사용하면 함수 컴포넌트에서 리액트의 상태 또는 기타 생명주기 기능들을 연동할 수 있습니다.

훅은 재사용이 가능한 로직을 포함하여 컴포넌트와 분리되어 있지만 쉽게 결합할 수 있습니다. HOC를 통해 감싸는 접근법과 달리 혹은 플러그인 형태의 방식을 제공하여 리액트를 통해 가벼운 형태로 관리하기 쉽습니다. 이러한 훅의 특성은 코드 모듈화를 쉽게 해줄 뿐만 아니라, 보다 깔끔하고 직관적인 형태로 컴포넌트의 기능을 덧붙일 수 있게 해줍니다.쉽게

훅은 생각보다 다양한 기능을 제공합니다. UI와 관련되 상태를 관리하는 것 뿐만 아니라, 데이터를 가져오거나 페이지 단위의 키보드 단축키 처리와 같은 전역 이벤트 핸들링 등 UI 의 부수 효과를 다룰 떄도 사용합니다. 앞서 키보드 이벤트를 다룰 떄 어떤 방식으로 사용하는지를 확인했습니다. 이제 훅을 통해 네트워크 요청을 어떻게 간소화하는지 알아봅니다.

## 10.2.1 원격 데이터 가져오기

## 10.2.2 깔끔하고 재사용성을 높이는 리팩터링

# 10.3 드롭다운 목록 컴포넌트 만들기

# 10.4 헤드리스 컴포넌트 패턴

로직·상태만 제공하고 UI는 직접 렌더링하는 패턴.

- 쓰면 좋은 때: 디자인이 자주 바뀜, 여러 스킨에서 재사용, 접근성 규칙 표준화 필요.
- 피할 때: UI가 고정, 팀이 추상화에 익숙치 않음, 커스텀 요구가 거의 없음.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# ch9-리액트-설계-원칙-적용

생성자: 영훈
생성 일시: 2025년 8월 20일 오후 4:30
카테고리: 패턴으로익히고설계로완성하는리액트
최종 편집자:: 영훈
최종 업데이트 시간: 2025년 8월 20일 오후 7:04

# 개요

- 설계원칙은 소프트웨어 개발에서 시간이 지나도 코드베이스의 유지보수성, 확장성, 그리고 가독성을 보장하기 위한 기본 원칙이다.
- 끊임없이 변화하는 기술 환경 속에서도 설계 원칙을 준수한다면 프로젝트의 장기적 성공을 이끌어낼 수 있으며, 버그가 빈번하게 발생하는 ‘코드 지옥’도 피할 수 있다.
- 리액트는 라이브러리의 선언적 특성, 컴포넌트 기반의 구조로 인해 설계원칙이 더욱 중요하다.
- 리액트는 작고 독립된 컴포넌트로 복잡한 UI를 만들 수 있다.
- 이러한 모듈화 접근 방식은 리액트의 강점이지만, 설계원칙을 지키지 않으면 관리하기 어려운 코드가 되기 쉽다.
- 설계원칙을 따르지 않으면 복잡한 의존관계로 인해 코드를 변경하기 어렵거나 읽기 어려워진다.
- 단일 책임 원칙(single responsibility principle)을 준수하지 않는다면, 컴포넌트 테스트와 리팩토링이 어려워진다.
- 인터페이스 분리 원칙(interface segregation principle)을 무시하면 특정 유스 케이스에 묶여 컴포넌트의 재사용성이 떨어진다.
- 리액트가 업데이트 되면서 훅과 concurrent mode같은 기능이 추가되고 설계 원칙 중심의 접근 방식은 이러한 기능 변화를 최소한의 코드 수정으로 적용할 수 있게 합니다.
- 이는 기술 부채를 해결하는 것보다 기능 개발, 버그 수정, 비즈니스 가치 전달 등 본질에 집중하게 한다.
- 리액트 개발에 설계 원칙의 적용은 단순한 방법론을 넘어 필수적 요소이다. 이는 복잡성에 대응하기 위한 사전적인 조치로 코드를 읽고 테스트하고 유지보수하기 쉽게 만든다.

# 9.1 단일 책임 원칙

- 핵심 : 컴포넌트의 주요 역할을 정확히 파악하는 것
- 컴포넌트가 수행해야할 핵심을 분리하면 부가적인 기능들을 리팩터링하고 추상화하기 쉬워진다.
- SRP는 상위 레벨의 가이드라인, 코드 레벨 수준으로 직접 적용하기 적합하다. 구현하는 방법은 다양하다. 특히 복잡도가 높아지는 상황에서는 어느 시점에 적용할지 잘 파악해야 한다.
- 가장 많이 사용되는 기술.
1. render prop: 리액트 컴포넌트 간 코드 공유를 위해 함수 prop을 이용하는 기술이다. 구현된 컴포넌트는 자체적으로 렌더링 로직을 구현하는 대신 리액트 요소를 반환하고, 이를 호출하는 함수를 사용한다.
2. 합성(composition): 작고 재사용 가능한 컴포넌트들을 만들어 이들을 조합해 더 복잡한 UI 요소를 만드는 기술이다.

## 9.1.1 render prop 패턴을 통한 단일 책임 원칙 적용

Render Prop 패턴 : 컴포넌트에 prop으로 함수를 전달하는 방식이다. 이 함수는 컴포넌트가 렌더링할 JSX를 반환합니다. 이는 부모 컴포넌트가 자식 컴포넌트의 렌더링 로직 일부를 제어할 수 있게 하여 더 유연하고 재사용 가능한 컴포넌트를 만들 수 있다. `Title` 컴포넌트 예시를 통해 이 패턴이 어떻게 작동하는지 볼 수 있다.

## 9.1.2 합성을 통한 단일 책임 원칙 적용

합성은 이 책의 많은 부분에서 사용한 개념. 핵심은 단일 책임 원칙,SRP에 있다.

`Avartar` 컴포넌트와 `Tooltip` 컴포넌트의 분리 예시를 통해 합성이 어떻게 SRP를 적용하여 더 간결하고 모듈화하며 맞춤 설정 가능하게 만드는지 볼 수 있다.

# 9.2 의존관계 역전 원칙

- 의존관계 역전 원칙(Dependency Inversion Principle)은 유지보수 가능하며 유연하고 확장 가능한 소프트웨어를 만들기 위해 필요한 SOLID 5가지 원칙 중 하나. 이 원칙은 구체적인 구현보다는 추상화에 초점을 맞춘다.
- 의존 관계 역전 원칙은 대규모 시스템을 구축하고 유지 관리할 때 직면하는 문제들을 해결한다.
- 단단하게 결합된 모듈로 인해 발생하는 문제
- 상위 레벨의 모듈이 하위 레벨의 모듈에 의존 관계를 맺게 되면, 하위 레벨 모듈을 조금만 변경해도 광범위한 영향을 미쳐 시스템 전체의 변경이 필요하다.

## 9.2.1 의존관계 역전 원칙의 원리

- 문제 상황: Application 클래스가 EmailNotification과 같은 구체적인 클래스에 강하게 결합(tightly coupled)되어 있다고 가정해볼 수 있습니다. 이 경우 알림 전송 방식(예: 이메일에서 SMS로)을 변경하려면 Application 클래스를 직접 수정해야 하며, 이는 단일 책임 원칙(SRP)을 위반하고 시스템의 유연성을 떨어뜨립니다.
- 해결책 (추상화에 의존):
- Notification 인터페이스 도입: Notification이라는 인터페이스를 도입하고, EmailNotification과 같은 구체적인 알림 클래스들이 이 인터페이스를 구현하도록 합니다.
- 상위 모듈의 의존성 역전: Application 클래스가 EmailNotification과 같은 구체적인 클래스 대신, Notification 인터페이스에 의존하도록 변경합니다.
- 유연성 확보: Application의 관점에서는 어떤 구체적인 구현(예: EmailNotification 또는 SMSNotification)이 전달되든 Notification 인터페이스를 구현하기만 하면 되므로, 알림 방식을 쉽게 교체할 수 있게 됩니다. 이렇게 하면 Application 클래스는 특정 알림 메커니즘으로부터 독립되어 더 유연하고 변경 또는 확장하기 쉬워집니다.

## 9.2.2 버튼 클릭 로그 수집에 의존관계 역전 원칙 적용하기

- 문제 상황: 애플리케이션 전반에서 사용되는 일반 Button 컴포넌트의 onClick 핸들러에 분석 로직을 직접 추가하면, 모든 버튼이 분석 기능을 필요로 하지 않을 때도 불필요하게 코드가 포함되고, 분석 로직 변경 시 여러 위치를 업데이트해야 하는 반복적인 로직(repetitive logic) 문제가 발생할 수 있습니다.
- 해결책 (컨텍스트와 인터페이스 사용):
- InteractionMeasurement 인터페이스 정의: measure 메서드를 포함하는 InteractionMeasurement라는 새로운 인터페이스를 정의합니다.
- React Context 도입: 이 인터페이스의 구현이 사용될 수 있도록 React Context(InteractionContext)를 생성하고 null로 초기화합니다.
- 버튼 컴포넌트의 의존성 역전: Button 컴포넌트 내부에서 useContext 훅을 사용하여 InteractionContext에 접근하고, 컨텍스트를 통해 제공되는 measure 함수를 이용해 클릭 이벤트를 추적합니다.
- 선택적 분석 추적: interactionContext가 null인 경우 measure 함수는 호출되지 않으므로, 분석 추적 기능을 선택적으로 활성화할 수 있습니다.
- 유연성과 재사용성: 분석 기능을 활성화하고 싶은 제품은 Button을 InteractionContext 인스턴스 내에서 사용하고, 분석을 원하지 않는 사용자는 Button 컴포넌트를 평소처럼 사용할 수 있습니다.

> 이 접근 방식은 코드 재사용성 및 시스템 유지보수성을 향상시키고 전체 번들 크기를 줄여줍니다. 특히 서로 다른 팀이 작업하는 대규모 코드베이스에서 유용합니다.
>

# 9.3 명령과 조회 책임 분리 원칙

명령과 조회 책임 분리 원칙(CQRS)는 소프트웨어 설계에서 메서드나 함수는 시스템의 상태를 수정하는 명령이거나 시스템 상태에 대한 정보를 조회하여 반환하는 쿼리 둘 중에 하나여야 하며, 2가지가 동시에 수행되지 않아야 한다는 원칙입니다.

명령 (또는 수정) 메서드는 액션 또는 객체의 상태 변경을 수행하며 값을 반환하지 않습니다. 반면에 조회 메서드는 객체의 상태를 변경없이 읽습니다. 명령과 조회를 분리하면 컴포넌트 사이에 결합을 분리하여 테스트와 유지보수 및 코드 변경을 쉽게 만듭니다.

동작에 대한 추론이 쉬워져 시스템 전반적인 설계르 개선할 수 있다.

## 9.3.1 useRuducer 훅

리액트의 useReducer 훅은 함수 컴포넌트에서 상태 관리를 하는데 사용됩니다. 특히 다음 상태가 이전 상태에 의전하여 변하거나, 복잡한 상태 로직일 때 유용합니다. useReducer 훅은 reduce 함수와 초기상태를 2개의 인자로 받습니다. 그리고 현재 상태와 업데이트를 수행하기 위한 dispatch 메서드를 반환합니다.

첫번째 인자인 reduce 함수는 현재 상태와 상태 업데이트에 필요한 정보를 담은 action 객체를 전달받습니다. 이 함수는 액션 타입과 페이 로드에 따라 새로운 사앹를 반환합니다.

두번째 인자는 초기 상태는 호출했을 때 초깃값으로 사용됩니다.

## 9.3.2 컨텍스트 안에서 reducer 함수 사용하기

(생략)