GDSC HUFS 3기/Android with Kotlin Team 2

[2팀]16 Android와 SQLite 데이터 베이스

어둠의 그림자 2021. 11. 25. 04:13

dk이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.

작성자 : 김민서

개발환경은 Windows, Android Studio입니다

0. 데이터베이스란?SQLite란?

:일반적으로 컴퓨터 시스템에 전자적으로 저장되는 구조화된 정보 또는 데이터의 조직화된 모음

앱에서 사용자 데이터를 저장하고 로드하는것은 굉장히 흔한 일이다. 앱 실행중에 메모리에 저장된 데이터는

앱 종료시 날아가기때문에, 앱 실행여부와 상관없이 영구적으로 데이터를 보관할수 있는 기능이 필요하다

(메모장, 메신저, sns, 등등..)

- SQLite는 데이터베이스를 관리하는 시스템

 

데이터베이스 정의

앱에서 데이터베이스 필요성

1. SQLite 사용하기 : 메모장 만들기_SQLite 클래스 생성

데이터베이스를 생성,입력,조회,삭제,변경해주는 클래스를 생성해주자

해당 클래스는 SQLiteOpenHelper를 상속받는다.

class SqliteHelper(context: Context, name:String, version:Int)
	:SQLiteOpenHelper(context: Context, name:String, factory:null, version:Int){
	override fun onCreate(db:SQLiteDatabase?){
    	val create = "create table memo (no interger primary key, content text, datetime integer)"
        db?.execSQL(create)
    }
    override fun onUpgrade(db:SQLiteDatabase?, oldVersion: Int, newVersion: Int){
    	//테이블에 변경사항이 있을경우 호출됨
        //SQLiteHelper()의 생성자를 호출할 때 기존 데이터베이스와 version을 비교해서 높으면
        //호출된다.
    }
  
}

1.1 데이터베이스 생성

파라미터

SQLiteOpenHelper 함수는 파라미터로 4개를 요함(context: Context, name:String, factory:null, version:Int)

이중 factory를 제외한 나머지 3개의 변수는 생성해준 클래스 SqliteHelper 의 생성자를 선언할때 파라미터로 받아주어야한다. 

필수 멤버함수

생성을 받았는데 인자로 받는 이름이 원래없엇ㄱ다-> 내장디렉토리 생성을 위해oncreate함수 호출

생성을 받았는데 같은이름으로 있는 내장디렉토리가 이미 존재하지만 버전이 다르다(더 높다)
->onupgrade 호출

onCreate함수

val create = "create table 테이블명 (column 명 형 primary key, content text, datetime integer)"

해당 프로젝트에서는 속성, 텍스트(메모할내용), 기록시간; 총 3가지의 데이터를 요구하기때문에 괄호안에 넣어주었다

+primary key를 붙여주면 null형으로 입력해도 자동으로 1,2,3,4..로 커진다

 


 

주로 상속받은 SQLiteOpenHelper 클래스의 내장함수를 사용하여 데이터베이스를 가공하여 가져온다.                                                   


1.2 입력함수

:값을 입력해주는 함수

fun insertMemo(memo:Memo){
         //db 가져오기
        val wd = writableDatabase
        //Memo를 입력타입으로 변환
        val values = ContentValues()
        values.put("content",memo.content)
        values.put("datatime", memo.datetime)
        //db에 넣기
        wd.insert(table:"memo", nullColumnHack:null, values)
        //db 닫기
        wd.close()
}

크게 db 가져오기, 데이터 입력타입으로 변환하기, db에 넣기, db 닫기;총 4단계로 구성된다.

+ 앞서 3가지 데이터를 요구하는데, 매번 인자로 넣어주기 번거로우므로 Memo라는 data class를 선언해주어서 인자를 받아주고, 파라미터로 넣어줬다

db 가져오기

상속받은 SQLiteOpenHelper 클래스의 함수도 갖다 쓸수있기때문에 writableDatabase를 써주자

db에 넣기

내장함수 insert의 파라미터로 1. 테이블명, 2.nullColumnHack 3.테이블에 들어갈값 가 들어간다

(2번은 강의에서 생략됐는데 3번 value값이 비어있을경우 들어가는 값이다)

여기서 3번은 ContentValues()로 값을 만들어주어야한다(걍 아무거나 갖다쓰면 안됌)

데이터 입력타입으로 변환하기;ContentValues()

values를 ContentValue 함수로 입력타입으로 변환해준다.

