fix(profile): 프로필 SNS 필드를 오픈채팅 기준으로 통일한다

This commit is contained in:
2026-02-24 20:01:38 +09:00
parent 63a52629a9
commit c74d27f4ab
16 changed files with 103 additions and 127 deletions

View File

@@ -46,8 +46,9 @@ data class CreatorResponse(
@SerializedName("introduce") val introduce: String = "", @SerializedName("introduce") val introduce: String = "",
@SerializedName("instagramUrl") val instagramUrl: String? = null, @SerializedName("instagramUrl") val instagramUrl: String? = null,
@SerializedName("youtubeUrl") val youtubeUrl: String? = null, @SerializedName("youtubeUrl") val youtubeUrl: String? = null,
@SerializedName("websiteUrl") val websiteUrl: String? = null, @SerializedName("kakaoOpenChatUrl") val kakaoOpenChatUrl: String? = null,
@SerializedName("blogUrl") val blogUrl: String? = null, @SerializedName("fancimmUrl") val fancimmUrl: String? = null,
@SerializedName("xUrl") val xUrl: String? = null,
@SerializedName("isAvailableChat") val isAvailableChat: Boolean = true, @SerializedName("isAvailableChat") val isAvailableChat: Boolean = true,
@SerializedName("isFollow") val isFollow: Boolean, @SerializedName("isFollow") val isFollow: Boolean,
@SerializedName("isNotify") val isNotify: Boolean, @SerializedName("isNotify") val isNotify: Boolean,
@@ -112,5 +113,3 @@ data class GetCreatorActivitySummary(
@SerializedName("liveContributorCount") val liveContributorCount: Int, @SerializedName("liveContributorCount") val liveContributorCount: Int,
@SerializedName("contentCount") val contentCount: Int @SerializedName("contentCount") val contentCount: Int
) )

View File

@@ -37,8 +37,9 @@ data class GetRoomDetailManager(
@SerializedName("introduce") val introduce: String, @SerializedName("introduce") val introduce: String,
@SerializedName("youtubeUrl") val youtubeUrl: String?, @SerializedName("youtubeUrl") val youtubeUrl: String?,
@SerializedName("instagramUrl") val instagramUrl: String?, @SerializedName("instagramUrl") val instagramUrl: String?,
@SerializedName("websiteUrl") val websiteUrl: String?, @SerializedName("kakaoOpenChatUrl") val kakaoOpenChatUrl: String?,
@SerializedName("blogUrl") val blogUrl: String?, @SerializedName("fancimmUrl") val fancimmUrl: String?,
@SerializedName("xUrl") val xUrl: String?,
@SerializedName("profileImageUrl") val profileImageUrl: String, @SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("isCreator") val isCreator: Boolean @SerializedName("isCreator") val isCreator: Boolean
) : Parcelable ) : Parcelable

View File

