diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventController.kt
index fdbe64b..17e8bab 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventController.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventController.kt
@@ -1,13 +1,56 @@
 package kr.co.vividnext.sodalive.event
 
 import kr.co.vividnext.sodalive.common.ApiResponse
+import org.springframework.security.access.prepost.PreAuthorize
+import org.springframework.web.bind.annotation.DeleteMapping
 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.RequestMapping
+import org.springframework.web.bind.annotation.RequestParam
 import org.springframework.web.bind.annotation.RestController
+import org.springframework.web.multipart.MultipartFile
 
 @RestController
 @RequestMapping("/event")
 class EventController(private val service: EventService) {
     @GetMapping
     fun getEventList() = ApiResponse.ok(service.getEventList())
+
+    @GetMapping("/popup")
+    fun getEventPopup() = ApiResponse.ok(service.getEventPopup())
+
+    @PostMapping
+    @PreAuthorize("hasRole('ADMIN')")
+    fun createEvent(
+        @RequestParam("thumbnail") thumbnail: MultipartFile,
+        @RequestParam(value = "detail", required = false) detail: MultipartFile? = null,
+        @RequestParam(value = "popup", required = false) popup: MultipartFile? = null,
+        @RequestParam(value = "link", required = false) link: String? = null,
+        @RequestParam(value = "title", required = false) title: String? = null,
+        @RequestParam(value = "isPopup") isPopup: Boolean
+    ) = ApiResponse.ok(
+        service.save(thumbnail, detail, popup, link, title, isPopup),
+        "등록되었습니다."
+    )
+
+    @PutMapping
+    @PreAuthorize("hasRole('ADMIN')")
+    fun updateEvent(
+        @RequestParam(value = "id") id: Long,
+        @RequestParam(value = "thumbnail", required = false) thumbnail: MultipartFile? = null,
+        @RequestParam(value = "detail", required = false) detail: MultipartFile? = null,
+        @RequestParam(value = "popup", required = false) popup: MultipartFile? = null,
+        @RequestParam(value = "link", required = false) link: String? = null,
+        @RequestParam(value = "title", required = false) title: String? = null,
+        @RequestParam(value = "isPopup", required = false) isPopup: Boolean? = null
+    ) = ApiResponse.ok(
+        service.update(id, thumbnail, detail, popup, link, title, isPopup),
+        "수정되었습니다."
+    )
+
+    @DeleteMapping("/{id}")
+    @PreAuthorize("hasRole('ADMIN')")
+    fun deleteEvent(@PathVariable id: Long) = ApiResponse.ok(service.delete(id), "삭제되었습니다.")
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventRepository.kt
index 9b45e2f..29624eb 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventRepository.kt
@@ -10,6 +10,7 @@ interface EventRepository : JpaRepository<Event, Long>, EventQueryRepository
 
 interface EventQueryRepository {
     fun getEventList(): List<EventItem>
+    fun getMainEventPopup(): EventItem?
 }
 
 @Repository
@@ -32,4 +33,27 @@ class EventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Even
             .orderBy(event.id.desc())
             .fetch()
     }
+
+    override fun getMainEventPopup(): EventItem? {
+        return queryFactory
+            .select(
+                QEventItem(
+                    event.id,
+                    event.title,
+                    event.thumbnailImage,
+                    event.detailImage,
+                    event.popupImage,
+                    event.link,
+                    event.isPopup
+                )
+            )
+            .from(event)
+            .where(
+                event.isActive.isTrue
+                    .and(event.isPopup.isTrue)
+                    .and(event.popupImage.isNotNull)
+            )
+            .orderBy(event.id.desc())
+            .fetchFirst()
+    }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt
index 0469524..e6ddd74 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt
@@ -1,12 +1,22 @@
 package kr.co.vividnext.sodalive.event
 
+import com.amazonaws.services.s3.model.ObjectMetadata
+import kr.co.vividnext.sodalive.aws.s3.S3Uploader
+import kr.co.vividnext.sodalive.common.SodaException
+import kr.co.vividnext.sodalive.utils.generateFileName
 import org.springframework.beans.factory.annotation.Value
+import org.springframework.data.repository.findByIdOrNull
 import org.springframework.stereotype.Service
