상태저장 패턴

프로젝트전공부·2024년 8월 25일

배달 주문 시스템에서 고객의 진행 상태를 백엔드에서 관리하는 방법을 구현하려면, 상태 관리를 통해 고객의 진행 상태를 추적하는 것이 필요합니다. 이를 위해 흔히 사용되는 패턴 중 하나는 상태 머신 패턴(State Machine Pattern)입니다. 이 패턴을 사용하면 고객이 주문 프로세스에서 어디에 있는지 쉽게 추적하고, 중간에 다른 작업을 하더라도 원활하게 이전 상태로 돌아올 수 있게 할 수 있습니다.

  1. 상태 머신 패턴 개요
    상태 머신은 시스템이 특정 상태에서 시작하여, 이벤트에 따라 다른 상태로 전이되는 구조입니다. 예를 들어, "메뉴 선택", "카드 선택", "결제" 등의 상태를 정의하고, 각 상태에서 특정 조건을 충족하면 다음 상태로 전이되도록 설계합니다.

  2. 상태 관리 로직
    상태 정의
    python
    코드 복사
    from enum import Enum

class OrderState(Enum):
MENU_SELECTION = "MENU_SELECTION"
CARD_SELECTION = "CARD_SELECTION"
PAYMENT = "PAYMENT"
COMPLETED = "COMPLETED"
상태 저장
고객의 상태는 세션 또는 데이터베이스에 저장됩니다.
고객이 어디까지 진행했는지를 저장하기 위해 각 고객에 대해 OrderState를 저장하는 필드를 사용합니다.
상태 전이 로직
python
코드 복사
def get_next_state(current_state):
if current_state == OrderState.MENU_SELECTION:
return OrderState.CARD_SELECTION
elif current_state == OrderState.CARD_SELECTION:
return OrderState.PAYMENT
elif current_state == OrderState.PAYMENT:
return OrderState.COMPLETED
else:
return OrderState.MENU_SELECTION # 기본값은 메뉴 선택
3. 상태 확인 및 재진입 처리
고객이 주문을 시작할 때, 현재 고객의 상태를 확인합니다. 만약 고객이 "메뉴 선택" 상태를 완료한 후 다른 작업을 하다가 다시 돌아온 경우, 고객의 현재 상태를 확인하여 "카드 선택" 단계로 바로 넘어갈 수 있도록 합니다.

python
코드 복사
def handle_order_request(user_id):
current_state = get_user_state(user_id) # DB나 세션에서 현재 상태를 가져옴

if current_state == OrderState.MENU_SELECTION:
    return render_menu_selection()
elif current_state == OrderState.CARD_SELECTION:
    return render_card_selection()
elif current_state == OrderState.PAYMENT:
    return render_payment_page()
else:
    return render_menu_selection()  # 기본값으로 메뉴 선택 페이지 렌더링
  1. 상태 업데이트
    각 단계가 완료되면 고객의 상태를 업데이트해야 합니다.

python
코드 복사
def update_user_state(user_id, next_state):

# DB나 세션에 다음 상태를 저장
save_user_state(user_id, next_state)

def menu_selection_completed(user_id):
update_user_state(user_id, OrderState.CARD_SELECTION)

def card_selection_completed(user_id):
update_user_state(user_id, OrderState.PAYMENT)

def payment_completed(user_id):
update_user_state(user_id, OrderState.COMPLETED)
5. 재진입 시 처리
고객이 중간에 다른 작업을 하다가 돌아왔을 때 handle_order_request 함수가 자동으로 현재 상태에 맞는 페이지를 렌더링하도록 보장합니다.

  1. 데이터베이스 스키마 예시
    sql
    코드 복사
    CREATE TABLE user_order_state (
    user_id INT PRIMARY KEY,
    order_state VARCHAR(20)
    );
    고객이 주문을 완료하거나 취소하면 해당 레코드를 업데이트하거나 삭제하여 상태 관리를 합니다.

결론
이와 같은 구조로 백엔드에서 상태 관리를 하면 고객이 주문 과정에서 어디까지 진행했는지 쉽게 추적할 수 있고, 중간에 다른 일을 하더라도 자연스럽게 주문을 이어나갈 수 있습니다. 상태 머신 패턴을 사용함으로써 코드의 가독성과 유지보수성도 높일 수 있습니다.