@@ -33,6 +33,7 @@ import kr.co.vividnext.sodalive.settings.language.LocaleHelper
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.util.Locale import java.util.Locale
import java.util.TimeZone import java.util.TimeZone
import androidx.core.net.toUri
class LiveRoomDetailFragment( class LiveRoomDetailFragment(
private val roomId: Long, private val roomId: Long,
@@ -273,26 +274,14 @@ class LiveRoomDetailFragment(
} }
if ( if (
manager.websiteUrl.isNullOrBlank() || manager.kakaoOpenChatUrl.isNullOrBlank() ||
!URLUtil.isValidUrl(manager.websiteUrl) !URLUtil.isValidUrl(manager.kakaoOpenChatUrl)
) { ) {
binding.ivManagerWebsite.visibility = View.GONE binding.ivManagerOpenChat.visibility = View.GONE
} else { } else {
binding.ivManagerWebsite.visibility = View.VISIBLE binding.ivManagerOpenChat.visibility = View.VISIBLE
binding.ivManagerWebsite.setOnClickListener { binding.ivManagerOpenChat.setOnClickListener {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(manager.websiteUrl))) startActivity(Intent(Intent.ACTION_VIEW, manager.kakaoOpenChatUrl.toUri()))
}
}
if (
manager.blogUrl.isNullOrBlank() ||
!URLUtil.isValidUrl(manager.blogUrl)
) {
binding.ivManagerBlog.visibility = View.GONE
} else {
binding.ivManagerBlog.visibility = View.VISIBLE
binding.ivManagerBlog.setOnClickListener {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(manager.blogUrl)))
} }
} }
@@ -304,7 +293,7 @@ class LiveRoomDetailFragment(
} else { } else {
binding.ivManagerInstagram.visibility = View.VISIBLE binding.ivManagerInstagram.visibility = View.VISIBLE
binding.ivManagerInstagram.setOnClickListener { binding.ivManagerInstagram.setOnClickListener {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(manager.instagramUrl))) startActivity(Intent(Intent.ACTION_VIEW, manager.instagramUrl.toUri()))
} }
} }
@@ -316,7 +305,7 @@ class LiveRoomDetailFragment(
} else { } else {
binding.ivManagerYoutube.visibility = View.VISIBLE binding.ivManagerYoutube.visibility = View.VISIBLE
binding.ivManagerYoutube.setOnClickListener { binding.ivManagerYoutube.setOnClickListener {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(manager.youtubeUrl))) startActivity(Intent(Intent.ACTION_VIEW, manager.youtubeUrl.toUri()))
} }
} }

View File

@@ -11,8 +11,9 @@ data class GetLiveRoomUserProfileResponse(
@SerializedName("gender") val gender: String, @SerializedName("gender") val gender: String,
@SerializedName("instagramUrl") val instagramUrl: String, @SerializedName("instagramUrl") val instagramUrl: String,
@SerializedName("youtubeUrl") val youtubeUrl: String, @SerializedName("youtubeUrl") val youtubeUrl: String,
@SerializedName("websiteUrl") val websiteUrl: String, @SerializedName("kakaoOpenChatUrl") val kakaoOpenChatUrl: String,
@SerializedName("blogUrl") val blogUrl: String, @SerializedName("fancimmUrl") val fancimmUrl: String?,
@SerializedName("xUrl") val xUrl: String?,
@SerializedName("introduce") val introduce: String, @SerializedName("introduce") val introduce: String,
@SerializedName("tags") val tags: String, @SerializedName("tags") val tags: String,
@SerializedName("isSpeaker") val isSpeaker: Boolean?, @SerializedName("isSpeaker") val isSpeaker: Boolean?,

View File

@@ -12,8 +12,9 @@ data class MyPageResponse(
@SerializedName("point") val point: Int, @SerializedName("point") val point: Int,
@SerializedName("youtubeUrl") val youtubeUrl: String?, @SerializedName("youtubeUrl") val youtubeUrl: String?,
@SerializedName("instagramUrl") val instagramUrl: String?, @SerializedName("instagramUrl") val instagramUrl: String?,
@SerializedName("websiteUrl") val websiteUrl: String?, @SerializedName("kakaoOpenChatUrl") val kakaoOpenChatUrl: String?,
@SerializedName("blogUrl") val blogUrl: String?, @SerializedName("fancimmUrl") val fancimmUrl: String?,
@SerializedName("xUrl") val xUrl: String?,
@SerializedName("liveReservationCount") val liveReservationCount: Int, @SerializedName("liveReservationCount") val liveReservationCount: Int,
@SerializedName("likeCount") val likeCount: Int, @SerializedName("likeCount") val likeCount: Int,
@SerializedName("isAuth") val isAuth: Boolean @SerializedName("isAuth") val isAuth: Boolean

View File

@@ -15,8 +15,7 @@ data class ProfileResponse(
@SerializedName("rewardCan") val rewardCan: Int, @SerializedName("rewardCan") val rewardCan: Int,
@SerializedName("youtubeUrl") val youtubeUrl: String?, @SerializedName("youtubeUrl") val youtubeUrl: String?,
@SerializedName("instagramUrl") val instagramUrl: String?, @SerializedName("instagramUrl") val instagramUrl: String?,
@SerializedName("blogUrl") val blogUrl: String?, @SerializedName("kakaoOpenChatUrl") val kakaoOpenChatUrl: String?,
@SerializedName("websiteUrl") val websiteUrl: String?,
@SerializedName("fancimmUrl") val fancimmUrl: String?, @SerializedName("fancimmUrl") val fancimmUrl: String?,
@SerializedName("xUrl") val xUrl: String?, @SerializedName("xUrl") val xUrl: String?,
@SerializedName("introduce") val introduce: String, @SerializedName("introduce") val introduce: String,

View File

@@ -75,16 +75,6 @@ class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
} }
private fun bindData() { private fun bindData() {
compositeDisposable.add(
binding.etBlog.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
viewModel.blogUrl = it.toString()
}
)
compositeDisposable.add( compositeDisposable.add(
binding.etFancimm.textChanges().skip(1) binding.etFancimm.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS) .debounce(500, TimeUnit.MILLISECONDS)
@@ -106,12 +96,12 @@ class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
) )
compositeDisposable.add( compositeDisposable.add(
binding.etWebsite.textChanges().skip(1) binding.etOpenChat.textChanges().skip(1)
.debounce(500, TimeUnit.MILLISECONDS) .debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe { .subscribe {
viewModel.websiteUrl = it.toString() viewModel.kakaoOpenChatUrl = it.toString()
} }
) )
@@ -160,8 +150,7 @@ class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
binding.tvNickname.text = it.nickname binding.tvNickname.text = it.nickname
it.youtubeUrl?.let { url -> binding.etYoutube.setText(url) } it.youtubeUrl?.let { url -> binding.etYoutube.setText(url) }
it.instagramUrl?.let { url -> binding.etInstagram.setText(url) } it.instagramUrl?.let { url -> binding.etInstagram.setText(url) }
it.websiteUrl?.let { url -> binding.etWebsite.setText(url) } it.kakaoOpenChatUrl?.let { url -> binding.etOpenChat.setText(url) }
it.blogUrl?.let { url -> binding.etBlog.setText(url) }
it.fancimmUrl?.let { url -> binding.etFancimm.setText(url) } it.fancimmUrl?.let { url -> binding.etFancimm.setText(url) }
it.xUrl?.let { url -> binding.etX.setText(url) } it.xUrl?.let { url -> binding.etX.setText(url) }
binding.etIntroduce.setText(it.introduce) binding.etIntroduce.setText(it.introduce)

View File

@@ -17,8 +17,7 @@ data class ProfileUpdateRequest(
@SerializedName("introduce") val introduce: String? = null, @SerializedName("introduce") val introduce: String? = null,
@SerializedName("youtubeUrl") val youtubeUrl: String? = null, @SerializedName("youtubeUrl") val youtubeUrl: String? = null,
@SerializedName("instagramUrl") val instagramUrl: String? = null, @SerializedName("instagramUrl") val instagramUrl: String? = null,
@SerializedName("websiteUrl") val websiteUrl: String? = null, @SerializedName("kakaoOpenChatUrl") val kakaoOpenChatUrl: String? = null,
@SerializedName("blogUrl") val blogUrl: String? = null,
@SerializedName("fancimmUrl") val fancimmUrl: String? = null, @SerializedName("fancimmUrl") val fancimmUrl: String? = null,
@SerializedName("xUrl") val xUrl: String? = null, @SerializedName("xUrl") val xUrl: String? = null,
@SerializedName("isVisibleDonationRank") val isVisibleDonationRank: Boolean? = null, @SerializedName("isVisibleDonationRank") val isVisibleDonationRank: Boolean? = null,

View File

@@ -20,8 +20,7 @@ class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewM
var youtubeUrl = "" var youtubeUrl = ""
var instagramUrl = "" var instagramUrl = ""
var websiteUrl = "" var kakaoOpenChatUrl = ""
var blogUrl = ""
var fancimmUrl = "" var fancimmUrl = ""
var xUrl = "" var xUrl = ""
var introduce = "" var introduce = ""
@@ -67,8 +66,7 @@ class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewM
profileResponse = it.data profileResponse = it.data
youtubeUrl = profileResponse.youtubeUrl ?: "" youtubeUrl = profileResponse.youtubeUrl ?: ""
instagramUrl = profileResponse.instagramUrl ?: "" instagramUrl = profileResponse.instagramUrl ?: ""
websiteUrl = profileResponse.websiteUrl ?: "" kakaoOpenChatUrl = profileResponse.kakaoOpenChatUrl ?: ""
blogUrl = profileResponse.blogUrl ?: ""
fancimmUrl = profileResponse.fancimmUrl ?: "" fancimmUrl = profileResponse.fancimmUrl ?: ""
xUrl = profileResponse.xUrl ?: "" xUrl = profileResponse.xUrl ?: ""
introduce = profileResponse.introduce introduce = profileResponse.introduce
@@ -134,8 +132,7 @@ class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewM
if ( if (
profileResponse.youtubeUrl != youtubeUrl || profileResponse.youtubeUrl != youtubeUrl ||
profileResponse.instagramUrl != instagramUrl || profileResponse.instagramUrl != instagramUrl ||
profileResponse.blogUrl != blogUrl || profileResponse.kakaoOpenChatUrl != kakaoOpenChatUrl ||
profileResponse.websiteUrl != websiteUrl ||
profileResponse.fancimmUrl != fancimmUrl || profileResponse.fancimmUrl != fancimmUrl ||
profileResponse.xUrl != xUrl || profileResponse.xUrl != xUrl ||
profileResponse.gender != _genderLiveData.value || profileResponse.gender != _genderLiveData.value ||
@@ -156,13 +153,8 @@ class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewM
} else { } else {
null null
}, },
blogUrl = if (profileResponse.blogUrl != blogUrl) { kakaoOpenChatUrl = if (profileResponse.kakaoOpenChatUrl != kakaoOpenChatUrl) {
blogUrl kakaoOpenChatUrl
} else {
null
},
websiteUrl = if (profileResponse.websiteUrl != websiteUrl) {
websiteUrl
} else { } else {
null null
}, },

View File

@@ -367,50 +367,17 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="6.7dp" android:layout_marginHorizontal="6.7dp"
android:fontFamily="@font/medium" android:fontFamily="@font/medium"
android:text="@string/screen_profile_update_website_label" android:text="@string/screen_profile_update_open_chat_label"
android:textColor="@color/color_eeeeee" android:textColor="@color/color_eeeeee"
android:textSize="12sp" /> android:textSize="12sp" />
<EditText <EditText
android:id="@+id/et_website" android:id="@+id/et_open_chat"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/edittext_underline" android:background="@drawable/edittext_underline"
android:fontFamily="@font/medium" android:fontFamily="@font/medium"
android:hint="@string/screen_profile_update_website_hint" android:hint="@string/screen_profile_update_open_chat_hint"
android:importantForAutofill="no"
android:inputType="textWebEditText"
android:paddingHorizontal="6.7dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="6.7dp"
android:fontFamily="@font/medium"
android:text="@string/screen_profile_update_blog_label"
android:textColor="@color/color_eeeeee"
android:textSize="12sp" />
<EditText
android:id="@+id/et_blog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_underline"
android:fontFamily="@font/medium"
android:hint="@string/screen_profile_update_blog_hint"
android:importantForAutofill="no" android:importantForAutofill="no"
android:inputType="textWebEditText" android:inputType="textWebEditText"
android:paddingHorizontal="6.7dp" android:paddingHorizontal="6.7dp"

View File

@@ -253,20 +253,12 @@
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/iv_manager_website" android:id="@+id/iv_manager_open_chat"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@null" android:contentDescription="@null"
android:src="@drawable/ic_website_blue" /> android:src="@drawable/ic_website_blue" />
<ImageView
android:id="@+id/iv_manager_blog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:contentDescription="@null"
android:src="@drawable/ic_blog_blue" />
<ImageView <ImageView
android:id="@+id/iv_manager_instagram" android:id="@+id/iv_manager_instagram"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -127,20 +127,12 @@
app:layout_constraintTop_toBottomOf="@+id/tv_tags"> app:layout_constraintTop_toBottomOf="@+id/tv_tags">
<ImageView <ImageView
android:id="@+id/iv_website" android:id="@+id/iv_open_chat"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@null" android:contentDescription="@null"
android:src="@drawable/ic_website_circle" /> android:src="@drawable/ic_website_circle" />
<ImageView
android:id="@+id/iv_blog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:contentDescription="@null"
android:src="@drawable/ic_blog_circle" />
<ImageView <ImageView
android:id="@+id/iv_instagram" android:id="@+id/iv_instagram"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -660,10 +660,8 @@
<string name="screen_profile_update_instagram_hint">Instagram URL</string> <string name="screen_profile_update_instagram_hint">Instagram URL</string>
<string name="screen_profile_update_youtube_label">YouTube channel</string> <string name="screen_profile_update_youtube_label">YouTube channel</string>
<string name="screen_profile_update_youtube_hint">YouTube channel URL</string> <string name="screen_profile_update_youtube_hint">YouTube channel URL</string>
<string name="screen_profile_update_website_label">Website</string> <string name="screen_profile_update_open_chat_label">Open Chat</string>
<string name="screen_profile_update_website_hint">Website URL</string> <string name="screen_profile_update_open_chat_hint">Open Chat URL</string>
<string name="screen_profile_update_blog_label">Blog</string>
<string name="screen_profile_update_blog_hint">Blog URL</string>
<string name="screen_profile_update_fancimm_label">FancimM</string> <string name="screen_profile_update_fancimm_label">FancimM</string>
<string name="screen_profile_update_fancimm_hint">FancimM URL</string> <string name="screen_profile_update_fancimm_hint">FancimM URL</string>
<string name="screen_profile_update_x_label">X</string> <string name="screen_profile_update_x_label">X</string>

View File

@@ -660,10 +660,8 @@
<string name="screen_profile_update_instagram_hint">Instagram URL</string> <string name="screen_profile_update_instagram_hint">Instagram URL</string>
<string name="screen_profile_update_youtube_label">YouTubeチャンネル</string> <string name="screen_profile_update_youtube_label">YouTubeチャンネル</string>
<string name="screen_profile_update_youtube_hint">YouTubeチャンネル URL</string> <string name="screen_profile_update_youtube_hint">YouTubeチャンネル URL</string>
<string name="screen_profile_update_website_label">ウェブサイ</string> <string name="screen_profile_update_open_chat_label">オープンチャッ</string>
<string name="screen_profile_update_website_hint">ウェブサイト URL</string> <string name="screen_profile_update_open_chat_hint">オープンチャット URL</string>
<string name="screen_profile_update_blog_label">ブログ</string>
<string name="screen_profile_update_blog_hint">ブログ URL</string>
<string name="screen_profile_update_fancimm_label">FancimM</string> <string name="screen_profile_update_fancimm_label">FancimM</string>
<string name="screen_profile_update_fancimm_hint">FancimM URL</string> <string name="screen_profile_update_fancimm_hint">FancimM URL</string>
<string name="screen_profile_update_x_label">X</string> <string name="screen_profile_update_x_label">X</string>

View File

@@ -659,10 +659,8 @@
<string name="screen_profile_update_instagram_hint">인스타그램 URL</string> <string name="screen_profile_update_instagram_hint">인스타그램 URL</string>
<string name="screen_profile_update_youtube_label">유튜브 채널</string> <string name="screen_profile_update_youtube_label">유튜브 채널</string>
<string name="screen_profile_update_youtube_hint">유튜브 채널 URL</string> <string name="screen_profile_update_youtube_hint">유튜브 채널 URL</string>
<string name="screen_profile_update_website_label">웹사이트</string> <string name="screen_profile_update_open_chat_label">오픈채팅</string>
<string name="screen_profile_update_website_hint">웹사이트 URL</string> <string name="screen_profile_update_open_chat_hint">오픈채팅 URL</string>
<string name="screen_profile_update_blog_label">블로그</string>
<string name="screen_profile_update_blog_hint">블로그 URL</string>
<string name="screen_profile_update_fancimm_label">팬심M</string> <string name="screen_profile_update_fancimm_label">팬심M</string>
<string name="screen_profile_update_fancimm_hint">팬심M URL</string> <string name="screen_profile_update_fancimm_hint">팬심M URL</string>
<string name="screen_profile_update_x_label">X</string> <string name="screen_profile_update_x_label">X</string>

View File

@@ -0,0 +1,61 @@
- [x] 1단계: 프로필 SNS 도메인 필드를 `websiteUrl`/`blogUrl`에서 `kakaoOpenChatUrl`로 전환한다.
- 대상 파일: `app/src/main/java/kr/co/vividnext/sodalive/mypage/profile/ProfileUpdateRequest.kt`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/profile/ProfileResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/profile/ProfileUpdateViewModel.kt`
- [x] 2단계: 프로필 수정 화면 입력 항목을 `instagram`, `youtube`, `kakaoOpenChatUrl`, `fancimm`, `x` 순서로 정리한다.
- 대상 파일: `app/src/main/res/layout/activity_profile_update.xml`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/profile/ProfileUpdateActivity.kt`
- [x] 3단계: 프로필 수정 화면 문자열 리소스에서 Website/Blog 라벨·힌트를 제거하고 OpenChat 문구를 추가한다.
- 대상 파일: `app/src/main/res/values/strings.xml`, `app/src/main/res/values-en/strings.xml`, `app/src/main/res/values-ja/strings.xml`
- [x] 4단계: 크리에이터/유저 프로필 조회 응답 모델의 SNS 필드 구성을 동일 규격으로 맞춘다.
- 대상 파일: `app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/GetCreatorProfileResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/profile/GetLiveRoomUserProfileResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageResponse.kt`
- [x] 5단계: 프로필 SNS 노출 UI(상세/라이브 상세)에서 Website/Blog 노출 및 클릭 처리를 OpenChat 기준으로 교체한다.
- 대상 파일: `app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailFragment.kt`, `app/src/main/res/layout/fragment_live_room_detail.xml`, `app/src/main/res/layout/layout_user_profile.xml`
- 아이콘 점검 파일: `app/src/main/res/drawable-xxhdpi/ic_website_blue.png`, `app/src/main/res/drawable-xxhdpi/ic_blog_blue.png`, `app/src/main/res/drawable-xxhdpi/ic_website_circle.png`, `app/src/main/res/drawable-xxhdpi/ic_blog_circle.png`, `app/src/main/res/drawable-xxhdpi/ic_login_kakao.png`
- [x] 6단계: 변경 영향 범위 컴파일/테스트를 수행하고 결과를 문서 하단 검증 기록에 누적한다.
- 대상 명령: `./gradlew :app:testDebugUnitTest`, `./gradlew :app:assembleDebug`
- [x] 7단계: 기존 SNS URL 필드 구성 지점에 `fancimmUrl`, `xUrl`를 추가 반영한다.
- 대상 파일: `app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/GetCreatorProfileResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/profile/GetLiveRoomUserProfileResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageResponse.kt`
## 검증 기록
### 1) 작업계획 문서 작성
- 무엇: 프로필 SNS 필드 전환 및 프로필 수정 UI 변경 작업을 단계별 체크리스트로 정의.
- 왜: 일부만 반영된 상태에서 누락 파일 없이 일관되게 마이그레이션하기 위함.
- 어떻게:
- `websiteUrl`, `blogUrl`, `kakaoOpenChatUrl`, `et_website`, `et_blog` 키워드 기준으로 Kotlin/XML 사용처를 탐색.
- 단계별로 실제 수정 후보 파일 경로를 체크리스트에 연결.
- 결과:
- 계획 문서 `docs/20260224_프로필SNS오픈채팅전환.md` 생성 완료.
- 구현은 아직 미수행(체크박스 전체 미완료 상태 유지).
### 2) 프로필 SNS 필드/수정 UI 오픈채팅 전환 구현
- 무엇: SNS 필드 규격을 `instagram`, `youtube`, `kakaoOpenChatUrl`, `fancimm`, `x`로 통일하고 프로필 수정 UI에서 Website/Blog 입력을 제거 후 Open Chat 입력으로 대체.
- 왜: 서버/클라이언트 모델 및 UI의 SNS 항목을 동일 스펙으로 맞추고, 부분 반영 상태를 해소하기 위함.
- 어떻게:
- 모델/요청/뷰모델(`ProfileUpdateRequest`, `ProfileResponse`, `ProfileUpdateViewModel`)의 `websiteUrl`/`blogUrl` 사용부를 `kakaoOpenChatUrl`로 변경.
- 프로필 수정 화면(`activity_profile_update.xml`, `ProfileUpdateActivity.kt`)에서 `et_website`/`et_blog` 제거 후 `et_open_chat` 입력으로 교체.
- 조회 응답 모델(`GetCreatorProfileResponse`, `GetLiveRoomUserProfileResponse`, `GetRoomDetailResponse`, `MyPageResponse`) 필드명 통일.
- 노출 UI(`fragment_live_room_detail.xml`, `layout_user_profile.xml`)와 클릭 처리(`LiveRoomDetailFragment.kt`)를 오픈채팅 기준으로 변경.
- 문자열 리소스(`values/strings.xml`, `values-en/strings.xml`, `values-ja/strings.xml`)에서 Website/Blog 문구 제거 후 Open Chat 문구 추가.
- 결과:
- app/src/main 기준 `websiteUrl`, `blogUrl`, `et_website`, `et_blog`, 관련 문자열 키가 제거되고 `kakaoOpenChatUrl`/`et_open_chat`/`screen_profile_update_open_chat_*` 기준으로 정렬됨.
### 3) 진단/테스트/빌드 검증
- 무엇: 변경 파일 정합성과 빌드 안정성을 검증.
- 왜: 필드명/뷰 ID/바인딩 변경으로 인한 컴파일 오류를 사전에 확인하기 위함.
- 어떻게:
- `lsp_diagnostics`를 수정된 Kotlin/XML 파일 전체에 실행 시도.
- `./gradlew :app:testDebugUnitTest :app:assembleDebug` 실행.
- 결과:
- LSP: 현재 환경에서 Kotlin/XML LSP 미설정으로 진단 도구 사용 불가(`No LSP server configured for extension: .kt/.xml`).
- Gradle: `BUILD SUCCESSFUL` (unit test + debug assemble 통과).
### 4) `fancimmUrl`/`xUrl` 필드 추가 반영
- 무엇: 기존 SNS URL 필드가 정의된 응답 모델 구간에 `fancimmUrl`, `xUrl`를 추가.
- 왜: SNS URL 필드 스키마를 모델 간 일관되게 유지하고, 서버 응답 확장 시 누락 파싱을 방지하기 위함.
- 어떻게:
- `GetCreatorProfileResponse.CreatorResponse`, `GetLiveRoomUserProfileResponse`, `GetRoomDetailManager`, `MyPageResponse``@SerializedName("fancimmUrl")`, `@SerializedName("xUrl")` 필드를 추가.
- `grep`으로 `fancimmUrl|xUrl` 사용처를 재탐색해 대상 파일에 반영 여부 확인.
- `lsp_diagnostics` 실행 시도 후 `./gradlew :app:testDebugUnitTest :app:assembleDebug` 수행.
- 결과:
- 대상 4개 모델 파일에 `fancimmUrl`, `xUrl` 필드 추가 완료.
- LSP: Kotlin LSP 미설정으로 진단 도구 사용 불가(`No LSP server configured for extension: .kt`).
- Gradle: `BUILD SUCCESSFUL` (unit test + debug assemble 통과).