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

Week2 과제 ... #3

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open

Week2 과제 ... #3

wants to merge 6 commits into from

Conversation

tunaunnie
Copy link
Contributor

@tunaunnie tunaunnie commented Oct 23, 2024

Related issue 🛠

Work Description ✏️

  1. 1차 과제 피드백 반영
  • Required parameter 먼저 받고 옵셔널 파라미터 받는 것으로 수정
  • Kotlin Coding Convention 기반 변수명 전부 삭제
  • 수행하는 기능에 따라 함수명 수정
  • shouldShowPassword의 값 기반으로 show/hide 변환
  1. 반복되는 컴포넌트 전부 컴포넌트화 완료
  • SignUp & Login 화면에서 쓰이는 TextField 컴포넌트화
  • 소셜 로그인 버튼 부분 컴포넌트화
  • Mypage의 프로필 섹션 & 구매하기 섹션 컴포넌트화
  • 상단바 2개, 하단 네비게이션바 컴포넌트화
  1. Pager와 LazyRow 사용하여 HomeScreen 구현 완료
  • LazyRow는 각 image 아이템의 가로, 세로 길이를 인자로 받아 조정하도록 컴포넌트화
  1. Jetpack Navigation 사용해서 SAA 구조로 화면 전환 적용

  2. 로그인 성공 시 접속한 이메일을 ViewModel을 사용하여 전역변수로 관리

Screenshot 📸

SOPT_second_assignment.mp4

Uncompleted Tasks 😅

  1. MVVM 구조 적용..
  2. Mypage에서 접속한 이메일 띄워주기 (전역변수로 넣었는데 계속 안돼서 우선 제출합니다..)
  3. 상단바 2개 사이에 이상하게 들어가는 패딩 제거

To Reviewers 📢

  1. 접속한 email을 MypageScreen에 매개변수로 넣어서 관리해줄 수 있을 것 같은데, Mypage에 Navigation을 이용해서 접속하다 보니까 어떻게 email값을 전달해줘야 할 지 잘 모르겠습니다....... 그래서 그냥 로그인 성공 시 viewModel을 이용해서 전역변수로 관리해주려 했는데 그래도 값이 안들어가서 우선 제출합니다.....

@tunaunnie tunaunnie changed the title Week2 과제입니다 ... Week2 과제 ... Oct 23, 2024
Copy link

@chattymin chattymin left a comment

Choose a reason for hiding this comment

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

열심히 작업하시느라 고생 많으셨습니다!!

지난주에 비해서 코드가 많이 깔끔해지고, 노력하신게 보이는 코드여서 즐겁게 읽고 리뷰 남겼습니다 :)
질문 생기시면 편하게 남겨주시면 시간 나는대로 답장 달겠습니다!

이번주에 남긴 리뷰도 제 주관적인 스타일이 담겨있기 때문에 정답이 아닙니다. 참고만 하시고, 다른 코드도 비교해보시고 결정하시면 좋을거에요


