-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
6차 과제 리뷰 반영 및 7차 과제 필수과제 완료🐱👤 #7
base: develop/view
Are you sure you want to change the base?
Conversation
Signup->SignIn
logger, client 초기화 코드 분리
data 디렉토리 구조 변경 - api 디렉토리에 ApiClient와 Service 합침 - DataSource가 아닌 Repository를 추상화(DataSourceImpl 삭제 및 Repository Impl 추가) - source 디렉토리를 local과 remote로 분리 domain 디렉토리 추가 - Repository interface 추가 Repository 모듈 수정
SignUpActivity의 에러 체크 로직 분리 -> inputErrorCheck
state_enabled에 따라 분리되어있던 파일을 하나로 합침
Success, Loading, Empty, Failure등 다양한 상태에 따른 분기 처리를 가능하도록 구현
music_add_item, music_info_item 추가
MusicFragment, ViewModel, xml 생성
Music Fetch, Upload(Multipart) 네트워크 구현
Music Base Url 추가
음악 추가 커스텀 다이얼로그 구현
외부 저장소 접근 권한 설정
굿럭입니다.. |
8차 세미나 전까지 코드 리뷰 달아드리겠습니당,,, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 완! 고생하셨습니다~
@@ -100,4 +97,6 @@ dependencies { | |||
implementation "com.google.dagger:hilt-android:$hilt_version" | |||
kapt "com.google.dagger:hilt-android-compiler:$hilt_version" | |||
|
|||
//coil | |||
implementation 'io.coil-kt:coil:2.2.2' | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Goooood!
import androidx.databinding.ViewDataBinding | ||
import androidx.fragment.app.DialogFragment | ||
|
||
abstract class BindingDialog<B : ViewDataBinding>(@LayoutRes private val layoutResId: Int) : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 BindingDialog까지... 좋습니다~~
object ApplicationModule { | ||
@Singleton | ||
@Provides | ||
fun provideApplication(): ApplicationClass { | ||
return ApplicationClass.getInstance() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
애플리케이션을 외부에서 의존성을 주입해야할 일이 있나용?! 언제 주입하고 있는 것일까요!
return HomeRepository( | ||
HomeDataSourceImpl( | ||
return HomeRepositoryImpl( | ||
HomeDataSource( | ||
ApiClient.getRetrofitForUserList()!!.create(HomeService::class.java) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
의존성 주입을 위한 서비스 모듈과 네트워크 모듈 따로 만들어 두는 것이 좋겠어요~
HomeService의 경우 아래 최하단에 적은 constructor inject이 불가능한 케이스의 2번 경우에 해당합니다.(관련 내용은 제 pr에 남겨주신 첫 댓글에 적어두었으니 참고바랍니다~) 따라서 생성자 주입을 위해 서비스 모듈을 만들어 주시면 HomeDataSource에서 homeService를 의존성 주입하실 수 있게 되고, 그렇다면 직접 homeService 객체를 생성하지 않고도 해당 객체 주입이 가능하기 때문에 다음과 같이 코드 수정이 가능합니다!
@Provides
@Singleton
fun provideHomeRepository(dataSource: HomeDataSource): HomeRepository = HomeRepositoryImpl(dataSource)
또한 더 나아가서 위 코드에서 Provides 대신에 Binds로 수정해볼 수 있습니다!
@Binds
@Singleton
fun bindHomeRepository(homeRepositoryImpl: HomeRepositoryImpl): HomeRepository
위와 같이 수정한 이유는
- Binds 를 사용할 수 있는 조건에 충족되기 때문입니다.
- Binds가 붙은 메서드는 객체 생성을 위한 별다른 구현이 필요없으면서, 반드시 하나의 파라미터만 가질 수 있는데 현재 파라미터 homeRepositoryImpl 하나만 필요한 상황입니다. 또한 함수의 반환 타입은 구현하고자 하는 interface type**이며, 매개변수는 실제 제공하고자 하는 interface의 구현체 class인 경우 Binds를 사용해 볼 수 있습니다.
- 그렇다면 왜 Provides 대신에 Binds를 사용하는 것이냐 하면 일단 문법 자체도 간결할 뿐만 아니라 Binds가 DI를 위한 코드를 덜 생성하기 때문입니다. Binds는 Provides의 특수한 형태일 뿐, 역할은 Provides와 같은데요! 다른점이라고 하면 Binds에는 여러 제약이 생기면서 처리해야할 부분이 줄어듦에 따라 DI를 위한 코드를 덜 생성하게 돼죠!
constructor inject이 불가능한 케이스
-> 생성자를 사용할 수 없는 Class를 주입해야 하는 경우
- Interface(constructor를 가질 수 없음), Builder를 통해 생성되는 Class
- Room과 Retrofit과 같은 직접 구현할 수 없는 클래스(
3rd party library
)3rd party library
- 개인 개발자나 프로젝트 팀, 혹은 업체등에서 개발하는 라이브러로 즉 제 3자 라이브러리입니다! 제작사에서 만든 것이 아니라 다른 업체에서 만든 해당 툴 지원 라이브러리. 제조사와 사용자 이외 외부의 생산자를 가리킵니다!
너무 많은 내용들이고 대부분 거의 생략하고 작성한 것입니다.. 추가적으로 binds 사용시 고려해야할 몇가지 사항이 있습니다! 고것은 이 문서를 참고해보시면 좋을 것 같아요~
|
||
@AndroidEntryPoint | ||
class MusicFragment : BindingFragment<FragmentMusicBinding>(R.layout.fragment_music) { | ||
private val viewModel: MusicViewModel by viewModels() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private val viewModel: MusicViewModel by viewModels() | |
private val viewModel: MusicViewModel by activityViewModels() |
홈프래그먼트와 뮤직 다이얼로그에서 뷰모델 초기화 방식을 by activityViewModels()로 바꿔주시면 발생하신 이슈가 해결될 것 같습니다~ by activityViewModels()는 Fragment에서만 사용 가능한 viewModel 초기화 방식입니다!! Fragment가 생성된 Activity의 라이프사이클에 ViewModel을 종속시키기 때문에 같은 Activity를 공유하는 Fragmenet간 데이터 공유가 가능하게 됩니다~
현재는 홈 프래그먼트와 뮤직 다이얼로그가 각각 viewModels()를 사용하고 있고 뮤직 다이얼로그에서 musicList에 값을 setValue하더라도 뮤직 다이얼로그(프래그먼트) 라이프사이클에 뷰모델을 종속시켜서 다이얼로그가 종료되면 setValue한 값도 사라지게 되기 때문이죠!
is MusicInfo -> MUSIC_INFO_TYPE | ||
is MusicAdd -> MUSIC_ADD_TYPE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
멀티 뷰홀더 좋네요~
binding.musicItemIv.load(infoData.img) | ||
binding.musicItemTitleTv.text = infoData.title | ||
binding.musicItemSingerTv.text = infoData.singer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
데이터 바인딩 쓰시면 깔꼼해질 것 같네용
dialog.isCancelable = false //배경클릭 금지 | ||
dialog.show(childFragmentManager, "MusicAddDialog") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분은 고차함수를 적용해보는 것도 좋겠습니다~
참고 링크입니다 :)
sealed class UiState { | ||
object Success : UiState() | ||
object Empty : UiState() | ||
object Failure : UiState() | ||
object Loading : UiState() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UiState 활용까지 대단하십니다~~ Gooooood!
} | ||
/** uri -> file-> RequestBody **/ | ||
|
||
fun toFormData() = MultipartBody.Part.createFormData("image", getFileName(), this) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
지훈쓰도 마찬가지입니다!
"image"대신에 name값을 생성자 파라미터로 받아도 좋을 것 같아요~
다른 api에서도 멀티파트로 이미지 업로드 처리를 하고, 서버쪽에서 명시한 key값이 image가 아닌 다른 문자열일 경우를 고려해서
어떤 name이든지 해당 클래스로 사용할 수 있도록 처리하면 좋겠네요~
📍 과제 수행 도중 발생한 이슈(미해결)
🎈목표
음악 추가 다이얼로그를 생성하고 정보 입력 후, 확인버튼을 누르면 업로드 및 다이얼로그가 종료됨과
동시에, 갱신된 음악 리스트가 뷰에 반영되는 것이 목표!
🎈문제 상황
※ MusicFragment와 MusicAddDialog에서 MusicViewModel의 musicState를 구독하고 있는 상태
음악 추가 다이얼로그(MusicAddDialog)에 정보 입력 후 확인 버튼 클릭 -> 음악 업로드 uploadMusic()(MusicViewModel) -> 음악 리스트 받아오기 fetchMusicList() 호출(MusicViewModel) -> musicList 데이터 변경(MusicViewModel) -> musicState.value = UiState.Success ->
MusicAddDialog에서 musicState를 구독하고 있다가, musicState값 변경에 따라 다이얼로그 종료
동시에
MusicFragment에서 musicState를 구독하고 있다가, musicState값 변경에 따라 MusicListAdapter의 setData 메소드 호출
🎈문제점
로그 확인 결과 musicList 데이터 변경(MusicViewModel)까지는 제대로 반영 되지만, MusicFragment에서 MusicListAdapter의 setData함수를 호출하지는 못함.
다시 말하면, 다이얼로그 종료 -> 음악 정보 업로드 -> 음악 리스트 받아오기 -> 다이얼로그 종료 까지만 실행되고, 그 이후 musicState를 구독하는 MusicFragment에서 리사이클러 뷰에 바뀐 음악 리스트를 반영해주는 setData 로직은 실행되지 않음.
다이얼로그 이미지
🎃시도해본 점
다이얼로그에서 MusicViewModel의 fetchMusicList()메소드를 호출해서 리사이클러뷰에 데이터를 전달하도록 구현.
즉, 다이얼로그가 살아있는 상태에서 음악 리스트 정보를 받아오고 리사이클러뷰까지
변경시킨 다음에 다이얼로그를 종료하는 로직 구현
=>
음악 리스트 정보를 받아올 수는 있으나,
그 이후의 리사이클러뷰를 변경하는 MusicFragment에 존재하는 setData 로직은 실행되지 않음.
다이얼로그가 보여지는 동안에는 MusicFragment에서 MusicViewModel의 musicState를 구독하지 못한다고 판단.
이미 MusicViewModel에는 fetch된 음악 리스트가 저장되어있는 상황이니,
MusicViewModel에 라이브데이터 isDialogAlive를 만들고, MusicFragment가 이를 구독하고 있다가,
다이얼로그가 꺼지면서 isDialogAlive값이 변경되면 음악 리스트를 가져와서 리사이클러 뷰에 반영하는 MusicFragment에 존재하는 setData로직을 수행하도록 구현
=>
여전히 다이얼로그 종료 까지만 실행되고, 그 이후 setData 로직은 실행되지 않음.