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

[FEAT/#19] Week6 / 심화 과제 #22

Merged
merged 32 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a594083
[UI/#18] TextInputLayout 적용
kangyuri1114 Dec 3, 2023
428b628
[UI/#18] TextInputLayout 길이 제한
kangyuri1114 Dec 3, 2023
5bb053d
[UI/#18] TextInputLayout 길이 제한
kangyuri1114 Dec 3, 2023
cae165a
[MOD/#18] 네이밍 수정
kangyuri1114 Dec 3, 2023
50eec15
[MOD/#18] 네이밍 수정
kangyuri1114 Dec 4, 2023
36ce63c
[MOD/#18] 에러 문구 추가
kangyuri1114 Dec 4, 2023
6e886a8
[MOD/#18] 아이디, 비밀번호 패턴 상수 추가
kangyuri1114 Dec 4, 2023
ecb6e57
[MOD/#18] 정규식 처리 라이브데이터 추가
kangyuri1114 Dec 4, 2023
d6e0f7a
[MOD/#18] TextWatcher 추가
kangyuri1114 Dec 4, 2023
acf6872
[MOD/#18] 정규식은 성공.. 버튼활성화랑 빈 데이터가 들어가는 이슈..
kangyuri1114 Dec 4, 2023
574b07a
[MOD/#18] 정규식은 성공.. 버튼활성화랑 빈 데이터가 들어가는 이슈..
kangyuri1114 Dec 4, 2023
d060b9a
[MOD/#18] 조건에 따라 회원가입 하기 성공
kangyuri1114 Dec 4, 2023
8049716
[MOD/#18] 조건에 따라 회원가입 하기 성공
kangyuri1114 Dec 4, 2023
c0e9b3a
[MOD/#18] 버튼 클릭 로직 함수화
kangyuri1114 Dec 4, 2023
c91896a
[MOD/#18] 함수 위치 수정
kangyuri1114 Dec 4, 2023
1504823
[MOD/#18] 함수화
kangyuri1114 Dec 4, 2023
541335a
[MOD/#18] 양방향 데이터 바인딩 적용 성공, Transformations.map
kangyuri1114 Dec 5, 2023
c37c4b0
[MOD/#18] 주석 제거, string 추가
kangyuri1114 Dec 5, 2023
d6841fc
[DOCS/#18] 리드미 수정
kangyuri1114 Dec 5, 2023
1eecf85
[DOCS/#18] 버전 업데이트
kangyuri1114 Dec 6, 2023
b2135ad
[ADD/#18] 바인딩 어댑터 추가
kangyuri1114 Dec 6, 2023
49569bc
[FEAT/#18] 바인딩 어댑터로 텍스트 크기 변환하기
kangyuri1114 Dec 6, 2023
fa11e83
[ADD/#19] UiState 파일 추가
kangyuri1114 Dec 6, 2023
d00240b
[MOD/#19] enqueue로 통신 했던 것을 코루틴 suspend로 대체 - 회원가입 성공
kangyuri1114 Dec 6, 2023
363da8f
[MOD/#19] enqueue로 통신 했던 것을 코루틴 suspend로 대체 - 로그인 성공, response 받아오는 건 못함
kangyuri1114 Dec 6, 2023
149c5b6
[MOD/#19] enqueue로 통신 했던 것을 코루틴 suspend로 대체 - 로그인 성공, response 받아오기 성공
kangyuri1114 Dec 6, 2023
ce865f7
[COMMENT/#19] 주석 제거, 추가
kangyuri1114 Dec 6, 2023
d17c116
[DELETE/#19] 중복 로직 삭제
kangyuri1114 Dec 6, 2023
140599e
[MOD/#19] 스낵바 수정
kangyuri1114 Dec 6, 2023
213e623
[MOD/#19] enqueue로 통신 했던 것을 코루틴 suspend로 대체 - open API 친구 리스트 성공
kangyuri1114 Dec 6, 2023
c8ce94b
[MOD/#19] 코드리뷰 반영
kangyuri1114 Dec 6, 2023
fe951ff
[MOD/#19] 함수화
kangyuri1114 Dec 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,19 @@
- [2주차 과제 정리 노션 링크](https://peaceful-minnow-33c.notion.site/2-9d6f1d9f666c4400b976de6ce74ddddf?pvs=4)
- [3주차 과제 정리 노션 링크](https://peaceful-minnow-33c.notion.site/3-d26d9c5df31a4a939dd07c2d1e826da2?pvs=4)
- [4주차 과제 정리 노션 링크](https://peaceful-minnow-33c.notion.site/4-c4d7667ba8384bd8a7b3f95e65137f90?pvs=4)
- [5주차 과제 정리 노션 링크](https://peaceful-minnow-33c.notion.site/5-2733cddc34d544f392ce346d7cd2cd9f?pvs=4)
- [6주차 과제 정리 노션 링크](https://peaceful-minnow-33c.notion.site/6-ba05ef999d564255a1f5371b4e7a6e7d?pvs=4)
- [7주차 과제 정리 노션 링크](https://peaceful-minnow-33c.notion.site/7-41bd07e56576401fa944fdb3421a17b3?pvs=4)

### develop
1차 세미나 : Android UI 구현 기초
2차 세미나 : Android UI 구현 심화 : 디자인 패턴
3차 세미나 : Material Design System과 안드로이드 UI/UX
4차 세미나 : Retrofit2를 활용한 서버 통신과 협업을 위한 대비
- 1차 세미나 : Android UI 구현 기초
- 2차 세미나 : Android UI 구현 심화 : 디자인 패턴
- 3차 세미나 : Material Design System과 안드로이드 UI/UX
- 4차 세미나 : Retrofit2를 활용한 서버 통신과 협업을 위한 대비
- 5차 세미나 : 합동 세미나
- 6차 세미나 : LiveData를 활용한 반응형 프로그래밍과 DataBinding, Coroutine 기초
- 7차 세미나 : Coroutine Flow와 UiState로 MVVM에 다가가기
- 8차 세미나 : 앱잼을 위한 고급 꿀팁 대방출

---

Expand Down
12 changes: 6 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
id 'org.jetbrains.kotlin.android'
id "kotlin-parcelize"
id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.20'
id 'kotlin-kapt'
}

Properties properties = new Properties()
Expand Down Expand Up @@ -32,17 +33,16 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '11'
}
viewBinding {
enable = true
jvmTarget = '17'
}
buildFeatures {
buildConfig true
viewBinding true
dataBinding true
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import retrofit2.http.POST
interface AuthService {
// 회원가입
@POST("api/v1/members")
fun signUp(
suspend fun signUp(
@Body request: RequestSignupDto,
): Call<Unit>
Copy link
Member

Choose a reason for hiding this comment

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

요 부분의 Call<>도 빼주셔도 될듯함다! 그 전에 Call이 여기서 어떤 역할을 했었고, 왜 이제 안 감싸주어도 작동이 되는지 공부해보면 더 좋을 듯 합니다 ~~


// 로그인
@POST("api/v1/members/sign-in")
fun login(
suspend fun login(
@Body request: RequestLoginDto,
): Call<ResponseLoginDto>
): ResponseLoginDto
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package org.sopt.dosopttemplate.data.service

import org.sopt.dosopttemplate.data.remote.response.ResponsePeopleListDto
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query

interface PeopleService {

// 사람들 리스트 받아오기
@GET("users")
fun PeopleListGet(
suspend fun PeopleListGet(
@Query("page") page: Int,
@Query("per_page") perPage: Int,
): Call<ResponsePeopleListDto>
): ResponsePeopleListDto
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import android.view.MotionEvent
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import org.sopt.dosopttemplate.R
import org.sopt.dosopttemplate.data.User
import org.sopt.dosopttemplate.databinding.ActivityLoginBinding
import org.sopt.dosopttemplate.di.UserSharedPreferences
import org.sopt.dosopttemplate.presentation.main.BnvActivity
import org.sopt.dosopttemplate.util.BackPressedUtil
import org.sopt.dosopttemplate.util.UiState
import org.sopt.dosopttemplate.util.hideKeyboard
import org.sopt.dosopttemplate.util.showShortSnackBar
import org.sopt.dosopttemplate.util.showShortToast

class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding
Expand All @@ -23,6 +27,13 @@ class LoginActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(binding.root)

autoLogin()
userClickSignUpBtn()
userCickLoginBtn()
backPressed()
}

private fun autoLogin() {
// 자동 로그인으로 저장된 유저 정보
val spUser = UserSharedPreferences.getUser(this)

Expand All @@ -33,45 +44,60 @@ class LoginActivity : AppCompatActivity() {
// 새로운 Activity를 수행하고 현재 Activity를 스텍에서 제거
startActivity(intent)
}
}

// 회원가입 하러 가기
// 회원가입 하러 가기
private fun userClickSignUpBtn() {
binding.btnSignupSignup.setOnClickListener {
val intent = Intent(this, SignUpActivity::class.java)
startActivity(intent)
}
}

// 로그인 하기
// 로그인 하기
private fun userCickLoginBtn() {
binding.btnLoginLogin.setOnClickListener {
// 자동 로그인이 적용되지 않고, 회원가입에서 넘어온 경우
val signUpUser = intent.getParcelableExtra<User>("signUpUser")

val inputId = binding.etSignupId.text.toString()
val inputPw = binding.etSignupPw.text.toString()

loginViewModel.loginUser(inputId, inputPw, this)
loginViewModel.loginUser(inputId, inputPw)

loginViewModel.loginResult.observe(
loginViewModel.getLoginInfo.observe(
Copy link
Member

Choose a reason for hiding this comment

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

옵저버는 onClickListener보다는 바깥쪽에 위치하는 게 더 좋을 것 같아요~!

Copy link
Member Author

Choose a reason for hiding this comment

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

오 생각해보니 그렇네염.,,,,,맞슴니다

this,
) { loginSuccessful ->
if (loginSuccessful) {
// showShortToast("getString(R.string.login_success)")

if (binding.cbLoginAutologin.isChecked) {
signUpUser?.let {
loginViewModel.saveUserForAutoLogin(this, it)
) { uiState ->
when (uiState) {
is UiState.Success -> {
if (binding.cbLoginAutologin.isChecked) {
signUpUser?.let {
loginViewModel.saveUserForAutoLogin(this, it)
Comment on lines +70 to +75
Copy link
Member

Choose a reason for hiding this comment

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

저도 UiSate 써보고 싶었는데,, 다음에 할 때 참고해야게써요 최곱니다,,,

}
}

val userId = uiState.data?.userId
showShortToast("로그인 성공, 유저 아이디 : $userId")
Copy link
Member

Choose a reason for hiding this comment

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

그냥 슥 봐서는 저는 두번 토스트 뜨는 이유가 안보이네욥... 로그 찍어봐야할듯 !

Copy link
Member Author

Choose a reason for hiding this comment

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

ㅋㅋㅋㅋㅋ 제가 버튼을 두번 누르는 이슈였습니다 ..ㅎㅎ^^

Copy link
Member

Choose a reason for hiding this comment

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

@kangyuri1114 제 코드 중에 util에 setOnSingleClickListener 라구 있거덩요? 그거 가져와서 대신 쓰면 더블 클릭에 대응하지 않게 된답니다~~!


val intent = Intent(this, BnvActivity::class.java)
intent.putExtra("signUpUser", signUpUser)
startActivity(intent)
finish()
}

is UiState.Failure -> {
showShortSnackBar(binding.root, "로그인 실패 : ${uiState.errorMessage}")
}

val intent = Intent(this, BnvActivity::class.java)
intent.putExtra("signUpUser", signUpUser)
startActivity(intent)
finish()
} else {
// showShortSnackBar(binding.root, getString(R.string.login_fail))
is UiState.Loading -> {
showShortSnackBar(binding.root, getString(R.string.uistate_loading))
}
}
}
}
}

private fun backPressed() {
val backPressedUtil = BackPressedUtil<ActivityLoginBinding>(this)
backPressedUtil.BackButton()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,35 @@
package org.sopt.dosopttemplate.presentation.auth

import android.content.Context
import android.widget.Toast
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.sopt.dosopttemplate.data.User
import org.sopt.dosopttemplate.data.remote.ServicePool.authService
import org.sopt.dosopttemplate.data.remote.ServicePool
import org.sopt.dosopttemplate.data.remote.request.RequestLoginDto
import org.sopt.dosopttemplate.data.remote.response.ResponseLoginDto
import org.sopt.dosopttemplate.di.UserSharedPreferences
import retrofit2.Call
import retrofit2.Response
import org.sopt.dosopttemplate.util.UiState

class LoginViewModel : ViewModel() {
private val _loginResult = MutableLiveData<Boolean>()
val loginResult: LiveData<Boolean> get() = _loginResult

fun loginUser(inputId: String, inputPw: String, context: Context) {
// _loginResult.value =
// signUpUser != null && signUpUser.userId == inputId && signUpUser.userPw == inputPw

authService.login(RequestLoginDto(inputId, inputPw))
.enqueue(object : retrofit2.Callback<ResponseLoginDto> {
override fun onResponse(
call: Call<ResponseLoginDto>,
response: Response<ResponseLoginDto>,
) {
if (response.isSuccessful) {
val data: ResponseLoginDto =
response.body() ?: ResponseLoginDto(-1, "null", "null")
val userId = data.id

Toast.makeText(
context,
"로그인이 성공하였고 유저의 ID는 $userId 입니둥",
Toast.LENGTH_SHORT,
).show()
_loginResult.value = true
}
}

override fun onFailure(call: Call<ResponseLoginDto>, t: Throwable) {
Toast.makeText(
context,
"ㅜ ㅜ 서버 에러 발생 ㅜ ㅜ",
Toast.LENGTH_SHORT,
).show()

_loginResult.value = false
}
})
// 로그인 response에서 받아온 유저 정보
private val _getLoginInfo = MutableLiveData<UiState<User>>()
val getLoginInfo: LiveData<UiState<User>> get() = _getLoginInfo
Comment on lines +18 to +19
Copy link
Member

Choose a reason for hiding this comment

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

변수명이 get으로 시작하면 너무 함수명같지 않나요..? ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

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

저 이제 변수명 정하는거 오류났어요
최종_진짜최종_찐최종 느낌..;; 좀 더 고민해볼게요 ㅎㅎ,,


fun loginUser(inputId: String, inputPw: String) = viewModelScope.launch {
_getLoginInfo.value = UiState.Loading

runCatching {
ServicePool.authService.login(
RequestLoginDto(inputId, inputPw),
)
}.onSuccess {
_getLoginInfo.value = UiState.Success(User(it.id.toString(), it.username, it.nickname))
}.onFailure {
_getLoginInfo.value = UiState.Failure(it.message.toString())
}
}

fun saveUserForAutoLogin(context: Context, signUpUser: User) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import org.sopt.dosopttemplate.R
import org.sopt.dosopttemplate.data.User
import org.sopt.dosopttemplate.databinding.ActivitySignupBinding
import org.sopt.dosopttemplate.util.UiState
import org.sopt.dosopttemplate.util.showShortSnackBar
import org.sopt.dosopttemplate.util.showShortToast

Expand All @@ -19,29 +19,62 @@ class SignUpActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(binding.root)

binding.lifecycleOwner = this
binding.authViewModel = signUpViewModel

observeValid()
clickSignUpBtn()
}

private fun observeValid() {
signUpViewModel.idFlag.observe(this) { idFlag ->
binding.telSignupId.error = if (idFlag) null else getString(R.string.id_layout_title)
Copy link
Member

Choose a reason for hiding this comment

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

오홍 이런식으로 if문 쓰는방법도 좋네요

Copy link
Member Author

Choose a reason for hiding this comment

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

조장이 고쳐줬어염 ㅎㅅㅎ

btnEnable()
}
signUpViewModel.pwFlag.observe(this) { pwFlag ->
binding.telSignupPw.error = if (pwFlag) null else getString(R.string.pw_layout_title)
btnEnable()
}
signUpViewModel.nicknameFlag.observe(this) { nicknameFlag ->
binding.telSignupNickname.error =
if (nicknameFlag) null else getString(R.string.nickname_layout_title)
btnEnable()
}
}

private fun btnEnable() {
signUpViewModel.signUpBtnFlag()
binding.btnSignupSignup.isEnabled = signUpViewModel.signUpBtnFlag.value == true
if (binding.btnSignupSignup.isEnabled) {
signUpViewModel.onUserTextSizeChanged(40)
} else {
signUpViewModel.onUserTextSizeChanged(10)
}
}

private fun clickSignUpBtn() {
binding.btnSignupSignup.setOnClickListener {
val signUpUserId = binding.etSignupId.text.toString()
val signUpUserPw = binding.etSignupPw.text.toString()
val signUpUserNickname = binding.etSignupNickname.text.toString()
// val signUpUserAge = binding.etSignupAge.text.toString()

val signUpUser = User(signUpUserId, signUpUserPw, signUpUserNickname)

signUpViewModel.signUpUser(signUpUser, this)

signUpViewModel.signUpResult.observe(this) { signUpSuccessful ->
if (signUpSuccessful) {
// 화면 전환
showShortToast(getString(R.string.signup_success))

val intent = Intent(this, LoginActivity::class.java)
intent.putExtra("signUpUser", signUpUser)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)
} else {
showShortSnackBar(binding.root, getString(R.string.signup_fail))
signUpViewModel.signUpResult.observe(this) { uiState ->
when (uiState) {
is UiState.Success -> {
showShortToast(getString(R.string.signup_success))
val intent = Intent(this, LoginActivity::class.java)
startActivity(intent)
finish()
}

is UiState.Failure -> {
showShortSnackBar(binding.root, "회원가입 실패 : ${uiState.errorMessage}")
}

is UiState.Loading -> {
showShortSnackBar(binding.root, getString(R.string.uistate_loading))
}
}
}
binding.btnSignupSignup.setOnClickListener {
signUpViewModel.signUpUserApi()
}
}
}
}
Loading