From ad658c15edd8c949ce21eca82b193a654cd93bd0 Mon Sep 17 00:00:00 2001 From: juseong Date: Thu, 19 Jun 2025 00:14:36 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat=20:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=8C=80=EC=83=81=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/simply407/patpat/data/api/RetrofitInstance.kt | 5 ++++- app/src/main/java/com/simply407/patpat/data/api/patApi.kt | 1 + .../com/simply407/patpat/data/model/CounselorMbtiInfo.kt | 3 ++- .../com/simply407/patpat/data/model/CreateLetterResponse.kt | 2 ++ .../simply407/patpat/data/model/GetAllLettersResponse.kt | 2 ++ .../simply407/patpat/data/model/SharedPreferencesManager.kt | 3 ++- .../simply407/patpat/data/repository/ChattingRepository.kt | 6 ++++-- .../simply407/patpat/data/repository/CounselorRepository.kt | 2 +- .../simply407/patpat/data/repository/LetterRepository.kt | 2 +- .../com/simply407/patpat/data/repository/LoginRepository.kt | 2 +- .../simply407/patpat/data/repository/UserInfoRepository.kt | 2 +- .../com/simply407/patpat/ui/chatting/ChattingAdapter.kt | 6 +++++- .../com/simply407/patpat/ui/chatting/ChattingFragment.kt | 5 +++++ .../java/com/simply407/patpat/ui/letter/LetterViewModel.kt | 3 ++- gradle/libs.versions.toml | 2 +- 15 files changed, 34 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt b/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt index 72b2abe..4271c96 100644 --- a/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt +++ b/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt @@ -3,6 +3,8 @@ package com.simply407.patpat.data.api import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory +// TODO object keyword로 선언하면 어떤일이 발생할까?? +// TODO eager vs lazy object RetrofitInstance { private const val BASE_URL = "https://oognuyh.asuscomm.com/" @@ -12,8 +14,9 @@ object RetrofitInstance { .addConverterFactory(GsonConverterFactory.create()) .build() + // TODO client의 선언을 val로 하면 되면 좋지 않을까?? fun getInstance(): Retrofit { return client } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/api/patApi.kt b/app/src/main/java/com/simply407/patpat/data/api/patApi.kt index 55f4503..9bcb6df 100644 --- a/app/src/main/java/com/simply407/patpat/data/api/patApi.kt +++ b/app/src/main/java/com/simply407/patpat/data/api/patApi.kt @@ -22,6 +22,7 @@ import retrofit2.http.PUT import retrofit2.http.Path import retrofit2.http.Query +// TODO 대문자, API보단 Service 라는 표현이 더 바람직해보임 interface patApi { @POST("api/v1/login") diff --git a/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt b/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt index 9d6f76c..4f8d1fb 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt @@ -2,6 +2,7 @@ package com.simply407.patpat.data.model data class CounselorMbtiInfo(val name: String, val mbtiList: List) +// TODO 샘플코드이긴 하지만... 만약 String을 객체로 담고 싶다면?? object MBTIRepository { val CounselorMbtiDataList = listOf( CounselorMbtiInfo("복남이", listOf("INFJ", "INFP", "ENFJ", "ENFP")), @@ -9,4 +10,4 @@ object MBTIRepository { CounselorMbtiInfo("곽두팔", listOf("ISTJ", "ISTP", "ESTJ", "ESTP")), CounselorMbtiInfo("코코", listOf("ISFJ", "ISFP", "ESFJ", "ESFP")) ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt b/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt index 9d0e2c6..f255adb 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt @@ -3,6 +3,8 @@ package com.simply407.patpat.data.model import android.os.Parcelable import kotlinx.parcelize.Parcelize +// TODO API data class 와 UI data class는 분리하는게 좋다. isLiked는 api에서 처리? +// TODO isLiked 는 var로 해야하나? val로 선언하는 방법은? mutable vs immutable @Parcelize data class CreateLetterResponse( val id: String, diff --git a/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt b/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt index 9ec7c11..a37d4e4 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt @@ -1,5 +1,7 @@ package com.simply407.patpat.data.model +// TODO data 가 null이 되면 어떤일이 벌어질까? totalElements 이 null이 되면 어떤일이 벌어질까? +// TODO 원시타입 vs 참조타입 data class GetAllLettersResponse( val data: List, val totalElements: Int, diff --git a/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt b/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt index 9a1c69e..bb3c905 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt @@ -3,6 +3,7 @@ package com.simply407.patpat.data.model import android.content.Context import android.content.SharedPreferences +// TODO Preference는 MainThread? IOThread? WorkerThread? object SharedPreferencesManager { const val FILE_NAME = "user_info" @@ -68,4 +69,4 @@ object SharedPreferencesManager { return sharedPref.getString(KEY_COUNSELOR_RECOMMENDATION, null) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt index 77e22fc..ec1e8a7 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt @@ -4,7 +4,9 @@ import com.simply407.patpat.data.api.RetrofitInstance import com.simply407.patpat.data.model.PostMessageRequest class ChattingRepository { - + // TODO 의존성주입을 하면 더 좋을 것 같다. hilt를 사용해 + // TODO create 의 내부 구현을 확인해보면 좋을듯 + // TODO API return형은 Response data class 를 반환 할 수 있게 repository에서 구현로직 들어가야함 val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) suspend fun getAllChattingRoomInfo(accessToken: String, counselorId: String, page: Int, size: Int) = patApi.getAllChattingRoomInfo(accessToken, counselorId, page, size) @@ -14,4 +16,4 @@ class ChattingRepository { counselorId: String, postMessageRequest: PostMessageRequest ) = patApi.postMessage(accessToken, counselorId, postMessageRequest) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt index 6a6211e..b2a8006 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt @@ -7,4 +7,4 @@ class CounselorRepository { val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) suspend fun getCounselors(accessToken: String) = patApi.getCounselors(accessToken) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt index 4a4617e..efe7f19 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt @@ -19,4 +19,4 @@ class LetterRepository { letterId: String, likeLetterRequest: LikeLetterRequest ) = patApi.likeLetter(accessToken, letterId, likeLetterRequest) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt index abca8b5..28485c4 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt @@ -13,4 +13,4 @@ class LoginRepository { suspend fun userLogout(accessToken: String) = patApi.userLogout(accessToken) suspend fun userWithdrawal(accessToken: String) = patApi.userWithdrawal(accessToken) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt index 78b284f..b8c9fba 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt @@ -11,4 +11,4 @@ class UserInfoRepository { suspend fun putUserInfo(accessToken: String, newUserInfo: NewUserInfo) = patApi.putUserInfo(accessToken, newUserInfo) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt index 6f43035..7a7d10b 100644 --- a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt +++ b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt @@ -10,6 +10,8 @@ import com.simply407.patpat.databinding.ItemChattingCounselorBinding import com.simply407.patpat.databinding.ItemChattingMyBinding import com.simply407.patpat.ui.main.MainActivity +// TODO allMessagesDataList는 val로 선언 +// TODO ListAdapter 공부 필요 class ChattingAdapter(private var allMessagesDataList: MutableList, private val counselorId: Int) : RecyclerView.Adapter() { inner class ChattingCounselorViewHolder(private val binding: ItemChattingCounselorBinding) : RecyclerView.ViewHolder(binding.root) { @@ -116,6 +118,7 @@ class ChattingAdapter(private var allMessagesDataList: MutableList, fun updateMessages(newMessages: List) { allMessagesDataList = newMessages.toMutableList() + // TODO notifyDataSetChanged는 최악의 갱신 method() notifyDataSetChanged() val mainActivity = MainActivity() @@ -123,8 +126,9 @@ class ChattingAdapter(private var allMessagesDataList: MutableList, } companion object { + // TODO 접근제어자 공부 필요 const val TYPE_ONE = 1 const val TYPE_TWO = 2 } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt index c65b055..18a9357 100644 --- a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt +++ b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt @@ -24,6 +24,9 @@ import com.simply407.patpat.ui.main.MainActivity class ChattingFragment : Fragment() { + // TODO Activity와 Fragment 통신 하는 방법 공부 + // TODO Fragment 간 통신 하는 방법 공부 + // TODO by lazy를 사용해보는건 어떠한가? lateinit var 개인적으로 최대한 사용을 안하는게 좋은것 같음 왜?? private lateinit var mainActivity: MainActivity private lateinit var binding: FragmentChatting2Binding private lateinit var chattingAdapter: ChattingAdapter @@ -33,6 +36,7 @@ class ChattingFragment : Fragment() { private var currentPageIndex: Int = 0 private var counselorId: String = "" + // TODO 접근제어자 val TAG = "ChattingFragment" override fun onCreateView( @@ -46,6 +50,7 @@ class ChattingFragment : Fragment() { currentPageIndex = arguments?.getInt("currentPageIndex") ?: 0 counselorId = arguments?.getString("counselorId") ?: "" + // TODO ViewModel ktx 공부 chattingViewModel = ViewModelProvider(this)[ChattingViewModel::class.java] chattingViewModel.getAllChattingRoomInfo(SharedPreferencesManager.getUserAccessToken()!!, counselorId, 1, 100) diff --git a/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt b/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt index 22d4010..3fdefed 100644 --- a/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt +++ b/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt @@ -31,6 +31,7 @@ class LetterViewModel: ViewModel() { fun createLetter(accessToken: String, createLetterRequest: CreateLetterRequest) { viewModelScope.launch { + // TODO postValue vs setValue, post를 하면 발생하는 상황도 함께 공부 _isLoading.postValue(true) try { val response = letterRepository.createLetter(accessToken, createLetterRequest) @@ -84,4 +85,4 @@ class LetterViewModel: ViewModel() { } } -} \ No newline at end of file +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8a90a0b..b0fbd8b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.5.0" +agp = "8.6.0" circleimageview = "3.1.0" converterGson = "2.9.0" glide = "4.16.0" From 8a36f3338fd6f071f0d9096bab6c5c1af4235d47 Mon Sep 17 00:00:00 2001 From: juseong Date: Thu, 19 Jun 2025 00:26:35 +0900 Subject: [PATCH 02/11] =?UTF-8?q?fix=20:=20kotlin=20coding=20convention=20?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20-=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EC=99=80=20=EA=B0=9D=EC=B2=B4=EC=9D=98=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=80=20=EB=8C=80=EB=AC=B8=EC=9E=90=20?= =?UTF-8?q?=EC=B9=B4=EB=A9=9C=20=ED=91=9C=EA=B8=B0=EB=B2=95=EC=9D=84=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../patpat/data/api/{patApi.kt => PatPatService.kt} | 5 ++++- .../patpat/data/repository/ChattingRepository.kt | 6 +++--- .../patpat/data/repository/CounselorRepository.kt | 4 ++-- .../simply407/patpat/data/repository/LetterRepository.kt | 8 ++++---- .../simply407/patpat/data/repository/LoginRepository.kt | 8 ++++---- .../patpat/data/repository/UserInfoRepository.kt | 6 +++--- 6 files changed, 20 insertions(+), 17 deletions(-) rename app/src/main/java/com/simply407/patpat/data/api/{patApi.kt => PatPatService.kt} (95%) diff --git a/app/src/main/java/com/simply407/patpat/data/api/patApi.kt b/app/src/main/java/com/simply407/patpat/data/api/PatPatService.kt similarity index 95% rename from app/src/main/java/com/simply407/patpat/data/api/patApi.kt rename to app/src/main/java/com/simply407/patpat/data/api/PatPatService.kt index 9bcb6df..12a06e7 100644 --- a/app/src/main/java/com/simply407/patpat/data/api/patApi.kt +++ b/app/src/main/java/com/simply407/patpat/data/api/PatPatService.kt @@ -23,7 +23,10 @@ import retrofit2.http.Path import retrofit2.http.Query // TODO 대문자, API보단 Service 라는 표현이 더 바람직해보임 -interface patApi { + +// kotlin 공식 coding convention +// - 클래스와 객체의 이름은 대문자 카멜 표기법을 사용합니다. +interface PatPatService { @POST("api/v1/login") suspend fun postLogin(@Body loginRequest: LoginRequest): Response diff --git a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt index ec1e8a7..cf09b9d 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt @@ -7,13 +7,13 @@ class ChattingRepository { // TODO 의존성주입을 하면 더 좋을 것 같다. hilt를 사용해 // TODO create 의 내부 구현을 확인해보면 좋을듯 // TODO API return형은 Response data class 를 반환 할 수 있게 repository에서 구현로직 들어가야함 - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) - suspend fun getAllChattingRoomInfo(accessToken: String, counselorId: String, page: Int, size: Int) = patApi.getAllChattingRoomInfo(accessToken, counselorId, page, size) + suspend fun getAllChattingRoomInfo(accessToken: String, counselorId: String, page: Int, size: Int) = patPatService.getAllChattingRoomInfo(accessToken, counselorId, page, size) suspend fun postMessage( accessToken: String, counselorId: String, postMessageRequest: PostMessageRequest - ) = patApi.postMessage(accessToken, counselorId, postMessageRequest) + ) = patPatService.postMessage(accessToken, counselorId, postMessageRequest) } diff --git a/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt index b2a8006..750714b 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt @@ -4,7 +4,7 @@ import com.simply407.patpat.data.api.RetrofitInstance class CounselorRepository { - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) - suspend fun getCounselors(accessToken: String) = patApi.getCounselors(accessToken) + suspend fun getCounselors(accessToken: String) = patPatService.getCounselors(accessToken) } diff --git a/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt index efe7f19..b22d5b3 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt @@ -6,17 +6,17 @@ import com.simply407.patpat.data.model.LikeLetterRequest class LetterRepository { - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) suspend fun createLetter(accessToken: String, createLetterRequest: CreateLetterRequest) = - patApi.createLetter(accessToken, createLetterRequest) + patPatService.createLetter(accessToken, createLetterRequest) suspend fun getAllLetters(accessToken: String, page: Int, size: Int, isLiked: Boolean) = - patApi.getAllLetters(accessToken, page, size, isLiked) + patPatService.getAllLetters(accessToken, page, size, isLiked) suspend fun likeLetter( accessToken: String, letterId: String, likeLetterRequest: LikeLetterRequest - ) = patApi.likeLetter(accessToken, letterId, likeLetterRequest) + ) = patPatService.likeLetter(accessToken, letterId, likeLetterRequest) } diff --git a/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt index 28485c4..f84c8bc 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt @@ -6,11 +6,11 @@ import com.simply407.patpat.data.model.LoginRequest class LoginRepository { - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) - suspend fun postLogin(loginRequest: LoginRequest) = patApi.postLogin(loginRequest) + suspend fun postLogin(loginRequest: LoginRequest) = patPatService.postLogin(loginRequest) - suspend fun userLogout(accessToken: String) = patApi.userLogout(accessToken) + suspend fun userLogout(accessToken: String) = patPatService.userLogout(accessToken) - suspend fun userWithdrawal(accessToken: String) = patApi.userWithdrawal(accessToken) + suspend fun userWithdrawal(accessToken: String) = patPatService.userWithdrawal(accessToken) } diff --git a/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt index b8c9fba..70069f4 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt @@ -5,10 +5,10 @@ import com.simply407.patpat.data.model.NewUserInfo class UserInfoRepository { - val patApi = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.patApi::class.java) + val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) - suspend fun getUserInfo(accessToken: String) = patApi.getUserInfo(accessToken) + suspend fun getUserInfo(accessToken: String) = patPatService.getUserInfo(accessToken) suspend fun putUserInfo(accessToken: String, newUserInfo: NewUserInfo) = - patApi.putUserInfo(accessToken, newUserInfo) + patPatService.putUserInfo(accessToken, newUserInfo) } From 1e306fb1185134bd8b4752f067a46a14c46728eb Mon Sep 17 00:00:00 2001 From: juseong Date: Sat, 21 Jun 2025 23:52:37 +0900 Subject: [PATCH 03/11] =?UTF-8?q?fix=20:=20object=20=EA=B0=9C=EB=85=90=20?= =?UTF-8?q?=EB=B0=8F=20eager=20vs=20lazy=20=EC=B0=A8=EC=9D=B4=EC=A0=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../simply407/patpat/data/api/RetrofitInstance.kt | 12 +++++++++++- .../patpat/data/repository/ChattingRepository.kt | 3 ++- .../patpat/data/repository/CounselorRepository.kt | 3 ++- .../patpat/data/repository/LetterRepository.kt | 3 ++- .../patpat/data/repository/LoginRepository.kt | 3 ++- .../patpat/data/repository/UserInfoRepository.kt | 3 ++- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt b/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt index 4271c96..96d5ec2 100644 --- a/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt +++ b/app/src/main/java/com/simply407/patpat/data/api/RetrofitInstance.kt @@ -5,10 +5,20 @@ import retrofit2.converter.gson.GsonConverterFactory // TODO object keyword로 선언하면 어떤일이 발생할까?? // TODO eager vs lazy + +// object +// - 싱글톤 패턴을 구현할 때 사용 +// - 해당 object가 처음 접근될 때 지연 초기화(lazy initialization)됩니다 + +// Eager Initialization (즉시 초기화) +// - 프로그램 시작 시 또는 클래스가 로드될 때 바로 인스턴스를 생성하는 방식 +// - 장점: 인스턴스가 항상 준비되어 있어 접근 시 지연이 없습니다 +// - 단점: 사용되지 않을 수도 있는 인스턴스에 대해 미리 자원을 할당하게 됩니다 + object RetrofitInstance { private const val BASE_URL = "https://oognuyh.asuscomm.com/" - private val client = Retrofit + val client: Retrofit = Retrofit .Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) diff --git a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt index cf09b9d..64ac27e 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt @@ -1,5 +1,6 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance import com.simply407.patpat.data.model.PostMessageRequest @@ -7,7 +8,7 @@ class ChattingRepository { // TODO 의존성주입을 하면 더 좋을 것 같다. hilt를 사용해 // TODO create 의 내부 구현을 확인해보면 좋을듯 // TODO API return형은 Response data class 를 반환 할 수 있게 repository에서 구현로직 들어가야함 - val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) suspend fun getAllChattingRoomInfo(accessToken: String, counselorId: String, page: Int, size: Int) = patPatService.getAllChattingRoomInfo(accessToken, counselorId, page, size) diff --git a/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt index 750714b..7059203 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/CounselorRepository.kt @@ -1,10 +1,11 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance class CounselorRepository { - val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) suspend fun getCounselors(accessToken: String) = patPatService.getCounselors(accessToken) } diff --git a/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt index b22d5b3..11885c9 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/LetterRepository.kt @@ -1,12 +1,13 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance import com.simply407.patpat.data.model.CreateLetterRequest import com.simply407.patpat.data.model.LikeLetterRequest class LetterRepository { - val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) suspend fun createLetter(accessToken: String, createLetterRequest: CreateLetterRequest) = patPatService.createLetter(accessToken, createLetterRequest) diff --git a/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt index f84c8bc..86b4081 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/LoginRepository.kt @@ -1,12 +1,13 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance import com.simply407.patpat.data.model.LoginRequest class LoginRepository { - val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) suspend fun postLogin(loginRequest: LoginRequest) = patPatService.postLogin(loginRequest) diff --git a/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt index 70069f4..5279a00 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/UserInfoRepository.kt @@ -1,11 +1,12 @@ package com.simply407.patpat.data.repository +import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance import com.simply407.patpat.data.model.NewUserInfo class UserInfoRepository { - val patPatService = RetrofitInstance.getInstance().create(com.simply407.patpat.data.api.PatPatService::class.java) + private val patPatService: PatPatService = RetrofitInstance.client.create(PatPatService::class.java) suspend fun getUserInfo(accessToken: String) = patPatService.getUserInfo(accessToken) From be7cf593a5730eee1b181b813fed27d42ca0f6c9 Mon Sep 17 00:00:00 2001 From: juseong Date: Sun, 22 Jun 2025 01:29:38 +0900 Subject: [PATCH 04/11] =?UTF-8?q?fix=20:=20=EB=AC=B8=EC=9E=90=EB=A5=BC=20?= =?UTF-8?q?=ED=95=98=EB=93=9C=EC=BD=94=EB=94=A9=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=ED=98=95=ED=83=9C=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../patpat/data/model/CounselorMbtiInfo.kt | 15 ++----- .../patpat/data/repository/MBTIRepository.kt | 44 +++++++++++++++++++ .../simply407/patpat/ui/home/HomeFragment.kt | 10 +++-- app/src/main/res/values/strings.xml | 22 +++++++++- 4 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/simply407/patpat/data/repository/MBTIRepository.kt diff --git a/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt b/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt index 4f8d1fb..5796ccd 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/CounselorMbtiInfo.kt @@ -1,13 +1,6 @@ package com.simply407.patpat.data.model -data class CounselorMbtiInfo(val name: String, val mbtiList: List) - -// TODO 샘플코드이긴 하지만... 만약 String을 객체로 담고 싶다면?? -object MBTIRepository { - val CounselorMbtiDataList = listOf( - CounselorMbtiInfo("복남이", listOf("INFJ", "INFP", "ENFJ", "ENFP")), - CounselorMbtiInfo("닥터 냉철한", listOf("INTJ", "INTP", "ENTJ", "ENTP")), - CounselorMbtiInfo("곽두팔", listOf("ISTJ", "ISTP", "ESTJ", "ESTP")), - CounselorMbtiInfo("코코", listOf("ISFJ", "ISFP", "ESFJ", "ESFP")) - ) -} +data class CounselorMbtiInfo( + val name: String, + val mbtiList: List +) diff --git a/app/src/main/java/com/simply407/patpat/data/repository/MBTIRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/MBTIRepository.kt new file mode 100644 index 0000000..122ddc9 --- /dev/null +++ b/app/src/main/java/com/simply407/patpat/data/repository/MBTIRepository.kt @@ -0,0 +1,44 @@ +package com.simply407.patpat.data.repository + +import android.content.Context +import com.simply407.patpat.R +import com.simply407.patpat.data.model.CounselorMbtiInfo + +// TODO 샘플코드이긴 하지만... 만약 String을 객체로 담고 싶다면?? +class MBTIRepository(context: Context) { + val counselorMbtiDataList = listOf( + CounselorMbtiInfo( + context.getString(R.string.counselor_boknam), + listOf( + context.getString(R.string.mbti_infj), + context.getString(R.string.mbti_infp), + context.getString(R.string.mbti_enfj), + context.getString(R.string.mbti_enfp) + ) + ), CounselorMbtiInfo( + context.getString(R.string.counselor_doctor), + listOf( + context.getString(R.string.mbti_intj), + context.getString(R.string.mbti_intp), + context.getString(R.string.mbti_entj), + context.getString(R.string.mbti_entp) + ) + ), CounselorMbtiInfo( + context.getString(R.string.counselor_kwak), + listOf( + context.getString(R.string.mbti_istj), + context.getString(R.string.mbti_istp), + context.getString(R.string.mbti_estj), + context.getString(R.string.mbti_estp) + ) + ), CounselorMbtiInfo( + context.getString(R.string.counselor_coco), + listOf( + context.getString(R.string.mbti_isfj), + context.getString(R.string.mbti_isfp), + context.getString(R.string.mbti_esfj), + context.getString(R.string.mbti_esfp) + ) + ) + ) +} diff --git a/app/src/main/java/com/simply407/patpat/ui/home/HomeFragment.kt b/app/src/main/java/com/simply407/patpat/ui/home/HomeFragment.kt index 4bfe7f4..473b869 100644 --- a/app/src/main/java/com/simply407/patpat/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/simply407/patpat/ui/home/HomeFragment.kt @@ -9,8 +9,8 @@ import android.view.ViewGroup import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import com.simply407.patpat.R -import com.simply407.patpat.data.model.MBTIRepository import com.simply407.patpat.data.model.SharedPreferencesManager +import com.simply407.patpat.data.repository.MBTIRepository import com.simply407.patpat.databinding.FragmentHomeBinding import com.simply407.patpat.databinding.ItemCounselorBinding import com.simply407.patpat.ui.login.LoginViewModel @@ -24,6 +24,8 @@ class HomeFragment : Fragment() { private lateinit var loginViewModel: LoginViewModel private lateinit var homeViewModel: HomeViewModel + private lateinit var mbtiRepository: MBTIRepository + val TAG = "HomeFragment1" override fun onCreateView( @@ -42,6 +44,8 @@ class HomeFragment : Fragment() { homeViewModel.getCounselors(it) } + mbtiRepository = MBTIRepository(requireContext()) + observeData() initUi() clickCounselor() @@ -161,7 +165,7 @@ class HomeFragment : Fragment() { // 유저의 mbti 를 토대로 상담사 추천 private fun counselorRecommendation(userMbti: String) { // 사용자의 MBTI와 일치하는 상담사 찾기 - val recommendedCounselor = MBTIRepository.CounselorMbtiDataList.find { counselorInfo -> + val recommendedCounselor = mbtiRepository.counselorMbtiDataList.find { counselorInfo -> counselorInfo.mbtiList.contains(userMbti) } @@ -222,4 +226,4 @@ class HomeFragment : Fragment() { } } -} \ No newline at end of file +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 008fd4e..f60a113 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,5 +48,25 @@ PS. 웃음이 보약인기라!! 😝 PS. 새겨 들어라 😼 붕붕아 중요한 발표가 있어서 긴장된다고 했지이~? 그치만 잘 할 수 있을거라고 믿어~! 열심히 준비 했으니까 잘 할 수 있을 것 같아~! 정말 느낌이 좋다니까? 자신을 한 번 믿어봐~ 나는 항상 널 믿어~! 지금까지 쭉 잘 해왔듯이 앞으로도 잘할어야아~! 그래도 너무 긴장이 되면, 아이스크림을 먹어 봐! 우리 집에 놀러오면 직접 만든 아이스크림을 줄게. + 복남이 + 닥터 냉철한 + 곽두팔 + 코코 + INFJ + INFP + ENFJ + ENFP + INTJ + INTP + ENTJ + ENTP + ISTJ + ISTP + ESTJ + ESTP + ISFJ + ISFP + ESFJ + ESFP - \ No newline at end of file + From e7300ceac71390280bd4f6b3493bc3b32180a65e Mon Sep 17 00:00:00 2001 From: juseong Date: Sun, 22 Jun 2025 01:48:22 +0900 Subject: [PATCH 05/11] =?UTF-8?q?fix=20:=20API=20data=20class=20=EC=99=80?= =?UTF-8?q?=20UI=20data=20class=20=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../patpat/data/model/CreateLetterResponse.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt b/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt index f255adb..5aeb10b 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/CreateLetterResponse.kt @@ -5,6 +5,14 @@ import kotlinx.parcelize.Parcelize // TODO API data class 와 UI data class는 분리하는게 좋다. isLiked는 api에서 처리? // TODO isLiked 는 var로 해야하나? val로 선언하는 방법은? mutable vs immutable + +// API data class 와 UI data class는 분리하는게 좋다 +// - 관심사 분리 +// - 유지보수 하기 좋다 + +// API 응답은 Immutable하게 val로 받는 것이 일반적 + +// CreateLetterResponse -> LetterResponse 이라는 이름이 더 좋음 @Parcelize data class CreateLetterResponse( val id: String, @@ -12,7 +20,19 @@ data class CreateLetterResponse( val footer: String?, val counselorId: String, val userId: String, - var isLiked: Boolean, + val isLiked: Boolean, val createdAt: String, val updatedAt: String ) : Parcelable + +// UI data class +// - API data 로 받아온 걸 UI data 형태로 데이터 변환(Mapping) 해서 표현되게 해줘야 함 +data class LetterUiModel( + val id: String, + val content: String, + val footer: String?, + val counselorName: String, + val userName: String, + var isLiked: Boolean, + val displayDate: String +) From 151369a027661c6658b88b52f8fb982eb0e8aea4 Mon Sep 17 00:00:00 2001 From: juseong Date: Sun, 22 Jun 2025 02:46:46 +0900 Subject: [PATCH 06/11] =?UTF-8?q?fix=20:=20=EC=9B=90=EC=8B=9C=ED=83=80?= =?UTF-8?q?=EC=9E=85=20vs=20=EC=B0=B8=EC=A1=B0=ED=83=80=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/model/GetAllLettersResponse.kt | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt b/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt index a37d4e4..a7d4750 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/GetAllLettersResponse.kt @@ -2,9 +2,25 @@ package com.simply407.patpat.data.model // TODO data 가 null이 되면 어떤일이 벌어질까? totalElements 이 null이 되면 어떤일이 벌어질까? // TODO 원시타입 vs 참조타입 + +// JsonDataException 또는 JsonParseException) 가 발생합니다. +// - non nullable -> nullable 변경해주기 +// ex) val data: List -> val data: List? + +// 원시타입 +// - 기본 데이터 타입으로, 메모리에 직접 값을 저장 +// - ex) Int, Long, Float, Double, Boolean, Char, Byte, Short 등 +// - null 값을 가질 수 없음 + +// 참조타입 +// - 객체의 메모리 주소를 저장 +// - ex) String, List, Map, Array, Class 등 +// - null 값을 가질 수 있음 +// - Int?, Boolean? → Kotlin에서 nullable한 원시 타입도 결국 참조 타입으로 처리됨 + data class GetAllLettersResponse( - val data: List, - val totalElements: Int, + val data: List?, + val totalElements: Int?, val currentPage: Int, val totalPages: Int ) From ba6027c4fe87a07d25649769efb005f8639a8802 Mon Sep 17 00:00:00 2001 From: juseong Date: Sun, 22 Jun 2025 03:12:09 +0900 Subject: [PATCH 07/11] fix : MainThread, IOThread, WorkerThread --- .../simply407/patpat/data/model/SharedPreferencesManager.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt b/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt index bb3c905..d9799aa 100644 --- a/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt +++ b/app/src/main/java/com/simply407/patpat/data/model/SharedPreferencesManager.kt @@ -4,6 +4,12 @@ import android.content.Context import android.content.SharedPreferences // TODO Preference는 MainThread? IOThread? WorkerThread? +// - MainThread 에서 SharedPreferences 작업을 요청하고, 실제 파일은 IOThread 에서 실행 + +// - MainThread : UI와 상호작용하는 메인 스레드 +// - IOThread : 입출력 작업을 수행하는 스레드 +// - WorkerThread : 일반적인 계산이나 비동기 작업을 수행하는 스레드 +// ex) bit map 처리 같은 무거운 작업을 할 때, 사용 되면 좋음 object SharedPreferencesManager { const val FILE_NAME = "user_info" From 020edaab787750e037fc18c0b07c57f9df257ad0 Mon Sep 17 00:00:00 2001 From: juseong Date: Sun, 22 Jun 2025 03:52:45 +0900 Subject: [PATCH 08/11] =?UTF-8?q?fix=20:=20Hilt=20=EC=9D=84=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=EC=A3=BC?= =?UTF-8?q?=EC=9E=85=20=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 8 ++++- .../data/repository/ChattingRepository.kt | 29 +++++++++++++++++++ build.gradle.kts | 5 ++-- gradle/libs.versions.toml | 4 +++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7c509aa..44b1a64 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,6 +5,8 @@ plugins { alias(libs.plugins.jetbrains.kotlin.android) id ("kotlin-kapt") id("kotlin-parcelize") + id("com.google.devtools.ksp") + id("com.google.dagger.hilt.android") } android { @@ -100,4 +102,8 @@ dependencies { // glide implementation(libs.glide) -} \ No newline at end of file + + // hilt + implementation(libs.hilt.android) + ksp(libs.hilt.android.compiler) +} diff --git a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt index 64ac27e..4d18fe1 100644 --- a/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt +++ b/app/src/main/java/com/simply407/patpat/data/repository/ChattingRepository.kt @@ -2,7 +2,12 @@ package com.simply407.patpat.data.repository import com.simply407.patpat.data.api.PatPatService import com.simply407.patpat.data.api.RetrofitInstance +import com.simply407.patpat.data.model.ChattingRoomInfo +import com.simply407.patpat.data.model.MessageInfo import com.simply407.patpat.data.model.PostMessageRequest +import retrofit2.Response +import javax.inject.Inject +import javax.inject.Singleton class ChattingRepository { // TODO 의존성주입을 하면 더 좋을 것 같다. hilt를 사용해 @@ -18,3 +23,27 @@ class ChattingRepository { postMessageRequest: PostMessageRequest ) = patPatService.postMessage(accessToken, counselorId, postMessageRequest) } + +// Hilt에 결합 정보를 제공하는 한 가지 방법은 생성자 삽입입니다 +// 클래스의 생성자에서 @Inject 주석을 사용하여 클래스의 인스턴스를 제공하는 방법을 Hilt에 알려줍니다 +@Singleton +class ChattingRepository @Inject constructor( + private val patPatService: PatPatService +) { + suspend fun getAllChattingRoomInfo( + accessToken: String, + counselorId: String, + page: Int, + size: Int + ): Response { + return patPatService.getAllChattingRoomInfo(accessToken, counselorId, page, size) + } + + suspend fun postMessage( + accessToken: String, + counselorId: String, + postMessageRequest: PostMessageRequest + ): Response { + return patPatService.postMessage(accessToken, counselorId, postMessageRequest) + } +} diff --git a/build.gradle.kts b/build.gradle.kts index 5f3e9e5..721300a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,5 +2,6 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.jetbrains.kotlin.android) apply false - -} \ No newline at end of file + id("com.google.devtools.ksp") version "1.9.23-1.0.20" apply false + id("com.google.dagger.hilt.android") version "2.56.2" apply false +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b0fbd8b..0b01491 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,6 +4,8 @@ circleimageview = "3.1.0" converterGson = "2.9.0" glide = "4.16.0" gson = "2.10.1" +hiltAndroid = "2.56.2" +hiltAndroidVersion = "2.56.2" jetbrainsKotlinxCoroutinesAndroid = "1.6.4" kotlin = "1.9.0" coreKtx = "1.13.1" @@ -43,6 +45,8 @@ circleimageview = { module = "de.hdodenhof:circleimageview", version.ref = "circ converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroidVersion" } +hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidVersion" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } From 0a070460de5e59d286989a4a94f920cc2372bf74 Mon Sep 17 00:00:00 2001 From: juseong Date: Sun, 22 Jun 2025 14:19:37 +0900 Subject: [PATCH 09/11] =?UTF-8?q?fix=20:=20ListAdapter=20=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../patpat/ui/chatting/ChattingAdapter.kt | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt index 7a7d10b..3c84cac 100644 --- a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt +++ b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingAdapter.kt @@ -3,11 +3,15 @@ package com.simply407.patpat.ui.chatting import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.simply407.patpat.R import com.simply407.patpat.data.model.MessageInfo import com.simply407.patpat.databinding.ItemChattingCounselorBinding import com.simply407.patpat.databinding.ItemChattingMyBinding +import com.simply407.patpat.ui.chatting.ChattingAdapter.Companion.TYPE_ONE +import com.simply407.patpat.ui.chatting.ChattingAdapter.Companion.TYPE_TWO import com.simply407.patpat.ui.main.MainActivity // TODO allMessagesDataList는 val로 선언 @@ -119,6 +123,8 @@ class ChattingAdapter(private var allMessagesDataList: MutableList, fun updateMessages(newMessages: List) { allMessagesDataList = newMessages.toMutableList() // TODO notifyDataSetChanged는 최악의 갱신 method() + // - 모든 데이터들을 모두 갱신하는 method() + // - 데이터가 많아지면 비효율적, 깜빡이는 현상 같은게 생김 notifyDataSetChanged() val mainActivity = MainActivity() @@ -127,8 +133,128 @@ class ChattingAdapter(private var allMessagesDataList: MutableList, companion object { // TODO 접근제어자 공부 필요 + // 1. Private + // 2. Public + // 3. Protected + // 4. inrenal const val TYPE_ONE = 1 const val TYPE_TWO = 2 } } + +// ListAdapter 선언 +class ChattingListAdapter( + private val counselorId: Int +) : ListAdapter(MessageDiffCallback()) { + + inner class ChattingCounselorViewHolder(private val binding: ItemChattingCounselorBinding) : RecyclerView.ViewHolder(binding.root) { + + fun bind(message: MessageInfo) { + + // 상담사 프로필 이미지 설정 + when (counselorId) { + 0 -> { + binding.imageViewChattingCounselorItem.setImageResource(R.drawable.ic_profile_boknam) + } + 1 -> { + binding.imageViewChattingCounselorItem.setImageResource(R.drawable.ic_doctor_profile) + } + 2 -> { + binding.imageViewChattingCounselorItem.setImageResource(R.drawable.ic_kwak_profile) + } + 3 -> { + binding.imageViewChattingCounselorItem.setImageResource(R.drawable.ic_coco_profile) + } + } + + // 상담사 메시지 설정 + binding.textViewChattingCounselorItem.text = message.content + } + + } + + inner class ChattingMyViewHolder(private val binding: ItemChattingMyBinding) : RecyclerView.ViewHolder(binding.root) { + + fun bind(message: MessageInfo) { + + // 유저 말풍선 색상 설정 + when (counselorId) { + 0 -> { + binding.textViewChattingMyItem.setBackgroundResource(R.drawable.bg_bok_chatting_body) + binding.imageViewTailChattingMyItem.setImageResource(R.drawable.ic_bok_chatting_tail) + } + 1 -> { + binding.textViewChattingMyItem.setBackgroundResource(R.drawable.bg_doctor_chatting_body) + binding.imageViewTailChattingMyItem.setImageResource(R.drawable.ic_doctor_chatting_tail) + } + 2 -> { + binding.textViewChattingMyItem.setBackgroundResource(R.drawable.bg_kwak_chatting_body) + binding.imageViewTailChattingMyItem.setImageResource(R.drawable.ic_kwak_chatting_tail) + } + 3 -> { + binding.textViewChattingMyItem.setBackgroundResource(R.drawable.bg_coco_chatting_body) + binding.imageViewTailChattingMyItem.setImageResource(R.drawable.ic_coco_chatting_tail) + } + } + + // 유저 메시지 설정 + binding.textViewChattingMyItem.text = message.content + } + + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when(viewType) { + TYPE_ONE -> { + val binding = ItemChattingCounselorBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ChattingCounselorViewHolder(binding) + } + TYPE_TWO -> { + val binding = ItemChattingMyBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ChattingMyViewHolder(binding) + } + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun getItemViewType(position: Int): Int { + return when (getItem(position).role) { + "ASSISTANT" -> TYPE_COUNSELOR + "USER" -> TYPE_USER + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val message = getItem(position) + when (holder.itemViewType) { + TYPE_COUNSELOR -> { + (holder as ChattingCounselorViewHolder).bind(message) + } + TYPE_USER -> { + (holder as ChattingMyViewHolder).bind(message) + } + } + } + + // viewModel 에서 가져와서 observe patten 으로 갱신(submitList) 하도록 하는게 더 좋음 + fun submitNewMessages(newMessages: List) { + submitList(newMessages) + } + + class MessageDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: MessageInfo, newItem: MessageInfo): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: MessageInfo, newItem: MessageInfo): Boolean { + return oldItem == newItem + } + } + + companion object { + const val TYPE_COUNSELOR = 1 + const val TYPE_USER = 2 + } +} From 4f97ec2b5f9fcba834f2681d8d87dfcf0b61d76f Mon Sep 17 00:00:00 2001 From: juseong Date: Sun, 22 Jun 2025 15:31:35 +0900 Subject: [PATCH 10/11] =?UTF-8?q?fix=20:=20by=20lazy,=20ViewModel=20ktx=20?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 4 ++++ .../patpat/ui/chatting/ChattingFragment.kt | 19 ++++++++++++++----- gradle/libs.versions.toml | 5 +++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 44b1a64..e19a2ad 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -106,4 +106,8 @@ dependencies { // hilt implementation(libs.hilt.android) ksp(libs.hilt.android.compiler) + + implementation(libs.androidx.lifecycle.viewmodel.ktx.v291) + implementation(libs.androidx.lifecycle.livedata.ktx) + implementation(libs.androidx.fragment.ktx) } diff --git a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt index 18a9357..cf170bb 100644 --- a/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt +++ b/app/src/main/java/com/simply407/patpat/ui/chatting/ChattingFragment.kt @@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -26,12 +27,20 @@ class ChattingFragment : Fragment() { // TODO Activity와 Fragment 통신 하는 방법 공부 // TODO Fragment 간 통신 하는 방법 공부 + // 1. ViewModel (가장 좋은 방식) + // 2. Bundle + // TODO by lazy를 사용해보는건 어떠한가? lateinit var 개인적으로 최대한 사용을 안하는게 좋은것 같음 왜?? + // - lateinit var 으로 선언 한 다음 초기화 전에 접근을 할 경우 NPE 발생할 수 있음 + // - lateinit var는 var 만 가능해서, 초기화된 후에도 언제든지 값을 변경할 수 있음 + // - by lazy 는 val 로 선언해서 불변성을 유지시켜줌 + private lateinit var mainActivity: MainActivity private lateinit var binding: FragmentChatting2Binding - private lateinit var chattingAdapter: ChattingAdapter - - private lateinit var chattingViewModel: ChattingViewModel + private val chattingAdapter: ChattingAdapter by lazy { + ChattingAdapter(mutableListOf(), currentPageIndex) + } + private val chattingViewModel by viewModels() private var currentPageIndex: Int = 0 private var counselorId: String = "" @@ -51,7 +60,7 @@ class ChattingFragment : Fragment() { counselorId = arguments?.getString("counselorId") ?: "" // TODO ViewModel ktx 공부 - chattingViewModel = ViewModelProvider(this)[ChattingViewModel::class.java] + // chattingViewModel = ViewModelProvider(this)[ChattingViewModel::class.java] chattingViewModel.getAllChattingRoomInfo(SharedPreferencesManager.getUserAccessToken()!!, counselorId, 1, 100) @@ -101,7 +110,7 @@ class ChattingFragment : Fragment() { } recyclerViewChatting.run { - chattingAdapter = ChattingAdapter(mutableListOf(), currentPageIndex) + // chattingAdapter = ChattingAdapter(mutableListOf(), currentPageIndex) adapter = chattingAdapter layoutManager = LinearLayoutManager(requireContext()) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0b01491..d665626 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,9 @@ [versions] agp = "8.6.0" +androidxLifecycleViewmodelKtx = "2.9.1" circleimageview = "3.1.0" converterGson = "2.9.0" +fragmentKtx = "1.8.8" glide = "4.16.0" gson = "2.10.1" hiltAndroid = "2.56.2" @@ -36,8 +38,11 @@ v2All = "2.20.3" adapter-rxjava2 = { module = "com.squareup.retrofit2:adapter-rxjava2", version.ref = "converterGson" } adapter-rxjava3 = { module = "com.squareup.retrofit2:adapter-rxjava3", version.ref = "converterGson" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" } +androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidxLifecycleViewmodelKtx" } androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } androidx-lifecycle-viewmodel-ktx-v240 = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtxVersion" } +androidx-lifecycle-viewmodel-ktx-v291 = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycleViewmodelKtx" } androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" } From 3af58283dcfa890ed6979057964b4502756f7b81 Mon Sep 17 00:00:00 2001 From: juseong Date: Sun, 22 Jun 2025 16:12:36 +0900 Subject: [PATCH 11/11] fix : postValue vs setValue --- .../com/simply407/patpat/ui/letter/LetterViewModel.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt b/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt index 3fdefed..06d9026 100644 --- a/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt +++ b/app/src/main/java/com/simply407/patpat/ui/letter/LetterViewModel.kt @@ -32,6 +32,17 @@ class LetterViewModel: ViewModel() { fun createLetter(accessToken: String, createLetterRequest: CreateLetterRequest) { viewModelScope.launch { // TODO postValue vs setValue, post를 하면 발생하는 상황도 함께 공부 + + // setValue + // - 메인(UI) 스레드에서만 호출 + // -> 메인 스레드가 아닌 다른 스레드에서 호출하면 IllegalStateException이 발생 + // - LiveData의 값을 즉시 동기적으로(synchronously) 업데이트 + + // postValue + // - 어떤 스레드에서든 호출 가능 + // - LiveData의 값을 즉시 업데이트하지 않고, 메인 스레드의 이벤트 큐에 값 업데이트 작업을 게시(post)합니다. + // 이 작업은 메인 스레드가 준비되었을 때 비동기적으로(asynchronously) 처리됩니다. + _isLoading.postValue(true) try { val response = letterRepository.createLetter(accessToken, createLetterRequest)