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

4주차 세미나 과제 #9

Merged
merged 17 commits into from
May 19, 2023
Merged

4주차 세미나 과제 #9

merged 17 commits into from
May 19, 2023

Conversation

b1urrrr
Copy link
Member

@b1urrrr b1urrrr commented May 12, 2023

⛳️ Work Description

Essential

  • (진짜) 회원가입 /로그인 기능 구현하기
  • Home 화면 꾸미기

Advanced

  • Kotlin Coroutines를 활용하여 서버통신

📸 Screenshot

Sign up

Android.Emulator.-.Galaxy_S21_API_31_5554.2023-05-13.01-52-18.mp4

Log in & Follower

Android.Emulator.-.Galaxy_S21_API_31_5554.2023-05-13.01-52-56.mp4

📢 To Reviewers

  • 이번에 특히 얼레벌레 구현한 부분이 많았는데 놓친 부분이나 개선할 부분이 있다면 알려주세요!

@b1urrrr b1urrrr self-assigned this May 12, 2023
@b1urrrr b1urrrr added essential 필수 과제 advanced 심화 과제 labels May 12, 2023
Copy link

@Dan2dani Dan2dani left a comment

Choose a reason for hiding this comment

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

채연 언니 코드는 항상 배울 점이 많아용

fun toFollower() = data.map { follower ->
Follower(
id = follower.id,
name = "${follower.firstName} ${follower.lastName}",

Choose a reason for hiding this comment

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

오 이렇게도 할 수 있구낭..!

import kotlinx.serialization.Serializable

@Serializable
data class BaseResponse<T>(

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.

와 이거 쇽샥해도 되나요 ? ㅋㅋ

Copy link

Choose a reason for hiding this comment

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

배웠습니다 ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

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

와 이거 쇽샥해도 되나요 ? ㅋㅋ

저희 다 오픈소스잖아요 🤭


fun toFollower() = data.map { follower ->
Follower(
id = follower.id,

Choose a reason for hiding this comment

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

DiffUtill에서 아이템을 비교할 때 혹시 명확한 식별을 위해서 id도 가져오는건가용? (뭔가 나의 말이 이상한데......)

Copy link
Member Author

Choose a reason for hiding this comment

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

areItemsTheSame 에서 id 값을 비교해주고 싶어서 가져왔습니다!

val followerList: List<Follower>?
get() = _followerList.value

private val _getFollowerListState = MutableLiveData<RemoteUiState>()

Choose a reason for hiding this comment

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

이거 좀 멍청 질문일 수도 있는데 UiState 의 장점은 뭔가영?!

Copy link
Member Author

Choose a reason for hiding this comment

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

UiState와 같은 클래스를 활용하면 ViewModel에서 수행한 결과(Success, Failure, Error, Loading 등등)에 따라 View에서 적절한 UI 처리를 할 수 있습니다! 효율적인 UI 처리가 가능하고 ViewModel과 View의 역할을 명확하게 구분할 수 있어서 좋은 것 같아요
NowInAndroid 프로젝트에서 이런 Ui 상태 처리를 잘 구현해둔 것 같은데 한번 참고해보셔도 좋을 것 같습니다 :)

Copy link
Member

@leeeha leeeha left a comment

Choose a reason for hiding this comment

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

코드 조금 더 자세히 살펴보고 리뷰 추가하겠습니다! 과제하느라 고생하셨어요!

@SerialName("message")
val message: String,
@SerialName("data")
val data: T? = null,
Copy link
Member

Choose a reason for hiding this comment

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

오오오 Response에서 data의 타입만 달라지니까 제네릭으로 만들 수 있군요! 한수 배워갑니다👍👍

suspend fun postSignIn(
@Body requestPostSignInDto: RequestPostSignInDto,
): BaseResponse<ResponsePostSignInDto>
}
Copy link
Member

Choose a reason for hiding this comment

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

오호 함수명이랑 Dto 클래스명에 어떤 HTTP 메서드를 사용하는지 명시해주니까 더 좋은 거 같아요!

@Module
@InstallIn(SingletonComponent::class)
object RetrofitModule {
private const val APPLICATION_JSON = "application/json"
Copy link
Member

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.

훨씬 깔끔하네유


@Provides
@Singleton
@Retrofit2(BaseUrlType.SOPT)
Copy link
Member

@leeeha leeeha May 14, 2023

Choose a reason for hiding this comment

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

오.. base url 타입을 어노테이션으로도 지정할 수 있네요!

Copy link

@amourxyoung amourxyoung left a comment

Choose a reason for hiding this comment

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

장인이시네요 👏 오늘도 배워갑니당 짱짱

import kotlinx.serialization.Serializable

@Serializable
data class BaseResponse<T>(

Choose a reason for hiding this comment

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

와 이거 쇽샥해도 되나요 ? ㅋㅋ

) : FollowerRepository {
override suspend fun getFollowerList(page: Int): Result<List<Follower>> =
runCatching {
followerDataSource.getFollowerList(page).toFollower()

Choose a reason for hiding this comment

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

오 처음부터 api url 에 page 숫자를 명시해준게 아닌, 인자로 받아서 여러 페이지 접근할 수 있도록 해주셨군요 짱이십니다 👍

Choose a reason for hiding this comment

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

22


override fun onDestroyView() {
super.onDestroyView()
followerAdapter = null

Choose a reason for hiding this comment

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

null 초기화 해준 후에 뷰를 소멸시키는게 메모리 누수 방지에 좋을 것 같습니당

Copy link
Member Author

Choose a reason for hiding this comment

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

onDestroyView는 뷰모로부터 뷰가 제거되기 전에 호출(Internally it is called after the view's state has been saved but before it has been removed from its parent.)되어서 뷰가 파괴된 후에 메모리가 누수되지 않도록 종속적인 자원을 해제하는 역할을 한다고 이해하고 공식문서와 같은 형식으로 작성해보았는데, super.onDestroyView()를 호출하기 전에 자원을 해제하지 않았을 때 메모리가 누수되는 경우가 있을까요?

.onFailure { t ->
if (t is HttpException) {
when (t.code()) {
CODE_INVALID_INPUT -> _signupState.value = Failure(CODE_INVALID_INPUT)

Choose a reason for hiding this comment

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

여러 경우에 대비해서 예외 처리해주신 부분 굉장히 섬세하시네요 👏

Copy link

@lsakee lsakee left a comment

Choose a reason for hiding this comment

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

좋은코드 잘 배워갑니다 ㅎㅎ State를 잘활용하시네요! 저도 배우겠습니다 ㅎ

import kotlinx.serialization.Serializable

@Serializable
data class BaseResponse<T>(
Copy link

Choose a reason for hiding this comment

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

배웠습니다 ㅎㅎ

Comment on lines +59 to +60
@Qualifier
annotation class Retrofit2(val baseUrlType: BaseUrlType)
Copy link

Choose a reason for hiding this comment

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

오 좋은 방법이네요!


_followerList.value = response
_getFollowerListState.value = Success
Timber.d("GET FOLLOWER LIST SUCCESS : $response")
Copy link

Choose a reason for hiding this comment

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

로그찍는 습관 좋네요 !

Copy link

@haeti-dev haeti-dev left a comment

Choose a reason for hiding this comment

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

눈물과 더불어 빵을 먹어 보지 않은 자는 인생의 참다운 맛을 모른다. -괴테

) : AuthRepository {
override suspend fun postSignup(requestPostSignUpDto: RequestPostSignUpDto): Result<ResponsePostSignUpDto?> =

Choose a reason for hiding this comment

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

Result 타입을 사용한 이유가 궁금합니다

Copy link
Member Author

Choose a reason for hiding this comment

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

좀 더 간결하게 이벤트를 처리하기 위해 runCatching을 활용해서 반환 타입을 Result로 지정해주었습니당

) : FollowerRepository {
override suspend fun getFollowerList(page: Int): Result<List<Follower>> =
runCatching {
followerDataSource.getFollowerList(page).toFollower()

Choose a reason for hiding this comment

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

22

@Module
@InstallIn(SingletonComponent::class)
object RetrofitModule {
private const val APPLICATION_JSON = "application/json"

Choose a reason for hiding this comment

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

훨씬 깔끔하네유

val pwd = MutableLiveData("")
val _id = MutableLiveData("")
private val id: String
get() = _id.value?.trim() ?: ""

Choose a reason for hiding this comment

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

센스 좋네요

Comment on lines +74 to +79
if (t is HttpException) {
when (t.code()) {
CODE_INVALID_INPUT -> _loginState.value = Failure(CODE_INVALID_INPUT)
else -> _loginState.value = Error
}
Timber.e("POST SIGNIN FAIL ${t.code()} : ${t.message()}")

Choose a reason for hiding this comment

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

고급지네요

Comment on lines +32 to +36
else -> throw ClassCastException(
parent.context.getString(
R.string.view_type_error_msg,
viewType,
),

Choose a reason for hiding this comment

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

섬세하네요

Comment on lines +45 to +47
override fun getItemCount(): Int {
return super.getItemCount() + HEADER_COUNT
}

Choose a reason for hiding this comment

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

새롭네요

private fun initRecyclerViewLayoutManager() {
val layoutManager = GridLayoutManager(activity, 2)
layoutManager.spanSizeLookup = object : SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {

Choose a reason for hiding this comment

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

이 함수가 뭐였죠 하하 기억이 안나네요

Copy link
Member Author

Choose a reason for hiding this comment

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

position에 해당하는 아이템이 gridLayout에서 몇 개의 폭을 차지하는지 반환해주는 함수입니다!
Header는 화면 너비를 꽉 채우고 싶어서 override 했습니다

@@ -4,14 +4,28 @@ import android.widget.ImageView
import androidx.databinding.BindingAdapter
import coil.load
import coil.transform.RoundedCornersTransformation
import org.android.go.sopt.R

object BindingAdapter {

Choose a reason for hiding this comment

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

object 로 생성하신 이유가 궁금합니따이

Copy link
Member Author

Choose a reason for hiding this comment

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

BindingAdapter 함수들은 프로젝트 전역에서 사용될 것이기 때문에 싱글톤 패턴으로 구현하는 것이 적합할 것 같아 object로 생성했습니다!

@b1urrrr b1urrrr merged commit f992303 into develop May 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
advanced 심화 과제 essential 필수 과제
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants