2024. 12. 22. 13:55ㆍFrontend
[ 문제상황 ]
QR 주문 관리자 페이지를 만들 때,
테이블을 관리할 수 있는 탭이 필요했다.
주문목록, 총액, 좌석 번호 등등.
테이블 정보를 시각적으로 보여주고 싶었다.
그리고 테이블을 생성/편집할 수 있어야 했다.
이런 요구사항을 충족할 수 있는 2D canvas 프레임워크 Konva를 알게 되었다.
기본적인 좌석 생성/수정/삭제 기능을 구현하고
좌석 메타데이터를 넣기 위해 redux의 훅을 사용했다.
그런데, Konva의 Stage 하위 컴포넌트에서 Provider를 찾지 못하는 에러가 발생했다.
[ 해결시도 1 ]
https://github.com/konvajs/react-konva/issues/311
Support of React-Redux 6.0.0 · Issue #311 · konvajs/react-konva
Hello, I am trying to use latest react@16.7.0 and react-redux@6.0.0 modules with React Konva and I get errors: browser.js:38 Uncaught Error: Could not find "store" in the context of "Connect(Compon...
github.com
위 링크에서 비슷한 이슈를 찾았다.
명시적으로 Konva 컴포넌트에 store를 부여하지 않아
오류가 발생한다는 담당 개발자의 답변이 있었다.
그래서 "ReactReduxContext"를 적용하고
Provider를 통해 store를 하위 컴포넌트로 내려주었다.
import { ReactReduxContext, Provider } from "react-redux";
<ReactReduxContext.Consumer>
<Provider>
<Stage>
<Table />
</Stage>
</Provider>
</ReactReduxContext.Consumer>
하지만 여전히 오류가 발생했다.
Uncaught Error: could not find react-redux context value: ~.
Redux의 훅은 적용되지 않았다.
[ 해결시도 2 ]
store의 state, dispatch를 하위 컴포넌트로 직접 내려주는 방법을 적용해 봤다.
{({ store }) => (
<Stage>
<Table store={store} />
</Stage>
)}
Table 컴포넌트에서 상태 접근 가능했고 갱신되었다.
다만, 상태는 한 번 갱신된 이후로 고정되었다.
일회용 값이었다.
그리고 받아온 store 내 dispatch는 작동하지 않았다.
[ 해결시도 3 ]
Konva 컴포넌트 밖에서 상태를 갱신하는 함수를 작성해 props로 전달해봤다.
<Stage >
<Table state={state} dispatch={dispatch} />
</Stage >
이 방법은 작동되었다.
하지만 props drilling 문제를 발생시켰다.
컴포넌트 독립성이 보장되지 않아 마음에 들지 않는 방법이었다.
[ 문제원인 ]
Konva와 Redux가 서로 연동되지 않는 이유를 GPT를 통해 알아봤다.
두 프레임워크와 라이브러리는 렌더링 방식에서 차이가 있다고 했다.
Konva는 HTML Canvas를 통해 렌더링 되어,
객체 내부 상태에 관여하지 않아 상태를 감지 하지 않는다.
그런데 Konva는 React 상태를 감지했다.
그 이유는 react-konva 라이브러리를 사용하기 때문이었다.
react-konva는 React > Konva > Canvas 트리 구조로 구성되어 있었다.
React에서 상태가 변경되면 상태가 Konva로 전달될 수 있었다.
Redux 훅은 action, dispatch를 통해 상태 변경을 감지한다.
내부 상태에 관여하지 않는 Canvas 특성 상,
Konva 컴포넌트를 업데이트 하려면 트리거가 필요했다.
훅은 context 오류로 Konva에서 사용할 수 없었다.
직접 Props로 내린 store 값을 사용하는 방법을 사용해야 했다.
상태가 변경되면 Canvas를 다시 그리거나
useEffect를 통해 해당 Konva 컴포넌트를 리렌더링 하는 방법이 떠올랐다.
조금 번거로운 방법이었다.
위 링크에서 아랫 내용을 천천히 보면
react-redux는 context 매커니즘을 사용하고 있어
저장 가능한 공간을 생성했다.
그러나 Konva의 하위 컴포넌트에서는
저장공간(context)이 손실되기 때문에
직접 값을 내려주어야 했다.
[ 해결방안 ]
- 리액트 함수 컴포넌트
첫 시도 때는 Store를 Stage 컴포넌트 위에서 전달시켰다.
<Store>
<Stage>
<Table />
</Stage>
</Store>
Konva는 캔버스 요소로 DOM 아니기에
React DOM과 연결이 끊겨 상태를 전달하지 못했다.
그래서 Stage 하위 컴포넌트는 저장공간이 손실 되어 Context 오류가 발생했다.
이를 해결하기 위해 Provider를 Stage 컴포넌트를 아래로 이동시켜 store를 전달했다.
<Stage>
<Store> // Stage -> Store 변경
<Table />
</Store>
</Stage>
결과는 성공이었다.
useSelector, useDispatch 모두 정상적으로 동작했다.
가끔 하위 요소가 상위 요소보다 상태값을 먼저 받는 오류가 발생할 수 있는데
업데이트 순서 문제로 그때는 해당 부분을 컴포넌트화 하여 해결하면 된다.
- 리액트 클래스 컴포넌트 (데모)
https://codesandbox.io/p/sandbox/zxny6w50o4?file=%2Fcomponents%2FTarget.js%3A78%2C16-78%2C23
codesandbox.io
[ 마무리 ]
이번 문제는
Konva 렌더링 특징을 잘 모르고,
issue 밑부분을 제대로 정독하지 않아 헤맸다.
코드 위치만 내려 해결할 수 있다는 것에 허탈하기도 하고
해결했다는 것에 기분이 좋기도 하다.
[ 참고 ]
konva 공식문서, https://konvajs.org/docs/react/index.html
canvas란, https://www.nextree.co.kr/p9307/
redux hooks 동작원리, https://react-redux.js.org/api/hooks
redux connect 메서드 동작원리, https://react-redux.js.org/api/connect#overview
'Frontend' 카테고리의 다른 글
브라우저 이벤트 호출 수는 어느 웹에서든 동일하지 않나 (0) | 2025.01.25 |
---|---|
웹 로딩 개선 과정, QR-ORDER 관리자 프로젝트 (0) | 2025.01.18 |
Vercel에서 Socket.IO 배포하는 방법 (+ 안 되는 이유) (0) | 2024.09.14 |
[JS] VPC서버에서 Micro Server 설정과 Node 파일 실행 어떻게 하나요? (0) | 2024.08.20 |
VPC서버에서 3세대 Micro Server는 어떻게 생성하나요? (0) | 2024.08.19 |