marp | theme |
---|---|
true |
gaia |
(feat. React)
Sangwon Lee
"클린 아키텍쳐는 의존성 규칙(Dependency Rules) 에 대한 이야기"
-
동심원 안쪽은 핵심(비즈니스) 로직 - 외부의 변화에 영향 X, 보호 필요
-
동심원 바깥쪽은 상세 구현체 - 핵심 로직에 영향을 주면 X, 변경 가능성 높음
-
의존의 방향은 항상 동심원 바깥에서 안쪽이어야 함 (동심원 안쪽의 소스가 바깥쪽의 소스를 직접 의존 절대 X)
Hexagonal Architecture (Ports-Adapters)
Domain Driven Design
"아키텍쳐는 의존성 규칙(Dependency Rules) 에 대한 이야기"
-
고수준(핵심 로직)과 저수준(상세)를 잘 분리하고 이들간의 의존 관계를 정리
-
고수준의 로직을 저수준의 변경사항으로부터 보호하기
“Depend upon abstractions, not concretions.”
Dependency Inversion Principle in SOLID (with DI technique)
Inversion of Control
a.k.a Hollywood Principle – “Don’t call us, We will call you.”
Callback
function processTurn(card, goOrStop) {
// 패 뒤집고, 점수 계산하고
if (scored && !goOrStop()) {
finishTurn()
} else {
nextTurn()
}
}
processTurn(cardGodori, () => askUserToGoOrStop())
processTurn(cardHongdan, () => decisionByAlphaGoStop())
Template Method (GoF Design Patterns)
abstract class Robot {
head: Head;
body: Body;
build(): void {
this.head = this.createHead();
this.body = this.createBody();
}
abstract createHead(): Head;
abstract createBody(): Body;
}
Template Method (GoF Design Patterns)
class FlyingRobot extends Robot {
createHead(): Head { return ... }
createBody(): Body { return ... }
}
class WalkingRobot extends Robot {
createHead(): Head { return ... }
createBody(): Body { return ... }
}
const robot = new FlyingRobot();
robot.build();
Framework
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
-
리액트 안에서 생각하기
-
리액트 밖에서 생각하기
사례 - Hook을 통한 저수준 모듈 접근 (DI through Hook)
const TodoBoardCotainer = () => {
const api = useApi();
const tasks = useTasks();
const onTaskAdded = async () => {
await api.createTask(...);
tasks.add(...);
};
return <TodoBoard ...
};
사례 - Hook을 통한 저수준 모듈 접근 (DI through Hook)
// import apiv1 from '@/helpers/apiv1';
import apiv2 from '@/helpers/apiv2';
const useApi = () => {
// 훅에서 저수준 모듈을 직접 접근하지만
// 이 Hook을 쓰는 입장에서는 영향을 받지 않음 (마치 DI가 이뤄진 것처럼)
return apiv2;
};
사례 - Hook을 통한 상태 및 액션 제공 (구체적인 상태관리시스템 감추기)
import { useDispatch, useSelector } from 'react-redux';
const useTasks = () => {
const dispatch = useDispatch();
const tasks = useSelector(states => states.tasks);
return {
add: (task) => dispatch.addTask,
...
};
};
사례 - Container와 Presentational Component의 느슨한 관계 만들기
import TodoBoard from '@/components/TodoBoard';
const TodoBoardContainer = () => {
...
return <TodoBoard ...
};
사례 - Container와 Presentational Component의 느슨한 관계 만들기
// @/components/TodoBoard/index.js
import MuiTodoBoard from './MuiTodoBoard'; // './AntdTodoBoard';
export default MuiTodoBoard;
// @/components/TodoBoard/MuiTodoBoard.js
import Table from '@mui/material/Table';
const TodoBoard = () => {
...
return <Table ... />
};
export default TodoBoard;
사례 - GUI 라이브러리 의존성 분리 (React/Vue as a just view library)
const TodoBoardContainer = () => {
const interactor = useInteractor();
const tasks = useObservableState(states => states.tasks);
const onTaskAdded = () => {
interactor.addTask(...);
};
return {tasks.map(...)};
};
사례 - GUI 라이브러리 의존성 분리 (React/Vue as a just view library)
import diContainer from '@/di/container';
const useInteractor = () => {
return diContainer.get('interactor');
}
const useObservableState = (selector) => {
const [state, setState] = useState();
const stateManager = diContainer.get('stateManager');
stateManager.listen(states => setState(selector(states)));
return state;
}