-
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
[4주차 과제] 서버 통신 기초 #9
Conversation
- MultiViewHolderFactory의 getViewHolder에서 MultiViewHolder 객체를 리턴할 때, `as MultiViewHolder<MultiViewItem>`로 강제 캐스팅을 해야 했음. (그 이유는 무변성 때문에) - Invariance: 형식 매개변수끼리는 하위 타입 관계를 만족시키더라도, 제네릭을 사용하는 클래스나 인터페이스는 하위 타입 관계가 유지되지 않는 것 - 그래서 MultiViewHolder의 형식 매개변수를 제거하면 하위 타입(HearderViewHolder, RepoViewHolder)에서 상위 타입(MultiViewHolder)으로 자동 캐스팅 되어 경고가 발생하지 않음. - MultiViewHolder의 형식 매개변수를 제거한 대신에, 어댑터의 onBindViewHolder에서 holder의 타입을 구분하여 bind 함수를 호출하도록 변경함.
- CoordinatorLayout, AppBarLayout, Toolbar, RecyclerView - 리사이클러뷰에 `appbar_scrolling_view_behavior` 속성을 설정하면, 스크롤 이벤트가 발생할 때마다 AppBarLayout에게 알림이 간다. (AppBarLayout 안에 있는 뷰들은 `layout_scrollFlags`에 설정된 플래그에 따라 동작한다.)
- 회원가입 화면에서 받은 인텐트가 아니라, prefs에 저장된 id, pw와 입력값을 비교하도록 수정 - 서버에 로그인 정보 등록 - prefs에 현재 로그인 상태를 true로 저장 (자동 로그인에 활용) - 마이페이지에는 prefs에 저장된 데이터 표시
- prefs의 모든 데이터를 삭제하지 않고, 로그인 상태를 나타내는 Boolean 값만 조정 - 회원가입 다시 하지 않아도 prefs에 저장된 데이터와 입력값 비교하여 바로 재로그인 가능함.
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.
이번 주도 과제하시느라 고생하셨습니다 👍👍
코드가 깔끔해서 읽기 너무 좋네요 🤭
class GoSoptApplication : Application() { | ||
override fun onCreate() { | ||
super.onCreate() | ||
prefs = PreferenceUtil(applicationContext) | ||
|
||
initTimber() | ||
initPrefsManager() | ||
} |
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.
함수화 너무 깔끔하네요 🤩
app/build.gradle
Outdated
// retrofit | ||
implementation 'com.squareup.retrofit2:retrofit:2.9.0' | ||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1' | ||
implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0' | ||
|
||
// define a BOM and its version | ||
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.10.0")) | ||
|
||
// define any required OkHttp artifacts without version | ||
implementation("com.squareup.okhttp3:okhttp") | ||
implementation("com.squareup.okhttp3:logging-interceptor") |
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.
implementation 할 때 Kotlin 스크립트(KTS)와 Groovy 문법이 섞여있는데 통일 시켜주시면 좋을 것 같습니다 :)
공식문서에 따르면 Groovy보다 KTS로 gradle을 작성하는 것이 선호된다고 합니다.
val name: String, | ||
@SerialName("full_name") | ||
val fullName: String, | ||
val `private`: Boolean, |
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은 소프트 키워드가 아닌 키워드(예약어)에 해당하기 때문에 @SerialName
을 통해 다른 이름으로 바꿔주는 것이 헷갈리지 않아 가독성과 유지보수성이 좋아질 것 같습니다 :)
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.
허걱 플러그인으로 자동 생성했는데 이렇게 변환했는지 몰랐네요! 자동 생성한 코드도 유심히 다시 살펴보겠습니다!
class GalleryFragment : BindingFragment<FragmentGalleryBinding>(R.layout.fragment_gallery) { | ||
private val listAdapter by lazy { MyListAdapter() } | ||
private val followerAdapter by lazy { FollowerAdapter() } |
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.
Adapter 변수도 GalleryFragment의 뷰에 종속된 리소스이기 때문에 메모리 누수를 예방하기 위해 onDestroyView()
에서 해제시켜주는 것이 좋을 것 같습니다!
private fun checkDuplicateId() { | ||
val userId = binding.etId.text.toString() | ||
AuthFactory.ServicePool.authService.getUserInfo(userId) | ||
.enqueue(object : retrofit2.Callback<ResLoginDto> { | ||
override fun onResponse(call: Call<ResLoginDto>, response: Response<ResLoginDto>) { | ||
// 이미 등록된 유저인 경우 | ||
if (response.isSuccessful) { | ||
response.body()?.let { | ||
Timber.d(it.status.toString()) | ||
Timber.d(it.message) | ||
Timber.d(it.data.id) | ||
Timber.d(it.data.name) | ||
Timber.d(it.data.skill) | ||
} | ||
showSnackbar(binding.root, getString(R.string.id_duplicate_error_msg)) | ||
return | ||
} | ||
|
||
// 새로운 유저 등록 | ||
registerNewUser() | ||
} |
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.
sign-up api의 요청으로 중복된 정보를 입력할 경우 status code 409
를 반환하고 있기 때문에 이에 대한 예외 처리를 하고 유저 정보 조회는 통신을 하지 않는 것이 서버 통신 횟수를 줄일 수 있어 효율적일 것 같습니다!
fun putUserData(user: User) { | ||
val json = Gson().toJson(user) | ||
putString(Intent.EXTRA_USER, json) | ||
} | ||
|
||
fun deleteAllData() { | ||
sharedPreferences.edit().clear().apply() | ||
fun getUserData(): User? { | ||
val json = getString(Intent.EXTRA_USER, "") | ||
return Gson().fromJson(json, User::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.
json 문자열로 객체를 저장하고 반환하도록 구현까지 하셨네요 크 👍👍
object BindingAdapter { | ||
@JvmStatic | ||
@BindingAdapter("app:imgUrl", "app:placeholder", "app:error") | ||
fun ImageView.loadImage( | ||
imgUrl: String, | ||
loadingImg: Drawable, | ||
errorImg: Drawable | ||
) { | ||
Glide.with(this.context) | ||
.load(imgUrl) | ||
.placeholder(loadingImg) | ||
.error(errorImg) | ||
.apply(RequestOptions().fitCenter()) | ||
.into(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.
BindingAdapter 활용 너무 좋습니다 👍
코드랩에서 활용하고 있는 Coil 라이브러리도 한번 찾아보시면 좋을 것 같습니다!
- 기존 방식: 서버에서 GET 요청으로 유저 정보 조회 성공하면 중복 id로 판단 - 바꾼 방식: 서버에 POST로 회원가입 요청을 보냈는데 409 에러가 뜨면 중복 id로 판단하여 에러 스낵바 띄우기 - 회원가입에서 로그인으로 다시 돌아올 때 회원가입 성공 스낵바 띄우도록 변경
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.
이번주도고생하셨습니다 💟 합세 화이팅
} | ||
|
||
companion object { | ||
private const val FILE_MOCK_REPO_LIST = "mock_repo_list.json" |
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.
json 파일명까지 상수화 꼼꼼하십니다 👍
} | ||
|
||
private fun compareInputWithPrefs(): Boolean { | ||
val id = binding.etId.text.toString() |
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.
User라는 데이터 클래스가 있으니 binding.~~.text.toString() 을 반복하는 것 보다는 savedUser처럼 User형을 하나 더 만들어서 재사용하는건 어떨까요 ?
} | ||
|
||
private fun initSignUpButtonClickListener() { | ||
val signUpResultLauncher = registerForActivityResult( | ||
ActivityResultContracts.StartActivityForResult() | ||
) { result -> | ||
if (result.resultCode == RESULT_OK) { | ||
handleSignUpResult(result) | ||
showSnackbar(binding.root, getString(R.string.sign_up_success_msg)) | ||
} | ||
} | ||
|
||
binding.btnSignUp.setOnClickListener { |
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 true | ||
} | ||
|
||
if (response.code() == CODE_DUPLICATE_ID) { |
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.
아이디 중복확인까지 👍
<string name="sign_up_success_msg">회원가입이 완료되었습니다.</string> | ||
<string name="login_success_msg">로그인에 성공했습니다.</string> | ||
<string name="login_fail_msg">로그인에 실패했습니다.</string> | ||
<string name="sign_up_invalid_input_err">모든 항목에 유효한 값을 입력해주세요.</string> | ||
<string name="invalid_input_error">모든 항목에 유효한 값을 입력해주세요.</string> | ||
|
||
<!-- login --> |
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.
상황별 꼼꼼한 처리 멋집니다
필수 과제
심화 과제
도전 과제
실행 결과
Screen_Recording_20230513_012507_GO.SOPT.Android.mp4
리팩토링 Todo