728x90
반응형
SMALL

1. 데이터베이스 (Database, DB)

앱 실행 중 메모리에 저장된 데이터는 앱의 실행이 종료되면 모두 사라지기 때문에, 앱의 실행 여부와 관계없이 유지되어야 하는 데이터는, 영구적 보관이 가능한 저장소에 저장을 해두어야 데이터가 삭제되는 것을 방지할 수 있습니다.

 

이를 위한 방법은 여러 가지가 있습니다.  여기서는 4가지만 언급을 드리겠습니다.                                                 1. SharedPreferences를 사용하여 앱의 설정 정보와 같은 간단한 데이터를 보관하는 방법,                                     2. 파일 입출력 API를 통해 기기의 저장소(storage)에 데이터를 직접 읽고 쓰는 방법,                                             3. SQL을 사용하여 로컬 데이터베이스에 데이터를 관리하는 방법,                                                                   4. 인터넷을 통해 연결된 서버와 데이터를 주고받는 방법 등, 다양한 방법                                                          위의 4가지 방법으로 데이터를 저장하고 저장된 데이터를 읽어올 수 있습니다.

 

위의 방법 중 어떤 것을 사용할 것인지는, 앱에서 관리할 데이터의 크기와 형식, 범위, 그리고 각 방법의 장단점을 고려하여 결정할 수 있습니다. 하지만 그 중 가장 범용적이고, 보편적인 방법을 사용해야 한다면, 데이터베이스를 사용하여 데이터를 관리하는 방법을 선택할 수 있습니다.

 

<데이터베이스 사용 시 장점>

데이터베이스를 사용하면, SharedPreferences의 키-값(key-value) 저장 형식의 한계점 극복, 파일 입출력 방법의 저장 형식 설계에 따른 복잡성 해소, 서버 통신 방법에서 요구되는 서버 구축 비용 절감 등의 장점을 획득할 수 있습니다.

 

데이터베이스의 동작을 이해하기 위한 개념 학습, 데이터베이스에 저장될 데이터를 위한 스키마 구조 설계에 대한 고민, 데이터베이스에서 값들을 읽고 쓰기 위해 사용되는 SQL에 대한 사용법 숙지 등, 다른 방법들에 비해 비교적 선행적으로 준비해야 할 내용들이 많기는 합니다. 그러나 한번 익혀두면 큰 변화없이 지속적으로 활용할 수 있는 내용들이므로, 데이터베이스를 접하는 초반의 생소함만 극복하면, 프로그램 개발에 쉽게 적용할 수 있습니다.

 

1.1 데이터베이스 관리 시스템(Database Management System, DBMS)

데이터베이스를 만들고, 데이터를 저장하거나 관리할 수 있도록 만들어주는 소프트웨어를 데이터베이스 관리 시스템(DBMS)이라고 하는데, 현재 제품으로 출시된 데이터베이스 관리 시스템(DBMS, Database Management System)은 그 종류가 매우 다양합니다. Oracle, Mysql, MS-SQL, SQLite 등 개발자라면 누구나 한번 쯤은 이름을 들어본 적 있을 유명한 데이터베이스 시스템이 출시, 판매되고 있죠.

 

어떤 데이터베이스 관리 시스템(DBMS)을 사용할 것인지는 데이터베이스 관리 시스템에서 제공되는 기능과 데이터 처리 용량, 사용 편의성, 기능 확장성, 시스템 복잡성, 소요 비용 등의 여러 요소를 고려하여 선택할 수 있는데, 다른 프레임워크에 비해 성능과 용량 측면에서 제약이 존재하는 안드로이드에서는 기본적으로 SQLite를 사용합니다.

2. SQLite. (www.sqlite.org)

SQLite(www.sqlite.org)는 서버 단위에서 대용량 데이터를 처리하기 위한 용도보다는, 단일 응용 프로그램에서 비교적 적은 용량의 데이터를 처리하는데 적합한 데이터베이스 관리 시스템(DBMS)입니다. 즉, 안드로이드 앱과 같은 소규모의 프로그램에서 데이터베이스 관리가 필요한 경우 사용하기 적합한 데이터베이스 관리 시스템이죠.

 

SQLite를 사용하기 위해서 별도의 복잡한 시스템, 서비스, 프로그램이 요구되지 않습니다. 단지 SQLite를 위한 API 함수를 호출하는 것만으로 데이터베이스를 위한 기능을 사용할 수 있으며, 데이터베이스 API를 통해 관리되는 모든 데이터는 하나의 파일에 저장됩니다. (그래서 SQLite의 데이터베이스를 백업하는 작업은 하나의 데이터베이스 저장 파일을 복사하는 것으로 끝납니다.)

3. 안드로이드에서 SQLite를 사용하기 전 준비 사항.

안드로이드에서 SQLite를 사용하기 위해 기본적으로 알아야 할 내용은 당연히 SQLite를 다루는 클래스에 대한 구조와 API 함수들의 사용법입니다. 하지만 그 전에, 데이터베이스에 대한 기본 개념과 구성 요소 그리고 데이터베이스의 데이터를 관리하는데 사용되는 SQL(Structured Query Language) 등을 알아둘 필요가 있습니다.

 

데이터베이스에 대한 개념과 이론을 설명한 문서에는 매우 방대한 내용이 포함되어 있습니다.                                모든 내용을 제대로 이해하고 습득하려면, 몇 일이 걸릴지, 몇 달이 걸릴지 알 수 없을만큼 많은 내용을 포함하고 있죠. 여기서 그 모든 내용을 설명하려면, 공간도 한참 부족할 뿐더러, 원래 설명하려고 했던 안드로이드에서 데이터베이스를 다루는 내용 따위는 안드로메다로 날아가 버릴 수도 있습니다.

 

