Skip to content

✒ [AOS] 개발일지

saeyoung Oh edited this page Dec 2, 2023 · 11 revisions

기술적 고민

분류 정리
api 응답 처리
Navigation Navigation 적용과 고민
화면 이동 어플 전반 화면 이동의 흐름
위치 권한 위치 권한 허용 여부에 따른 기능의 동작
지도 마커 보여주기 필터를 변경할 때 지도 화면을 어떻게 이동시킬까?
DataStore 왜 DataStore 인가?
Flow Flow vs. LiveData



간단한 논의

2023-11-13

주요 안건

  • 디자인 수정
  • 초기 세팅
  • 컨벤션 추가

회의 내용

  • git branch 관련 [] (괄호 부분에 대한 인식이 되지 않음)

    • 문제가 있어서 깃 branch관련 네이밍 수정

    • **feature/[aos/be]-기능명 를 feature/aos-기능명으로 수정 → 서버도 동일시 하게 적용**

  • 안드로이드 초기 셋팅

    • 사용할 기술(dependencies) 적용, 아키텍처 ,single vs multi 모듈에 대한 선택, 패턴 적용
  • 네이밍 논의

    • 권한요청, constant, 상수 끼리 비슷한 성질끼리 네이밍,

    • 공용함수 확장함수를 쓸때는 맛집 상세같은 global action같은것은 ui 하단에 파일로 만들어서 진행할지에 대한 고민

    • extension이랑 util의 분리?

    • uiState → 해당 패키지 내에서?

    • viewModel 의uiState , event | effect?

2023-11-14

멘토링 내용 정리

  • 마지막에는 기술에 대한 답변을 할 수 있어야한다. (최종 목표가 되어야 할 듯)
    • Glide 와 비교되는 라이브러리 몇개 있는데, 이것도 비교해서 왜 썼는지 질문 받았을 때 당황하지 않고 말할 수 있어야 한다.
    • Viewmodel factory 사용해서 주입하고 -> hilt 를 적용
  • Navigation 일단 써보자. 쓰다가 중간에 activitiy 로 바꾸고 싶을 수도 있다.
  • 프로젝트에 어떤 매력이 있나요?
    • Navigation 관련한 스토리텔링하면 매력적일것이다~
    • 모듈화도 매력일 수 있음
    • 배포를 위해 위치 정보 및 개인정보 약관을 확인하고 출시하는 과정을 고민하는 것 자체는 매력적임. 하지만 기간 내에는 조금 어려울 수 있음.
  • 수정 후의 feature-list 는 현실적인지?
    • 기능 시원하게 날린 것은 잘 했다고 생각함.
    • MVP 를 4주차로 잡아서 조금 빠듯할 수는 있음.
    • 기능을 구현할 때 협업 방식은 수직과 수평이 있는데 장단이 있으니 잘 선택해서~
      • 수직은 domain, server, data, viewmodel, ui 작업 단위로 쪼갤 수 있는데 이를 어떻게 분배하냐가 수직의 관점 (하나의 기능에 여러 명이 붙어서)
      • 수평은 하나의 기능은 한명이 맡는 것
2023-11-15

기술적 고민과 해결

  • Navigation 사용시, 뒤로가기를 어떻게 처리하는게 좋을까?

    • navigateUp() vs popBackStack()

    • 해결

      • Navigation 적용과 고민 링크에서 확인 가능
  • flow 로 ui event 관리 + navigation 사용

    • navigationUp 으로 뒤로가기를 처리했을 때 처음에는 한번, 두번째에는 두번, 세번째에는 세번 클릭해야 뒤로가기가 실행되는 문제

    • 해결

      • sharedFlow 옵션
        • replay : 새로운 수신자(subscriber)가 등록될 때 이전 이벤트를 재생하는지 여부. 즉 최신의 이벤트만 처리하고 싶으면 값을 0으로 주어야 함.
        • extraBufferCapacity : buffer 개수 정하는 옵션. flow의 emit이 빠르고 collect가 느릴 때 지정된 갯수만큼 buffer에 저장되며 지정된 갯수가 넘어가면 onBufferOverflow 정책에 따라 동작.
        • onBufferOverflow : buffer 가 다 찼을 경우 정책.
        • ex) replay 를 1로 설정하고 back 버튼을 누르니까 back & (이전 이벤트) goToEdit 이 함께 눌러지면서 back 이 동작이 실행 안됨. replay = 0 으로 설정하니 back 만 잡힘.
  • 현업 개발자들은 Merge Conflict 관리를 어떻게 하는지 궁금. github 에서 충돌 병합을 하진 않을거 같은데..

    • 멘토링 시간에 조언을 들음.
