diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs
index f3815c7..e4b1dbf 100644
--- a/Assets/Script/Game/GameLogic.cs
+++ b/Assets/Script/Game/GameLogic.cs
@@ -1,302 +1,335 @@
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
-using UnityEngine.SceneManagement;
 using PimDeWitte.UnityMainThreadDispatcher;
 using Random = UnityEngine.Random;
 
-public abstract class BasePlayerState
+public partial class GameLogic : IDisposable
 {
-    public abstract void OnEnter(GameLogic gameLogic);
-    public abstract void OnExit(GameLogic gameLogic);
-    public abstract void HandleMove(GameLogic gameLogic, int row, int col);
-    public abstract void HandleNextTurn(GameLogic gameLogic);
-    
-    protected string _roomId;
-    protected bool _isMultiplay;
-    protected MultiplayManager _multiplayManager;
+    #region Fields
 
-    public void ProcessMove(GameLogic gameLogic, Enums.PlayerType playerType, int row, int col)
-    {
-        gameLogic.fioTimer.PauseTimer();
-        gameLogic.SetNewBoardValue(playerType, row, col);
-        gameLogic.CountStoneCounter();
-        if (_isMultiplay)
-        {
-            _multiplayManager.SendPlayerMove(_roomId, new Vector2Int(row, col));
-        }
-        
-        if (gameLogic.CheckGameWin(playerType, row, col))
-        {
-            var gameResult = playerType == Enums.PlayerType.PlayerA? Enums.GameResult.Win:Enums.GameResult.Lose;
-            if (gameLogic.gameType == Enums.GameType.MultiPlay)
-            {
-                if (gameLogic.firstPlayerState.GetType() != typeof(PlayerState))
-                {
-                    gameResult = gameResult == Enums.GameResult.Win ? Enums.GameResult.Lose : Enums.GameResult.Win;
-                }
-            }
-            GameManager.Instance.panelManager.OpenEffectPanel(gameResult);
-            gameLogic.EndGame(gameResult);
-        }
-        else
-        {
-            if (gameLogic.TotalStoneCounter >= Constants.MinCountForDrawCheck)
-            {
-                if (gameLogic.CheckGameDraw())
-                {
-                    GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Draw);
-                    gameLogic.EndGame(Enums.GameResult.Draw);
-                }
-                else
-                {
-                    HandleNextTurn(gameLogic);
-                }
-            }
-            else
-            {
-                HandleNextTurn(gameLogic);
-            }
-        }
-    }
-}
-
-public class PlayerState : BasePlayerState
-{
-    private Enums.PlayerType _playerType;
-    private bool _isFirstPlayer;
-    
-    public PlayerState(bool isFirstPlayer)
-    {
-        _isFirstPlayer = isFirstPlayer;
-        _playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB;
-        _isMultiplay = false;
-    }
-    
-    public PlayerState(bool isFirstPlayer, MultiplayManager multiplayManager, string roomId)
-        : this(isFirstPlayer)
-    {
-        _isFirstPlayer = isFirstPlayer;
-        _multiplayManager = multiplayManager;
-        _roomId = roomId;
-        _isMultiplay = true;
-    }
-    
-    public override void OnEnter(GameLogic gameLogic)
-    {
-        gameLogic.fioTimer.StartTimer();
-        
-        //TODO: 첫번째 플레이어면 렌주 룰 확인
-        #region Renju Turn Set
-        // 턴이 변경될 때마다 금수 위치 업데이트
-        gameLogic.UpdateForbiddenMoves();
-        #endregion
-        
-        gameLogic.currentTurn = _playerType;
-        gameLogic.stoneController.OnStoneClickedDelegate = (row, col) =>
-        {
-            HandleMove(gameLogic, row, col);
-        };
-    }
-    
-    public override void OnExit(GameLogic gameLogic)
-    {
-        //TODO: 렌주 룰 금수자리 초기화
-        
-        gameLogic.fioTimer.InitTimer();
-        gameLogic.stoneController.OnStoneClickedDelegate = null;
-    }
-
-    public override void HandleMove(GameLogic gameLogic, int row, int col)
-    {
-        gameLogic.SetStoneSelectedState(row, col);
-    }
-
-    public override void HandleNextTurn(GameLogic gameLogic)
-    {
-        if (_isFirstPlayer)
-        {
-            gameLogic.SetState(gameLogic.secondPlayerState);
-        }
-        else
-        {
-            gameLogic.SetState(gameLogic.firstPlayerState);
-        }
-    }
-}
-
-public class AIState: BasePlayerState
-{
-    public override void OnEnter(GameLogic gameLogic)
-    {
-        gameLogic.fioTimer.StartTimer();
-        OmokAI.Instance.StartBestMoveSearch(gameLogic.GetBoard(), (bestMove) =>
-        {
-            if(bestMove.HasValue)
-                HandleMove(gameLogic, bestMove.Value.Item1, bestMove.Value.Item2);
-        });
-    }
-
-    public override void OnExit(GameLogic gameLogic)
-    {
-        gameLogic.fioTimer.InitTimer();
-    }
-
-    public override void HandleMove(GameLogic gameLogic, int row, int col)
-    {
-        ProcessMove(gameLogic, Enums.PlayerType.PlayerB,row, col);
-    }
-
-    public override void HandleNextTurn(GameLogic gameLogic)
-    {
-        gameLogic.SetState(gameLogic.firstPlayerState);
-    }
-}
-public class MultiPlayerState: BasePlayerState
-{
-    private Enums.PlayerType _playerType;
-    private bool _isFirstPlayer;
-    
-    private MultiplayManager _multiplayManager;
-    
-    public MultiPlayerState(bool isFirstPlayer, MultiplayManager multiplayManager)
-    {
-        _isFirstPlayer = isFirstPlayer;
-        _playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB;
-        _multiplayManager = multiplayManager;
-    }
-    
-    public override void OnEnter(GameLogic gameLogic)
-    {
-        gameLogic.fioTimer.StartTimer();
-        //TODO: 첫번째 플레이어면 렌주 룰 확인
-        #region Renju Turn Set
-        // 턴이 변경될 때마다 금수 위치 업데이트
-        gameLogic.UpdateForbiddenMoves();
-        #endregion
-        
-        // gameLogic.currentTurn = _playerType;
-        // gameLogic.stoneController.OnStoneClickedDelegate = (row, col) =>
-        // {
-        //     HandleMove(gameLogic, row, col);
-        // };
-        _multiplayManager.OnOpponentMove = moveData =>
-        {
-            var row = moveData.position.x;
-            var col = moveData.position.y;
-            UnityThread.executeInUpdate(() =>
-            {
-                HandleMove(gameLogic, row, col);                
-            });
-        };
-    }
-
-    public override void OnExit(GameLogic gameLogic)
-    {
-        gameLogic.fioTimer.InitTimer();
-        _multiplayManager.OnOpponentMove = null;
-    }
-
-    public override void HandleMove(GameLogic gameLogic, int row, int col)
-    {
-        ProcessMove(gameLogic, _playerType, row, col);
-    }
-
-    public override void HandleNextTurn(GameLogic gameLogic)
-    {
-        if (_isFirstPlayer)
-        {
-            gameLogic.SetState(gameLogic.secondPlayerState);
-        }
-        else
-        {
-            gameLogic.SetState(gameLogic.firstPlayerState);
-        }
-    }
-}
-
-public class GameLogic : IDisposable
-{
     private Enums.PlayerType[,] _board;
-    public StoneController stoneController;
-    public Enums.PlayerType currentTurn;
-    public Enums.GameType gameType;
-    //총 착수된 돌 카운터
-    public int _totalStoneCounter;
-    public int TotalStoneCounter{get{return _totalStoneCounter;}}
-    //무승부 요청 가능 여부
-    private bool _requestDrawChance;
-    public bool RequestDrawChance{
-        get { return _requestDrawChance;}
-        set { _requestDrawChance = value;}
-    }
-    
-    public BasePlayerState firstPlayerState;
-    public BasePlayerState secondPlayerState;
-    private BasePlayerState _currentPlayerState;
-
-    //타이머
-    public FioTimer fioTimer;
-    
-    //선택된 좌표
-    public int selectedRow;
-    public int selectedCol;
-    //마지막 배치된 좌표
-    private int _lastRow;
-    private int _lastCol;
-    
-    // 멀티 플레이 관련
-    public MultiplayManager _multiplayManager;
+    private int _totalStoneCounter;                         // 총 착수된 돌 카운터
+    private int _lastRow, _lastCol;                         // 마지막 배치된 좌표
+    private RenjuForbiddenMoveDetector _forbiddenDetector;  // 렌주룰 금수 검사기
+    private List<Vector2Int> _forbiddenMoves = new ();      // 현재 금수 위치 목록
     private string _roomId;
-    private string opponentNickname;
-    private int opponentImageIndex;
-    
-    
-#region Renju Members
-    // 렌주룰 금수 검사기
-    private RenjuForbiddenMoveDetector _forbiddenDetector;
 
-    // 현재 금수 위치 목록
-    private List<Vector2Int> _forbiddenMoves = new List<Vector2Int>();
-#endregion
+    #endregion
+
+    #region Properties
+
+    public int TotalStoneCounter => _totalStoneCounter;
+    public bool RequestDrawChance { get; set; }             // 무승부 요청 가능 여부
+    public MultiplayManager MultiPlayManager { get; private set; }
+    public Enums.PlayerType CurrentTurn { get; set; }
+    public Enums.GameType GameType { get; set; }
+    public StoneController StoneController { get; set; }
+    public BasePlayerState CurrentPlayerState { get; private set; }
+    public BasePlayerState FirstPlayerState { get; private set; }
+    public BasePlayerState SecondPlayerState { get; private set; }
+    public int SelectedRow { get; private set; }
+    public int SelectedCol { get; private set; }
+    public FioTimer FioTimer { get; private set; }
+
+    #endregion
+
+    #region Constructor and Initialization
 
     public GameLogic(StoneController stoneController, Enums.GameType gameType, FioTimer fioTimer = null)
     {
-        //보드 초기화
+        _forbiddenDetector = new RenjuForbiddenMoveDetector();  // 금수 감지기 초기화
+        InitializeBoard(stoneController, gameType);             // 보드 초기화
+        InitializeFioTimer(fioTimer);                           // timer 초기화
+        GameModeSetter(gameType);                               // 게임 모드 설정
+    }
+
+    // 게임 모드 분기 처리
+    private void GameModeSetter(Enums.GameType gameType)
+    {
+        switch (gameType)
+        {
+            case Enums.GameType.MultiPlay:
+                InitializeMultiplayerMode();
+                break;
+            case Enums.GameType.Replay:
+                //TODO: 리플레이 구현
+                break;
+            // 현재 싱글 플레이로 바로 넘어가지 않기 때문에 미사용
+            // case Enums.GameType.SinglePlay:
+            //     InitializeSinglePlayMode();
+            //     break;
+        }
+    }
+
+    private void InitializeMultiplayerMode()
+    {
+        // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요
+        ExecuteOnMainThread(() =>
+        {
+            GameManager.Instance.panelManager.OpenLoadingPanel(true, true);
+        });
+
+        MultiPlayManager = new MultiplayManager((state, data) =>
+        {
+            Debug.Log($"## {state}");
+
+            switch (state)
+            {
+                case Constants.MultiplayManagerState.CreateRoom:
+                    Debug.Log("## Create Room");
+                    _roomId = data as string;
+                    break;
+                case Constants.MultiplayManagerState.JoinRoom:
+                    Debug.Log("## Join Room");
+                    var joinRoomData = data as JoinRoomData;
+
+                    // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요
+                    if (!ValidateRoomData(joinRoomData, "Join Room")) return;
+
+                    // 플레이어 셋업
+                    SetupPlayer(joinRoomData.isBlack, joinRoomData.roomId, joinRoomData.opponentNickname, joinRoomData.opponentImageIndex);
+
+                    // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요
+                    StartGameOnMainThread();
+                    break;
+                case Constants.MultiplayManagerState.SwitchAI:
+                    Debug.Log("## Switching to AI Mode");
+                    SwitchToSinglePlayer();
+                    break;
+                case Constants.MultiplayManagerState.StartGame:
+                    Debug.Log("## Start Game");
+                    var startGameData = data as StartGameData;
+
+                    // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요
+                    if (!ValidateRoomData(startGameData, "Start Game")) return;
+
+                    // 플레이어 셋업
+                    SetupPlayer(startGameData.isBlack, _roomId, startGameData.opponentNickname, startGameData.opponentImageIndex);
+
+                    // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요
+                    StartGameOnMainThread();
+                    break;
+                case Constants.MultiplayManagerState.ExitRoom:
+                    Debug.Log("## Exit Room");
+                    // TODO: Exit Room 처리
+                    break;
+                case Constants.MultiplayManagerState.EndGame:
+                    Debug.Log("## End Game");
+                    // TODO: End Room 처리
+                    break;
+                case Constants.MultiplayManagerState.DoSurrender:
+                    Debug.Log("상대방의 항복 요청 들어옴");
+                    ExecuteOnMainThread(() =>
+                    {
+                        GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Win);
+                        EndGame(Enums.GameResult.Win);
+                    });
+                    break;
+                case Constants.MultiplayManagerState.SurrenderConfirmed:
+                    Debug.Log("항복 요청 전송 완료");
+                    ExecuteOnMainThread(() =>
+                    {
+                        GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Lose);
+                        EndGame(Enums.GameResult.Lose);
+                    });
+                    break;
+                case Constants.MultiplayManagerState.ReceiveDrawRequest:
+                    Debug.Log("상대방의 무승부 요청 들어옴");
+                    ExecuteOnMainThread(() =>
+                    {
+                        GameManager.Instance.panelManager.OpenDrawConfirmPanel("무승부 요청을 승락하시겠습니까?", () =>
+                        {
+                            GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Draw);
+                            EndGame(Enums.GameResult.Draw);
+                            MultiPlayManager.AcceptDraw();
+                        }, () =>
+                        {
+                            MultiPlayManager.RejectDraw();
+                        });
+                    });
+                    break;
+                case Constants.MultiplayManagerState.DrawRequestSent:
+                    Debug.Log("무승부 요청 전송 완료");
+                    break;
+                case Constants.MultiplayManagerState.DrawAccepted:
+                    Debug.Log("무승부 요청이 승락이 들어옴");
+                    ExecuteOnMainThread(() =>
+                    {
+                        GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Draw);
+                        EndGame(Enums.GameResult.Draw);
+                    });
+                    break;
+                case Constants.MultiplayManagerState.DrawConfirmed:
+                    Debug.Log("무승부 요청 승락 완료");
+                    break;
+                case Constants.MultiplayManagerState.DrawRejected:
+                    Debug.Log("무승부 요청이 거부가 들어옴");
+                    ExecuteOnMainThread(() =>
+                    {
+                        GameManager.Instance.panelManager.OpenConfirmPanel("무승부 요청을 거부하였습니다.", () => { });
+                    });
+                    break;
+                case Constants.MultiplayManagerState.DrawRejectionConfirmed:
+                    Debug.Log("무승부 요청 거부 완료");
+
+                    break;
+                case Constants.MultiplayManagerState.ReceiveTimeout:
+                    Debug.Log("상대방이 타임 아웃 됨");
+                    ExecuteOnMainThread(() =>
+                    {
+                        GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Win);
+                        EndGame(Enums.GameResult.Win);
+                    });
+                    break;
+            }
+            ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,"nicknameB");
+        });
+
+        MultiPlayManager.RegisterPlayer(UserManager.Instance.Nickname, UserManager.Instance.Rating, UserManager.Instance.imageIndex);
+    }
+
+    private void SetupPlayer(bool isBlack, string roomId, string opponentNickname, int opponentImageIndex)
+    {
+        // 선공, 후공 처리
+        var isFirstPlayer = isBlack;
+
+        if (isFirstPlayer)
+        {
+            Debug.Log("해당 플레이어가 선공 입니다");
+            FirstPlayerState = new PlayerState(true, MultiPlayManager, roomId);
+            SecondPlayerState = new MultiPlayerState(false, MultiPlayManager);
+
+            UpdateUIForFirstPlayer(opponentNickname, opponentImageIndex);
+        }
+        else
+        {
+            Debug.Log("해당 플레이어가 후공 입니다");
+            FirstPlayerState = new MultiPlayerState(true, MultiPlayManager);
+            SecondPlayerState = new PlayerState(false, MultiPlayManager, roomId);
+
+            UpdateUIForSecondPlayer(opponentNickname, opponentImageIndex);
+        }
+    }
+
+    private void UpdateUIForFirstPlayer(string opponentNickname, int opponentImageIndex)
+    {
+        ExecuteOnMainThread(() =>
+        {
+            GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, opponentNickname);
+            GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, opponentImageIndex);
+
+            // 리플레이 데이터 업데이트
+            ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname, opponentNickname, UserManager.Instance.imageIndex, opponentImageIndex);
+        });
+    }
+
+    private void UpdateUIForSecondPlayer(string opponentNickname, int opponentImageIndex)
+    {
+        ExecuteOnMainThread(() =>
+        {
+            GameManager.Instance.InitPlayersName(opponentNickname, UserManager.Instance.Nickname);
+            GameManager.Instance.InitProfileImages(opponentImageIndex, UserManager.Instance.imageIndex);
+
+            // 리플레이 데이터 업데이트
+            ReplayManager.Instance.InitReplayData(opponentNickname, UserManager.Instance.Nickname, opponentImageIndex, UserManager.Instance.imageIndex);
+        });
+    }
+
+
+    // 메인스레드에서 게임 시작
+    private void StartGameOnMainThread()
+    {
+        ExecuteOnMainThread(() =>
+        {
+            // 로딩 패널 열려있으면 닫기
+            GameManager.Instance.panelManager.CloseLoadingPanel();
+
+            // 게임 시작
+            SetState(FirstPlayerState);
+        });
+    }
+
+    // 방 데이터 유효성 검사 헬퍼 함수
+    private bool ValidateRoomData(object roomData, string operationName)
+    {
+        if (roomData == null)
+        {
+            Debug.Log($"{operationName} 응답값이 null 입니다");
+            return false;
+        }
+        return true;
+    }
+
+    // 메인 스레드에서 실행하는 헬퍼 함수
+    private void ExecuteOnMainThread(Action action)
+    {
+        UnityMainThreadDispatcher.Instance().Enqueue(action);
+    }
+
+    private void InitializeSinglePlayMode()
+    {
+        FirstPlayerState = new PlayerState(true);
+        SecondPlayerState = new AIState();
+        // AI 난이도 설정(급수 설정)
+        OmokAI.Instance.SetRating(UserManager.Instance.Rating);
+
+        // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요
+        UnityMainThreadDispatcher.Instance().Enqueue(() =>
+        {
+            // 스레드 확인 로그: 추후 디버깅 시 필요할 수 있을 것 같아 남겨둡니다
+            // Debug.Log($"[UnityMainThreadDispatcher] 실행 스레드: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
+
+            //AI닉네임 랜덤생성
+            var aiName = RandomAINickname();
+            var imageIndex = Random.Range(0, 2);
+
+            //유저 이름 사진 초기화
+            GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName);
+            GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex);
+            // 리플레이 데이터 업데이트
+            ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,aiName, UserManager.Instance.imageIndex, imageIndex);
+
+            // 로딩 패널 열려있으면 닫기
+            GameManager.Instance.panelManager.CloseLoadingPanel();
+
+            // 첫 번째 플레이어(유저)부터 시작
+            SetState(FirstPlayerState);
+        });
+    }
+
+    private void InitializeBoard(StoneController stoneController, Enums.GameType gameType)
+    {
         _board = new Enums.PlayerType[15, 15];
-        this.stoneController = stoneController;
-        this.gameType = gameType;
+        StoneController = stoneController;
+        GameType = gameType;
         _totalStoneCounter = 0;
         RequestDrawChance = true;
-        
-        selectedRow = -1;
-        selectedCol = -1;
-
-#region Renju Init
-        // 금수 감지기 초기화
-        _forbiddenDetector = new RenjuForbiddenMoveDetector();
-#endregion
 
+        SelectedRow = -1;
+        SelectedCol = -1;
         _lastRow = -1;
         _lastCol = -1;
-        //timer 초기화
+    }
+
+    private void InitializeFioTimer(FioTimer fioTimer)
+    {
         if (fioTimer != null)
         {
-            this.fioTimer = fioTimer;
-            this.fioTimer.InitTimer();
+            FioTimer = fioTimer;
+            FioTimer.InitTimer();
             //timer 시간초과시 진행 함수
-            this.fioTimer.OnTimeout = () =>
+            FioTimer.OnTimeout = () =>
             {
                 // 현재 턴의 플레이어가 로컬(유저)인지 확인
-                bool isCurrentPlayerLocal = (currentTurn == Enums.PlayerType.PlayerA && firstPlayerState is PlayerState) ||
-                                            (currentTurn == Enums.PlayerType.PlayerB && secondPlayerState is PlayerState);
+                bool isCurrentPlayerLocal = (CurrentTurn == Enums.PlayerType.PlayerA && FirstPlayerState is PlayerState) ||
+                                            (CurrentTurn == Enums.PlayerType.PlayerB && SecondPlayerState is PlayerState);
 
                 if (isCurrentPlayerLocal) // 내가 타임 오버일 때
                 {
-                    if (this.gameType == Enums.GameType.MultiPlay) // 멀티플레이인 경우
+                    if (this.GameType == Enums.GameType.MultiPlay) // 멀티플레이인 경우
                     {
-                        _multiplayManager?.SendTimeout();
+                        MultiPlayManager?.SendTimeout();
                     }
                     GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Lose);
                     EndGame(Enums.GameResult.Lose);
@@ -309,631 +342,45 @@ public class GameLogic : IDisposable
                 }
             };
         }
