SSOT(Single Source of Truth)

우디(박연기)·2025년 5월 18일
7
post-thumbnail

내가 생각하는 SSOT란? 🧐

SSOT를 한글로 해석하면, “단일 진실 공급원”이 된다. 이를 내 나름대로 해석하면 “동일한 데이터를 가지는 상태는 하나” 라고 해석했다.

예를 들어 아래와 같이 HeaderProductList 컴포넌트가 있다.

<Header/>
<ProductList/>

// Header 컴포넌트
function Header(){
  const [cartItems, setCartItems] = useState<CartItemType[]>([]);
	//...
	 useEffect(() => {
    getCartItems().then(setCartItems);
  }, []);
  
	//...
}

// ProductList 컴포넌트
function ProductList(){
  const [cartItems, setCartItems] = useState<CartItemType[]>([]);
	// ...
	 useEffect(() => {
    getCartItems().then(setCartItems);
  }, []);

  const handleToggleCartItem = async (productId: number) => {
    const existingCartItem = cartItems.find(
      (item) => item.product.id === productId
    );

    if (existingCartItem) {
      await deleteCartItem(existingCartItem.id);
    } else {
      await addCartItem(productId);
    }

    const updatedCartItems = await getCartItems();
    setCartItems(updatedCartItems);
  };
	
	// ...
}

위 예시 코드를 보면, cartItems 이라는 동일한 데이터를 두 개의 컴포넌트에서 각각 상태로 관리하고 있다. 이렇게 코드를 작성했을 때, 문제점은 HeaderProductList 컴포넌트가 동기화가 되지 않는 문제가 발생한다.

예를 들어, 장바구니에 아이템을 추가한 경우 Header에서도 다시 fetching이 일어나야 하지만, 현재 코드에서는 그렇지 못한다. 즉, 동일한 데이터를 2곳에서 따로 관리하니까 동기화가 되지 않는 문제가 발생한다.

결론적으로, 내가 생각하는 SSOT는 동일한 데이터는 하나의 상태로 관리하여 사용하는 것이라고 생각한다.

찾아본 SSOT 🔎

SSOT(Single Source of Truth)는,  “어떤 특정 데이터나 정보에 대해 그 데이터의 정확하고 신뢰할 수 있는 하나의 출처나 저장 위치가 있어야 한다” 이다. 이 원칙은 중복, 모순 및 데이터 불일치 문제를 방지하는 데 도움을 준다.

  • 블로그

모든 데이터 요소를 한 곳에서만 제어 또는 편집하도록 조직하는 관례를 이른다.

위의 말들을 정리해보면, “특정 데이터를 관리하는 곳은 하나”라고 정리할 수 있을 거 같다.

SSOT의 장점 👍

  1. 데이터 일관성 유지
    • 여러 곳에 같은 데이터가 있지 않고, 오직 한 곳에서만 관리되기 때문에 동기화 문제, 데이터 충돌, 버그를 예방할 수 있다.
    • 실시간 데이터, 사용자 상태, 폼 입력 등에서 매우 중요하다.
  2. 예측 가능한 상태 관리
    • 데이터가 어디서 바뀌는지 명확하니, 버그가 발생했을 때 추적이 쉽고 디버깅도 간단하다.
  3. 유지보수와 확정성 확장
    • 단 하나의 출처만 수정하면 되니까 기능 추가, 리팩토링, 테스트 모두 쉬워진다.

SSOT 원칙을 지키는 React 방법론 🤔

수업중 위와 같은 질문을 받았다. 해당 질문을 받았을 때, 어떤 대답을 해야할 지 막막했다.

막막했던 이유를 찾아보면 SSOT 원칙이 무엇인지 모르고, 방법론이라는 단어의 뜻도 제대로 몰라서 해당 문장 자체가 이해가 되지 않았다. 따라서 이해가 되지 않았던 부분부터 알아보고, 해당 질문에 대한 답을 찾아보고자 한다.

SSOT 원칙을 지킨다는 것

