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] 4주차 과제 #8

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

[feat] 4주차 과제 #8

wants to merge 5 commits into from

Conversation

t1nm1ksun
Copy link
Contributor

@t1nm1ksun t1nm1ksun commented Nov 14, 2024

Related issue 🛠

Work Description ✏️

  • 유저 등록 API 연동
  • 유저 로그인 API 연동
  • 내 취미 조회 API 연동

Screenshot 📸

2024-11-14.8.32.01.mov

Uncompleted Tasks 😅

  • N/A

To Reviewers 📢

다들 합세 화이팅~!

@t1nm1ksun t1nm1ksun added the feat Feature label Nov 14, 2024
@t1nm1ksun t1nm1ksun requested a review from a team November 14, 2024 11:33
@t1nm1ksun t1nm1ksun self-assigned this Nov 14, 2024
@t1nm1ksun t1nm1ksun requested review from chattymin, tunaunnie, jihyunniiii and angryPodo and removed request for a team November 14, 2024 11:33
@t1nm1ksun t1nm1ksun changed the title Week4 [feat] 4주차 과제 Nov 14, 2024

Choose a reason for hiding this comment

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

저도 Gson사용으로 바꿔봐야 겠어요


return characterTypesCount >= 3
private fun isValidateHobby(hobby: String): Boolean {
return hobby.length in 1..8

Choose a reason for hiding this comment

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

저도 이렇게 코틀린스러움 다시 생각하겟습니다ㅠ

Choose a reason for hiding this comment

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

xml 무서워서 안건드렸는데 저도 스트링추출 적용해보겠습니다!!

Copy link

@angryPodo angryPodo left a comment

Choose a reason for hiding this comment

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

이번에도 많이 배우고 갑니다. 민석이 코드 없으면 과제를 못해잉..
파일관리에서 진짜 많이 배우고 저도 DTO분리 더 진행해보겟슴다. 오비 진짜 바쁠텐데 화이팅!!!!!

Choose a reason for hiding this comment

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

저는 Result에 해당하는 부분 data class를 아예 외부에 분리해서 짰었는데 뭔가 기능상에 차이점이 있나요?? 이 방식이 가독성 측면에선 훨씬 좋은 것 같아보이네요

Choose a reason for hiding this comment

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

이렇게 Result를 아예 밖에 분리하는 거랑 앞처럼 본문 안에 써주는거랑 차이점이 있는지 궁금합니다!!


interface UserService {
@POST("/user")
suspend fun postSignup(@Body requestDto: RequestSignUpDto): Response<ResponseSignUpDto>

Choose a reason for hiding this comment

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

응답 타입이 늘 ResponseSignUpDto로 설정되어 있는데, 응답이 실패한 경우에는 어떻게 FailedDTO를 반환할 수 있는 건가요?? 저도 성공/실패 DTO를 구분했었는데, 이 부분에 반환값을 늘 성공한 경우 DTO로 줘도 되는 건지 헷갈리더라구요 ..

val response = userService.getUserHobby(userInfoLocalDataSource.accessToken)
if (response.isSuccessful) {
Log.d("ㅋㅋ", "Status code: ${response.code()}")
response.body()?.result?.hobby ?: "내 취미 내놔!!"

Choose a reason for hiding this comment

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

🫢

}
} catch (e: Exception) {
Log.e("MyPageViewModel", "Exception while fetching hobby: ${e.localizedMessage}", e)
"에러났지렁이"

Choose a reason for hiding this comment

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

에러로그 이렇게 킹받게 써도 되는거였나요

Copy link
Contributor

Choose a reason for hiding this comment

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

아 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 개웃기다

_user.value.email = email
private fun loadUserData() {
viewModelScope.launch {
val nickname = userInfoLocalDataSource.nickname

Choose a reason for hiding this comment

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

마이페이지 Screen(뷰)쪽에서는 늘 마이페이지ViewModel에서 데이터를 가져오는거고, 마이페이지ViewModel 안에서는 늘 최신 데이터(?)를 이 userInfoLocalDataSource에서 가져오는 건가요?? (약간 전역처럼..) 저는 코드를 짜다보니까 UserViewModel이랑 MypageViewModel을 사용하는 게 점점 짬뽕이 돼서 어떻게 기능상의 분리를 해야 하는건지 헷갈리더라구요..! 이 userInfoLocalDataSoucre가, 각 페이지의 ViewModel이 유저 관련 제일 최신 데이터를 가져오는 최상위 뷰모델(?) 전역변수? 뷰모델의 뷰모델..? 느낌으로 이해해도 되는 건가요??ㅜ

Copy link
Contributor

Choose a reason for hiding this comment

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

userInfoLocalDataSource는 SharedPreferences 관련 로직들을 담고 있는 부분인데요! SharedPreferences 이 친구가 어떤 역할을 해주는 친구인지 찾아봅시다!

Copy link

@tunaunnie tunaunnie left a comment

Choose a reason for hiding this comment

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

세미나 결석으로 생긴 빵꾸 메우는 데 마치 모범코드처럼 민석님 코드 많이 참고하면서 배웠습니다ㅠ (파일 구조 만드는 것도 거의 따라하면서 체화한 것 같아요) 세미나 때도 늘 친절하게 알려주셔서 감사합니다! 과제 수행하시느라 고생하셨습니다~

Copy link

@chattymin chattymin left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!!
합세 리드 화이팅~!!

@@ -54,8 +53,8 @@ fun SignInScreen(

val snackBarHostState = remember { SnackbarHostState() }
val coroutine = rememberCoroutineScope()
val loginResult by signInViewModel.isLoginSuccessful.collectAsState()

Choose a reason for hiding this comment

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

collectAsState의 단점을 찾아보시고, 해결하는 방법을 적용해보시면 좋을 것 같습니다 :)

OB분이시니 직접적인 답은 드리지 않을거에요!
힌트는 LiveDataFlow의 차이점을 생각해보세용 ㅎㅎ

Comment on lines +134 to +144
LaunchedEffect(loginResult) {
loginResult?.let {
if (it) {
snackBarHostState.showSnackbar(message = context.getString(R.string.sign_in_success))
delay(300)
navigateToMyPage(userEmail.value)
} else {
snackBarHostState.showSnackbar(message = context.getString(R.string.sign_in_failed))
}
}
}

Choose a reason for hiding this comment

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

화면을 이동시키는 로직을 UI가 직접적으로 호출하고 있어요.
loginResult에 따라 ViewModel에 알려주고, event를 발생시켜 snackBar 호출 및 화면 이동을 하면 좋을 것 같아요.

Log.e("ㅋㅋ", "Status code: ${response.code()} and error code: $errorCode")
_isLoginSuccessful.value = false
}
} catch (e: Exception) {

Choose a reason for hiding this comment

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

try-catch 문법을 사용한다면 Exception을 명시해주는게 좋아요.
그래야 추후 디버깅을 할 때에도, 다른사람이 내 코드를 볼 때에도 왜 try-catch문을 사용했는지 알려줄 수 있어요

Comment on lines +20 to +21
private val _signUpResult = MutableStateFlow<Result<Unit>?>(null)
val signUpResult: StateFlow<Result<Unit>?> = _signUpResult

Choose a reason for hiding this comment

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

image
Effective Kotlin에서 나와있는 내용중 일부에요
Unit?을 리턴하는 것은 권장되지 않아요.

그리고 아래 사용법을 보니 상태를 저장한다기 보다는 event를 발생시키는 용도로 사용하고자 하시는 것 같은데 StateFlowSharedFlow의 사용법 차이를 찾아보시고 적용하면 좋을 것 같아요,.

Comment on lines +72 to +81
private fun isValidEmail(email: String): Boolean {
return email.length in 1..8
}

val characterTypesCount =
listOf(lowercase > 0, uppercase > 0, digit > 0, specialChar > 0).count { it }
private fun isValidPassword(password: String): Boolean {
return password.length in 1..8
}

return characterTypesCount >= 3
private fun isValidateHobby(hobby: String): Boolean {
return hobby.length in 1..8

Choose a reason for hiding this comment

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

유효한 길이라는 규칙은 유동적으로 수정될 수 있고, 이렇게 숫자로 되어있다면 추후 파악하기 힘들 수 있어요.
상수로 빼두고 호출해서 사용한다면 더욱 좋을 것 같네요!

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 +82 to +90
// Network
implementation(platform(libs.okhttp.bom))
implementation(libs.okhttp)
implementation(libs.okhttp.logging.interceptor)
implementation(libs.retrofit)
implementation(libs.retrofit.kotlin.serialization.converter)
implementation(libs.kotlinx.serialization.json)
implementation(libs.retrofit.v2110)
implementation(libs.converter.gson)
Copy link
Contributor

Choose a reason for hiding this comment

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

bundle을 사용해보셔도 좋을 것 같슴다 ~

Comment on lines +13 to +14


Copy link
Contributor

Choose a reason for hiding this comment

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

2줄 !

Comment on lines +5 to +6


Copy link
Contributor

Choose a reason for hiding this comment

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

2줄 !

Comment on lines +7 to +11
@Serializable
data class ResponseLoginDto(
@SerialName("result")
val result: Result
) {
Copy link
Contributor

Choose a reason for hiding this comment

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

해당 부분이 계속 공통되는 것 같은데 BaseResponse를 사용하시는 것도 방법이 될 것 같네요!

private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
Copy link
Contributor

Choose a reason for hiding this comment

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

왜 Gson을 사용하신 건지 여쭤봐두 되나염?

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import org.sopt.and.data.datalocal.datasource.UserInfoLocalDataSource
Copy link
Contributor

Choose a reason for hiding this comment

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

클린 아키텍처에서 의존성의 방향에 대해서 생각해봅시다! 어떻게 수정되어야 할까요?

}
} catch (e: Exception) {
Log.e("MyPageViewModel", "Exception while fetching hobby: ${e.localizedMessage}", e)
"에러났지렁이"
Copy link
Contributor

Choose a reason for hiding this comment

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

아 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 개웃기다

_user.value.email = email
private fun loadUserData() {
viewModelScope.launch {
val nickname = userInfoLocalDataSource.nickname
Copy link
Contributor

Choose a reason for hiding this comment

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

userInfoLocalDataSource는 SharedPreferences 관련 로직들을 담고 있는 부분인데요! SharedPreferences 이 친구가 어떤 역할을 해주는 친구인지 찾아봅시다!

) {

myPageViewModel.updateUserEmail(email = email)
val user by myPageViewModel.user.collectAsState()
Copy link
Contributor

Choose a reason for hiding this comment

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

collectAsState 말고 어떤 걸 쓰면 좋을까요?

Copy link
Contributor

Choose a reason for hiding this comment

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

ㅋㅋ

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

Successfully merging this pull request may close these issues.

[feat] 4주차 과제
5 participants