4. 관계형 데이터베이스(Relational Database)

안드로이드에서 기본적으로 제공하는 SQLite는 관계형 데이터베이스 관리 시스템(Relatinoal Database Management System, RDBMS)의 한 종류입니다.

4.1 관계형 데이터베이스에서 데이터 구조화하기.

관계형(Relational) 데이터베이스에서는 데이터베이스 내에 데이터를 저장할 때, 데이터들이 공통적으로 포함하는(그리고 논리적 구분이 가능한) 속성들을 식별한 다음, 각 데이터를 앞서 식별된 속성에 해당하는 값들로 나열하여 저장합니다. 

 

<예시>

과일을 판매하는 과일 가게를 떠올려보죠. 과일 가게에는 아래 그림과 같이, 종류가 다른 과일들이 각각의 바구니에 담겨져 있습니다.

 

고객의 요구사항은, "가게에 남아 있는 과일의 종류가 어떤 것인지, 종류 별로 몇 개가 남아 있는지를 관리하는 것"입니다. 이 정보를 저장할 때 관계형 데이터베이스를 사용한다면, 데이터베이스에 데이터가 구조화되는 과정은 아래와 같습니다.

 

4.1.1. 저장하고자 하는 데이터가 공통적으로 가지는 속성 식별하기.

먼저, 데이터가 가지는 공통 속성을 식별해야 합니다. 앞서 한 문장으로 기술된 요구사항에 적혀 있듯이 "1.과일의 종류"와 "2.남은 갯수"를 데이터가 가진 공통적인 속성이 될 수 있죠. 그리고 속성을 식별하고 나면, 속성들을 가로로 나열합니다.

4.1.2 데이터들을 속성에 해당하는 값들로 나열하기.

속성이 식별되고나면, 다음 단계는 각 데이터를 속성에 해당하는 값들로 나열하는 것입니다. 여기서 데이터는 세로 방향으로 나열합니다.

 

속성의 식별과 속성에 따른 데이터 나열이 완료되면 아래 그림과 같이 테이블 형태의 구조가 완성됩니다.

 

4.1.3 속성 식별 과정에서의 주의사항.

속성의 식별은 데이터베이스의 데이터 구조화 과정에 있어 가장 중요한, 그리고 데이터베이스 전체 용량을 결정하는 가장 중요한 요소입니다. 

 

<주의사항>

먼저, 속성은 그 값의 크기와 관계없이 데이터들에 공통적으로 포함되는 것이어야 합니다. 만약 위의 과일 가게 관리 예제에서 "다리 갯수"라는 속성을 식별했다고 가정했을 때, 모든 과일은 다리를 가지고 있지 않으므로 "다리 갯수"라는 속성은 전혀 의미가 없는 속성입니다.

 

 

다음으로, 속성의 값들은 논리적 구분이 가능해야 합니다. 위의 예제에서, "식물", "동물" 등으로 구분되는 "생물 분류"를 공통 속성으로 식별했다고 가정한다면, 분명 데이터가 가지는 공통 속성임에는 분명하지만, 모든 데이터가 "식물" 값을 가지게 되어 논리적 구분이 불가능합니다. 즉, 모든 데이터가 같은 값을 가지므로 의미없는 속성이 되는 것입니다.

 

또 한가지, 속성은 데이터베이스를 사용하는 곳에서 기능적으로 유의미해야 합니다. 예제에서, 추가적으로 과일의 "색상" 속성을 추가하면, "색상" 속성은 모든 과일이 공통적으로 포함하고 있고, 각 과일은 저마다의 색을 가지고 있기 때문에, 논리적 구분도 가능합니다. 그런데 새롭게 구분된 "색상"은 요구사항에 적용되지 않는 속성입니다. 즉, 속성을 식별하고 값을 채운다고 해도 전혀 사용되지 않습니다. 과일 종류 별 남은 갯수를 관리하는 게 유일한 목적이기 때문에, "색상"은 무의미한 속성이 되는 것입니다.

 

4.2 관계형 데이터베이스 용어

관계형 데이터베이스에서 데이터를 속성과 데이터 값으로 구조화하면 2차원 표 형태의 구조가 만들어지는 것을 확인하였습니다. 속성은 가로 방향으로, 데이터 값은 세로 방향으로 나열되었습니다. 데이터들을 속성에 따라 값으로 나열한 2차원 표 형태를, 관계형 데이터베이스에서는 "관계(Relation)"라고 부릅니다. 즉, 관계형 테이터베이스에서 데이터를 구조화한다는 것은, 속성과 그 데이터 값들의 "관계(Relation)"를 찾아내고 그것을 표 모양의 구조로 도식화하는 것이라고 볼 수 있는 것입니다.

 

하지만 "관계(Relation)"라는 용어는, 설명을 아무리 쉽게 해도 그 의미가 쉽게 전달되지 않을 것 입니다. 데이터베이스와 관련하여 범용적으로 사용되지 않은 용어라서 더욱 어려울 것이라 생각이 듭니다. 그래서 일반적으로 "관계(Relation)" 대신 2차원 표 모양에 기인한 "테이블(Table)" 이라는 용어가 더 익숙하게 사용됩니다.

 