2023-11-16

기술적 고민과 해결

  • Duplicate class android.support … 관련 에러 발생

  • Failed resolution of: Lcom/google/android/gms/location/LocationRequest 에러 발생

    • naverMap 현위치 트래킹을 위해, "com.google.android.gms:play-services-location:16.0.0" 를 썼었는데, 버전을 21.0.1 로 변경 후 해결
  • AutoCompleteTextView 에 처음부터 값을 설정하니 해당 지역만 drop-down 에 표시됨

    • ex. 용산구를 설정하고 drop-down 하니 용산구만 표시됨
  • 활동지역 설정 Spinner 로 변경 후 해결

2023-11-20

안건

  • Navigation action 처리 방식 통일 논의
  • Api, Repository 파일 분리 단위 논의
  • Repository 에러 헨들링 방식 논의

논의 내용

  • naming

    • → response data class : 기능 + Response

    • → api 관련 메소드 : method 빼고 동사 + 명사 or 명사 + 명사 ex) loginNaver

    • api - repository - repositoryImpl - viewModel 전부 다 같은 함수명

  • Api, Repository 파일 분리 단위 논의

    • 화면으로만 쓰일 것은 화면으로만 API
      • Intro 는 하나로 관리
    • 중복으로 쓰일 것들은 기능 API
      • validation (email, nickname)
      • refreshToken 발급
  • 함수 네이밍은 동사 + 명사 / 명사 + 명사 / 동사 + 명사 + 명사 의 형식을 유지

멘토링

  • 캐시 한번 찾아보기
    • 메모리에 저장하거나 로컬에 저장하거나
  • api 호출을 최적화 하는 방안을 생각해보기 (근거를 만들어서 결론지어보기)
  • 클러스터링
    • 고민이 필요할 수도
    • 많은 부분에 마커가 찍혀있다면 하나로 처리해주는 것 생각해보기
    • 삽질기록 경험으로 남기면 됨
  • 패키지 컨벤션 찾아보기
  • 위치 권한과 관련해서는 시각화 자료 만들면 좋을 듯
  • network error 는 error 대로, 자식 viewmodel 에서 error 는 error 대로 나면서 나뉠 수 있음
    • 중복코드 해결
    • 있는것 자체는 괜찮음
2023-11-22

논의 내용

  • Api 응답 처리 로직 고민

    • BaseResponse generic 타입 data class 로 구현

    • ApiState → BaseState 로 변경하고

    • runNNApi → runRemote / runLocal 로 BaseState 반환하도록 수정

  • 토큰 관련

    • accesstoken 을 datastore 에서 비우는 경우 → 로그아웃, 회원탈퇴, refreshtoken 만료

    • 네이버 로그인 토큰을 헤더에 넣을 때는 직접 넣어주기

    • interceptor 로 구현하는건 access token 헤더에 넣는 경우에만 사용하기

2023-11-23

기술적 고민

  • Home Filter RecyclerView 클릭 이벤트 이슈

    • item 을 databinding 으로 주입시켜서, text 를 set 하면 잔상이 남음
