diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt
index 6c2f964..a404594 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanRepository.kt
@@ -27,6 +27,7 @@ interface CanQueryRepository {
     fun getCanUseStatus(member: Member, pageable: Pageable): List<UseCan>
     fun getCanChargeStatus(member: Member, pageable: Pageable, container: String): List<Charge>
     fun isExistPaidLiveRoom(memberId: Long, roomId: Long): UseCan?
+    fun getCanUsedForLiveRoomNotRefund(memberId: Long, roomId: Long): UseCan?
 }
 
 @Repository
@@ -111,4 +112,19 @@ class CanQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CanQue
             .orderBy(useCan.id.desc())
             .fetchFirst()
     }
+
+    override fun getCanUsedForLiveRoomNotRefund(memberId: Long, roomId: Long): UseCan? {
+        return queryFactory
+            .selectFrom(useCan)
+            .innerJoin(useCan.member, member)
+            .innerJoin(useCan.room, liveRoom)
+            .where(
+                member.id.eq(memberId)
+                    .and(liveRoom.id.eq(roomId))
+                    .and(useCan.canUsage.eq(CanUsage.LIVE))
+                    .and(useCan.isRefund.isFalse)
+            )
+            .orderBy(useCan.id.desc())
+            .fetchFirst()
+    }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeRepository.kt
index d9d02d6..d8d5bf4 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/charge/ChargeRepository.kt
@@ -1,7 +1,78 @@
 package kr.co.vividnext.sodalive.can.charge
 
+import com.querydsl.core.types.dsl.BooleanExpression
+import com.querydsl.jpa.impl.JPAQueryFactory
+import kr.co.vividnext.sodalive.can.charge.QCharge.charge
+import kr.co.vividnext.sodalive.can.payment.PaymentGateway
+import kr.co.vividnext.sodalive.can.payment.PaymentStatus
+import kr.co.vividnext.sodalive.can.payment.QPayment.payment
+import kr.co.vividnext.sodalive.member.QMember.member
 import org.springframework.data.jpa.repository.JpaRepository
 import org.springframework.stereotype.Repository
 
 @Repository
