diff --git a/docs/BLACKJACK.md b/docs/BLACKJACK.md
index 76812cc101..86db4d9faa 100644
--- a/docs/BLACKJACK.md
+++ b/docs/BLACKJACK.md
@@ -53,6 +53,44 @@ jason카드: 7클로버, K스페이드 - 결과: 17
 
 ### 3. 게임 규칙 및 결과 판정
 
-- [ ] 21점 초과 판정 로직: 플레이어 또는 딜러의 손패 합계가 21을 초과하면 게임에서 패배한다.
-- [ ] 승리 조건 계산: 플레이어와 딜러 중 21에 가장 근접한 쪽이 승리한다.
-- [ ] 게임 결과 출력: 최종 승자와 각 플레이어의 최종 손패 및 점수를 출력한다.
\ No newline at end of file
+- [x] 21점 초과 판정 로직: 플레이어 또는 딜러의 손패 합계가 21을 초과하면 게임에서 패배한다.
+- [x] 승리 조건 계산: 플레이어와 딜러 중 21에 가장 근접한 쪽이 승리한다.
+- [x] 게임 결과 출력: 최종 승자와 각 플레이어의 최종 손패 및 점수를 출력한다.
+
+## 딜러가 추가된 사전 구현 계획
+
+- [x] 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다.
+  - [x] 만일 딜러가 <Ace, 6>을 갖고 있다면?
+  - [x] 딜러는 다양한 전략을 사용할 수 있도록 한다.
+
+### 도메인 모델들이 결정되었다면 블랙잭 게임 구현 계획
+도메인 모듈이 뷰에게 의존하지 않도록 구현할 것이다.
+
+- [x] **게임 초기화 상태 확인**
+  - [x] blackjackGame이 게임을 초기화해야 하는 상태인지 확인
+  - [x] 필요한 경우, blackjackGame에 필요한 정보를 사용자로부터 받아 전달
+
+- [x] **플레이어의 턴 상태 확인 (첫 번째)**
+  - [x] blackjackGame이 플레이어들의 턴인 상태인지 확인
+  - [x] 현재 blackjackGame의 플레이어 중 누구의 턴인지 확인
+  - [x] 해당 플레이어가 카드를 뽑을 것인지 말 것인지를 사용자로부터 받아 전달
+
+- [x] **플레이어의 턴 상태 재확인 (두 번째)**
+  - [x] 다시 blackjackGame이 플레이어들의 턴인 상태인지 확인
+  - [x] 다시 현재 플레이어 중 누구의 턴인지 확인
+  - [x] 해당 플레이어가 카드를 뽑을 것인지 말 것인지를 사용자로부터 받아 전달
+
+- [x] **마지막 플레이어의 턴과 딜러의 턴 상태 전환**
+  - [x] 마지막 플레이어의 턴에서 더 이상 카드를 뽑을 수 없거나, 뽑지 않겠다는 결정이 내려진 경우 확인
+  - [x] blackjackGame의 상태를 딜러의 턴 상태로 전환
+
+- [x] **딜러의 턴 상태 확인**
+  - [x] blackjackGame이 딜러의 턴인 상태인지 확인
+  - [x] 딜러가 카드를 뽑을지 안 뽑을지 결정하고 사용자에게 알림
+  - [x] blackjackGame을 `End` 상태로 전환
+
+- [x] **End 상태 확인**
+  - [x] blackjackGame이 End 상태인지 확인
+  - [x] blackjackGame에게 BlackjackResult를 요청하고 사용자에게 보여줌
+
+- [x] **게임 종료**
diff --git a/domain/src/main/kotlin/action/BlackJackAction.kt b/domain/src/main/kotlin/action/BlackJackAction.kt
new file mode 100644
index 0000000000..7fc6b271d6
--- /dev/null
+++ b/domain/src/main/kotlin/action/BlackJackAction.kt
@@ -0,0 +1,6 @@
+package action
+
+enum class BlackJackAction {
+    HIT,
+    STAND,
+}
diff --git a/domain/src/main/kotlin/blackjack/BlackjackParticipant.kt b/domain/src/main/kotlin/blackjack/BlackjackParticipant.kt
new file mode 100644
index 0000000000..a5e46cb21c
--- /dev/null
+++ b/domain/src/main/kotlin/blackjack/BlackjackParticipant.kt
@@ -0,0 +1,9 @@
+package blackjack
+
+import blackjack.card.Card
+
+interface BlackjackParticipant {
+    fun receiveCard(card: Card): BlackjackParticipant
+    fun receiveCard(cards: List<Card>): BlackjackParticipant
+    fun calculateBestValue(): Int
+}
diff --git a/domain/src/main/kotlin/blackjack/dealer/Dealer.kt b/domain/src/main/kotlin/blackjack/dealer/Dealer.kt
new file mode 100644
index 0000000000..c7938dd491
--- /dev/null
+++ b/domain/src/main/kotlin/blackjack/dealer/Dealer.kt
@@ -0,0 +1,26 @@
+package blackjack.dealer
+
+import action.BlackJackAction
+import blackjack.BlackjackParticipant
+import blackjack.card.Card
+import blackjack.deck.Deck
+import blackjack.hand.Hand
+import blackjack.hand.StandardHand
+
+data class Dealer(
+    val dealerStrategy: DealerStrategy = DefaultDealerStrategy(),
+    private val hand: Hand = StandardHand(),
+) : BlackjackParticipant {
+
+    val cards: List<Card> get() = hand.cards()
+
+    override fun receiveCard(card: Card): Dealer = copy(hand = hand.addCard(card))
+
+    override fun receiveCard(cards: List<Card>): Dealer = copy(hand = hand.addCard(cards))
+
+    override fun calculateBestValue(): Int = hand.calculateBestValue()
+
+    fun decideAction(deck: Deck): BlackJackAction {
+        return dealerStrategy.decideAction(hand, deck)
+    }
+}
diff --git a/domain/src/main/kotlin/blackjack/dealer/DealerStrategy.kt b/domain/src/main/kotlin/blackjack/dealer/DealerStrategy.kt
new file mode 100644
index 0000000000..016af3f4ef
--- /dev/null
+++ b/domain/src/main/kotlin/blackjack/dealer/DealerStrategy.kt
@@ -0,0 +1,9 @@
+package blackjack.dealer
+
+import action.BlackJackAction
+import blackjack.deck.Deck
+import blackjack.hand.Hand
+
+interface DealerStrategy {
+    fun decideAction(hand: Hand, deck: Deck): BlackJackAction
+}
diff --git a/domain/src/main/kotlin/blackjack/dealer/DefaultDealerStrategy.kt b/domain/src/main/kotlin/blackjack/dealer/DefaultDealerStrategy.kt
new file mode 100644
index 0000000000..1d0815f04b
--- /dev/null
+++ b/domain/src/main/kotlin/blackjack/dealer/DefaultDealerStrategy.kt
@@ -0,0 +1,37 @@
+package blackjack.dealer
+
+import action.BlackJackAction
+import blackjack.card.Card
+import blackjack.card.CardRank
+import blackjack.deck.Deck
+import blackjack.hand.Hand
+
+internal class DefaultDealerStrategy : DealerStrategy {
+    override fun decideAction(hand: Hand, deck: Deck): BlackJackAction {
+        val dealerScore = hand.calculateBestValue()
+        val dealerMinScore = hand.calculateMinValue()
+
+        val bustingProbability = maxOf(
+            calculateProbabilityOfBusting(dealerScore, deck),
+            calculateProbabilityOfBusting(dealerMinScore, deck)
+        )
+
+        return if (bustingProbability > 0.5) BlackJackAction.STAND else BlackJackAction.HIT
+    }
+
+    private fun calculateProbabilityOfBusting(currentScore: Int, deck: Deck): Double {
+        val remainedScore = 21 - currentScore
+        val safeCards = deck.remainingCards.count { isSafe(it, remainedScore) }
+
+        return 1.0 - safeCards.toDouble() / deck.remainingCards.size
+    }
+
+    private fun isSafe(card: Card, remainedScore: Int): Boolean {
+        val cardValue = when (card.rank) {
+            CardRank.KING, CardRank.QUEEN, CardRank.JACK -> 10
+            CardRank.ACE -> 11
+            else -> card.rank.ordinal + 1
+        }
+        return cardValue <= remainedScore
+    }
+}
diff --git a/domain/src/main/kotlin/blackjack/deck/Deck.kt b/domain/src/main/kotlin/blackjack/deck/Deck.kt
index d3ae3bc5f3..ccb068e60d 100644
--- a/domain/src/main/kotlin/blackjack/deck/Deck.kt
+++ b/domain/src/main/kotlin/blackjack/deck/Deck.kt
@@ -1,7 +1,7 @@
 package blackjack.deck
 
 import blackjack.card.Card