그리고 관계형 데이터베이스의 "속성(Attribute)" 또한, (행/열에서 '열'을 의미하는) "컬럼(Column)" 또는 "필드(Field)"라는 용어로 더 자주 사용됩니다. 세로로 나열되는 데이터의 값은 관계형 데이터베이스 이론에서는 "튜플(Tuple)"이라는 용어로 불리지만, 일반적인 경우에 (행/열에서 '행'을 의미하는) "로우(Row)" 또는 "레코드(Record)"라는 용어로 더 많이 사용됩니다.

 

여러 용어들이 혼재되어 좀 헷갈릴수도 있습니다. 하지만 관계형 데이터베이스 이론에서 정의하는 용어인 "속성(Attribute)", "튜플(Tuple)" 등은 일반적인 상황에서는 잘 사용되지 않으므로 개념적으로만 알아두시고, "컬럼(Column)" 또는 "필드(Field)""로우(Row)" 또는 "레코드(Record)" 등의 용어가 더 범용적으로 사용된다는 것을 기억해두시기 바랍니다.

4.3 데이터베이스 스키마(Schema)

앞서 설명한 범용 용어를 사용하여 데이터베이스 구조화를 정리하면, 관계형 데이터베이스에 데이터를 저장하기 위한 가장 기본이 되는 단위는 테이블(Table)이며, 테이블(Table)은 컬럼(Column)과 로우(Row)로 구성되어있습니다.

 

그래서 개발자가 데이터베이스를 사용하여 데이터를 저장할 때 첫 번째로 해야 할 일은, 관리하고자 하는 데이터의 공통 속성을 식별하여 컬럼(Column)으로 정의하고 테이블(Table)을 만드는 것입니다. 일단, 테이블이 만들어지면 새로운 데이터를 추가할 수 있고, 추가된 하나의 데이터는 하나의 로우(Row)로 삽입됩니다.

 

 

그런데 데이터베이스 내에 하나의 테이블만 만들어 사용하는 경우는 거의 없습니다. 물론 요구사항이 매우 단순한 경우라면 테이블 하나에 관리하고자 하는 모든 정보를 저장할 수 있겠지만, 대부분의 경우 하나의 테이블만으로는 고객의 요구사항을 만족시키기 힘들죠. 때론 구조의 복잡도에 따라 수십 개 또는 수백 개의 테이블이 만들어지기도 합니다.

 

데이터 저장의 가장 기본 단위인 테이블과 (여기서 설명하지 않지만) 인덱스, 뷰, 권한 등과 같은 데이터베이스 요소들은 데이터베이스 내부에서 어떻게 만들어지고 관리되는 것일까요? 음, 구체적인 구현 방법은 정확히 알 수 없지만, 데이터베이스 내부에서 관리되는 요소들은 개념적으로 스키마(Schema)라는 것으로 관리됩니다.

 

스키마(Schema)라는 단어의 의미를 정의해보자면, "데이터베이스의 테이블 구조 및 형식, 관계 등의 정보를 형식 언어(formal language)로 기술한 것"을 의미합니다. 즉, 스키마는 데이터베이스의 설계도 역할을 수행하며, 테이블, 인덱스, 뷰 등의 구조에 대한 모든 정보를 가진 것을 말하죠.

 

스키마(Schema)가 데이터베이스의 설계도와 같은 역할을 수행하고, 형식 언어(formal language)로 기술된다는 것은 데이터베이스 관리에 있어서 상당한 이점을 제공해줍니다. 하나의 데이터베이스 스키마(Schema)를 가지면, 다른 시스템에 똑같은 데이터베이스를 만들 수 있다는 것이니까요. 하지만 이를 데이터베이스 백업과 혼동해서는 안되고, 단지 동일한 구조의 데이터베이스를 새로 생성할 수 있다는 의미입니다.

 

4.4 SQL(Structured Query Language)

SQL(Structured Query Language)은 관계형 데이터베이스 관리 시스템에서 데이터를 관리하기 위해 사용되는 표준 프로그래밍 언어(Language)입니다. 데이터베이스 스키마 생성 및 수정, 테이블 관리, 데이터 추가, 수정, 삭제, 조회 등, 데이터베이스와 관련된 거의 모든 작업을 위해 사용되는 언어이므로, 관계형 데이터베이스를 다루기 위해서는 필수적으로 알아야 할 언어입니다.

 

SQL이 프로그래밍 언어이긴 하지만 Java 또는 C 언어처럼 프로그램을 개발하기 위한 목적이 아닌, 데이터베이스를 관리하기 위해 사용되는 특수 목적 언어입니다.

 

SQL은 대화식으로 동작합니다. 하나의 기능을 수행하면 실행 결과를 확인한 다음 다른 기능을 실행하는 형태로 동작하는 것입니다. 예를 들어 테이블에 데이터를 추가하고 조회하는 경우, INSERT 명령으로 시작되는 SQL 문장을 실행하고 그 결과가 리턴되면, 그 다음 데이터 조회를 위한 SELECT 명령을 실행할 수 있습니다.

 

SQL에 정의된 명령어의 종류는 크게 세 가지로 나뉩니다. 이는 명령어가 사용되는 목적 및 대상에 따라 분류되며, 데이터 정의 언어(DDL, Data Definition Language), 데이터 처리 언어(DML, Data Manipulation Language), 데이터 제어 언어(DCL, Data Control Language)가 바로 명령어의 세 가지 종류입니다.

 