class HomeFilterViewHolder(
    private val binding: ItemHomeFilterBinding
): RecyclerView.ViewHolder(binding.root){

    fun bind(item: UiFilterData){
        binding.item = item
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="item"
            type="com.avengers.nibobnebob.presentation.ui.main.home.model.UiFilterData" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="12dp">

        <TextView
            android:id="@+id/tv_filter"
            style="@style/TextMediumBold"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/rect_primary3fill_nostroke_30radius"
            android:onClick="@{() -> item.onItemClicked.invoke(item.name)}"
            android:paddingHorizontal="12dp"
            android:paddingVertical="4dp"
            android:text="@{item.name}" 
            android:textColor="@color/white"
            app:filterBackground="@{item.isSelected}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
  • ViewHolder 내부에서 처리하게 되면, 잔상이 남지 않음
class HomeFilterViewHolder(
    private val binding: ItemHomeFilterBinding
): RecyclerView.ViewHolder(binding.root){

    fun bind(item: UiFilterData){
        binding.item = item
				binding.tvFilter.text = item.name
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="item"
            type="com.avengers.nibobnebob.presentation.ui.main.home.model.UiFilterData" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="12dp">

        <TextView
            android:id="@+id/tv_filter"
            style="@style/TextMediumBold"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/rect_primary3fill_nostroke_30radius"
            android:onClick="@{() -> item.onItemClicked.invoke(item.name)}"
            android:paddingHorizontal="12dp"
            android:paddingVertical="4dp"
            android:textColor="@color/white"
            app:filterBackground="@{item.isSelected}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

💡 어떤 차이로 발생하는지 아직 파악 하지 못함.. 잔상을 없애기 위해, ViewHolder 내부 코드로 commit 예정
  • Home Filter 클릭시, 깜빡임 현상

    • 클릭 이벤트를 HomeViewModel 에서 함수로 정의해, 함수 레퍼런스로 Item에 전달하고 있는중
    • HomeFilterAdapter 에서 DiffUtil로 아이템 내용 바뀌었을때 감지하도록 설정 하였으나, 클릭후 backGround 바뀔때 사알짝 깜빡임 현상 발생
2023-11-25

기술적 고민과 해결

  • 로그아웃 / 회원탈퇴 후 기존 activity 모두 종료하고 IntroActivity 나타내기
    • 기존 코드

      (*activity* as MainActivity).finish()
      val intent = Intent(context, IntroActivity::class.java)
                              startActivity(intent)
    • 리뷰 참고하여 Intent Flag 설정

      • FLAG_ACTIVITY_NEW_TASK : 기존 activity 스택이 아닌 새로운 스택에 activity 가 추가됨.
      • FLAG_ACTIVITY_CLEAR_TASK : FLAG_ACTIVITY_NEW_TASK 와 함께 사용해야 함. 다른 activity 모두 종료시킴.
      val intent = Intent(context, IntroActivity::class.java)
      intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
      startActivity(intent)
2023-11-28

페어프로그래밍 안건

  • 뒤로가기 두번 눌러서 앱 종료 → o
  • bottomsheet 내리는 시점
  • 제일 처음 지도가 표시되는 위치 지금 설정 되어있는건지? → 찾아보기 (시청역으로 되어있음)
  • 검색 marker 도 marker 리스트에 추가해야하는가? → 검색 bottom sheet 올라오고 난 후에 그게 내려가고 이후 동작을 정해야할듯
  • 마이페이지 수정, 맛집 추가 등 post 보내고 난 후에 유저에게 어떻게 메세지 보여줄 건지 정하기
  • 통신 에러 시 어떻게 메세지 제공할 것인지
  • GlobalRepository / Global 네이밍
  • global 패키지 main 하위에 두기
  • 마커 이미지 바꾸기

기술적 고민

  • Adapter 에 Item 안에 ClickListener 를 주입하는데, 어떻게 주입하는게 바람직할까

    • 팔로우 버튼을 클릭했을때의 동작을 onBtnClickListener로 넣어주려고 한다
    • 팔로잉 상태라면, 팔로우 취소 API 요청을 해야하고, 논팔로잉 상태라면 팔로우 API 호출을 해야한다
    • 이럴떄 다음과같은 고민이 생김
data class UiFollowData(
    val nickName: String = "",
    val region: String = "",
    val isFollowing: Boolean = true,
    val onBtnClickListener: (String, String) -> Unit,
    val onRootClickListener: (String) -> Unit
)
  • 함수 네이밍을, Adapter 입장에서 함수의 동작을 알고있다는 가정을 해야하는가?

  • item 의 상태값에 따라, 다른 동작을하는 두 함수를 호출해야 될때, 한개의 함수에 두개의 인자를 받을것인가? 아니면 두개의 함수를 받을 것인가?

2023-12-01

마스터 클래스 리뷰

  • Interceptor 부분에서 책임 분리에 대해서 신경 써주기

    • Bearer에서 listener를 해주는건 어떤지?
  • App부분에서 Hilt를 제대로 쓴건가? 라는 생각이 바로 들것 같다는 의견이심

companion object{
        lateinit var instance : App
        val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = APP_NAME)
        fun getContext(): Context = instance.applicationContext
    }
  • 다 이런식으로 동일하게 repository에서 진행하고 있는데 줄이기
Flow<BaseState<BaseResponse<List<FollowListResponse>>>>
data class BaseStateResponse<T>(val baseState: BaseState, val baseResponse: BaseResponse<T>)

typealias FollowListFlow<T> = Flow<BaseStateResponse<List<T>>>

FollowListFlow<FollowListResponse>
Clone this wiki locally