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

Week6 #10

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
Open

Week6 #10

wants to merge 11 commits into from

Conversation

sayyyho
Copy link
Contributor

@sayyyho sayyyho commented Dec 8, 2024

Related issue 🛠

Work Description ✏️

clean architecture 적용

📦org.sopt.and
├─📂data
│  ├─📂datasource
│  ├─📂mapper
│  ├─📂model
│  │  ├─📂request
│  │  └─📂response
│  ├─📂repositoryimpl
│  ├─📂service
├─📂domain
│  ├─📂model
│  ├─📂repository
│  ├─📂usecase
├─📂presentation
│  ├─📂components
│  ├─📂navigation
│  ├─📂home
│  ├─📂myinfo
│  ├─📂search
│  ├─📂signin
│  ├─📂signup
│  ├─📂util
│  ├─📂viewmodelfactory
└─📂ui.theme

To Reviewers 📢

제가 너무 늦게 제출해서 리뷰에 차질 생긴 점도 정말 죄송합니다.
또, 항상 꼼꼼하게 리뷰해주셔서 정말 감사드립니다.

늘 스스로 더 고민하지 못하고 다른 분들께 여쭤보고 또, 코드 보고 많이 도움받아서 겨우겨우 구현을 하네요ㅠㅠ
여전히 여러 개념들을 이해하지 못한 거 같아서 부족한 점도 다시금 느끼네요 😂

다들 바쁘실텐데 다른 핑계를 대기엔, 결국 제 공부 부족이 원인인 걸 잘 알고 있습니다ㅠㅠ
도움받았던 코드들과 코드 리뷰 피드백을 토대로 이번주는 열심히 공부하겠습니다.

다음엔 조금 더 스스로 해결하고 제대로 된 코드로 보여드릴 수 있도록 노력하겠습니다.
늘 귀한 시간 내주셔서, 다시 한 번 감사합니다 🥹

@sayyyho sayyyho self-assigned this Dec 8, 2024
Copy link

@hyoeunjoo hyoeunjoo left a comment

Choose a reason for hiding this comment

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

뭔가 대대적인 큰 변화가 있는데요??! 이번주 과제도 수고 많으셨습니다

Comment on lines +14 to +36