+import org.springframework.transaction.annotation.Transactional
+import org.springframework.web.multipart.MultipartFile
 
 @Service
 class EventService(
     private val repository: EventRepository,
+    private val s3Uploader: S3Uploader,
 
+    @Value("\${cloud.aws.s3.bucket}")
+    private val bucket: String,
     @Value("\${cloud.aws.cloud-front.host}")
     private val cloudFrontHost: String
 ) {
@@ -32,4 +42,164 @@ class EventService(
 
         return GetEventResponse(0, eventList)
     }
+
+    fun getEventPopup(): EventItem? {
+        val eventPopup = repository.getMainEventPopup()
+
+        if (eventPopup != null) {
+            if (!eventPopup.thumbnailImageUrl.startsWith("https://")) {
+                eventPopup.thumbnailImageUrl = "$cloudFrontHost/${eventPopup.thumbnailImageUrl}"
+            }
+
+            if (eventPopup.detailImageUrl != null && !eventPopup.detailImageUrl!!.startsWith("https://")) {
+                eventPopup.detailImageUrl = "$cloudFrontHost/${eventPopup.detailImageUrl}"
+            }
+
+            if (eventPopup.popupImageUrl != null && !eventPopup.popupImageUrl!!.startsWith("https://")) {
+                eventPopup.popupImageUrl = "$cloudFrontHost/${eventPopup.popupImageUrl}"
+            }
+        }
+
+        return eventPopup
+    }
+
+    @Transactional
+    fun save(
+        thumbnail: MultipartFile,
+        detail: MultipartFile? = null,
+        popup: MultipartFile? = null,
+        link: String? = null,
+        title: String? = null,
+        isPopup: Boolean
+    ): Long {
+        if (detail == null && link.isNullOrBlank()) throw SodaException("상세이미지 혹은 링크를 등록하세요")
+
+        val event = repository.save(
+            Event(
+                thumbnailImage = "",
+                detailImage = null,
+                popupImage = null,
+                link = link,
+                title = title,
+                isPopup = isPopup
+            )
+        )
+
+        var metadata = ObjectMetadata()
+        metadata.contentLength = thumbnail.size
+        val thumbnailImagePath = s3Uploader.upload(
+            inputStream = thumbnail.inputStream,
+            bucket = bucket,
+            filePath = "event/${event.id}/${generateFileName()}",
+            metadata = metadata
+        )
+
+        val detailImagePath = if (detail != null) {
+            metadata = ObjectMetadata()
+            metadata.contentLength = detail.size
+
+            s3Uploader.upload(
+                inputStream = detail.inputStream,
+                bucket = bucket,
+                filePath = "event/${event.id}/${generateFileName()}",
+                metadata = metadata
+            )
+        } else {
+            null
+        }
+
+        val popupImagePath = if (popup != null) {
+            metadata = ObjectMetadata()
+            metadata.contentLength = popup.size
+
+            s3Uploader.upload(
+                inputStream = popup.inputStream,
+                bucket = bucket,
+                filePath = "event/${event.id}/${generateFileName()}",
+                metadata = metadata
+            )
+        } else {
+            null
+        }
+
+        event.thumbnailImage = thumbnailImagePath
+        event.detailImage = detailImagePath
+        event.popupImage = popupImagePath
+
+        return event.id ?: throw SodaException("이벤트 등록을 하지 못했습니다.")
+    }
+
+    @Transactional
+    fun update(
+        id: Long,
+        thumbnail: MultipartFile? = null,
+        detail: MultipartFile? = null,
+        popup: MultipartFile? = null,
+        link: String? = null,
+        title: String? = null,
+        isPopup: Boolean? = null
+    ) {
+        if (id <= 0) throw SodaException("잘못된 요청입니다.")
+
+        if (thumbnail == null && detail == null && link.isNullOrBlank() && title.isNullOrBlank()) {
+            throw SodaException("수정할 내용을 입력하세요.")
+        }
+
+        val event = repository.findByIdOrNull(id)
+            ?: throw SodaException("잘못된 요청입니다.")
+
+        if (thumbnail != null) {
+            val metadata = ObjectMetadata()
+            metadata.contentLength = thumbnail.size
+
+            event.thumbnailImage = s3Uploader.upload(
+                inputStream = thumbnail.inputStream,
+                bucket = bucket,
+                filePath = "event/${event.id}/${generateFileName()}"
+            )
+        }
+
+        if (detail != null) {
+            val metadata = ObjectMetadata()
+            metadata.contentLength = detail.size
+
+            event.detailImage = s3Uploader.upload(
+                inputStream = detail.inputStream,
+                bucket = bucket,
+                filePath = "event/${event.id}/${generateFileName()}"
+            )
+        }
+
+        if (popup != null) {
+            val metadata = ObjectMetadata()
+            metadata.contentLength = popup.size
+
+            event.popupImage = s3Uploader.upload(
+                inputStream = popup.inputStream,
+                bucket = bucket,
+                filePath = "event/${event.id}/${generateFileName()}"
+            )
+        }
+
+        if (!link.isNullOrBlank() && event.link != link) {
+            event.link = link
+        }
+
+        if (!title.isNullOrBlank() && event.title != title) {
+            event.title = title
+        }
+
+        if (isPopup != null) {
+            event.isPopup = isPopup
+        }
+    }
+
+    @Transactional
+    fun delete(id: Long) {
+        if (id <= 0) throw SodaException("잘못된 요청입니다.")
+        val event = repository.findByIdOrNull(id)
+            ?: throw SodaException("잘못된 요청입니다.")
+
+        event.isActive = false
+    }
 }