Skip to content

Commit

Permalink
test: 코루틴 테스트용 커밋
Browse files Browse the repository at this point in the history
  • Loading branch information
belljun3395 committed Jan 11, 2025
1 parent 47ea415 commit f1717c0
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 97 deletions.
1 change: 1 addition & 0 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
/** jooq */
jooqCodegen("org.jooq:jooq-meta-extensions:${DependencyVersion.JOOQ}")
implementation("org.springframework.boot:spring-boot-starter-jooq")
implementation("org.jooq:jooq-kotlin-coroutines:${DependencyVersion.JOOQ}")

/** flyway */
implementation("org.flywaydb:flyway-core:${DependencyVersion.FLYWAY}")
Expand Down
13 changes: 13 additions & 0 deletions api/src/main/kotlin/com/few/api/domain/article/repo/ArticleDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.few.api.domain.article.repo.record.*
import com.few.api.domain.common.vo.MemberType
import jooq.jooq_dsl.tables.*
import jooq.jooq_dsl.tables.MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE
import kotlinx.coroutines.reactive.awaitSingle
import kotlinx.coroutines.reactor.awaitSingle
import org.jooq.*
import org.jooq.impl.DSL
import org.springframework.cache.annotation.Cacheable
Expand Down Expand Up @@ -131,6 +133,17 @@ class ArticleDao(
selectArticleContentsQuery(articleIds)
.fetchInto(SelectArticleContentsRecord::class.java)

suspend fun selectArticleContentsAsync(articleId: Long): SelectArticleContentsRecord =
dslContext
.select(
ArticleIfo.ARTICLE_IFO.ARTICLE_MST_ID.`as`(SelectArticleContentsRecord::articleId.name),
ArticleIfo.ARTICLE_IFO.CONTENT.`as`(SelectArticleContentsRecord::content.name),
).from(ArticleIfo.ARTICLE_IFO)
.where(ArticleIfo.ARTICLE_IFO.ARTICLE_MST_ID.eq(articleId))
.and(ArticleIfo.ARTICLE_IFO.DELETED_AT.isNull)
.awaitSingle()
.into(SelectArticleContentsRecord::class.java)

fun selectArticleContentsQuery(articleIds: Set<Long>) =
dslContext
.select(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.few.api.domain.article.repo.record.ArticleMainCardRecord
import com.few.api.domain.article.repo.support.ArticleMainCardMapper
import com.few.api.domain.article.repo.support.CommonJsonMapper
import jooq.jooq_dsl.tables.ArticleMainCard.ARTICLE_MAIN_CARD
import kotlinx.coroutines.reactive.awaitSingle
import org.jooq.*
import org.jooq.impl.DSL.*
import org.springframework.stereotype.Repository
Expand All @@ -18,9 +19,34 @@ class ArticleMainCardDao(
) {
fun selectArticleMainCardsRecord(articleIds: Set<Long>): Set<ArticleMainCardRecord> =
selectArticleMainCardsRecordQuery(articleIds)
.query
.fetch(articleMainCardMapper)
.toSet()

suspend fun selectArticleMainCardsRecordAsync(articleId: Long): ArticleMainCardRecord? =
dslContext
.select(
ARTICLE_MAIN_CARD.ID.`as`(ArticleMainCardRecord::articleId.name),
ARTICLE_MAIN_CARD.TITLE.`as`(ArticleMainCardRecord::articleTitle.name),
ARTICLE_MAIN_CARD.MAIN_IMAGE_URL.`as`(ArticleMainCardRecord::mainImageUrl.name),
ARTICLE_MAIN_CARD.CATEGORY_CD.`as`(ArticleMainCardRecord::categoryCd.name),
ARTICLE_MAIN_CARD.CREATED_AT.`as`(ArticleMainCardRecord::createdAt.name),
ARTICLE_MAIN_CARD.WRITER_ID.`as`(ArticleMainCardRecord::writerId.name),
ARTICLE_MAIN_CARD.WRITER_EMAIL.`as`(ArticleMainCardRecord::writerEmail.name),
jsonGetAttributeAsText(
ARTICLE_MAIN_CARD.WRITER_DESCRIPTION,
"name",
).`as`(ArticleMainCardRecord::writerName.name),
jsonGetAttribute(ARTICLE_MAIN_CARD.WRITER_DESCRIPTION, "url").`as`(ArticleMainCardRecord::writerUrl.name),
jsonGetAttribute(ARTICLE_MAIN_CARD.WRITER_DESCRIPTION, "imageUrl").`as`(ArticleMainCardRecord::writerImgUrl.name),
ARTICLE_MAIN_CARD.WORKBOOKS.`as`(ArticleMainCardRecord::workbooks.name),
).from(ARTICLE_MAIN_CARD)
.where(ARTICLE_MAIN_CARD.ID.eq(articleId))
.awaitSingle()
.map {
articleMainCardMapper.map(it)
}

fun selectArticleMainCardsRecordQuery(articleIds: Set<Long>) =
dslContext
.select(
Expand All @@ -40,7 +66,6 @@ class ArticleMainCardDao(
ARTICLE_MAIN_CARD.WORKBOOKS.`as`(ArticleMainCardRecord::workbooks.name),
).from(ARTICLE_MAIN_CARD)
.where(ARTICLE_MAIN_CARD.ID.`in`(articleIds))
.query

/**
* NOTE - The query performed in this function do not save the workbook.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ import com.few.api.domain.article.repo.record.SelectArticleViewsRecord
import com.few.api.domain.common.vo.CategoryType
import jooq.jooq_dsl.tables.ArticleViewCount.ARTICLE_VIEW_COUNT
import jooq.jooq_dsl.tables.SendArticleEventHistory.SEND_ARTICLE_EVENT_HISTORY
import kotlinx.coroutines.reactive.awaitSingle
import kotlinx.coroutines.reactor.awaitSingle
import org.jooq.DSLContext
import org.jooq.impl.DSL.*
import org.springframework.stereotype.Repository
import reactor.core.publisher.Flux

object TempTable {
const val ARTICLE_ID_COLUMN = "ARTICLE_ID"
Expand Down Expand Up @@ -93,8 +96,14 @@ class ArticleViewCountDao(

fun selectRankByViews(query: SelectRankByViewsQuery): Long? =
selectRankByViewsQuery(query)
.query
.fetchOneInto(Long::class.java)

suspend fun selectRankByViewsAsync(query: SelectRankByViewsQuery): Long? =
selectRankByViewsQuery(query)
.awaitSingle()
.into(Long::class.java)

fun selectRankByViewsQuery(query: SelectRankByViewsQuery) =
dslContext
.select(field(ROW_RANK_TABLE_OFFSET, Long::class.java))
Expand Down Expand Up @@ -135,12 +144,19 @@ class ArticleViewCountDao(
).asTable(TOTAL_VIEW_COUNT_TABLE),
).asTable(ROW_RANK_TABLE),
).where(field(ROW_RANK_TABLE_ARTICLE_ID).eq(query.articleId))
.query

fun selectArticlesOrderByViews(query: SelectArticlesOrderByViewsQuery): List<SelectArticleViewsRecord> =
selectArticlesOrderByViewsQuery(query)
.query
.fetchInto(SelectArticleViewsRecord::class.java)

suspend fun selectArticlesOrderByViewsAsync(query: SelectArticlesOrderByViewsQuery): List<SelectArticleViewsRecord> =
Flux
.from(selectArticlesOrderByViewsQuery(query).query)
.map { it.into(SelectArticleViewsRecord::class.java) }
.collectList()
.awaitSingle()

fun selectArticlesOrderByViewsQuery(query: SelectArticlesOrderByViewsQuery) =
dslContext
.select(
Expand Down Expand Up @@ -186,5 +202,4 @@ class ArticleViewCountDao(
else -> field(ARTICLE_VIEW_COUNT_OFFSET_TABLE_CATEGORY_CD).eq(query.category.code)
},
).limit(11)
.query
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import com.few.api.domain.article.repo.ArticleViewCountDao
import com.few.api.domain.article.repo.query.SelectArticlesOrderByViewsQuery
import com.few.api.domain.article.repo.query.SelectRankByViewsQuery
import com.few.api.domain.article.repo.record.ArticleMainCardRecord
import com.few.api.domain.article.repo.record.SelectArticleContentsRecord
import com.few.api.domain.article.repo.record.SelectArticleViewsRecord
import com.few.api.domain.article.usecase.dto.*
import com.few.api.domain.common.exception.NotFoundException
import com.few.api.domain.common.vo.CategoryType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.springframework.stereotype.Component
import java.util.*
import kotlin.Comparator
Expand All @@ -24,88 +26,91 @@ class BrowseArticlesUseCase(
) {
@ApiTransactional(readOnly = true)
fun execute(useCaseIn: ReadArticlesUseCaseIn): ReadArticlesUseCaseOut {
/**
* 아티클 조회수 테이블에서 마지막 읽은 아티클 아이디, 카테고리를 기반으로 Offset(테이블 row 순위)을 구함
*/
val offset =
if (useCaseIn.prevArticleId <= 0) {
0L
} else {
articleViewCountDao.selectRankByViews(
SelectRankByViewsQuery(useCaseIn.prevArticleId),
) ?: 0
}

/**
* 구한 Offset을 기준으로 이번 스크롤에서 보여줄 아티클 11개를 뽑아옴
* 카테고리 별, 조회수 순 11개. 조회수가 같을 경우 최신 아티클이 우선순위를 가짐
*/
val articleViewsRecords: MutableList<SelectArticleViewsRecord> =
articleViewCountDao
.selectArticlesOrderByViews(
SelectArticlesOrderByViewsQuery(
offset,
CategoryType.fromCode(useCaseIn.categoryCd) ?: CategoryType.All,
),
).toMutableList()

/**
* 11개를 조회한 상황에서 11개가 조회되지 않았다면 마지막 스크롤로 판단
*/
val isLast =
if (articleViewsRecords.size == 11) {
articleViewsRecords.removeAt(10)
false
} else {
true
return runBlocking(Dispatchers.IO) {
/**
* 아티클 조회수 테이블에서 마지막 읽은 아티클 아이디, 카테고리를 기반으로 Offset(테이블 row 순위)을 구함
*/
val offset =
if (useCaseIn.prevArticleId <= 0) {
0L
} else {
articleViewCountDao.selectRankByViewsAsync(
SelectRankByViewsQuery(useCaseIn.prevArticleId),
) ?: 0L
}

/**
* 구한 Offset을 기준으로 이번 스크롤에서 보여줄 아티클 11개를 뽑아옴
* 카테고리 별, 조회수 순 11개. 조회수가 같을 경우 최신 아티클이 우선순위를 가짐
*/
val articleViewsRecords: MutableList<SelectArticleViewsRecord> =
articleViewCountDao
.selectArticlesOrderByViewsAsync(
SelectArticlesOrderByViewsQuery(
offset,
CategoryType.fromCode(useCaseIn.categoryCd) ?: CategoryType.All,
),
).toMutableList()

/**
* 11개를 조회한 상황에서 11개가 조회되지 않았다면 마지막 스크롤로 판단
*/
val isLast =
if (articleViewsRecords.size == 11) {
articleViewsRecords.removeAt(10)
false
} else {
true
}

val recordViewIds = articleViewsRecords.map { it.articleId }.toSet()
val articleMainCardRecords = mutableSetOf<ArticleMainCardRecord>()
recordViewIds.forEach { id ->
async {
val articleMainCardRecord = articleMainCardDao.selectArticleMainCardsRecordAsync(id)
val selectArticleContentsRecord = articleDao.selectArticleContentsAsync(id)

articleMainCardRecord?.apply {
this.content = selectArticleContentsRecord.content
articleMainCardRecords.add(this)
}
}.await()
}

/**
* ARTICLE_MAIN_CARD 테이블에서 이번 스크롤에서 보여줄 10개 아티클 조회 (TODO: 캐싱 적용)
*/
val articleMainCardRecords: Set<ArticleMainCardRecord> =
articleMainCardDao.selectArticleMainCardsRecord(articleViewsRecords.map { it.articleId }.toSet())

/**
* 아티클 컨텐츠는 ARTICLE_MAIN_CARD가 아닌 ARTICLE_IFO에서 조회 (TODO: 캐싱 적용)
*/
val selectArticleContentsRecords: List<SelectArticleContentsRecord> =
articleDao.selectArticleContents(articleMainCardRecords.map { it.articleId }.toSet())
setContentsToRecords(selectArticleContentsRecords, articleMainCardRecords)

/**
* 아티클 조회수 순, 조회수가 같을 경우 최신 아티클이 우선순위를 가지도록 정렬 (TODO: 삭제시 양향도 파악 필요)
*/
val sortedArticles = updateAndSortArticleViews(articleMainCardRecords, articleViewsRecords)

val articleUseCaseOuts: List<ReadArticleUseCaseOut> =
sortedArticles
.map { a ->
ReadArticleUseCaseOut(
id = a.articleId,
writer =
WriterDetail(
id = a.writerId,
name = a.writerName,
imageUrl = a.writerImgUrl,
url = a.writerUrl,
),
mainImageUrl = a.mainImageUrl,
title = a.articleTitle,
content = a.content,
problemIds = emptyList(),
category =
CategoryType.fromCode(a.categoryCd)?.displayName
?: throw NotFoundException("article.invalid.category"),
createdAt = a.createdAt,
views = a.views,
workbooks =
a.workbooks
.map { WorkbookDetail(it.id!!, it.title!!) },
)
}.toList()

return ReadArticlesUseCaseOut(articleUseCaseOuts, isLast)
/**
* 아티클 조회수 순, 조회수가 같을 경우 최신 아티클이 우선순위를 가지도록 정렬 (TODO: 삭제시 양향도 파악 필요)
*/
val sortedArticles = updateAndSortArticleViews(articleMainCardRecords, articleViewsRecords)

val articleUseCaseOuts: List<ReadArticleUseCaseOut> =
sortedArticles
.map { a ->
ReadArticleUseCaseOut(
id = a.articleId,
writer =
WriterDetail(
id = a.writerId,
name = a.writerName,
imageUrl = a.writerImgUrl,
url = a.writerUrl,
),
mainImageUrl = a.mainImageUrl,
title = a.articleTitle,
content = a.content,
problemIds = emptyList(),
category =
CategoryType.fromCode(a.categoryCd)?.displayName
?: throw NotFoundException("article.invalid.category"),
createdAt = a.createdAt,
views = a.views,
workbooks =
a.workbooks
.map { WorkbookDetail(it.id!!, it.title!!) },
)
}.toList()

return@runBlocking ReadArticlesUseCaseOut(articleUseCaseOuts, isLast)
}
}

private fun updateAndSortArticleViews(
Expand Down Expand Up @@ -143,16 +148,4 @@ class BrowseArticlesUseCase(

return sortedSet
}

private fun setContentsToRecords(
articleContentsRecords: List<SelectArticleContentsRecord>,
articleMainCardRecords: Set<ArticleMainCardRecord>,
) {
val articleMainCardRecordsMap: Map<Long, ArticleMainCardRecord> =
articleMainCardRecords.associateBy { it.articleId }

articleContentsRecords.map { articleContentRecord ->
articleMainCardRecordsMap[articleContentRecord.articleId]?.content = articleContentRecord.content
}
}
}
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/DependencyVersion.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ object DependencyVersion {
const val FLYWAY = "9.16.0"

/** jooq */
const val JOOQ = "3.19.10"
const val JOOQ = "3.19.17"

/** test */
const val MOCKK = "1.13.9"
Expand Down

0 comments on commit f1717c0

Please sign in to comment.