데이터 정의 언어(DDL, Data Definition Language)는 말 그대로 데이터의 구조를 정의하는 것을 말합니다. 테이블(TABLE), 인덱스(INDEX), 트리거(TRIGGER) 등의 개체를 만들고 관리하는데 사용되는 명령들이 데이터 정의 언어에 속하며, CREATE, ALTER, DROP 등이 있습니다.

DML 명령설명

CREATE 테이블(TABLE), 인덱스(INDEX) 등의 데이터베이스 개체 생성.
ALTER CREATE로 생성된 테이블, 인덱스 등의 데이터베이스 개체 수정.
DROP CREATE 명령으로 생성된 데이터베이스 개체 삭제.
[SQL-예제] 데이터 정의 언어(DDL) 사용 예.
    /* CREATE 명령으로 FRUITSHOP_T 테이블 생성. */
    CREATE TABLE FRUITSHOP_T (NAME TEXT NOT NULL, COUNT INTEGER) ;

    /* ALTER 명령으로 FRUITSHOP_T 테이블에 새로운 컬럼(COLUMN) 추가. */
    ALTER TABLE FRUITSHOP_T ADD SIZE INTEGER ;

    /* DROP 명령으로 FRUITSHOP_T 테이블 삭제. */
    DROP TABLE FRUITSHOP_T ;

데이터 조작 언어(DML, Data Manipulation Language)는 데이터 정의 언어로 생성된 개체(테이블 등)에 데이터를 추가하거나, 수정, 삭제, 조회 등의 기능을 수행할 때 사용합니다. 주로 사용되는 명령에는 INSERT, UPDATE, DELETE, SELECT 등이 있습니다.

DML 명령설명

INSERT 테이블(Table)에 하나 이상의 데이터 추가.
UPDATE 테이블(Table)에 저장된 하나 이상의 데이터 수정.
DELETE 테이블(Table)의 데이터 삭제.
SELECT 테이블(Table)에 저장된 데이터 조회.
[SQL-예제] 데이터 조작 언어(DML) 사용 예.
    /* INSERT 문을 사용하여 FRUITSHOP_T 테이블에 데이터 추가. */
    INSERT INTO FRUITSHOP_T (NAME, COUNT) VALUES ('apple', 2) ;

    /* UPDATE 문으로 FRUITSHOP_T 테이블 데이터 수정. */
    UPDATE FRUITSHOP_T SET COUNT=3 WHERE NAME='apple' ;

    /* DELETE 명령으로 FRUITSHOP_T 테이블의 데이터 삭제. */
    DELETE FROM FRUITSHOP_T WHERE NAME='apple' ;

    /* SELECT 명령으로 FRUITSHOP_T 테이블의 데이터 조회 */
    SELECT * FROM FRUITSHOP_T ;

데이터 제어 언어(DCL, Data Control Language)는 데이터를 다루는데 있어 권한을 설정하거나, 데이터의 무결성 처리 등을 수행하는데 사용되는 언어입니다. GRANT, BEGIN, COMMIT, ROLLBACK 등이 이에 속합니다.

DCL 명령설명

GRANT 데이터베이스 개체(테이블, 인덱스 등)에 대한 사용 권한 설정.
BEGIN 트랜잭션(Transaction) 시작.
COMMIT 트랜잭션(Transaction) 내의 실행 결과 적용.
ROLLBACK 트랜잭션(Transaction)의 실행 취소.
[SQL-예제] 데이터 제어 언어(DML) 사용 예.
    /* GRANT 명령을 사용하여 FRUITSHOP_T 테이블 권한 설정. */
    GRANT ALL ON FRUITSHOP_T TO PUBLIC ;

    /* COMMIT 멸영으로 트랜잭션 실행 결과 적용. */
    BEGIN TRANSACTION ;
    INSERT INTO FRUITSHOP_T (NAME, COUNT) VALUES ('banana', 3) ;
    INSERT INTO FRUITSHOP_T (NAME, COUNT) VALUES ('orange', 5) ;
    COMMIT TRANSACTION ;

    /* ROLLBACK 명령으로 트랜잭션 취소. */
    BEGIN TRANSACTION ;
    DELETE FROM FRUITSHOP_T ;
    ROLLBACK TRANSACTION ;

5. 참고.

 

728x90
반응형
LIST
728x90
반응형
SMALL

1. 관계형 데이터베이스(Relational Database)

안드로이드에서 데이터베이스를 사용하는 구체적인 방법을 설명하기에 앞서, 데이터베이스 프로그래밍을 하기 위해서 미리 알아두면 좋을 데이터베이스 이론에 대해 설명드리겠습니다.

 

<데이터베이스 이론>                                                                                                                           안드로이드에서 기본적으로 제공되는 데이터베이스인 SQLite가 관계형 데이터베이스 구조를 따르고, 또한 표준으로 정의된 SQL 기능의 대부분을 지원하기 때문에, 안드로이드에서 SQLite를 사용하기 위해 데이터베이스 이론을 공부한다는 것은 상당한 도움이 되는 과정이라 할 수 있습니다.

<안드로이드에서 제공되는 SQLite 관련 클래스에 대한 구조 및 API 함수들의 사용법>                                          대부분의 내용이 데이터베이스 처리를 위한 SQL 문과 그 SQL을 실행하는 함수에 대한 설명, 그리고 함수의 실행 결과를 확인하는 방법들로 구성될텐데요, 여기서 설명하는 내용 정도만 이해하고 있어도 실질적인 구현 과정에서 큰 어려움없이 SQLite 를 사용할 수 있을거라 생각합니다.

 

<SQLite를 다루는 클래스에 대한 구조와 API 함수들의 사용법에 대해 설명>