그후 put으로 값을 넣어준다

db 닫기

닫아주자(꼭)

파이썬 파일입출력처럼 열었으면 닫아주는 그런것같다

1.3 조회함수

: 값을 조회하여 리턴해주는 함수

 fun selectMemo():MutableList<Memo>{
       val list = mutableListOf<Memo>()
        val select = "select * from memo"
        val rd = readableDatabase
        val cursor = rd.rawQuery(select, selectionArgs:null)
        while(cursor.moveToNext()){
         val no = cursor.getString(cursor.getColumnIndex(columnName: "no"))
         val content = cursor.getString(cursor.getColumnIndex(columnName: "content"))
         val datetime = cursor.getLong(cursor.getColumnIndex(columnName: "datetime"))
         
         val memo = Memo(no, content, datetime)
         list.add(memo)
        }
        cursor.close())
        rd.close()
        return list
        
    }

전체를 돌면서 조회하는 방식

0. 반환할 list 선언 

1. select query 날리기

" select (날릴내용) from memo" <- 어차피 전부다 날릴것이므로 *표시하면 된다고 한다

val select = "select * from memo"

2. readableDatabase로 가져오기

3. rawQuery로 줄단위로 입력값 받아오기 <- while문 돌면서 

4. cursor,rd 닫아주기

1.3 수정함수

:값을 수정해주는 함수

fun updateMemo(memo:Memo){
       val wd = writeableDatabase
        
        val values = ContentValues()
        values.put("content", memo.content)
        values.put("datetime", memo.datetime)
        
        wd.update(table:"memo",values, whereClause "no = ${memo.no}", whereArgs:null)
        wd.close()
    }

몇번째 커서값을 수정할건지 넣어주어야함(no)

1.4 삭제함수

fun deleteMemo(memo:Memo){
	val wd = writableDatabase
    //val deleteMemo = "delete from memo where no = ${memo.no}"
    //wd.exeSQL(delete)
    wd.delete(table="memo",whereClause:"no={memo.no}", whereArgs:null)
    wd.close()
    
}

마찬가지로 몇번째 커서값을 수정할건지 넣어주어야함(no)

1. 쿼리를 사용하는 방식(주석)

2. delete 내장함수를 사용하는방식

2. 기능 사용하기_리사이클러뷰

리사이클러뷰를 활용하여 앞서 만들어준 SqliteHelper로 메모장을 만들어보자

mainActivity.xml

리사이클러뷰를 이용하여 메모리스트를 화면에 표시해주었고,

텍스트뷰와 버튼을 이용하여 메모 저장버튼과 입력창을 만들어주었다.

item_recycler.xml

layout에서 위와 같은 새 리소스파일을 만들어주었고,layout height를 조정하여 아래와 같이 만들어주었다.

생성해준 item_recycler 파일을 리사이클러뷰에서 listitem으로 지정해준다 

RecyclerAdapater.kt - class Holder를 만들어주고, setMemo 함수에서 앞선 item_recycler의 텍스트의 값에 데이터를 넣어주자

//RecyclerAdapter.kt
class Holder(itemView:View):RecyclerView.ViewHolder(itemView){
	fun setMemo(memo:Memo){
    	itemView.textNo.text = "${memo.no}"
        itemView.textContent.text = "${memo.content}"
        val sdf = SimpleDateFormat(pattern:"yyyy/mm/dd hh:mm")
        val datetime = sdf.format(memo.datetime)
        itemView.textDatetime.text = "${datetime}"
        }

}

Adapter class;onCreateViewHolder 함수에서 LayoutInfalter를 활용하여, Holder 생성자를 만들어주고 onBindViewHolder 함수에서 데이터들을 넣어줄수있도록 holder의 setMemo함수를 호출한다.

class RecyclerAdapter: RecyclerView.Adapter<Holder>() {
    var listData = mutableListOf<Memo>()
    //item_recyler에서 요소들을 가져와서 Holder 파라미터로 넣어주자
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_recycler,parent,false)
        return Holder(view)
    }
    //실제 화면에 그려주는 함수;Adapter
    override fun onBindViewHolder(holder: Holder, position: Int) {
        val memo = listData.get(position)
        holder.setMemo(memo)
    }
    //몇줄을 그릴꺼야! 미리 알려주기
    override fun getItemCount(): Int {
        return listData.size
    }

}

이를 main에서 활용하여 코드를 작성해주고, button의 기능도 설정해준다.