-interface ChargeRepository : JpaRepository<Charge, Long>
+interface ChargeRepository : JpaRepository<Charge, Long>, ChargeQueryRepository
+
+interface ChargeQueryRepository {
+    fun getOldestChargeWhereRewardCanGreaterThan0(chargeId: Long, memberId: Long, container: String): Charge?
+    fun getOldestChargeWhereChargeCanGreaterThan0(chargeId: Long, memberId: Long, container: String): Charge?
+}
+
+class ChargeQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : ChargeQueryRepository {
+    override fun getOldestChargeWhereRewardCanGreaterThan0(
+        chargeId: Long,
+        memberId: Long,
+        container: String
+    ): Charge? {
+        return queryFactory
+            .selectFrom(charge)
+            .innerJoin(charge.member, member)
+            .leftJoin(charge.payment, payment)
+            .where(
+                member.id.eq(memberId)
+                    .and(charge.rewardCan.gt(0))
+                    .and(charge.id.gt(chargeId))
+                    .and(payment.status.eq(PaymentStatus.COMPLETE))
+                    .and(getPaymentGatewayCondition(container))
+            )
+            .orderBy(charge.id.asc())
+            .fetchFirst()
+    }
+
+    override fun getOldestChargeWhereChargeCanGreaterThan0(
+        chargeId: Long,
+        memberId: Long,
+        container: String
+    ): Charge? {
+        return queryFactory
+            .selectFrom(charge)
+            .innerJoin(charge.member, member)
+            .leftJoin(charge.payment, payment)
+            .where(
+                member.id.eq(memberId)
+                    .and(charge.chargeCan.gt(0))
+                    .and(charge.id.gt(chargeId))
+                    .and(payment.status.eq(PaymentStatus.COMPLETE))
+                    .and(getPaymentGatewayCondition(container))
+            )
+            .orderBy(charge.id.asc())
+            .fetchFirst()
+    }
+
+    private fun getPaymentGatewayCondition(container: String): BooleanExpression? {
+        val paymentGatewayCondition = when (container) {
+            "aos" -> {
+                payment.paymentGateway.eq(PaymentGateway.PG)
+                    .or(payment.paymentGateway.eq(PaymentGateway.GOOGLE_IAP))
+            }
+
+            "ios" -> {
+                payment.paymentGateway.eq(PaymentGateway.PG)
+                    .or(payment.paymentGateway.eq(PaymentGateway.APPLE_IAP))
+            }
+
+            else -> payment.paymentGateway.eq(PaymentGateway.PG)
+        }
+        return paymentGatewayCondition
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt
new file mode 100644
index 0000000..434ced3
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt
@@ -0,0 +1,245 @@
+package kr.co.vividnext.sodalive.can.payment
+
+import kr.co.vividnext.sodalive.can.charge.ChargeRepository
+import kr.co.vividnext.sodalive.can.use.CanUsage
+import kr.co.vividnext.sodalive.can.use.SpentCan
+import kr.co.vividnext.sodalive.can.use.TotalSpentCan
+import kr.co.vividnext.sodalive.can.use.UseCan
+import kr.co.vividnext.sodalive.can.use.UseCanCalculate
+import kr.co.vividnext.sodalive.can.use.UseCanCalculateRepository
+import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
+import kr.co.vividnext.sodalive.can.use.UseCanRepository
+import kr.co.vividnext.sodalive.common.SodaException
+import kr.co.vividnext.sodalive.live.room.LiveRoom
+import kr.co.vividnext.sodalive.member.MemberRepository
+import org.springframework.data.repository.findByIdOrNull
+import org.springframework.stereotype.Service
+import org.springframework.transaction.annotation.Transactional
+
+@Service
+class CanPaymentService(
+    private val memberRepository: MemberRepository,
+    private val chargeRepository: ChargeRepository,
+    private val useCanRepository: UseCanRepository,
+    private val useCanCalculateRepository: UseCanCalculateRepository
+) {
+    @Transactional
+    fun spendCan(
+        memberId: Long,
+        needCan: Int,
+        canUsage: CanUsage,
+        liveRoom: LiveRoom? = null,
+        container: String
+    ) {
+        val member = memberRepository.findByIdOrNull(id = memberId)
+            ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
+        val useRewardCan = spendRewardCan(memberId, needCan, container)
+        val useChargeCan = if (needCan - useRewardCan.total > 0) {
+            spendChargeCan(memberId, needCan = needCan - useRewardCan.total, container = container)
+        } else {
+            null
+        }
+
+        if (needCan - useRewardCan.total - (useChargeCan?.total ?: 0) > 0) {
+            throw SodaException(
+                "${needCan - useRewardCan.total - (useChargeCan?.total ?: 0)} " +
+                    "캔이 부족합니다. 충전 후 이용해 주세요."
+            )
+        }
+
+        if (!useRewardCan.verify() || useChargeCan?.verify() == false) {
+            throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
+        }
+
+        val useCan = UseCan(
+            canUsage = canUsage,
+            can = useChargeCan?.total ?: 0,
+            rewardCan = useRewardCan.total
+        )
+
+        var recipientId: Long? = null
+        if (canUsage == CanUsage.LIVE && liveRoom != null) {
+            recipientId = liveRoom.member!!.id!!
+            useCan.room = liveRoom
+            useCan.member = member
+        } else if (canUsage == CanUsage.CHANGE_NICKNAME) {
+            useCan.member = member
+        } else if (canUsage == CanUsage.DONATION && liveRoom != null) {
+            recipientId = liveRoom.member!!.id!!
+            useCan.room = liveRoom
+            useCan.member = member
+        } else {
+            throw SodaException("잘못된 요청입니다.")
+        }
+
+        useCanRepository.save(useCan)
+
+        setUseCoinCalculate(recipientId, useRewardCan, useChargeCan, useCan, paymentGateway = PaymentGateway.PG)
+        setUseCoinCalculate(
+            recipientId,
+            useRewardCan,
+            useChargeCan,
+            useCan,
+            paymentGateway = PaymentGateway.GOOGLE_IAP
+        )
+        setUseCoinCalculate(
+            recipientId,
+            useRewardCan,
+            useChargeCan,
+            useCan,
+            paymentGateway = PaymentGateway.APPLE_IAP
+        )
+    }
+
+    private fun setUseCoinCalculate(
+        recipientId: Long?,
+        useRewardCan: TotalSpentCan,
+        useChargeCan: TotalSpentCan?,
+        useCan: UseCan,
+        paymentGateway: PaymentGateway
+    ) {
+        val totalSpentRewardCan = useRewardCan.spentCans
+            .filter { it.paymentGateway == paymentGateway }
+            .fold(0) { sum, spentCans -> sum + spentCans.can }
+
+        val useCanCalculate = if (useChargeCan != null) {
+            val totalSpentChargeCan = useChargeCan.spentCans
+                .filter { it.paymentGateway == paymentGateway }
+                .fold(0) { sum, spentCans -> sum + spentCans.can }
+
+            UseCanCalculate(
+                can = totalSpentChargeCan + totalSpentRewardCan,
+                paymentGateway = paymentGateway,
+                status = UseCanCalculateStatus.RECEIVED
+            )
+        } else {
+            UseCanCalculate(
+                can = totalSpentRewardCan,
+                paymentGateway = paymentGateway,
+                status = UseCanCalculateStatus.RECEIVED
+            )
+        }
+
+        if (useCanCalculate.can > 0) {
+            useCanCalculate.useCan = useCan
+            useCanCalculate.recipientCreatorId = recipientId
+            useCanCalculateRepository.save(useCanCalculate)
+        }
+    }
+
+    private fun spendRewardCan(memberId: Long, needCan: Int, container: String): TotalSpentCan {
+        return if (needCan > 0) {
+            val member = memberRepository.findByIdOrNull(id = memberId)
+                ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
+
+            val spentCans = mutableListOf<SpentCan>()
+            var chargeId = 0L
+            var total = 0
+
+            while (needCan - total > 0) {
+                val remainingNeedCan = needCan - total
+                val charge = chargeRepository.getOldestChargeWhereRewardCanGreaterThan0(chargeId, memberId, container)
+                    ?: break
+
+                if (charge.rewardCan >= remainingNeedCan) {
+                    charge.rewardCan -= remainingNeedCan
+
+                    when (charge.payment!!.paymentGateway) {
+                        PaymentGateway.PG -> member.pgRewardCan -= remainingNeedCan
+                        PaymentGateway.APPLE_IAP -> member.appleRewardCan -= remainingNeedCan
+                        PaymentGateway.GOOGLE_IAP -> member.pgRewardCan -= remainingNeedCan
+                    }
+
+                    total += remainingNeedCan
+
+                    spentCans.add(
+                        SpentCan(
+                            paymentGateway = charge.payment!!.paymentGateway,
+                            can = remainingNeedCan
+                        )
+                    )
+                } else {
+                    total += charge.rewardCan
+                    spentCans.add(
+                        SpentCan(
+                            paymentGateway = charge.payment!!.paymentGateway,
+                            can = charge.rewardCan
+                        )
+                    )
+
+                    when (charge.payment!!.paymentGateway) {
+                        PaymentGateway.PG -> member.pgRewardCan -= remainingNeedCan
+                        PaymentGateway.APPLE_IAP -> member.appleRewardCan -= remainingNeedCan
+                        PaymentGateway.GOOGLE_IAP -> member.pgRewardCan -= remainingNeedCan
+                    }
+
+                    charge.rewardCan = 0
+                }
+
+                chargeId = charge.id!!
+            }
+
+            TotalSpentCan(spentCans, total)
+        } else {
+            TotalSpentCan(total = 0)
+        }
+    }
+
+    private fun spendChargeCan(memberId: Long, needCan: Int, container: String): TotalSpentCan {
+        return if (needCan > 0) {
+            val member = memberRepository.findByIdOrNull(id = memberId)
+                ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
+
+            val spentCans = mutableListOf<SpentCan>()
+            var chargeId = 0L
+            var total = 0
+
+            while (needCan - total > 0) {
+                val remainingNeedCan = needCan - total
+                val charge = chargeRepository.getOldestChargeWhereChargeCanGreaterThan0(chargeId, memberId, container)
+                    ?: break
+
+                if (charge.rewardCan >= remainingNeedCan) {
+                    charge.rewardCan -= remainingNeedCan
+
+                    when (charge.payment!!.paymentGateway) {
+                        PaymentGateway.PG -> member.pgRewardCan -= remainingNeedCan
+                        PaymentGateway.APPLE_IAP -> member.appleRewardCan -= remainingNeedCan
+                        PaymentGateway.GOOGLE_IAP -> member.pgRewardCan -= remainingNeedCan
+                    }
+
+                    total += remainingNeedCan
+
+                    spentCans.add(
+                        SpentCan(
+                            paymentGateway = charge.payment!!.paymentGateway,
+                            can = remainingNeedCan
+                        )
+                    )
+                } else {
+                    total += charge.rewardCan
+                    spentCans.add(
+                        SpentCan(
+                            paymentGateway = charge.payment!!.paymentGateway,
+                            can = charge.rewardCan
+                        )
+                    )
+
+                    when (charge.payment!!.paymentGateway) {
+                        PaymentGateway.PG -> member.pgRewardCan -= remainingNeedCan
+                        PaymentGateway.APPLE_IAP -> member.appleRewardCan -= remainingNeedCan
+                        PaymentGateway.GOOGLE_IAP -> member.pgRewardCan -= remainingNeedCan
+                    }
+
+                    charge.rewardCan = 0
+                }
+
+                chargeId = charge.id!!
+            }
+
+            TotalSpentCan(spentCans, total)
+        } else {
+            TotalSpentCan(total = 0)
+        }
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/TotalSpentCan.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/TotalSpentCan.kt
new file mode 100644
index 0000000..7b3f283
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/TotalSpentCan.kt
@@ -0,0 +1,18 @@
+package kr.co.vividnext.sodalive.can.use
+
+import kr.co.vividnext.sodalive.can.payment.PaymentGateway
+
+data class TotalSpentCan(
+    val spentCans: MutableList<SpentCan> = mutableListOf(),
+    val total: Int
+) {
+    fun verify(): Boolean {
+        val sumSpentCans = spentCans.fold(0) { sum, spentCan -> sum + spentCan.can }
+        return total == sumSpentCans
+    }
+}
+
+data class SpentCan(
+    val paymentGateway: PaymentGateway,
+    val can: Int
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanCalculate.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanCalculate.kt
index 0982736..3b0efbe 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanCalculate.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanCalculate.kt
@@ -30,6 +30,8 @@ data class UseCanCalculate(
             value?.useCanCalculates?.add(this)
             field = value
         }
+
+    var recipientCreatorId: Long? = null
 }
 
 enum class UseCanCalculateStatus {
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanCalculateRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanCalculateRepository.kt
new file mode 100644
index 0000000..3d47f56
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanCalculateRepository.kt
@@ -0,0 +1,12 @@
+package kr.co.vividnext.sodalive.can.use
+
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface UseCanCalculateRepository : JpaRepository<UseCanCalculate, Long> {
+    fun findByUseCanIdAndStatus(
+        useCanId: Long,
+        status: UseCanCalculateStatus = UseCanCalculateStatus.RECEIVED
+    ): List<UseCanCalculate>
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanRepository.kt
new file mode 100644
index 0000000..f30429a
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCanRepository.kt
@@ -0,0 +1,7 @@
+package kr.co.vividnext.sodalive.can.use
+
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface UseCanRepository : JpaRepository<UseCan, Long>
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationController.kt
new file mode 100644
index 0000000..a0abd2c
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationController.kt
@@ -0,0 +1,24 @@
+package kr.co.vividnext.sodalive.live.reservation
+
+import kr.co.vividnext.sodalive.common.ApiResponse
+import kr.co.vividnext.sodalive.common.SodaException
+import kr.co.vividnext.sodalive.member.Member
+import org.springframework.security.core.annotation.AuthenticationPrincipal
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/live/reservation")
+class LiveReservationController(private val service: LiveReservationService) {
+    @PostMapping
+    fun makeReservation(
+        @RequestBody request: MakeLiveReservationRequest,
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.makeReservation(request, member.id!!))
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationRepository.kt
new file mode 100644
index 0000000..ecaf7d2
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationRepository.kt
@@ -0,0 +1,70 @@
+package kr.co.vividnext.sodalive.live.reservation
+
+import com.querydsl.jpa.impl.JPAQueryFactory
+import kr.co.vividnext.sodalive.live.reservation.QLiveReservation.liveReservation
+import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
+import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.member.QMember.member
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface LiveReservationRepository : JpaRepository<LiveReservation, Long>, LiveReservationQueryRepository
+
+interface LiveReservationQueryRepository {
+    fun getReservationList(roomId: Long): List<LiveReservation>
+
+    fun cancelReservation(roomId: Long)
+
+    fun getReservationBookerList(roomId: Long): List<Member>
+
+    fun isExistsReservation(roomId: Long, memberId: Long): Boolean
+}
+
+@Repository
+class LiveReservationQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : LiveReservationQueryRepository {
+    override fun getReservationList(roomId: Long): List<LiveReservation> {
+        return queryFactory
+            .selectFrom(liveReservation)
+            .innerJoin(liveReservation.room, liveRoom)
+            .where(
+                liveRoom.id.eq(roomId)
+                    .and(liveReservation.isActive.isTrue)
+            )
+            .fetch()
+    }
+
+    override fun cancelReservation(roomId: Long) {
+        queryFactory
+            .update(liveReservation)
+            .set(liveReservation.isActive, false)
+            .where(liveReservation.room.id.eq(roomId))
+            .execute()
+    }
+
+    override fun getReservationBookerList(roomId: Long): List<Member> {
+        return queryFactory
+            .select(member)
+            .from(liveReservation)
+            .innerJoin(liveReservation.member, member)
+            .innerJoin(liveReservation.room, liveRoom)
+            .where(
+                liveRoom.id.eq(roomId)
+                    .and(liveReservation.isActive.isTrue)
+            )
+            .fetch()
+    }
+
+    override fun isExistsReservation(roomId: Long, memberId: Long): Boolean {
+        return queryFactory
+            .selectFrom(liveReservation)
+            .innerJoin(liveReservation.member, member)
+            .innerJoin(liveReservation.room, liveRoom)
+            .where(
+                liveReservation.isActive.isTrue
+                    .and(liveReservation.room.id.eq(roomId))
+                    .and(liveReservation.member.id.eq(memberId))
+            )
+            .fetchFirst() != null
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationService.kt
new file mode 100644
index 0000000..9184f72
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/LiveReservationService.kt
@@ -0,0 +1,79 @@
+package kr.co.vividnext.sodalive.live.reservation
+
+import kr.co.vividnext.sodalive.can.payment.CanPaymentService
+import kr.co.vividnext.sodalive.can.use.CanUsage
+import kr.co.vividnext.sodalive.common.SodaException
+import kr.co.vividnext.sodalive.live.room.LiveRoomRepository
+import kr.co.vividnext.sodalive.live.room.LiveRoomType
+import kr.co.vividnext.sodalive.member.MemberRepository
+import org.springframework.data.repository.findByIdOrNull
+import org.springframework.stereotype.Service
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+
+@Service
+class LiveReservationService(
+    private val repository: LiveReservationRepository,
+    private val liveRoomRepository: LiveRoomRepository,
+    private val memberRepository: MemberRepository,
+    private val canPaymentService: CanPaymentService
+) {
+    fun makeReservation(request: MakeLiveReservationRequest, memberId: Long): MakeLiveReservationResponse {
+        val room = liveRoomRepository.findByIdOrNull(id = request.roomId)
+            ?: throw SodaException(message = "잘못된 요청입니다.\n다시 시도해 주세요.")
+
+        val member = memberRepository.findByIdOrNull(id = memberId)
+            ?: throw SodaException(message = "로그인 정보를 확인해주세요.")
+
+        if (
+            room.member!!.id!! != memberId &&
+            room.type == LiveRoomType.PRIVATE &&
+            (request.password == null || request.password != room.password)
+        ) {
+            throw SodaException("비밀번호가 일치하지 않습니다.\n다시 확인 후 입력해주세요.")
+        }
+
+        if (repository.isExistsReservation(roomId = request.roomId, memberId = memberId)) {
+            throw SodaException("이미 예약한 라이브 입니다.")
+        }
+
+        val haveCan = member.getChargeCan(request.container) + member.getRewardCan(request.container)
+        if (haveCan < room.price) {
+            throw SodaException("${room.price - haveCan}캔이 부족합니다. 충전 후 이용해 주세요.")
+        }
+
+        if (room.price > 0) {
+            canPaymentService.spendCan(
+                memberId = member.id!!,
+                needCan = room.price,
+                canUsage = CanUsage.LIVE,
+                liveRoom = room,
+                container = request.container
+            )
+        }
+
+        val reservation = LiveReservation()
+        reservation.room = room
+        reservation.member = member
+        repository.save(reservation)
+
+        val beginDateTime = room.beginDateTime
+            .atZone(ZoneId.of("UTC"))
+            .withZoneSameInstant(ZoneId.of(request.timezone))
+
+        return MakeLiveReservationResponse(
+            reservationId = reservation.id!!,
+            nickname = room.member!!.nickname,
+            title = room.title,
+            beginDateString = beginDateTime.format(DateTimeFormatter.ofPattern("yyyy년 M월 d일 (E), a hh:mm")),
+            price = if (room.price > 0) {
+                "${room.price} 캔"
+            } else {
+                "무료"
+            },
+            haveCan = haveCan,
+            useCan = room.price,
+            remainingCan = haveCan - room.price
+        )
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/MakeLiveReservationRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/MakeLiveReservationRequest.kt
new file mode 100644
index 0000000..01d00a8
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/MakeLiveReservationRequest.kt
@@ -0,0 +1,8 @@
+package kr.co.vividnext.sodalive.live.reservation
+
+data class MakeLiveReservationRequest(
+    val roomId: Long,
+    val container: String,
+    val timezone: String = "Asia/Seoul",
+    val password: String? = null
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/MakeLiveReservationResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/MakeLiveReservationResponse.kt
new file mode 100644
index 0000000..3cd4ff0
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/reservation/MakeLiveReservationResponse.kt
@@ -0,0 +1,12 @@
+package kr.co.vividnext.sodalive.live.reservation
+
+data class MakeLiveReservationResponse(
+    val reservationId: Long,
+    val nickname: String,
+    val title: String,
+    val beginDateString: String,
+    val price: String,
+    val haveCan: Int,
+    val useCan: Int,
+    val remainingCan: Int
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/EditLiveRoomInfoRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/EditLiveRoomInfoRequest.kt
new file mode 100644
index 0000000..437e8f9
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/EditLiveRoomInfoRequest.kt
@@ -0,0 +1,9 @@
+package kr.co.vividnext.sodalive.live.room
+
+data class EditLiveRoomInfoRequest(
+    val title: String?,
+    val notice: String?,
+    val numberOfPeople: Int?,
+    val beginDateTimeString: String?,
+    val timezone: String?
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/EnterOrQuitLiveRoomRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/EnterOrQuitLiveRoomRequest.kt
new file mode 100644
index 0000000..223c7e8
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/EnterOrQuitLiveRoomRequest.kt
@@ -0,0 +1,7 @@
+package kr.co.vividnext.sodalive.live.room
+
+data class EnterOrQuitLiveRoomRequest(
+    val roomId: Long,
+    val container: String,
+    val password: String? = null
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetRecentRoomInfoResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetRecentRoomInfoResponse.kt
new file mode 100644
index 0000000..4a076d2
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetRecentRoomInfoResponse.kt
@@ -0,0 +1,9 @@
+package kr.co.vividnext.sodalive.live.room
+
+data class GetRecentRoomInfoResponse(
+    val title: String,
+    val notice: String,
+    var coverImageUrl: String,
+    val coverImagePath: String,
+    val numberOfPeople: Int
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoom.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoom.kt
index 85035a3..3c95441 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoom.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoom.kt
@@ -3,6 +3,8 @@ package kr.co.vividnext.sodalive.live.room
 import kr.co.vividnext.sodalive.can.use.UseCan
 import kr.co.vividnext.sodalive.common.BaseEntity
 import kr.co.vividnext.sodalive.live.reservation.LiveReservation
+import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancel
+import kr.co.vividnext.sodalive.live.room.visit.LiveRoomVisit
 import kr.co.vividnext.sodalive.member.Member
 import java.time.LocalDateTime
 import javax.persistence.CascadeType
@@ -44,6 +46,11 @@ data class LiveRoom(
     @OneToMany(mappedBy = "room", fetch = FetchType.LAZY)
     var reservations: MutableList<LiveReservation> = mutableListOf()
 
+    @OneToMany(mappedBy = "room", fetch = FetchType.LAZY)
+    var visits: MutableList<LiveRoomVisit> = mutableListOf()
+
+    @OneToOne(mappedBy = "room")
+    var cancel: LiveRoomCancel? = null
     var channelName: String? = null
     var isActive: Boolean = true
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt
index c15d880..9ff9e48 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt
@@ -2,12 +2,15 @@ package kr.co.vividnext.sodalive.live.room
 
 import kr.co.vividnext.sodalive.common.ApiResponse
 import kr.co.vividnext.sodalive.common.SodaException
+import kr.co.vividnext.sodalive.live.room.cancel.CancelLiveRequest
 import kr.co.vividnext.sodalive.member.Member
 import org.springframework.data.domain.Pageable
 import org.springframework.security.core.annotation.AuthenticationPrincipal
 import org.springframework.web.bind.annotation.GetMapping
 import org.springframework.web.bind.annotation.PathVariable
 import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.PutMapping
+import org.springframework.web.bind.annotation.RequestBody
 import org.springframework.web.bind.annotation.RequestMapping
 import org.springframework.web.bind.annotation.RequestParam
 import org.springframework.web.bind.annotation.RequestPart
@@ -48,10 +51,59 @@ class LiveRoomController(private val service: LiveRoomService) {
         @RequestParam timezone: String,
         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
     ) = run {
-        if (member != null) {
-            ApiResponse.ok(service.getRoomDetail(id, member, timezone))
-        } else {
-            throw SodaException("로그인 정보를 확인해주세요.")
-        }
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.getRoomDetail(id, member, timezone))
+    }
+
+    @PostMapping("/enter")
+    fun enterLive(
+        @RequestBody request: EnterOrQuitLiveRoomRequest,
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.enterLive(request, member))
+    }
+
+    @PutMapping("/start")
+    fun startLive(
+        @RequestBody request: StartLiveRequest,
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.startLive(request, member))
+    }
+
+    @PutMapping("/cancel")
+    fun cancelLive(
+        @RequestBody request: CancelLiveRequest,
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.cancelLive(request, member))
+    }
+
+    @GetMapping("/recent-room-info")
+    fun getRecentRoomInfo(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.getRecentRoomInfo(member))
+    }
+
+    @PutMapping("/{id}")
+    fun editLiveRoomInfo(
+        @PathVariable("id") roomId: Long,
+        @RequestPart("coverImage") coverImage: MultipartFile?,
+        @RequestPart("request") requestString: String?,
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.editLiveRoomInfo(roomId, coverImage, requestString, member))
     }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomRepository.kt
index 169ae95..f9f0873 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomRepository.kt
@@ -2,11 +2,10 @@ package kr.co.vividnext.sodalive.live.room
 
 import com.querydsl.core.types.OrderSpecifier
 import com.querydsl.core.types.Predicate
+import com.querydsl.core.types.Projections
 import com.querydsl.core.types.dsl.CaseBuilder
 import com.querydsl.core.types.dsl.Expressions
 import com.querydsl.jpa.impl.JPAQueryFactory
-import kr.co.vividnext.sodalive.live.reservation.LiveReservation
-import kr.co.vividnext.sodalive.live.reservation.QLiveReservation.liveReservation
 import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
 import kr.co.vividnext.sodalive.member.Member
 import kr.co.vividnext.sodalive.member.QMember
@@ -33,7 +32,8 @@ interface LiveRoomQueryRepository {
     ): List<LiveRoom>
 
     fun getLiveRoom(id: Long): LiveRoom?
-    fun getReservationList(roomId: Long): List<LiveReservation>
+    fun getLiveRoomAndAccountId(roomId: Long, memberId: Long): LiveRoom?
+    fun getRecentRoomInfo(memberId: Long, cloudFrontHost: String): GetRecentRoomInfoResponse?
 }
 
 class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : LiveRoomQueryRepository {
@@ -112,15 +112,35 @@ class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : L
             .fetchFirst()
     }
 
-    override fun getReservationList(roomId: Long): List<LiveReservation> {
+    override fun getLiveRoomAndAccountId(roomId: Long, memberId: Long): LiveRoom? {
         return queryFactory
-            .selectFrom(liveReservation)
-            .innerJoin(liveReservation.room, liveRoom)
+            .selectFrom(liveRoom)
+            .innerJoin(liveRoom.member, member)
             .where(
                 liveRoom.id.eq(roomId)
-                    .and(liveReservation.isActive.isTrue)
+                    .and(liveRoom.isActive.isTrue)
+                    .and(member.id.eq(memberId))
             )
-            .fetch()
+            .fetchFirst()
+    }
+
+    override fun getRecentRoomInfo(memberId: Long, cloudFrontHost: String): GetRecentRoomInfoResponse? {
+        return queryFactory
+            .select(
+                Projections.constructor(
+                    GetRecentRoomInfoResponse::class.java,
+                    liveRoom.title,
+                    liveRoom.notice,
+                    liveRoom.coverImage.prepend("/").prepend(cloudFrontHost),
+                    liveRoom.coverImage,
+                    liveRoom.numberOfPeople
+                )
+            )
+            .from(liveRoom)
+            .where(liveRoom.member.id.eq(memberId))
+            .orderBy(liveRoom.id.desc())
+            .limit(1)
+            .fetchFirst()
     }
 
     private fun orderByFieldAccountId(
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt
index 6a6e191..032a5a6 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt
@@ -4,14 +4,31 @@ import com.amazonaws.services.s3.model.ObjectMetadata
 import com.fasterxml.jackson.databind.ObjectMapper
 import kr.co.vividnext.sodalive.aws.s3.S3Uploader
 import kr.co.vividnext.sodalive.can.CanRepository
+import kr.co.vividnext.sodalive.can.charge.Charge
+import kr.co.vividnext.sodalive.can.charge.ChargeRepository
+import kr.co.vividnext.sodalive.can.charge.ChargeStatus
+import kr.co.vividnext.sodalive.can.payment.CanPaymentService
+import kr.co.vividnext.sodalive.can.payment.Payment
+import kr.co.vividnext.sodalive.can.payment.PaymentGateway
+import kr.co.vividnext.sodalive.can.payment.PaymentStatus
+import kr.co.vividnext.sodalive.can.use.CanUsage
+import kr.co.vividnext.sodalive.can.use.UseCanCalculateRepository
+import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
 import kr.co.vividnext.sodalive.common.SodaException
 import kr.co.vividnext.sodalive.extensions.convertLocalDateTime
+import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
+import kr.co.vividnext.sodalive.live.room.cancel.CancelLiveRequest
+import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancel
+import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancelRepository
 import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailManager
 import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
 import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
+import kr.co.vividnext.sodalive.live.room.info.LiveRoomInfo
 import kr.co.vividnext.sodalive.live.room.info.LiveRoomInfoRedisRepository
+import kr.co.vividnext.sodalive.live.room.visit.LiveRoomVisitService
 import kr.co.vividnext.sodalive.live.tag.LiveTagRepository
 import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.member.MemberRepository
 import kr.co.vividnext.sodalive.utils.generateFileName
 import org.springframework.beans.factory.annotation.Value
 import org.springframework.data.domain.Pageable
@@ -28,7 +45,14 @@ import java.time.format.DateTimeFormatter
 class LiveRoomService(
     private val repository: LiveRoomRepository,
     private val roomInfoRepository: LiveRoomInfoRedisRepository,
+    private val roomCancelRepository: LiveRoomCancelRepository,
 
+    private val useCanCalculateRepository: UseCanCalculateRepository,
+    private val reservationRepository: LiveReservationRepository,
+    private val roomVisitService: LiveRoomVisitService,
+    private val canPaymentService: CanPaymentService,
+    private val chargeRepository: ChargeRepository,
+    private val memberRepository: MemberRepository,
     private val tagRepository: LiveTagRepository,
     private val canRepository: CanRepository,
     private val objectMapper: ObjectMapper,
@@ -194,7 +218,7 @@ class LiveRoomService(
         val response = GetRoomDetailResponse(
             roomId = roomId,
             title = room.title,
-            content = room.notice,
+            notice = room.notice,
             price = room.price,
             tags = room.tags.asSequence().filter { it.tag.isActive }.map { it.tag.tag }.toList(),
             numberOfParticipantsTotal = room.numberOfPeople,
@@ -254,7 +278,7 @@ class LiveRoomService(
                 response.numberOfParticipants = users.size
             }
         } else {
-            val reservationList = repository.getReservationList(roomId)
+            val reservationList = reservationRepository.getReservationList(roomId)
             response.participatingUsers = reservationList
                 .asSequence()
                 .map {
@@ -270,4 +294,201 @@ class LiveRoomService(
 
         return response
     }
+
+    @Transactional
+    fun startLive(request: StartLiveRequest, member: Member) {
+        val room = repository.getLiveRoomAndAccountId(request.roomId, member.id!!)
+            ?: throw SodaException("해당하는 라이브가 없습니다.")
+
+        val dateTime = LocalDateTime.now()
+            .atZone(ZoneId.of("UTC"))
+            .withZoneSameInstant(ZoneId.of(request.timezone))
+            .toLocalDateTime()
+
+        val beginDateTime = room.beginDateTime
+            .atZone(ZoneId.of("UTC"))
+            .withZoneSameInstant(ZoneId.of(request.timezone))
+            .toLocalDateTime()
+
+        if (dateTime.plusMinutes(10).isBefore(beginDateTime)) {
+            val startAvailableDateTimeString = beginDateTime.minusMinutes(10).format(
+                DateTimeFormatter.ofPattern("yyyy.MM.dd E hh:mm a")
+            )
+            throw SodaException("$startAvailableDateTimeString 이후에 시작할 수 있습니다.")
+        }
+
+        room.channelName = "SODA_LIVE_CHANNEL_" +
+            "${member.id}_${dateTime.year}_${dateTime.month}_${dateTime.dayOfMonth}_" +
+            "${dateTime.hour}_${dateTime.minute}"
+    }
+
+    fun cancelLive(request: CancelLiveRequest, member: Member) {
+        val room = repository.getLiveRoomAndAccountId(request.roomId, member.id!!)
+            ?: throw SodaException("해당하는 라이브가 없습니다.")
+
+        if (request.reason.isBlank()) {
+            throw SodaException("취소사유를 입력해 주세요.")
+        }
+        room.isActive = false
+
+        val roomCancel = LiveRoomCancel(request.reason)
+        roomCancel.room = room
+        roomCancelRepository.save(roomCancel)
+
+        // 유료방인 경우 환불처리
+        if (room.price > 0) {
+            val bookerList = reservationRepository.getReservationBookerList(roomId = room.id!!)
+            for (booker in bookerList) {
+                val useCan = canRepository.getCanUsedForLiveRoomNotRefund(memberId = booker.id!!, roomId = room.id!!)
+                    ?: continue
+                useCan.isRefund = true
+
+                val useCanCalculate = useCanCalculateRepository.findByUseCanIdAndStatus(useCanId = useCan.id!!)
+                useCanCalculate.forEach {
+                    it.status = UseCanCalculateStatus.REFUND
+
+                    val charge = Charge(0, it.can, status = ChargeStatus.REFUND_CHARGE)
+                    charge.title = "${it.can} 코인"
+                    charge.useCan = useCan
+
+                    when (it.paymentGateway) {
+                        PaymentGateway.PG -> booker.pgRewardCan += charge.rewardCan
+                        PaymentGateway.GOOGLE_IAP -> booker.googleRewardCan += charge.rewardCan
+                        PaymentGateway.APPLE_IAP -> booker.appleRewardCan += charge.rewardCan
+                    }
+                    charge.member = booker
+
+                    val payment = Payment(
+                        status = PaymentStatus.COMPLETE,
+                        paymentGateway = it.paymentGateway
+                    )
+                    payment.method = "환불"
+                    charge.payment = payment
+
+                    chargeRepository.save(charge)
+                }
+            }
+        }
+
+        reservationRepository.cancelReservation(roomId = room.id!!)
+    }
+
+    fun enterLive(request: EnterOrQuitLiveRoomRequest, member: Member) {
+        val room = repository.getLiveRoom(id = request.roomId)
+            ?: throw SodaException("해당하는 라이브가 없습니다.")
+
+        if (
+            room.member!!.id!! != member.id!! &&
+            room.type == LiveRoomType.PRIVATE &&
+            (request.password == null || request.password != room.password)
+        ) {
+            throw SodaException("비밀번호가 일치하지 않습니다.\n다시 확인 후 입력해주세요.")
+        }
+
+        var roomInfo = roomInfoRepository.findByIdOrNull(request.roomId)
+        if (roomInfo == null) {
+            roomInfo = roomInfoRepository.save(LiveRoomInfo(roomId = request.roomId))
+        }
+
+        if (roomInfo.speakerCount + roomInfo.listenerCount + roomInfo.managerCount >= room.numberOfPeople) {
+            throw SodaException("방이 가득찼습니다.")
+        }
+
+        if (
+            room.price > 0 &&
+            room.member!!.id!! != member.id!! &&
+            canRepository.isExistPaidLiveRoom(memberId = member.id!!, roomId = request.roomId) == null
+        ) {
+            val findMember = memberRepository.findByIdOrNull(id = member.id!!)
+                ?: throw SodaException("로그인 정보를 확인해 주세요.")
+
+            val totalCan = findMember.getChargeCan(request.container) + findMember.getRewardCan(request.container)
+            if (totalCan < room.price) {
+                throw SodaException("${room.price - totalCan}캔이 부족합니다. 충전 후 이용해 주세요.")
+            }
+
+            canPaymentService.spendCan(
+                memberId = member.id!!,
+                needCan = room.price,
+                canUsage = CanUsage.LIVE,
+                liveRoom = room,
+                container = request.container
+            )
+        }
+
+        roomInfo.removeListener(member)
+        roomInfo.removeSpeaker(member)
+        roomInfo.removeManager(member)
+
+        if (room.member!!.id == member.id) {
+            roomInfo.addSpeaker(member)
+        } else {
+            roomInfo.addListener(member)
+        }
+
+        roomInfoRepository.save(roomInfo)
+        roomVisitService.roomVisit(room, member)
+    }
+
+    fun getRecentRoomInfo(member: Member): GetRecentRoomInfoResponse {
+        return repository.getRecentRoomInfo(memberId = member.id!!, cloudFrontHost = cloudFrontHost)
+            ?: throw SodaException("최근 데이터가 없습니다.")
+    }
+
+    @Transactional
+    fun editLiveRoomInfo(roomId: Long, coverImage: MultipartFile?, requestString: String?, member: Member) {
+        val room = repository.getLiveRoom(roomId)
+        if (member.id == null || room?.member?.id != member.id!!) {
+            throw SodaException("잘못된 요청입니다.")
+        }
+
+        if (coverImage == null && requestString == null) {
+            throw SodaException("변경사항이 없습니다.")
+        }
+
+        if (requestString != null) {
+            val request = objectMapper.readValue(requestString, EditLiveRoomInfoRequest::class.java)
+
+            if (request.title != null) {
+                room.title = request.title
+            }
+
+            if (request.notice != null) {
+                room.notice = request.notice
+            }
+
+            if (request.numberOfPeople != null) {
+                room.numberOfPeople = request.numberOfPeople
+            }
+
+            if (request.beginDateTimeString != null && request.timezone != null) {
+                room.beginDateTime = request.beginDateTimeString.convertLocalDateTime("yyyy-MM-dd HH:mm")
+                    .atZone(ZoneId.of(request.timezone))
+                    .withZoneSameInstant(ZoneId.of("UTC"))
+                    .toLocalDateTime()
+            }
+        }
+
+        if (coverImage != null) {
+            val metadata = ObjectMetadata()
+            metadata.contentLength = coverImage.size
+
+            // 커버 이미지 파일명 생성
+            val coverImageFileName = generateFileName(prefix = "${room.id}-cover")
+
+            // 커버 이미지 업로드
+            val coverImagePath = s3Uploader.upload(
+                inputStream = coverImage.inputStream,
+                bucket = coverImageBucket,
+                filePath = "suda_room_cover/${room.id}/$coverImageFileName",
+                metadata = metadata
+            )
+
+            room.bgImage = coverImagePath
+
+            if (room.channelName == null) {
+                room.coverImage = coverImagePath
+            }
+        }
+    }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/StartLiveRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/StartLiveRequest.kt
new file mode 100644
index 0000000..b59570f
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/StartLiveRequest.kt
@@ -0,0 +1,8 @@
+package kr.co.vividnext.sodalive.live.room
+
+import java.util.TimeZone
+
+data class StartLiveRequest(
+    val roomId: Long,
+    val timezone: String = TimeZone.getDefault().id
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/CancelLiveRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/CancelLiveRequest.kt
new file mode 100644
index 0000000..d6add40
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/CancelLiveRequest.kt
@@ -0,0 +1,3 @@
+package kr.co.vividnext.sodalive.live.room.cancel
+
+data class CancelLiveRequest(val roomId: Long, val reason: String)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/LiveRoomCancel.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/LiveRoomCancel.kt
new file mode 100644
index 0000000..e4bc12c
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/LiveRoomCancel.kt
@@ -0,0 +1,21 @@
+package kr.co.vividnext.sodalive.live.room.cancel
+
+import kr.co.vividnext.sodalive.common.BaseEntity
+import kr.co.vividnext.sodalive.live.room.LiveRoom
+import javax.persistence.Entity
+import javax.persistence.FetchType
+import javax.persistence.JoinColumn
+import javax.persistence.OneToOne
+
+@Entity
+data class LiveRoomCancel(
+    val reason: String
+) : BaseEntity() {
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "room_id", nullable = false)
+    var room: LiveRoom? = null
+        set(value) {
+            value?.cancel = this
+            field = value
+        }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/LiveRoomCancelRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/LiveRoomCancelRepository.kt
new file mode 100644
index 0000000..959a77d
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/cancel/LiveRoomCancelRepository.kt
@@ -0,0 +1,7 @@
+package kr.co.vividnext.sodalive.live.room.cancel
+
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface LiveRoomCancelRepository : JpaRepository<LiveRoomCancel, Long>
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt
index 8a8a0d8..be95f68 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt
@@ -7,7 +7,7 @@ data class GetRoomDetailResponse(
     val roomId: Long,
     val price: Int,
     val title: String,
-    val content: String,
+    val notice: String,
     var isPaid: Boolean,
     val isPrivateRoom: Boolean,
     val password: String?,
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/GetRecentVisitRoomMemberResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/GetRecentVisitRoomMemberResponse.kt
new file mode 100644
index 0000000..a5dc76c
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/GetRecentVisitRoomMemberResponse.kt
@@ -0,0 +1,7 @@
+package kr.co.vividnext.sodalive.live.room.visit
+
+data class GetRecentVisitRoomMemberResponse(
+    val userId: Long,
+    val nickname: String,
+    val profileImageUrl: String
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisit.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisit.kt
new file mode 100644
index 0000000..622a585
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisit.kt
@@ -0,0 +1,24 @@
+package kr.co.vividnext.sodalive.live.room.visit
+
+import kr.co.vividnext.sodalive.common.BaseEntity
+import kr.co.vividnext.sodalive.live.room.LiveRoom
+import kr.co.vividnext.sodalive.member.Member
+import javax.persistence.Entity
+import javax.persistence.FetchType
+import javax.persistence.JoinColumn
+import javax.persistence.ManyToOne
+
+@Entity
+class LiveRoomVisit : BaseEntity() {
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "member_id", nullable = false)
+    var member: Member? = null
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "room_id", nullable = false)
+    var room: LiveRoom? = null
+        set(value) {
+            value?.visits?.add(this)
+            field = value
+        }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisitRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisitRepository.kt
new file mode 100644
index 0000000..b90ce96
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisitRepository.kt
@@ -0,0 +1,41 @@
+package kr.co.vividnext.sodalive.live.room.visit
+
+import com.querydsl.jpa.impl.JPAQueryFactory
+import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
+import kr.co.vividnext.sodalive.live.room.visit.QLiveRoomVisit.liveRoomVisit
+import kr.co.vividnext.sodalive.member.QMember.member
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface LiveRoomVisitRepository : JpaRepository<LiveRoomVisit, Long>, LiveRoomVisitQueryRepository
+
+interface LiveRoomVisitQueryRepository {
+    fun findByRoomIdAndMemberId(roomId: Long, memberId: Long): LiveRoomVisit?
+    fun findFirstByMemberIdOrderByUpdatedAtDesc(memberId: Long): LiveRoomVisit?
+}
+
+@Repository
+class LiveRoomVisitQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : LiveRoomVisitQueryRepository {
+    override fun findByRoomIdAndMemberId(roomId: Long, memberId: Long): LiveRoomVisit? {
+        return queryFactory
+            .selectFrom(liveRoomVisit)
+            .innerJoin(liveRoomVisit.room, liveRoom)
+            .innerJoin(liveRoomVisit.member, member)
+            .where(
+                liveRoom.id.eq(roomId)
+                    .and(member.id.eq(memberId))
+            )
+            .orderBy(liveRoomVisit.id.desc())
+            .fetchFirst()
+    }
+
+    override fun findFirstByMemberIdOrderByUpdatedAtDesc(memberId: Long): LiveRoomVisit? {
+        return queryFactory
+            .selectFrom(liveRoomVisit)
+            .innerJoin(liveRoomVisit.room, liveRoom)
+            .where(member.id.eq(memberId))
+            .orderBy(liveRoomVisit.updatedAt.desc())
+            .fetchFirst()
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisitService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisitService.kt
new file mode 100644
index 0000000..df18ba1
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/visit/LiveRoomVisitService.kt
@@ -0,0 +1,25 @@
+package kr.co.vividnext.sodalive.live.room.visit
+
+import kr.co.vividnext.sodalive.live.room.LiveRoom
+import kr.co.vividnext.sodalive.member.Member
+import org.springframework.stereotype.Service
+import org.springframework.transaction.annotation.Transactional
+import java.time.LocalDateTime
+
+@Service
+@Transactional(readOnly = true)
+class LiveRoomVisitService(private val repository: LiveRoomVisitRepository) {
+    @Transactional
+    fun roomVisit(room: LiveRoom, member: Member) {
+        var roomVisit = repository.findByRoomIdAndMemberId(room.id!!, member.id!!)
+        if (roomVisit == null) {
+            roomVisit = LiveRoomVisit()
+            roomVisit.member = member
+            roomVisit.room = room
+        } else {
+            roomVisit.updatedAt = LocalDateTime.now()
+        }
+
+        repository.save(roomVisit)
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt
index a500c93..b8badb2 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt
@@ -53,9 +53,9 @@ data class Member(
 
     // 화폐
     private var pgChargeCan: Int = 0
-    private var pgRewardCan: Int = 0
+    var pgRewardCan: Int = 0
     private var googleChargeCan: Int = 0
-    private var googleRewardCan: Int = 0
+    var googleRewardCan: Int = 0
     var appleChargeCan: Int = 0
     var appleRewardCan: Int = 0