2. 안드로이드에서 SQLite 사용하기.

안드로이드에서 기본적으로 제공되는 데이터베이스는 SQLite입니다. SQLite는 비교적 작은 규모의 안드로이드 앱에서 사용하기 적합한 데이터베이스로써, SQLite에서 제공하는 몇 가지 API 함수를 호출하는 것만으로 데이터베이스 기능을 사용할 수 있는 특징이 있습니다.

 

안드로이드의 SQLite 관련 클래스 및 API 함수는 "android.database.sqlite" 패키지에 들어 있으며, 그 중 가장 중요한 클래스는 SQLiteDatabase 클래스입니다.

 

아래 그림은 앞으로 설명할 SQLite 데이터베이스를 사용하는 과정을 요약한 것입니다.

 

2.1 SQLiteDatabase 클래스

SQLite 데이터베이스에 데이터를 추가(INSERT)하거나, 삭제(DELETE), 수정(UPDATE) 또는 조회(SELECT)를 하기 위해서는 SQLiteDatabase 클래스를 사용해야 합니다. 그리고 데이터가 저장될 테이블 생성 및 삭제, 수정 등의 기능 또한 SQLiteDatabase 클래스에서 제공되죠. (https://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html)

 

즉, SQLiteDatabase 클래스는 하나의 SQLite 데이터베이스를 다루기 위한 핵심 역할을 수행하는 클래스입니다. 그러므로 SQLite 데이터베이스 작업을 수행하기 전 반드시 SQLiteDatabase 클래스 객체의 참조를 획득해야 합니다.

 

일단 객체에 대한 참조를 획득하고 나면, SQLiteDatabase 클래스에 정의된 함수를 통해 데이터베이스 기능을 사용할 수 있습니다.

 

SQLiteDatabase 객체의 참조를 획득하는 것은 SQLite 데이터베이스 파일을 열거나, 새로운 파일을 생성함으로써 획득할 수 있습니다.

2.2 SQLite 데이터베이스 열기. (SQLiteDatabase 객체 참조 획득)

SQLite 데이터베이스를 사용하기 위해서는 가장 먼저 데이터베이스 파일을 열거나 생성해야 합니다.  SQLiteDatabase 클래스에 정의된 몇 가지 static 함수를 통해 수행될 수 있습니다.

리턴 타입메소드 이름

SQLiteDatabase openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags)
SQLiteDatabase openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags, DatabaseErrorHandler errorHandler)
SQLiteDatabase openOrCreateDatabase(File file, SQLiteDatabase.CursorFactory factory)
SQLiteDatabase openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler)
SQLiteDatabase openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory)

표에 나와 있는 static 함수의 이름을 통해 알 수 있듯이, 데이터베이스를 여는 과정에서 만날 수 있는 상황은 크게 두 가지로 나뉠 수 있습니다. "이미 데이터베이스 파일이 존재하는 경우(openDatabase() 함수 사용)"와 "데이터베이스 파일이 없을 수도 있는 경우(openOrCreateDatabase() 함수 사용)" 입니다. 주로 openOrCreateDatabase() 함수를 사용하여, 데이터베이스 파일 열기를 시도한 다음 만약 파일이 존재하지 않는다면 새로운 데이터베이스 파일을 생성하는 방법을 사용합니다.

 

아래의 코드는 openOrCreateDatabase() 함수를 사용하여 "sample.db" 파일을 여는 예제입니다. "sample.db" 파일이 존재하지 않는다면, 새로 만들게 됩니다.

 
[STEP-1] SQLiteDatabase - 데이터베이스 열기. (openOrCreateDatabase() 함수 호출.)
    SQLiteDatabase sqliteDB = null ;

    try {
        sqliteDB = SQLiteDatabase.openOrCreateDatabase("sample.db", null) ;
    } catch (SQLiteException e) {
        e.printStackTrace() ;
    }

2.3 SQLite 데이터베이스에 테이블 생성하기. (CREATE TABLE)

SQLite 데이터베이스 파일을 열어 SQLiteDatabase 객체의 참조를 확보했다면, 이제 객체의 참조를 통해 데이터베이스에 데이터를 추가하거나, 삭제 또는 조회 등의 작업을 수행할 수 있습니다. 하지만 데이터베이스 파일을 열었다고 해서 무턱대고 데이터를 추가할 순 없습니다.

앞서 관계형 데이터베이스에 대해 설명할 때, 데이터베이스에 데이터를 저장하기 위해 데이터를 구조화하는 과정에 대해 간단히 언급하였습니다. 이 때 데이터 구조화의 결과로써, 데이터의 속성과 그 값의 관계를 나타내는 테이블(Table)이 만들어지는 것을 설명했습니다.

 

SQLite 데이터베이스 파일을 열었다면, 다음 해야 할 일은 데이터베이스 내에 테이블(Table)을 생성하는 것입니다. SQLite 데이터베이스에 테이블을 만드는 방법은 테이블 생성을 위한 SQL 문자열을 SQLiteDatabase 클래스의 execSQL() 함수를 통해 전달하는 것입니다.

 

아래 예제 코드는 정수형(INTEGER) 데이터를 저장하기 위한 "NO" 필드와 문자열(TEXT) 데이터를 저장하기 위한 "NAME" 필드를 가지는 "ORDER_T"라는 테이블을 생성하는 코드입니다.