-        
-        switch (gameType)
-        {
-            // TODO: 현재 싱글 플레이로 바로 넘어가지 않기 때문에 미사용 중
-            // case Enums.GameType.SinglePlay:
-            //     firstPlayerState = new PlayerState(true);
-            //     secondPlayerState = new AIState();
-            //     // AI 난이도 설정(급수 설정)
-            //     OmokAI.Instance.SetRating(UserManager.Instance.Rating);
-            //     
-            //     //AI닉네임 랜덤생성
-            //     var aiName = RandomAINickname();
-            //     var imageIndex = UnityEngine.Random.Range(0, 2);
-            //     
-            //     //유저 이름 사진 초기화
-            //     GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName);
-            //     GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex);
-            //     
-            //     ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,aiName, UserManager.Instance.imageIndex, imageIndex);
-            //     
-            //     SetState(firstPlayerState);
-            //     break;
-            case Enums.GameType.MultiPlay:
-                // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요
-                bool isFirstPlayer;
-                UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                {
-                    GameManager.Instance.panelManager.OpenLoadingPanel(true, true);
-                });
-                _multiplayManager = new MultiplayManager((state, data) =>
-                {
-                switch (state)
-                {
-                    case Constants.MultiplayManagerState.CreateRoom:
-                        Debug.Log("## Create Room");
-                        _roomId = data as string;
-                        break;
-                    case Constants.MultiplayManagerState.JoinRoom:
-                        Debug.Log("## Join Room");
-                        var joinRoomData = data as JoinRoomData;
-                        
-                        // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요
-                        if (joinRoomData == null)
-                        {
-                            Debug.Log("Join Room 응답값이 null 입니다");
-                            return;
-                        }
-                        _roomId = joinRoomData.roomId;
-
-                        // 선공, 후공 처리
-                        isFirstPlayer = joinRoomData.isBlack;
-
-                        opponentNickname = joinRoomData.opponentNickname;
-                        opponentImageIndex = joinRoomData.opponentImageIndex;
-                        if (isFirstPlayer)
-                        {
-                            Debug.Log("해당 플레이어가 선공 입니다");
-                            firstPlayerState = new PlayerState(true, _multiplayManager, _roomId);
-                            secondPlayerState = new MultiPlayerState(false, _multiplayManager);
-                            UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                            {
-                                GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, joinRoomData.opponentNickname);
-                                GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, joinRoomData.opponentImageIndex);
-                                
-                                // 리플레이 데이터 업데이트
-                                ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname, joinRoomData.opponentNickname, UserManager.Instance.imageIndex, joinRoomData.opponentImageIndex);
-                            });
-                        }
-                        else
-                        {
-                            Debug.Log("해당 플레이어가 후공 입니다");
-                            firstPlayerState = new MultiPlayerState(true, _multiplayManager);
-                            secondPlayerState = new PlayerState(false, _multiplayManager, _roomId);
-                            
-                            UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                            {
-                                GameManager.Instance.InitPlayersName(joinRoomData.opponentNickname, UserManager.Instance.Nickname);
-                                GameManager.Instance.InitProfileImages(joinRoomData.opponentImageIndex, UserManager.Instance.imageIndex);
-                                
-                                // 리플레이 데이터 업데이트
-                                ReplayManager.Instance.InitReplayData(joinRoomData.opponentNickname, UserManager.Instance.Nickname, joinRoomData.opponentImageIndex, UserManager.Instance.imageIndex);
-                            });
-                        }
-                        
-                        // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            // 로딩 패널 열려있으면 닫기
-                            GameManager.Instance.panelManager.CloseLoadingPanel();
-                            
-                            // 게임 시작
-                            SetState(firstPlayerState);
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.SwitchAI:
-                        Debug.Log("## Switching to AI Mode");
-                        SwitchToSinglePlayer();
-                        break;
-                    case Constants.MultiplayManagerState.StartGame:
-                        Debug.Log("## Start Game");
-                        var startGameData = data as StartGameData;
-                        
-                        // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요
-                        if (startGameData == null)
-                        {
-                            Debug.Log("Start Game 응답값이 null 입니다");
-                            return;
-                        }
-                        // 선공, 후공 처리
-                        isFirstPlayer = startGameData.isBlack;
-                        opponentNickname = startGameData.opponentNickname;
-                        opponentImageIndex = startGameData.opponentImageIndex;
-                        if (isFirstPlayer)
-                        {
-                            Debug.Log("해당 플레이어가 선공 입니다");
-                            firstPlayerState = new PlayerState(true, _multiplayManager, _roomId);
-                            secondPlayerState = new MultiPlayerState(false, _multiplayManager);
-                            UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                            {
-                                GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, startGameData.opponentNickname);
-                                GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, startGameData.opponentImageIndex);
-                                
-                                // 리플레이 데이터 업데이트
-                                ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname, startGameData.opponentNickname, UserManager.Instance.imageIndex, startGameData.opponentImageIndex);
-                            });
-                        }
-                        else
-                        {
-                            Debug.Log("해당 플레이어가 후공 입니다");
-                            firstPlayerState = new MultiPlayerState(true, _multiplayManager);
-                            secondPlayerState = new PlayerState(false, _multiplayManager, _roomId);
-                            UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                            {
-                                GameManager.Instance.InitPlayersName(startGameData.opponentNickname, UserManager.Instance.Nickname);
-                                GameManager.Instance.InitProfileImages(startGameData.opponentImageIndex, UserManager.Instance.imageIndex);
-                                
-                                // 리플레이 데이터 업데이트
-                                ReplayManager.Instance.InitReplayData(startGameData.opponentNickname, UserManager.Instance.Nickname, startGameData.opponentImageIndex, UserManager.Instance.imageIndex);
-                            });
-                        }
-                        
-                        // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            // 로딩 패널 열려있으면 닫기
-                            GameManager.Instance.panelManager.CloseLoadingPanel();
-                            
-                            // 게임 시작
-                            SetState(firstPlayerState);
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.ExitRoom:
-                        Debug.Log("## Exit Room");
-                        // TODO: Exit Room 처리
-                        break;
-                    case Constants.MultiplayManagerState.EndGame:
-                        Debug.Log("## End Game");
-                        // TODO: End Room 처리
-                        break;
-                    case Constants.MultiplayManagerState.DoSurrender:
-                        Debug.Log("상대방의 항복 요청 들어옴");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Win);
-                            EndGame(Enums.GameResult.Win);
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.SurrenderConfirmed:
-                        Debug.Log("항복 요청 전송 완료");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Lose);
-                            EndGame(Enums.GameResult.Lose);
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.ReceiveDrawRequest:
-                        Debug.Log("상대방의 무승부 요청 들어옴");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenDrawConfirmPanel("무승부 요청을 승낙하시겠습니까?", () =>
-                            {
-                                GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Draw);
-                                EndGame(Enums.GameResult.Draw);
-                                _multiplayManager.AcceptDraw();
-                            }, () =>
-                            {
-                                _multiplayManager.RejectDraw();
-                            });
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.DrawRequestSent:
-                        Debug.Log("무승부 요청 전송 완료");
-                        break;
-                    case Constants.MultiplayManagerState.DrawAccepted:
-                        Debug.Log("무승부 요청이 승낙이 들어옴");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Draw);
-                            EndGame(Enums.GameResult.Draw);
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.DrawConfirmed:
-                        Debug.Log("무승부 요청 승낙 완료");
-                        break;
-                    case Constants.MultiplayManagerState.DrawRejected:
-                        Debug.Log("무승부 요청이 거부가 들어옴");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenConfirmPanel("무승부 요청을 거부하였습니다.", () => { });
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.DrawRejectionConfirmed:
-                        Debug.Log("무승부 요청 거부 완료");
-                        
-                        break;
-                    case Constants.MultiplayManagerState.ReceiveTimeout:
-                        Debug.Log("상대방이 타임 아웃 됨");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Win);
-                            EndGame(Enums.GameResult.Win);
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.RevengeRequestSent:
-                        Debug.Log("재대결 요청: 전송 완료");
-                        break;
-                    case Constants.MultiplayManagerState.ReceiveRevengeRequest:
-                        Debug.Log("상대방의 재대결 요청이 들어옴");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenDrawConfirmPanel("상대방의 재대결 요청을\n승낙하시겠습니까?", () =>
-                            {
-                                _multiplayManager.AcceptRevenge();
-                            }, () =>
-                            {
-                                _multiplayManager.RejectRevenge();
-                            });
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.RevengeAccepted:
-                        Debug.Log("재대결 요청: 승낙이 들어옴");
-                        var revengeAcceptedData = data as RevengeData;
-                        
-                        // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요
-                        if (revengeAcceptedData == null)
-                        {
-                            Debug.Log("RevengeAccepted 응답값이 null 입니다");
-                            return;
-                        }
-                        
-                        // 선공, 후공 처리
-                        isFirstPlayer = revengeAcceptedData.isBlack;
-
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenConfirmPanel("상대방이\n재대결을 승낙하였습니다.\n게임이 다시 시작됩니다.", () =>
-                            {
-                                InitBoardForRevenge(isFirstPlayer);
-                            });
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.RevengeConfirmed:
-                        Debug.Log("재대결 요청: 승낙 완료");
-                        var revengConfirmedData = data as RevengeData;
-                        
-                        // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요
-                        if (revengConfirmedData == null)
-                        {
-                            Debug.Log("RevengeConfirmed 응답값이 null 입니다");
-                            return;
-                        }
-                        
-                        // 선공, 후공 처리
-                        isFirstPlayer = revengConfirmedData.isBlack;
-                        
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenConfirmPanel("재대결 요청을\n승낙하였습니다.\n게임이 다시 시작됩니다.", () =>
-                            {
-                                InitBoardForRevenge(isFirstPlayer);
-                            });
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.RevengeRejected:
-                        Debug.Log("재대결 요청: 거부가 들어옴");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenConfirmPanel("상대방이\n재대결 요청을\n거부하였습니다.", () =>
-                            {
-                                GameManager.Instance.panelManager.CloseLoadingPanel();
-                            });
-                        });
-                        break;
-                    case Constants.MultiplayManagerState.RevengeRejectionConfirmed:
-                        Debug.Log("재대결 요청: 거부 완료");
-                        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-                        {
-                            GameManager.Instance.panelManager.OpenConfirmPanel("재대결 요청을\n거부하였습니다.", () =>
-                            {
-                                GameManager.Instance.panelManager.CloseLoadingPanel();
-                            });
-                        });
-                        break;
-                }
-                ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,opponentNickname);
-                
-                });
-                _multiplayManager.RegisterPlayer(UserManager.Instance.Nickname, UserManager.Instance.Rating, UserManager.Instance.imageIndex);
-                break;
-            case Enums.GameType.Replay:
-                //TODO: 리플레이 구현
-                break;
-        }
     }
 