object Mapper {
fun toMyHobbyEntity(getHobbyResponseResultDto: MyHobbyResponseResultDto) =
MyHobbyEntity(myHobby = getHobbyResponseResultDto.myHobby)

fun toSignUpResponseEntity(signUpResponseDto: Response<SignUpResponseDto>) =
signUpResponseDto.body()?.result?.let {
SignUpResponseEntity(
no = it.no,
status = signUpResponseDto.code(),
code = signUpResponseDto.body()!!.code
)
}

fun toSignInResponseEntity(signInResponseDto: Response<SignInResponseDto>) =
signInResponseDto.body()?.result?.let {
SignInResponseEntity(
token = it.token,
status = signInResponseDto.code(),
code = signInResponseDto.body()!!.code
)
}

Choose a reason for hiding this comment

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

매퍼 object에 모든 매퍼를 넣어놓으신 이유가 있을까요?? 분리해주셔도 좋을 것 같습니다!

Copy link
Contributor

Choose a reason for hiding this comment

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

저도 궁금합니다 ~

Comment on lines +6 to +10
@Serializable
data class MyHobbyResponseDto(
val result: MyHobbyResponseResultDto? = null,
val code: String? = null
)

Choose a reason for hiding this comment

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

Dto는 명세서에서 Response Body나 Request Body값을 보고 만들면 됩니다!

@Serializable
data class MyHobbyResponseResultDto(
    @SerialName("hobby")
    val myHobby: String
)

아래 적어주신 이 부분만을 Dto로 남겨주시고 지금 result, code는 따로 BaseResponse형태를 만들어주시는게 더 나아보입니다!

Comment on lines +11 to +14
override suspend fun getMyHobby(): Result<MyHobbyEntity> =
runCatching {
getMyHobbyDataSource.getMyHobby().result?.let { Mapper.toMyHobbyEntity(it) }!!
}

Choose a reason for hiding this comment

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

!!은 이왕이면 사용하지 않는 것이 좋습니다 ㅎㅎ NPE를 불러올 수 있기 때문이죠
데이터가 없을 경우 명시적으로 처리하도록 수정하는 것이 좋아보입니다

Comment on lines +13 to +14
interface UserService {
suspend fun signUp(@Body request: SignUpRequestDto): Response<SignUpResponseDto>

Choose a reason for hiding this comment

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

여기는 왜 Post 어노테이션이 없나요??

Comment on lines +2 to +7

data class SignInResponseEntity(
val token: String? = null,
val status: Int? = null,
val code: String? = null
)

Choose a reason for hiding this comment

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

domain에 있는 Entity는 무엇일까요? 어떤 역할을 하는걸까요??

Comment on lines +64 to +78
if (signInResponseEntity.status == 200) {
_signInResult.value = SignInResult.Success
signInResponseEntity.token?.let { token ->
tokenManager.saveToken(token)
}
} else if (signInResponseEntity.code == SignInFailureCase.FAILURE_LENGTH.errorCode
&& signInResponseEntity.status == SignInFailureCase.FAILURE_LENGTH.statusCode
) {
_signInResult.value = SignInResult.FailurePasswordLength
} else if (signInResponseEntity.code == SignInFailureCase.FAILURE_WRONG_PASSWORD.errorCode
&& signInResponseEntity.status == SignInFailureCase.FAILURE_WRONG_PASSWORD.statusCode
) {
_signInResult.value = SignInResult.FailureWrongPassword
}
}

Choose a reason for hiding this comment

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

fun signUp(request: RequestSignUpDto) {
    viewModelScope.launch {
        runCatching {
            authService.signUp(request)
        }.onSuccess {
            // 성공 시 로직
        }.onFailure {
            // 실패 시 로직
        }
    }
}

else if문을 쓰기보단 이렇게 쓰는게 더 가독성 좋을 것 같다는 생각이 들어요
세미나 코드 발췌

Comment on lines +38 to +54

fun Context.showToast(
@StringRes message: Int
) = Toast.makeText(
this,
this.getString(message),
Toast.LENGTH_SHORT
).show()

fun Context.showSnackbar(
scope: CoroutineScope,
snackbarHostState: SnackbarHostState,
@StringRes message: Int
) = scope.launch {
snackbarHostState.showSnackbar(message = getString(message))
}
}

Choose a reason for hiding this comment

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

이런건 따로 확장함수 폴더로 빼주시면 나중에 비슷한 확장함수 추가할때도 더 빠르고 쉽게 찾을 수 있을 것 같아용

Comment on lines +8 to +24

class SignUpViewModelFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return when (modelClass) {

SignUpViewModel::class.java -> {
SignUpViewModel(
SignUpUseCase(
signUpRepository = SignUpRepository.create()
)
) as T
}

else -> throw IllegalArgumentException("Unknown ViewModel Class")
}
}
}

Choose a reason for hiding this comment

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

ViewModelProvider.Factory를 상속받는 Factory들은 각 뷰모델 별로 만들 필요는 없습니다!
하나의 ViewModel Class 안에 if else 혹은 when문을 통해 각 뷰모델과 repository 별로 분기처리 하시면 됩니다!

Copy link

@imtaejugkim imtaejugkim left a comment

Choose a reason for hiding this comment

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

정말 많은 노력의 흔적들이 보입니다...! 데이터, 도메인, 프레젠테이션 각각 생각하시면서 코드를 구성 하신게 보입니다. 클린 아키텍쳐를 적용시키는게 저를 포함해서 아마 많이 어려우셨을 거라 생각해요.. 어려웠던 만큼, 더욱 도움이 됐던 주차였길 바랍니다!

private val getMyHobbyDataSource: MyHobbyDataSource
) : MyHobbyRepository {
override suspend fun getMyHobby(): Result<MyHobbyEntity> =
runCatching {

Choose a reason for hiding this comment

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

runCatching 굿입니다!

Comment on lines +11 to +32
val Context.dataStore by preferencesDataStore(DATASTORE_NAME)

class TokenManager(private val context: Context) {
private val TOKEN_KEY = stringPreferencesKey(TOKEN_NAME)

fun getToken(): Flow<String?> {
return context.dataStore.data.map { preferences ->
preferences[TOKEN_KEY]
}
}

suspend fun saveToken(token: String) {
context.dataStore.edit { preferences ->
preferences[TOKEN_KEY] = token
}
}

companion object {
const val DATASTORE_NAME = "token"
const val TOKEN_NAME = "auth_token"
}
}

Choose a reason for hiding this comment

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

오오 data store을 사용하셨군요!
shared Preference와 다르게 Data Store가 요즘 더 권장? 추천? 하는 방식이라고 얼핏 들은것 같습니다!
둘의 어떤 장단점의 차이가 있나요??

Comment on lines +9 to +24
class MyInfoViewModelFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return when (modelClass) {

MyInfoViewModel::class.java -> {
MyInfoViewModel(
MyHobbyUseCase(
getMyHobbyRepository = MyHobbyRepository.create()
)
) as T
}

else -> throw IllegalArgumentException("Unknown ViewModel Class")
}
}
}

Choose a reason for hiding this comment

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

hilt를 안쓸 때의 viewModel 주입 방법인가요?

Choose a reason for hiding this comment

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

저도 이번 피드백 내용이었는데.. domain단에는 response와 request와 같은, 서버에서 사용한 dto 형식의 네이밍은 좋지 않다고 들었어요!

Copy link
Contributor

@jihyunniiii jihyunniiii left a comment

Choose a reason for hiding this comment

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

고생하셨습니다 ~

Comment on lines +14 to +36

object Mapper {
fun toMyHobbyEntity(getHobbyResponseResultDto: MyHobbyResponseResultDto) =
MyHobbyEntity(myHobby = getHobbyResponseResultDto.myHobby)

fun toSignUpResponseEntity(signUpResponseDto: Response<SignUpResponseDto>) =
signUpResponseDto.body()?.result?.let {
SignUpResponseEntity(
no = it.no,
status = signUpResponseDto.code(),
code = signUpResponseDto.body()!!.code
)
}

fun toSignInResponseEntity(signInResponseDto: Response<SignInResponseDto>) =
signInResponseDto.body()?.result?.let {
SignInResponseEntity(
token = it.token,
status = signInResponseDto.code(),
code = signInResponseDto.body()!!.code
)
}

Copy link
Contributor

Choose a reason for hiding this comment

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

저도 궁금합니다 ~

SignUpResponseEntity(
no = it.no,
status = signUpResponseDto.code(),
code = signUpResponseDto.body()!!.code
Copy link
Contributor

Choose a reason for hiding this comment

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

!!는 NPE가 발생할 수 있으니 사용을 지양하시는 것이 좋습니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

data layer에 위치시키신 이유가 있나요?

Comment on lines +3 to +7
import org.sopt.and.data.datasource.SignInDataSource
import org.sopt.and.data.repositoryimpl.SignInRepositoryImpl
import org.sopt.and.data.service.ServicePool
import org.sopt.and.domain.model.SignInInformationEntity
import org.sopt.and.domain.model.SignInResponseEntity
Copy link
Contributor

Choose a reason for hiding this comment

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

domain은 어떠한 외부 레이어와도 의존성이 있으면 안 됩니다.
수정해보시면 좋을 것 같네요

Comment on lines +16 to +17
private val _uiState = MutableStateFlow(MyInfoUiState())
val uiState: StateFlow<MyInfoUiState> = _uiState.asStateFlow()
Copy link
Contributor

Choose a reason for hiding this comment

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

StateFlow와 SharedFlow의 차이는 무엇일까요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Week6] 6주차 과제 구현
4 participants