maecbanseok / BakedEggs

1 stars 0 forks source link

내배캠 숙련 팀프로젝트 피드백 : 맥반석 팀 #55

Open tutorhwang opened 2 months ago

tutorhwang commented 2 months ago

총평

안녕하세요!. 열심히 개발한 티가 나네요! ViewModel, Repository, 광고, SNS, Alarm, EventBus 등 지금까지 배운 것을 열심히 녹여내었어요! 잘한 것들은 이미 발표회에서 칭찬을 많이 들었으니 개선점들 많이 많이 선물해 드릴게요! 기능적으로 너무 화려하지만 공들인 만큼 UX가 예쁘게 나오진 않은 것 같긴합니다. 패션의 완성은 얼굴이라는 우스갯 소리가 있는 것처럼 앱도 UX도 중요하니 다음에는 더 깜찍한 UX 기대해 볼게요! 기술적으로 배운 내용을 녹여내려고 고민 많이 한 흔적이 보여서 멋지구요. 아직 완벽하진 않기 때문에 애매한 부분은 튜터룸에 와서 자주 물어봐 주세요! 앞으로 심화 과정과 최종 프로젝트를 하면서 얼마나 더 성장할지 기대가 됩니다. 아직은 화면마다 담당자가 다른 것인지 각자의 코드가 구현되어 있지만, 앞으로는 숲을 보며 구조 설계를 먼저 하고 개발을 진행하면 코드 구조 통일에 도움이 될 것 같아요! 전반적으로 Item이 없는 경우에 대한 NoItems(contact이나 callLog가 없는 경우 등) 처리가 안되어 있네요. 이 부분도 처리가 되면 좀 더 사용성이 높아질 것이예요. 전반적으로 라인 정리가 안되어 있네요. Ctrl(Mac: Cmd) + Alt + L 라인정리를 생활화하세요! 파일이 많아져서 기능별로 package를 나눠서 관리한 점 잘 했어요. 심화과제부터는 MVVM 패턴이나, 클린 아키텍쳐에 대해 배웠으니 data/presentation layer로 분리해서도 구성해 보세요. 코드양이 많아서 모든 것을 피드백 드리진 못했으니 궁금한 것은 언제나 튜터룸으로!! 너무 수고하셨습니다. 엄청난 발전에 박수를!!!

코드 피드백

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/AndroidManifest.xml#L47-L49

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/AddContact/AddFragment.kt#L48-L52

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/AddContact/AddFragment.kt#L70

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/AddContact/AddFragment.kt#L81

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/AddContact/AddFragment.kt#L109-L113

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/AddContact/AddFragment.kt#L123

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/AddContact/AddFragment.kt#L137-L145

fun EditText.isValidPhone(warningView: TextView, pattern: Regex): Boolean { val phone = text.toString().trim() return when { phone.isEmpty() -> { warningView.setWarning("번호를 입력해 주세요") false } !phone.matches(pattern) -> { warningView.setWarning("번호는 9~11자 입니다.", Color.MAGENTA) false } else -> { warningView.clearWarning() true } } }

fun EditText.isValidEmail(warningView: TextView, pattern: Regex): Boolean { val email = text.toString().trim() return when { email.isEmpty() -> { warningView.setWarning("이메일을 입력해 주세요") false } !email.matches(pattern) -> { warningView.setWarning("입력한 이메일을 확인해 주세요", Color.MAGENTA) false } else -> { warningView.clearWarning() true } } }

// TextView 확장 함수 정의 fun TextView.setWarning(message: String, color: Int = Color.RED) { text = message setTextColor(color) }

fun TextView.clearWarning() { text = "" }


https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/alarm/ViewModel/AlarmViewModel.kt#L19
- viewModel에서는 viewModelScope 사용하면 라이프사이클에 따라 동작할수 있어요.

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/alarm/ViewModel/AlarmViewModel.kt#L8
- interface가 있었었군요! 삭제한 이유가 있을까요?