-    private void InitBoardForRevenge(bool isFirstPlayer)
+    #endregion
+
+    public Enums.PlayerType[,] GetBoard() => _board;
+
+    // 보드 초기화
+    public void ResetBoard() => Array.Clear(_board, 0, _board.Length);
+
+    // 상대가 매칭되지 않을 경우 AI로 전환하는 함수
+    private void SwitchToSinglePlayer()
     {
-        //보드 초기화
-        _board = new Enums.PlayerType[15, 15];
-        _totalStoneCounter = 0;
-        stoneController.InitStones();
-        RequestDrawChance = false;
-
-        selectedRow = -1;
-        selectedCol = -1;
-
-        // 금수 감지기 초기화
-        _forbiddenDetector.RenjuForbiddenMove(_board);
-
-        _lastRow = -1;
-        _lastCol = -1;
-        
-        //timer 초기화
-        fioTimer.InitTimer();
-        
-        if (isFirstPlayer)
-        {
-            Debug.Log("해당 플레이어가 선공 입니다");
-            firstPlayerState = new PlayerState(true, _multiplayManager, _roomId);
-            secondPlayerState = new MultiPlayerState(false, _multiplayManager);
-            GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, opponentNickname);
-            GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, opponentImageIndex);
-            
-            // 리플레이 데이터 업데이트
-            ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname, opponentNickname, UserManager.Instance.imageIndex, opponentImageIndex);
-        }
-        else
-        {
-            Debug.Log("해당 플레이어가 후공 입니다");
-            firstPlayerState = new MultiPlayerState(true, _multiplayManager);
-            secondPlayerState = new PlayerState(false, _multiplayManager, _roomId);
-            GameManager.Instance.InitPlayersName(opponentNickname, UserManager.Instance.Nickname);
-            GameManager.Instance.InitProfileImages(opponentImageIndex, UserManager.Instance.imageIndex);
-            
-            // 리플레이 데이터 업데이트
-            ReplayManager.Instance.InitReplayData(opponentNickname, UserManager.Instance.Nickname, opponentImageIndex, UserManager.Instance.imageIndex);
-        }
-        
-        GameManager.Instance.panelManager.CloseLoadingPanel();
-        
-        // 첫 번째 플레이어(유저)부터 시작
-        SetState(firstPlayerState);
-    }
-
-    //AI닉네임 랜덤 생성
-    private string RandomAINickname()
-    {
-        string[] AI_NAMIES = { "이세돌",  "신사동호랭이","진짜인간임","종로3가짱돌","마스터김춘배","62세황순자","고준일 강사님"};
-        
-        var index = UnityEngine.Random.Range(0, AI_NAMIES.Length);
-        
-        return AI_NAMIES[index];
-    }
-    
-    public void SwitchToSinglePlayer()
-    {
-        _multiplayManager?.Dispose();
+        MultiPlayManager?.Dispose();
         
         // 기존 멀티플레이 상태 초기화
-        _multiplayManager = null;
+        MultiPlayManager = null;
         _roomId = null;
 
         // 싱글 플레이 상태로 변경
-        firstPlayerState = new PlayerState(true);
-        secondPlayerState = new AIState();
-        // AI 난이도 설정(급수 설정)
-        OmokAI.Instance.SetRating(UserManager.Instance.Rating);
-
-        // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요
-        UnityMainThreadDispatcher.Instance().Enqueue(() =>
-        {
-            // 스레드 확인 로그: 추후 디버깅 시 필요할 수 있을 것 같아 남겨둡니다
-            // Debug.Log($"[UnityMainThreadDispatcher] 실행 스레드: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
-            
-            //AI닉네임 랜덤생성
-            var aiName = RandomAINickname();
-            var imageIndex = UnityEngine.Random.Range(0, 2);
-            
-            //유저 이름 사진 초기화
-            GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName);
-            GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex);
-            // 리플레이 데이터 업데이트  
-            ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,aiName, UserManager.Instance.imageIndex, imageIndex);
-
-            // 로딩 패널 열려있으면 닫기
-            GameManager.Instance.panelManager.CloseLoadingPanel();
-            
-            // 첫 번째 플레이어(유저)부터 시작
-            SetState(firstPlayerState);
-        });
-    }
-    
-    public void Dispose()
-    {
-        _multiplayManager?.LeaveRoom(_roomId);
-        _multiplayManager?.Dispose();
-    }
-    
-    //돌 카운터 증가 함수
-    public void CountStoneCounter()
-    {
-        _totalStoneCounter++;
-    }
-    
-    //착수 버튼 클릭시 호출되는 함수
-    public void OnConfirm()
-    {
-        _currentPlayerState.ProcessMove(this, currentTurn, selectedRow, selectedCol);
-    }
-    //보드 초기화
-    public void ResetBoard()
-    {
-        Array.Clear(_board, 0, _board.Length);
+        InitializeSinglePlayMode();
     }
 
     public void SetState(BasePlayerState state)
     {
-        _currentPlayerState?.OnExit(this);
-        _currentPlayerState = state;
-        _currentPlayerState?.OnEnter(this);
-        //턴 표시
-        GameManager.Instance.SetTurnIndicator(_currentPlayerState == firstPlayerState);
-    }
-    
-    //스톤의 상태변경 명령함수
-    public void SetStoneNewState(Enums.StoneState state, int row, int col)
-    {
-        stoneController.SetStoneState(state, row, col);
+        CurrentPlayerState?.OnExit(this);
+        CurrentPlayerState = state;
+        CurrentPlayerState?.OnEnter(this);
+        // 턴 표시
+        GameManager.Instance.SetTurnIndicator(CurrentPlayerState == FirstPlayerState);
     }
 