세션에서 주문 상태를 관리하고 각 상태를 2분간 유지하려면 스프링에서 세션 타임아웃을 설정하고, 세션을 사용하여 상태를 저장 및 관리하는 방법을 구현할 수 있습니다. 아래는 스프링을 사용하여 메뉴 선택, 카드 선택, 결제 상태를 세션에서 관리하는 예시입니다.

  1. 스프링 세션 설정
    스프링 부트에서는 기본적으로 HTTP 세션을 사용하여 상태를 저장합니다. 세션 타임아웃은 기본적으로 30분으로 설정되어 있지만, 이를 2분으로 줄이기 위해 application.properties 파일에서 설정할 수 있습니다.

properties
코드 복사
server.servlet.session.timeout=2m
2. 상태 Enum 정의
주문 상태를 정의하는 OrderState Enum을 작성합니다.

java
코드 복사
public enum OrderState {
MENU_SELECTION,
CARD_SELECTION,
PAYMENT,
COMPLETED
}
3. 서비스 클래스
서비스 클래스에서는 세션에서 상태를 관리하는 메소드를 제공합니다.

java
코드 복사
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpSession;

@Service
public class OrderService {

public void setOrderState(HttpSession session, OrderState state) {
    session.setAttribute("orderState", state);
}

public OrderState getOrderState(HttpSession session) {
    OrderState state = (OrderState) session.getAttribute("orderState");
    return (state != null) ? state : OrderState.MENU_SELECTION;
}

public void advanceState(HttpSession session) {
    OrderState currentState = getOrderState(session);
    OrderState nextState;

    switch (currentState) {
        case MENU_SELECTION:
            nextState = OrderState.CARD_SELECTION;
            break;
        case CARD_SELECTION:
            nextState = OrderState.PAYMENT;
            break;
        case PAYMENT:
            nextState = OrderState.COMPLETED;
            break;
        default:
            nextState = OrderState.MENU_SELECTION;
    }

    setOrderState(session, nextState);
}

public void resetOrder(HttpSession session) {
    setOrderState(session, OrderState.MENU_SELECTION);
}

}
4. 컨트롤러 클래스
컨트롤러에서는 HTTP 요청을 처리하고 상태를 업데이트합니다.

java
코드 복사
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.http.HttpSession;

@Controller
public class OrderController {

@Autowired
private OrderService orderService;

@GetMapping("/order")
public String orderProcess(HttpSession session, Model model) {
    OrderState currentState = orderService.getOrderState(session);

    switch (currentState) {
        case MENU_SELECTION:
            return "menuSelection";  // 메뉴 선택 페이지
        case CARD_SELECTION:
            return "cardSelection";  // 카드 선택 페이지
        case PAYMENT:
            return "payment";        // 결제 페이지
        case COMPLETED:
            return "orderCompleted"; // 주문 완료 페이지
        default:
            return "menuSelection";  // 기본적으로 메뉴 선택 페이지로 돌아감
    }
}

@PostMapping("/order/menu")
public String menuSelectionCompleted(HttpSession session) {
    orderService.advanceState(session);
    return "redirect:/order";
}

@PostMapping("/order/card")
public String cardSelectionCompleted(HttpSession session) {
    orderService.advanceState(session);
    return "redirect:/order";
}

@PostMapping("/order/payment")
public String paymentCompleted(HttpSession session) {
    orderService.advanceState(session);
    return "redirect:/order";
}

@PostMapping("/order/reset")
public String resetOrder(HttpSession session) {
    orderService.resetOrder(session);
    return "redirect:/order";
}

}
5. 뷰 템플릿
각 상태에 맞는 뷰 템플릿을 작성합니다. 예를 들어 menuSelection.html, cardSelection.html, payment.html, orderCompleted.html 등을 작성하여 상태에 맞는 UI를 제공합니다.

  1. 결론
    위의 예제에서는 스프링의 HTTP 세션을 사용하여 주문 상태를 관리하고, 상태를 변경할 때마다 세션에 저장하도록 구현했습니다. 세션 타임아웃을 2분으로 설정하여, 사용자가 2분 동안 아무런 작업을 하지 않으면 세션이 만료되도록 하였습니다.

이 방법은 상태 정보를 서버에서 유지하므로 보안상 안전하며, 세션 타임아웃을 통해 상태를 자동으로 만료시킬 수 있습니다.

profile
프로젝트전에 해당 부분을 깊게 공부한것을 올려두는 벨로그

0개의 댓글