diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesController.kt
new file mode 100644
index 0000000..bc747b1
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesController.kt
@@ -0,0 +1,40 @@
+package kr.co.vividnext.sodalive.admin.content.series.recommend
+
+import kr.co.vividnext.sodalive.common.ApiResponse
+import org.springframework.security.access.prepost.PreAuthorize
+import org.springframework.web.bind.annotation.GetMapping
+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
+import org.springframework.web.bind.annotation.RestController
+import org.springframework.web.multipart.MultipartFile
+
+@RestController
+@PreAuthorize("hasRole('ADMIN')")
+@RequestMapping("/admin/audio-content/series/recommend")
+class AdminRecommendSeriesController(private val service: AdminRecommendSeriesService) {
+    @GetMapping
+    fun getRecommendSeriesList(@RequestParam isFree: Boolean) = ApiResponse.ok(
+        service.getRecommendSeriesList(isFree = isFree)
+    )
+
+    @PostMapping
+    fun createRecommendSeries(
+        @RequestPart("image") image: MultipartFile,
+        @RequestPart("request") requestString: String
+    ) = ApiResponse.ok(service.createRecommendSeries(image, requestString))
+
+    @PutMapping
+    fun modifyRecommendSeries(
+        @RequestPart("image", required = false) image: MultipartFile? = null,
+        @RequestPart("request") requestString: String
+    ) = ApiResponse.ok(service.updateRecommendSeries(image, requestString))
+
+    @PutMapping("/orders")
+    fun updateRecommendSeriesOrders(
+        @RequestBody request: UpdateRecommendSeriesOrdersRequest
+    ) = ApiResponse.ok(service.updateRecommendSeriesOrders(request.ids))
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesRepository.kt
new file mode 100644
index 0000000..04b47f8
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesRepository.kt
@@ -0,0 +1,43 @@
+package kr.co.vividnext.sodalive.admin.content.series.recommend
+
+import com.querydsl.jpa.impl.JPAQueryFactory
+import kr.co.vividnext.sodalive.content.main.tab.QRecommendSeries.recommendSeries
+import kr.co.vividnext.sodalive.content.main.tab.RecommendSeries
+import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.data.jpa.repository.JpaRepository
+
+interface AdminRecommendSeriesRepository :
+    JpaRepository<RecommendSeries, Long>,
+    AdminRecommendSeriesQueryRepository
+
+interface AdminRecommendSeriesQueryRepository {
+    fun getRecommendSeriesList(isFree: Boolean): List<GetAdminRecommendSeriesListResponse>
+}
+
+class AdminRecommendSeriesQueryRepositoryImpl(
+    private val queryFactory: JPAQueryFactory,
+
+    @Value("\${cloud.aws.cloud-front.host}")
+    private val imageHost: String
+) : AdminRecommendSeriesQueryRepository {
+    override fun getRecommendSeriesList(isFree: Boolean): List<GetAdminRecommendSeriesListResponse> {
+        return queryFactory
+            .select(
+                QGetAdminRecommendSeriesListResponse(
+                    recommendSeries.id,
+                    series.id,
+                    series.title,
+                    recommendSeries.imagePath.prepend("/").prepend(imageHost)
+                )
+            )
+            .from(recommendSeries)
+            .innerJoin(recommendSeries.series, series)
+            .where(
+                recommendSeries.isActive.isTrue
+                    .and(series.isActive.isTrue)
+                    .and(recommendSeries.isFree.eq(isFree))
+            )
+            .fetch()
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt
new file mode 100644
index 0000000..cf697e4
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt
@@ -0,0 +1,87 @@
+package kr.co.vividnext.sodalive.admin.content.series.recommend
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import kr.co.vividnext.sodalive.admin.content.series.AdminContentSeriesRepository
+import kr.co.vividnext.sodalive.aws.s3.S3Uploader
+import kr.co.vividnext.sodalive.common.SodaException
+import kr.co.vividnext.sodalive.content.main.tab.RecommendSeries
+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 AdminRecommendSeriesService(
+    private val s3Uploader: S3Uploader,
+    private val repository: AdminRecommendSeriesRepository,
+    private val seriesRepository: AdminContentSeriesRepository,
+    private val objectMapper: ObjectMapper,
+
+    @Value("\${cloud.aws.s3.bucket}")
+    private val bucket: String
+) {
+    fun getRecommendSeriesList(isFree: Boolean): List<GetAdminRecommendSeriesListResponse> {
+        return repository.getRecommendSeriesList(isFree = isFree)
+    }
+
+    @Transactional
+    fun createRecommendSeries(image: MultipartFile, requestString: String) {
+        val request = objectMapper.readValue(requestString, CreateRecommendSeriesRequest::class.java)
+        val series = seriesRepository.findByIdOrNull(request.seriesId)
+            ?: throw SodaException("잘못된 요청입니다.")
+
+        val recommendSeries = RecommendSeries(isFree = request.isFree)
+        recommendSeries.series = series
+        repository.save(recommendSeries)
+
+        val fileName = generateFileName()
+        val imagePath = s3Uploader.upload(
+            inputStream = image.inputStream,
+            bucket = bucket,
+            filePath = "recommend_series/${recommendSeries.id}/$fileName"
+        )
+        recommendSeries.imagePath = imagePath
+    }
+
+    @Transactional
+    fun updateRecommendSeries(image: MultipartFile?, requestString: String) {
+        val request = objectMapper.readValue(requestString, UpdateRecommendSeriesRequest::class.java)
+        val recommendSeries = repository.findByIdOrNull(request.id)
+            ?: throw SodaException("잘못된 요청입니다.")
+
+        if (image != null) {
+            val fileName = generateFileName()
+            val imagePath = s3Uploader.upload(
+                inputStream = image.inputStream,
+                bucket = bucket,
+                filePath = "recommend_series/${recommendSeries.id}/$fileName"
+            )
+            recommendSeries.imagePath = imagePath
+        }
+
+        if (request.isActive != null) {
+            recommendSeries.isActive = request.isActive
+        }
+
+        if (request.seriesId != null) {
+            val series = seriesRepository.findByIdOrNull(request.seriesId)
+
+            if (series != null) {
+                recommendSeries.series = series
+            }
+        }
+    }
+
+    @Transactional
+    fun updateRecommendSeriesOrders(ids: List<Long>) {
+        for (index in ids.indices) {
+            val recommendSeries = repository.findByIdOrNull(ids[index])
+
+            if (recommendSeries != null) {
+                recommendSeries.orders = index + 1
+            }
+        }
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/CreateRecommendSeriesRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/CreateRecommendSeriesRequest.kt
new file mode 100644
index 0000000..7bb67be
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/CreateRecommendSeriesRequest.kt
@@ -0,0 +1,6 @@
+package kr.co.vividnext.sodalive.admin.content.series.recommend
+
+data class CreateRecommendSeriesRequest(
+    val seriesId: Long,
+    val isFree: Boolean
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/GetAdminRecommendSeriesListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/GetAdminRecommendSeriesListResponse.kt
new file mode 100644
index 0000000..ec9b039
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/GetAdminRecommendSeriesListResponse.kt
@@ -0,0 +1,10 @@
+package kr.co.vividnext.sodalive.admin.content.series.recommend
+
+import com.querydsl.core.annotations.QueryProjection
+
+data class GetAdminRecommendSeriesListResponse @QueryProjection constructor(
+    val id: Long,
+    val seriesId: Long,
+    val seriesTitle: String,
+    val imageUrl: String
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesOrdersRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesOrdersRequest.kt
new file mode 100644
index 0000000..90038e0
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesOrdersRequest.kt
@@ -0,0 +1,5 @@
+package kr.co.vividnext.sodalive.admin.content.series.recommend
+
+data class UpdateRecommendSeriesOrdersRequest(
+    val ids: List<Long>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesRequest.kt
new file mode 100644
index 0000000..d820c74
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/UpdateRecommendSeriesRequest.kt
@@ -0,0 +1,7 @@
+package kr.co.vividnext.sodalive.admin.content.series.recommend
+
+data class UpdateRecommendSeriesRequest(
+    val id: Long,
+    val seriesId: Long?,
+    val isActive: Boolean?
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt
index 34261ea..fa0ea92 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt
@@ -42,8 +42,8 @@ class AuditionVoteService(
             endDate = endDate
         )
 
-        if (voteCount > 10) {
-            throw SodaException("오늘 응원은 여기까지!\n하루 최대 10회까지 응원이 가능합니다.\n내일 다시 이용해주세요.")
+        if (voteCount > 100) {
+            throw SodaException("오늘 응원은 여기까지!\n하루 최대 100회까지 응원이 가능합니다.\n내일 다시 이용해주세요.")
         }
 
         if (voteCount > 0) {
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt
index f1ac4e8..f855b57 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt
@@ -6,9 +6,9 @@ import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
 import kr.co.vividnext.sodalive.content.category.QCategoryContent.categoryContent
 import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
 import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
+import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse
 import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
 import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
-import kr.co.vividnext.sodalive.content.main.GetNewContentUploadCreator
 import kr.co.vividnext.sodalive.content.main.QGetAudioContentMainItem
 import kr.co.vividnext.sodalive.content.main.QGetAudioContentRankingItem
 import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner
@@ -24,6 +24,7 @@ import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentThe
 import kr.co.vividnext.sodalive.event.QEvent.event
 import kr.co.vividnext.sodalive.member.MemberRole
 import kr.co.vividnext.sodalive.member.QMember.member
+import kr.co.vividnext.sodalive.member.block.QBlockMember.blockMember
 import org.springframework.beans.factory.annotation.Value
 import org.springframework.data.jpa.repository.JpaRepository
 import org.springframework.stereotype.Repository
@@ -61,14 +62,14 @@ interface AudioContentQueryRepository {
     ): List<OtherContentResponse>
 
     fun findByTheme(
-        cloudfrontHost: String,
         memberId: Long,
         theme: String = "",
         sortType: SortType = SortType.NEWEST,
         isAdult: Boolean = false,
         contentType: ContentType,
         offset: Long = 0,
-        limit: Long = 20
+        limit: Long = 20,
+        isFree: Boolean = false
     ): List<GetAudioContentMainItem>
 
     fun totalCountByTheme(
@@ -98,13 +99,12 @@ interface AudioContentQueryRepository {
     fun getNewContentUploadCreatorList(
         cloudfrontHost: String,
         isAdult: Boolean = false
-    ): List<GetNewContentUploadCreator>
+    ): List<ContentCreatorResponse>
 
     fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner>
     fun getAudioContentCurations(isAdult: Boolean): List<AudioContentCuration>
     fun findAudioContentByCurationId(
         curationId: Long,
-        cloudfrontHost: String,
         isAdult: Boolean,
         contentType: ContentType
     ): List<GetAudioContentMainItem>
@@ -334,15 +334,19 @@ class AudioContentQueryRepositoryImpl(
     }
 
     override fun findByTheme(
-        cloudfrontHost: String,
         memberId: Long,
         theme: String,
         sortType: SortType,
         isAdult: Boolean,
         contentType: ContentType,
         offset: Long,
-        limit: Long
+        limit: Long,
+        isFree: Boolean
     ): List<GetAudioContentMainItem> {
+        val blockMemberCondition = blockMember.member.id.eq(member.id)
+            .and(blockMember.isActive.isTrue)
+            .and(blockMember.blockedMember.id.eq(memberId))
+
         val orderBy = when (sortType) {
             SortType.NEWEST -> listOf(audioContent.releaseDate.desc(), audioContent.id.desc())
             SortType.PRICE_HIGH -> listOf(
@@ -365,6 +369,7 @@ class AudioContentQueryRepositoryImpl(
                     .or(audioContent.releaseDate.loe(LocalDateTime.now()))
                     .or(audioContent.member.id.eq(memberId))
             )
+            .and(blockMember.id.isNull)
 
         if (!isAdult) {
             where = where.and(audioContent.isAdult.isFalse)
@@ -380,14 +385,18 @@ class AudioContentQueryRepositoryImpl(
             where = where.and(audioContentTheme.theme.eq(theme))
         }
 
+        if (isFree) {
+            where = where.and(audioContent.price.loe(0))
+        }
+
         return queryFactory
             .select(
                 QGetAudioContentMainItem(
                     audioContent.id,
-                    audioContent.coverImage.prepend("/").prepend(cloudfrontHost),
+                    audioContent.coverImage.prepend("/").prepend(imageHost),
                     audioContent.title,
                     member.id,
-                    member.profileImage.prepend("/").prepend(cloudfrontHost),
+                    member.profileImage.prepend("/").prepend(imageHost),
                     member.nickname,
                     audioContent.price,
                     audioContent.duration
@@ -396,6 +405,7 @@ class AudioContentQueryRepositoryImpl(
             .from(audioContent)
             .innerJoin(audioContent.member, member)
             .innerJoin(audioContent.theme, audioContentTheme)
+            .leftJoin(blockMember).on(blockMemberCondition)
             .where(where)
             .offset(offset)
             .limit(limit)
@@ -533,7 +543,7 @@ class AudioContentQueryRepositoryImpl(
     override fun getNewContentUploadCreatorList(
         cloudfrontHost: String,
         isAdult: Boolean
-    ): List<GetNewContentUploadCreator> {
+    ): List<ContentCreatorResponse> {
         var where = audioContent.releaseDate.after(LocalDateTime.now().minusWeeks(2))
             .and(audioContent.isActive.isTrue)
             .and(audioContent.duration.isNotNull)
@@ -552,7 +562,7 @@ class AudioContentQueryRepositoryImpl(
             .limit(20)
             .fetch()
             .map {
-                GetNewContentUploadCreator(
+                ContentCreatorResponse(
                     it.id!!,
                     it.nickname,
                     creatorProfileImageUrl = if (it.profileImage != null) {
@@ -596,7 +606,6 @@ class AudioContentQueryRepositoryImpl(
 
     override fun findAudioContentByCurationId(
         curationId: Long,
-        cloudfrontHost: String,
         isAdult: Boolean,
         contentType: ContentType
     ): List<GetAudioContentMainItem> {
@@ -620,12 +629,12 @@ class AudioContentQueryRepositoryImpl(
             .select(
                 QGetAudioContentMainItem(
                     audioContent.id,
-                    audioContent.coverImage.prepend("/").prepend(cloudfrontHost),
+                    audioContent.coverImage.prepend("/").prepend(imageHost),
                     audioContent.title,
                     member.id,
                     member.profileImage
                         .prepend("/")
-                        .prepend(cloudfrontHost),
+                        .prepend(imageHost),
                     member.nickname,
                     audioContent.price,
                     audioContent.duration
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt
index 3e32d6d..b72b829 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt
@@ -39,7 +39,6 @@ class AudioContentMainService(
         pageable: Pageable
     ): List<GetAudioContentMainItem> {
         return repository.findByTheme(
-            cloudfrontHost = imageHost,
             memberId = member.id!!,
             theme = theme,
             isAdult = member.auth != null && isAdultContentVisible,
@@ -47,7 +46,6 @@ class AudioContentMainService(
             offset = pageable.offset,
             limit = pageable.pageSize.toLong()
         )
-            .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) }
     }
 
     @Transactional(readOnly = true)
@@ -80,7 +78,7 @@ class AudioContentMainService(
 
     @Transactional(readOnly = true)
     @Cacheable(cacheNames = ["default"], key = "'newContentUploadCreatorList:' + #memberId + ':' + #isAdult")
-    fun getNewContentUploadCreatorList(memberId: Long, isAdult: Boolean): List<GetNewContentUploadCreator> {
+    fun getNewContentUploadCreatorList(memberId: Long, isAdult: Boolean): List<ContentCreatorResponse> {
         return repository.getNewContentUploadCreatorList(
             cloudfrontHost = imageHost,
             isAdult = isAdult
@@ -164,7 +162,6 @@ class AudioContentMainService(
                 description = it.description,
                 contents = repository.findAudioContentByCurationId(
                     curationId = it.id!!,
-                    cloudfrontHost = imageHost,
                     isAdult = isAdult,
                     contentType = contentType
                 )
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetNewContentUploadCreator.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/ContentCreatorResponse.kt
similarity index 83%
rename from src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetNewContentUploadCreator.kt
rename to src/main/kotlin/kr/co/vividnext/sodalive/content/main/ContentCreatorResponse.kt
index a53d1d3..c6ace0c 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetNewContentUploadCreator.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/ContentCreatorResponse.kt
@@ -3,7 +3,7 @@ package kr.co.vividnext.sodalive.content.main
 import com.fasterxml.jackson.annotation.JsonProperty
 import com.querydsl.core.annotations.QueryProjection
 
-data class GetNewContentUploadCreator @QueryProjection constructor(
+data class ContentCreatorResponse @QueryProjection constructor(
     @JsonProperty("creatorId") val creatorId: Long,
     @JsonProperty("creatorNickname") val creatorNickname: String,
     @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepository.kt
new file mode 100644
index 0000000..0fd76c6
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepository.kt
@@ -0,0 +1,41 @@
+package kr.co.vividnext.sodalive.content.main.banner
+
+import com.querydsl.jpa.impl.JPAQueryFactory
+import kr.co.vividnext.sodalive.content.main.banner.QAudioContentBanner.audioContentBanner
+import kr.co.vividnext.sodalive.content.main.tab.QAudioContentMainTab.audioContentMainTab
+import kr.co.vividnext.sodalive.event.QEvent.event
+import kr.co.vividnext.sodalive.member.QMember.member
+import org.springframework.data.jpa.repository.JpaRepository
+
+interface AudioContentBannerRepository : JpaRepository<AudioContentBanner, Long>, AudioContentBannerQueryRepository
+
+interface AudioContentBannerQueryRepository {
+    fun getAudioContentMainBannerList(tabId: Long, isAdult: Boolean): List<AudioContentBanner>
+}
+
+class AudioContentBannerQueryRepositoryImpl(
+    private val queryFactory: JPAQueryFactory
+) : AudioContentBannerQueryRepository {
+    override fun getAudioContentMainBannerList(tabId: Long, isAdult: Boolean): List<AudioContentBanner> {
+        var where = audioContentBanner.isActive.isTrue
+
+        where = if (tabId == 1L) {
+            where.and(audioContentBanner.tab.isNull)
+        } else {
+            where.and(audioContentBanner.tab.id.eq(tabId))
+        }
+
+        if (!isAdult) {
+            where = where.and(audioContentBanner.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .selectFrom(audioContentBanner)
+            .leftJoin(audioContentBanner.tab, audioContentMainTab)
+            .leftJoin(audioContentBanner.event, event)
+            .leftJoin(audioContentBanner.creator, member)
+            .where(where)
+            .orderBy(audioContentBanner.orders.asc())
+            .fetch()
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerService.kt
new file mode 100644
index 0000000..022a888
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerService.kt
@@ -0,0 +1,67 @@
+package kr.co.vividnext.sodalive.content.main.banner
+
+import kr.co.vividnext.sodalive.event.EventItem
+import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.stereotype.Service
+
+@Service
+class AudioContentBannerService(
+    private val repository: AudioContentBannerRepository,
+    private val blockMemberRepository: BlockMemberRepository,
+
+    @Value("\${cloud.aws.cloud-front.host}")
+    private val imageHost: String
+) {
+    fun getBannerList(tabId: Long, memberId: Long, isAdult: Boolean): List<GetAudioContentBannerResponse> {
+        return repository.getAudioContentMainBannerList(tabId, isAdult)
+            .filter {
+                if (it.type == AudioContentBannerType.CREATOR && it.creator != null) {
+                    !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.creator!!.id!!)
+                } else if (it.type == AudioContentBannerType.SERIES && it.series != null) {
+                    !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.series!!.member!!.id!!)
+                } else {
+                    true
+                }
+            }
+            .map {
+                GetAudioContentBannerResponse(
+                    type = it.type,
+                    thumbnailImageUrl = "$imageHost/${it.thumbnailImage}",
+                    eventItem = if (it.type == AudioContentBannerType.EVENT && it.event != null) {
+                        EventItem(
+                            id = it.event!!.id!!,
+                            thumbnailImageUrl = if (!it.event!!.thumbnailImage.startsWith("https://")) {
+                                "$imageHost/${it.event!!.thumbnailImage}"
+                            } else {
+                                it.event!!.thumbnailImage
+                            },
+                            detailImageUrl = if (
+                                it.event!!.detailImage != null &&
+                                !it.event!!.detailImage!!.startsWith("https://")
+                            ) {
+                                "$imageHost/${it.event!!.detailImage}"
+                            } else {
+                                it.event!!.detailImage
+                            },
+                            popupImageUrl = null,
+                            link = it.event!!.link
+                        )
+                    } else {
+                        null
+                    },
+                    creatorId = if (it.type == AudioContentBannerType.CREATOR && it.creator != null) {
+                        it.creator!!.id
+                    } else {
+                        null
+                    },
+                    seriesId = if (it.type == AudioContentBannerType.SERIES && it.series != null) {
+                        it.series!!.id
+                    } else {
+                        null
+                    },
+                    link = it.link
+                )
+            }
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationQueryRepository.kt
index 916d860..28c6985 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationQueryRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationQueryRepository.kt
@@ -6,6 +6,8 @@ import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
 import kr.co.vividnext.sodalive.content.SortType
 import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
 import kr.co.vividnext.sodalive.content.main.QGetAudioContentMainItem
+import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCuration.audioContentCuration
+import kr.co.vividnext.sodalive.content.main.tab.QAudioContentMainTab.audioContentMainTab
 import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme
 import kr.co.vividnext.sodalive.member.QMember.member
 import org.springframework.stereotype.Repository
@@ -87,4 +89,42 @@ class AudioContentCurationQueryRepository(private val queryFactory: JPAQueryFact
             .orderBy(orderBy)
             .fetch()
     }
+
+    fun findByContentMainTabId(tabId: Long, isAdult: Boolean): List<AudioContentCuration> {
+        var where = audioContentCuration.isActive.isTrue
+            .and(audioContentMainTab.id.eq(tabId))
+
+        if (!isAdult) {
+            where = where.and(audioContentCuration.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .selectFrom(audioContentCuration)
+            .innerJoin(audioContentCuration.tab, audioContentMainTab)
+            .where(where)
+            .orderBy(audioContentCuration.orders.asc())
+            .fetch()
+    }
+
+    fun findByContentMainTabIdAndTitle(
+        tabId: Long,
+        title: String,
+        isAdult: Boolean,
+        offset: Long = 0,
+        limit: Long = 12
+    ): List<AudioContentCuration> {
+        var where = audioContentCuration.isActive.isTrue
+            .and(audioContentMainTab.id.eq(tabId))
+
+        if (!isAdult) {
+            where = where.and(audioContentCuration.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .selectFrom(audioContentCuration)
+            .innerJoin(audioContentCuration.tab, audioContentMainTab)
+            .where(where)
+            .orderBy(audioContentCuration.orders.asc())
+            .fetch()
+    }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentCurationResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentCurationResponse.kt
new file mode 100644
index 0000000..b020326
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentCurationResponse.kt
@@ -0,0 +1,8 @@
+package kr.co.vividnext.sodalive.content.main.tab
+
+import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
+
+data class GetContentCurationResponse(
+    val title: String,
+    val items: List<GetAudioContentMainItem>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetRecommendSeriesListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetRecommendSeriesListResponse.kt
new file mode 100644
index 0000000..a84a20d
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetRecommendSeriesListResponse.kt
@@ -0,0 +1,11 @@
+package kr.co.vividnext.sodalive.content.main.tab
+
+import com.querydsl.core.annotations.QueryProjection
+
+data class GetRecommendSeriesListResponse @QueryProjection constructor(
+    val seriesId: Long,
+    val title: String,
+    val imageUrl: String,
+    val creatorId: Long,
+    val creatorNickname: String
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeries.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeries.kt
new file mode 100644
index 0000000..9d0290d
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeries.kt
@@ -0,0 +1,28 @@
+package kr.co.vividnext.sodalive.content.main.tab
+
+import kr.co.vividnext.sodalive.common.BaseEntity
+import kr.co.vividnext.sodalive.creator.admin.content.series.Series
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.FetchType
+import javax.persistence.JoinColumn
+import javax.persistence.ManyToOne
+
+@Entity
+data class RecommendSeries(
+    @Column(nullable = false)
+    var imagePath: String = "",
+
+    @Column(nullable = false)
+    var orders: Int = 1,
+
+    @Column(nullable = false)
+    var isFree: Boolean = false,
+
+    @Column(nullable = false)
+    var isActive: Boolean = true
+) : BaseEntity() {
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "series_id", nullable = false)
+    var series: Series? = null
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeriesRepository.kt
new file mode 100644
index 0000000..b17a583
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/RecommendSeriesRepository.kt
@@ -0,0 +1,70 @@
+package kr.co.vividnext.sodalive.content.main.tab
+
+import com.querydsl.jpa.impl.JPAQueryFactory
+import kr.co.vividnext.sodalive.content.main.tab.QRecommendSeries.recommendSeries
+import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
+import kr.co.vividnext.sodalive.member.QMember.member
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.stereotype.Repository
+
+@Repository
+class RecommendSeriesRepository(
+    private val queryFactory: JPAQueryFactory,
+
+    @Value("\${cloud.aws.cloud-front.host}")
+    private val imageHost: String
+) {
+    fun getNewSeriesList(isAdult: Boolean): List<GetRecommendSeriesListResponse> {
+        var where = recommendSeries.isActive.isTrue
+            .and(recommendSeries.isFree.isFalse)
+            .and(series.isActive.isTrue)
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(
+                QGetRecommendSeriesListResponse(
+                    series.id,
+                    series.title,
+                    recommendSeries.imagePath.prepend("/").prepend(imageHost),
+                    member.id,
+                    member.nickname
+                )
+            )
+            .from(recommendSeries)
+            .innerJoin(recommendSeries.series, series)
+            .innerJoin(series.member, member)
+            .where(where)
+            .orderBy(recommendSeries.orders.asc())
+            .fetch()
+    }
+
+    fun getRecommendSeriesList(isAdult: Boolean): List<GetRecommendSeriesListResponse> {
+        var where = recommendSeries.isActive.isTrue
+            .and(recommendSeries.isFree.isTrue)
+            .and(series.isActive.isTrue)
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(
+                QGetRecommendSeriesListResponse(
+                    series.id,
+                    series.title,
+                    recommendSeries.imagePath.prepend("/").prepend(imageHost),
+                    member.id,
+                    member.nickname
+                )
+            )
+            .from(recommendSeries)
+            .innerJoin(recommendSeries.series, series)
+            .innerJoin(series.member, member)
+            .where(where)
+            .orderBy(recommendSeries.orders.asc())
+            .fetch()
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/AudioContentMainTabAlarmController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/AudioContentMainTabAlarmController.kt
new file mode 100644
index 0000000..68bae3c
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/AudioContentMainTabAlarmController.kt
@@ -0,0 +1,22 @@
+package kr.co.vividnext.sodalive.content.main.tab.alarm
+
+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.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/v2/audio-content/main/alarm")
+class AudioContentMainTabAlarmController(private val service: AudioContentMainTabAlarmService) {
+    @GetMapping
+    fun fetchContentMainTabAlarm(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.fetchData(member))
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/AudioContentMainTabAlarmService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/AudioContentMainTabAlarmService.kt
new file mode 100644
index 0000000..81128fe
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/AudioContentMainTabAlarmService.kt
@@ -0,0 +1,84 @@
+package kr.co.vividnext.sodalive.content.main.tab.alarm
+
+import kr.co.vividnext.sodalive.content.AudioContentRepository
+import kr.co.vividnext.sodalive.content.ContentType
+import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
+import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
+import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
+import kr.co.vividnext.sodalive.event.EventService
+import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.rank.RankingService
+import org.springframework.stereotype.Service
+import java.time.DayOfWeek
+import java.time.LocalDateTime
+import java.time.temporal.TemporalAdjusters
+
+@Service
+class AudioContentMainTabAlarmService(
+    private val bannerService: AudioContentBannerService,
+    private val contentRepository: AudioContentRepository,
+    private val rankingService: RankingService,
+    private val eventService: EventService,
+    private val curationRepository: AudioContentCurationQueryRepository
+) {
+    fun fetchData(member: Member): GetContentMainTabAlarmResponse {
+        val isAdult = member.auth != null
+        val memberId = member.id!!
+
+        val contentBannerList = bannerService.getBannerList(
+            tabId = 4,
+            memberId = memberId,
+            isAdult = isAdult
+        )
+
+        val alarmThemeList = listOf("모닝콜", "슬립콜", "알람")
+        val newAlarmContentList = contentRepository.findByTheme(
+            memberId = memberId,
+            theme = alarmThemeList[0],
+            isAdult = isAdult,
+            contentType = ContentType.ALL,
+            limit = 10
+        )
+
+        // 주간 랭킹 기간
+        val currentDateTime = LocalDateTime.now()
+        val startDate = currentDateTime
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+            .minusWeeks(1)
+            .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
+        val endDate = startDate
+            .plusDays(6)
+
+        val rankAlarmContentList = rankingService.getContentRanking(
+            memberId = memberId,
+            isAdult = isAdult,
+            startDate = startDate,
+            endDate = endDate,
+            theme = alarmThemeList[0]
+        )
+
+        val eventBannerList = eventService.getEventList(isAdult = isAdult)
+        val curationList = curationRepository.findByContentMainTabId(tabId = 4, isAdult = isAdult)
+            .map {
+                GetContentCurationResponse(
+                    title = it.title,
+                    items = contentRepository.findAudioContentByCurationId(
+                        curationId = it.id!!,
+                        isAdult = isAdult,
+                        contentType = ContentType.ALL
+                    )
+                )
+            }
+
+        return GetContentMainTabAlarmResponse(
+            contentBannerList = contentBannerList,
+            alarmThemeList = alarmThemeList,
+            newAlarmContentList = newAlarmContentList,
+            rankAlarmContentList = rankAlarmContentList,
+            eventBannerList = eventBannerList,
+            curationList = curationList
+        )
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/GetContentMainTabAlarmResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/GetContentMainTabAlarmResponse.kt
new file mode 100644
index 0000000..46d61de
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/alarm/GetContentMainTabAlarmResponse.kt
@@ -0,0 +1,17 @@
+package kr.co.vividnext.sodalive.content.main.tab.alarm
+
+import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
+import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
+import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
+import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
+import kr.co.vividnext.sodalive.event.GetEventResponse
+
+data class GetContentMainTabAlarmResponse(
+    val tab: Long = 4,
+    val contentBannerList: List<GetAudioContentBannerResponse>,
+    val alarmThemeList: List<String>,
+    val newAlarmContentList: List<GetAudioContentMainItem>,
+    val rankAlarmContentList: List<GetAudioContentRankingItem>,
+    val eventBannerList: GetEventResponse,
+    val curationList: List<GetContentCurationResponse>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrController.kt
new file mode 100644
index 0000000..ad35a6d
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrController.kt
@@ -0,0 +1,22 @@
+package kr.co.vividnext.sodalive.content.main.tab.asmr
+
+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.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/v2/audio-content/main/asmr")
+class AudioContentMainTabAsmrController(private val service: AudioContentMainTabAsmrService) {
+    @GetMapping
+    fun fetchContentMainTabAsmr(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.fetchData(member))
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrService.kt
new file mode 100644
index 0000000..9748778
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/AudioContentMainTabAsmrService.kt
@@ -0,0 +1,84 @@
+package kr.co.vividnext.sodalive.content.main.tab.asmr
+
+import kr.co.vividnext.sodalive.content.AudioContentRepository
+import kr.co.vividnext.sodalive.content.ContentType
+import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
+import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
+import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
+import kr.co.vividnext.sodalive.event.EventService
+import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.rank.RankingService
+import org.springframework.stereotype.Service
+import java.time.DayOfWeek
+import java.time.LocalDateTime
+import java.time.temporal.TemporalAdjusters
+
+@Service
+class AudioContentMainTabAsmrService(
+    private val bannerService: AudioContentBannerService,
+    private val contentRepository: AudioContentRepository,
+    private val rankingService: RankingService,
+    private val eventService: EventService,
+    private val curationRepository: AudioContentCurationQueryRepository
+) {
+    fun fetchData(member: Member): GetContentMainTabAsmrResponse {
+        val isAdult = member.auth != null
+        val memberId = member.id!!
+        val theme = "ASMR"
+        val tabId = 5L
+
+        val contentBannerList = bannerService.getBannerList(
+            tabId = tabId,
+            memberId = memberId,
+            isAdult = isAdult
+        )
+
+        val newAsmrContentList = contentRepository.findByTheme(
+            memberId = memberId,
+            theme = theme,
+            isAdult = isAdult,
+            contentType = ContentType.ALL,
+            limit = 10
+        )
+
+        val currentDateTime = LocalDateTime.now()
+        val startDate = currentDateTime
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+            .minusWeeks(1)
+            .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
+        val endDate = startDate
+            .plusDays(6)
+
+        val rankAsmrContentList = rankingService.getContentRanking(
+            memberId = memberId,
+            isAdult = isAdult,
+            startDate = startDate,
+            endDate = endDate,
+            theme = theme
+        )
+
+        val eventBannerList = eventService.getEventList(isAdult = isAdult)
+
+        val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult)
+            .map {
+                GetContentCurationResponse(
+                    title = it.title,
+                    items = contentRepository.findAudioContentByCurationId(
+                        curationId = it.id!!,
+                        isAdult = isAdult,
+                        contentType = ContentType.ALL
+                    )
+                )
+            }
+
+        return GetContentMainTabAsmrResponse(
+            contentBannerList = contentBannerList,
+            newAsmrContentList = newAsmrContentList,
+            rankAsmrContentList = rankAsmrContentList,
+            eventBannerList = eventBannerList,
+            curationList = curationList
+        )
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/GetContentMainTabAsmrResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/GetContentMainTabAsmrResponse.kt
new file mode 100644
index 0000000..5b406e1
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/asmr/GetContentMainTabAsmrResponse.kt
@@ -0,0 +1,16 @@
+package kr.co.vividnext.sodalive.content.main.tab.asmr
+
+import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
+import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
+import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
+import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
+import kr.co.vividnext.sodalive.event.GetEventResponse
+
+data class GetContentMainTabAsmrResponse(
+    val tab: Long = 5,
+    val contentBannerList: List<GetAudioContentBannerResponse>,
+    val newAsmrContentList: List<GetAudioContentMainItem>,
+    val rankAsmrContentList: List<GetAudioContentRankingItem>,
+    val eventBannerList: GetEventResponse,
+    val curationList: List<GetContentCurationResponse>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/AudioContentMainTabContentController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/AudioContentMainTabContentController.kt
new file mode 100644
index 0000000..80762a1
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/AudioContentMainTabContentController.kt
@@ -0,0 +1,22 @@
+package kr.co.vividnext.sodalive.content.main.tab.content
+
+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.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/v2/audio-content/main/content")
+class AudioContentMainTabContentController(private val service: AudioContentMainTabContentService) {
+    @GetMapping
+    fun fetchContentMainTabContent(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.fetchData(member))
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/AudioContentMainTabContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/AudioContentMainTabContentService.kt
new file mode 100644
index 0000000..6807e14
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/AudioContentMainTabContentService.kt
@@ -0,0 +1,105 @@
+package kr.co.vividnext.sodalive.content.main.tab.content
+
+import kr.co.vividnext.sodalive.content.AudioContentRepository
+import kr.co.vividnext.sodalive.content.ContentType
+import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
+import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository
+import kr.co.vividnext.sodalive.event.EventService
+import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.rank.RankingService
+import org.springframework.stereotype.Service
+import java.time.LocalDateTime
+
+@Service
+class AudioContentMainTabContentService(
+    private val bannerService: AudioContentBannerService,
+    private val audioContentRepository: AudioContentRepository,
+    private val audioContentThemeRepository: AudioContentThemeQueryRepository,
+    private val rankingService: RankingService,
+    private val eventService: EventService
+) {
+    fun fetchData(member: Member): GetContentMainTabContentResponse {
+        val memberId = member.id!!
+        val isAdult = member.auth != null
+
+        // 단편 배너
+        val contentBannerList = bannerService.getBannerList(
+            tabId = 3,
+            memberId = memberId,
+            isAdult = isAdult
+        )
+
+        // 새로운 단편 테마
+        val themeOfContentList = audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult)
+
+        // 새로운 단편
+        val newContentList = if (themeOfContentList.isNotEmpty()) {
+            audioContentRepository.findByTheme(
+                memberId = member.id!!,
+                theme = themeOfContentList[0],
+                isAdult = member.auth != null,
+                contentType = ContentType.ALL,
+                offset = 0,
+                limit = 10
+            )
+        } else {
+            emptyList()
+        }
+
+        // 일간 랭킹
+        val currentDateTime = LocalDateTime.now()
+        val dailyRankingStartDate = currentDateTime
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+            .minusDays(2)
+        val dailyRankingEndDate = dailyRankingStartDate
+            .plusDays(1)
+
+        val rankContentList = rankingService.getContentRanking(
+            memberId = memberId,
+            isAdult = isAdult,
+            startDate = dailyRankingStartDate,
+            endDate = dailyRankingEndDate
+        )
+
+        // 이벤트 배너
+        val eventBannerList = eventService.getEventList(isAdult = isAdult)
+
+        val contentRankCreatorList = rankingService.fetchCreatorByContentRevenueRankTop20(
+            memberId = member.id!!,
+            startDate = dailyRankingStartDate.minusDays(1),
+            endDate = dailyRankingEndDate
+        )
+
+        val salesRankContentList = if (contentRankCreatorList.isNotEmpty()) {
+            rankingService.fetchCreatorContentBySalesTop2(
+                creatorId = contentRankCreatorList[0].creatorId,
+                isAdult = member.auth != null
+            )
+        } else {
+            emptyList()
+        }
+
+        val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) {
+            rankingService.fetchCreatorContentBySalesCountTop2(
+                creatorId = contentRankCreatorList[0].creatorId,
+                isAdult = member.auth != null
+            )
+        } else {
+            emptyList()
+        }
+
+        return GetContentMainTabContentResponse(
+            bannerList = contentBannerList,
+            contentThemeList = themeOfContentList,
+            newContentList = newContentList,
+            rankSortTypeList = listOf("매출", "댓글", "좋아요"),
+            rankContentList = rankContentList,
+            contentRankCreatorList = contentRankCreatorList,
+            salesRankContentList = salesRankContentList,
+            salesCountRankContentList = salesCountRankContentList,
+            eventBannerList = eventBannerList
+        )
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/GetContentMainTabContentResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/GetContentMainTabContentResponse.kt
new file mode 100644
index 0000000..721e960
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/content/GetContentMainTabContentResponse.kt
@@ -0,0 +1,20 @@
+package kr.co.vividnext.sodalive.content.main.tab.content
+
+import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse
+import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
+import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
+import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
+import kr.co.vividnext.sodalive.event.GetEventResponse
+
+data class GetContentMainTabContentResponse(
+    val tabId: Long = 3,
+    val bannerList: List<GetAudioContentBannerResponse>,
+    val contentThemeList: List<String>,
+    val newContentList: List<GetAudioContentMainItem>,
+    val rankSortTypeList: List<String>,
+    val rankContentList: List<GetAudioContentRankingItem>,
+    val contentRankCreatorList: List<ContentCreatorResponse>,
+    val salesRankContentList: List<GetAudioContentRankingItem>,
+    val salesCountRankContentList: List<GetAudioContentRankingItem>,
+    val eventBannerList: GetEventResponse
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/AudioContentMainTabFreeController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/AudioContentMainTabFreeController.kt
new file mode 100644
index 0000000..e588782
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/AudioContentMainTabFreeController.kt
@@ -0,0 +1,22 @@
+package kr.co.vividnext.sodalive.content.main.tab.free
+
+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.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/v2/audio-content/main/free")
+class AudioContentMainTabFreeController(private val service: AudioContentMainTabFreeService) {
+    @GetMapping
+    fun fetchContentMainFree(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.fetchData(member))
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/AudioContentMainTabFreeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/AudioContentMainTabFreeService.kt
new file mode 100644
index 0000000..b64a2ab
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/AudioContentMainTabFreeService.kt
@@ -0,0 +1,91 @@
+package kr.co.vividnext.sodalive.content.main.tab.free
+
+import kr.co.vividnext.sodalive.content.AudioContentRepository
+import kr.co.vividnext.sodalive.content.ContentType
+import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
+import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
+import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
+import kr.co.vividnext.sodalive.content.main.tab.RecommendSeriesRepository
+import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository
+import kr.co.vividnext.sodalive.member.Member
+import org.springframework.stereotype.Service
+
+@Service
+class AudioContentMainTabFreeService(
+    private val bannerService: AudioContentBannerService,
+    private val recommendSeriesRepository: RecommendSeriesRepository,
+    private val curationRepository: AudioContentCurationQueryRepository,
+    private val contentRepository: AudioContentRepository,
+    private val audioContentRepository: AudioContentRepository,
+    private val audioContentThemeRepository: AudioContentThemeQueryRepository
+) {
+    fun fetchData(member: Member): GetContentMainTabFreeResponse {
+        val isAdult = member.auth != null
+        val memberId = member.id!!
+        val tabId = 7L
+
+        val contentBannerList = bannerService.getBannerList(
+            tabId = tabId,
+            memberId = memberId,
+            isAdult = isAdult
+        )
+
+        val introduceCreator = curationRepository.findByContentMainTabIdAndTitle(
+            tabId = tabId,
+            title = "크리에이터 소개",
+            isAdult = isAdult
+        )
+            .map {
+                GetContentCurationResponse(
+                    title = it.title,
+                    items = contentRepository.findAudioContentByCurationId(
+                        curationId = it.id!!,
+                        isAdult = isAdult,
+                        contentType = ContentType.ALL
+                    )
+                )
+            }
+
+        val recommendSeriesList = recommendSeriesRepository.getRecommendSeriesList(isAdult = isAdult)
+
+        val themeList = audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult)
+        val newFreeContentList = if (themeList.isNotEmpty()) {
+            audioContentRepository.findByTheme(
+                memberId = member.id!!,
+                theme = themeList[0],
+                isAdult = member.auth != null,
+                contentType = ContentType.ALL,
+                offset = 0,
+                limit = 10,
+                isFree = true
+            )
+        } else {
+            emptyList()
+        }
+
+        val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult)
+            .map {
+                GetContentCurationResponse(
+                    title = it.title,
+                    items = contentRepository.findAudioContentByCurationId(
+                        curationId = it.id!!,
+                        isAdult = isAdult,
+                        contentType = ContentType.ALL
+                    )
+                )
+            }
+
+        return GetContentMainTabFreeResponse(
+            contentBannerList = contentBannerList,
+            introduceCreator = if (introduceCreator.isNotEmpty()) {
+                introduceCreator[0]
+            } else {
+                null
+            },
+            recommendSeriesList = recommendSeriesList,
+            themeList = themeList,
+            newFreeContentList = newFreeContentList,
+            curationList = curationList
+        )
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/GetContentMainTabFreeResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/GetContentMainTabFreeResponse.kt
new file mode 100644
index 0000000..aed9911
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/free/GetContentMainTabFreeResponse.kt
@@ -0,0 +1,16 @@
+package kr.co.vividnext.sodalive.content.main.tab.free
+
+import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
+import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
+import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
+import kr.co.vividnext.sodalive.content.main.tab.GetRecommendSeriesListResponse
+
+data class GetContentMainTabFreeResponse(
+    val tabId: Long = 7,
+    val contentBannerList: List<GetAudioContentBannerResponse>,
+    val introduceCreator: GetContentCurationResponse?,
+    val recommendSeriesList: List<GetRecommendSeriesListResponse>,
+    val themeList: List<String>,
+    val newFreeContentList: List<GetAudioContentMainItem>,
+    val curationList: List<GetContentCurationResponse>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeController.kt
new file mode 100644
index 0000000..601f82d
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeController.kt
@@ -0,0 +1,22 @@
+package kr.co.vividnext.sodalive.content.main.tab.home
+
+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.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/v2/audio-content/main/home")
+class AudioContentMainTabHomeController(private val service: AudioContentMainTabHomeService) {
+    @GetMapping
+    fun fetchContentMainHome(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.fetchData(member))
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeService.kt
new file mode 100644
index 0000000..8532a33
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/AudioContentMainTabHomeService.kt
@@ -0,0 +1,117 @@
+package kr.co.vividnext.sodalive.content.main.tab.home
+
+import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
+import kr.co.vividnext.sodalive.event.EventService
+import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.notice.ServiceNoticeService
+import kr.co.vividnext.sodalive.rank.RankingService
+import org.springframework.stereotype.Service
+import java.time.DayOfWeek
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+import java.time.temporal.TemporalAdjusters
+
+@Service
+class AudioContentMainTabHomeService(
+    private val noticeService: ServiceNoticeService,
+    private val bannerService: AudioContentBannerService,
+    private val rankingService: RankingService,
+    private val eventService: EventService
+) {
+    fun fetchData(member: Member): GetContentMainTabHomeResponse {
+        // 주간 랭킹 기간
+        val currentDateTime = LocalDateTime.now()
+        val startDate = currentDateTime
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+            .minusWeeks(1)
+            .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
+        val endDate = startDate
+            .plusDays(6)
+
+        val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일")
+        val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일")
+
+        val formattedLastMonday = startDate.format(startDateFormatter)
+        val formattedLastSunday = endDate.format(endDateFormatter)
+
+        // 최근 공지사항
+        val latestNotice = noticeService.getLatestNotice()
+
+        // 메인 배너 (홈)
+        val contentBannerList = bannerService.getBannerList(
+            tabId = 1,
+            memberId = member.id!!,
+            isAdult = member.auth != null
+        )
+
+        // 인기 크리에이터
+        val rankCreatorList = rankingService.getCreatorRanking(
+            memberId = member.id!!,
+            rankingDate = "$formattedLastMonday ~ $formattedLastSunday"
+        )
+
+        // 인기 시리즈
+        val rankSeriesList = rankingService.getSeriesRanking(
+            memberId = member.id!!,
+            isAdult = member.auth != null,
+            startDate = startDate.minusDays(1),
+            endDate = endDate
+        )
+
+        // 인기 콘텐츠
+        val rankContentList = rankingService.getContentRanking(
+            memberId = member.id!!,
+            isAdult = member.auth != null,
+            startDate = startDate.minusDays(1),
+            endDate = endDate
+        )
+
+        // 이벤트 배너
+        val eventBannerList = eventService.getEventList(isAdult = member.auth != null)
+
+        /* 채널별 인기 콘텐츠
+         * - 콘텐츠를 4개 이상 등록한 채널
+         * - 주간 콘텐츠 매출 Top 20 채널
+         * - 해당 채널의 누적 매출 Top 2
+         * - 해당 채널의 누적 판매 개수 Top 2
+         */
+        val contentRankCreatorList = rankingService.fetchCreatorByContentRevenueRankTop20(
+            memberId = member.id!!,
+            startDate = startDate.minusDays(1),
+            endDate = endDate
+        )
+
+        val salesRankContentList = if (contentRankCreatorList.isNotEmpty()) {
+            rankingService.fetchCreatorContentBySalesTop2(
+                creatorId = contentRankCreatorList[0].creatorId,
+                isAdult = member.auth != null
+            )
+        } else {
+            emptyList()
+        }
+
+        val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) {
+            rankingService.fetchCreatorContentBySalesCountTop2(
+                creatorId = contentRankCreatorList[0].creatorId,
+                isAdult = member.auth != null
+            )
+        } else {
+            emptyList()
+        }
+
+        return GetContentMainTabHomeResponse(
+            latestNotice = latestNotice,
+            bannerList = contentBannerList,
+            rankCreatorList = rankCreatorList,
+            rankSeriesList = rankSeriesList,
+            rankSortTypeList = listOf("매출", "댓글", "좋아요"),
+            rankContentList = rankContentList,
+            eventBannerList = eventBannerList,
+            contentRankCreatorList = contentRankCreatorList,
+            salesRankContentList = salesRankContentList,
+            salesCountRankContentList = salesCountRankContentList
+        )
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/GetContentMainTabHomeResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/GetContentMainTabHomeResponse.kt
new file mode 100644
index 0000000..c104a77
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/home/GetContentMainTabHomeResponse.kt
@@ -0,0 +1,23 @@
+package kr.co.vividnext.sodalive.content.main.tab.home
+
+import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse
+import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
+import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
+import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse
+import kr.co.vividnext.sodalive.event.GetEventResponse
+import kr.co.vividnext.sodalive.explorer.GetExplorerSectionResponse
+import kr.co.vividnext.sodalive.notice.NoticeTitleItem
+
+data class GetContentMainTabHomeResponse(
+    val tabId: Long = 1,
+    val latestNotice: NoticeTitleItem?,
+    val bannerList: List<GetAudioContentBannerResponse>,
+    val rankCreatorList: GetExplorerSectionResponse,
+    val rankSeriesList: List<GetSeriesListResponse.SeriesListItem>,
+    val rankSortTypeList: List<String>,
+    val rankContentList: List<GetAudioContentRankingItem>,
+    val eventBannerList: GetEventResponse,
+    val contentRankCreatorList: List<ContentCreatorResponse>,
+    val salesRankContentList: List<GetAudioContentRankingItem>,
+    val salesCountRankContentList: List<GetAudioContentRankingItem>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayController.kt
new file mode 100644
index 0000000..c9b3739
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayController.kt
@@ -0,0 +1,22 @@
+package kr.co.vividnext.sodalive.content.main.tab.replay
+
+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.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/v2/audio-content/main/replay")
+class AudioContentMainTabLiveReplayController(private val service: AudioContentMainTabLiveReplayService) {
+    @GetMapping
+    fun fetchContentMainTabLiveReplay(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.fetchData(member))
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayService.kt
new file mode 100644
index 0000000..560ca76
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/AudioContentMainTabLiveReplayService.kt
@@ -0,0 +1,84 @@
+package kr.co.vividnext.sodalive.content.main.tab.replay
+
+import kr.co.vividnext.sodalive.content.AudioContentRepository
+import kr.co.vividnext.sodalive.content.ContentType
+import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
+import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
+import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
+import kr.co.vividnext.sodalive.event.EventService
+import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.rank.RankingService
+import org.springframework.stereotype.Service
+import java.time.DayOfWeek
+import java.time.LocalDateTime
+import java.time.temporal.TemporalAdjusters
+
+@Service
+class AudioContentMainTabLiveReplayService(
+    private val bannerService: AudioContentBannerService,
+    private val contentRepository: AudioContentRepository,
+    private val rankingService: RankingService,
+    private val eventService: EventService,
+    private val curationRepository: AudioContentCurationQueryRepository
+) {
+    fun fetchData(member: Member): GetContentMainTabLiveReplayResponse {
+        val isAdult = member.auth != null
+        val memberId = member.id!!
+        val theme = "다시듣기"
+        val tabId = 6L
+
+        val contentBannerList = bannerService.getBannerList(
+            tabId = tabId,
+            memberId = memberId,
+            isAdult = isAdult
+        )
+
+        val newLiveReplayContentList = contentRepository.findByTheme(
+            memberId = memberId,
+            theme = theme,
+            isAdult = isAdult,
+            contentType = ContentType.ALL,
+            limit = 10
+        )
+
+        val currentDateTime = LocalDateTime.now()
+        val startDate = currentDateTime
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+            .minusWeeks(1)
+            .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
+        val endDate = startDate
+            .plusDays(6)
+
+        val rankLiveReplayContentList = rankingService.getContentRanking(
+            memberId = memberId,
+            isAdult = isAdult,
+            startDate = startDate,
+            endDate = endDate,
+            theme = theme
+        )
+
+        val eventBannerList = eventService.getEventList(isAdult = isAdult)
+
+        val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult)
+            .map {
+                GetContentCurationResponse(
+                    title = it.title,
+                    items = contentRepository.findAudioContentByCurationId(
+                        curationId = it.id!!,
+                        isAdult = isAdult,
+                        contentType = ContentType.ALL
+                    )
+                )
+            }
+
+        return GetContentMainTabLiveReplayResponse(
+            contentBannerList = contentBannerList,
+            newLiveReplayContentList = newLiveReplayContentList,
+            rankLiveReplayContentList = rankLiveReplayContentList,
+            eventBannerList = eventBannerList,
+            curationList = curationList
+        )
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/GetContentMainTabLiveReplayResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/GetContentMainTabLiveReplayResponse.kt
new file mode 100644
index 0000000..eea8309
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/replay/GetContentMainTabLiveReplayResponse.kt
@@ -0,0 +1,16 @@
+package kr.co.vividnext.sodalive.content.main.tab.replay
+
+import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
+import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
+import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
+import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
+import kr.co.vividnext.sodalive.event.GetEventResponse
+
+data class GetContentMainTabLiveReplayResponse(
+    val tabId: Long = 6,
+    val contentBannerList: List<GetAudioContentBannerResponse>,
+    val newLiveReplayContentList: List<GetAudioContentMainItem>,
+    val rankLiveReplayContentList: List<GetAudioContentRankingItem>,
+    val eventBannerList: GetEventResponse,
+    val curationList: List<GetContentCurationResponse>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesController.kt
new file mode 100644
index 0000000..1e27aa6
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesController.kt
@@ -0,0 +1,22 @@
+package kr.co.vividnext.sodalive.content.main.tab.series
+
+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.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/v2/audio-content/main/series")
+class AudioContentMainTabSeriesController(private val service: AudioContentMainTabSeriesService) {
+    @GetMapping
+    fun fetchContentMainSeries(
+        @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
+    ) = run {
+        if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
+
+        ApiResponse.ok(service.fetchData(member))
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesService.kt
new file mode 100644
index 0000000..681dd5f
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesService.kt
@@ -0,0 +1,149 @@
+package kr.co.vividnext.sodalive.content.main.tab.series
+
+import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
+import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
+import kr.co.vividnext.sodalive.content.main.tab.RecommendSeriesRepository
+import kr.co.vividnext.sodalive.content.series.ContentSeriesService
+import kr.co.vividnext.sodalive.event.EventService
+import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.rank.RankingService
+import org.springframework.stereotype.Service
+import java.time.DayOfWeek
+import java.time.LocalDateTime
+import java.time.temporal.TemporalAdjusters
+
+@Service
+class AudioContentMainTabSeriesService(
+    private val bannerService: AudioContentBannerService,
+    private val seriesService: ContentSeriesService,
+    private val rankingService: RankingService,
+    private val recommendSeriesRepository: RecommendSeriesRepository,
+    private val eventService: EventService,
+    private val curationRepository: AudioContentCurationQueryRepository
+) {
+    fun fetchData(member: Member): GetContentMainTabSeriesResponse {
+        val isAdult = member.auth != null
+
+        // 메인 배너 (시리즈)
+        val contentBannerList = bannerService.getBannerList(
+            tabId = 2,
+            memberId = member.id!!,
+            isAdult = isAdult
+        )
+
+        val originalAudioDrama = seriesService.getOriginalAudioDramaList(
+            isAdult = isAdult,
+            offset = 0,
+            limit = 20
+        )
+
+        // 일간 랭킹
+        val currentDateTime = LocalDateTime.now()
+        val dailyRankingStartDate = currentDateTime
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+            .minusDays(2)
+        val dailyRankingEndDate = dailyRankingStartDate
+            .plusDays(1)
+
+        val rankSeriesList = rankingService.getSeriesRanking(
+            memberId = member.id!!,
+            isAdult = isAdult,
+            startDate = dailyRankingStartDate,
+            endDate = dailyRankingEndDate
+        )
+
+        // 시리즈 장르
+        val genreList = seriesService.getGenreList(isAdult = isAdult)
+
+        // 장르별 추천 시리즈
+        val recommendSeriesList = if (genreList.isNotEmpty()) {
+            rankingService.getSeriesAllRankingByGenre(
+                memberId = member.id!!,
+                isAdult = isAdult,
+                genreId = genreList[0].id
+            )
+        } else {
+            emptyList()
+        }
+
+        // 새로운 시리즈
+        val newSeriesList = recommendSeriesRepository.getNewSeriesList(isAdult = isAdult)
+
+        // 완결 시리즈 월간 랭킹
+        val monthlyRankingStartDate = currentDateTime
+            .withDayOfMonth(1)
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+            .minusDays(1)
+        val monthlyRankingEndDate = monthlyRankingStartDate
+            .plusMonths(1)
+
+        val rankCompleteSeriesList = rankingService.getCompleteSeriesRanking(
+            memberId = member.id!!,
+            isAdult = isAdult,
+            startDate = monthlyRankingStartDate,
+            endDate = monthlyRankingEndDate
+        )
+
+        val startDate = currentDateTime
+            .withHour(15)
+            .withMinute(0)
+            .withSecond(0)
+            .minusWeeks(1)
+            .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
+        val endDate = startDate
+            .plusDays(6)
+
+        val seriesRankCreatorList = rankingService.fetchCreatorBySeriesRevenueRankTop20(
+            memberId = member.id!!,
+            startDate = startDate.minusDays(1),
+            endDate = endDate
+        )
+
+        val salesRankContentList = if (seriesRankCreatorList.isNotEmpty()) {
+            rankingService.fetchCreatorSeriesBySales(
+                creatorId = seriesRankCreatorList[0].creatorId,
+                isAdult = isAdult
+            )
+        } else {
+            emptyList()
+        }
+
+        // 이벤트 배너
+        val eventBannerList = eventService.getEventList(isAdult = isAdult)
+
+        // 큐레이션
+        val curationList = curationRepository.findByContentMainTabId(tabId = 2, isAdult = isAdult)
+            .map {
+                GetSeriesCurationResponse(
+                    title = it.title,
+                    items = seriesService.fetchSeriesByCurationId(
+                        curationId = it.id!!,
+                        memberId = member.id!!,
+                        isAdult = isAdult
+                    )
+                )
+            }
+
+        return GetContentMainTabSeriesResponse(
+            contentBannerList = contentBannerList,
+            originalAudioDrama = if (originalAudioDrama.size >= 3) {
+                originalAudioDrama
+            } else {
+                emptyList()
+            },
+            rankSeriesList = rankSeriesList,
+            genreList = genreList,
+            recommendSeriesList = recommendSeriesList,
+            newSeriesList = newSeriesList,
+            rankCompleteSeriesList = rankCompleteSeriesList,
+            seriesRankCreatorList = seriesRankCreatorList,
+            salesRankContentList = salesRankContentList,
+            eventBannerList = eventBannerList,
+            curationList = curationList
+        )
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/GetContentMainTabSeriesResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/GetContentMainTabSeriesResponse.kt
new file mode 100644
index 0000000..d7155b0
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/GetContentMainTabSeriesResponse.kt
@@ -0,0 +1,23 @@
+package kr.co.vividnext.sodalive.content.main.tab.series
+
+import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse
+import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
+import kr.co.vividnext.sodalive.content.main.tab.GetRecommendSeriesListResponse
+import kr.co.vividnext.sodalive.content.series.GetSeriesGenreListResponse
+import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse
+import kr.co.vividnext.sodalive.event.GetEventResponse
+
+data class GetContentMainTabSeriesResponse(
+    val tabId: Long = 2,
+    val contentBannerList: List<GetAudioContentBannerResponse>,
+    val originalAudioDrama: List<GetSeriesListResponse.SeriesListItem>,
+    val rankSeriesList: List<GetSeriesListResponse.SeriesListItem>,
+    val genreList: List<GetSeriesGenreListResponse>,
+    val recommendSeriesList: List<GetSeriesListResponse.SeriesListItem>,
+    val newSeriesList: List<GetRecommendSeriesListResponse>,
+    val rankCompleteSeriesList: List<GetSeriesListResponse.SeriesListItem>,
+    val seriesRankCreatorList: List<ContentCreatorResponse>,
+    val salesRankContentList: List<GetSeriesListResponse.SeriesListItem>,
+    val eventBannerList: GetEventResponse,
+    val curationList: List<GetSeriesCurationResponse>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/GetSeriesCurationResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/GetSeriesCurationResponse.kt
new file mode 100644
index 0000000..bb24415
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/GetSeriesCurationResponse.kt
@@ -0,0 +1,8 @@
+package kr.co.vividnext.sodalive.content.main.tab.series
+
+import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse
+
+data class GetSeriesCurationResponse(
+    val title: String,
+    val items: List<GetSeriesListResponse.SeriesListItem>
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt
index 10b8f25..e87f059 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt
@@ -2,15 +2,21 @@ package kr.co.vividnext.sodalive.content.series
 
 import com.querydsl.core.types.dsl.Expressions
 import com.querydsl.jpa.impl.JPAQueryFactory
+import kr.co.vividnext.sodalive.admin.content.series.genre.QSeriesGenre.seriesGenre
 import kr.co.vividnext.sodalive.content.ContentType
 import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
 import kr.co.vividnext.sodalive.content.hashtag.QHashTag.hashTag
+import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCuration.audioContentCuration
+import kr.co.vividnext.sodalive.content.main.curation.QAudioContentCurationItem.audioContentCurationItem
 import kr.co.vividnext.sodalive.content.series.content.GetSeriesContentMinMaxPriceResponse
 import kr.co.vividnext.sodalive.content.series.content.QGetSeriesContentMinMaxPriceResponse
 import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
 import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent
 import kr.co.vividnext.sodalive.creator.admin.content.series.Series
 import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.QSeriesKeyword.seriesKeyword
+import kr.co.vividnext.sodalive.member.MemberRole
+import kr.co.vividnext.sodalive.member.QMember.member
+import kr.co.vividnext.sodalive.member.block.QBlockMember.blockMember
 import org.springframework.data.jpa.repository.JpaRepository
 
 interface ContentSeriesRepository : JpaRepository<Series, Long>, ContentSeriesQueryRepository
@@ -29,6 +35,9 @@ interface ContentSeriesQueryRepository {
     fun getKeywordList(seriesId: Long): List<String>
     fun getSeriesContentMinMaxPrice(seriesId: Long): GetSeriesContentMinMaxPriceResponse
     fun getRecommendSeriesList(isAuth: Boolean, contentType: ContentType, limit: Long): List<Series>
+    fun getOriginalAudioDramaList(isAdult: Boolean, offset: Long = 0, limit: Long = 20): List<Series>
+    fun getGenreList(isAdult: Boolean): List<GetSeriesGenreListResponse>
+    fun findByCurationId(curationId: Long, memberId: Long, isAdult: Boolean): List<Series>
 }
 
 class ContentSeriesQueryRepositoryImpl(
@@ -133,4 +142,61 @@ class ContentSeriesQueryRepositoryImpl(
             .limit(limit)
             .fetch()
     }
+
+    override fun getOriginalAudioDramaList(isAdult: Boolean, offset: Long, limit: Long): List<Series> {
+        var where = series.isOriginal.isTrue
+            .and(series.isActive.isTrue)
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .selectFrom(series)
+            .where(where)
+            .orderBy(Expressions.numberTemplate(Double::class.java, "function('rand')").asc())
+            .offset(offset)
+            .limit(limit)
+            .fetch()
+    }
+
+    override fun getGenreList(isAdult: Boolean): List<GetSeriesGenreListResponse> {
+        var where = seriesGenre.isActive.isTrue
+
+        if (!isAdult) {
+            where = where.and(seriesGenre.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(QGetSeriesGenreListResponse(seriesGenre.id, seriesGenre.genre))
+            .from(seriesGenre)
+            .where(where)
+            .orderBy(seriesGenre.orders.asc())
+            .fetch()
+    }
+
+    override fun findByCurationId(curationId: Long, memberId: Long, isAdult: Boolean): List<Series> {
+        val blockMemberCondition = blockMember.member.id.eq(member.id)
+            .and(blockMember.isActive.isTrue)
+            .and(blockMember.blockedMember.id.eq(memberId))
+
+        var where = series.isActive.isTrue
+            .and(member.isActive.isTrue)
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(blockMember.id.isNull)
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(series)
+            .from(audioContentCurationItem)
+            .innerJoin(audioContentCurationItem.curation, audioContentCuration)
+            .innerJoin(audioContentCurationItem.series, series)
+            .innerJoin(series.member, member)
+            .leftJoin(blockMember).on(blockMemberCondition)
+            .where(where)
+            .fetch()
+    }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesService.kt
index 2a60ae3..1e95021 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesService.kt
@@ -30,6 +30,19 @@ class ContentSeriesService(
     @Value("\${cloud.aws.cloud-front.host}")
     private val coverImageHost: String
 ) {
+    fun getOriginalAudioDramaList(
+        isAdult: Boolean,
+        offset: Long = 0,
+        limit: Long = 20
+    ): List<GetSeriesListResponse.SeriesListItem> {
+        val originalAudioDramaList = repository.getOriginalAudioDramaList(isAdult, offset, limit)
+        return seriesToSeriesListItem(originalAudioDramaList, isAdult)
+    }
+
+    fun getGenreList(isAdult: Boolean): List<GetSeriesGenreListResponse> {
+        return repository.getGenreList(isAdult = isAdult)
+    }
+
     fun getSeriesList(
         creatorId: Long,
         member: Member,
@@ -170,6 +183,15 @@ class ContentSeriesService(
         return seriesToSeriesListItem(seriesList = seriesList, isAdult = member.auth != null)
     }
 
+    fun fetchSeriesByCurationId(
+        curationId: Long,
+        memberId: Long,
+        isAdult: Boolean
+    ): List<GetSeriesListResponse.SeriesListItem> {
+        val seriesList = repository.findByCurationId(curationId = curationId, memberId = memberId, isAdult = isAdult)
+        return seriesToSeriesListItem(seriesList, isAdult)
+    }
+
     private fun seriesToSeriesListItem(
         seriesList: List<Series>,
         isAdult: Boolean
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/GetSeriesGenreListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/GetSeriesGenreListResponse.kt
new file mode 100644
index 0000000..30aa078
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/GetSeriesGenreListResponse.kt
@@ -0,0 +1,8 @@
+package kr.co.vividnext.sodalive.content.series
+
+import com.querydsl.core.annotations.QueryProjection
+
+data class GetSeriesGenreListResponse @QueryProjection constructor(
+    val id: Long,
+    val genre: String
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeService.kt
index 5a3c6a9..b0ad97a 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeService.kt
@@ -6,19 +6,13 @@ import kr.co.vividnext.sodalive.content.ContentType
 import kr.co.vividnext.sodalive.content.SortType
 import kr.co.vividnext.sodalive.content.theme.content.GetContentByThemeResponse
 import kr.co.vividnext.sodalive.member.Member
-import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
-import org.springframework.beans.factory.annotation.Value
 import org.springframework.stereotype.Service
 import org.springframework.transaction.annotation.Transactional
 
 @Service
 class AudioContentThemeService(
     private val queryRepository: AudioContentThemeQueryRepository,
-    private val blockMemberRepository: BlockMemberRepository,
-    private val contentRepository: AudioContentRepository,
-
-    @Value("\${cloud.aws.cloud-front.host}")
-    private val imageHost: String
+    private val contentRepository: AudioContentRepository
 ) {
     @Transactional(readOnly = true)
     fun getThemes(): List<GetAudioContentThemeResponse> {
@@ -46,7 +40,6 @@ class AudioContentThemeService(
         )
 
         val items = contentRepository.findByTheme(
-            cloudfrontHost = imageHost,
             memberId = member.id!!,
             theme = theme.theme,
             sortType = sortType,
@@ -55,9 +48,6 @@ class AudioContentThemeService(
             offset = offset,
             limit = limit
         )
-            .asSequence()
-            .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) }
-            .toList()
 
         return GetContentByThemeResponse(
             theme = theme.theme,
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthController.kt
index e0bc549..ac823f2 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthController.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthController.kt
@@ -24,10 +24,8 @@ class AuthController(private val service: AuthService) {
         if (service.isBlockAuth(authenticateData)) {
             service.signOut(member.id!!)
             throw SodaException("운영정책을 위반하여 이용을 제한합니다.")
-        } else {
-            service.authenticate(authenticateData, member.id!!)
         }
 
-        ApiResponse.ok(null, null)
+        ApiResponse.ok(service.authenticate(authenticateData, member.id!!))
     }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthResponse.kt
new file mode 100644
index 0000000..b061dc6
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthResponse.kt
@@ -0,0 +1,3 @@
+package kr.co.vividnext.sodalive.member.auth
+
+data class AuthResponse(val gender: Int)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthService.kt
index 9d24456..53ce8de 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthService.kt
@@ -73,7 +73,7 @@ class AuthService(
     }
 
     @Transactional
-    fun authenticate(certificate: AuthVerifyCertificate, memberId: Long) {
+    fun authenticate(certificate: AuthVerifyCertificate, memberId: Long): AuthResponse {
         val memberIds = repository.getActiveMemberIdsByDi(di = certificate.di)
         if (memberIds.size >= 3) {
             throw SodaException(
@@ -96,8 +96,9 @@ class AuthService(
             auth.member = member
 
             repository.save(auth)
+            return AuthResponse(gender = certificate.gender)
         } else {
-            throw SodaException("2005년 1월 1일 이전 출생자만 본인인증이 가능합니다.")
+            throw SodaException("${nowYear - 19}년 1월 1일 이전 출생자만 본인인증이 가능합니다.")
         }
     }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/notice/GetNoticeResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/notice/GetNoticeResponse.kt
index aa35bbf..246846e 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/notice/GetNoticeResponse.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/notice/GetNoticeResponse.kt
@@ -1,5 +1,7 @@
 package kr.co.vividnext.sodalive.notice
 
+import com.querydsl.core.annotations.QueryProjection
+
 data class GetNoticeResponse(
     val totalCount: Int,
     val noticeList: List<NoticeItem>
@@ -11,3 +13,8 @@ data class NoticeItem(
     val content: String,
     val date: String
 )
+
+data class NoticeTitleItem @QueryProjection constructor(
+    val id: Long,
+    val title: String
+)
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceNoticeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceNoticeService.kt
index 10d28c2..f76e57f 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceNoticeService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceNoticeService.kt
@@ -63,4 +63,8 @@ class ServiceNoticeService(private val repository: ServiceServiceNoticeRepositor
 
         return GetNoticeResponse(totalCount, noticeList)
     }
+
+    fun getLatestNotice(): NoticeTitleItem? {
+        return repository.getLatestNotice()
+    }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceServiceNoticeRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceServiceNoticeRepository.kt
index 90d5a47..d5772f5 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceServiceNoticeRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/notice/ServiceServiceNoticeRepository.kt
@@ -12,6 +12,7 @@ interface ServiceServiceNoticeRepository : JpaRepository<ServiceNotice, Long>, S
 interface ServiceNoticeQueryRepository {
     fun getNoticeTotalCount(): Int
     fun getNoticeList(pageable: Pageable): List<ServiceNotice>
+    fun getLatestNotice(): NoticeTitleItem?
 }
 
 @Repository
@@ -34,4 +35,18 @@ class ServiceNoticeQueryRepositoryImpl(private val queryFactory: JPAQueryFactory
             .orderBy(serviceNotice.id.desc())
             .fetch()
     }
+
+    override fun getLatestNotice(): NoticeTitleItem? {
+        return queryFactory
+            .select(
+                QNoticeTitleItem(
+                    serviceNotice.id,
+                    serviceNotice.title
+                )
+            )
+            .from(serviceNotice)
+            .where(serviceNotice.isActive.isTrue)
+            .orderBy(serviceNotice.id.desc())
+            .fetchFirst()
+    }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt
new file mode 100644
index 0000000..2250eee
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt
@@ -0,0 +1,497 @@
+package kr.co.vividnext.sodalive.rank
+
+import com.querydsl.core.types.dsl.Expressions
+import com.querydsl.jpa.impl.JPAQueryFactory
+import kr.co.vividnext.sodalive.admin.content.series.genre.QSeriesGenre.seriesGenre
+import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
+import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
+import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
+import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse
+import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
+import kr.co.vividnext.sodalive.content.main.QContentCreatorResponse
+import kr.co.vividnext.sodalive.content.main.QGetAudioContentRankingItem
+import kr.co.vividnext.sodalive.content.order.QOrder.order
+import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme
+import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
+import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent
+import kr.co.vividnext.sodalive.creator.admin.content.series.Series
+import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesState
+import kr.co.vividnext.sodalive.explorer.QCreatorRanking.creatorRanking
+import kr.co.vividnext.sodalive.member.Member
+import kr.co.vividnext.sodalive.member.MemberRole
+import kr.co.vividnext.sodalive.member.QMember.member
+import kr.co.vividnext.sodalive.member.block.QBlockMember.blockMember
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.stereotype.Repository
+import java.time.LocalDateTime
+
+@Repository
+class RankingRepository(
+    private val queryFactory: JPAQueryFactory,
+
+    @Value("\${cloud.aws.cloud-front.host}")
+    private val imageHost: String
+) {
+    fun getCreatorRankings(): List<Member> {
+        return queryFactory
+            .select(member)
+            .from(creatorRanking)
+            .innerJoin(creatorRanking.member, member)
+            .orderBy(creatorRanking.ranking.asc())
+            .fetch()
+    }
+
+    fun getAudioContentRanking(
+        memberId: Long,
+        isAdult: Boolean,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime,
+        offset: Long,
+        limit: Long,
+        sortType: String,
+        theme: String = ""
+    ): List<GetAudioContentRankingItem> {
+        val blockMemberCondition = blockMember.member.id.eq(member.id)
+            .and(blockMember.isActive.isTrue)
+            .and(blockMember.blockedMember.id.eq(memberId))
+
+        var where = audioContent.isActive.isTrue
+            .and(audioContent.member.isActive.isTrue)
+            .and(audioContent.member.isNotNull)
+            .and(audioContent.member.role.eq(MemberRole.CREATOR))
+            .and(audioContent.duration.isNotNull)
+            .and(audioContentTheme.isActive.isTrue)
+            .and(audioContent.limited.isNull)
+            .and(blockMember.id.isNull)
+
+        if (!isAdult) {
+            where = where.and(audioContent.isAdult.isFalse)
+        }
+
+        if (theme.isNotBlank()) {
+            where = where.and(audioContentTheme.theme.eq(theme))
+        }
+
+        var select = queryFactory
+            .select(
+                QGetAudioContentRankingItem(
+                    audioContent.id,
+                    audioContent.title,
+                    audioContent.coverImage.prepend("/").prepend(imageHost),
+                    audioContentTheme.theme,
+                    audioContent.price,
+                    audioContent.duration,
+                    member.id,
+                    member.nickname
+                )
+            )
+
+        select = when (sortType) {
+            "후원" -> {
+                select
+                    .from(audioContentComment)
+                    .innerJoin(audioContentComment.audioContent, audioContent)
+                    .innerJoin(audioContent.member, member)
+                    .innerJoin(audioContent.theme, audioContentTheme)
+                    .leftJoin(blockMember).on(blockMemberCondition)
+                    .where(
+                        where
+                            .and(audioContentComment.isActive.isTrue)
+                            .and(audioContentComment.donationCan.gt(0))
+                            .and(audioContentComment.createdAt.goe(startDate))
+                            .and(audioContentComment.createdAt.lt(endDate))
+                    )
+                    .groupBy(audioContent.id)
+                    .orderBy(audioContentComment.donationCan.sum().desc(), audioContent.createdAt.asc())
+            }
+
+            "댓글" -> {
+                select
+                    .from(audioContentComment)
+                    .innerJoin(audioContentComment.audioContent, audioContent)
+                    .innerJoin(audioContent.member, member)
+                    .innerJoin(audioContent.theme, audioContentTheme)
+                    .leftJoin(blockMember).on(blockMemberCondition)
+                    .where(
+                        where
+                            .and(audioContentComment.isActive.isTrue)
+                            .and(audioContentComment.createdAt.goe(startDate))
+                            .and(audioContentComment.createdAt.lt(endDate))
+                    )
+                    .groupBy(audioContentComment.audioContent.id)
+                    .orderBy(audioContentComment.id.count().desc(), audioContent.createdAt.asc())
+            }
+
+            "좋아요" -> {
+                select
+                    .from(audioContentLike)
+                    .innerJoin(audioContentLike.audioContent, audioContent)
+                    .innerJoin(audioContent.member, member)
+                    .innerJoin(audioContent.theme, audioContentTheme)
+                    .leftJoin(blockMember).on(blockMemberCondition)
+                    .where(
+                        where
+                            .and(audioContentLike.isActive.isTrue)
+                            .and(audioContentLike.createdAt.goe(startDate))
+                            .and(audioContentLike.createdAt.lt(endDate))
+                    )
+                    .groupBy(audioContentLike.audioContent.id)
+                    .orderBy(audioContentLike.id.count().desc(), audioContent.createdAt.asc())
+            }
+
+            else -> {
+                select
+                    .from(order)
+                    .innerJoin(order.audioContent, audioContent)
+                    .innerJoin(audioContent.member, member)
+                    .innerJoin(audioContent.theme, audioContentTheme)
+                    .leftJoin(blockMember).on(blockMemberCondition)
+                    .where(
+                        where
+                            .and(order.isActive.isTrue)
+                            .and(order.createdAt.goe(startDate))
+                            .and(order.createdAt.lt(endDate))
+                    )
+                    .groupBy(audioContent.id)
+                    .orderBy(order.can.sum().desc(), audioContent.createdAt.asc())
+            }
+        }
+
+        return select
+            .offset(offset)
+            .limit(limit)
+            .fetch()
+    }
+
+    fun getSeriesRanking(
+        memberId: Long,
+        isAdult: Boolean,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime
+    ): List<Series> {
+        val blockMemberCondition = blockMember.member.id.eq(member.id)
+            .and(blockMember.isActive.isTrue)
+            .and(blockMember.blockedMember.id.eq(memberId))
+
+        var where = series.isActive.isTrue
+            .and(audioContent.isActive.isTrue)
+            .and(member.isActive.isTrue)
+            .and(member.isNotNull)
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(audioContent.duration.isNotNull)
+            .and(audioContent.limited.isNull)
+            .and(blockMember.id.isNull)
+            .and(order.isActive.isTrue)
+            .and(order.createdAt.goe(startDate))
+            .and(order.createdAt.lt(endDate))
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(series)
+            .from(seriesContent)
+            .innerJoin(seriesContent.series, series)
+            .innerJoin(seriesContent.content, audioContent)
+            .innerJoin(series.member, member)
+            .leftJoin(order).on(audioContent.id.eq(order.audioContent.id))
+            .leftJoin(blockMember).on(blockMemberCondition)
+            .where(where)
+            .groupBy(series.id)
+            .orderBy(
+                order.can.sum().desc(),
+                Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
+            )
+            .offset(0)
+            .limit(10)
+            .fetch()
+    }
+
+    fun getCompleteSeriesRanking(
+        memberId: Long,
+        isAdult: Boolean,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime,
+        offset: Long,
+        limit: Long
+    ): List<Series> {
+        val blockMemberCondition = blockMember.member.id.eq(member.id)
+            .and(blockMember.isActive.isTrue)
+            .and(blockMember.blockedMember.id.eq(memberId))
+
+        var where = series.isActive.isTrue
+            .and(series.state.eq(SeriesState.COMPLETE))
+            .and(audioContent.isActive.isTrue)
+            .and(member.isActive.isTrue)
+            .and(member.isNotNull)
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(audioContent.duration.isNotNull)
+            .and(audioContent.limited.isNull)
+            .and(blockMember.id.isNull)
+            .and(order.isActive.isTrue)
+            .and(order.createdAt.goe(startDate))
+            .and(order.createdAt.lt(endDate))
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(series)
+            .from(seriesContent)
+            .innerJoin(seriesContent.series, series)
+            .innerJoin(seriesContent.content, audioContent)
+            .innerJoin(series.member, member)
+            .leftJoin(order).on(audioContent.id.eq(order.audioContent.id))
+            .leftJoin(blockMember).on(blockMemberCondition)
+            .where(where)
+            .groupBy(series.id)
+            .orderBy(
+                order.can.sum().desc(),
+                Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
+            )
+            .offset(offset)
+            .limit(limit)
+            .fetch()
+    }
+
+    fun getSeriesAllRankingByGenre(memberId: Long, isAdult: Boolean, genreId: Long): List<Series> {
+        val blockMemberCondition = blockMember.member.id.eq(member.id)
+            .and(blockMember.isActive.isTrue)
+            .and(blockMember.blockedMember.id.eq(memberId))
+
+        var where = series.isActive.isTrue
+            .and(seriesGenre.id.eq(genreId))
+            .and(audioContent.isActive.isTrue)
+            .and(member.isActive.isTrue)
+            .and(member.isNotNull)
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(audioContent.duration.isNotNull)
+            .and(audioContent.limited.isNull)
+            .and(blockMember.id.isNull)
+            .and(order.isActive.isTrue)
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(series)
+            .from(seriesContent)
+            .innerJoin(seriesContent.series, series)
+            .innerJoin(series.genre, seriesGenre)
+            .innerJoin(seriesContent.content, audioContent)
+            .innerJoin(series.member, member)
+            .leftJoin(order).on(audioContent.id.eq(order.audioContent.id))
+            .leftJoin(blockMember).on(blockMemberCondition)
+            .where(where)
+            .groupBy(series.id)
+            .orderBy(
+                order.can.sum().desc(),
+                Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
+            )
+            .offset(0)
+            .limit(20)
+            .fetch()
+    }
+
+    fun fetchCreatorByContentRevenueRankTop20(
+        memberId: Long,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime
+    ): List<ContentCreatorResponse> {
+        val blockMemberCondition = blockMember.member.id.eq(member.id)
+            .and(blockMember.isActive.isTrue)
+            .and(blockMember.blockedMember.id.eq(memberId))
+
+        val ordersCondition = order.audioContent.id.eq(audioContent.id)
+            .and(order.isActive.isTrue)
+            .and(order.createdAt.goe(startDate))
+            .and(order.createdAt.lt(startDate))
+
+        val where = member.isActive.isTrue
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(audioContent.isActive.isTrue)
+            .and(audioContent.duration.isNotNull)
+            .and(audioContent.limited.isNull)
+            .and(blockMember.id.isNull)
+
+        return queryFactory
+            .select(
+                QContentCreatorResponse(
+                    member.id,
+                    member.nickname,
+                    member.profileImage.prepend("/").prepend(imageHost)
+                )
+            )
+            .from(member)
+            .innerJoin(audioContent).on(member.id.eq(audioContent.member.id))
+            .leftJoin(order).on(ordersCondition)
+            .leftJoin(blockMember).on(blockMemberCondition)
+            .where(where)
+            .groupBy(member.id)
+            .having(audioContent.id.count().goe(4))
+            .orderBy(
+                order.can.sum().desc(),
+                Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
+            )
+            .offset(0)
+            .limit(20)
+            .fetch()
+    }
+
+    fun fetchCreatorContentBySalesTop2(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
+        var where = member.isActive.isTrue
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(audioContent.isActive.isTrue)
+            .and(audioContent.duration.isNotNull)
+            .and(audioContent.limited.isNull)
+            .and(order.isActive.isTrue)
+            .and(member.id.eq(creatorId))
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(
+                QGetAudioContentRankingItem(
+                    audioContent.id,
+                    audioContent.title,
+                    audioContent.coverImage.prepend("/").prepend(imageHost),
+                    audioContentTheme.theme,
+                    audioContent.price,
+                    audioContent.duration,
+                    member.id,
+                    member.nickname
+                )
+            )
+            .from(order)
+            .innerJoin(order.audioContent, audioContent)
+            .innerJoin(audioContent.member, member)
+            .where(where)
+            .groupBy(audioContent.id)
+            .orderBy(order.can.sum().desc())
+            .offset(0)
+            .limit(2)
+            .fetch()
+    }
+
+    fun fetchCreatorContentBySalesCountTop2(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
+        var where = member.isActive.isTrue
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(audioContent.isActive.isTrue)
+            .and(audioContent.duration.isNotNull)
+            .and(audioContent.limited.isNull)
+            .and(order.isActive.isTrue)
+            .and(member.id.eq(creatorId))
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(
+                QGetAudioContentRankingItem(
+                    audioContent.id,
+                    audioContent.title,
+                    audioContent.coverImage.prepend("/").prepend(imageHost),
+                    audioContentTheme.theme,
+                    audioContent.price,
+                    audioContent.duration,
+                    member.id,
+                    member.nickname
+                )
+            )
+            .from(order)
+            .innerJoin(order.audioContent, audioContent)
+            .innerJoin(audioContent.member, member)
+            .where(where)
+            .groupBy(audioContent.id)
+            .orderBy(order.id.count().desc())
+            .offset(0)
+            .limit(2)
+            .fetch()
+    }
+
+    fun fetchCreatorBySeriesRevenueRankTop20(
+        memberId: Long,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime
+    ): List<ContentCreatorResponse> {
+        val blockMemberCondition = blockMember.member.id.eq(member.id)
+            .and(blockMember.isActive.isTrue)
+            .and(blockMember.blockedMember.id.eq(memberId))
+
+        val ordersCondition = order.audioContent.id.eq(audioContent.id)
+            .and(order.isActive.isTrue)
+            .and(order.createdAt.goe(startDate))
+            .and(order.createdAt.lt(startDate))
+
+        val where = member.isActive.isTrue
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(series.isActive.isTrue)
+            .and(audioContent.isActive.isTrue)
+            .and(audioContent.duration.isNotNull)
+            .and(audioContent.limited.isNull)
+            .and(blockMember.id.isNull)
+
+        return queryFactory
+            .select(
+                QContentCreatorResponse(
+                    member.id,
+                    member.nickname,
+                    member.profileImage.prepend("/").prepend(imageHost)
+                )
+            )
+            .from(seriesContent)
+            .innerJoin(seriesContent.series, series)
+            .innerJoin(seriesContent.content, audioContent)
+            .innerJoin(series.member, member)
+            .leftJoin(order).on(ordersCondition)
+            .leftJoin(blockMember).on(blockMemberCondition)
+            .where(where)
+            .groupBy(member.id)
+            .having(series.id.countDistinct().goe(3))
+            .orderBy(
+                order.can.sum().desc(),
+                Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
+            )
+            .offset(0)
+            .limit(20)
+            .fetch()
+    }
+
+    fun fetchCreatorSeriesBySales(creatorId: Long, isAdult: Boolean): List<Series> {
+        var where = member.isActive.isTrue
+            .and(member.role.eq(MemberRole.CREATOR))
+            .and(series.isActive.isTrue)
+            .and(audioContent.isActive.isTrue)
+            .and(audioContent.duration.isNotNull)
+            .and(audioContent.limited.isNull)
+            .and(order.isActive.isTrue)
+            .and(member.id.eq(creatorId))
+
+        if (!isAdult) {
+            where = where.and(series.isAdult.isFalse)
+        }
+
+        return queryFactory
+            .select(series)
+            .from(seriesContent)
+            .innerJoin(seriesContent.series, series)
+            .innerJoin(seriesContent.content, audioContent)
+            .innerJoin(series.member, member)
+            .leftJoin(order).on(audioContent.id.eq(order.audioContent.id))
+            .where(where)
+            .groupBy(series.id)
+            .orderBy(
+                order.can.sum().desc(),
+                Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
+            )
+            .offset(0)
+            .limit(10)
+            .fetch()
+    }
+}
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt
new file mode 100644
index 0000000..a3504fc
--- /dev/null
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt
@@ -0,0 +1,198 @@
+package kr.co.vividnext.sodalive.rank
+
+import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse
+import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
+import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse
+import kr.co.vividnext.sodalive.content.series.content.ContentSeriesContentRepository
+import kr.co.vividnext.sodalive.creator.admin.content.series.Series
+import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
+import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesState
+import kr.co.vividnext.sodalive.explorer.GetExplorerSectionResponse
+import kr.co.vividnext.sodalive.member.MemberService
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.stereotype.Service
+import java.time.LocalDateTime
+
+@Service
+class RankingService(
+    private val repository: RankingRepository,
+    private val memberService: MemberService,
+    private val seriesContentRepository: ContentSeriesContentRepository,
+
+    @Value("\${cloud.aws.cloud-front.host}")
+    private val imageHost: String
+) {
+    fun getCreatorRanking(memberId: Long, rankingDate: String): GetExplorerSectionResponse {
+        val creatorRankings = repository
+            .getCreatorRankings()
+            .filter { !memberService.isBlocked(blockedMemberId = memberId, memberId = it.id!!) }
+            .map { it.toExplorerSectionCreator(imageHost) }
+
+        return GetExplorerSectionResponse(
+            title = "인기 크리에이터",
+            coloredTitle = "인기",
+            color = "FF5C49",
+            desc = rankingDate,
+            creators = creatorRankings
+        )
+    }
+
+    fun getContentRanking(
+        memberId: Long,
+        isAdult: Boolean,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime,
+        offset: Long = 0,
+        limit: Long = 12,
+        sortType: String = "매출",
+        theme: String = ""
+    ): List<GetAudioContentRankingItem> {
+        return repository.getAudioContentRanking(
+            memberId = memberId,
+            isAdult = isAdult,
+            startDate = startDate,
+            endDate = endDate,
+            offset = offset,
+            limit = limit,
+            sortType = sortType,
+            theme = theme
+        )
+    }
+
+    fun getSeriesRanking(
+        memberId: Long,
+        isAdult: Boolean,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime
+    ): List<GetSeriesListResponse.SeriesListItem> {
+        val seriesList = repository.getSeriesRanking(memberId, isAdult, startDate, endDate)
+        return seriesToSeriesListItem(seriesList = seriesList, isAdult = isAdult)
+    }
+
+    fun getSeriesAllRankingByGenre(
+        memberId: Long,
+        isAdult: Boolean,
+        genreId: Long
+    ): List<GetSeriesListResponse.SeriesListItem> {
+        val seriesList = repository.getSeriesAllRankingByGenre(
+            memberId = memberId,
+            isAdult = isAdult,
+            genreId = genreId
+        )
+        return seriesToSeriesListItem(seriesList = seriesList, isAdult = isAdult)
+    }
+
+    fun getCompleteSeriesRanking(
+        memberId: Long,
+        isAdult: Boolean,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime,
+        offset: Long = 0,
+        limit: Long = 10
+    ): List<GetSeriesListResponse.SeriesListItem> {
+        val seriesList = repository.getCompleteSeriesRanking(
+            memberId = memberId,
+            isAdult = isAdult,
+            startDate = startDate,
+            endDate = endDate,
+            offset = offset,
+            limit = limit
+        )
+        return seriesToSeriesListItem(seriesList = seriesList, isAdult = isAdult)
+    }
+
+    private fun seriesToSeriesListItem(
+        seriesList: List<Series>,
+        isAdult: Boolean
+    ): List<GetSeriesListResponse.SeriesListItem> {
+        return seriesList
+            .map {
+                GetSeriesListResponse.SeriesListItem(
+                    seriesId = it.id!!,
+                    title = it.title,
+                    coverImage = "$imageHost/${it.coverImage!!}",
+                    publishedDaysOfWeek = publishedDaysOfWeekText(it.publishedDaysOfWeek),
+                    isComplete = it.state == SeriesState.COMPLETE,
+                    creator = GetSeriesListResponse.SeriesListItemCreator(
+                        creatorId = it.member!!.id!!,
+                        nickname = it.member!!.nickname,
+                        profileImage = "$imageHost/${it.member!!.profileImage!!}"
+                    )
+                )
+            }
+            .map {
+                it.numberOfContent = seriesContentRepository.getContentCount(
+                    seriesId = it.seriesId,
+                    isAdult = isAdult
+                )
+
+                it
+            }
+            .map {
+                val nowDateTime = LocalDateTime.now()
+
+                it.isNew = seriesContentRepository.isNewContent(
+                    seriesId = it.seriesId,
+                    isAdult = isAdult,
+                    fromDate = nowDateTime.minusDays(7),
+                    nowDate = nowDateTime
+                )
+
+                it
+            }
+    }
+
+    private fun publishedDaysOfWeekText(publishedDaysOfWeek: Set<SeriesPublishedDaysOfWeek>): String {
+        val dayOfWeekText = publishedDaysOfWeek.toList().sortedBy { it.ordinal }
+            .map {
+                when (it) {
+                    SeriesPublishedDaysOfWeek.SUN -> "일"
+                    SeriesPublishedDaysOfWeek.MON -> "월"
+                    SeriesPublishedDaysOfWeek.TUE -> "화"
+                    SeriesPublishedDaysOfWeek.WED -> "수"
+                    SeriesPublishedDaysOfWeek.THU -> "목"
+                    SeriesPublishedDaysOfWeek.FRI -> "금"
+                    SeriesPublishedDaysOfWeek.SAT -> "토"
+                    SeriesPublishedDaysOfWeek.RANDOM -> "랜덤"
+                }
+            }
+            .joinToString(", ") { it }
+
+        return if (publishedDaysOfWeek.contains(SeriesPublishedDaysOfWeek.RANDOM)) {
+            dayOfWeekText
+        } else if (publishedDaysOfWeek.size < 7) {
+            "매주 $dayOfWeekText"
+        } else {
+            "매일"
+        }
+    }
+
+    fun fetchCreatorByContentRevenueRankTop20(
+        memberId: Long,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime
+    ): List<ContentCreatorResponse> {
+        return repository.fetchCreatorByContentRevenueRankTop20(memberId, startDate, endDate)
+    }
+
+    fun fetchCreatorContentBySalesTop2(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
+        return repository.fetchCreatorContentBySalesTop2(creatorId, isAdult)
+    }
+
+    fun fetchCreatorContentBySalesCountTop2(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
+        return repository.fetchCreatorContentBySalesCountTop2(creatorId, isAdult)
+    }
+
+    fun fetchCreatorBySeriesRevenueRankTop20(
+        memberId: Long,
+        startDate: LocalDateTime,
+        endDate: LocalDateTime
+    ): List<ContentCreatorResponse> {
+        return repository.fetchCreatorBySeriesRevenueRankTop20(memberId, startDate, endDate)
+    }
+
+    fun fetchCreatorSeriesBySales(creatorId: Long, isAdult: Boolean): List<GetSeriesListResponse.SeriesListItem> {
+        val seriesList = repository.fetchCreatorSeriesBySales(creatorId = creatorId, isAdult = isAdult)
+        return seriesToSeriesListItem(seriesList, isAdult)
+    }
+}