-    public void SetStoneSelectedState(int row, int col)
-    {
-        if (_board[row, col] != Enums.PlayerType.None) return;
-        
-        if (stoneController.GetStoneState(row, col) != Enums.StoneState.None && currentTurn == Enums.PlayerType.PlayerA) return;
-        //첫수 및 중복 확인
-        if ((selectedRow != row || selectedCol != col) && (selectedRow != -1 && selectedCol != -1))
-        {
-            stoneController.SetStoneState(Enums.StoneState.None,selectedRow, selectedCol);
-        }
-        selectedRow = row;
-        selectedCol = col;
-        stoneController.SetStoneState(Enums.StoneState.Selected, row, col);
-    }
-    
-    //보드에 돌추가 함수
-    public void SetNewBoardValue(Enums.PlayerType playerType, int row, int col)
-    {
-        if (_board[row, col] != Enums.PlayerType.None) return;
+    #region Utility
 
-        switch (playerType)
-        {
-            case Enums.PlayerType.PlayerA:
-                stoneController.SetStoneType(Enums.StoneType.Black, row, col);
-                stoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col);
-                _board[row, col] = Enums.PlayerType.PlayerA;
-                LastNSelectedSetting(row, col);
-                
-                ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.Black, row, col);      //기보 데이터 저장
-                break;
-            case Enums.PlayerType.PlayerB:
-                stoneController.SetStoneType(Enums.StoneType.White, row, col);
-                stoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col);
-                _board[row, col] = Enums.PlayerType.PlayerB;
-                LastNSelectedSetting(row, col);
-                
-                ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.White, row, col);
-                
-                break;
-        }
-    }
-
-    //돌 지우는 함수
-    public void RemoveStone(int row, int col)
-    {
-        _board[row, col] = Enums.PlayerType.None;
-        stoneController.SetStoneType(Enums.StoneType.None, row, col);
-        stoneController.SetStoneState(Enums.StoneState.None, row, col);
-    }
-
-    //마지막 좌표와 선택 좌표 세팅
-    private void LastNSelectedSetting(int row, int col)
-    {
-        //첫수 확인
-        if (_lastRow != -1 || _lastCol != -1)
-        {
-            stoneController.SetStoneState(Enums.StoneState.None, _lastRow, _lastCol);
-        }
-        //마지막 좌표 저장
-        _lastRow = row;
-        _lastCol = col;
-        //선택 좌표 초기화
-        selectedRow = -1;
-        selectedCol = -1;
-    }
-    //게임 끝
-    public void EndGame(Enums.GameResult result)
-    {
-        SetState(null);
-        ReplayManager.Instance.SaveReplayDataResult(result);
-        //TODO: 게임 종료 후 행동 구현
-    }
-    
-    //승리 확인 함수
-    public bool CheckGameWin(Enums.PlayerType player, int row, int col)
-    {
-        return OmokAI.Instance.CheckGameWin(player, _board, row, col);
-    }
-    
-    // 특정 방향으로 같은 돌 개수와 열린 끝 개수를 계산하는 함수
-    private (int count, int openEnds) CountStones(
-        Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player)
-    {
-        int size = board.GetLength(0);
-        int count = 0;
-        int openEnds = 0;
-
-        // 정방향 탐색
-        int r = row + direction[0], c = col + direction[1];
-        while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
-        {
-            count++;
-            r += direction[0]; // row값 옮기기
-            c += direction[1]; // col값 옮기기
-        }
-
-        if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
-        {
-            openEnds++;
-        }
-
-        // 역방향 탐색
-        r = row - direction[0]; 
-        c = col - direction[1];
-        while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
-        {
-            count++;
-            r -= direction[0];
-            c -= direction[1];
-        }
-
-        if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
-        {
-            openEnds++;
-        }
-        
-        return (count, openEnds);
-    }
-
-    public Enums.PlayerType[,] GetBoard()
-    {
-        return _board;
-    }
-    //무승부 확인
-    public bool CheckGameDraw()
-    {
-        if (CheckIsFull(_board)) return true; // 빈 칸이 없으면 무승부
-        bool playerAHasChance = CheckFiveChance(_board, Enums.PlayerType.PlayerA);
-        bool playerBHasChance = CheckFiveChance(_board, Enums.PlayerType.PlayerB);
-        return !(playerAHasChance || playerBHasChance); // 둘 다 기회가 없으면 무승부
-    }
-
-    //연속되는 5개가 만들어질 기회가 있는지 판단
-    private bool CheckFiveChance(Enums.PlayerType[,] board, Enums.PlayerType player)
-    {
-        var tempBoard = (Enums.PlayerType[,])board.Clone();
-        int size = board.GetLength(0);
-        for (int row = 0; row < size; row++)
-        {
-            for (int col = 0; col < size; col++)
-            {
-                if (tempBoard[row, col] != Enums.PlayerType.None) continue;
-                tempBoard[row, col] = player;
-                foreach (var dir in AIConstants.Directions)
-                {
-                    var (count, _) = CountStones(tempBoard, row, col, dir, player);
-
-                    // 자기 자신 포함하여 5개 이상일 시 true 반환
-                    if (count + 1 >= Constants.WIN_COUNT) return true;
-                }
-            }
-        }
-        return false;
-    }
-    //보드가 꽉 찼는지 확인
-    private static bool CheckIsFull(Enums.PlayerType[,] board)
-    {
-        int size = board.GetLength(0);
-        for (int row = 0; row < size; row++)
-        {
-            for (int col = 0; col < size; col++)
-            {
-                if (board[row, col] == Enums.PlayerType.None) return false;
-            }
-        }
-        return true;
-    }
-
-    #region Renju Rule Detector
     // 금수 위치 업데이트 및 표시
     public void UpdateForbiddenMoves()
     {
         ClearForbiddenMarks();
 
-        if (currentTurn == Enums.PlayerType.PlayerA)
+        if (CurrentTurn == Enums.PlayerType.PlayerA)
         {
             var cloneBoard = (Enums.PlayerType[,])_board.Clone();
             _forbiddenMoves = _forbiddenDetector.RenjuForbiddenMove(cloneBoard);
@@ -957,5 +404,22 @@ public class GameLogic : IDisposable
             }
         }
     }
