본문 바로가기
안드로이드 코틀린

[Kotlin][Android] SQLite 를 활용한 간단 회원가입/로그인 구현

by teamnova 2021. 2. 4.
728x90

이번 예제에서는 SQLite를 사용한 아주 간단한 회원가입 / 로그인 기능을 구현해볼 예정입니다. 

스틱코드를 작성해서 좀 더 빠르게 기능을 만들어 보았습니다.

 

 

stickode.com/detail.html?no=1867 - xml, activity 코드

stickode.com/detail.html?no=1868 - sqlite 관련 코드

 

관련한 모든 코드내역은 해당 링크에서 확인하실수 있습니다.

 

해당 포스팅을 즐겨찾기, 혹은 내 스틱코드로 복사하여 포스팅 후 안드로이드 스튜디오를 재시작(모든 프로젝트 종료후)

이후에 포스팅을 따라서 구현해주시기 바랍니다.

 

Activity코드, xml 코드는 파일명으로

나머지 코드는 sqlite 를 치고 자동완성하실수 있습니다. 

 

 

그래들 View-Binding 설정

 

View-Binding 설정을 해줘야합니다.

android{
	viewBinding {
        enabled = true
    }

}

 

로그인 / 회원가입 만들기 구성목록 정리

MainActivity(로그인 하는 액티비티) , RegisterActivity(회원가입 하는 액티비티),

SecondActivity(로그인 성공시 넘어가는 빈 액티비티)

LocalDB(SQLite DB 클래스) LocalDatas(SQLite DB 테이블 구조 정의된 클래스)

 

 

구성목록 만들기

 

엑티비티 클래스, xml 파일 생성

우선 위 메뉴로 들어가서 Activity와 xml 파일을 자동 생성해줍니다.

기본적으로 주어진 MainActivity 외에 회원가입을 진행할 RegisterActivity,

로그인시 넘겨줄 화면인 SecondActivity를 생성해줍니다.

 

LocalDatas object, LocalDB 클래스 생성

다음은 SQLite를 제어할 클래스,object 파일을 생성합니다.

LocalDB Class와 LocalDatas Object 를 생성하면 됩니다.

 

 

이러면 우선 기본적인 파일 생성이 끝났습니다.

 

 

로그인 / 회원가입 관련 XML 만들기

최소한의 View로 구현된 XML 파일입니다.

View-Binding을 이용했기 때문에 id 값이 변수로 적용되는점을 참고하시면 좋습니다.

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/edit_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="312dp"
        android:ems="10"
        android:hint="아이디"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/edit_pw"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:hint="비밀번호"
        android:inputType="textPassword"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edit_id" />

    <Button
        android:id="@+id/btn_login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="100dp"
        android:layout_marginLeft="100dp"
        android:layout_marginTop="24dp"
        android:text="로그인"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edit_pw" />

    <Button
        android:id="@+id/btn_register"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:layout_marginEnd="100dp"
        android:layout_marginRight="100dp"
        android:text="가입하기"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edit_pw" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

activity_register.xml

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".RegisterActivity">

    <EditText
        android:id="@+id/edit_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="312dp"
        android:ems="10"
        android:hint="아이디"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/edit_pw"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:hint="비밀번호"
        android:inputType="textPassword"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edit_id" />

    <EditText
        android:id="@+id/edit_pw_re"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:ems="10"
        android:hint="비밀번호 확인"
        android:inputType="textPassword"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edit_pw" />

    <Button
        android:id="@+id/btn_register"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:layout_marginEnd="160dp"
        android:layout_marginRight="160dp"
        android:text="회원가입"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edit_pw_re" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

스틱코드 사용하기

stickode.com/detail.html?no=1867

링크의 즐겨찾기후 스틱코드를 사용하면 다음과 같이 XML 파일을 빠르게 생성할수 있습니다.

 

기본 xml 파일을 지우고 코드를 복사 붙여넣기 하거나 스틱코드를 이용하시면 됩니다.

 

activity_main.xml, activity_register.xml  작성이 끝났습니다.

 

 