- 알람 동작을 위해 Room도 활용했군요! 열심히 준비한 모습 멋지네요!

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/alarm/AlarmReceiver.kt#L31
- 이 조건은 minSDK가 28이라 필요없겠네요)

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/alarm/RebootAlarmReceiver.kt#L14
- reboot까지 신경쓰다니! 내공이 보이네요!

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/data/ViewModel/ContactViewModel.kt#L15
- 구현체가 아닌 Interface로 인자를 받으시는게 구현체에 대한 의존성을 제거하는데 좋습니다. (Repository Pattern 참고)
`class ContactViewModel(private val contactRepository: ContactRepository)`

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/data/ViewModel/ContactViewModel.kt#L17-L18
- LiveData나 Flow로 구현해주셔야 실시간 observe가 가능해집니다!

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/data/ViewModel/ContactViewModel.kt#L29
- viewModel에 직접 adapter를 넘기지 않고 View와 ViewModel을 끊어내야 합니다. LiveData로 list data 들을 관리해서 값 변경시 Adapter가 실시간 모니터링 하도록 구조 개선해서 MVVM 패턴의 장점을 살려보세요.
private val _contacts = MutableLiveData<List<ContactEntity>>()
val contacts: LiveData<List<ContactEntity>> get() = _contacts

private val _callLogs = MutableLiveData<List<CallLogEntity>>()
val callLogs: LiveData<List<CallLogEntity>> get() = _callLogs

private fun observeContacts() {
    viewModelScope.launch {
        contactRepository.getContactList().collectLatest { contactList ->
            _contacts.postValue(contactList)
        }
    }
}

private fun observeCallLogs() {
    viewModelScope.launch {
        contactRepository.getCallLogs().collectLatest { callLogList ->
            _callLogs.postValue(callLogList)
        }
    }
}

class MainActivity : AppCompatActivity() {

private val contactViewModel: ContactViewModel by viewModels()

.. contactAdapter = ContactAdapter() recyclerViewContacts.layoutManager = LinearLayoutManager(this) recyclerViewContacts.adapter = contactAdapter contactViewModel.contacts.observe(this, Observer { contacts -> contactAdapter.submitList(contacts) })


https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/data/CallLogAdapter.kt#L8
- MVVM 패턴이 적용되면 ListAdapter가 더 사용이 편리합니다. 다음 프로젝트에선 ListAdapter도 활용해 보세요!

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/data/contactMethods.kt#L23
- Contact/callLog 모두 잘 읽어왔네요! use나 generateSequence 등을 활용하면 좀 더 가독성을 높일 수 있어요.

fun Context.contactList(): List { val projection = arrayOf( ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Photo.PHOTO_URI, ContactsContract.Contacts.STARRED )

return contentResolver.query(
    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
    projection,
    null,
    null,
    null
)?.use { cursor ->
    generateSequence { if (cursor.moveToNext()) cursor else null }
        .map {
            val name = it.getStringOrNull(cursor.getColumnIndexOrThrow(projection[1])) ?: ""
            val number = it.getStringOrNull(cursor.getColumnIndexOrThrow(projection[2]))?.replace("-", "") ?: ""
            val photoUri = it.getStringOrNull(cursor.getColumnIndexOrThrow(projection[3]))?.toUri()
            val starred = it.getInt(cursor.getColumnIndexOrThrow(projection[4]))

            ContactEntity(name, convertString(name), number, starred, photoUri, null, null)
        }
        .toList()
        .sortedWith(compareByDescending<ContactEntity> { it.tag }.thenBy { it.name })
} ?: emptyList()

}



https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/List/ListAdapter.kt#L56-L57
- 한줄로 작성되지 않는 if else 문은 괄호 생략하지 않아요!
[](https://developer.android.com/kotlin/style-guide?hl=ko#braces)

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/List/ListFragment.kt#L59
- 어떤 Fragment인지 정확히 알수 있도록 ContactListFragment로 개명! 누구나 이름만 보면 역할을 알수있도록 이름을 지어주세요!

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/List/ListFragment.kt#L71-L73
- 특별한 동작이 추가되지 않았으면 생략가능해요!

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/List/ListFragment.kt#L89
- 외부에서 사용하지 않으니 private

https://github.com/maecbanseok/BakedEggs/blob/2f5b57f775e636ea426a2ea6c2d02cddeaab40b3/app/src/main/java/com/example/bakedeggs/mypage/data/data/MyPageData.kt#L10
- object로 구현했으면 MyPageDataObj가 없어도 됬겠어요!
MunkiJeon commented 2 months ago

이렇게 세세하게 피드백 해주시다니 감삼다!!!!!!!!!!!!!!

AnnaJuuu commented 2 months ago

감사합니다!! 꼭 참고하겠습니다!!