-#endregion
+
+    // AI닉네임 랜덤 생성
+    private string RandomAINickname()
+    {
+        string[] AI_NAMIES = { "이세돌",  "신사동호랭이","진짜인간임","종로3가짱돌","마스터김춘배","62세황순자","고준일 강사님"};
+
+        var index = Random.Range(0, AI_NAMIES.Length);
+
+        return AI_NAMIES[index];
+    }
+
+    #endregion
+    
+    public void Dispose()
+    {
+        MultiPlayManager?.LeaveRoom(_roomId);
+        MultiPlayManager?.Dispose();
+    }
 }
diff --git a/Assets/Script/Game/GameManager.cs b/Assets/Script/Game/GameManager.cs
index 3890a27..d8d7d77 100644
--- a/Assets/Script/Game/GameManager.cs
+++ b/Assets/Script/Game/GameManager.cs
@@ -30,7 +30,7 @@ public class GameManager : Singleton<GameManager>
     
     public MultiplayManager GetMultiplayManager()
     {
-        _multiplayManager = _gameLogic._multiplayManager;
+        _multiplayManager = _gameLogic.MultiPlayManager;
         if (_multiplayManager == null) Debug.Log("MultiplayManager가 null입니다");
         return _multiplayManager;
     }
@@ -49,7 +49,7 @@ public class GameManager : Singleton<GameManager>
     
     public void OnClickConfirmButton()
     {
-        if (_gameLogic.selectedRow != -1 && _gameLogic.selectedCol != -1)
+        if (_gameLogic.SelectedRow != -1 && _gameLogic.SelectedCol != -1)
         {
             _gameLogic.OnConfirm();
         }
@@ -99,7 +99,7 @@ public class GameManager : Singleton<GameManager>
         if (_gameLogic == null) return;
         _gameLogic.ResetBoard();
         _stoneController.InitStones();
-        _gameLogic.SetState(_gameLogic.firstPlayerState);
+        _gameLogic.SetState(_gameLogic.FirstPlayerState);
     }
     //유저 이름 Game UI에 초기화
     public void InitPlayersName(string playerNameA, string playerNameB)
