diff --git a/README.md b/README.md
index dd6e2bb9a..7817229dc 100644
--- a/README.md
+++ b/README.md
@@ -20,4 +20,25 @@
 - [x] 덱에서 카드를 하나 꺼낸다.
 - [x] 유저는 카드 목록을 보유한다.
 - [x] 카드목록에서 점수를 계산한다.
-- [x] 카드를 덱에서 하나 뽑는다.
\ No newline at end of file
+- [x] 카드를 덱에서 하나 뽑는다.
+
+## Step3 - 블랙잭(딜러)
+
+### Step2 - 리뷰반영사항
+- [x] BlackJackGame::game table 이 users 와 deck 을 상태로서 관리하도록 수정
+- [x] BlackJackGame::start 메서드 분리
+- [x] BlackJackGame::유저목록을 받아 card 출력하도록 수정
+- [x] BlackJackGame::카드 히트 여부확인시 출력과 입력을 통합
+- [x] BlackJackGame::while 문 내부 if 절을 while 조건식으로 통합
+- [x] BlackJackGame::게임진행책임 분리
+- [x] BlackJackGame::receive 를 hit 로 메서드명 수정
+- [x] Rank::enum 클래스로 수정
+
+### 기능 구현사항
+- [x] 딜러는 처음받는 2장의 합계가 16이하면 반드시 1장의 카드를 추가로 받는다
+- [x] 딜러가 21을 초과하면 남은 플레이어들은 패에 상관없이 승리한다
+- [x] 게임 완료 후 각 플레이어별로 승패를 출력한다
+- [x] data class , 일반 클래스 설정 일관성 유지
+- [x] 도메인 패키지 분리
+- [x] 객체 상태를 객체가 관리
+- [x] 게임 관련 로직 분리
\ No newline at end of file
diff --git a/src/main/kotlin/blackjack/Main.kt b/src/main/kotlin/blackjack/Main.kt
index 865246b0a..c857254f0 100644
--- a/src/main/kotlin/blackjack/Main.kt
+++ b/src/main/kotlin/blackjack/Main.kt
@@ -1,10 +1,7 @@
 package blackjack
 
-import blackjack.controller.BlackJackGame
-import blackjack.domain.GameTable
-import blackjack.view.InputView
-import blackjack.view.ResultView
+import blackjack.controller.BlackjackGame
 
 fun main() {
-    BlackJackGame(GameTable, InputView, ResultView).start()
+    BlackjackGame.start()
 }
