fix(comment-nickname): deleted_ 로 시작하는 닉네임 접두사 노출을 제거한다

This commit is contained in:
2026-02-20 18:47:01 +09:00
parent 211eb3507c
commit c3a2ca66f8
6 changed files with 84 additions and 6 deletions

View File

@@ -0,0 +1,22 @@
# 20260220 삭제 닉네임 접두사 표시 정리
## 구현 계획
- [x] 콘텐츠 댓글, 팬톡 응원, 커뮤니티 댓글의 닉네임 표시 흐름(조회/매핑/응답 DTO)을 각각 식별한다.
- [x] 닉네임이 `deleted_`로 시작하는지 판별하고 표시 시 접두사만 제거하는 공통 처리 지점을 설계한다.
- [x] 콘텐츠 댓글 표시 로직에 `deleted_` 접두사 제거 규칙을 적용한다.
- [x] 팬톡 응원 표시 로직에 `deleted_` 접두사 제거 규칙을 적용한다.
- [x] 커뮤니티 댓글 표시 로직에 `deleted_` 접두사 제거 규칙을 적용한다.
- [x] `deleted_` 미포함 닉네임, `deleted_` 포함 닉네임, 접두사만 존재하는 경계 케이스를 기준으로 테스트 케이스를 추가/보강한다.
## 검증 계획
- [x] 닉네임 표시에 영향이 있는 테스트를 우선 실행하고 실패 시 원인을 보정한다.
- [x] `./gradlew test`를 실행해 회귀 여부를 확인한다.
- [x] 필요 시 `./gradlew ktlintCheck`로 스타일 규칙 위반 여부를 확인한다.
- [x] `./gradlew build`를 실행해 전체 빌드 성공을 확인한다.
## 검증 기록
- [x] 작업 완료 후 검증 결과를 기록한다.
- 무엇을: `String.removeDeletedNicknamePrefix()` 공통 확장 함수를 추가하고, 콘텐츠 댓글(`AudioContentCommentRepository`), 팬톡 응원(`ExplorerQueryRepository#getCheersList`), 커뮤니티 댓글(`CreatorCommunityCommentRepository`) 응답 닉네임에 동일 규칙을 적용했다.
- 왜: 탈퇴/비활성 사용자 닉네임 저장 정책(`deleted_` 접두사 유지)과 화면 표시 정책(접두사 제거)을 분리해, 사용자에게는 일관된 표시값을 제공하기 위해서다.
- 어떻게 검증했는지: `./gradlew test --tests "kr.co.vividnext.sodalive.extensions.StringExtensionsTest"`, `./gradlew test`, `./gradlew ktlintCheck`, `./gradlew build`를 실행해 모두 `BUILD SUCCESSFUL`을 확인했다. 또한 경계 케이스(`deleted_testUser`, `testUser`, `deleted_`) 단위 테스트를 추가해 기대 출력이 각각 `testUser`, `testUser`, `""`인지 검증했다.

View File

