-
Notifications
You must be signed in to change notification settings - Fork 0
✒ [AOS] 개발일지
분류 | 정리 |
---|---|
Navigation | Navigation 적용과 고민 |
화면 이동 | 어플 전반 화면 이동의 흐름 |
위치 권한 | 위치 권한 허용 여부에 따른 기능의 동작 |
지도 마커 보여주기 | 필터를 변경할 때 지도 화면을 어떻게 이동시킬까? |
DataStore | 왜 DataStore 인가? |
지도와 마커 | --- |
계층분리 | Clean Architecture 도입 이유 |
Glide | Glide 를 선택한 이유 |
MVVM 아키텍쳐 | MVVM vs. MVP vs. MVC |
네트워크 통신 | Retrofit 과 Okhttp |
비동기 | Coroutine |
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 만 잡힘.
-
- sharedFlow 옵션
-
-
현업 개발자들은 Merge Conflict 관리를 어떻게 하는지 궁금. github 에서 충돌 병합을 하진 않을거 같은데..
- 멘토링 시간에 조언을 들음.
2023-11-16
기술적 고민과 해결
-
Duplicate class android.support … 관련 에러 발생
-
해결
-
android.enableJetifier=true
를 gradle.properties에 추가하니까 해결됨 -
앱 내에서 사용된 써드파티 라이브러리와 AndroidX 와의 충돌인걸로 추정됨
-
-
Failed resolution of: Lcom/google/android/gms/location/LocationRequest 에러 발생
- naverMap 현위치 트래킹을 위해,
"com.google.android.gms:play-services-location:16.0.0"
를 썼었는데, 버전을 21.0.1 로 변경 후 해결
- naverMap 현위치 트래킹을 위해,
-
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
-
함수 네이밍은 동사 + 명사 / 명사 + 명사 / 동사 + 명사 + 명사 의 형식을 유지
멘토링
- 캐시 한번 찾아보기
- 메모리에 저장하거나 로컬에 저장하거나
- 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>
-
👬 팀 회고
-
🙍♂️ 개인 회고