티스토리 뷰
회사에서 Node 기반으로 개발을 할 수 없는 경우, 기본적인 HTML, CSS, JS를 이용할 수 밖에 없고, Spring Boot를 이용 시 타임리프를 이용해서 개발을 할 수도 있습니다.
해당 기술로도 왠만한 사이트는 구현 가능하고, 모바일 사이트의 경우 경량화를 위해서 일부러 바닐라 JS를 이용하기도 합니다. 하지만 간혹 여러 페이지를 하나의 싱글 페이지오 만들어야하는 기획서를 마주한다면 기존 방식으로 구현하는 것이 조금 어려울 수도 있습니다.
하나의 Html에서 여러 페이지를 만들어야 하기 때문에 UI 렌더링 로직이 복잡해지고, 데이터 조작 관련 로직이 서로 엮여 읽기도 어렵습니다. 또한 두 렌더링 로직과 데이터 관련 로직 구분도 어려워 집니다.
그런 어쩔 수 없는 상황에서 상태란 개념만 가져와 데이터와 UI 렌더링을 분리하여 코드를 정리해보려고 합니다.
상태 관리 라이브러리
데이터 로직과 UI 렌더링이 분리되려면 상태 관리 개념을 이용해야 합니다. 더 정확히는 옵저버 패턴을 이용한다고 볼 수도 있습니다.
기존 프론트 아키텍처의 문제
기존 바닐라 JS나 Jquery로 개발할 때 문제가 되는 부분은 데이터가 받아오는 코드와 데이터를 이용해서 화면을 그리는 코드가 혼합되어 있다는 점입니다. 혼합되어 있다는 의미는 해당 데이터를 받은 후 UI 렌더링을 관련 코드를 직접 작성해서 사용한다입니다.
해당 설명에서 데이터를 받아와서 어딘가 저장하고 UI를 그리는 함수를 만들어서 호출하면 되지 않나요?라고 이야기할 수 있습니다. 하지만 변경된 데이터에 해당하는 UI만 변경되는 것을 상상해보세요.
그렇기 때문에 해당 사이트를 개발하지 않은 사람은 화면이 동작하는 로직을 알기 위해서 ajax call을 확인하거나 화면 내에 있는 태그를 확인하여 태그를 어디서 그려주는지 확인하여야 합니다.
반면 리액트 코드를 보면 name을 div태그에 { } 을 이용하여 넣고 있습니다. 그래서 name의 데이터만 바꾸면 자동으로 렌더링이 발생하는 구조입니다.그렇기 때문에 데이터 적재와 렌더링 코드가 분리되기 때문에 코드가 책임에 따라 분리되어 가독성이 좋아집니다.
import React, { useState } from 'react';
import './App.css';
function MyComponent() {
const [name, setName] = useState("홍길동");
return (
<div>
{name}
</div>
);
}
function App() {
return (
<div className="App">
<header className="App-header">
<MyComponent />
</header>
</div>
);
}
export default App;
Zustand
어떻게 바닐라 Js에서 상태 관리를 적용할 수 있을까? 저는 직접 구현도 가능하겠지만, 되도록이면 라이브러리를 이용하면 좋겠다고 생각합니다.
그렇기 때문에 기존 상태 관리 라이브러리를 찾아보면서 확인해보니 Zustand와 Jotai란 라이브러리를 바닐라 Js에서도 사용할 수 있음을 알게 되었습니다.
Zustand를 이용한 예시입니다.
코드 조각으로 나누어 해당 기능에 대해서 알아보겠습니다.
먼저 zustand를 vallina로 사용할 수 있도록 불러옵니다.
import { createStore } from 'https://cdn.jsdelivr.net/npm/zustand@4.5.4/esm/vanilla.js';
또한 module 형식으로 불러오기 위해서 <script type="moudle"> </script> 를 사용해야 합니다.
해당 mudule 관련되어 아이폰 6 플러스 iOS 12 버전에서 동작함을 확인하였습니다.
브라우저의 내의 DB라고 불렀던 상태 관리소를 Zustand에서는 store라고 부릅니다.
Store에는 저장되는 변수와 이 변수를 변경시킬 수 있는 함수가 들어갑니다.
const useStore = createStore((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}));
그리고 UI를 변경시키는 함수도 하나 생성합니다.
// UI 업데이트 함수
function updateUI() {
const state = useStore.getState();
$('#count').text(state.count);
}
그렇다면 이 업데이트 함수는 언제 실행될까요? 바로 Storea(DB)에 있는 값이 변경되었을 때입니다.
그러면 이 업데이트 함수는 Stora의 값이 변경되었을 때 작동되어야 하는데 어떻게 Store가 변경되었을 때 UpdateUI함수를 실행시킬까요?
useStore.subscribe(updateUI);
Store 객체에 구독 함수를 이용해서 함수를 연결하여 사용합니다.
왜 상태 관리를 사용 해야 하는가
지금까지 설명을 따라오면 문득 이런 생각을 하게 됩니다. 기존 방식과 비슷한데 굳이 이 방식을 사용해야 하나? 크게 다른가?
기존의 것으로 쓰면 되는데 (한심...)
왜 굳이 써야 할까요?
UI 렌더링 시 데이터만 조작하는데 집중하면 됩니다.
const useStore = createStore((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}));
// UI 업데이트 함수
function updateUI() {
const state = useStore.getState();
$('#count').text(state.count);
}
더 나아간다면 ... Node
더 나아간다면 Node 환경에서 개발해서 조금 더 현대스러운 개발을 해야 할지도 모릅니다. 다른 라이브러리들을 이용하기 위해서는 cdn으로 이용하기엔 부족한 점이 많기 때문입니다.
다른 글에서 어떠한 방식으로 zustand를 이용할 수 있는 예제 몇가지 올려두겠습니다.
'코딩 관련 > 자바스크립트' 카테고리의 다른 글
Vite + Spring Boot 짬뽕 (0) | 2024.10.29 |
---|---|
웹 최적화: Webp 변환 및 미지원 브라우저 대응법 (0) | 2024.08.28 |
Youtube Ifram을 하는데 썸네일, 또는 버튼을 바꾸고 싶은 경우 (0) | 2024.06.28 |
'Vanilla JS X Dom 객체를 이용한 SPA처럼 페이지 만드는 방법' (0) | 2024.06.25 |
Currying/Partial Application 인수를 하나를 받아, 복잡도를 낮추는 방법 (0) | 2023.10.19 |