diff --git a/src/main/kotlin/blackjack/controller/BlackJackGame.kt b/src/main/kotlin/blackjack/controller/BlackJackGame.kt
deleted file mode 100644
index 3595c1016..000000000
--- a/src/main/kotlin/blackjack/controller/BlackJackGame.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package blackjack.controller
-
-import blackjack.domain.Deck
-import blackjack.domain.GameTable
-import blackjack.domain.User
-import blackjack.view.InputView
-import blackjack.view.ResultView
-
-class BlackJackGame(
-    private val gameTable: GameTable,
-    private val inputView: InputView,
-    private val resultView: ResultView,
-) {
-    fun start() {
-        val names = inputView.inputNames()
-        val deck = Deck.create()
-        val users = names.map { User.create(name = it) }
-
-        val initCardReceivedUsers = gameTable.dealInitCard(users, deck)
-
-        println()
-        resultView.printInitCardReceive(initCardReceivedUsers)
-        initCardReceivedUsers.forEach { resultView.printUserCards(user = it, printScore = false) }
-
-        val allCardReceivedUsers =
-            initCardReceivedUsers.map { user ->
-                var currentUser = user
-                while (true) {
-                    if (!currentUser.canReceiveCard()) {
-                        resultView.printCanNotReceivedCard()
-                        break
-                    }
-                    resultView.printAskReceiveMoreCard(currentUser)
-                    val moreCard = inputView.inputReceiveMoreCard()
-                    if (moreCard) {
-                        currentUser = currentUser.receiveCard(deck.draw())
-                        resultView.printUserCards(user = currentUser, printScore = false)
-                    } else {
-                        break
-                    }
-                }
-                currentUser
-            }
-
-        println()
-        allCardReceivedUsers.forEach { user ->
-            resultView.printUserCards(user = user, printScore = true)
-        }
-    }
-}
diff --git a/src/main/kotlin/blackjack/controller/BlackjackGame.kt b/src/main/kotlin/blackjack/controller/BlackjackGame.kt
new file mode 100644
index 000000000..c090e01c1
--- /dev/null
+++ b/src/main/kotlin/blackjack/controller/BlackjackGame.kt
@@ -0,0 +1,35 @@
+package blackjack.controller
+
+import blackjack.domain.card.Deck
+import blackjack.domain.player.Dealer
+import blackjack.domain.player.Player
+import blackjack.view.InputView
+import blackjack.view.ResultView
+
+object BlackjackGame {
+    fun start() {
+        val gameTable = setUp()
+        initDeal(gameTable)
+        turnStart(gameTable)
+        ResultView.printAfterTurn(gameTable)
+    }
+
+    private fun setUp(): GameTable {
+        val gameTable = GameTable(Deck(), Dealer(), getPlayers())
+        ResultView.linebreak()
+        return gameTable
+    }
+
+    private fun getPlayers(): List<Player> = InputView.inputNames().map { Player(it) }
+
+    private fun initDeal(gameTable: GameTable) {
+        gameTable.dealInitCard()
+        ResultView.printDealInitCard(gameTable)
+    }
+
+    private fun turnStart(gameTable: GameTable) {
+        gameTable.playersTurn()
+        ResultView.linebreak()
+        gameTable.dealerTurn()
+    }
+}
diff --git a/src/main/kotlin/blackjack/controller/GameTable.kt b/src/main/kotlin/blackjack/controller/GameTable.kt
new file mode 100644
index 000000000..4fa31a914
--- /dev/null
+++ b/src/main/kotlin/blackjack/controller/GameTable.kt
@@ -0,0 +1,46 @@
+package blackjack.controller
+
+import blackjack.domain.card.Deck
+import blackjack.domain.game.GameResult
+import blackjack.domain.player.Dealer
+import blackjack.domain.player.Player
+import blackjack.view.InputView
+import blackjack.view.ResultView
+
+class GameTable(
+    val deck: Deck,
+    val dealer: Dealer,
+    val players: List<Player>,
+) {
+    fun dealInitCard() =
+        repeat(INIT_CARD_DRAW_COUNT) {
+            dealer.hit(deck.draw())
+            players.forEach { it.hit(deck.draw()) }
+        }
+
+    fun playersTurn() = players.forEach { playerTurn(it) }
+
+    fun dealerTurn() {
+        if (!dealer.canHit()) {
+            return
+        }
+        ResultView.printDealerHit()
+        dealer.hit(deck.draw())
+        dealerTurn()
+    }
+
+    fun getGameResult(): GameResult = GameResult.from(dealer, players)
+
+    private fun playerTurn(player: Player) {
+        if (!player.canHit() || !InputView.inputHit(player)) {
+            return
+        }
+        player.hit(deck.draw())
+        ResultView.printPlayerCard(player)
+        playerTurn(player)
+    }
+
+    companion object {
+        const val INIT_CARD_DRAW_COUNT = 2
+    }
+}
diff --git a/src/main/kotlin/blackjack/domain/Card.kt b/src/main/kotlin/blackjack/domain/Card.kt
deleted file mode 100644
index c648d9a78..000000000
--- a/src/main/kotlin/blackjack/domain/Card.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package blackjack.domain
-
-import blackjack.domain.Rank.Companion.ACE
-
-data class Card(
-    val rank: Rank,
-    val suit: Suit,
-) {
-    val score = rank.score
-
-    fun isAce(): Boolean {
-        return rank == ACE
-    }
-
-    companion object {
-        val ALL: List<Card> =
-            Suit.entries.flatMap { suit -> Rank.ALL.map { rank -> Card(rank, suit) } }
-    }
-}
diff --git a/src/main/kotlin/blackjack/domain/Deck.kt b/src/main/kotlin/blackjack/domain/Deck.kt
deleted file mode 100644
index d6b33e27c..000000000
--- a/src/main/kotlin/blackjack/domain/Deck.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package blackjack.domain
-
-data class Deck(private val cards: MutableList<Card>) {
-    fun draw(): Card {
-        check(cards.isNotEmpty()) { "카드가 모두 소진되었습니다" }
-        return cards.removeFirst()
-    }
-
-    companion object {
-        fun create(): Deck {
-            return Deck(Card.ALL.shuffled().toMutableList())
-        }
-    }
-}
diff --git a/src/main/kotlin/blackjack/domain/GameTable.kt b/src/main/kotlin/blackjack/domain/GameTable.kt
deleted file mode 100644
index c56f577b2..000000000
--- a/src/main/kotlin/blackjack/domain/GameTable.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package blackjack.domain
-
-object GameTable {
-    const val INIT_CARD_DRAW_COUNT = 2
-
-    fun dealInitCard(
-        users: List<User>,
-        deck: Deck,
-    ): List<User> {
-        return users.map { user ->
-            (1..INIT_CARD_DRAW_COUNT).fold(user) { acc, _ ->
-                acc.receiveCard(deck.draw())
-            }
-        }
-    }
-}
diff --git a/src/main/kotlin/blackjack/domain/Rank.kt b/src/main/kotlin/blackjack/domain/Rank.kt
deleted file mode 100644
index 67049a95b..000000000
--- a/src/main/kotlin/blackjack/domain/Rank.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package blackjack.domain
-
-@JvmInline
-value class Rank(val value: String) {
-    init {
-        require(value in RANK_VALUES) { "랭크는 $RANK_VALUES 에 포함되어야 합니다" }
-    }
-
-    val score: Int
-        get() =
-            when (value) {
-                in NUMBER_VALUES -> value.toInt()
-                in FACE_VALUES -> FACE_SCORE
-                ACE_VALUE -> DEFAULT_ACE_SCORE
-                else -> throw IllegalArgumentException("유효하지 않은 랭크 값입니다")
-            }
-
-    companion object {
-        private const val FACE_SCORE = 10
-        private const val DEFAULT_ACE_SCORE = 11
-
-        private const val ACE_VALUE = "A"
-        private val NUMBER_VALUES = listOf("2", "3", "4", "5", "6", "7", "8", "9", "10")
-        private val FACE_VALUES = listOf("J", "Q", "K")
-
-        private val RANK_VALUES = NUMBER_VALUES + FACE_VALUES + ACE_VALUE
-
-        val ACE = Rank(ACE_VALUE)
-        val ALL = RANK_VALUES.map { Rank(it) }
-    }
-}
diff --git a/src/main/kotlin/blackjack/domain/User.kt b/src/main/kotlin/blackjack/domain/User.kt
deleted file mode 100644
index ceab91f53..000000000
--- a/src/main/kotlin/blackjack/domain/User.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package blackjack.domain
-
-data class User(
-    val name: String,
-    val cards: Cards,
-) {
-    fun canReceiveCard(): Boolean {
-        return cards.isScoreLowerThanLimit()
-    }
-
-    fun receiveCard(card: Card): User {
-        return this.copy(cards = cards.add(card))
-    }
-
-    companion object {
-        fun create(name: String): User {
-            return User(name, Cards(emptyList()))
-        }
-    }
-}
diff --git a/src/main/kotlin/blackjack/domain/card/Card.kt b/src/main/kotlin/blackjack/domain/card/Card.kt
new file mode 100644
index 000000000..49e38e89d
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/card/Card.kt
@@ -0,0 +1,19 @@
+package blackjack.domain.card
+
+import blackjack.domain.card.Rank.ACE
+
+data class Card(
+    val rank: Rank,
+    val suit: Suit,
+) {
+    val score: Int
+        get() = rank.score
+
+    val isAce: Boolean
+        get() = rank == ACE
+
+    companion object {
+        val ALL: List<Card> =
+            Suit.entries.flatMap { suit -> Rank.entries.map { rank -> Card(rank, suit) } }
+    }
+}
diff --git a/src/main/kotlin/blackjack/domain/card/Deck.kt b/src/main/kotlin/blackjack/domain/card/Deck.kt
new file mode 100644
index 000000000..6f20fd3ce
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/card/Deck.kt
@@ -0,0 +1,8 @@
+package blackjack.domain.card
+
+class Deck(private val cards: MutableList<Card> = Card.ALL.shuffled().toMutableList()) {
+    fun draw(): Card {
+        check(cards.isNotEmpty()) { "카드가 모두 소진되었습니다" }
+        return cards.removeFirst()
+    }
+}
diff --git a/src/main/kotlin/blackjack/domain/Cards.kt b/src/main/kotlin/blackjack/domain/card/Hand.kt
similarity index 58%
rename from src/main/kotlin/blackjack/domain/Cards.kt
rename to src/main/kotlin/blackjack/domain/card/Hand.kt
index 248a97f8b..bebed6e7d 100644
--- a/src/main/kotlin/blackjack/domain/Cards.kt
+++ b/src/main/kotlin/blackjack/domain/card/Hand.kt
@@ -1,20 +1,19 @@
-package blackjack.domain
+package blackjack.domain.card
+
+class Hand(private val _cards: MutableList<Card> = mutableListOf()) {
+    val cards: List<Card>
+        get() = _cards.toList()
 
-data class Cards(val values: List<Card>) {
     val score: Int
         get() = calculateScore()
 
-    fun isScoreLowerThanLimit(): Boolean {
-        return score < BLACKJACK_SCORE_LIMIT
-    }
-
-    fun add(card: Card): Cards {
-        return Cards(values + card)
+    fun add(card: Card) {
+        _cards.add(card)
     }
 
     private fun calculateScore(): Int {
-        val totalScore = values.sumOf { it.score }
-        var aceCount = values.count { it.isAce() }
+        val totalScore = cards.sumOf { it.score }
+        var aceCount = cards.count { it.isAce }
 
         var adjustedScore = totalScore
         while (adjustedScore > BLACKJACK_SCORE_LIMIT && aceCount > 0) {
diff --git a/src/main/kotlin/blackjack/domain/card/Rank.kt b/src/main/kotlin/blackjack/domain/card/Rank.kt
new file mode 100644
index 000000000..7b7e1ad82
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/card/Rank.kt
@@ -0,0 +1,24 @@
+package blackjack.domain.card
+
+enum class Rank(val value: String, val score: Int) {
+    ACE("A", 11),
+    TWO("2", 2),
+    THREE("3", 3),
+    FOUR("4", 4),
+    FIVE("5", 5),
+    SIX("6", 6),
+    SEVEN("7", 7),
+    EIGHT("8", 8),
+    NINE("9", 9),
+    TEN("10", 10),
+    JACK("J", 10),
+    QUEEN("Q", 10),
+    KING("K", 10),
+    ;
+
+    companion object {
+        fun from(value: String): Rank =
+            entries.firstOrNull { it.value == value }
+                ?: throw IllegalArgumentException("유효하지 않은 랭크 값입니다: $value")
+    }
+}
diff --git a/src/main/kotlin/blackjack/domain/Suit.kt b/src/main/kotlin/blackjack/domain/card/Suit.kt
similarity index 82%
rename from src/main/kotlin/blackjack/domain/Suit.kt
rename to src/main/kotlin/blackjack/domain/card/Suit.kt
index bc2d994b1..fd563f625 100644
--- a/src/main/kotlin/blackjack/domain/Suit.kt
+++ b/src/main/kotlin/blackjack/domain/card/Suit.kt
@@ -1,4 +1,4 @@
-package blackjack.domain
+package blackjack.domain.card
 
 enum class Suit(val description: String) {
     SPADE("스페이드"),
diff --git a/src/main/kotlin/blackjack/domain/game/GameResult.kt b/src/main/kotlin/blackjack/domain/game/GameResult.kt
new file mode 100644
index 000000000..cf7856903
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/game/GameResult.kt
@@ -0,0 +1,31 @@
+package blackjack.domain.game
+
+import blackjack.domain.game.MatchResult.DRAW
+import blackjack.domain.game.MatchResult.LOSE
+import blackjack.domain.game.MatchResult.WIN
+import blackjack.domain.game.dto.DealerGameResult
+import blackjack.domain.game.dto.PlayerGameResult
+import blackjack.domain.player.Dealer
+import blackjack.domain.player.Player
+
+data class GameResult(
+    val dealerGameResult: DealerGameResult,
+    val playerGameResults: List<PlayerGameResult>,
+) {
+    companion object {
+        fun from(
+            dealer: Dealer,
+            players: List<Player>,
+        ): GameResult {
+            val playerGameResults = players.map { player -> PlayerGameResult(player, player.matchHand(dealer)) }
+            return GameResult(
+                DealerGameResult(
+                    winCount = playerGameResults.count { it.result == LOSE },
+                    loseCount = playerGameResults.count { it.result == WIN },
+                    drawCount = playerGameResults.count { it.result == DRAW },
+                ),
+                playerGameResults,
+            )
+        }
+    }
+}
diff --git a/src/main/kotlin/blackjack/domain/game/MatchResult.kt b/src/main/kotlin/blackjack/domain/game/MatchResult.kt
new file mode 100644
index 000000000..fd60f033b
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/game/MatchResult.kt
@@ -0,0 +1,7 @@
+package blackjack.domain.game
+
+enum class MatchResult(val description: String) {
+    WIN("승"),
+    LOSE("패"),
+    DRAW("무"),
+}
diff --git a/src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt b/src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt
new file mode 100644
index 000000000..aa160e028
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/game/dto/DealerGameResult.kt
@@ -0,0 +1,7 @@
+package blackjack.domain.game.dto
+
+data class DealerGameResult(
+    val winCount: Int,
+    val loseCount: Int,
+    val drawCount: Int,
+)
diff --git a/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt b/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt
new file mode 100644
index 000000000..40e60dfc9
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/game/dto/PlayerGameResult.kt
@@ -0,0 +1,9 @@
+package blackjack.domain.game.dto
+
+import blackjack.domain.game.MatchResult
+import blackjack.domain.player.Player
+
+data class PlayerGameResult(
+    val player: Player,
+    val result: MatchResult,
+)
diff --git a/src/main/kotlin/blackjack/domain/player/Dealer.kt b/src/main/kotlin/blackjack/domain/player/Dealer.kt
new file mode 100644
index 000000000..c8d958b08
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/player/Dealer.kt
@@ -0,0 +1,16 @@
+package blackjack.domain.player
+
+import blackjack.domain.card.Card
+
+class Dealer : Player(
+    DEALER_NAME,
+) {
+    override fun canHit(): Boolean = hand.score < DEALER_SCORE_LIMIT
+
+    override fun hit(card: Card) = hand.add(card)
+
+    companion object {
+        private const val DEALER_NAME = "딜러"
+        private const val DEALER_SCORE_LIMIT = 17
+    }
+}
diff --git a/src/main/kotlin/blackjack/domain/player/Player.kt b/src/main/kotlin/blackjack/domain/player/Player.kt
new file mode 100644
index 000000000..ff465d03c
--- /dev/null
+++ b/src/main/kotlin/blackjack/domain/player/Player.kt
@@ -0,0 +1,42 @@
+package blackjack.domain.player
+
+import blackjack.domain.card.Card
+import blackjack.domain.card.Hand
+import blackjack.domain.game.MatchResult
+import blackjack.domain.game.MatchResult.DRAW
+import blackjack.domain.game.MatchResult.LOSE
+import blackjack.domain.game.MatchResult.WIN
+
+open class Player(
+    val name: String,
+    val hand: Hand = Hand(),
+) {
+    open fun canHit(): Boolean = hand.score < PLAYER_SCORE_LIMIT
+
+    open fun hit(card: Card) = hand.add(card)
+
+    fun matchHand(other: Player): MatchResult =
+        when {
+            other.isBust() -> WIN
+            this.isBust() -> LOSE
+            other.isBlackjack() && this.isBlackjack() -> DRAW
+            other.isBlackjack() -> LOSE
+            this.isBlackjack() -> WIN
+            else -> compareScore(other)
+        }
+
+    private fun compareScore(other: Player): MatchResult =
+        when {
+            this.hand.score > other.hand.score -> WIN
+            this.hand.score < other.hand.score -> LOSE
+            else -> DRAW
+        }
+
+    private fun isBust(): Boolean = hand.score > PLAYER_SCORE_LIMIT
+
+    private fun isBlackjack(): Boolean = hand.score == PLAYER_SCORE_LIMIT
+
+    companion object {
+        private const val PLAYER_SCORE_LIMIT = 21
+    }
+}
diff --git a/src/main/kotlin/blackjack/view/InputView.kt b/src/main/kotlin/blackjack/view/InputView.kt
index 12bbe7f90..79b34c263 100644
--- a/src/main/kotlin/blackjack/view/InputView.kt
+++ b/src/main/kotlin/blackjack/view/InputView.kt
@@ -1,5 +1,7 @@
 package blackjack.view
 
+import blackjack.domain.player.Player
+
 object InputView {
     fun inputNames(): List<String> {
         println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)")
@@ -10,7 +12,8 @@ object InputView {
         return names
     }
 
-    fun inputReceiveMoreCard(): Boolean {
+    fun inputHit(player: Player): Boolean {
+        println("${player.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)")
         return when (readlnOrNull()) {
             "y" -> true
             "n" -> false
diff --git a/src/main/kotlin/blackjack/view/ResultView.kt b/src/main/kotlin/blackjack/view/ResultView.kt
index 56430ad2c..80ec6a4d4 100644
--- a/src/main/kotlin/blackjack/view/ResultView.kt
+++ b/src/main/kotlin/blackjack/view/ResultView.kt
@@ -1,27 +1,57 @@
 package blackjack.view
 
-import blackjack.domain.GameTable.INIT_CARD_DRAW_COUNT
-import blackjack.domain.User
+import blackjack.controller.GameTable
+import blackjack.controller.GameTable.Companion.INIT_CARD_DRAW_COUNT
+import blackjack.domain.player.Player
 
 object ResultView {
-    fun printInitCardReceive(users: List<User>) {
-        println("${users.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.")
+    fun printDealerHit() = println("딜러는 16이하라 한장의 카드를 더 받았습니다.")
+
+    fun printDealInitCard(gameTable: GameTable) {
+        println("딜러와 ${gameTable.players.joinToString(", ") { it.name }}에게 ${INIT_CARD_DRAW_COUNT}장의 카드를 나누었습니다.")
+        printPlayerCard(gameTable.dealer)
+        printPlayersCard(gameTable.players)
+        linebreak()
+    }
+
+    fun printAfterTurn(gameTable: GameTable) {
+        printFinalHand(gameTable)
+        printGameResult(gameTable)
     }
 
-    fun printUserCards(
-        user: User,
-        printScore: Boolean,
+    fun printPlayerCard(
+        player: Player,
+        printScore: Boolean = false,
     ) {
-        val cards = user.cards.values.joinToString(", ") { "${it.rank.value}${it.suit.description}" }
-        val scoreText = "- 결과: ${user.cards.score}"
-        println("${user.name}카드: $cards ${if (printScore) scoreText else ""}")
+        val cards = player.hand.cards.joinToString(", ") { "${it.rank.value}${it.suit.description}" }
+        val scoreText = "- 결과: ${player.hand.score}"
+        println("${player.name} 카드: $cards ${if (printScore) scoreText else ""}")
     }
 
-    fun printAskReceiveMoreCard(user: User) {
-        println("${user.name}는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)")
+    fun linebreak() = println()
+
+    private fun printFinalHand(gameTable: GameTable) {
+        linebreak()
+        printPlayerCard(gameTable.dealer, printScore = true)
+        printPlayersCard(gameTable.players, printScore = true)
     }
 
-    fun printCanNotReceivedCard() {
-        println("더 이상 카드를 받을 수 없습니다")
+    private fun printGameResult(gameTable: GameTable) {
+        linebreak()
+        val gameResult = gameTable.getGameResult()
+        val winMessage =
+            if (gameResult.dealerGameResult.winCount > 0) "${gameResult.dealerGameResult.winCount}승" else ""
+        val lossMessage =
+            if (gameResult.dealerGameResult.loseCount > 0) "${gameResult.dealerGameResult.loseCount}패" else ""
+        val drawMessage =
+            if (gameResult.dealerGameResult.drawCount > 0) "${gameResult.dealerGameResult.drawCount}무" else ""
+        println("## 최종 승패")
+        println("딜러: $winMessage $lossMessage $drawMessage")
+        gameResult.playerGameResults.forEach { println("${it.player.name}: ${it.result.description}") }
     }
+
+    private fun printPlayersCard(
+        players: List<Player>,
+        printScore: Boolean = false,
+    ) = players.forEach { printPlayerCard(it, printScore) }
 }
diff --git a/src/test/kotlin/blackjack/domain/CardTest.kt b/src/test/kotlin/blackjack/domain/CardTest.kt
deleted file mode 100644
index 0a22b88f9..000000000
--- a/src/test/kotlin/blackjack/domain/CardTest.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package blackjack.domain
-
-import blackjack.domain.Suit.SPADE
-import io.kotest.core.spec.style.StringSpec
-import io.kotest.matchers.shouldBe
-
-class CardTest : StringSpec({
-    "카드는 에이스로 만들어진다면 에이스 카드이다" {
-        val card = Card(Rank("A"), SPADE)
-
-        card.isAce() shouldBe true
-    }
-
-    "카드는 스페이드로 만들어진다면 에이스 카드가 아니다" {
-        val card = Card(Rank("3"), SPADE)
-
-        card.isAce() shouldBe false
-    }
-})
diff --git a/src/test/kotlin/blackjack/domain/DeckTest.kt b/src/test/kotlin/blackjack/domain/DeckTest.kt
deleted file mode 100644
index bbcd5afbc..000000000
--- a/src/test/kotlin/blackjack/domain/DeckTest.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package blackjack.domain
-
-import io.kotest.assertions.throwables.shouldThrow
-import io.kotest.core.spec.style.StringSpec
-import io.kotest.matchers.shouldNotBe
-
-class DeckTest : StringSpec({
-    "덱에서 카드를 하나 꺼낸다" {
-        val deck = Deck.create()
-        val card = deck.draw()
-
-        card shouldNotBe null
-    }
-
-    "덱에 카드가 없을 때 카드를 꺼내면 예외 발생한다" {
-        val deck = Deck.create()
-        repeat(52) { deck.draw() }
-
-        shouldThrow<IllegalStateException> { deck.draw() }
-    }
-})
diff --git a/src/test/kotlin/blackjack/domain/GameTableTest.kt b/src/test/kotlin/blackjack/domain/GameTableTest.kt
deleted file mode 100644
index 44d328492..000000000
--- a/src/test/kotlin/blackjack/domain/GameTableTest.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package blackjack.domain
-
-import io.kotest.core.spec.style.StringSpec
-import io.kotest.matchers.shouldBe
-
-class GameTableTest : StringSpec({
-    "최초 딜 시 카드를 2장 나누어준다" {
-        val users =
-            listOf(
-                User("홍길동", Cards(emptyList())),
-                User("홍길덩", Cards(emptyList())),
-            )
-        val initCardReceivedUsers = GameTable.dealInitCard(users, Deck.create())
-
-        initCardReceivedUsers.forEach {
-            it.cards.values.size shouldBe 2
-        }
-    }
-})
diff --git a/src/test/kotlin/blackjack/domain/RankTest.kt b/src/test/kotlin/blackjack/domain/RankTest.kt
deleted file mode 100644
index 350438fd0..000000000
--- a/src/test/kotlin/blackjack/domain/RankTest.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package blackjack.domain
-
-import io.kotest.assertions.throwables.shouldThrow
-import io.kotest.core.spec.style.StringSpec
-import io.kotest.inspectors.forAll
-import io.kotest.matchers.shouldBe
-
-class RankTest : StringSpec({
-    "카드의 랭크가 2~10,J,Q,K,A가 아닌 경우 예외 발생한다" {
-        listOf("0", "1", "-1", "B", "D").forAll { invalidRank ->
-            shouldThrow<IllegalArgumentException> { Rank(invalidRank) }
-        }
-    }
-
-    "랭크가 숫자이면 점수는 숫자의 값으로 계산된다" {
-        listOf("2", "3", "4", "5", "6").forAll { number ->
-            Rank(number).score shouldBe number.toInt()
-        }
-    }
-
-    "랭크가 J,Q,K 이면 점수는 10점으로 계산된다" {
-        listOf("J", "Q", "K").forAll { face ->
-            Rank(face).score shouldBe 10
-        }
-    }
-
-    "랭크가 에이스이면 기본점수는 11점이다" {
-        Rank("A").score shouldBe 11
-    }
-})
diff --git a/src/test/kotlin/blackjack/domain/UserTest.kt b/src/test/kotlin/blackjack/domain/UserTest.kt
deleted file mode 100644
index ce7ef2cfe..000000000
--- a/src/test/kotlin/blackjack/domain/UserTest.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package blackjack.domain
-
-import blackjack.fixtures.createCard
-import io.kotest.core.spec.style.StringSpec
-import io.kotest.data.forAll
-import io.kotest.data.headers
-import io.kotest.data.row
-import io.kotest.data.table
-import io.kotest.matchers.shouldBe
-
-class UserTest : StringSpec({
-
-    "유저는 카드목록의 점수합이 21점 미만일 경우 카드를 더 받을 수 있다" {
-        table(
-            headers("ranks"),
-            row(listOf("2", "3", "4")),
-            row(listOf("4", "5", "10")),
-            row(listOf("K", "Q")),
-            row(listOf("5", "5", "4", "3", "2")),
-        ).forAll { ranks ->
-            val cards = Cards(ranks.map { createCard(it) })
-
-            User("홍길동", cards).canReceiveCard() shouldBe true
-        }
-    }
-
-    "유저는 카드목록의 점수합이 21점 이상할 경우 카드를 더 받을 수 없다" {
-        table(
-            headers("ranks", "score"),
-            row(listOf("J", "Q", "K"), 30),
-            row(listOf("A", "K"), 21),
-            row(listOf("Q", "10", "A"), 21),
-            row(listOf("10", "9", "2"), 21),
-            row(listOf("10", "10", "3"), 23),
-        ).forAll { ranks, score ->
-            val cards = Cards(ranks.map { createCard(it) })
-
-            cards.score shouldBe score
-            User("홍길동", cards).canReceiveCard() shouldBe false
-        }
-    }
-
-    "유저는 카드 2장을 받은 후 점수 합이 21점인 경우 카드를 더 받지 못한다" {
-        val cards = Cards(emptyList())
-        val user =
-            User("홍길동", cards)
-                .receiveCard(createCard("A"))
-                .receiveCard(createCard("K"))
-
-        user.canReceiveCard() shouldBe false
-    }
-
-    "유저는 카드 2장을 받은 후 점수 합이 21점 미만인 경우 카드를 더 받을 수 있다" {
-        val cards = Cards(emptyList())
-        val user =
-            User("홍길동", cards)
-                .receiveCard(createCard("10"))
-                .receiveCard(createCard("5"))
-
-        user.canReceiveCard() shouldBe true
-    }
-})
diff --git a/src/test/kotlin/blackjack/domain/card/DeckTest.kt b/src/test/kotlin/blackjack/domain/card/DeckTest.kt
new file mode 100644
index 000000000..b82dcc0d4
--- /dev/null
+++ b/src/test/kotlin/blackjack/domain/card/DeckTest.kt
@@ -0,0 +1,30 @@
+package blackjack.domain.card
+
+import blackjack.domain.card.Rank.ACE
+import blackjack.fixtures.createCard
+import io.kotest.assertions.throwables.shouldThrow
+import io.kotest.core.spec.style.BehaviorSpec
+import io.kotest.matchers.shouldBe
+
+class DeckTest : BehaviorSpec({
+    Given("덱에 카드가 존재하는 경우") {
+        val deck = Deck(mutableListOf(createCard("A")))
+
+        When("드로우하면") {
+            Then("카드를 반환한다") {
+                deck.draw().rank shouldBe ACE
+            }
+        }
+    }
+    Given("덱에 카드가 없는 경우") {
+        val deck = Deck(mutableListOf())
+
+        When("드로우하면") {
+            Then("예외 발생한다") {
+                shouldThrow<IllegalStateException> {
+                    deck.draw()
+                }
+            }
+        }
+    }
+})
diff --git a/src/test/kotlin/blackjack/domain/CardsTest.kt b/src/test/kotlin/blackjack/domain/card/HandTest.kt
similarity index 54%
rename from src/test/kotlin/blackjack/domain/CardsTest.kt
rename to src/test/kotlin/blackjack/domain/card/HandTest.kt
index 280d5844d..e53a937f6 100644
--- a/src/test/kotlin/blackjack/domain/CardsTest.kt
+++ b/src/test/kotlin/blackjack/domain/card/HandTest.kt
@@ -1,15 +1,15 @@
-package blackjack.domain
+package blackjack.domain.card
 
 import blackjack.fixtures.createCard
-import io.kotest.core.spec.style.StringSpec
+import io.kotest.core.spec.style.BehaviorSpec
 import io.kotest.data.forAll
 import io.kotest.data.headers
 import io.kotest.data.row
 import io.kotest.data.table
 import io.kotest.matchers.shouldBe
 
-class CardsTest : StringSpec({
-    "카드목록의 점수합이 21을 초과할 경우 에이스는 1점으로 보정된다" {
+class HandTest : BehaviorSpec({
+    Given("카드목록의 점수 합이 21점을 초과하는 경우") {
         table(
             headers("ranks", "expected"),
             row(listOf("A", "2", "10"), 13),
@@ -18,9 +18,13 @@ class CardsTest : StringSpec({
             row(listOf("A", "A", "A"), 13),
             row(listOf("A", "3", "9"), 13),
         ).forAll { ranks, expected ->
-            val cards = Cards(ranks.map { createCard(it) })
+            val hand = Hand(ranks.map { createCard(it) }.toMutableList())
 
-            cards.score shouldBe expected
+            When("점수 계산하면") {
+                Then("에이스는 1점으로 보정된다") {
+                    hand.score shouldBe expected
+                }
+            }
         }
     }
 })
diff --git a/src/test/kotlin/blackjack/domain/card/RankTest.kt b/src/test/kotlin/blackjack/domain/card/RankTest.kt
new file mode 100644
index 000000000..0d099ad8f
--- /dev/null
+++ b/src/test/kotlin/blackjack/domain/card/RankTest.kt
@@ -0,0 +1,30 @@
+package blackjack.domain.card
+
+import io.kotest.assertions.throwables.shouldThrow
+import io.kotest.core.spec.style.StringSpec
+import io.kotest.inspectors.forAll
+import io.kotest.matchers.shouldBe
+
+class RankTest : StringSpec({
+    "유효하지 않은 랭크값을 통해 랭크 생성시 예외 발생한다" {
+        listOf("0", "1", "-1", "B", "D").forAll { invalidRankValue ->
+            shouldThrow<IllegalArgumentException> { Rank.from(invalidRankValue) }
+        }
+    }
+
+    "랭크가 숫자이면 점수는 숫자의 값이다" {
+        listOf("2", "3", "4", "5", "6").forAll { number ->
+            Rank.from(number).score shouldBe number.toInt()
+        }
+    }
+
+    "랭크가 J,Q,K 이면 점수는 10점이다" {
+        listOf("J", "Q", "K").forAll { face ->
+            Rank.from(face).score shouldBe 10
+        }
+    }
+
+    "랭크가 에이스이면 기본점수는 11점이다" {
+        Rank.from("A").score shouldBe 11
+    }
+})
diff --git a/src/test/kotlin/blackjack/domain/game/GameResultTest.kt b/src/test/kotlin/blackjack/domain/game/GameResultTest.kt
new file mode 100644
index 000000000..0864293be
--- /dev/null
+++ b/src/test/kotlin/blackjack/domain/game/GameResultTest.kt
@@ -0,0 +1,66 @@
+package blackjack.domain.game
+
+import blackjack.domain.card.Suit.DIAMOND
+import blackjack.domain.card.Suit.HEART
+import blackjack.domain.card.Suit.SPADE
+import blackjack.domain.player.Dealer
+import blackjack.domain.player.Player
+import blackjack.fixtures.createCard
+import io.kotest.core.spec.style.BehaviorSpec
+import io.kotest.matchers.shouldBe
+
+class GameResultTest : BehaviorSpec({
+    Given("플레이어가 이긴 경우") {
+        val dealer = Dealer()
+        val player1 = Player("유저1")
+        val player2 = Player("유저2")
+
+        dealer.hit(createCard("3", SPADE))
+        player1.hit(createCard("4", HEART))
+        player2.hit(createCard("5", DIAMOND))
+
+        When("게임결과를 생성하면") {
+            val actual = GameResult.from(dealer, listOf(player1, player2))
+
+            Then("딜러의 패배 카운트가 증가한다") {
+                actual.dealerGameResult.loseCount shouldBe 2
+            }
+        }
+    }
+
+    Given("플레이어가 진 경우") {
+        val dealer = Dealer()
+        val player1 = Player("유저1")
+        val player2 = Player("유저2")
+
+        dealer.hit(createCard("10", SPADE))
+        player1.hit(createCard("4", HEART))
+        player2.hit(createCard("5", DIAMOND))
+
+        When("게임결과를 생성하면") {
+            val actual = GameResult.from(dealer, listOf(player1, player2))
+
+            Then("딜러의 승리 카운트가 증가한다") {
+                actual.dealerGameResult.winCount shouldBe 2
+            }
+        }
+    }
+
+    Given("플레이어가 딜러와 무승부인 경우") {
+        val dealer = Dealer()
+        val player1 = Player("유저1")
+        val player2 = Player("유저2")
+
+        dealer.hit(createCard("10", SPADE))
+        player1.hit(createCard("10", HEART))
+        player2.hit(createCard("10", DIAMOND))
+
+        When("게임결과를 생성하면") {
+            val actual = GameResult.from(dealer, listOf(player1, player2))
+
+            Then("딜러의 무승부 카운트가 증가한다") {
+                actual.dealerGameResult.drawCount shouldBe 2
+            }
+        }
+    }
+})
diff --git a/src/test/kotlin/blackjack/domain/player/DealerTest.kt b/src/test/kotlin/blackjack/domain/player/DealerTest.kt
new file mode 100644
index 000000000..7f4b9cb6e
--- /dev/null
+++ b/src/test/kotlin/blackjack/domain/player/DealerTest.kt
@@ -0,0 +1,42 @@
+package blackjack.domain.player
+
+import blackjack.fixtures.createCard
+import io.kotest.core.spec.style.BehaviorSpec
+import io.kotest.matchers.shouldBe
+
+class DealerTest : BehaviorSpec({
+    Given("딜러 히트하는 경우") {
+        val dealer = Dealer()
+
+        When("히트하면") {
+            dealer.hit(createCard("J"))
+
+            Then("카드를 추가로 갖는다") {
+                dealer.hand.cards.count() shouldBe 1
+            }
+        }
+    }
+
+    Given("딜러 핸드가 17점 이상인 경우") {
+        val dealer = Dealer()
+        dealer.hit(createCard("J"))
+        dealer.hit(createCard("Q"))
+
+        When("히트 가능 여부 질의하면") {
+            Then("결과 false 이다") {
+                dealer.canHit() shouldBe false
+            }
+        }
+    }
+
+    Given("딜러 핸드가 17점 미만인 경우") {
+        val dealer = Dealer()
+        dealer.hit(createCard("J"))
+
+        When("히트 가능 여부 질의하면") {
+            Then("결과는 true 이다") {
+                dealer.canHit() shouldBe true
+            }
+        }
+    }
+})
diff --git a/src/test/kotlin/blackjack/domain/player/PlayerTest.kt b/src/test/kotlin/blackjack/domain/player/PlayerTest.kt
new file mode 100644
index 000000000..6ddb5c533
--- /dev/null
+++ b/src/test/kotlin/blackjack/domain/player/PlayerTest.kt
@@ -0,0 +1,140 @@
+package blackjack.domain.player
+
+import blackjack.domain.card.Hand
+import blackjack.domain.card.Suit.DIAMOND
+import blackjack.domain.card.Suit.HEART
+import blackjack.domain.card.Suit.SPADE
+import blackjack.domain.game.MatchResult.DRAW
+import blackjack.domain.game.MatchResult.LOSE
+import blackjack.domain.game.MatchResult.WIN
+import blackjack.fixtures.createCard
+import io.kotest.core.spec.style.BehaviorSpec
+import io.kotest.data.forAll
+import io.kotest.data.headers
+import io.kotest.data.row
+import io.kotest.data.table
+import io.kotest.inspectors.forAll
+import io.kotest.matchers.shouldBe
+
+class PlayerTest : BehaviorSpec({
+    Given("플레이어 핸드가 21점 미만인 경우") {
+        table(
+            headers("ranks"),
+            row(listOf("2", "3", "4")),
+            row(listOf("4", "5", "10")),
+            row(listOf("K", "Q")),
+            row(listOf("5", "5", "4", "3", "2")),
+        ).forAll { ranks ->
+            val player = Player("홍길동", Hand(ranks.map { createCard(it) }.toMutableList()))
+
+            When("히트 가능 여부 질의하면") {
+                Then("결과는 true 이다") {
+                    player.canHit() shouldBe true
+                }
+            }
+        }
+    }
+
+    Given("플레이어 핸드가 21점 이상인 경우") {
+        table(
+            headers("ranks"),
+            row(listOf("J", "Q", "K")),
+            row(listOf("A", "K")),
+            row(listOf("Q", "10", "A")),
+            row(listOf("10", "9", "2")),
+            row(listOf("10", "10", "3")),
+        ).forAll { ranks ->
+            val player = Player("홍길동", Hand(ranks.map { createCard(it) }.toMutableList()))
+
+            When("히트 가능 여부 질의하면") {
+                Then("결과는 false 이다") {
+                    player.canHit() shouldBe false
+                }
+            }
+        }
+    }
+
+    Given("플레이어가 딜러보다 점수가 높은 경우") {
+        val dealer = Dealer()
+        val player1 = Player("유저1")
+        val player2 = Player("유저2")
+
+        dealer.hit(createCard("3"))
+
+        player1.hit(createCard("4"))
+        player2.hit(createCard("5"))
+
+        When("게임 결과 비교하면") {
+            Then("플레이어가 승리한다") {
+                listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN }
+            }
+        }
+    }
+
+    Given("플레이어가 딜러보다 점수가 낮은 경우") {
+        val dealer = Dealer()
+        val player1 = Player("유저1")
+        val player2 = Player("유저2")
+
+        dealer.hit(createCard("10"))
+
+        player1.hit(createCard("4"))
+        player2.hit(createCard("5"))
+
+        When("게임 결과 비교하면") {
+            Then("플레이어가 승리한다") {
+                listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe LOSE }
+            }
+        }
+    }
+
+    Given("플레이어가 딜러보다 점수가 동일한 경우") {
+        val dealer = Dealer()
+        val player1 = Player("유저1")
+        val player2 = Player("유저2")
+
+        dealer.hit(createCard("10", SPADE))
+
+        player1.hit(createCard("10", HEART))
+        player2.hit(createCard("10", DIAMOND))
+
+        When("게임 결과 비교하면") {
+            Then("플레이어가 승리한다") {
+                listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe DRAW }
+            }
+        }
+    }
+
+    Given("딜러가 버스트인 경우") {
+        val dealer = Dealer()
+        val player1 = Player("유저1")
+        val player2 = Player("유저2")
+
+        dealer.hit(createCard("K", SPADE))
+        dealer.hit(createCard("Q", SPADE))
+        dealer.hit(createCard("J", SPADE))
+
+        player1.hit(createCard("7", HEART))
+        player2.hit(createCard("8", DIAMOND))
+
+        When("게임 결과 비교하면") {
+            Then("모든 플레이어가 승리한다") {
+                listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN }
+            }
+        }
+
+        When("게임 결과 비교하면 플레이어 핸드가 21점을 초과해도") {
+            player1.hit(createCard("K", HEART))
+            player1.hit(createCard("Q", HEART))
+            player1.hit(createCard("J", HEART))
+
+            player2.hit(createCard("K", DIAMOND))
+            player2.hit(createCard("Q", DIAMOND))
+            player2.hit(createCard("J", DIAMOND))
+
+            Then("모든 플레이어가 승리한다") {
+                listOf(player1, player2).forAll { player -> player.matchHand(dealer) shouldBe WIN }
+            }
+        }
+    }
+})
diff --git a/src/test/kotlin/blackjack/fixtures/CardFixture.kt b/src/test/kotlin/blackjack/fixtures/CardFixture.kt
index 69494ab58..caf9c481b 100644
--- a/src/test/kotlin/blackjack/fixtures/CardFixture.kt
+++ b/src/test/kotlin/blackjack/fixtures/CardFixture.kt
@@ -1,13 +1,13 @@
 package blackjack.fixtures
 
-import blackjack.domain.Card
-import blackjack.domain.Rank
-import blackjack.domain.Suit
-import blackjack.domain.Suit.SPADE
+import blackjack.domain.card.Card
+import blackjack.domain.card.Rank
+import blackjack.domain.card.Suit
+import blackjack.domain.card.Suit.SPADE
 
 fun createCard(
     rank: String,
     suit: Suit = SPADE,
 ): Card {
-    return Card(Rank(rank), suit)
+    return Card(Rank.from(rank), suit)
 }