연습장/실습

4주차_ SNS 앱 1. TextWatcher 유효성 검사

아이른 2024. 4. 1. 22:38

문제 1. 로그인, 회원 가입 페이지 ( SignInActivity, SignUpActivity )

  • 비밀번호 입력 시, 보안을 유지하기 위해 마스킹 처리(***)로 표현해 주세요.
  • 로그인 이후 사용자 이름이 보이게 하는 상세로직을 정해보세요.
  • ex) data class 프로퍼티 넘기기, 캐싱 값 불러오기 등

문제 2. 로그인, 회원 가입 예외 처리

  • 예외 발생 시 사용자에게 안내 메시지를 표시하며, 가능한 다음 동작을 안내하세요.
  • 사용자의 입력 편의를 위해 이메일, 비밀번호의 유효성 검사를 실시간으로 제공해보세요.(TextWatcher, Regex Pattern등을 활용해 보세요)

1. TextWatcher

  • EditText의 값이 변경될 때마다 값을 실시간으로 관찰하면서 입력 시점에 따라 이벤트를 주는 방법
    • addTextChangedListener{} : EditText 문자 변경 시, 그 안에 들어 있는 함수에 맞춰서 실행
      • beforeTextChanged : 텍스트 변경 전 호출
      • onTextChanged : 텍스트 변경 중 호출
      • afterTextChanged : 텍스트 변경 후 호출
  • 정규 표현식
 

[Android] 자바 코틀린 (Pattern, Matcher)정규식을 사용하여 패스워드 조건을 만들어보자

동아리 프로젝트 중, 비밀번호 변경을 구현하는 단계에서 정규식을 사용할 일이 생겼습니다. 조건은 '영문, 숫자, 특수문자 중 2개를 사용하여 최소 8자리 이상'의 비밀번호를 입력해야 합니다.

itstory1592.tistory.com

 

1-2. TextWatcher 사용법

① addTextChangedListener에 TextWatcher 인터페이스 연결

  • 인터페이스이기 때문에 TextWatcher가 가지고 있는 메서드는 모두 구현
val signUpPw = findViewById<EditText>(R.id.signUpPw)

signUpPw.addTextChangedListener(object : TextWatcher{ //Ctrl+i
	override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
	}

	override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
	}

	override fun afterTextChanged(s: Editable?) {
    //비밀번호 유효성 검사 : 영문, 숫자, 특수문자 4~10자리
    //양식에 맞지 않을 시 아래에 TextView로 확인 text 띄우기
	}
})

 

 