SQLite 모듈화 사용 코드 작성

 

우선 사용될 테이블부터 정의해보겠습니다. 저는 최소한의 기능만을 가지고 있기를 원하기 때문에

id, password 값만을 저장하는 테이블을 작성하겠습니다.  그외에는 _ID 라는 primary key autoincrement 인덱스 값을 

컬럼으로 가집니다.

 

 

우선 LocalDatas object 파일에 가서 패키지 부분을 제외한 나머지 코드를 지우고 스틱코드를 입력합니다.

오류가 뜨는 부분은 위에서부터 alt+enter로 지워줍니다.

 

LocalDatas 파일

object LocalDatas { //  로컬 데이터 베이스의 자료형태 정의된 object
    object userData : BaseColumns {  //  users 라는 DB 테이블의 데이터 컬럼 내용 정리
        const val TABLE_NAME = "users"
        const val COLUMN_NAME_ID = "id"        							//	임의의 컬럼명 작성
        const val COLUMN_NAME_PASSWORD = "password"  //	임의의 컬럼명 작성
    }
    object Groups :BaseColumns{ // 만약 그룹에 관련한 DB 형식을 지정하고 싶다면 동일한 방식으로 추가합니다.

    }
}

해당 코드에서 테이블명과 컬럼명을 미리 정의해둡니다. 사용하고자하는 테이블 형태에 맞게 추가하시면 됩니다.

 

LocalDB 파일

LocalDB.kt 파일내용을 모두 지우고 sqlite 를 입력후 스틱코드를 입력합니다.

 

마찬가지로 오류가 뜨는 부분은 위에서부터 alt+enter로 지워줍니다.

 

최초실행(혹은 sqlite버전업)시 아까 정의해둔 컬럼값들을 가지고 DB 테이블을 생성합니다.

 

    fun createDatabase(db: SQLiteDatabase) {
        // 테이블이 존재하지 않는경우 생성
        var sql: String = "CREATE TABLE if not exists ${LocalDatas.userData.TABLE_NAME} (" +
                "${BaseColumns._ID} integer primary key autoincrement," +
                "${LocalDatas.userData.COLUMN_NAME_ID} varchar(15)," +
                "${LocalDatas.userData.COLUMN_NAME_PASSWORD} varchar(20)"+
                ");"

        db.execSQL(sql)
    }

LocalDatas에 맞게 작성해 주면 됩니다. BaseColumns._ID값은 위에서 말한 PK 인덱스값입니다.

 

다음은 DB내에 로그인, 회원가입 함수, 같은 ID의 유저가 존재하는지 여부를 확인하는 함수 를 추가해보겠습니다.

 

우선 id, password 두개의 값을 받아서 SQLite 테이블에 insert하는 함수를 작성해 보겠습니다.

    fun registerUser(id:String, password:String){
        
    }

sqlite insert를 선택합니다.

자동완성된 상태에서 컬럼명을 LocalDatas의 값으로, insert할 값을 id, password로 변경해줍니다.

 

fun registerUser(id: String, password:String){
	val db =this.writableDatabase
	val values = ContentValues().apply {// insert될 데이터값
		put(LocalDatas.userData.COLUMN_NAME_ID, id)
		put(LocalDatas.userData.COLUMN_NAME_PASSWORD, password)
	}
	val newRowId = db?.insert(LocalDatas.userData.TABLE_NAME, null, values)
	// 인서트후 인서트된 primary key column의 값(_id) 반환.
}	

 

이제 아이디를 확인하는 함수를 만들어 보겠습니다. 아이디값을 받아서 값이 존재여부에 따라 true/false를 반환합니다.