[STEP-2] SQLiteDatabase - 테이블 만들기. ("CREATE TABLE ... " 문을 execSQL()로 실행.)
    String sqlCreateTbl = "CREATE TABLE ORDER_T (NO INTEGER, NAME TEXT)" ;

    sqliteDB.execSQL(sqlCreateTbl) ;

그런데 "CREATE TABLE" SQL 문은 데이터베이스 파일이 생성되고나서 최초에 한번만 실행할 수 있습니다. 만약 생성하고자 하는 테이블과 같은 이름의 테이블이 이미 존재하는 상황에서 "CREATE TABLE" 명령을 실행하면, 다음과 같이 예외 상황이 발생합니다.

Caused by: android.database.sqlite.SQLiteException: table ORDER_T already exists (code 1): , while compiling: CREATE TABLE ORDER_T (NO INTEGER, NAME TEXT)

이런 경우, 테이블 중복 생성으로 인한 예외 상황(Exception)이 발생하는 것을 막기 위해서는 테이블이 존재하지 않는 경우에만 테이블을 새로 만들도록 만들어야 합니다. 이를 위해서는 "CREATE TABLE" 문에 "IF NOT EXISTS" 옵션을 추가하여 실행하면 됩니다. 예외 상황이 발생하는 문제를 해결한 코드는 아래와 같습니다.

[STEP-2.1] SQLiteDatabase - 테이블이 없는 경우 새로 만들기. ("CREATE TABLE IF NOT EXISTS ... " 문을 execSQL()로 실행.)
    String sqlCreateTbl = "CREATE TABLE IF NOT EXISTS ORDER_T (NO INTEGER, NAME TEXT)" ;

    sqliteDB.execSQL(sqlCreateTbl) ;

2.4 테이블에 데이터 추가, 수정, 삭제하기.

2.4.1 테이블에 데이터 추가하기. (INSERT)

SQLite 데이터베이스를 열고 테이블을 생성했다면, 이제 생성된 테이블에 데이터를 추가할 수 있습니다. 테이블에 데이터를 추가할 때는 "INSERT" 문을 사용합니다.

 

아래 예제 코드는 앞서 만든 "ORDER_T" 테이블의 "NO", "NAME" 필드에 각각 1과 "ppotta" 값을 추가하는 코드입니다. 앞서 테이블 생성 때와 마찬가지로 SQLiteDatabase 클래스의 execSQL() 함수를 사용합니다.

 
[STEP-3] SQLiteDatabase - 데이터 추가. ("INSERT INTO ... " 문을 execSQL()로 실행.)
    String sqlInsert = "INSERT INTO ORDER_T (NO, NAME) VALUES (1, 'ppotta')" ;

    sqliteDB.execSQL(sqlInsert) ;

"INSERT" 문을 실행하면 테이블에 새로운 데이터를 추가합니다. 이 때 새로 추가되는 값은 테이블 내 동일한 값을 가진 로우(Row)의 존재 여부와 관계없이, 새로운 로우(Row)로 추가됩니다.

 

그런데 어떤 상황에서는 새로운 값이 추가되는 것 대신, 기존에 저장된 로우(Row)의 값을 수정하고 싶을 때도 있을 것입니다. 물론 뒤에서 살펴볼 "UPDATE" 명령을 통해 데이터를 수정하는 것이 일반적이긴 하지만, "INSERT" 명령을 실행할 때 미리 조건에 맞는 데이터가 있으면, 새로운 로우(Row)를 추가하지 않고 이미 들어있던 로우(Row)의 값을 수정하도록 만들 수 있습니다. 이를 위해 "INSERT OR REPLACE" 문을 사용합니다.

[STEP-3.1] SQLiteDatabase - 데이터 추가. 이미 존재하면 수정. ("INSERT OR REPLACE INTO ... " 문을 execSQL()로 실행.)
    String sqlInsert = "INSERT OR REPLACE INTO ORDER_T (NO, NAME) VALUES (1, 'ppotta')" ;

    sqliteDB.execSQL(sqlInsert) ;

2.4.2 테이블 데이터 수정하기. (UPDATE)

테이블에 데이터가 추가되어 있다면, "UPDATE" 문을 사용하여 데이터의 내용을 수정할 수 있습니다.

 

아래 예제 코드는 ORDER_T 테이블의 "NO"와 "NAME" 필드 값을 각각 2, "ppotta2"로 수정하는 코드입니다.

[STEP-4] SQLiteDatabase - 데이터 수정. ("UPDATE ... " 문을 execSQL()로 실행.)
    String sqlUpdate = "UPDATE ORDER_T SET NO=2, NAME='ppotta2'" ;

    sqliteDB.execSQL(sqlUpdate) ;

그런데 위의 코드를 수행하면 테이블 내의 모든 행의 값이 수정됩니다. 이는 "UPDATE" 문이 실행될 데이터에 대한 조건이 지정되지 않았기 때문입니다. 만약 모든 로우(Row)가 아닌 특정 로우(Row)의 값만 수정하고자 한다면, 아래와 같이 "UPDATE" 문에 "WHERE"를 사용하여 조건을 추가하면 됩니다.

 

아래는 "WHERE"를 사용하여 "NO" 필드 값이 1인 로우(Row)에 대해서만 값을 수정하도록 만드는 코드입니다.

[STEP-4.1] SQLiteDatabase - 조건에 해당하는 데이터 수정. ("UPDATE ... WHERE ... " 문을 execSQL()로 실행.)
    String sqlUpdate = "UPDATE ORDER_T SET NO=2, NAME='ppotta2' WHERE NO=1" ;

    sqliteDB.execSQL(sqlUpdate) ;