@Composable
fun CustomTopAppBarSecond(navController: NavController){
TopAppBar(

Choose a reason for hiding this comment

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

Uncompleted Tasks에 적혀있는 두 TopAppBar 사이의 간격에 대해서는 TopAppBar 자체의 구현 내용을 보시면 좋을 것 같아요. 아래 사진에 보시면 elevation: Dp = AppBarDefaults.TopAppBarElevation 라는 기본값이 있어요.

그리고 AppBarDefaults를 들어가서 보시면 val TopAppBarElevation = 4.dp이 선언되어 있어요. 즉, TopAppBar에는 기본적으로 4.dp만큼의 패딩이 있기 때문에 두 TopAppBar 사이에는 8.dp만큼의 간격이 존재할거에요.

그리고 두번째 가능성은 TopAppBar 자체의 높이가 할당되어있다는 거에요. TopAppBar를 구현한 AppBar를 보시면 AppBarHeight라는 높이만큼 높이를 할당하고 있어요. private val AppBarHeight = 56.dp 그렇기 때문에 의도하신 높이와 다를 수 있어요.

현재 구조상 두개의 TopAppBar를 겹치신다면 TopAppBar의 높이인 56과 위아래 패딩을 합친 8, 이게 두개니까 128.dp의 높이를 가지게 될거에요

image

Comment on lines +53 to +62
Scaffold(
topBar = {
Column {
CustomTopAppBar(navController = navController)
CustomTopAppBarSecond(navController = navController)
}
},
bottomBar = {
CustomBottomAppBar(navController = navController)
}

Choose a reason for hiding this comment

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

이 코드가 Home, Search, My에서 중복되고 있는 것 같아요. 외부로 빼는건 어떨까요?
지금 방법의 문제점은 아래처럼 느껴져요.

  1. 같은 코드가 여러 화면에서 중복된다.
  2. 화면 이동을 할 때 bottomBar도 깜빡이며 화면이 재구성된다.

NavHost 자체를 Scaffold로 감싸는 방법도 있을 것 같아요. 그렇게 되면 SignUp과 같은 화면에도 topBar와 bottomBar가 나올거에요. 그 때 조건을 통해 특정 화면일 때에만 나오도록 설정할 수 있어요!

물론 다른 방법이 있다면 그렇게 하는 것도 좋아요! 저는 여러 방법중 하나를 제안드리는거지 절대 정답은 아닙니다 :)

)
composable<LoginScreen> { backStackEntry ->
val item = backStackEntry.toRoute<LoginScreen>()
val scope = rememberCoroutineScope()

Choose a reason for hiding this comment

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

scope변수를 Screen 밖에서 선언한 후 Screen에 매개변수로 넘겨준 이유가 있을까요?

외부에서 scope를 사용하고 있지 않고있기 때문에 Screen 내부에서 선언하는게 좋을 것 같아요.


@Composable
fun Greeting3(modifier: Modifier = Modifier) {
fun MypageScreen(
navController: NavController,

Choose a reason for hiding this comment

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

navController를 매개변수로 받아서, 화면 내에서 이동을 하는 방법을 사용하고 있는데 윗부분에서는 navigation과 관련된 함수는 람다를 통해 위로 올려줘서 관리하고 있더라구요. 두가지 방법을 섞어서 사용하신 이유가 있을까요?

우선 저는 navController를 매개변수로 넘겨주는 것을 선호하지 않아요. 그렇게 된다면 화면 내부에 navigation 로직이 존재하게 되고, 추후 수정할 때 하나하나 찾기 귀찮더라구요

그리구 나중에 멀티모듈을 하시게 된다면, 화면 밖으로 빼주는 것의 장점을 한번 더 느끼실 수 있으실 거에요. 물론 나아아아아중에 하시면 되는 개념이고, 심지어 안해보셔도 돼요 :) 걱정하지 않으셔도 됩니당

Comment on lines +26 to +33
@Composable
fun HomeLazyRow(
title: String,
images: List<Int>,
height: Int,
width: Int,
modifier: Modifier = Modifier
) {

Choose a reason for hiding this comment

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

깔끔하게 잘 만드셨네요!! 고민 하시며 만드신게 보여요 :)

하나 수정하면 좋을 것 같은 부분은 변수명을 정하는 부분이에요!
weight와 height라는 변수명은 LazyRow 내부에 있는 Image의 크기를 말하는 것인지, 해당 컴포넌트 자체의 크기를 의미하는 것인지 오해의 소지가 있어요. 그렇기 때문에 조금 더 직관적인 이름을 사용하시면 좋을 것 같아요

) {
Image(
modifier = Modifier
.size(70.dp, 70.dp)

Choose a reason for hiding this comment

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

Suggested change
.size(70.dp, 70.dp)
.size(70.dp)

이렇게 해도 같습니당

Choose a reason for hiding this comment

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

동민님 리뷰에 덧붙이자면 이러한 경우를 방지하기위해 Named Arguments를 사용하면 좋습니다!
Text("텍스트") -> Text(text="텍스트") 이렇게요😁

Comment on lines +29 to +36
Text(
"$sectionDescription",
color = Color.Gray
)
Text(
"구매하기 >",
color = Color.White
)

Choose a reason for hiding this comment

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

named call argument 가 없다면 가독성에서도 아쉬워지고, 추후 수정을 할 때에도 불편할 수 있어요. 붙이는 습관을 들이면 좋을 것 같아요!

Suggested change
Text(
"$sectionDescription",
color = Color.Gray
)
Text(
"구매하기 >",
color = Color.White
)
Text(
text = "$sectionDescription",
color = Color.Gray
)
Text(
text = "구매하기 >",
color = Color.White
)

Choose a reason for hiding this comment

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

아하 named argument에 대한 이야기가 여기 이미 달렸네용

Comment on lines +20 to +32
@Composable
fun SignUpTextField(
modifier: Modifier = Modifier,
onValueChange: (String) -> Unit,
fieldType: String, //Email 혹은 Password로 전달 예정
text: String,
conditionCheck: Boolean,
errMessage: String,
placeholder: String,
shouldShowPassword: Boolean = false,
onPasswordVisibilityChange: () -> Unit = {},
descriptionText: String = "",
){

Choose a reason for hiding this comment

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

fieldType에 "Email", "Password"만 들어온다는 제한이 존재한다면 더욱 강력한 제약을 걸면 좋을 것 같아요. Enum으로 만드는 방법도 있을 것 같네요.

그리고 onPasswordVisibilityChange의 경우 Password일 때에만 사용되는 코드 같아요. 그렇다면 이 값을 넣어줬냐, 넣어주지 않았냐로 "Email"인지 "Password"인지를 판단하는 근거로 사용해도 좋을 것 같네요. 물론 명시적이지 않은 방법이여서 가독성을 더 챙기는 과정을 해야할 것 같아요

저라면 textField를 2개를 만들 것 같아요. BaseTextField를 하나 만들어 기본적인 토대를 만들고, 이를 활용하는 TextField와 PasswordTextField 두개로 만들어서 관리한다면 더욱 직관적일 수 있을 것 같아요.

Comment on lines +27 to +44
NavIcon(
navController = navController,
route = "home",
icon = Icons.Filled.Home,
text = "홈"
)
NavIcon(
navController = navController,
route = "search",
icon = Icons.Filled.Search,
text = "검색"
)
NavIcon(
navController = navController,
route = "profile",
icon = Icons.Filled.Person,
text = "MY"
)

Choose a reason for hiding this comment

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

이 세 화면은 route를 String으로 지정하신 이유가 있을까요??

호출하는 부분에서도 String값이라면 옮겨적는 과정에서 실수를 해서 잘못 적을 수도 있고, 일부는 data Class이고 일부는 String이라면 통일성이 없어서 추후 관리하기 헷갈릴 것 같아요

Copy link

@t1nm1ksun t1nm1ksun left a comment

Choose a reason for hiding this comment

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

시험아직 안끝나서 정신없으셨을텐데 고생 많으셨습니다!!

Comment on lines +41 to +56
fun EmailValidCheck(email: String): Boolean {
var isValid = false
val inputStr : CharSequence = email
val pattern = Patterns.EMAIL_ADDRESS
val matcher = pattern.matcher(inputStr)
if(matcher.matches()){
isValid = true
}
return isValid
}

fun PasswordValidCheck(password: String): Boolean {
val pattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#\$%^&*])|(?=.*[a-z])(?=.*\\d)(?=.*[!@#\$%^&*])|(?=.*[A-Z])(?=.*\\d)(?=.*[!@#\$%^&*]).{8,20}\$".toRegex()
return password.matches(pattern)

}

Choose a reason for hiding this comment

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

컴포즈함수가 아니면 함수명은 소문자로 시작해주시는게 좋을거같아요!!

Comment on lines +30 to +31
height: Int,
width: Int,

Choose a reason for hiding this comment

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

사용하는 곳 보니까 하나만 사이즈가 다르고 나머지는 똑같아 보이던데 자주 쓰이는 값을 기본값으로 주면 더 편하게 쓸 수 있을거같아요!
(나중에 사용되는 곳이 많아지면 헷갈리더라구요!)

) {
Image(
modifier = Modifier
.size(70.dp, 70.dp)

Choose a reason for hiding this comment

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

동민님 리뷰에 덧붙이자면 이러한 경우를 방지하기위해 Named Arguments를 사용하면 좋습니다!
Text("텍스트") -> Text(text="텍스트") 이렇게요😁

)
Spacer(modifier = Modifier.width(10.dp))
Text(
"${deliveredEmail}님",

Choose a reason for hiding this comment

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

중괄호 없어도 되지롱롱소세지~

Comment on lines +29 to +36
Text(
"$sectionDescription",
color = Color.Gray
)
Text(
"구매하기 >",
color = Color.White
)

Choose a reason for hiding this comment

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

아하 named argument에 대한 이야기가 여기 이미 달렸네용

modifier = modifier.fillMaxWidth()
)
{
TextField(

Choose a reason for hiding this comment

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

커스텀 굿 입니다👍

@@ -0,0 +1,50 @@
@file:OptIn(ExperimentalMaterial3Api::class)

Choose a reason for hiding this comment

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

파일 전체단위에 OptIn 어노테이션을 사용하신 이유가 있으신가요?
(저도 잘몰라서 궁금해서요...!)

t1nm1ksun

This comment was marked as duplicate.

t1nm1ksun

This comment was marked as duplicate.

fun MypageScreen(
navController: NavController,
userViewModel: UserViewModel = viewModel()
) {

Choose a reason for hiding this comment

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

MypageScreen 함수에서 userViewModel 을 매개변수로 받고 있는데, 이 부분을 @Composable함수 내부에서 선언하는 건 어떨까요? 외부에서 사용되지 않는다면 불필요한 매개변수를 줄일 수 있을 것 같아요!

val userViewModel: UserViewModel = viewModel()

isValid = true
}
return isValid
}
Copy link

@angryPodo angryPodo Oct 27, 2024

Choose a reason for hiding this comment

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

if 문을 사용하여 조건을 확인한 후 값을 설정하는 대신, 조건 자체를 반환하는 방식으로 코드를 간소화 해볼 수 있을 것 같아요!

fun EmailValidCheck(email: String): Boolean {
    return Patterns.EMAIL_ADDRESS.matcher(email).matches()
}

Copy link
Contributor

Choose a reason for hiding this comment

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

fun EmailValidCheck(email: String): Boolean = Patterns.EMAIL_ADDRESS.matcher(email).matches()
이렇게까지 줄일 수도 있습니다.

}
}
} else {
/*0개가 아니면 리스트뷰로 보여주기*/

Choose a reason for hiding this comment

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

1주차에 제 코드에 달렸던 리뷰인데

Suggested change
/*0개가 아니면 리스트뷰로 보여주기*/
//TODO: 리스트뷰로 보여줄 내용 구현 예정

를 쓰시면 가독성도 좋고 의미도 확실해 집니다!

.size(30.dp, 30.dp)
.clip(CircleShape),
painter = painterResource(id = R.drawable.btn_kakao),
contentDescription = "Kakao Logo",

Choose a reason for hiding this comment

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

SNS 버튼들도 컴포넌트화 하면 좋을 것 같아요!

Copy link
Contributor

Choose a reason for hiding this comment

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

그리고 Enum 등을 통해 관리하면 더 가독성이 높아질 것 같아요!

Copy link

@angryPodo angryPodo left a comment

Choose a reason for hiding this comment

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

시험기간에 바쁘셨을텐데 많은 고민 하시면서 코드 짜신게 느껴졌어요..
부족한 제자신에게 반성하고 갑니다. 코드 잘봤어요!

Copy link
Contributor

@jihyunniiii jihyunniiii left a comment

Choose a reason for hiding this comment

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

고생하셨습니다 ~

Comment on lines +17 to +32
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".HomeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SearchActivity"
Copy link
Contributor

Choose a reason for hiding this comment

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

이번주 과제가 SAA 적용이었죠?
Single Activity가 되려면 말 그대로 액티비티가 하나여야 합니다.
즉, manifest에 등록한 액티비티가 하나여야 해요!
이를 바탕으로 리팩해보시면 좋을 것 같아요!

@Composable
fun HomeScreen(
modifier: Modifier = Modifier,
navController: NavController, // navController를 넘겨 받아 사용
Copy link
Contributor

Choose a reason for hiding this comment

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

하위 컴포저블 함수에 NavController를 인자로 받는 것은 권장하지 않는 방법입니다.
필요한 동작들을 함수화해서 함수를 넘겨주세요!

Comment on lines +73 to +79
val images = listOf(
R.drawable.food_pic1,
R.drawable.food_pic2,
R.drawable.food_pic3,
R.drawable.food_pic4,
R.drawable.food_pic5
)
Copy link
Contributor

Choose a reason for hiding this comment

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

세미나 때 AAC 뷰모델에 대해 배웠었죠?
이 값은 뷰모델로 옮기면 좋을 것 같아요!!
(왜인지는 세미나 자료 참고)

Copy link
Contributor

Choose a reason for hiding this comment

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

추가적으로 persistentListOf에 대해 공부해보시는 걸 추천드려요

CustomBottomAppBar(navController = navController)
}
) { innerPadding ->
Column(
Copy link
Contributor

Choose a reason for hiding this comment

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

LazyColumn을 사용하는 것이 성능상으로 더 좋을 것 같아요
(마찬가지로 자세한 내용은 세미나 자료 참고 부탁드립니다.)

isValid = true
}
return isValid
}
Copy link
Contributor

Choose a reason for hiding this comment

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

fun EmailValidCheck(email: String): Boolean = Patterns.EMAIL_ADDRESS.matcher(email).matches()
이렇게까지 줄일 수도 있습니다.

Comment on lines +70 to +75
var emailText = remember { mutableStateOf("") }
var passwordText = remember { mutableStateOf("") }

var shouldShowPassword = remember {mutableStateOf(false)}
var isEmailValid = remember { mutableStateOf(true) }
var isPasswordValid = remember { mutableStateOf(true) }
Copy link
Contributor

Choose a reason for hiding this comment

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

해당 값들은 뷰모델에서 관리하는 것이 좋아요!
이번주 과제에 MVVM 아키텍처를 적용하는 것도 포함이었는데요!
나중에라도 꼭 적용해보세요.

.size(30.dp, 30.dp)
.clip(CircleShape),
painter = painterResource(id = R.drawable.btn_kakao),
contentDescription = "Kakao Logo",
Copy link
Contributor

Choose a reason for hiding this comment

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

그리고 Enum 등을 통해 관리하면 더 가독성이 높아질 것 같아요!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Week2
5 participants