프론트엔드에서 SSOT를 지킨다는 것은

  1. 동일한 데이터의 상태를 하나의 주체만 소유한다.
    • 장바구니 상태가 여러 컴포넌트에서 중복 선언되어 있지 않고, 하나의 store/context/component에서만 관리됨
  2. 데이터를 계산하거나 가공하는 곳은 분산되지 않는다.
    • 가격 총합 계산을 여러 곳에서 하지 않고, 단일 함수에서만 처리

방법론이란

  1. 어떤 학문이 사용하는 방법들, 법칙들, 가설들의 원리들을 분석
  1. 어떤 학문 안에서 사용할 수 있거나 혹은 사용해온 방법들의 체계적 연구

  2. 방법들의 연구나 방법들의 서술

-wikipedia(https://ko.wikipedia.org/wiki/방법론)

결국 “SSOT 원칙을 지키는 React 방법론”은 SSOT를 지키기 위해서 리액트에서는 어떤 방법을 사용하는 지를 묻는 질문이다!

SSOT 지키기 위한 리액트에서 방법들

1. 상태 끌어올리기 (Lifting State Up)

  • 동일한 데이터는 가장 가까운 공통 조상 컴포넌트에 두고 하위 컴포넌트는 props로만 접근하는 방식
function App(){
	// 기존 Header와 ProductList 컴포넌트 각각에서 관리했던 상태를 공통 조상 컴포넌트인 App으로 끌어 올림
	const [cartItems, setCartItems] = useState<CartItemType[]>([]);
	
	return (
		<>
			<Header cartItems={cartItems}/>
			<ProductList cartItems={cartItems} setCartItems={setCartItems}/>
		</>
	)
}

2. 전역 상태 관리 도구 사용 (Recoil, Zustand, Redux 등)

  • 앱 전체에서 공유하는 상태를 하나의 store 또는 atom에서 관리.
  • 상태를 여러 곳에서 사용하더라도 중앙에서만 관리하여 SSOT를 유지.
// store(중앙 저장소) 생성 (프로그램 전체에서 사용할 수 있는 상태 선언) 
export const useCartStore = create<CartStore>((set) => ({
  cartItems: [], // 장바구니 상태
  addItem: (item) =>
    set((state) => {
      // 장바구니에 추가하는 코드
    }),
  removeItem: (id) =>
    set((state) => ({
	    // 장바구니에서 제거하는 코드
    })),
}));

// 실제 사용
function App(){
	return (
		<>
			<Header />
			<ProductList />
		</>
	)
}

// Header
function Header() {
	// 중앙에서 장바구니 데이터 가져오기
  const cartItems = useCartStore((state) => state.cartItems);
	// ...
}

// ProductList
function ProductList() {
	// 중앙에서 장바구니 데이터 가져오기
  const {catrItems, addItem, removeItem} = useCartStore();
	// ...
}

3. Context API

  • 예: 로그인 상태, 테마, 언어 설정 등은 Context API로 묶어서 단일 상태로 관리.
  • 중복 props 전달(props drilling)을 줄이면서도 SSOT 유지 가능.
// 저장소 생성
export const CartItemContext = createContext()

// Context Provider로 값 전달하기
function App(){
	const [cartItems, setCartItems] = useState<CartItemType[]>([]);
	
	return (
		<>
			**<CartItemContext.Provider value={{cartItems, setCartItems}} >**
					<Header />
					<ProductList />
			**</CartItemContext.Provider>**
		</>
	)
}

// Header
function Header() {
	// 중앙에서 장바구니 데이터 가져오기
  const {cartItems} = useContext(CartItemContext);
	// ...
}

// ProductList
function ProductList() {
	// 중앙에서 장바구니 데이터 가져오기
  const {cartItems, setCartItems} = useContext(CartItemContext);
  
  // 추가 함수 만들기
	// ...
}

위와 같은 방식은 상태 끌어올리기(Lifting State Up)와 유사하다. 즉, Context API는 상태를 끌어올린 뒤 props drilling 문제를 해결하기 위한 도구라고 생각한다.

따라서 “Context API가 SSOT(Single Source of Truth)를 위한 방법이냐?”고 묻는다면, 나는 그렇지 않다고 대답하고 싶다.

Context는 단지 상태 공유와 전달을 편리하게 해주는 수단이지, SSOT 자체를 보장하는 구조는 아니라고 생각한다.

API 서버는 언제나 진실의 원천일까?

항상 그렇지는 않다.
API 서버가 SSOT가 될 수도 있고, 아닐 수도 있다.

대부분의 경우, API 서버데이터를 생성·보관·검증하는 주체다.

  • 예: 사용자 정보, 주문 내역, 상품 정보 등 DB에 저장되는 모든 데이터

또한, 아래와 같은 경우엔 API 서버가 SSOT다.

  • API 서버가 데이터베이스와 직접 연결
  • 클라이언트나 다른 서비스는 이 서버를 통해서만 데이터에 접근

따라서, 데이터는 항상 API 서버를 통해서만 가져오는 경우 API 서버는 진실의 원천이라고 말할 수 있다.

그런데 항상 API 서버가 항상 DB와 연결되어 있는 것은 아니다!

1. API 서버가 다른 시스템의 프록시인 경우

  • API 서버가 외부 결제 시스템, 배송 시스템다른 백엔드와 통신만 함. 이때 SSOT는 외부 시스템(API 서버가 아닌 곳)이다.
  • 즉, API 서버는 캐싱이나 중계자 역할만 할 수 있으니, API 서버가 SSOT 아니다.

2. 비동기 동기화 상황

  • 클라이언트나 다른 서비스가 서버보다 더 최신 데이터를 가지고 있는 경우
  • 오프라인 모드에서 변경된 로컬 상태 (서버와 클라이언트가 동기화 안 됨)
  • React Query의 최신 캐시가 실제 서버보다 최신인 경우
    • 낙관적 업데이트를 사용하면, 클라이언트에서는 일시적으로 SSOT처럼 동작하는 캐시를 사용한다.
    • 즉, 실제 서버가 진실이지만, UX를 위한 임시 SSOT 역할을 캐시가 수행하는 셈이다.
    • 클라이언트의 임시 상태가 “진짜처럼” 동작함. 일시적으로 클라이언트가 SSOT처럼 보일 수 있음

3. 분산 시스템

  • 마이크로서비스 아키텍처에서는 각 서비스가 자체 DB를 갖고 있음
  • A 서비스의 API 서버B 서비스의 API 서버서로 다른 진실을 가지고 있을 수 있음
  • 즉, 분산 시스템에서는 시스템마다 SSOT가 다를 수 있다. 따라서 하나의 API 서버가 절대적인 진실일 수는 없다.

결과적으로, API 서버는 언제나 진실의 원천이 아니고, 상황이나 문맥에 따라 SSOT는 변경된다!

React 에서 data fetching 시에 SSOT 를 지키기 위해, 어떤 방법들을 실제로 사용되고 있을까?

1. React Query(TanStack Query)

  • 클라이언트에서 서버 상태(데이터)를 캐싱하고, 캐시한 데이터를 SSOT처럼 사용하는 방식
  • 즉, 데이터는 서버에 있지만, 클라이언트에선 React Query 캐시를 단일 진실로 취급한다.

2. 전역 상태 관리 라이브러리 사용(Zustand, Recoil, Redux 등..)

  • 서버에서 가져온 데이터를 전역에서 관리하여 모든 컴포넌트가 하나의 상태를 사용하도록 하여, SSOT를 지키는 방식

3. Context API

  • 상위 컴포넌트에서 데이터 패칭한 뒤, Context를 통해 하위 컴포넌트에 데이터 전달
  • 여러 컴포넌트가 같은 Context를 참조하므로 SSOT 성립

참고

https://ko.wikipedia.org/wiki/단일진실공급원

https://www.atlassian.com/work-management/knowledge-sharing/documentation/building-a-single-source-of-truth-ssot-for-your-team

https://velog.io/@chy8165/SSOT-Single-Source-of-Truth (thanks to 호초)

https://m.blog.naver.com/lifeyun24/223246623000

profile
프론트엔드 개발하는 사람

2개의 댓글

comment-user-thumbnail
2025년 5월 18일

SSOT 알차게 정리한 거 좋은데요?! 👍

답글 달기
comment-user-thumbnail
2025년 5월 19일

SSOT까지 마스터하시다니 ... 멋있는데요

답글 달기