-import java.util.Stack
+import java.util.*
 
 class Deck(
     cardProvider: CardProvider = StandardCardProvider(),
@@ -11,11 +11,16 @@ class Deck(
         addAll(cardShuffler.shuffle(cardProvider.provideCards()))
     }
 
-    val size
-        get() = cards.size
+    val remainingCards: List<Card>
+        get() = cards.toList()
 
     fun drawCard(): Card {
         check(cards.isNotEmpty()) { "덱에 카드가 없으면 카드를 뽑을 수 없습니다." }
         return cards.pop()
     }
+
+    fun drawCard(count: Int): List<Card> {
+        check(cards.size >= count) { "덱에 $count 만큼 카드가 없습니다." }
+        return List(count) { cards.pop() }
+    }
 }
diff --git a/domain/src/main/kotlin/blackjack/deck/RandomCardShuffler.kt b/domain/src/main/kotlin/blackjack/deck/RandomCardShuffler.kt
index 40a69d5e6a..981d9ab18f 100644
--- a/domain/src/main/kotlin/blackjack/deck/RandomCardShuffler.kt
+++ b/domain/src/main/kotlin/blackjack/deck/RandomCardShuffler.kt
@@ -2,6 +2,6 @@ package blackjack.deck
 
 import blackjack.card.Card
 
