프론트엔드 아키텍쳐를 알아보자 (2) Redux, Observer-Observable, Atomic
이전글: 프론트엔드 아키텍쳐를 알아보자 (1) MVC, MVVM, Flu프론트엔드 아키텍쳐
1. Redux
여러 컴포넌트가 공유하는 상태를 관리하기 위한 라이브러리.
*Flux 패턴을 이용한 대표적인 구현체이다.
*recap: flux패턴은 데이터의 흐름이 한 방향으로만 흘러가도록 하는 아키텍쳐이다.
View를 각각의 MVC 컴포넌트 관점으로 보는 것이 아니라 하나의 큰 View로 본다.
1. 사용자가 View에서 이벤트 발생 -> Action 객체가 생성됨
2. Dispatch 메소드를 통해 이 Action이 Store로 전달
3. Middleware가 있는 경우, Action 처리
4. Reducer는 현재 상태와 Action을 받아 새로운 상태를 반환
5. Store는 이 새로운 상태를 저장
6. 상태가 변경되면 View가 이를 감지하여 스스로 업데이트
View
- 사용자 인터페이스
- 사용자의 입력(Input)을 감지하고, 이를 Actions으로 변환하여 Dispatcher에게 전달
Actions
- 상태 변경을 위한 지시사항
- Action의 종류를 나타내는 문자열인 type과 상태 변경에 필요한 추가 데이터 payload를 포함한다.
Dispatch
- Action을 Store로 보내기 위해 사용되는 메서드
- Dispatcher 역할을 대신하며, Action을 트리거
Store
- dispatch된 Action을 받고 상태를 업데이트
- Middleware와 Reducer를 통해 상태 변경을 처리
Middleware(선택적)
- Action이 Reducer에 도달하기 전에 중간에 실행되는 함수
- 비동기 작업 처리, 로깅, 데이터 변환 등
Reducer
- Actions을 기반으로 State를 변경하는 순수 함수
- 이전 State와 Action을 입력받아 새로운 State를 반환
1.1 Redux 코드로 보기
const { createStore } = require('redux'); // Redux의 핵심 요소를 가져오기
// 1. Action 정의
const incrementAction = {
type: 'INCREMENT', // Action의 종류를 나타내는 문자열
payload: 1 // 상태 변경에 필요한 추가 데이터 (옵션)
};
// 2. Reducer 정의: 상태 변경 로직
const initialState = { count: 0 }; // 초기 상태
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT': // new state 반환
return { count: state.count + action.payload };
default: // 알 수 없는 Action은 현재 상태를 그대로 반환
return state;
}
}
// 3. Store 생성
// Store는 상태를 보관하고, Action을 통해 상태를 변경하며, 상태 변경을 구독
const store = createStore(counterReducer);
// 4. View를 위한 상태 변경 구독: 상태가 변경될 때마다 호출될 함수를 등록
store.subscribe(() => {
console.log('State updated:', store.getState());
});
// 5. Action을 Dispatch: Action을 Store에 전달하여 상태 변경을 트리거
store.dispatch(incrementAction); // { count: 1 }
// 6. 또 다른 Action Dispatch
store.dispatch({ type: 'INCREMENT', payload: 2 }); // { count: 3 }
Redux는 기본적으로 동기적 상태 관리를 위한 설계이기 때문에, 비동기 데이터의 흐름이 복잡하다는 단점이 존재한다.
(비동기 작업을 위해서 미들웨어가 필요하다.)
2. Observer-Observable (옵저버 패턴)
유튜브같다. 어떤 객체 상태(유튜브 채널)을 구독하고 있는 구독자에게, 변경(동영상 업로드)되었을 때 알림을 보내는 패턴이다.
1. Observer들은 Observable에 등록(subscribe)한다.
2. Observable의 상태가 변경되면, 등록된 모든 Observer에게 알림 전송
3. Observer는 알림을 받고, 필요한 작업 수행
Observable (subject)
- 상태를 보유하고 있으며, 상태가 변경되면 Observer들에게 알림 전송
- Observer 객체 등록/제거 메서드 제공
Observer
- Observable의 상태 변화를 감지하는 객체
- Observable로부터 상태 변경 알림을 받으며, 필요에 따라 자신의 상태나 행동을 변경
2.1 Observer 코드로 보기
// Observable 클래스 정의
class Observable {
constructor() {
this.observers = []; // Observer 리스트
}
// Observer 등록
subscribe(observer) {
this.observers.push(observer);
}
// Observer 제거
unsubscribe(observer) {
this.observers = this.observers.filter(subscriber => subscriber !== observer);
}
// 상태 변경 시 모든 Observer에게 알리기
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
// Observer 클래스 정의
class Observer {
constructor(name) {
this.name = name;
}
// Observable에서 알림을 받으면 호출되는 메서드
update(data) {
console.log(`${this.name} received update:`, data);
}
}
// e.g.
// Observable 인스턴스 생성
const observable = new Observable();
// Observer 인스턴스 생성
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');
// Observer를 Observable에 등록
observable.subscribe(observer1);
observable.subscribe(observer2);
// 상태 변경 시 알림
observable.notify('New Data Available');
// Observer2를 구독 해지
observable.unsubscribe(observer2);
// 다시 상태 변경 시 알림
observable.notify('Another Update');
3. Atomic
UI 디자인, 개발에서 컴포넌트를 구성하고 조직화하는 방법론.
컴포넌트를 재사용 하능하게 하기 위해 잘게 쪼개는 것이다.
Atoms (원자)
- UI의 가장 기본적인 구성 요소.
- 더이상 쪼갤 수 없는 작은 단위로, HTML 태그 하나 혹은 최소한의 기능을 가진 요소들
- 예시: 버튼, 입력 필드, 레이블 등
Molecules (분자)
- 독립적인 기능을 수행할 수 있는 최소한의 단위로, 여러 Atoms가 모여 하나의 기능을 제공
- 예시: 입력 필드와 버튼을 결합한 검색 양식
Organisms (생명체)
- 여러 Molecules와 Atoms가 결합하여 더 복잡한 UI의 섹션을 구성
- 페이지의 주요 부분을 이루며, 다양한 데이터와 상호작용을 처리
- 예시: 네비게이션 바, 헤더, 푸터
Templates (템플릿)
- Organisms를 배치하여 전체 페이지의 레이아웃을 정의한다.
- 데이터가 없는 상태에서의 구조만을 제공하며, 반복적으로 사용할 수 있는 페이지의 틀
- 예시: 블로그 글 목록 페이지 레이아웃
Pages (페이지)
- Templates에 실제 데이터를 추가하여 최종 사용자에게 제공되는 UI를 구성한다.
- 예시: 실제 블로그 글이 채워진 블로그 페이지
3.1 Atomic 코드로 보기
import React from 'react';
const noop = () => {};
// Atoms
const Button = ({ onClick, children, type }) => <button type={type} onClick={onClick}>{children}</button>;
const Label = ({ htmlFor, children }) => <label htmlFor={htmlFor}>{children}</label>;
const Input = ({ id, type, onChange, value = "" }) => <input id={id} type={type} onChange={onChange} value={value} />;
const Search = ({ onChange }) => <input type="search" onChange={onChange} />;
const NavMenu = ({ items }) => <ul>{items.map((item) => <li>{item}</li>)}</ul>;
// Molecules
const Form = ({ onSubmit }) => (
<form onSubmit={onSubmit}>
<Label htmlFor="email">Email:</Label>
<Input id="email" type="email" onChange={noop} />
<Button type="submit" onClick={noop}>Submit</Button>
</form>
);
// Organisms
const Header = () => (
<header>
<Search onChange={noop} />
<NavMenu items={[]} />
</header>
);
const Content = ({ children }) => (
<main>
{children}
<Form onSubmit={noop} />
</main>
);
// Templates
const MainTemplate = ({ children }) => (
<>
<Header />
<Content>{children}</Content>
</>
);
// Pages
const HomePage = () => (
<MainTemplate>
<h2>My Form</h2>
<p>This is a basic example demonstrating Atomic Design in React.</p>
</MainTemplate>
);
refference
[React-Redux] 개념부터 기본 사용법 까지
리덕스의 개념과 적용방법에 대한 스터디(1) 작성중
velog.io
Redux Fundamentals, Part 1: Redux Overview | Redux
The official Fundamentals tutorial for Redux: learn the fundamentals of using Redux
redux.js.org
Diving into the great observer pattern in javascript
Design patterns are an international language, they're also a great way of dealing with problems, sometimes you'll be able to notice when a specific pattern is useful and other times you'll have to think a little harder to figure out which one to use (or i
enmascript.com
↑옵저버 패턴 with 쟈스 예시. 읽어보시길 추천드립니다
Atomic Design Pattern: Structuring Your React Application
As we build scalable applications in React, we often encounter challenges in managing the growing complexity of component structures. The…
rjroopal.medium.com