@@ -4,6 +4,7 @@ import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
import kr.co.vividnext.sodalive.extensions.removeDeletedNicknamePrefix
import kr.co.vividnext.sodalive.fcm.PushTokenInfo
import kr.co.vividnext.sodalive.fcm.QPushToken.pushToken
import kr.co.vividnext.sodalive.fcm.QPushTokenInfo
@@ -103,8 +104,10 @@ class AudioContentCommentQueryRepositoryImpl(
.orderBy(audioContentComment.createdAt.desc())
.fetch()
.map {
it.replyCount = commentReplyCountByAudioContentCommentId(it.id)
it
it.copy(
nickname = it.nickname.removeDeletedNicknamePrefix(),
replyCount = commentReplyCountByAudioContentCommentId(it.id)
)
}
}
@@ -187,6 +190,9 @@ class AudioContentCommentQueryRepositoryImpl(
.limit(limit.toLong())
.orderBy(audioContentComment.createdAt.desc())
.fetch()
.map {
it.copy(nickname = it.nickname.removeDeletedNicknamePrefix())
}
}
override fun findPushTokenByContentIdAndCommentParentIdMyMemberId(

View File

@@ -19,6 +19,7 @@ import kr.co.vividnext.sodalive.explorer.profile.CreatorCheers
import kr.co.vividnext.sodalive.explorer.profile.QChannelNotice.channelNotice
import kr.co.vividnext.sodalive.explorer.profile.QCreatorCheers.creatorCheers
import kr.co.vividnext.sodalive.explorer.profile.TimeDifferenceResult
import kr.co.vividnext.sodalive.extensions.removeDeletedNicknamePrefix
import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
@@ -487,7 +488,7 @@ class ExplorerQueryRepository(
GetCheersResponseItem(
cheersId = it.id!!,
memberId = it.member!!.id!!,
nickname = it.member!!.nickname,
nickname = it.member!!.nickname.removeDeletedNicknamePrefix(),
profileUrl = if (it.member!!.profileImage != null) {
"$cloudFrontHost/${it.member!!.profileImage}"
} else {
@@ -505,7 +506,7 @@ class ExplorerQueryRepository(
GetCheersResponseItem(
cheersId = cheers.id!!,
memberId = cheers.member!!.id!!,
nickname = cheers.member!!.nickname,
nickname = cheers.member!!.nickname.removeDeletedNicknamePrefix(),
profileUrl = if (cheers.member!!.profileImage != null) {
"$cloudFrontHost/${cheers.member!!.profileImage}"
} else {

View File

@@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment
import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.QCreatorCommunityComment.creatorCommunityComment
import kr.co.vividnext.sodalive.extensions.removeDeletedNicknamePrefix
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.jpa.repository.JpaRepository
import java.time.LocalDateTime
@@ -93,8 +94,10 @@ class CreatorCommunityCommentQueryRepositoryImpl(
.orderBy(creatorCommunityComment.createdAt.desc())
.fetch()
.map {
it.replyCount = commentReplyCountByCommentId(it.id)
it
it.copy(
nickname = it.nickname.removeDeletedNicknamePrefix(),
replyCount = commentReplyCountByCommentId(it.id)
)
}
}
@@ -174,5 +177,8 @@ class CreatorCommunityCommentQueryRepositoryImpl(
.limit(limit)
.orderBy(creatorCommunityComment.createdAt.desc())
.fetch()
.map {
it.copy(nickname = it.nickname.removeDeletedNicknamePrefix())
}
}
}

View File

@@ -5,6 +5,8 @@ import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
private const val DELETED_NICKNAME_PREFIX = "deleted_"
fun String.convertLocalDateTime(format: String): LocalDateTime {
val dateTimeFormatter = DateTimeFormatter.ofPattern(format)
return LocalDateTime.parse(this, dateTimeFormatter)
@@ -24,3 +26,11 @@ fun String.convertLocalDateTime(
.withZoneSameInstant(ZoneId.of("UTC"))
.toLocalDateTime()
}
fun String.removeDeletedNicknamePrefix(): String {
return if (startsWith(DELETED_NICKNAME_PREFIX)) {
removePrefix(DELETED_NICKNAME_PREFIX)
} else {
this
}
}

View File

@@ -0,0 +1,33 @@
package kr.co.vividnext.sodalive.extensions
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class StringExtensionsTest {
@Test
fun shouldRemoveDeletedPrefixWhenNicknameStartsWithDeletedPrefix() {
val nickname = "deleted_testUser"
val sanitizedNickname = nickname.removeDeletedNicknamePrefix()
assertEquals("testUser", sanitizedNickname)
}
@Test
fun shouldKeepNicknameWhenDeletedPrefixDoesNotExist() {
val nickname = "testUser"
val sanitizedNickname = nickname.removeDeletedNicknamePrefix()
assertEquals("testUser", sanitizedNickname)
}
@Test
fun shouldReturnEmptyStringWhenNicknameContainsOnlyDeletedPrefix() {
val nickname = "deleted_"
val sanitizedNickname = nickname.removeDeletedNicknamePrefix()
assertEquals("", sanitizedNickname)
}
}