-class RandomCardShuffler : CardShuffler {
+internal class RandomCardShuffler : CardShuffler {
     override fun shuffle(cards: List<Card>): List<Card> = cards.shuffled()
 }
diff --git a/domain/src/main/kotlin/blackjack/deck/StandardCardProvider.kt b/domain/src/main/kotlin/blackjack/deck/StandardCardProvider.kt
index ba77587382..1939a173e0 100644
--- a/domain/src/main/kotlin/blackjack/deck/StandardCardProvider.kt
+++ b/domain/src/main/kotlin/blackjack/deck/StandardCardProvider.kt
@@ -4,7 +4,7 @@ import blackjack.card.Card
 import blackjack.card.CardRank
 import blackjack.card.CardSuit
 
-class StandardCardProvider : CardProvider {
+internal class StandardCardProvider : CardProvider {
     override fun provideCards(): List<Card> =
         CardSuit.values().flatMap { suit ->
             CardRank.values().map { rank -> Card(suit, rank) }
diff --git a/domain/src/main/kotlin/blackjack/game/BlackjackGame.kt b/domain/src/main/kotlin/blackjack/game/BlackjackGame.kt
new file mode 100644
index 0000000000..fba04b359a
--- /dev/null
+++ b/domain/src/main/kotlin/blackjack/game/BlackjackGame.kt
@@ -0,0 +1,147 @@
+package blackjack.game
+
+import action.BlackJackAction
+import blackjack.BlackjackParticipant
+import blackjack.card.Card
+import blackjack.dealer.Dealer
+import blackjack.dealer.DealerStrategy
+import blackjack.dealer.DefaultDealerStrategy
+import blackjack.deck.Deck
+import blackjack.player.Player
+
+class BlackjackGame private constructor(
+    players: List<Player>,
+    dealer: Dealer = Dealer(),
+    private val deck: Deck = Deck(),
+) {
+    init {
+        require(players.toSet().isNotEmpty()) { "플레이어가 최소 한 명은 존재해야 합니다." }
+    }
+
+    var state: GameState = GameState.InitialDeal(players, dealer)
+        private set
+
+    val players: List<Player> get() = state.players
+    val dealer: Dealer get() = state.dealer
+
+    fun dealInitialCards() {
+        check(state is GameState.InitialDeal) { "Initial Deal 상태가 아닙니다." }
+        val nPlayers = List(players.size) { players[it].receiveCard(deck.drawCard(2)) }
+        val nDealer = dealer.receiveCard(deck.drawCard(2))
+        state = GameState.PlayerTurn(nPlayers, nDealer, currentPlayerIndex = 0)
+    }
+
+    fun dealPlayerTurn(player: Player, isDeal: Boolean) {
+        val playerTurnState = state as? GameState.PlayerTurn ?: throw IllegalStateException("Player Turn이 아닙니다.")
+        require(players.contains(player)) { "${player.name}이라는 플레이어는 없습니다." }
+        require(player == playerTurnState.currentPlayer) { "현재 턴은 ${player.name}의 턴이 아닙니다." }
+
+        if (isDeal.not()) {
+            // 다음 플레이어로 넘어감
+            moveToNextPlayerOrDealerTurn(playerTurnState.currentPlayerIndex)
+        } else {
+            // 카드 받기
+            check(player.canHit() == BlackJackAction.HIT) { "해당 플레이어는 더 이상 카드를 받을 수 없습니다." }
+            val nPlayers = players.map { if (it == player) it.receiveCard(deck.drawCard()) else it }
+            state = GameState.PlayerTurn(nPlayers, dealer, playerTurnState.currentPlayerIndex)
+        }
+    }
+
+    fun dealDealerTurn(): BlackJackAction {
+        check(state is GameState.DealerTurn) { "Dealer Turn이 아닙니다." }
+        val dealerAction = dealer.decideAction(deck)
+        return if (dealerAction == BlackJackAction.HIT) {
+            val drawnCard = deck.drawCard()
+            state = GameState.End(players, dealer.receiveCard(drawnCard))
+            BlackJackAction.HIT
+        } else {
+            state = GameState.End(players, dealer)
+            BlackJackAction.STAND
+        }
+    }
+
+    fun calculateResult(): Map<BlackjackParticipant, BlackjackResult> {
+        val results = mutableMapOf<BlackjackParticipant, BlackjackResult>()
+        results[dealer] = calculateDealerResult()
+        players.forEach { results[it] = calculatePlayerResult(it) }
+        return results
+    }
+
+    fun showPlayerCards(playerName: String): List<Card> {
+        val player = state.players.find { it.name == playerName }
+            ?: throw IllegalArgumentException("${playerName}이라는 플레이어는 없습니다.")
+        return player.cards
+    }
+
+    private fun calculateDealerResult(): BlackjackResult {
+        val dealerScore = dealer.calculateBestValue()
+        var win = 0
+        var loss = 0
+        players.forEach {
+            if (dealerScore > 21) loss++
+            else if (it.calculateBestValue() > 21) win++
+            else if (dealerScore > it.calculateBestValue()) win++
+            else if (dealerScore <= it.calculateBestValue()) loss++
+        }
+        return BlackjackResult(win, loss)
+    }
+
+    private fun calculatePlayerResult(player: Player): BlackjackResult {
+        val playerScore = player.calculateBestValue()
+        val dealerScore = dealer.calculateBestValue()
+        return if (dealerScore > 21) {
+            BlackjackResult(1, 0)
+        } else if (playerScore > 21) {
+            BlackjackResult(0, 1)
+        } else if (playerScore >= dealerScore) {
+            BlackjackResult(1, 0)
+        } else {
+            BlackjackResult(0, 1)
+        }
+    }
+
+    private fun moveToNextPlayerOrDealerTurn(currentPlayerIndex: Int) {
+        val nextPlayerIndex = (currentPlayerIndex + 1) % players.size
+        state = if (nextPlayerIndex == 0) {
+            GameState.DealerTurn(players, dealer)
+        } else {
+            GameState.PlayerTurn(players, dealer, nextPlayerIndex)
+        }
+    }
+
+    class BlackjackGameBuilder {
+        private val players: MutableList<Player> = mutableListOf()
+        private var dealerStrategy: DealerStrategy = DefaultDealerStrategy()
+
+        fun join(name: String) {
+            players.add(Player(name = name))
+        }
+
+        fun join(names: List<String>) {
+            names.forEach {
+                join(it)
+            }
+        }
+
+        fun dealerStrategy(strategy: DealerStrategyType) {
+            when (strategy) {
+                DealerStrategyType.DEFAULT_DEALER_STRATEGY -> dealerStrategy = DefaultDealerStrategy()
+                // 다른 전략 추가
+            }
+        }
+
+        fun build(): BlackjackGame {
+            return BlackjackGame(
+                players = players.toList(),
+                dealer = Dealer(dealerStrategy = dealerStrategy)
+            )
+        }
+    }
+}
+
+enum class DealerStrategyType {
+    DEFAULT_DEALER_STRATEGY
+}
+
+fun blackjackOpen(block: BlackjackGame.BlackjackGameBuilder.() -> Unit): BlackjackGame =
+    BlackjackGame.BlackjackGameBuilder().apply(block).build()
diff --git a/domain/src/main/kotlin/blackjack/game/BlackjackResult.kt b/domain/src/main/kotlin/blackjack/game/BlackjackResult.kt
new file mode 100644
index 0000000000..9056c754e6
--- /dev/null
+++ b/domain/src/main/kotlin/blackjack/game/BlackjackResult.kt
@@ -0,0 +1,6 @@
+package blackjack.game
+
+data class BlackjackResult(
+    val win: Int,
+    val lose: Int,
+)
diff --git a/domain/src/main/kotlin/blackjack/game/GameState.kt b/domain/src/main/kotlin/blackjack/game/GameState.kt
new file mode 100644
index 0000000000..aca64a967d
--- /dev/null
+++ b/domain/src/main/kotlin/blackjack/game/GameState.kt
@@ -0,0 +1,33 @@
+package blackjack.game
+
+import blackjack.dealer.Dealer
+import blackjack.player.Player
+
+sealed class GameState(
+    val players: List<Player>,
+    val dealer: Dealer,
+) {
+    class InitialDeal(
+        players: List<Player>,
+        dealer: Dealer,
+    ) : GameState(players, dealer)
+
+    class PlayerTurn(
+        players: List<Player>,
+        dealer: Dealer,
+        val currentPlayerIndex: Int,
+    ) : GameState(players, dealer) {
+        val currentPlayer: Player
+            get() = players[currentPlayerIndex]
+    }
+
+    class DealerTurn(
+        players: List<Player>,
+        dealer: Dealer,
+    ) : GameState(players, dealer)
+
+    class End(
+        players: List<Player>,
+        dealer: Dealer,
+    ) : GameState(players, dealer)
+}
diff --git a/domain/src/main/kotlin/blackjack/hand/Hand.kt b/domain/src/main/kotlin/blackjack/hand/Hand.kt
index 3c57027b42..b527880bb0 100644
--- a/domain/src/main/kotlin/blackjack/hand/Hand.kt
+++ b/domain/src/main/kotlin/blackjack/hand/Hand.kt
@@ -1,36 +1,18 @@
 package blackjack.hand
 
 import blackjack.card.Card
-import blackjack.card.CardRank
 
-data class Hand(
-    val cards: List<Card> = emptyList()
-) {
-    fun addCard(card: Card): Hand = copy(cards = cards + card)
-
-    fun calculateBestValue(): Int {
-        val sumWithoutAces = cards.filter { it.rank != CardRank.ACE }.sumOf { cardValue(it) }
-        val aceCount = cards.count { it.rank == CardRank.ACE }
-        return calculateBestAceValue(sumWithoutAces, aceCount)
-    }
-
-    private fun cardValue(card: Card): Int = when (card.rank) {
-        CardRank.KING, CardRank.QUEEN, CardRank.JACK -> FACE_CARD_VALUE
-        else -> card.rank.ordinal + 1
-    }
-
-    private fun calculateBestAceValue(sumWithoutAces: Int, aceCount: Int): Int {
-        var sum = sumWithoutAces
-        repeat(aceCount) {
-            sum += if (sum + ACE_HIGH_VALUE > MAX_HAND_VALUE) ACE_LOW_VALUE else ACE_HIGH_VALUE
-        }
-        return sum
-    }
+interface Hand {
+    fun cards(): List<Card>
+    fun addCard(card: Card): Hand
+    fun addCard(cards: List<Card>): Hand
+    fun calculateMinValue(): Int
+    fun calculateBestValue(): Int
 
     companion object {
-        private const val FACE_CARD_VALUE = 10
-        private const val MAX_HAND_VALUE = 21
-        private const val ACE_HIGH_VALUE = 11
-        private const val ACE_LOW_VALUE = 1
+        const val FACE_CARD_VALUE = 10
+        const val MAX_HAND_VALUE = 21
+        const val ACE_HIGH_VALUE = 11
+        const val ACE_LOW_VALUE = 1
     }
 }
diff --git a/domain/src/main/kotlin/blackjack/hand/StandardHand.kt b/domain/src/main/kotlin/blackjack/hand/StandardHand.kt
new file mode 100644
index 0000000000..3c245bef5c
--- /dev/null
+++ b/domain/src/main/kotlin/blackjack/hand/StandardHand.kt
@@ -0,0 +1,51 @@
+package blackjack.hand
+
+import blackjack.card.Card
+import blackjack.card.CardRank
+import blackjack.hand.Hand.Companion.ACE_HIGH_VALUE
+import blackjack.hand.Hand.Companion.ACE_LOW_VALUE
+import blackjack.hand.Hand.Companion.FACE_CARD_VALUE
+import blackjack.hand.Hand.Companion.MAX_HAND_VALUE
+
+internal class StandardHand(
+    val cards: Set<Card> = emptySet()
+) : Hand {
+    override fun cards(): List<Card> = cards.toList()
+
+    override fun addCard(card: Card): StandardHand = StandardHand(cards = cards + card)
+
+    override fun addCard(cards: List<Card>): StandardHand = StandardHand(cards = this.cards + cards)
+
+    override fun calculateBestValue(): Int {
+        val sumWithoutAces = cards.filter { it.rank != CardRank.ACE }.sumOf { cardValue(it) }
+        val aceCount = cards.count { it.rank == CardRank.ACE }
+        return calculateBestAceValue(sumWithoutAces, aceCount)
+    }
+
+    override fun calculateMinValue(): Int {
+        val sumWithoutAces = cards.filter { it.rank != CardRank.ACE }.sumOf { cardValue(it) }
+        val aceCount = cards.count { it.rank == CardRank.ACE }
+        return calculateMinAceValue(sumWithoutAces, aceCount)
+    }
+
+    private fun cardValue(card: Card): Int = when (card.rank) {
+        CardRank.KING, CardRank.QUEEN, CardRank.JACK -> FACE_CARD_VALUE
+        else -> card.rank.ordinal + 1
+    }
+
+    private fun calculateBestAceValue(sumWithoutAces: Int, aceCount: Int): Int {
+        var sum = sumWithoutAces
+        repeat(aceCount) {
+            sum += if (sum + ACE_HIGH_VALUE > MAX_HAND_VALUE) ACE_LOW_VALUE else ACE_HIGH_VALUE
+        }
+        return sum
+    }
+
+    private fun calculateMinAceValue(sumWithoutAces: Int, aceCount: Int): Int {
+        var sum = sumWithoutAces
+        repeat(aceCount) {
+            sum += ACE_LOW_VALUE
+        }
+        return sum
+    }
+}
diff --git a/domain/src/main/kotlin/blackjack/player/Player.kt b/domain/src/main/kotlin/blackjack/player/Player.kt
index 0aa382d713..99d2b42355 100644
--- a/domain/src/main/kotlin/blackjack/player/Player.kt
+++ b/domain/src/main/kotlin/blackjack/player/Player.kt
@@ -1,18 +1,27 @@
 package blackjack.player
 
+import action.BlackJackAction
+import blackjack.BlackjackParticipant
 import blackjack.card.Card
 import blackjack.hand.Hand
+import blackjack.hand.StandardHand
 
 data class Player(
     val name: String,
-    private val hand: Hand,
-) {
-    val cards: List<Card>
-        get() = hand.cards
+    private val hand: Hand = StandardHand()
+) : BlackjackParticipant {
 
-    fun canReceiveCard(): Boolean = hand.calculateBestValue() <= 21
-    fun receiveCard(card: Card): Player {
-        return copy(hand = hand.addCard(card))
+    val cards: List<Card> get() = hand.cards()
+
+    fun canHit(): BlackJackAction = if (hand.calculateMinValue() <= 21) {
+        BlackJackAction.HIT
+    } else {
+        BlackJackAction.STAND
     }
-    fun calculateBestValue(): Int = hand.calculateBestValue()
+
+    override fun receiveCard(card: Card): Player = copy(hand = hand.addCard(card))
+
+    override fun receiveCard(cards: List<Card>): Player = copy(hand = hand.addCard(cards))
+
+    override fun calculateBestValue(): Int = hand.calculateBestValue()
 }
diff --git a/domain/src/test/kotlin/blackjack/dealer/DealerTest.kt b/domain/src/test/kotlin/blackjack/dealer/DealerTest.kt
new file mode 100644
index 0000000000..f4d6f3cd32
--- /dev/null
+++ b/domain/src/test/kotlin/blackjack/dealer/DealerTest.kt
@@ -0,0 +1,37 @@
+package blackjack.dealer
+
+import action.BlackJackAction
+import blackjack.card.Card
+import blackjack.card.CardRank
+import blackjack.card.CardSuit
+import blackjack.deck.Deck
+import blackjack.hand.StandardHand
+import io.kotest.core.spec.style.FunSpec
+import io.kotest.matchers.shouldBe
+
+class DealerTest : FunSpec({
+
+    test("처음 딜러의 손패 수는 0이다.") {
+        val dealer = Dealer(hand = StandardHand())
+        dealer.cards.size shouldBe 0
+    }
+
+    test("손패의 수가 0일 때 결정할 액션은 HIT이다") {
+        val dealer = Dealer(hand = StandardHand())
+        dealer.decideAction(deck = Deck()) shouldBe BlackJackAction.HIT
+    }
+
+    test("딜러는 카드를 받으면 손패의 수가 1 증가한다.") {
+        Dealer(hand = StandardHand()).also {
+            it.cards.size shouldBe 0
+        }.receiveCard(card = Card(suit = CardSuit.CLUBS, rank = CardRank.ACE))
+            .cards.size shouldBe 1
+    }
+
+    test("ACE와 JACK을 가지고 있을 때, 베스트는 21이다.") {
+        Dealer(hand = StandardHand())
+            .receiveCard(card = Card(suit = CardSuit.CLUBS, rank = CardRank.ACE))
+            .receiveCard(card = Card(suit = CardSuit.DIAMONDS, rank = CardRank.JACK))
+            .calculateBestValue() shouldBe 21
+    }
+})
diff --git a/domain/src/test/kotlin/blackjack/deck/DeckTest.kt b/domain/src/test/kotlin/blackjack/deck/DeckTest.kt
index aa798f3417..a13fe5892a 100644
--- a/domain/src/test/kotlin/blackjack/deck/DeckTest.kt
+++ b/domain/src/test/kotlin/blackjack/deck/DeckTest.kt
@@ -17,9 +17,9 @@ class DeckTest : StringSpec({
 
     "drawCard 함수가 실행되면 덱의 사이즈가 1 줄어든다." {
         val deck = Deck(StandardCardProvider(), RandomCardShuffler())
-        val initialSize = deck.size
+        val initialSize = deck.remainingCards.size
         deck.drawCard()
-        deck.size shouldBe (initialSize - 1)
+        deck.remainingCards.size shouldBe (initialSize - 1)
     }
 
     "덱이 비어있을 때 카드를 뽑으려고 하면 예외가 발생해야 한다" {
diff --git a/domain/src/test/kotlin/blackjack/game/BlackjackGameTest.kt b/domain/src/test/kotlin/blackjack/game/BlackjackGameTest.kt
new file mode 100644
index 0000000000..fcf84a3c4b
--- /dev/null
+++ b/domain/src/test/kotlin/blackjack/game/BlackjackGameTest.kt
@@ -0,0 +1,104 @@
+package blackjack.game
+
+import action.BlackJackAction
+import blackjack.dealer.DefaultDealerStrategy
+import io.kotest.assertions.throwables.shouldThrow
+import io.kotest.core.spec.style.StringSpec
+import io.kotest.matchers.collections.shouldContainExactly
+import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
+import io.kotest.matchers.collections.shouldHaveSize
+import io.kotest.matchers.shouldBe
+import io.kotest.matchers.shouldNotBe
+import io.kotest.matchers.throwable.shouldHaveMessage
+import io.kotest.matchers.types.shouldBeInstanceOf
+
+class BlackjackGameTest : StringSpec({
+
+    "플레이어 추가 확인" {
+        val game = blackjackOpen {
+            join("Alice")
+            join("Bob")
+        }
+        game.players.map { it.name } shouldContainExactly listOf("Alice", "Bob")
+    }
+
+    "딜러 전략 설정 확인" {
+        val game = blackjackOpen {
+            join("Alice")
+            join("Bob")
+            dealerStrategy(DealerStrategyType.DEFAULT_DEALER_STRATEGY)
+        }
+        game.dealer.dealerStrategy.shouldBeInstanceOf<DefaultDealerStrategy>()
+    }
+
+    "게임 인스턴스 생성 확인" {
+        val game = blackjackOpen {
+            join("Charlie")
+        }
+        game shouldNotBe null
+    }
+
+    "플레이어 없이 게임 생성 시 예외 발생 확인" {
+        val exception = shouldThrow<IllegalArgumentException> {
+            blackjackOpen { }
+        }
+        exception shouldHaveMessage "플레이어가 최소 한 명은 존재해야 합니다."
+    }
+
+    "초기 카드 배분 확인" {
+        val game = blackjackOpen {
+            join("Alice")
+            join("Bob")
+        }
+        game.dealInitialCards()
+
+        game.players.forEach { player ->
+            player.cards shouldHaveSize 2
+        }
+        game.dealer.cards shouldHaveSize 2
+    }
+
+    "플레이어 턴 처리 확인" {
+        val game = blackjackOpen {
+            join("Alice")
+            join("Bob")
+        }
+        game.dealInitialCards()
+        val alice = game.players.first { it.name == "Alice" }
+
+        // Alice가 턴을 끝냄
+        game.dealPlayerTurn(alice, false)
+        alice.cards shouldHaveSize 2
+
+        // Bob의 턴으로 넘어감
+        val bob = game.players.first { it.name == "Bob" }
+        (game.state as GameState.PlayerTurn).currentPlayer shouldBe bob
+    }
+
+    "딜러 턴 처리 확인" {
+        val game = blackjackOpen {
+            join("Alice")
+            join("Bob")
+        }
+        game.dealInitialCards()
+        // 모든 플레이어의 턴을 종료
+        game.players.forEach { game.dealPlayerTurn(it, false) }
+
+        // 딜러 턴 시작
+        game.state.shouldBeInstanceOf<GameState.DealerTurn>()
+    }
+
+    "게임 결과 계산 확인" {
+        val game = blackjackOpen {
+            join("Alice")
+            join("Bob")
+        }
+        game.dealInitialCards()
+        // 모든 턴 종료
+        game.players.forEach { game.dealPlayerTurn(it, false) }
+        game.dealDealerTurn()
+
+        val results = game.calculateResult()
+        results.keys shouldContainExactlyInAnyOrder game.players + game.dealer
+    }
+})
diff --git a/domain/src/test/kotlin/blackjack/hand/HandTest.kt b/domain/src/test/kotlin/blackjack/hand/HandTest.kt
index 5cda518013..32c161f2b8 100644
--- a/domain/src/test/kotlin/blackjack/hand/HandTest.kt
+++ b/domain/src/test/kotlin/blackjack/hand/HandTest.kt
@@ -8,8 +8,8 @@ import io.kotest.matchers.shouldBe
 
 class HandTest : FunSpec({
     test("손패가 Ace, 10으로 이뤄져 있다면 21로 계산한다.") {
-        val hand = Hand(
-            cards = listOf(
+        val hand = StandardHand(
+            cards = setOf(
                 Card(suit = CardSuit.CLUBS, rank = CardRank.ACE),
                 Card(suit = CardSuit.HEARTS, rank = CardRank.TEN)
             )
@@ -19,8 +19,8 @@ class HandTest : FunSpec({
     }
 
     test("손패가 Ace, 10, 2으로 이뤄져 있다면 13으로 계산한다.") {
-        val hand = Hand(
-            cards = listOf(
+        val hand = StandardHand(
+            cards = setOf(
                 Card(suit = CardSuit.CLUBS, rank = CardRank.ACE),
                 Card(suit = CardSuit.HEARTS, rank = CardRank.TEN),
                 Card(suit = CardSuit.HEARTS, rank = CardRank.TWO),
diff --git a/domain/src/test/kotlin/blackjack/player/PlayerTest.kt b/domain/src/test/kotlin/blackjack/player/PlayerTest.kt
index e659735123..762fda39ef 100644
--- a/domain/src/test/kotlin/blackjack/player/PlayerTest.kt
+++ b/domain/src/test/kotlin/blackjack/player/PlayerTest.kt
@@ -1,30 +1,30 @@
 package blackjack.player
 
+import action.BlackJackAction
 import blackjack.card.Card
 import blackjack.card.CardRank
 import blackjack.card.CardSuit
-import blackjack.hand.Hand
+import blackjack.hand.StandardHand
 import io.kotest.core.spec.style.StringSpec
-import io.kotest.matchers.booleans.shouldBeTrue
 import io.kotest.matchers.shouldBe
 
 class PlayerTest : StringSpec({
 
     "플레이어는 핸드의 최고 값이 21 이하일 때 카드를 뽑을 수 있어야 한다" {
-        val hand = Hand(listOf(Card(CardSuit.SPADES, CardRank.ACE)))
+        val hand = StandardHand(setOf(Card(CardSuit.SPADES, CardRank.ACE)))
         val player = Player("테스터", hand)
-        player.canReceiveCard().shouldBeTrue()
+        player.canHit() shouldBe BlackJackAction.HIT
     }
 
     "플레이어가 카드를 뽑을 때 플레이어의 핸드가 업데이트 되어야 한다" {
-        val player = Player("테스터", Hand())
+        val player = Player("테스터", StandardHand())
         player.cards.size shouldBe 0
         val newPlayer = player.receiveCard(card = Card(CardSuit.SPADES, CardRank.ACE))
         newPlayer.cards.size shouldBe 1
     }
 
     "플레이어의 최고 값 계산이 올바르게 수행되어야 한다: A스페이드 + 10다이아는 21이다." {
-        val hand = Hand(listOf(Card(CardSuit.SPADES, CardRank.ACE), Card(CardSuit.DIAMONDS, CardRank.TEN)))
+        val hand = StandardHand(setOf(Card(CardSuit.SPADES, CardRank.ACE), Card(CardSuit.DIAMONDS, CardRank.TEN)))
         val player = Player("테스터", hand)
         player.calculateBestValue() shouldBe 21
     }
diff --git a/presenter/src/main/kotlin/model/BlackjackParticipants.kt b/presenter/src/main/kotlin/model/BlackjackParticipants.kt
new file mode 100644
index 0000000000..e13d2385ef
--- /dev/null
+++ b/presenter/src/main/kotlin/model/BlackjackParticipants.kt
@@ -0,0 +1,9 @@
+package model
+
+import blackjack.dealer.Dealer
+import blackjack.player.Player
+
+data class BlackjackParticipants(
+    val dealer: Dealer,
+    val players: List<Player>
+)
diff --git a/presenter/src/main/kotlin/ui/Main.kt b/presenter/src/main/kotlin/ui/Main.kt
index fac17ff9ca..b3efab04d8 100644
--- a/presenter/src/main/kotlin/ui/Main.kt
+++ b/presenter/src/main/kotlin/ui/Main.kt
@@ -1,8 +1,9 @@
 package ui
 
-import blackjack.deck.Deck
-import blackjack.hand.Hand
-import blackjack.player.Player
+import blackjack.game.BlackjackGame
+import blackjack.game.DealerStrategyType
+import blackjack.game.GameState
+import blackjack.game.blackjackOpen
 import ui.input.InputView
 import ui.result.ResultView
 
@@ -11,30 +12,56 @@ fun main() {
     val resultView = ResultView()
     val playerNames = inputView.inputPlayerNames()
 
-    val deck = Deck()
-    val players = playerNames.map { Player(it, Hand()) }
+    val blackjackGame = blackjackOpen {
+        join(playerNames)
+        dealerStrategy(DealerStrategyType.DEFAULT_DEALER_STRATEGY)
+    }
 
-    playBlackjack(deck, players, inputView, resultView)
+    while (blackjackGame.state !is GameState.End) {
+        processGameState(blackjackGame, inputView, resultView)
+    }
+    processGameState(blackjackGame, inputView, resultView)
 }
 
-fun playBlackjack(deck: Deck, players: List<Player>, inputView: InputView, resultView: ResultView) {
-    val playingPlayers = resultView.showInitialCards(deck, players).toMutableList()
-    println()
-
-    playingPlayers.forEach { player ->
-        handlePlayerTurn(deck, player, inputView, resultView)
+private fun processGameState(blackjackGame: BlackjackGame, inputView: InputView, resultView: ResultView) {
+    when (val gameState = blackjackGame.state) {
+        is GameState.PlayerTurn -> processPlayerTurn(gameState, blackjackGame, inputView, resultView)
+        is GameState.DealerTurn -> processDealerTurn(blackjackGame, resultView)
+        is GameState.InitialDeal -> processInitialDeal(blackjackGame, resultView)
+        is GameState.End -> processGameEnd(blackjackGame, resultView)
     }
+}
 
-    resultView.showFinalResults(playingPlayers)
+private fun processPlayerTurn(
+    gameState: GameState.PlayerTurn,
+    blackjackGame: BlackjackGame,
+    inputView: InputView,
+    resultView: ResultView
+) {
+    val currentPlayer = gameState.currentPlayer
+    val isDeal = inputView.askForAdditionalCard(currentPlayer.name)
+    blackjackGame.dealPlayerTurn(currentPlayer, isDeal)
+    resultView.showCards(currentPlayer.name, blackjackGame.showPlayerCards(currentPlayer.name))
 }
 
-fun handlePlayerTurn(deck: Deck, player: Player, inputView: InputView, resultView: ResultView) {
-    while (player.canReceiveCard()) {
-        if (!inputView.askForAdditionalCard(player.name)) {
-            return
-        }
-        player.drawCard(deck)
-        resultView.showHandCards(player)
-    }
+private fun processDealerTurn(blackjackGame: BlackjackGame, resultView: ResultView) {
+    val action = blackjackGame.dealDealerTurn()
+    resultView.showDealerTurn(action)
+}
+
+private fun processInitialDeal(blackjackGame: BlackjackGame, resultView: ResultView) {
+    blackjackGame.dealInitialCards()
+    resultView.showInitialDealMessage(blackjackGame.players)
+    resultView.showCards(blackjackGame.dealer)
+    resultView.showCards(blackjackGame.players)
+}
+
+private fun processGameEnd(blackjackGame: BlackjackGame, resultView: ResultView) {
     println()
+    val result = blackjackGame.calculateResult()
+
+    resultView.showCards(blackjackGame.dealer)
+    blackjackGame.players.forEach { resultView.showCards(it) }
+
+    resultView.showParticipantsRecord(blackjackGame, result)
 }
diff --git a/presenter/src/main/kotlin/ui/input/InputView.kt b/presenter/src/main/kotlin/ui/input/InputView.kt
index 72902bcc40..bd136b9f66 100644
--- a/presenter/src/main/kotlin/ui/input/InputView.kt
+++ b/presenter/src/main/kotlin/ui/input/InputView.kt
@@ -1,10 +1,11 @@
 package ui.input
 
 class InputView {
-
     fun inputPlayerNames(): List<String> {
         println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)")
         val input = readln()
+        println()
+        input.ifBlank { throw IllegalArgumentException("한 명이라도 입력해야 합니다.") }
         return input.split(",").map { it.trim() }
     }
 
diff --git a/presenter/src/main/kotlin/ui/result/ResultView.kt b/presenter/src/main/kotlin/ui/result/ResultView.kt
index 3eaf02cb91..a11a930fc9 100644
--- a/presenter/src/main/kotlin/ui/result/ResultView.kt
+++ b/presenter/src/main/kotlin/ui/result/ResultView.kt
@@ -1,31 +1,61 @@
 package ui.result
 
-import blackjack.deck.Deck
+import action.BlackJackAction
+import blackjack.BlackjackParticipant
+import blackjack.card.Card
+import blackjack.dealer.Dealer
+import blackjack.game.BlackjackGame
+import blackjack.game.BlackjackResult
 import blackjack.player.Player
 import toUiString
 
 class ResultView {
 
-    private fun showCardsForPlayer(player: Player) {
-        println("${player.name}카드: ${player.cards.joinToString(", ") { it.toUiString() }}")
+    fun showCards(cardOwner: String, cards: List<Card>) {
+        println("${cardOwner}카드: ${cards.joinToString(", ") { it.toUiString() }}")
     }
 
-    fun showInitialCards(deck: Deck, players: List<Player>): List<Player> {
-        val resultPlayers = players.take(2).map { it.receiveCard(deck.drawCard()).receiveCard(deck.drawCard()) }
+    fun showCards(participant: BlackjackParticipant) {
+        when (participant) {
+            is Dealer -> println("딜러 카드: ${participant.cards.joinToString(", ") { it.toUiString() }} - 결과: ${participant.calculateBestValue()}")
+            is Player -> println("${participant.name} 카드: ${participant.cards.joinToString(", ") { it.toUiString() }} - 결과: ${participant.calculateBestValue()}")
+        }
+    }
 
-        println("${resultPlayers.joinToString(", ") { it.name }}에게 2장의 나누었습니다.")
-        resultPlayers.forEach(this::showCardsForPlayer)
+    fun showCards(players: List<Player>) {
+        players.forEach(::showCards)
+        println()
+    }
 
-        return resultPlayers
+    fun showDealerTurn(dealerAction: BlackJackAction) {
+        println()
+        val message = if (dealerAction == BlackJackAction.HIT) {
+            "딜러는 16 이하라 1장 더 받습니다."
+        } else {
+            "딜러는 16을 초과하여 받지 않습니다."
+        }
+        println(message)
     }
 
-    fun showHandCards(player: Player) {
-        showCardsForPlayer(player)
+    fun showInitialDealMessage(players: List<Player>) {
+        println("딜러와 ${players.joinToString(", ") { it.name }}에게 2장의 카드를 나누었습니다.")
     }
 
-    fun showFinalResults(players: List<Player>) {
-        players.forEach { player ->
-            println("${player.name}카드: ${player.cards.joinToString(", ") { it.toUiString() }} - 결과: ${player.calculateBestValue()}")
+    fun showParticipantsRecord(blackjackGame: BlackjackGame, result: Map<BlackjackParticipant, BlackjackResult>) {
+        println()
+        println("## 최종 승패")
+        showDealerRecord(result[blackjackGame.dealer] ?: throw IllegalArgumentException())
+        blackjackGame.players.forEach {
+            showPlayerRecord(it, result[it] ?: throw IllegalArgumentException())
         }
     }
+
+    private fun showDealerRecord(dealerResult: BlackjackResult) {
+        println("딜러: ${dealerResult.win}승 ${dealerResult.lose}패")
+    }
+
+    private fun showPlayerRecord(player: Player, playerResult: BlackjackResult) {
+        val winOrLose = if (playerResult.win == 1) "승" else "패"
+        println("${player.name}: $winOrLose")
+    }
 }