2.4.3 테이블 데이터 삭제하기. (DELETE)

테이블에 저장되어 있는 데이터를 삭제하려면 "DELETE" 문을 사용합니다.

 

아래 예제 코드는 "ORDER_T" 테이블의 모든 데이터를 삭제하는 코드입니다.

[STEP-5] SQLiteDatabase - 데이터 삭제. ("DELETE ... " 문을 execSQL()로 실행.)
    String sqlDelete = "DELETE FROM ORDER_T" ;

    sqliteDB.execSQL(sqlDelete) ;

"ORDER_T" 테이블의 데이터 중에서 특정 로우(Row)만 삭제하길 원한다면 "WHERE"를 추가하여 조건을 지정할 수 있습니다.

 

아래 코드는 "NO"의 값이 2인 모든 데이터를 삭제하는 예제입니다.

[STEP-5.1] SQLiteDatabase - 조건에 맞는 데이터 삭제. ("DELETE ... WHERE ... " 문을 execSQL()로 실행.)
    String sqlDelete = "DELETE FROM ORDER_T WHERE NO=2" ;

    sqliteDB.execSQL(sqlDelete) ;

 

 

2.5 테이블 데이터 조회하기.

테이블에 저장된 데이터를 조회하려면 "SELECT" 문을 사용합니다. 하지만 앞에서 살펴 본 데이터 추가, 수정, 삭제를 위한 SQL 문장을 실행할 때와는 다르게 추가적으로 알아두어야 할 요소가 두 가지 있습니다. 바로 쿼리(Query)와 커서(Cursor) 입니다.

2.5.1 쿼리(Query)

쿼리(Query)라는 단어를 우리 말로 표현할 때 주로 "질의"라는 용어를 사용합니다. "질의"라는 단어의 사전적 의미는, "의심나거나 모르는 점을 묻는 것"을 말합니다. 자신이 모르는 사실을 알기 위해, 누군가에게 질문하여 정보를 요청하는 것이 바로 "쿼리(Query)"라는 단어의 의미인 것입니다.

 

이제 관점을 데이터베이스로 옮겨보도록 하겠습니다. 데이터베이스에 저장된 데이터는, 그 정보를 획득하기 전까지는 사용자가 모르는(가지고 있지 않은) 정보입니다. 그래서 만약 그 정보를 얻기 위해서 데이터베이스 시스템에 정보를 요청한다면, 우리는 그것을 "데이터베이스에 쿼리(Query)"한다고 말할 수 있는 것입니다.

 

정리하자면, 쿼리(Query)란, 원하는 데이터를 얻기 위해 데이터베이스에 정보를 요청(Request)하는 것을 말하며, SQLite 데이터베이스에서 그 요청(Request)은 "SELECT" 문을 사용하여 작성할 수 있습니다.

 

그런데 앞서 살펴본 데이터 추가(INSERT), 수정(UPDATE), 삭제(DELETE)는 데이터베이스로 전달되는 단-방향 명령인데 반해, 조회(SELECT)를 위한 쿼리(Query)는 데이터베이스로부터 결과 데이터 전달이 필요한 양-방향 명령입니다. 그러므로 아무런 값도 리턴하지 않는 execSQL() 함수 대신, "SELECT" 문의 조건에 따라 선택된 레코드 집합(RecordSet)을 리턴하는 query() 함수 또는 rawQuery() 함수를 사용해야 합니다.

 

리턴 타입메소드

Cursor query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
Cursor query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit, CancellationSignal cancellationSignal)
Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
Cursor queryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit, CancellationSignal cancellationSignal)
Cursor queryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
Cursor rawQuery(String sql, String[] selectionArgs, CancellationSignal cancellationSignal)
Cursor rawQuery(String sql, String[] selectionArgs)
Cursor rawQueryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, String sql, String[] selectionArgs, String editTable, CancellationSignal cancellationSignal)
Cursor rawQueryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, String sql, String[] selectionArgs, String editTable)

많은 종류의 쿼리(Query) 함수가 제공되긴 하지만 많은 경우에 "SELECT" 문 전체를 한번에 전달할 수 있는 rawQuery() 함수가 주로 사용됩니다.

 

또한 모든 쿼리(Query) 함수가 리턴하는 레코드 집합(RecordSet)은 Cursor 인터페이스 타입으로 전달됩니다.

 

참고로 레코드 집합(RecordSet)이라는 용어는 쿼리(Query) 결과에 포함된 레코드(Record)의 묶음(Set)을 말하는 것입니다. 보통 데이터를 쿼리(Query)한 결과는 하나 또는 하나 이상의 레코드(Record)를 포함하고 있는데, 이렇게 리턴된 결과 레코드들의 집합을 레코드 집합(RecordSet)이라고 지칭합니다.

 

또한 레코드 집합(RecordSet)을 다른 용어로 로우 집합(RowSet)이라고 부르기도 합니다. 이전 글에서 관계형 데이터베이스 용어에 대해 설명할 때 "로우(Row)=레코드(Record)"라는 것을 설명하였으므로 그 연관성을 쉽게 이해할 수 있으리라 생각합니다.

2.5.2 커서(Cursor)

일반적인 컴퓨팅 환경에서 커서(Cursor)란, 화면에 표시된 내용에서 사용자가 내용 입력 또는 확인을 위해, 사용자가 현재 주시하고 있는 위치에 대한 표시를 말합니다. 키보드 커서 또는 마우스 커서 등이 대표적입니다.
그리고 데이터베이스에서도 그 의미는 크게 다르지 않습니다. 데이터베이스의 쿼리 결과로 리턴된 데이터에서 현재 그 내용을 확인하고 있는 위치를 나타내는 정보를, 커서(Cursor)라는 용어를 사용하여 지칭합니다.

 

