관리 메뉴

deVlog

[λ””μžμΈ νŒ¨ν„΄] ν–‰μœ„ νŒ¨ν„΄ - Status Pattern (μƒνƒœ νŒ¨ν„΄) λ³Έλ¬Έ

πŸ› οΈ Software Architecture/Design Pattern

[λ””μžμΈ νŒ¨ν„΄] ν–‰μœ„ νŒ¨ν„΄ - Status Pattern (μƒνƒœ νŒ¨ν„΄)

은루밍 2025. 4. 2. 23:21

λͺ©μ°¨

     

    πŸ“‡ κ°œμš”

    μƒνƒœ νŒ¨ν„΄(State Pattern)은 객체의 λ‚΄λΆ€ μƒνƒœκ°€ 변경될 λ•Œ 객체의 행동이 λ³€κ²½λ˜λ„λ‘ ν•˜λŠ” 행동 λ””μžμΈ νŒ¨ν„΄μ΄λ‹€. 즉, 객체의 μƒνƒœμ— 따라 객체의 행동이 λ‹¬λΌμ§€κ²Œ ν•˜λŠ” νŒ¨ν„΄μœΌλ‘œ, 마치 객체의 ν΄λž˜μŠ€κ°€ λ³€κ²½λ˜λŠ” κ²ƒμ²˜λŸΌ 보이게 ν•œλ‹€.

     

    νŠΉμ§•

    • μƒνƒœ μ „ν™˜ λ‘œμ§μ„ μΊ‘μŠν™”ν•˜μ—¬ μ½”λ“œμ˜ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ„ 높인닀.
    • μƒνƒœλ³„ λ™μž‘μ„ λ³„λ„μ˜ 클래슀둜 λΆ„λ¦¬ν•˜μ—¬ 단일 μ±…μž„ 원칙(SRP)을 μ€€μˆ˜ν•œλ‹€.
    • 쑰건문(if-else, switch)을 μ œκ±°ν•˜κ³  객체지ν–₯적인 λ°©μ‹μœΌλ‘œ μƒνƒœ μ „ν™˜μ„ μ²˜λ¦¬ν•œλ‹€.

     

    πŸ‘©πŸ»‍πŸ’» μ½”λ“œλ‘œ μ•Œμ•„λ³΄μž

    자판기의 μƒνƒœ λ³€ν™”λ₯Ό κ΅¬ν˜„ν•˜λŠ” 예제λ₯Ό 톡해 μƒνƒœ νŒ¨ν„΄μ„ μ‚΄νŽ΄λ³΄μž.

    // 1. μƒνƒœ μΈν„°νŽ˜μ΄μŠ€
    interface State {
        void insertCoin(VendingMachine vendingMachine);
        void selectProduct(VendingMachine vendingMachine);
        void dispense(VendingMachine vendingMachine);
        void refund(VendingMachine vendingMachine);
    }
    
    // 2. ꡬ체적인 μƒνƒœ ν΄λž˜μŠ€λ“€
    class NoCoinState implements State {
        @Override
        public void insertCoin(VendingMachine vendingMachine) {
            System.out.println("동전이 νˆ¬μž…λ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
            vendingMachine.setState(vendingMachine.getHasCoinState());
        }
        
        @Override
        public void selectProduct(VendingMachine vendingMachine) {
            System.out.println("동전을 λ¨Όμ € λ„£μ–΄μ£Όμ„Έμš”.");
        }
        
        @Override
        public void dispense(VendingMachine vendingMachine) {
            System.out.println("동전을 λ¨Όμ € λ„£μ–΄μ£Όμ„Έμš”.");
        }
        
        @Override
        public void refund(VendingMachine vendingMachine) {
            System.out.println("λ°˜ν™˜ν•  동전이 μ—†μŠ΅λ‹ˆλ‹€.");
        }
    }
    
    class HasCoinState implements State {
        @Override
        public void insertCoin(VendingMachine vendingMachine) {
            System.out.println("이미 동전이 λ“€μ–΄μžˆμŠ΅λ‹ˆλ‹€. μƒν’ˆμ„ μ„ νƒν•΄μ£Όμ„Έμš”.");
        }
        
        @Override
        public void selectProduct(VendingMachine vendingMachine) {
            System.out.println("μƒν’ˆμ΄ μ„ νƒλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
            vendingMachine.setState(vendingMachine.getProductSelectedState());
        }
        
        @Override
        public void dispense(VendingMachine vendingMachine) {
            System.out.println("μƒν’ˆμ„ λ¨Όμ € μ„ νƒν•΄μ£Όμ„Έμš”.");
        }
        
        @Override
        public void refund(VendingMachine vendingMachine) {
            System.out.println("동전이 λ°˜ν™˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
            vendingMachine.setState(vendingMachine.getNoCoinState());
        }
    }
    
    class ProductSelectedState implements State {
        @Override
        public void insertCoin(VendingMachine vendingMachine) {
            System.out.println("이미 μƒν’ˆμ΄ μ„ νƒλ˜μ—ˆμŠ΅λ‹ˆλ‹€. μΆ”κ°€ 동전은 ν•„μš” μ—†μŠ΅λ‹ˆλ‹€.");
        }
        
        @Override
        public void selectProduct(VendingMachine vendingMachine) {
            System.out.println("이미 μƒν’ˆμ΄ μ„ νƒλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
        }
        
        @Override
        public void dispense(VendingMachine vendingMachine) {
            System.out.println("μƒν’ˆμ΄ λ‚˜μ™”μŠ΅λ‹ˆλ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€!");
            vendingMachine.setState(vendingMachine.getNoCoinState());
        }
        
        @Override
        public void refund(VendingMachine vendingMachine) {
            System.out.println("μƒν’ˆ 선택이 μ·¨μ†Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. 동전이 λ°˜ν™˜λ©λ‹ˆλ‹€.");
            vendingMachine.setState(vendingMachine.getNoCoinState());
        }
    }
    
    class SoldOutState implements State {
        @Override
        public void insertCoin(VendingMachine vendingMachine) {
            System.out.println("μ£„μ†‘ν•©λ‹ˆλ‹€. μž¬κ³ κ°€ μ—†μŠ΅λ‹ˆλ‹€. 동전이 λ°˜ν™˜λ©λ‹ˆλ‹€.");
        }
        
        @Override
        public void selectProduct(VendingMachine vendingMachine) {
            System.out.println("μ£„μ†‘ν•©λ‹ˆλ‹€. μž¬κ³ κ°€ μ—†μŠ΅λ‹ˆλ‹€.");
        }
        
        @Override
        public void dispense(VendingMachine vendingMachine) {
            System.out.println("μ‹€νŒ¨: μž¬κ³ κ°€ μ—†μŠ΅λ‹ˆλ‹€.");
        }
        
        @Override
        public void refund(VendingMachine vendingMachine) {
            System.out.println("λ°˜ν™˜ν•  동전이 μ—†μŠ΅λ‹ˆλ‹€.");
        }
    }
    
    // 3. μ»¨ν…μŠ€νŠΈ 클래슀
    class VendingMachine {
        private State noCoinState;
        private State hasCoinState;
        private State productSelectedState;
        private State soldOutState;
        
        private State currentState;
        private int count; // μƒν’ˆ 재고
        
        public VendingMachine(int count) {
            // μƒνƒœ 객체듀 μ΄ˆκΈ°ν™”
            noCoinState = new NoCoinState();
            hasCoinState = new HasCoinState();
            productSelectedState = new ProductSelectedState();
            soldOutState = new SoldOutState();
            
            this.count = count;
            if (count > 0) {
                currentState = noCoinState;
            } else {
                currentState = soldOutState;
            }
        }
        
        // μ‚¬μš©μž 행동 λ©”μ„œλ“œλ“€
        public void insertCoin() {
            currentState.insertCoin(this);
        }
        
        public void selectProduct() {
            currentState.selectProduct(this);
        }
        
        public void dispense() {
            currentState.dispense(this);
            if (currentState != soldOutState) {
                count--;
                if (count == 0) {
                    setState(soldOutState);
                }
            }
        }
        
        public void refund() {
            currentState.refund(this);
        }
        
        // μƒνƒœ λ³€κ²½ λ©”μ„œλ“œ
        public void setState(State state) {
            this.currentState = state;
        }
        
        // μƒνƒœ νšλ“ λ©”μ„œλ“œλ“€
        public State getNoCoinState() {
            return noCoinState;
        }
        
        public State getHasCoinState() {
            return hasCoinState;
        }
        
        public State getProductSelectedState() {
            return productSelectedState;
        }
        
        public State getSoldOutState() {
            return soldOutState;
        }
        
        // ν˜„μž¬ μƒνƒœ 좜λ ₯
        public void printStatus() {
            System.out.println("ν˜„μž¬ μƒνƒœ: " + currentState.getClass().getSimpleName());
            System.out.println("남은 재고: " + count);
        }
    }
    
    // 4. ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œ
    public class StatePatternExample {
        public static void main(String[] args) {
            // 자판기 생성 (재고 3개)
            VendingMachine vendingMachine = new VendingMachine(3);
            
            // 정상 μ‹œλ‚˜λ¦¬μ˜€: 동전 νˆ¬μž… -> μƒν’ˆ 선택 -> μƒν’ˆ 배좜
            System.out.println("----- μ‹œλ‚˜λ¦¬μ˜€ 1: 정상 ꡬ맀 κ³Όμ • -----");
            vendingMachine.printStatus();
            vendingMachine.insertCoin();
            vendingMachine.selectProduct();
            vendingMachine.dispense();
            vendingMachine.printStatus();
            
            // 동전 λ°˜ν™˜ μ‹œλ‚˜λ¦¬μ˜€: 동전 νˆ¬μž… -> λ°˜ν™˜
            System.out.println("\n----- μ‹œλ‚˜λ¦¬μ˜€ 2: 동전 λ°˜ν™˜ -----");
            vendingMachine.insertCoin();
            vendingMachine.refund();
            vendingMachine.printStatus();
            
            // μƒν’ˆ 선택 μ·¨μ†Œ μ‹œλ‚˜λ¦¬μ˜€: 동전 νˆ¬μž… -> μƒν’ˆ 선택 -> μ·¨μ†Œ
            System.out.println("\n----- μ‹œλ‚˜λ¦¬μ˜€ 3: μƒν’ˆ 선택 μ·¨μ†Œ -----");
            vendingMachine.insertCoin();
            vendingMachine.selectProduct();
            vendingMachine.refund();
            vendingMachine.printStatus();
            
            // 재고 μ†Œμ§„ μ‹œλ‚˜λ¦¬μ˜€
            System.out.println("\n----- μ‹œλ‚˜λ¦¬μ˜€ 4: 재고 μ†Œμ§„ -----");
            vendingMachine.insertCoin();
            vendingMachine.selectProduct();
            vendingMachine.dispense();
            
            vendingMachine.insertCoin();
            vendingMachine.selectProduct();
            vendingMachine.dispense();
            vendingMachine.printStatus();
            
            // 재고 μ—†μŒ μ‹œλ‚˜λ¦¬μ˜€
            System.out.println("\n----- μ‹œλ‚˜λ¦¬μ˜€ 5: 재고 μ—†μŒ -----");
            vendingMachine.insertCoin();
            vendingMachine.printStatus();
        }
    }
    
    • State μΈν„°νŽ˜μ΄μŠ€λŠ” 자판기의 λͺ¨λ“  행동에 λŒ€ν•œ λ©”μ„œλ“œλ₯Ό μ •μ˜ν•œλ‹€.
    • ꡬ체적인 μƒνƒœ ν΄λž˜μŠ€λ“€(NoCoinState, HasCoinState, ProductSelectedState, SoldOutState)은 각 μƒνƒœμ—μ„œμ˜ 행동을 κ΅¬ν˜„ν•œλ‹€.
    • VendingMachine ν΄λž˜μŠ€λŠ” μ»¨ν…μŠ€νŠΈ 역할을 ν•˜λ©°, ν˜„μž¬ μƒνƒœλ₯Ό μœ μ§€ν•˜κ³  μ‚¬μš©μž 행동을 ν˜„μž¬ μƒνƒœμ— μœ„μž„ν•œλ‹€.
    • μƒνƒœκ°€ μ „ν™˜λ  λ•Œλ§ˆλ‹€ μ»¨ν…μŠ€νŠΈμ˜ setState() λ©”μ„œλ“œλ₯Ό 톡해 μƒνƒœ 객체가 λ³€κ²½λœλ‹€.

     

    ☘️ μž₯/단점

    βœ… μž₯점

    1. 쑰건문 제거
      • λ³΅μž‘ν•œ 쑰건문(if-else, switch)을 μ œκ±°ν•˜κ³  객체지ν–₯적인 λ°©μ‹μœΌλ‘œ μƒνƒœλ³„ 행동을 λΆ„λ¦¬ν•œλ‹€.
      • μ΄λŠ” μ½”λ“œμ˜ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ„ 크게 ν–₯μƒμ‹œν‚¨λ‹€.
    2. 단일 μ±…μž„ 원칙(SRP) μ€€μˆ˜
      • 각 μƒνƒœμ˜ 행동을 λ³„λ„μ˜ 클래슀둜 μΊ‘μŠν™”ν•˜μ—¬ μ±…μž„μ„ λΆ„μ‚°μ‹œν‚¨λ‹€.
      • νŠΉμ • μƒνƒœμ™€ κ΄€λ ¨λœ ν–‰λ™λ§Œ λ³€κ²½ν•˜λ©΄ λ˜λ―€λ‘œ μ½”λ“œ 변경이 쉽닀.
    3. 개방/폐쇄 원칙(OCP) μ€€μˆ˜
      • κΈ°μ‘΄ μ½”λ“œλ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³ λ„ μƒˆλ‘œμš΄ μƒνƒœμ™€ 행동을 μΆ”κ°€ν•  수 μžˆλ‹€.
      • 예: μžνŒκΈ°μ— '점검 쀑' μƒνƒœλ₯Ό μΆ”κ°€ν•˜λ”λΌλ„ κΈ°μ‘΄ μƒνƒœ ν΄λž˜μŠ€λŠ” λ³€κ²½ν•  ν•„μš”κ°€ μ—†λ‹€.
    4. μƒνƒœ μ „ν™˜ 둜직의 λͺ…ν™•ν™”
      • μƒνƒœ μ „ν™˜ 둜직이 각 μƒνƒœ 클래슀 내뢀에 λΆ„μ‚°λ˜μ–΄ μžˆμ–΄ μƒνƒœ 관리가 λͺ…ν™•ν•΄μ§„λ‹€.
      • 디버깅과 μ½”λ“œ 이해가 μ‰¬μ›Œμ§„λ‹€.

    ❌ 단점

    1. 클래슀 수 증가
      • 각 μƒνƒœλ§ˆλ‹€ λ³„λ„μ˜ ν΄λž˜μŠ€κ°€ ν•„μš”ν•˜λ―€λ‘œ 전체 클래슀 μˆ˜κ°€ λ§Žμ•„μ§ˆ 수 μžˆλ‹€.
      • κ°„λ‹¨ν•œ μƒνƒœ κ΄€λ¦¬μ—λŠ” κ³Όλ„ν•œ λ””μžμΈμ΄ 될 수 μžˆλ‹€.
    2. μƒνƒœ κ°„ μ˜μ‘΄μ„± 관리
      • μƒνƒœ μ „ν™˜ 둜직이 μƒνƒœ ν΄λž˜μŠ€μ— λΆ„μ‚°λ˜μ–΄ 있기 λ•Œλ¬Έμ— μƒνƒœ κ°„ μ˜μ‘΄μ„±μ΄ 생길 수 μžˆλ‹€.
      • 이둜 인해 ν•œ μƒνƒœλ₯Ό λ³€κ²½ν•  λ•Œ λ‹€λ₯Έ μƒνƒœλ„ ν•¨κ»˜ λ³€κ²½ν•΄μ•Ό ν•  수 μžˆλ‹€.
    3. μ»¨ν…μŠ€νŠΈ 쒅속성
      • μƒνƒœ ν΄λž˜μŠ€κ°€ μ»¨ν…μŠ€νŠΈ(VendingMachine)에 κ°•ν•˜κ²Œ κ²°ν•©λ˜μ–΄ μžˆμ–΄ μž¬μ‚¬μš©μ„±μ΄ μ œν•œλ  수 μžˆλ‹€.
      • μƒνƒœ ν΄λž˜μŠ€λŠ” μ»¨ν…μŠ€νŠΈμ˜ λ©”μ„œλ“œμ™€ 속성에 μ ‘κ·Όν•΄μ•Ό ν•  수 μžˆλ‹€.
    4. μƒνƒœ 객체 관리 μ˜€λ²„ν—€λ“œ
      • λͺ¨λ“  μƒνƒœ 객체λ₯Ό 미리 μƒμ„±ν•˜κ³  μœ μ§€ν•΄μ•Ό ν•˜λ―€λ‘œ λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ 증가할 수 μžˆλ‹€.
      • 특히 μƒνƒœκ°€ 많고 λ³΅μž‘ν•œ μ‹œμŠ€ν…œμ—μ„œλŠ” 이 λ¬Έμ œκ°€ λ‘λ“œλŸ¬μ§ˆ 수 μžˆλ‹€.

     

    πŸ“Œ 정리

    μƒνƒœ νŒ¨ν„΄μ€ 객체의 λ‚΄λΆ€ μƒνƒœκ°€ 변경될 λ•Œ 객체의 행동이 λ³€κ²½λ˜λ„λ‘ ν•˜λŠ” λ””μžμΈ νŒ¨ν„΄μ΄λ‹€.

    이 νŒ¨ν„΄μ€ μƒνƒœλ³„ 행동을 λ³„λ„μ˜ 클래슀둜 μΊ‘μŠν™”ν•˜μ—¬ 쑰건문을 μ œκ±°ν•˜κ³ , 객체지ν–₯적인 λ°©μ‹μœΌλ‘œ μƒνƒœ μ „ν™˜μ„ κ΄€λ¦¬ν•œλ‹€. μƒνƒœ μ „ν™˜ λ‘œμ§μ„ λΆ„μ‚°μ‹œν‚€κ³  각 μƒνƒœκ°€ μžμ‹ λ§Œμ˜ 행동을 μ±…μž„μ§€κ²Œ ν•¨μœΌλ‘œμ¨ μ½”λ“œμ˜ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ„ ν–₯μƒμ‹œν‚¨λ‹€.

     

    βœ… μ–Έμ œ μ‚¬μš©ν•˜λ©΄ μ’‹μ„κΉŒ?

    • 객체의 행동이 μƒνƒœμ— 따라 크게 λ‹¬λΌμ§ˆ λ•Œ
    • 객체 μƒνƒœ 변화에 λ”°λ₯Έ 쑰건문(if-else, switch)이 λ³΅μž‘ν•΄μ§ˆ λ•Œ
    • μƒνƒœ μ „ν™˜ λ‘œμ§μ„ 더 λͺ…ν™•ν•˜κ²Œ ν‘œν˜„ν•˜κ³  싢을 λ•Œ
    • 객체 μ§€ν–₯적인 λ°©μ‹μœΌλ‘œ μƒνƒœ 관리λ₯Ό κ΅¬ν˜„ν•˜κ³  싢을 λ•Œ

    ❌ μ–Έμ œ ν”Όν•΄μ•Ό ν• κΉŒ?

    • κ°„λ‹¨ν•œ μƒνƒœ κ΄€λ¦¬λ§Œ ν•„μš”ν•œ 경우(μ˜€λ²„μ—”μ§€λ‹ˆμ–΄λ§ κ°€λŠ₯μ„±)
    • μƒνƒœμ™€ μ „ν™˜μ΄ 적고 변경이 거의 μ—†λŠ” 경우
    • μ„±λŠ₯이 μ€‘μš”ν•œ μƒν™©μ—μ„œ μƒνƒœ 객체 생성 μ˜€λ²„ν—€λ“œκ°€ λ¬Έμ œκ°€ 될 λ•Œ
    • λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ μ€‘μš”ν•œ μ œν•œλœ ν™˜κ²½μ—μ„œ

     

    πŸ’‘ μžλ°”/μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬μ—μ„œμ˜ μƒνƒœ νŒ¨ν„΄

    1. μŠ€ν”„λ§ μŠ€ν…Œμ΄νŠΈ λ¨Έμ‹ (Spring Statemachine)

    μŠ€ν”„λ§μ—μ„œλŠ” μƒνƒœ νŒ¨ν„΄μ„ κ΅¬ν˜„ν•˜κΈ° μœ„ν•œ μ „μš© ν”„λ ˆμž„μ›Œν¬μΈ Spring Statemachine을 μ œκ³΅ν•œλ‹€.

    import org.springframework.statemachine.StateMachine;
    import org.springframework.statemachine.config.StateMachineBuilder;
    import org.springframework.statemachine.config.StateMachineBuilder.Builder;
    
    public class OrderStateMachineExample {
        // μ£Όλ¬Έ μƒνƒœ μ—΄κ±°ν˜•
        public enum OrderStates {
            CREATED, PAID, PROCESSING, SHIPPED, DELIVERED, CANCELLED
        }
        
        // μ£Όλ¬Έ 이벀트 μ—΄κ±°ν˜•
        public enum OrderEvents {
            PAY, PROCESS, SHIP, DELIVER, CANCEL
        }
        
        public static void main(String[] args) throws Exception {
            // μƒνƒœ λ¨Έμ‹  λΉŒλ” 생성
            Builder<OrderStates, OrderEvents> builder = StateMachineBuilder.builder();
            
            // μƒνƒœ λ¨Έμ‹  ꡬ성
            builder.configureStates()
                .withStates()
                .initial(OrderStates.CREATED)
                .states(EnumSet.allOf(OrderStates.class));
            
            // μƒνƒœ μ „ν™˜ μ •μ˜
            builder.configureTransitions()
                .withExternal()
                    .source(OrderStates.CREATED).target(OrderStates.PAID)
                    .event(OrderEvents.PAY)
                    .and()
                .withExternal()
                    .source(OrderStates.PAID).target(OrderStates.PROCESSING)
                    .event(OrderEvents.PROCESS)
                    .and()
                .withExternal()
                    .source(OrderStates.PROCESSING).target(OrderStates.SHIPPED)
                    .event(OrderEvents.SHIP)
                    .and()
                .withExternal()
                    .source(OrderStates.SHIPPED).target(OrderStates.DELIVERED)
                    .event(OrderEvents.DELIVER)
                    .and()
                .withExternal()
                    .source(OrderStates.CREATED).target(OrderStates.CANCELLED)
                    .event(OrderEvents.CANCEL)
                    .and()
                .withExternal()
                    .source(OrderStates.PAID).target(OrderStates.CANCELLED)
                    .event(OrderEvents.CANCEL);
            
            // μƒνƒœ λ¨Έμ‹  생성
            StateMachine<OrderStates, OrderEvents> stateMachine = builder.build();
            stateMachine.start();
            
            // μƒνƒœ λ¨Έμ‹  μ‚¬μš©
            System.out.println("Current state: " + stateMachine.getState().getId());
            
            stateMachine.sendEvent(OrderEvents.PAY);
            System.out.println("Current state: " + stateMachine.getState().getId());
            
            stateMachine.sendEvent(OrderEvents.PROCESS);
            System.out.println("Current state: " + stateMachine.getState().getId());
            
            stateMachine.sendEvent(OrderEvents.SHIP);
            System.out.println("Current state: " + stateMachine.getState().getId());
            
            stateMachine.sendEvent(OrderEvents.DELIVER);
            System.out.println("Current state: " + stateMachine.getState().getId());
        }
    }
    

     

    2. JPA Entity μƒνƒœ 관리

    μ•„λž˜μ™€ 같이 JPA μ—”ν‹°ν‹°μ˜ μƒνƒœλ₯Ό 관리할 λ•Œλ„ μƒνƒœ νŒ¨ν„΄μ„ μ‘μš©ν•  수 μžˆλ‹€.

    import javax.persistence.*;
    import java.time.LocalDateTime;
    
    @Entity
    public class Order {
        @Id
        @GeneratedValue
        private Long id;
        
        @Enumerated(EnumType.STRING)
        private OrderStatus status;
        
        private LocalDateTime orderDate;
        private LocalDateTime paidDate;
        private LocalDateTime shippedDate;
        private LocalDateTime deliveredDate;
        private LocalDateTime cancelledDate;
        
        // μƒνƒœμ— λ”°λ₯Έ 행동을 μ²˜λ¦¬ν•˜λŠ” μƒνƒœ 객체
        @Transient
        private OrderState state;
        
        public Order() {
            this.orderDate = LocalDateTime.now();
            this.status = OrderStatus.CREATED;
            this.state = new CreatedOrderState();
        }
        
        // μƒνƒœ λ³€κ²½ λ©”μ„œλ“œ
        public void setState(OrderState state) {
            this.state = state;
            this.status = state.getStatus();
        }
        
        // μ£Όλ¬Έ 처리 행동
        public void pay() {
            state.pay(this);
        }
        
        public void ship() {
            state.ship(this);
        }
        
        public void deliver() {
            state.deliver(this);
        }
        
        public void cancel() {
            state.cancel(this);
        }
        
        // getter/setter
        // ...
    }
    
    // μƒνƒœ μΈν„°νŽ˜μ΄μŠ€
    interface OrderState {
        OrderStatus getStatus();
        void pay(Order order);
        void ship(Order order);
        void deliver(Order order);
        void cancel(Order order);
    }
    
    // μ£Όλ¬Έ 생성 μƒνƒœ
    class CreatedOrderState implements OrderState {
        @Override
        public OrderStatus getStatus() {
            return OrderStatus.CREATED;
        }
        
        @Override
        public void pay(Order order) {
            order.setPaidDate(LocalDateTime.now());
            order.setState(new PaidOrderState());
        }
        
        @Override
        public void ship(Order order) {
            throw new IllegalStateException("Created order cannot be shipped directly");
        }
        
        @Override
        public void deliver(Order order) {
            throw new IllegalStateException("Created order cannot be delivered directly");
        }
        
        @Override
        public void cancel(Order order) {
            order.setCancelledDate(LocalDateTime.now());
            order.setState(new CancelledOrderState());
        }
    }
    
    // μ§€λΆˆ μ™„λ£Œ μƒνƒœ
    class PaidOrderState implements OrderState {
        // κ΅¬ν˜„...
    }
    
    // μ£Όλ¬Έ μƒνƒœ μ—΄κ±°ν˜•
    enum OrderStatus {
        CREATED, PAID, SHIPPED, DELIVERED, CANCELLED
    }
    

     

    πŸ“š μ‹€μ œ μ‚¬μš© 사둀

    1. μ›Œν¬ν”Œλ‘œμš° 관리 μ‹œμŠ€ν…œ
      • λΉ„μ¦ˆλ‹ˆμŠ€ ν”„λ‘œμ„ΈμŠ€μ˜ λ‹€μ–‘ν•œ 단계와 μƒνƒœ μ „ν™˜μ„ 관리
      • 예: JIRA와 같은 이슈 νŠΈλž˜ν‚Ή μ‹œμŠ€ν…œμ˜ ν‹°μΌ“ μƒνƒœ 관리
    2. μ£Όλ¬Έ 처리 μ‹œμŠ€ν…œ
      • μ£Όλ¬Έ 생성, 결제, 처리, 배솑, μ™„λ£Œ λ“±μ˜ μƒνƒœ 관리
      • 각 μƒνƒœμ—μ„œ ν—ˆμš©λ˜λŠ” μž‘μ—…κ³Ό λ‹€μŒ μƒνƒœλ‘œμ˜ μ „ν™˜ μ œμ–΄
    3. κ²Œμž„ 캐릭터 AI
      • μΊλ¦­ν„°μ˜ μƒνƒœ(λŒ€κΈ°, 순찰, 좔격, 곡격 λ“±)에 λ”°λ₯Έ 행동 μ œμ–΄
      • 상황에 따라 μƒνƒœλ₯Ό λ™μ μœΌλ‘œ μ „ν™˜
    4. λ„€νŠΈμ›Œν¬ μ—°κ²° 관리
      • TCP μ—°κ²° μƒνƒœ(μ—°κ²° 쀑, 연결됨, μ’…λ£Œ 쀑 λ“±) 관리
      • μƒνƒœμ— λ”°λ₯Έ νŒ¨ν‚· 처리 둜직 κ΅¬ν˜„
    5. λ¬Έμ„œ 관리 μ‹œμŠ€ν…œ
      • λ¬Έμ„œμ˜ μƒνƒœ(μ΄ˆμ•ˆ, κ²€ν†  쀑, 승인됨, κ²Œμ‹œλ¨ λ“±) 관리
      • μƒνƒœμ— λ”°λ₯Έ μ ‘κ·Ό κΆŒν•œ 및 μž‘μ—… μ œμ–΄
    6. UI μƒνƒœ 관리
      • μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€ ꡬ성 μš”μ†Œμ˜ μƒνƒœ(ν™œμ„±ν™”, λΉ„ν™œμ„±ν™”, 선택됨 λ“±) 관리
      • React, Vue.js와 같은 ν”„λ‘ νŠΈμ—”λ“œ ν”„λ ˆμž„μ›Œν¬μ—μ„œμ˜ μƒνƒœ 관리
    7. μ „μž μƒκ±°λž˜ 결제 처리
      • 결제 ν”„λ‘œμ„ΈμŠ€μ˜ λ‹€μ–‘ν•œ μƒνƒœ(결제 μš”μ²­, 승인 λŒ€κΈ°, 승인됨, 거뢀됨 λ“±) 관리
      • μƒνƒœμ— λ”°λ₯Έ 결제 μ‹œμŠ€ν…œ λ™μž‘ μ œμ–΄

     

     

    πŸ“‹ μ°Έκ³