MVVM 패턴으로 비즈니스 로직 분리하기 (1)

Jessie·2024년 8월 15일

소프트웨어를 설계할 때, 비즈니스 로직을 분리하는 것은 중요하다고 한다. UI와 비즈니스 로직은 변경되는 이유와 속도가 다르기 때문이다. UI는 사소한 이유로 바뀔 수 있고, 클라이언트의 요구에 따라 자주 바뀔 수 있다. 반면 핵심 비즈니스 로직은 사업에서 핵심적인 역할을 하며, 그렇기 때문에 바뀔 가능성이 비교적 낮다.

위키피디아에 적힌 비즈니스 로직의 정의는 이렇다.

비즈니스 로직(Business logic)은 컴퓨터 프로그램에서 실세계의 규칙에 따라 데이터를 생성·표시·저장·변경하는 부분을 일컫는다.

비즈니스 로직에 대해 처음 공부를 시작했을때 이 정의가 무슨말인지 이해가 잘 안갔다. 그래서 여러가지 자료를 찾아보고 읽어봤다. 내가 비즈니스 로직에 대해 알아보고 이해한 것을 토대로 간단한 예시를 들어보자면 이렇다. 청년 A는 온라인 사탕 판매 사업을 시작하려고 한다. A는 소프트웨어 엔지니어에게 가서 사탕을 판매하고, 결제를 받고, 고객의 요청에 따라 사탕 구매에 대한 영수증을 발행해주는 웹사이트를 만들어달라고 할 것이다. A가 요구하는 이러한 작업들은 소프트웨어가 해결해야 하는 문제 (또는 위키피디아에서 말하는 실세계의 규칙)인 비즈니스 규칙이다. 그리고 이 문제들을 해결하기 위해 소프트웨어 내부에 작성된 코드는 비즈니스 로직이 된다.

위에 적어놓은 예시에 데이터라는 단어는 전혀 들어가지 않았다. 데이터는 어디에 들어가는 것일까? 다시 구글링을 열심히 해보니 서비스의 정책이나 조건에 따라 정보를 가공, 검증, 제공하는 코드가 비즈니스 코드가 되는 것 같았다. 그렇다면 A의 웹사이트에는 이런 코드들이 비즈니스 로직이 될 것 같다.

  1. 사탕에 대한 상품 정보를 제공한다. 각 사탕의 재고량을 확인하여 현재 구매할 수 있는 상품과 아닌 상품을 구분하고, 회원 할인이나 시즌할인 등 상품의 조건에 따라 할인을 적용한다.
  2. 고객이 장바구니에 사탕을 담으면 종류와 갯수에 따라 총 결제 금액을 계산한다.
  3. 결제를 하기 전에 고객으로부터 연락처와 배송지 주소를 입력받고, 입력된 정보의 유효성을 검사한다.
  4. 결제 정보에 따라 고객의 유저 정보를 업데이트한다. 결제 금액에 따른 포인트 적립, 전자 영수증 발행 체크 여부에 따른 영수증 정보 추가 등.

물론 아직은 배우고 있는 단계라 부족할 수 있지만, 이러한 이해를 바탕으로 내가 작성한 코드를 분리해보고자 했다.

모든것이 섞인 코드

function ProductDetailScene() {
    const { id } = useParams()
    const [productData, setProductData] = useState<ProductDataType>({
        id: '',
        title: '',
        content: '',
        price: 0,
        discount: 0,
        registeredDate: ''
    })
    
    useEffect(() => {
        // 상품의 정보를 받아오는 로직
        const getProductDetail = async () => {
            try {
                const productData = await Axios.get( ... )
                setProductData(productData.data)
            } catch(error) {
                console.error(error)
            }
        }
        
        getProductDetail()
    }, [])
    
    async function addProductToCart() {
        // 장바구니에 상품을 담는 로직
        await Axios.post( ... )
    }
    
    function formatDate(date: string) {
       // ... Date 객체의 날짜를 문자열로 표현한 포맷을 '0000년 00월 00일' 포맷으로 바꿔주는 함수
    }
    
    // 받아온 정보에서 정가와 할인율을 이용하여 할인가를 계산하는 로직
    const discountPrice = productData.price - (productData.price * (productData.discount * 0.01))

	return (
		<ProductDetailContainer>
            <ProductDetailTitle>{productData.title}</ProductDetailTitle>
            <ProductDetailDate>
                상품등록일: {FormatDate(productData.registeredDate)}
            </ProductDetailDate>
			<ProductDetailPriceContainer>
                <ProductDetailOriginalPrice>상품 가격: {productData.price}</ProductDetailOriginalPrice>
                <ProductDetailDiscount>할인: {productData.discount}%</ProductDetailDiscount>
                <ProductDetailDiscountPrice>할인가: {discountPrice}</ProductDetailDiscountPrice>
            </ProductDetailPriceContainer>
            <ProductDetailContent>{productData.content}</ProductDetailContent>
            <ProductDetailCartButton onClick={() => addProductToCart(productId)}> 
                /* 장바구니 버튼 컴포넌트 내용 */
            </ProductDetailCartButton>
		</ProductDetailContainer>
	)
}

export default ExperienceDetailScene

이 코드는 상품의 정보를 보여주는 상세 페이지를 보여주는 컴포넌트 코드이다. MVVM 패턴과 비즈니스 로직에 대해 몰랐을 때는 모든 코드를 하나의 파일 안에 묶어두었는데, 코드의 주석에서 볼 수 있듯이 데이터를 불러오고, 가공하고, 화면을 그리는 모든 로직이 이 컴포넌트 안에 들어있다.


여기에서 비즈니스 로직을 분리해 Model에 넣고, View와 Model을 연결하는 ViewModel 코드도 작성해보겠다. >> 2편에서 계속..

profile
주니어 프론트엔드 개발자입니다 😎

0개의 댓글