들어가며
저희 회사에서는 React Native를 사용하여 교육용 앱 서비스를 개발하고 있습니다. 이 서비스에서 저는 카드 게임이 들어가는 기능을 맡아 개발하게 되었습니다.
하지만 개발을 시작한 지 얼마 지나지 않아 다양한 인터랙션과 애니메이션, 게임의 상태 관리, 카드의 위치와 상태 관리 등 복잡하게 뒤엉킨 요구사항에 부딪히게 되었습니다.
이러한 복잡한 요구사항에 디자인 패턴을 적용하여 풀어나간 이야기를 써보고자 합니다.
요구사항
핵심 요구사항은 카드 슬롯 교체 기능이었습니다. 2개의 보드 슬롯 중 한 곳에 이미 카드가 있을 때, 다른 카드를 올리면 기존 카드가 자동으로 빈 슬롯으로 이동해야 했습니다.
이를 구현하기 위해 카드 배치 상태 플래그, 드래그 시의 Snap 로직, 위치 변경 애니메이션 트리거 등 useState, useRef, react-native-reanimated 로직이 메인 컴포넌트에 집중되었습니다. 게임에 다른 여러가지 기능이 추가되면서 메인 컴포넌트는 수많은 state와 side effect들로 인해 점점 비대해져갔습니다.
이 문제를 해결하기 위해 카드간의 관계와 애니메이션을 제어하는 로직을 Mediator 패턴이라는 디자인 패턴을 적용하여 분리해보자 라는 생각을 하게 되었습니다.
Mediator(중재자) 패턴이란?
중재자 패턴은 서로 독립적으로 작동해야 하는 컴포넌트 간의 모든 직접 통신을 중단한 후, 컴포넌트들의 호출을 적절한 컴포넌트들로 리다이렉션하는 특수 중재자 객체를 호출하여 간접적으로 협력하게 하라고 제안합니다. 그러면 컴포넌트들은 수십 개의 동료 컴포넌트들과 결합되는 대신 단일 중재자 클래스에만 의존하게 되어 결합도가 크게 낮아집니다.
카드 컴포넌트들은 드래그앤드롭을 통해 독립적으로 움직이지만, 동시에 다른 카드의 위치와 상태에 따라 자신의 위치가 강제로 변경되어야 했습니다."독립성을 유지하면서 상호작용은 필요한" 상황이었기에, 중재자 패턴을 통해 카드 간의 복잡한 위치 결정 로직을 분리하는 것이 적합하다고 판단하였습니다.
구현
Mediator 클래스를 구현하여 카드가 드롭되었을 때 카드 간 액션을 어떻게 할 것인지 결정하고 다른쪽 카드의 id를 찾아서 해당 카드의 움직임을 제어하는 로직을 구현하였습니다. 이러한 로직을 통해 카드간의 직접적인 상호작용을 피하고 복잡한 의존 관계를 줄일 수 있었습니다.
class Mediator {
...
public handleDrop = (cardId) => {
// 1. 카드에 발생해야 하는 이벤트를 계산
const event = this.calculateEvent();
// 2. 계산 결과를 바탕으로 UI 애니메이션을 지시
this.animatePositions(event, cardId);
};
/**
* 계산된 결과에 따라 Reanimated를 통해 카드들의 움직임을 제어
* UI 스레드에서 실행되도록 runOnUI를 사용
*/
private animatePositions = (event, cardId) => {
// UI 스레드에서 애니메이션 실행 지시
runOnUI(() => {
'worklet';
if (event==='snap') {
// 단일 카드 애니메이션 지시
} else if (event==='swap') {
// 카드 A (드롭된 카드)의 위치 애니메이션 지시
this.springAnimation(targetPositionA,cardId);
// 카드 B (밀려난 카드)의 위치 애니메이션 지시
this.swapAnimation(targetPositionB,otherCardId);
}
})();
};
}
맺음말
그동안은 프론트엔드 코드를 작성하면서 컴포넌트 간의 복잡한 의존 관계를 줄이는 경험이 부족했었습니다. 코드에 Mediator 패턴을 적용한 결과, 메인 컴포넌트는 깔끔하게 유지되었고 복잡했던 카드 상호작용 로직은 단일 책임 클래스로 캡슐화되었습니다.
이번 기능 구현과 리팩토링을 통해 디자인 패턴이 단순한 이론이 아니라 복잡성을 관리하고 코드의 수명을 연장할 수 있는 도구 중 하나라는 사실을 깨닫게 되었고 코드의 유지보수성을 높이는 좋은 경험을 할 수 있었습니다.
본문의 코드 예시는 이해를 돕기 위한 간단한 예시일 뿐입니다.
실제 프로덕션용 코드로 사용하기에는 수정이 필요한 부분이 많다는 점 참고바랍니다.
- 참고 링크
https://refactoring.guru/ko/design-patterns/mediator
'Frontend' 카테고리의 다른 글
| 테블리 프로젝트 폴더 구조 설계 (0) | 2025.01.05 |
|---|