마찬가지로 확인할 값에 맞게 코드를 변경해줍니다.

    fun checkIdExist(id: String): Boolean {
        val db = this.readableDatabase

        // 리턴받고자 하는 컬럼 값의 array
        val projection = arrayOf(BaseColumns._ID)
        //,LocalDatas.userData.COLUMN_NAME_ID, LocalDatas.userData.COLUMN_NAME_PASSWORD)


        //  WHERE "id" = id AND "password"=password 구문 적용하는 부분
        val selection = "${LocalDatas.userData.COLUMN_NAME_ID} = ?"
        val selectionArgs = arrayOf(id)

//         정렬조건 지정
//        val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC"

        val cursor = db.query(
            LocalDatas.userData.TABLE_NAME,   // 테이블
            projection,             // 리턴 받고자 하는 컬럼
            selection,              // where 조건
            selectionArgs,          // where 조건에 해당하는 값의 배열
            null,                   // 그룹 조건
            null,                   // having 조건
            null               // orderby 조건 지정
        )
        if(cursor.count>0){//  반환된 cursor 값이 존재
            return true;
        }else{//반환된 cursor 값이 없음
            return false;
        }

    }

 

마지막으로 로그인 확인 함수입니다. id, password를 받아서 db값과 비교후 유저의 존재여부를 true/false로 반환합니다.

마찬가지로 확인할 값에 맞게 코드를 변경해줍니다.

    fun logIn(id: String, password:String): Boolean {
        val db = this.readableDatabase

        // 리턴받고자 하는 컬럼 값의 array
        val projection = arrayOf(BaseColumns._ID)
            //,LocalDatas.userData.COLUMN_NAME_ID, LocalDatas.userData.COLUMN_NAME_PASSWORD)


    //  WHERE "id" = id AND "password"=password 구문 적용하는 부분
        val selection = "${LocalDatas.userData.COLUMN_NAME_ID} = ? AND ${LocalDatas.userData.COLUMN_NAME_PASSWORD} = ?"
        val selectionArgs = arrayOf(id,password)

//         정렬조건 지정
//        val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC"

        val cursor = db.query(
            LocalDatas.userData.TABLE_NAME,   // 테이블
            projection,             // 리턴 받고자 하는 컬럼
            selection,              // where 조건
            selectionArgs,          // where 조건에 해당하는 값의 배열
            null,                   // 그룹 조건
            null,                   // having 조건
            null               // orderby 조건 지정
        )
        if(cursor.count>0){//  반환된 cursor의 0번째 값이 null이면
            return true;
        }else{
            return false;
        }

    }

 

이상으로 회원가입에 사용할 SQLite 모듈의 기능 작성이 끝났습니다. 

 

액티비티 코드 작성

 

MainActivity.kt

패키지 부분을 제외한 나머지 코드를 지우고

다음과 같이 스틱코드를 입력하면 됩니다. 오류가 뜨는 부분은 제일 위쪽부터 마우스를 올리고 alt+enter를 누르면 전부 사라집니다. RegisterActivity도 마찬가지로 작성해주시면 됩니다.

 

각 액티비티(회원가입,로그인)는 아까 정의해둔 LocalDB 클래스를 초기화하여 이용하게 됩니다. 

 

 //MainActivity.kt
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        binding = ActivityMainBinding.inflate(layoutInflater) // 뷰바인딩
        val view = binding.root
        setContentView(view)
        localDB= LocalDB(this, DATABASE_NAME,null, DATABASE_VERSION) // SQLite 모듈 생성
        binding.btnLogin.setOnClickListener { view->		//	로그인 버튼 클릭 리스너
            val id = binding.editId.text.toString()
            val passwd = binding.editPw.text.toString()
            val exist = localDB.logIn(id,passwd) // 로그인 실행
            if(exist){//로그인 성공
                val intent =Intent(this,SecondActivity::class.java)
                startActivity(intent)
            }else{//실패
                Toast.makeText(this@MainActivity, "아이디나 비밀번호가 틀렸습니다.", Toast.LENGTH_SHORT).show()
            }
        }
        binding.btnRegister.setOnClickListener { view->		//회원가입화면으로 이동
            val intent =Intent(this,RegisterActivity::class.java)
            startActivity(intent)
        }
    }

 

각 코드에 대한 설명은 주석을 참고해주세요.

 

 

액티비티의 작성까지 끝났다면 기본적인 회원가입을 시험해봅니다. 문제없이 동작하는것을 확인하실수 있습니다.