데이터베이스에 저장된 데이터를 쿼리하면 그 결과 데이터는, 한 개의 레코드만 가지거나, 또는 여러 개의 레코드가 포함된 레코드 집합(RecordSet)입니다. 이 때 레코드 집합(RecordSet)에 들어 있는 개별 레코드에 접근하여 그 값을 확인할 수 있는 기능을 제공해주는 것이 바로 커서(Cursor)가 하는 역할인 것입니다. 그래서 SQLiteDatabase가 제공하는 모든 쿼리(Query) 관련 함수들은 Cursor 타입을 리턴하도록 되어 있습니다.

 

안드로이드에서 데이터베이스 커서(Cursor) 기능은 Cursor(android.database.Cursor) 인터페이스에 정의되어 있습니다. (https://developer.android.com/reference/android/database/Cursor.html)

 

Cursor 인터페이스에는 많은 수의 함수가 정의되어 있습니다. 하지만 대부분의 경우, 몇 개의 함수 사용만으로 쿼리 결과로 리턴된 데이터를 처리할 수 있습니다.

아래 예제 코드는 Cursor 인터페이스를 사용하여 쿼리 결과 데이터를 탐색하는 과정을 나타낸 것입니다.

    Cursor cursor = sqliteDB.rawQuery("SELECT ...", null) ;

    while (cursor.moveToNext()) {
        // 첫 번째 컬럼(Column)이 INTEGER 타입인 경우.
        int val = cursor.getInt(0) ;

        // 두 번째 컬럼(Column)의 타입이 TEXT 인 경우.
        String str = cursor.getText(1) ;

        // 세 번째 컬럼(Column)이 REAL 타입으로 선언된 경우.
        float real = cursor.getFloat(2) ;

        // 코드 계속...
    }

2.5.3 데이터베이스 데이터 조회하기. (SELECT)

앞서 설명한 쿼리(Query) 함수 및 커서(Cursor)를 사용하여 테이블에 저장된 모든 데이터를 조회하는 코드입니다.

[STEP-6] SQLiteDatabase - 데이터 조회. ("SELECT ... " 문을 rawQuery()로 실행.)
    String sqlSelect = "SELECT * FROM ORDER_T" ;
    Cursor cursor = null ;

    cursor = sqliteDB.rawQuery(sqlSelect) ;
    while (cursor.moveToNext()) {
        // INTEGER로 선언된 첫 번째 "NO" 컬럼 값 가져오기.
        int no = cursor.getInt(0) ;

        // TEXT로 선언된 두 번째 "NAME" 컬럼 값 가져오기.
        String name = cursor.getText(1) ;

        // TODO : use no, name.
    }

"SELECT" 문에 조건을 추가하여 특정 데이터만 조회하고자 한다면, 다른 SQL 문과 마찬가지로 "WHERE"를 추가하여 조건을 기술할 수 있습니다.

[STEP-6.1] SQLiteDatabase - 조건을 추가하여 데이터 조회. ("SELECT ... WHERE ..." 문을 rawQuery()로 실행.)
    // NAME 컬럼 값이 'ppotta'인 모든 데이터 조회.
    String sqlSelect = "SELECT * FROM ORDER_T WHERE NAME='ppotta'" ;

    Cursor cursor = sqliteDB.rawQuery(sqlSelect) ;
    while (cursor.moveToNext()) {
        // INTEGER로 선언된 첫 번째 "NO" 컬럼 값 가져오기.
        int no = cursor.getInt(0) ;

        // TEXT로 선언된 두 번째 "NAME" 컬럼 값 가져오기.
        String name = cursor.getText(1) ;

        // TODO : use no, name.
    }

2.6 SQLite 데이터베이스 테이블 삭제하기.

SQLite 데이터베이스에 만들어져 있는 테이블을 앱 실행 중 삭제하는 경우는 매우 드뭅니다. 하지만 앱의 기능이 변경되고 데이터베이스에 저장되는 데이터 구조가 복잡해지면, 기존에 만들어놓은 테이블만으로는 복잡해진 데이터를 처리하는 게 힘들어질 수 있습니다. 이 때 개발자가 선택할 수 있는 방법 중 하나는, 앱이 업그레이드된 후 처음 실행될 때 새로운 테이블을 만들어 기존 테이블의 데이터를 옮긴 다음, 기존 테이블은 삭제하는 것입니다.

 

테이블을 삭제하기 위해서는 "DROP TABLE" 문을 사용합니다.

[STEP-7] SQLiteDatabase - 테이블 삭제. ("DROP TABLE ... " 문을 execSQL()로 실행.)
    String sqlDropTbl = "DROP TABLE ORDER_T" ;

    sqliteDB.execSQL(sqlDropTbl) ;

"DROP TABLE" 문을 사용하여 테이블을 삭제할 때 주의해야 할 점은, 삭제하기 전 저장되어있던 데이터 또한 삭제와 동시에 모두 지워진다는 것과, 삭제된 데이터와 테이블은 다시 복구가 불가능하다는 것입니다. 그러므로 삭제하고자 하는 테이블에 중요한 데이터가 저장되어 있다면, 반드시 백업을 해둔 다음 테이블을 삭제해야 합니다.

3. 참고.

.END.

728x90
반응형
LIST

+ Recent posts