② 정규식 표현을 담은 ragularPw() 메서드 만들기

  • trim() : 문자열 앞뒤 공백 제거
  • matches() : 문자열에서 원하는 패턴이 있는지 알아보는 매서드 (정규표현식 허용)
  • ②-1 문제점 : 비밀번호 양식에 맞지 않을 시 textView로 text를 보여줄 예정. view의 보이는지 여부를 알 수 있는 활용 메서드 = isVisible
    private fun ragularPw() {

        val pwd = signUpPw.text.toString().trim()
        val pwdPattern = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[\$@\$!%*#?&.])[A-Za-z[0-9]\$@\$!%*#?&.]{8,20}\$"
        val pattern = Pattern.matches(pwdPattern, pwd)

        if(pattern){
            signUpPwText.isVisible = false
        }else{
            signUpPwText.isVisible = true
            signUpPwText.text = getString(R.string.signUpPwmatch)
            //양식에 맞지 않을 시 확인 text
        }
    }

 

SignUpActivity.kt

class SignUpActivity : AppCompatActivity() {

    private lateinit var signUpPwText :TextView
    private lateinit var signUpPw :EditText
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sign_up)

        val signUpButton = findViewById<Button>(R.id.signUpButton)
        val signUpName = findViewById<EditText>(R.id.signUpName)
        val inputUserName = signUpName.text.toString()
        val signUpId = findViewById<EditText>(R.id.signUpID)
        val inputUserId = signUpId.text.toString()
        signUpPw = findViewById<EditText>(R.id.signUpPw)
        val inputUserPw = signUpPw.text.toString()
        signUpPwText = findViewById<TextView>(R.id.signUpPwText)

        val isName = inputUserName.isEmpty()
        val isId = inputUserId.isEmpty()
        val isPw = inputUserPw.isEmpty()

        signUpPw.addTextChangedListener(object : TextWatcher{
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }

            override fun afterTextChanged(s: Editable?) {
                ragularPw()
            }
        })

        signUpButton.setOnClickListener {

            if (isName || isId || isPw){
                showToast("입력되지 않은 정보가 있습니다")
            }
            finish()
        }
    }
    private fun showToast(message: String){
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
    private fun ragularPw() {

        val pwd = signUpPw.text.toString().trim()
        val pwdPattern = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[\$@\$!%*#?&.])[A-Za-z[0-9]\$@\$!%*#?&.]{8,20}\$"
        val pattern = Pattern.matches(pwdPattern, pwd)

        if(pattern){
            signUpPwText.isVisible = false
        }else{
            signUpPwText.isVisible = true
            signUpPwText.text = getString(R.string.signUpPwmatch)
        }
    }
}

 

3주차_ 소개글 앱 1. Intent + Toast msg (tistory.com)

 

3주차_ 소개글 앱 1. Intent + Toast msg

문제Lv1. [①] 새 프로젝트를 만들고 MainActivity의 이름을 SignInActivity로 바꿔주세요. [②] 로고 이미지는 원하는 이미지로 넣어주세요. [③] 아이디, 비밀번호를 입력 받는 EditText를 넣어주세요.(미리

hyelan-note.tistory.com

  • ③-1 문제점 : ID만 치고 버튼을 누르면 Toast메시지만 띄어져야 하지만 로그인 화면도 같이 돌아감
    • 반환값을 설정해 주지 않았음 = return@setOnClickListener
  • ③-2 문제점 : 모두 작성 후(비어 있는 부분이 없음)에도 Toast메시지가 띄어짐
    • val isName = inputUserName.isEmpty() 동작을 하여도 변수 선언 한 부분에서 true가 됨
  • ③-3 문제점 : 모두 작성 후(비밀번호 유효성 검사 마침), 버튼을 눌러도 해당 페이지에 머물러 있음
    • 인텐트를 걸어주지 않았음

SignUpActivity.kt (③ 문제점 해결)

더보기
class SignUpActivity : AppCompatActivity() {

    private lateinit var signUpPwText :TextView
    private lateinit var signUpPw :EditText
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sign_up)

        val signUpButton = findViewById<Button>(R.id.signUpButton)
        val signUpName = findViewById<EditText>(R.id.signUpName)
        val inputUserName = signUpName.text.toString()
        val signUpId = findViewById<EditText>(R.id.signUpID)
        val inputUserId = signUpId.text.toString()
        signUpPw = findViewById(R.id.signUpPw)//lateinit 초기화
        val inputUserPw = signUpPw.text.toString()
        signUpPwText = findViewById(R.id.signUpPwText)//lateinit 초기화


        signUpPw.addTextChangedListener(object : TextWatcher{
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }

            override fun afterTextChanged(s: Editable?) {
                regularPw()
            }
        })

        signUpButton.setOnClickListener {

            if (inputUserName.isEmpty() || inputUserId.isEmpty() || inputUserPw.isEmpty()){
                showToast(getString(R.string.toastnotenter))
                return@setOnClickListener
            }
            val intent = Intent(this, SignInActivity::class.java)
            startActivity(intent)
            finish()
        }
    }
    private fun showToast(message: String){
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
    private fun regularPw() {

        val pwd = signUpPw.text.toString().trim()
        val pwdPattern = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[\$@\$!%*#?&.])[A-Za-z[0-9]\$@\$!%*#?&.]{8,20}\$"
        val pattern = Pattern.matches(pwdPattern, pwd)
        Log.d("패턴","$pattern")

        if(pattern){
            signUpPwText.isVisible = false
        }else{
            signUpPwText.isVisible = true
            signUpPwText.text = getString(R.string.signUpPwmatch)
        }
    }
}
  • ④-1 문제점 : 비밀번호의 양식에 맞는 유무와 관련없이 화면전환
    • regularPw()에 대한 true, false 을 조건문으로 삽입
    • regularPw()의 반환값 필요
  • ④-2 문제점 : if (inputUserName.isEmpty() || inputUserId.isEmpty() || inputUserPw.isEmpty()) 입력하는 시점이 공란이미로 해당 조건문에 걸리게 되어 창을 다 입력하여도 조건문에 걸리게 됨
    • val inputUserName = signUpName.text.toString() 초기화 시점에 빈 값(text.toString())이 들어오게 되므로 if(signUpName.text.toString().isEmpty() ||...) 조건문 안에서 설정
class SignUpActivity : AppCompatActivity() {

    private lateinit var signUpPwText: TextView
    private lateinit var signUpPw: EditText
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sign_up)

        val signUpButton = findViewById<Button>(R.id.signUpButton)
        val signUpName = findViewById<EditText>(R.id.signUpName)
        val signUpId = findViewById<EditText>(R.id.signUpID)
        signUpPw = findViewById(R.id.signUpPw)
        signUpPwText = findViewById(R.id.signUpPwText)

        signUpPw.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }

            override fun afterTextChanged(s: Editable?) {
                regularPw()
            }
        })

        signUpButton.setOnClickListener {

            if (signUpName.text.toString().isEmpty() || signUpId.text.toString().isEmpty() || signUpPw.text.toString().isEmpty()) {
                showToast(getString(R.string.toastnotenter))
                return@setOnClickListener
            }
            if (!regularPw()){
                showToast(getString(R.string.signUpPwmatch))
                return@setOnClickListener
            }

            val intent = Intent(this, SignInActivity::class.java)
            startActivity(intent)
            finish()
        }
    }

    private fun showToast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }

    private fun regularPw():Boolean {

        val pwd = signUpPw.text.toString().trim()
        val pwdPattern =
            "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[\$@\$!%*#?&.])[A-Za-z[0-9]\$@\$!%*#?&.]{5,10}\$"
        val pattern = Pattern.matches(pwdPattern, pwd)

        if (pattern) {
            signUpPwText.isVisible = false
            return true//검사 결과 일때
        } else {
            signUpPwText.isVisible = true
            signUpPwText.text = getString(R.string.signUpPwmatch)
            return false
        }
    }
}

 

⑤ SignInActivity.kt

더보기
class SignInActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sign_in)

        val signInButton = findViewById<Button>(R.id.signInButton)
        val signUpButton = findViewById<Button>(R.id.signUpButton)
        val userId = findViewById<EditText>(R.id.userId)
        val userPw = findViewById<EditText>(R.id.userPw)

        signUpButton.setOnClickListener {
            val intent = Intent(this, SignUpActivity::class.java)
            startActivity(intent)
        }
        signInButton.setOnClickListener {

            if (userId.text.isEmpty()) {
                showToast(getString(R.string.input_id))
                return@setOnClickListener
            }
            if (userPw.text.isEmpty()) {
                showToast(getString(R.string.input_pw))
                return@setOnClickListener
            }
            
            val intent = Intent(this, MainActivity::class.java)
            startActivity(intent)
        }
    }

    private fun showToast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}

동작화면

형식에 맞는 비밀번호 형식에 맞지 않은 비밀번호