Skip to content
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

Feat/week xml07 #19

Open
wants to merge 5 commits into
base: develop-xml
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
id 'kotlin-parcelize'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.0' // build.gradle project에서 확인 가능
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
}

// plugin{}과 andriod{} 사이
Expand Down Expand Up @@ -43,6 +44,9 @@ android {
viewBinding true
buildConfig true // build 할 때 buildConfig 파일이 만들어짐 -> 망치 이미지(build) 누르기!!
}
kapt {
correctErrorTypes true
}
}

dependencies {
Expand Down Expand Up @@ -75,14 +79,13 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")

// dagger hilt
implementation "com.google.dagger:hilt-android:2.48.1"
kapt "com.google.dagger:hilt-compiler:2.48.1"

// viewmodel_ktx
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"

// coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'

// dagger hilt
implementation "com.google.dagger:hilt-android:2.51"
kapt "com.google.dagger:hilt-compiler:2.51"
Comment on lines +88 to +90

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

키야.. 믓찌다..

}
2 changes: 2 additions & 0 deletions app/src/main/java/com/sopt/now/ApplicationClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package com.sopt.now
import android.app.Application
import com.sopt.now.ApplicationClass.SharedPreferences.editor
import com.sopt.now.ApplicationClass.SharedPreferences.sSharedPreferences
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class ApplicationClass: Application() {
object SharedPreferences {
lateinit var sSharedPreferences: android.content.SharedPreferences
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/com/sopt/now/data/AuthRepositoryImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.sopt.now.data

import com.sopt.now.datasource.AuthService
import com.sopt.now.model.login.RequestLoginDto
import com.sopt.now.model.signup.RequestSignUpDto
import com.sopt.now.repository.AuthRepository
import com.sopt.now.utils.BaseResponse
import retrofit2.Response

class AuthRepositoryImpl(
private val authService: AuthService
): AuthRepository {
override suspend fun loginUser(data: RequestLoginDto): Response<BaseResponse<Unit>> {
return authService.login(data)
}

override suspend fun signupUser(data: RequestSignUpDto): Response<BaseResponse<Unit>> {
return authService.signUp(data)
}
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/sopt/now/data/MyPageRepositoryImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sopt.now.data

import com.sopt.now.datasource.InfoService
import com.sopt.now.model.info.UserInfo
import com.sopt.now.repository.MyPageRepository
import com.sopt.now.utils.BaseResponse
import retrofit2.Response

class MyPageRepositoryImpl(
private val infoService: InfoService
) : MyPageRepository {
override suspend fun getUserInfo(): Response<BaseResponse<UserInfo>> {
return infoService.getUserInfo()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.sopt.now.presentation.auth.login

import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.flowWithLifecycle
Expand All @@ -15,12 +14,14 @@ import com.sopt.now.presentation.main.MainActivity
import com.sopt.now.utils.Constants.Companion.MEMBER_ID
import com.sopt.now.utils.UiState
import com.sopt.now.utils.showToast
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

@AndroidEntryPoint
class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding
private val loginViewModel by viewModels<LoginViewModel>()
private val loginViewModel: LoginViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 뷰모델의 서버 통신 로직들은 ui단의 뷰모델이 아닌 data단의 repostiory impl에서 수행하는게 더 좋을 것 같아요!

Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
package com.sopt.now.presentation.auth.login

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sopt.now.ApplicationClass.SharedPreferences.editor
import com.sopt.now.model.login.RequestLoginDto
import com.sopt.now.repository.AuthRepository
import com.sopt.now.utils.Constants.Companion.MEMBER_ID
import com.sopt.now.utils.NetworkUtil
import com.sopt.now.utils.ServicePool.loginService
import com.sopt.now.utils.UiState
import dagger.hilt.android.HiltAndroidApp
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

class LoginViewModel : ViewModel() {
@HiltViewModel
class LoginViewModel @Inject constructor(
private val authRepository: AuthRepository
) : ViewModel() {
private val _state = MutableStateFlow<UiState<Unit>>(UiState.LOADING)
val state get() = _state.asStateFlow()
Comment on lines 23 to 24

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사소하지만 변수의 타입을 명시적으로 적어주는 것이 좋습니다!


fun postLogin(data: RequestLoginDto) {
viewModelScope.launch(Dispatchers.IO) {
_state.value = UiState.LOADING
runCatching {
loginService.login(data)
authRepository.loginUser(data)
}.onSuccess {
if (it.isSuccessful) {
editor.putString(MEMBER_ID, it.headers()["location"])
Expand All @@ -33,12 +36,14 @@ class LoginViewModel : ViewModel() {
_state.value = UiState.SUCCESS(null)
} else {
_state.value = UiState.FAILURE(
it.errorBody()?.let { e -> NetworkUtil.getErrorResponse(e)?.message }.toString()
it.errorBody()?.let { e -> NetworkUtil.getErrorResponse(e)?.message }
.toString()
)
}
}.onFailure {
it.printStackTrace()
}

}
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
package com.sopt.now.presentation.auth.signup

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sopt.now.model.signup.RequestSignUpDto
import com.sopt.now.repository.AuthRepository
import com.sopt.now.utils.NetworkUtil
import com.sopt.now.utils.ServicePool.authService
import com.sopt.now.utils.UiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

class SignUpViewModel : ViewModel() {
@HiltViewModel
class SignUpViewModel @Inject constructor(
private val authRepository: AuthRepository
) : ViewModel() {
private val _state = MutableStateFlow<UiState<Unit>>(UiState.LOADING)
val state = _state.asStateFlow()
val state get() = _state.asStateFlow()

fun signUp(data: RequestSignUpDto) {
viewModelScope.launch(Dispatchers.IO) {
_state.value = UiState.LOADING
runCatching {
authService.signUp(data)
authRepository.signupUser(data)
}.onSuccess {
if (it.isSuccessful) _state.value = UiState.SUCCESS(null)
else {
Comment on lines +16 to 30

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 진짜 멋있다..이 언니

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import com.sopt.now.databinding.ActivitySignupBinding
import com.sopt.now.model.signup.RequestSignUpDto
import com.sopt.now.utils.UiState
import com.sopt.now.utils.showToast
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

@AndroidEntryPoint
class SignupActivity : AppCompatActivity() {
private lateinit var binding: ActivitySignupBinding
private val signUpViewModel by viewModels<SignUpViewModel>()
private val signUpViewModel: SignUpViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import com.sopt.now.databinding.ActivityMainBinding
import com.sopt.now.presentation.main.home.HomeFragment
import com.sopt.now.presentation.main.mypage.MyPageFragment
import com.sopt.now.presentation.main.search.SearchFragment
import dagger.hilt.android.AndroidEntryPoint


@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import com.sopt.now.databinding.FragmentMypageBinding
import com.sopt.now.model.info.UserInfo
import com.sopt.now.utils.UiState
import com.sopt.now.utils.showToast
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

@AndroidEntryPoint
class MyPageFragment : Fragment() {
private var _binding: FragmentMypageBinding? = null
private val binding get() = requireNotNull(_binding)
private val myPageViewModel by viewModels<MyPageViewModel>()
private val myPageViewModel: MyPageViewModel by viewModels()

override fun onCreateView(
inflater: LayoutInflater,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
package com.sopt.now.presentation.main.mypage

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sopt.now.model.info.UserInfo
import com.sopt.now.repository.MyPageRepository
import com.sopt.now.utils.NetworkUtil
import com.sopt.now.utils.ServicePool.infoService
import com.sopt.now.utils.UiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

class MyPageViewModel : ViewModel() {
@HiltViewModel
class MyPageViewModel @Inject constructor(
private val myPageRepository: MyPageRepository
) : ViewModel() {
private val _state = MutableStateFlow<UiState<UserInfo>>(UiState.LOADING)
val state get() = _state.asStateFlow()

fun getUserInfo() {
_state.value = UiState.LOADING
viewModelScope.launch(Dispatchers.IO) {
runCatching {
infoService.getUserInfo()
myPageRepository.getUserInfo()
}.onSuccess {
if (it.isSuccessful) {
_state.value = UiState.SUCCESS(
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/com/sopt/now/repository/AuthRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.sopt.now.repository

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repository역시 독립적으로 나와있을 친구가 아닌 것 같군요!
아래 안드로이드 앱 아키텍처 공식 문서를 제공 첨부해드릴테니 레이어분리를 하는 이유와 레이어 분리시 이점을 잘 알아가시면 앱잼에서도 도움이 많이 되실 것 같습니다.

https://developer.android.com/topic/architecture?hl=ko
P.S 클린 아키텍처는 완전 다른 친구입니다.


import com.sopt.now.model.login.RequestLoginDto
import com.sopt.now.model.signup.RequestSignUpDto
import com.sopt.now.utils.BaseResponse
import retrofit2.Response

interface AuthRepository {
suspend fun loginUser(data: RequestLoginDto): Response<BaseResponse<Unit>>

suspend fun signupUser(data: RequestSignUpDto): Response<BaseResponse<Unit>>
}
9 changes: 9 additions & 0 deletions app/src/main/java/com/sopt/now/repository/MyPageRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sopt.now.repository

import com.sopt.now.model.info.UserInfo
import com.sopt.now.utils.BaseResponse
import retrofit2.Response

interface MyPageRepository {
suspend fun getUserInfo(): Response<BaseResponse<UserInfo>>
}
22 changes: 16 additions & 6 deletions app/src/main/java/com/sopt/now/utils/ApiFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import com.sopt.now.BuildConfig
import com.sopt.now.datasource.AuthService
import com.sopt.now.datasource.InfoService
import com.sopt.now.utils.ApiFactory.create
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit

@Module
@InstallIn(SingletonComponent::class)
object ApiFactory {
private const val BASE_URL: String = BuildConfig.AUTH_BASE_URL

Comment on lines +19 to 23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApiFactory와 모듈이 utils 폴더 아래에 있는 것은 어색하다고 느껴지네요!
엄연히 data layer의 친구들을 다루니 data 아래로 옮기는 것도 좋을 것 같습니다.

Expand All @@ -30,12 +36,16 @@ object ApiFactory {
.build()
}

@Provides
fun provideAuthService(): AuthService {
return retrofit.create(AuthService::class.java)
}

@Provides
fun provideInfoService(): InfoService {
return retrofit.create(InfoService::class.java)
}

inline fun <reified T> create(): T =
retrofit.create(T::class.java) // create를 통해서 retrofit 구현체 생성
}
Comment on lines 49 to 51

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어 inline함수다 코인액에서 본..!! 역시 아는 만큼 보인다..


object ServicePool {
val authService: AuthService by lazy { create<AuthService>() }
val loginService: AuthService by lazy { create<AuthService>() }
val infoService: InfoService by lazy { create<InfoService>() }
}
26 changes: 26 additions & 0 deletions app/src/main/java/com/sopt/now/utils/AuthModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.sopt.now.utils

import com.sopt.now.data.AuthRepositoryImpl
import com.sopt.now.data.MyPageRepositoryImpl
import com.sopt.now.datasource.AuthService
import com.sopt.now.datasource.InfoService
import com.sopt.now.repository.AuthRepository
import com.sopt.now.repository.MyPageRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
object AuthModule {
@Provides
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Singleton

컴포넌트도 붙여주세요!
싱글톤을 사용해야 해당 객체가 어플리케이션에서 생성될때 하나만 생성되고 모든 곳에서 동일한 인스턴스를 사용하게 됩니다!
이로 인해 리로스를 절약할 수 잇어요

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 repository 인터페이서의 경우 provides보단 binds가 적합합니다!
image
https://developer.android.com/training/dependency-injection/hilt-android?hl=ko
https://hungseong.tistory.com/29

fun provideAuthRepository(authService: AuthService): AuthRepository {
return AuthRepositoryImpl(authService)
}

@Provides
fun provideMyPageRepository(infoService: InfoService): MyPageRepository {
return MyPageRepositoryImpl(infoService)
}
}
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ plugins {
id 'com.android.application' version '8.3.1' apply false
id 'com.android.library' version '8.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
id 'com.google.dagger.hilt.android' version '2.51.1' apply false
}