diff --git a/Assets/Script/Game/GameStates.meta b/Assets/Script/Game/GameStates.meta
new file mode 100644
index 0000000..7abcde1
--- /dev/null
+++ b/Assets/Script/Game/GameStates.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: b15e90e5f55e4d338705bfffd558d056
+timeCreated: 1743068734
\ No newline at end of file
diff --git a/Assets/Script/Game/GameStates/AIState.cs b/Assets/Script/Game/GameStates/AIState.cs
new file mode 100644
index 0000000..29de06a
--- /dev/null
+++ b/Assets/Script/Game/GameStates/AIState.cs
@@ -0,0 +1,27 @@
+public class AIState: BasePlayerState
+{
+    public override void OnEnter(GameLogic gameLogic)
+    {
+        gameLogic.FioTimer.StartTimer();
+        OmokAI.Instance.StartBestMoveSearch(gameLogic.GetBoard(), (bestMove) =>
+        {
+            if(bestMove.HasValue)
+                HandleMove(gameLogic, bestMove.Value.Item1, bestMove.Value.Item2);
+        });
+    }
+
+    public override void OnExit(GameLogic gameLogic)
+    {
+        gameLogic.FioTimer.InitTimer();
+    }
+
+    public override void HandleMove(GameLogic gameLogic, int row, int col)
+    {
+        ProcessMove(gameLogic, Enums.PlayerType.PlayerB,row, col);
+    }
+
+    public override void HandleNextTurn(GameLogic gameLogic)
+    {
+        gameLogic.SetState(gameLogic.FirstPlayerState);
+    }
+}
\ No newline at end of file
diff --git a/Assets/Script/Game/GameStates/AIState.cs.meta b/Assets/Script/Game/GameStates/AIState.cs.meta
new file mode 100644
index 0000000..877d37d
--- /dev/null
+++ b/Assets/Script/Game/GameStates/AIState.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: fbd2216a641845bc9892444713d3497f
+timeCreated: 1743068785
\ No newline at end of file
diff --git a/Assets/Script/Game/GameStates/BasePlayerState.cs b/Assets/Script/Game/GameStates/BasePlayerState.cs
new file mode 100644
index 0000000..7a73f37
--- /dev/null
+++ b/Assets/Script/Game/GameStates/BasePlayerState.cs
@@ -0,0 +1,58 @@
+using UnityEngine;
+
+public abstract class BasePlayerState
+{
+    public abstract void OnEnter(GameLogic gameLogic);
+    public abstract void OnExit(GameLogic gameLogic);
+    public abstract void HandleMove(GameLogic gameLogic, int row, int col);
+    public abstract void HandleNextTurn(GameLogic gameLogic);
+
+    protected string _roomId;
+    protected bool _isMultiplay;
+    protected MultiplayManager _multiplayManager;
+
+    public void ProcessMove(GameLogic gameLogic, Enums.PlayerType playerType, int row, int col)
+    {
+        gameLogic.FioTimer.PauseTimer();
+        gameLogic.SetNewBoardValue(playerType, row, col);
+        gameLogic.CountStoneCounter();
+
+        if (_isMultiplay)
+        {
+            _multiplayManager.SendPlayerMove(_roomId, new Vector2Int(row, col));
+        }
+
+        if (gameLogic.CheckGameWin(playerType, row, col))
+        {
+            var gameResult = playerType == Enums.PlayerType.PlayerA? Enums.GameResult.Win:Enums.GameResult.Lose;
+            if (gameLogic.GameType == Enums.GameType.MultiPlay)
+            {
+                if (gameLogic.FirstPlayerState.GetType() != typeof(PlayerState))
+                {
+                    gameResult = gameResult == Enums.GameResult.Win ? Enums.GameResult.Lose : Enums.GameResult.Win;
+                }
+            }
+            GameManager.Instance.panelManager.OpenEffectPanel(gameResult);
+            gameLogic.EndGame(gameResult);
+        }
+        else
+        {
+            if (gameLogic.TotalStoneCounter >= Constants.MinCountForDrawCheck)
+            {
+                if (gameLogic.CheckGameDraw())
+                {
+                    GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Draw);
+                    gameLogic.EndGame(Enums.GameResult.Draw);
+                }
+                else
+                {
+                    HandleNextTurn(gameLogic);
+                }
+            }
+            else
+            {
+                HandleNextTurn(gameLogic);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Assets/Script/Game/GameStates/BasePlayerState.cs.meta b/Assets/Script/Game/GameStates/BasePlayerState.cs.meta
new file mode 100644
index 0000000..7d599d0
--- /dev/null
+++ b/Assets/Script/Game/GameStates/BasePlayerState.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 28db9c0415a04bd288598b502b43066f
+timeCreated: 1743068714
\ No newline at end of file
diff --git a/Assets/Script/Game/GameStates/MultiPlayerState.cs b/Assets/Script/Game/GameStates/MultiPlayerState.cs
new file mode 100644
index 0000000..bb0390b
--- /dev/null
+++ b/Assets/Script/Game/GameStates/MultiPlayerState.cs
@@ -0,0 +1,62 @@
+public class MultiPlayerState: BasePlayerState
+{
+    private Enums.PlayerType _playerType;
+    private bool _isFirstPlayer;
+
+    private MultiplayManager _multiplayManager;
+
+    public MultiPlayerState(bool isFirstPlayer, MultiplayManager multiplayManager)
+    {
+        _isFirstPlayer = isFirstPlayer;
+        _playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB;
+        _multiplayManager = multiplayManager;
+    }
+
+    public override void OnEnter(GameLogic gameLogic)
+    {
+        gameLogic.FioTimer.StartTimer();
+        //TODO: 첫번째 플레이어면 렌주 룰 확인
+        #region Renju Turn Set
+        // 턴이 변경될 때마다 금수 위치 업데이트
+        gameLogic.UpdateForbiddenMoves();
+        #endregion
+
+        gameLogic.CurrentTurn = _playerType;
+        // gameLogic.stoneController.OnStoneClickedDelegate = (row, col) =>
+        // {
+        //     HandleMove(gameLogic, row, col);
+        // };
+        _multiplayManager.OnOpponentMove = moveData =>
+        {
+            var row = moveData.position.x;
+            var col = moveData.position.y;
+            UnityThread.executeInUpdate(() =>
+            {
+                HandleMove(gameLogic, row, col);
+            });
+        };
+    }
+
+    public override void OnExit(GameLogic gameLogic)
+    {
+        gameLogic.FioTimer.InitTimer();
+        _multiplayManager.OnOpponentMove = null;
+    }
+
+    public override void HandleMove(GameLogic gameLogic, int row, int col)
+    {
+        ProcessMove(gameLogic, _playerType, row, col);
+    }
+
+    public override void HandleNextTurn(GameLogic gameLogic)
+    {
+        if (_isFirstPlayer)
+        {
+            gameLogic.SetState(gameLogic.SecondPlayerState);
+        }
+        else
+        {
+            gameLogic.SetState(gameLogic.FirstPlayerState);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Assets/Script/Game/GameStates/MultiPlayerState.cs.meta b/Assets/Script/Game/GameStates/MultiPlayerState.cs.meta
new file mode 100644
index 0000000..a97769a
--- /dev/null
+++ b/Assets/Script/Game/GameStates/MultiPlayerState.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 20e292a1c2064962ab8eaaa5a80bc3b9
+timeCreated: 1743068802
\ No newline at end of file
diff --git a/Assets/Script/Game/GameStates/PlayerState.cs b/Assets/Script/Game/GameStates/PlayerState.cs
new file mode 100644
index 0000000..c5d38a9
--- /dev/null
+++ b/Assets/Script/Game/GameStates/PlayerState.cs
@@ -0,0 +1,63 @@
+public class PlayerState : BasePlayerState
+{
+    private Enums.PlayerType _playerType;
+    private bool _isFirstPlayer;
+
+    public PlayerState(bool isFirstPlayer)
+    {
+        _isFirstPlayer = isFirstPlayer;
+        _playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB;
+        _isMultiplay = false;
+    }
+
+    public PlayerState(bool isFirstPlayer, MultiplayManager multiplayManager, string roomId)
+        : this(isFirstPlayer)
+    {
+        _isFirstPlayer = isFirstPlayer;
+        _multiplayManager = multiplayManager;
+        _roomId = roomId;
+        _isMultiplay = true;
+    }
+
+    public override void OnEnter(GameLogic gameLogic)
+    {
+        gameLogic.FioTimer.StartTimer();
+
+        //TODO: 첫번째 플레이어면 렌주 룰 확인
+        #region Renju Turn Set
+        // 턴이 변경될 때마다 금수 위치 업데이트
+        gameLogic.UpdateForbiddenMoves();
+        #endregion
+
+        gameLogic.CurrentTurn = _playerType;
+        gameLogic.StoneController.OnStoneClickedDelegate = (row, col) =>
+        {
+            HandleMove(gameLogic, row, col);
+        };
+    }
+
+    public override void OnExit(GameLogic gameLogic)
+    {
+        //TODO: 렌주 룰 금수자리 초기화
+
+        gameLogic.FioTimer.InitTimer();
+        gameLogic.StoneController.OnStoneClickedDelegate = null;
+    }
+
+    public override void HandleMove(GameLogic gameLogic, int row, int col)
+    {
+        gameLogic.SetStoneSelectedState(row, col);
+    }
+
+    public override void HandleNextTurn(GameLogic gameLogic)
+    {
+        if (_isFirstPlayer)
+        {
+            gameLogic.SetState(gameLogic.SecondPlayerState);
+        }
+        else
+        {
+            gameLogic.SetState(gameLogic.FirstPlayerState);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Assets/Script/Game/GameStates/PlayerState.cs.meta b/Assets/Script/Game/GameStates/PlayerState.cs.meta
new file mode 100644
index 0000000..5f9c74f
--- /dev/null
+++ b/Assets/Script/Game/GameStates/PlayerState.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 951fc71a9c154522b3562053350536b8
+timeCreated: 1743068763
\ No newline at end of file
diff --git a/Assets/Script/Game/GameUtility.meta b/Assets/Script/Game/GameUtility.meta
new file mode 100644
index 0000000..cc52462
--- /dev/null
+++ b/Assets/Script/Game/GameUtility.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 79bd83540d994967a0e3b7ee6d5bc835
+timeCreated: 1743076291
\ No newline at end of file
diff --git a/Assets/Script/Game/GameUtility/GameRoutine.cs b/Assets/Script/Game/GameUtility/GameRoutine.cs
new file mode 100644
index 0000000..18c131f
--- /dev/null
+++ b/Assets/Script/Game/GameUtility/GameRoutine.cs
@@ -0,0 +1,89 @@
+public partial class GameLogic
+{
+    // 돌 카운터 증가 함수
+    public void CountStoneCounter() => _totalStoneCounter++;
+
+    // 착수 버튼 클릭시 호출되는 함수
+    public void OnConfirm() => CurrentPlayerState.ProcessMove(this, CurrentTurn, SelectedRow, SelectedCol);
+
+    // 스톤의 상태변경 명령함수
+    private void SetStoneNewState(Enums.StoneState state, int row, int col)
+    {
+        StoneController.SetStoneState(state, row, col);
+    }
+
+    public void SetStoneSelectedState(int row, int col)
+    {
+        if (_board[row, col] != Enums.PlayerType.None) return;
+
+        if (StoneController.GetStoneState(row, col) != Enums.StoneState.None &&
+            CurrentTurn == Enums.PlayerType.PlayerA) return;
+        // 첫수 및 중복 확인
+        if ((SelectedRow != row || SelectedCol != col) && (SelectedRow != -1 && SelectedCol != -1))
+        {
+            StoneController.SetStoneState(Enums.StoneState.None, SelectedRow, SelectedCol);
+        }
+
+        (SelectedRow, SelectedCol) = (row, col);
+
+        StoneController.SetStoneState(Enums.StoneState.Selected, row, col);
+    }
+
+    // 보드에 돌추가 함수
+    public void SetNewBoardValue(Enums.PlayerType playerType, int row, int col)
+    {
+        if (_board[row, col] != Enums.PlayerType.None) return;
+
+        switch (playerType)
+        {
+            case Enums.PlayerType.PlayerA:
+                StoneController.SetStoneType(Enums.StoneType.Black, row, col);
+                StoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col);
+                _board[row, col] = Enums.PlayerType.PlayerA;
+                LastNSelectedSetting(row, col);
+
+                ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.Black, row, col); //기보 데이터 저장
+                break;
+            case Enums.PlayerType.PlayerB:
+                StoneController.SetStoneType(Enums.StoneType.White, row, col);
+                StoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col);
+                _board[row, col] = Enums.PlayerType.PlayerB;
+                LastNSelectedSetting(row, col);
+
+                ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.White, row, col);
+                break;
+        }
+    }
+
+    // 돌 지우는 함수
+    public void RemoveStone(int row, int col)
+    {
+        _board[row, col] = Enums.PlayerType.None;
+        StoneController.SetStoneType(Enums.StoneType.None, row, col);
+        StoneController.SetStoneState(Enums.StoneState.None, row, col);
+    }
+
+    // 마지막 좌표와 선택 좌표 세팅
+    private void LastNSelectedSetting(int row, int col)
+    {
+        //첫수 확인
+        if (_lastRow != -1 || _lastCol != -1)
+        {
+            StoneController.SetStoneState(Enums.StoneState.None, _lastRow, _lastCol);
+        }
+
+        //마지막 좌표 저장
+        (_lastRow, _lastCol) = (row, col);
+
+        //선택 좌표 초기화
+        (SelectedRow, SelectedCol) = (-1, -1);
+    }
+
+    // 게임 끝
+    public void EndGame(Enums.GameResult result)
+    {
+        SetState(null);
+        ReplayManager.Instance.SaveReplayDataResult(result);
+        //TODO: 게임 종료 후 행동 구현
+    }
+}
\ No newline at end of file
diff --git a/Assets/Script/Game/GameUtility/GameRoutine.cs.meta b/Assets/Script/Game/GameUtility/GameRoutine.cs.meta
new file mode 100644
index 0000000..80cebb3
--- /dev/null
+++ b/Assets/Script/Game/GameUtility/GameRoutine.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: d911ca14696a433ebc112577b60de664
+timeCreated: 1743076571
\ No newline at end of file
diff --git a/Assets/Script/Game/GameUtility/GameWinCheck.cs b/Assets/Script/Game/GameUtility/GameWinCheck.cs
new file mode 100644
index 0000000..2487c7a
--- /dev/null
+++ b/Assets/Script/Game/GameUtility/GameWinCheck.cs
@@ -0,0 +1,96 @@
+public partial class GameLogic
+{
+    //승리 확인 함수
+    public bool CheckGameWin(Enums.PlayerType player, int row, int col)
+    {
+        return OmokAI.Instance.CheckGameWin(player, _board, row, col);
+    }
+
+    // 특정 방향으로 같은 돌 개수와 열린 끝 개수를 계산하는 함수
+    private (int count, int openEnds) CountStones(
+        Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player)
+    {
+        int size = board.GetLength(0);
+        int count = 0;
+        int openEnds = 0;
+
+        // 정방향 탐색
+        int r = row + direction[0], c = col + direction[1];
+        while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
+        {
+            count++;
+            r += direction[0]; // row값 옮기기
+            c += direction[1]; // col값 옮기기
+        }
+
+        if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
+        {
+            openEnds++;
+        }
+
+        // 역방향 탐색
+        r = row - direction[0];
+        c = col - direction[1];
+        while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
+        {
+            count++;
+            r -= direction[0];
+            c -= direction[1];
+        }
+
+        if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
+        {
+            openEnds++;
+        }
+
+        return (count, openEnds);
+    }
+
+    //무승부 확인
+    public bool CheckGameDraw()
+    {
+        if (CheckIsFull(_board)) return true; // 빈 칸이 없으면 무승부
+        bool playerAHasChance = CheckFiveChance(_board, Enums.PlayerType.PlayerA);
+        bool playerBHasChance = CheckFiveChance(_board, Enums.PlayerType.PlayerB);
+        return !(playerAHasChance || playerBHasChance); // 둘 다 기회가 없으면 무승부
+    }
+
+    //연속되는 5개가 만들어질 기회가 있는지 판단
+    private bool CheckFiveChance(Enums.PlayerType[,] board, Enums.PlayerType player)
+    {
+        var tempBoard = (Enums.PlayerType[,])board.Clone();
+        int size = board.GetLength(0);
+        for (int row = 0; row < size; row++)
+        {
+            for (int col = 0; col < size; col++)
+            {
+                if (tempBoard[row, col] != Enums.PlayerType.None) continue;
+                tempBoard[row, col] = player;
+                foreach (var dir in AIConstants.Directions)
+                {
+                    var (count, _) = CountStones(tempBoard, row, col, dir, player);
+
+                    // 자기 자신 포함하여 5개 이상일 시 true 반환
+                    if (count + 1 >= Constants.WIN_COUNT) return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    //보드가 꽉 찼는지 확인
+    private static bool CheckIsFull(Enums.PlayerType[,] board)
+    {
+        int size = board.GetLength(0);
+        for (int row = 0; row < size; row++)
+        {
+            for (int col = 0; col < size; col++)
+            {
+                if (board[row, col] == Enums.PlayerType.None) return false;
+            }
+        }
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Assets/Script/Game/GameUtility/GameWinCheck.cs.meta b/Assets/Script/Game/GameUtility/GameWinCheck.cs.meta
new file mode 100644
index 0000000..f6d41b4
--- /dev/null
+++ b/Assets/Script/Game/GameUtility/GameWinCheck.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 367e9d42ab0f4a879d00bac722922970
+timeCreated: 1743076291
\ No newline at end of file