-
Notifications
You must be signed in to change notification settings - Fork 33
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
부산대 Android_이지은 1주차 과제 #33
base: jieunyume
Are you sure you want to change the base?
부산대 Android_이지은 1주차 과제 #33
Conversation
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.
안녕하세요 지은님! 앞으로 함께할 강대규 멘토입니다.
앞으로 6주동안 잘 부탁드려요 :)
고생 정말 많으셨습니다. 코드에 노력의 흔적이 엿보입니다.
과제를 다 끝내고 보니 저의 commit 메세지가 일관성이 없는 것 같다는 생각이 듭니다. xml 디자인 작업과 kotlin 기능 작업을 구분하지 않고 commit 메세지를 작성했는데, 구분해서 커밋하는 것이 좋을까요? 그리고 어떤 작업을 했는지 명확하게 알 수 있게 하려면 커밋 메세지를 어떤 식으로 작성하는 것이 좋을지 코치님만의 좋은 공식이 있을까요? 커밋 메세지에 대한 피드백 부탁드립니다!
커밋 메시지는 기능 단위로 잘 쪼개신 것 같아요. 커밋을 잘 쪼갤 수 있다는 것은 개발을 할 때 설계를 미리 하고, 그에 따른 세분화된 일정을 산출하는 것과 같아요. 결국 경험이 가장 필요로 되는 항목인 것 같습니다. 그럼에도 불구하고 기능단위로 잘 쪼개주셨어요. xml 단위와 kotlin 기능 단위는 크게 중요하지는 않아요. 커밋의 사이즈가 너무 크지만 않다면 (20 file changes 넘는다든가 등...) 괜챃습니다. 다른 분들의 코드 리뷰를 한번 모의로 진행해보세요. 혹은 라이브러리의 커밋을 관찰해보세요. 기능단위를 확인해볼 수 있을겁니다.
이전 미니 과제에서는 함수 분리를 어떻게 해야할지 감이 왔었는데, 안드로이드 프로젝트에서는 잘 모르겠습니다.
최대한 쪼갠다고 생각하면 조금 더 수월할 거에요.
val name_input: EditText = findViewById(R.id.name_input) | ||
val phone_number_input: EditText = findViewById(R.id.phone_number_input) | ||
val email_input: EditText = findViewById(R.id.email_input) | ||
|
||
val birth_date_input: EditText = findViewById(R.id.birth_date_input) | ||
val birth_date_radiogroup: RadioGroup = findViewById(R.id.birth_date_radiogroup) | ||
|
||
val gender_input: EditText = findViewById(R.id.gender_input) | ||
val memo_input: EditText = findViewById(R.id.memo_input) | ||
|
||
val save_button: Button = findViewById(R.id.save_button) | ||
val cancel_button: Button = findViewById(R.id.cancel_button) | ||
val see_more_button: LinearLayoutCompat = findViewById(R.id.see_more_button) | ||
val see_more_input_form: LinearLayoutCompat = findViewById(R.id.see_more_input_form) |
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.
기본적으로 코틀린은 Kotlin coding convention 을 따르게 됩니다.
코딩 컨벤션을 따르게 된다면 다른 리뷰어가 지은님의 코드를 보게될 때 비교적 쉽게 코드를 읽게 도와줍니다.
(https://velog.io/@productuidev/coding-conventionstyle-guide)
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.
또한 ViewBinding 이라는 것이 있어요.
findViewById 의 번거로움을 획기적으로 줄여줄 뿐더러, null safety, type safety 하다는 장점을 가지기도 합니다.
ViewBInding 을 사용하신다면 조금 더 코드작성이 수월해질 것으로 예상됩니다.
val see_more_button: LinearLayoutCompat = findViewById(R.id.see_more_button) | ||
val see_more_input_form: LinearLayoutCompat = findViewById(R.id.see_more_input_form) | ||
|
||
see_more_input_form.visibility = View.GONE |
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.
View 의 extension 함수 중 isVisible 이라는 놈이 존재합니다.
이것을 사용한다면 아래처럼 조금 더 간편하게 visibility 를 컨트롤할 수 있을 것입니다.
seeMoreInputForm.isVisible = false
this.let { it1 -> | ||
DatePickerDialog(it1, { _, year, month, day -> | ||
run { | ||
birth_date_input.setText(year.toString() + "." + (month + 1).toString() + "." + day.toString()) | ||
} | ||
}, year, month, day) | ||
}?.show() |
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.
여기서의 let 은 사용하지 않는 것이 좋을 것 같아요. apply, let, run, also, with 등을 범위 지정 함수라고 하는데, 코틀린 코드에 익숙해지기 전까지는 사용하지 않는 것이 조금 더 성장에 도움이 될 것 같아요.
만약 사용하고 싶다면, 도움이 되는 레퍼런스 공유드려요.
(https://kotlinworld.com/255)
if (name_input.text.isNullOrEmpty()) { | ||
Toast.makeText(this, "이름을 입력해주세요.", Toast.LENGTH_SHORT).show() | ||
} | ||
else if (phone_number_input.text.isNullOrEmpty()) { | ||
Toast.makeText(this, "전화번호를 입력해주세요.", Toast.LENGTH_SHORT).show() | ||
} else if (!phone_number_input.text.all { Character.isDigit(it) }){ | ||
Toast.makeText(this, "전화번호는 숫자로만 입력해주세요.", Toast.LENGTH_SHORT).show() | ||
} | ||
else{ | ||
Toast.makeText(this, "저장이 완료되었습니다.", Toast.LENGTH_SHORT).show() | ||
val contact: Contact = Contact( | ||
name_input.text.toString(), | ||
phone_number_input.text.toString(), | ||
email_input.text.toString(), | ||
birth_date_input.text.toString(), | ||
gender_input.text.toString(), | ||
memo_input.text.toString() | ||
) | ||
val intent: Intent = Intent() | ||
intent.putExtra("contactInfo", contact) | ||
setResult(RESULT_OK, intent) | ||
finish() | ||
} |
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.
이렇게 if 문이 반복해서 나올 경우에는 when 절으로 처리하는 것이 조금 더 명확합니다.
when {
name_input.text.isNullOrEmpty() -> {
Toast.makeText(this, "이름을 입력해주세요.", Toast.LENGTH_SHORT).show()
}
phone_number_input.text.isNullOrEmpty() -> {
Toast.makeText(this, "전화번호를 입력해주세요.", Toast.LENGTH_SHORT).show()
}
!phone_number_input.text.all { Character.isDigit(it) } -> {
Toast.makeText(this, "전화번호는 숫자로만 입력해주세요.", Toast.LENGTH_SHORT).show()
}
else -> {
// do else things...
}
}
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.
또한 각 when 절에서 직접 수식값을 사용하기보다, 명확한 boolean 형태의 값을 지역변수로 선언하여 사용하면 리뷰어가 리뷰하기 조금 더 수월해집니다. 추후 코드 관리 측면에서도 이해하기 편하구요.
val isNameEmpty = name_input.text.isNullOrEmpty()
val isPhoneNumberEmpty = phone_number_input.text.isNullOrEmpty()
val isPhoneNumberNotDigits = !phone_number_input.text.all { Character.isDigit(it) }
when {
isNameEmpty-> {
Toast.makeText(this, "이름을 입력해주세요.", Toast.LENGTH_SHORT).show()
}
isPhoneNumberEmpty-> {
Toast.makeText(this, "전화번호를 입력해주세요.", Toast.LENGTH_SHORT).show()
}
isPhoneNumberNotDigits -> {
Toast.makeText(this, "전화번호는 숫자로만 입력해주세요.", Toast.LENGTH_SHORT).show()
}
else -> {
// do else things...
}
}
super.onCreate(savedInstanceState) | ||
setContentView(R.layout.activity_contact_detail) | ||
|
||
val contact = intent.getSerializableExtra("contactDetail") as Contact |
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.
이부분 또한 아쉬웠어요. Contact 정보가 필요하다면, Contact 를 Parcelable 하게 만드는 것이 조금 더 좋아보입니다.
또한 kotlin-parcelize 를 사용한다면 조금 더 Contact 를 parcelable 하게 만들 수 있습니다.
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.
만약 parcelable 을 쓰지 않더라도, contact 내부의 값을 모두 따로따로 String 으로 주고받아도 좋을 것 같아요. 예를들어,
val name = intent.getStringExtra("name")
val phoneNumber = intent.getStringExtra("phoneNumber")
val email = intent.getStringExtra("email")
// 이하 생략
val contact = Context(
name = name,
phoneNumber = phoneNumber,
email = email,
// 이하 생략
)
1 -> { | ||
when(resultCode){ | ||
AppCompatActivity.RESULT_OK -> { | ||
val contact: Contact? = data?.getSerializableExtra("contactInfo") as Contact? |
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.
오호 흥미로운 주제네요. kotlin 의 safe cast 와 nullable cast 의 충돌인데, 한번 해당 링크 확인해보면 좋을 것 같네요 :)
private val activity: ContactListActivity | ||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { | ||
|
||
inner class ViewHolder(val itemView: View) : RecyclerView.ViewHolder(itemView) { |
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.
요거 굳이 inner class 로 사용하는 이유가 있을까요?
class ContactRecyclerAdapter( | ||
private val contactList: MutableList<Contact>, | ||
private val inflater: LayoutInflater, | ||
private val activity: ContactListActivity |
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.
activity 를 변수로 넘기는 것은 금기시해야할 것 중 하나입니다. inflater 또한 파라미터로 받을 필요는 없을 것 같구요.
아이템을 클릭했을 때 이동해야한다면, 아이템이 클릭되었음을 알려주는 listener 를 정의하고 그것을 파라미터로 받는 것이 좋습니다.
interface OnContactClickListener {
fun onContactClicked(position: Int)
}
📞기능 목록
step1
step2
📞어려웠던 점
📞중점적으로 리뷰해주셨으면 하는 부분