안드로이드 데이터베이스(DB) 프로그래밍 4. [SQLiteOpenHelper 사용 예제] (Android Database 4)

2017.04.18 15:42


1. SQLiteDatabase 사용 예제에 대한 문제와 개선

이전 글 [안드로이드 데이터베이스(DB) 프로그래밍 3 -SQLiteDatabase 사용 예제]에서 SQLiteDatabase 클래스를 사용하여 SQLite 데이터베이스를 다루는 예제에 대해 살펴봤습니다. SQLiteDatabase.openOrCreateDatabase() 함수를 호출하여 데이터베이스를 열고, SQLiteDatabase 클래스 객체를 확보한 다음, 데이터베이스를 다루는 함수를 사용하여 데이터를 추가하거나, 수정, 삭제 또는 조회하는 예제를 작성하였습니다.


그런데 분명 [안드로이드 데이터베이스(DB) 프로그래밍 3 -SQLiteDatabase 사용 예제]에서 작성한 예제가, SQLiteDatabase에 대한 사용법을 보여주고 있고, 앱의 동작도 문제가 없지만, 코드 작성에 대하여 몇 가지 개선해야 할 점이 있습니다.

1.1 SQL Helper(SQLiteOpenHelper 클래스) 사용.

먼저, 예제에서 데이터베이스 파일을 열 때 SQLiteDatabase.openOrCreateDatabase() 함수를 직접 호출했는데, 안드로이드에서는 이 방법보다 SQLiteOpenHelper 클래스를 사용하기를 권장하고 있습니다. (관련 내용은 [SQL Helper를 사용하여 데이터베이스 생성]에서 확인할 수 있습니다.)


SQLiteOpenHelper를 사용하면 SQLiteDatabase.openOrCreateDatabase() 함수를 사용하여 데이터베이스를 열 때 파일의 경로까지 직접 지정해야 하는 번거로움을 해소할 수 있습니다. 그리고 앱의 기능 수정으로 인해 데이터베이스 테이블의 구조가 바뀌어야 하는 상황(데이터베이스 업그레이드)에 대해서도 적절히 대처할 수 있는 기반을 제공함으로써 데이터베이스 관리를 용이하게 만들어줍니다.


그러므로 데이터베이스를 열 때는 SQLiteDatabase.openOrCreateDatabase()를 직접 호출하지 말고, SQLiteOpenHelper를 사용하는 것이 좋습니다. 일단 SQLiteOpenHelper 객체를 생성하고 나면, SQLiteOpenHelpergetWritableDatabase() 또는 getReadableDatabase() 함수를 통해 SQLiteDatabase 객체를 참조할 수 있습니다.

1.2 계약 클래스 (Contract Class) 사용.

[안드로이드 데이터베이스(DB) 프로그래밍 3 -SQLiteDatabase 사용 예제]의 예제에서는 SQL 문장을 만들 때, 테이블 이름(CONTACT_T)과 필드 이름(NO, NAME, ...)을 매번 직접 지정하였습니다.

    private void init_tables() {
        // 코드 계속 ...
        String sqlCreateTbl = "CREATE TABLE IF NOT EXISTS CONTACT_T (" +
                    "NO "           + "INTEGER NOT NULL," +
                    "NAME "         + "TEXT," +
                    "PHONE "        + "TEXT," +
                    "OVER20 "       + "INTEGER" + ")" ;
        // ... 코드 계속
    }

    private void load_values() {
        // 코드 계속 ...
        String sqlQueryTbl = "SELECT * FROM CONTACT_T" ;
        // ... 코드 계속
    }

    private void save_values() {
        // 코드 계속 ...
        String sqlInsert = "INSERT INTO CONTACT_T " +
                    "(NO, NAME, PHONE, OVER20) VALUES (" +
                    Integer.toString(no) + "," +
                    "'" + name + "'," +
                    "'" + phone + "'," +
                    ((isOver20 == true) ? "1" : "0") + ")" ;
        // ... 코드 계속
    }

그런데 만약, 기능 수정 또는 확장으로 인해 데이터베이스 구조가 변경되어야 한다면 어떻게 될까요? 그러한 구조 변경으로 인해 테이블 이름 또는 필드 이름이 수정되어야 된다면? 음.. 뭐, 어쩔 수 없이 모든 테이블, 필드 이름을 찾아서 수정된 이름으로 변경해야겠죠.


물론, 테이블이나 필드 이름이 사용되는 곳이 얼마 없으니 상관없지 않을까... 라고 생각할 수도 있습니다. 하지만 테이블과 필드의 갯수가 많아지고 SQL 문장을 조합하는 곳이 많아지면, 이름 수정 작업은 큰 부담으로 다가올 수 밖에 없습니다.


이런 문제를 해결할 수 있는 방법은 계약 클래스(Contract Class)를 사용하는 것입니다.


계약 클래스(Contract Class)는 프로그램 개발 과정에서 참조되는 여러 상수들을 정의한 클래스를 말합니다. 이 의미를 데이터베이스에서 적용해보자면, 데이터베이스의 계약 클래스(Contract Class)란, 테이블 이름, 열(Column) 이름, 기능 별 SQL 문장들에 대한 상수 정의를 포함한 클래스를 의미합니다. "계약(Contract)"이라는 단어가 "어떤 행위를 함에 있어 관련된 사람들이 지켜야 할 의무 또는 약속을 문서로 남긴 것"을 의미하듯, 계약 클래스(Contract Class)는 데이터베이스를 사용함에 있어 개발자가 사용해야 할 여러 정보를 상수로 정의해둔 것"이라고 정리할 수 있죠.


계약 클래스(Contract Class)를 사용함으로써 얻을 수 있는 장점은 데이터베이스와 관련된 정보를 한 곳에서 관리할 수 있다는 것과, 그로 인해 데이터베이스 구조 또는 이름이 변경될 때의 수정 작업을 최소화할 수 있다는 것입니다.


참고로, 계약 클래스(Contract Class)를 만들 때는 앱의 패키지 루트에 생성하는 것이 좋습니다. 앱 소스의 어디서든 바로 참조 가능하도록 만드는 것이 좋기 때문입니다. 그리고 계약 클래스(Contract Class)는 인스턴스를 만들 필요가 없기 때문에 생성자의 접근 제한자(Access Modifier)를 private으로 선언합니다.


계약 클래스(Contract Class)에 대한 구체적인 사용 방법은 아래 예제의 ContactDBCtrct 클래스를 참고하시기 바랍니다.

2. 개선된 SQLite 데이터베이스 사용 예제

그럼 이제 예제를 통해 [안드로이드 데이터베이스(DB) 프로그래밍 3 -SQLiteDatabase 사용 예제]의 예제를 개선해보도록 하겠습니다.


예제의 화면 레이아웃은 [안드로이드 데이터베이스(DB) 프로그래밍 3 -SQLiteDatabase 사용 예제]에서 사용한 레이아웃을 수정없이 그대로 사용하겠습니다.

예제 화면 레이아웃


2.1 MainActivity의 레이아웃 작성.

MainActivity의 레이아웃은 이전 예제에서 사용한 코드를 그대로 적용합니다.

[STEP-1] "activity_main.xml" - MainActivity의 레이아웃 XML 코드 작성.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:id="@+id/content_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.ppottasoft.databaseadvancedexample.MainActivity"
    tools:showIn="@layout/activity_main">

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:stretchColumns="1">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="No" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextNo"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Name" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextName"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Phone" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextPhone"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Over20" />

            <CheckBox
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:id="@+id/checkBoxOver20"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_span="2">

                <Button
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:textSize="24dp"
                    android:text="Save"
                    android:id="@+id/buttonSave" />

                <Button
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:textSize="24dp"
                    android:text="CLEAR"
                    android:id="@+id/buttonClear" />
            </LinearLayout>

        </TableRow>
    </TableLayout>
</RelativeLayout>

2.2 계약 클래스 (Contract Class) 추가.

이제 코드 개선의 첫 번째 과정으로, 데이터베이스와 관련된 상수를 포함하는 계약 클래스(Contract Class)를 추가합니다. 계약 클래스(Contract Class)에 어떤 정보를 포함시킬 것인지는 오로지 개발자의 결정에 따릅니다. 단순히 데이터베이스 이름, 테이블 이름, 열(Column) 이름 정도의 상수만 포함시킬 수 있고, 더 나아가 단순 조합 SQL 문 또는 기능 별 컬럼과 파라미터 조합을 리턴하는 클래스 정적 메소드까지 선언할 수도 있습니다.


예제에서 사용하는 계약 클래스는 테이블 이름, 열(Column) 이름, 단순 조합 SQL 문 정도만 포함합니다.

[STEP-2] "ContactDBCtrct.java" - 데이터베이스 Contact를 위한 계약 클래스.
public class ContactDBCtrct {

    private ContactDBCtrct() {} ;

    public static final String TBL_CONTACT = "CONTACT_T" ;
    public static final String COL_NO = "NO" ;
    public static final String COL_NAME = "NAME" ;
    public static final String COL_PHONE = "PHONE" ;
    public static final String COL_OVER20 = "OVER20" ;

    // CREATE TABLE IF NOT EXISTS CONTACT_T (NO INTEGER NOT NULL, NAME TEXT, PHONE TEXT, OVER20 INTEGER)
    public static final String SQL_CREATE_TBL = "CREATE TABLE IF NOT EXISTS " + TBL_CONTACT + " " +
            "(" +
                COL_NO +        " INTEGER NOT NULL" +   ", " +
                COL_NAME +      " TEXT"             +   ", " +
                COL_PHONE +     " TEXT"             +   ", " +
                COL_OVER20 +    " INTEGER"          +
            ")" ;

    // DROP TABLE IF EXISTS CONTACT_T
    public static final String SQL_DROP_TBL = "DROP TABLE IF EXISTS " + TBL_CONTACT ;

    // SELECT * FROM CONTACT_T
    public static final String SQL_SELECT = "SELECT * FROM " + TBL_CONTACT ;

    // INSERT OR REPLACE INTO CONTACT_T (NO, NAME, PHONE, OVER20) VALUES (x, x, x, x)
    public static final String SQL_INSERT = "INSERT OR REPLACE INTO " + TBL_CONTACT + " " +
            "(" + COL_NO + ", " + COL_NAME + ", " + COL_PHONE + ", " + COL_OVER20 + ") VALUES " ;

    // DELETE FROM CONTACT_T
    public static final String SQL_DELETE = "DELETE FROM " + TBL_CONTACT ;
}

2.3 SQLiteOpenHelper 상속 및 구현.

코드 개선의 두 번째 과정으로, SQLiteOpenHelper를 상속하는 클래스를 만듭니다.

[STEP-3] "ContactDBHelper.java" - SQLiteOpenHelper를 상속한 Helper 클래스 정의
public class ContactDBHelper extends SQLiteOpenHelper {

    public static final int DB_VERSION = 1 ;
    public static final String DBFILE_CONTACT = "contact.db" ;

    public ContactDBHelper(Context context) {
        super(context, DBFILE_CONTACT, null, DB_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(ContactDBCtrct.SQL_CREATE_TBL) ;
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // db.execSQL(ContactDBCtrct.SQL_DROP_TBL) ;
        onCreate(db) ;
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // onUpgrade(db, oldVersion, newVersion);
    }
}

2.4 데이터베이스 Helper 클래스 변수 선언 및 초기화.

SQLiteOpenHelper클래스를 상속한 ContactDBHelper를 사용하기 위해 MainActivity에 클래스 변수로 선언하고, 초기화 과정을 수행합니다. 초기화 과정은 매우 간단합니다. ContactDBHelper 클래스의 인스턴스를 만드는 것만으로 수행되니까요.

[STEP-4] "MainActivity.java" - ContactDBHelper 변수 선언 및 초기화.
public class MainActivity extends AppCompatActivity {

    ContactDBHelper dbHelper = null ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 코드 계속 ...

        // init sqlite db helper.
        init_tables() ;

        // ... 코드 계속
    }

    private void init_tables() {
        dbHelper = new ContactDBHelper(this) ;
    }

    // ... 코드 계속
}

2.5 앱 실행 시, 테이블 데이터 조회하여 표시. (SELECT)

이제 새로 추가된 ContactDBHelper 클래스 객체를 사용하는 데이터 조회 기능을 작성하겠습니다. [안드로이드 데이터베이스(DB) 프로그래밍 3 -SQLiteDatabase 사용 예제]의 예제에서는 SQLiteDatabase.openOrCreateDatabase() 함수를 통해 확보한 SQLiteDatabase 객체를 직접 사용했지만, 여기서는 SQLiteOpenHelper 클래스에서 제공하는 getReadableDatabase()함수를 통해 SQLiteDatabase 객체를 가져옵니다.


그리고 데이터 조회를 위한 SELECT 문을 직접 만들지 않고, 계약 클래스에 미리 만들어둔 ContactDBCtrct.SQL_SELECT 를 사용합니다.

[STEP-5] "MainActivity.java" - SELECT 문을 통해 데이터 조회 및 표시.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        // init sqlite db helper.
        init_tables() ;

        // load values from db.
        load_values() ;

        // 코드 계속  ...
    }

    private void load_values() {

        SQLiteDatabase db = dbHelper.getReadableDatabase() ;
        Cursor cursor = db.rawQuery(ContactDBCtrct.SQL_SELECT, null) ;

        if (cursor.moveToFirst()) {
            // no (INTEGER) 값 가져오기.
            int no = cursor.getInt(0) ;
            EditText editTextNo = (EditText) findViewById(R.id.editTextNo) ;
            editTextNo.setText(Integer.toString(no)) ;

            // name (TEXT) 값 가져오기
            String name = cursor.getString(1) ;
            EditText editTextName = (EditText) findViewById(R.id.editTextName) ;
            editTextName.setText(name) ;

            // phone (TEXT) 값 가져오기
            String phone = cursor.getString(2) ;
            EditText editTextPhone = (EditText) findViewById(R.id.editTextPhone) ;
            editTextPhone.setText(phone) ;

            // over20 (INTEGER) 값 가져오기.
            int over20 = cursor.getInt(3) ;
            CheckBox checkBoxOver20 = (CheckBox) findViewById(R.id.checkBoxOver20) ;
            if (over20 == 0) {
                checkBoxOver20.setChecked(false) ;
            } else {
                checkBoxOver20.setChecked(true) ;
            }
        }
    }

    // 코드 계속  ...
}

2.6 입력 데이터 저장하기. (INSERT INTO)

데이터 조회 기능과 마찬가지로, 데이터베이스 Helper 클래스로부터 SQLiteDatabase의 객체를 가져온 다음, INSERT 문을 실행합니다. 이 때, INSERT 문이 실행되면 데이터베이스의 내용이 변경되기 때문에, SQLiteOpenHelper.getWritableDatabase() 함수를 통해 SQLiteDatabase 객체를 가져와야 합니다.

[STEP-6] "MainActivity.java" - 데이터 저장하기.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        Button buttonSave = (Button) findViewById(R.id.buttonSave) ;
        buttonSave.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                save_values() ;
            }
        });

        // 코드 계속  ...
    }

    // ... 코드 계속

    private void save_values() {
        SQLiteDatabase db = dbHelper.getWritableDatabase() ;

        db.execSQL(ContactDBCtrct.SQL_DELETE) ;

        EditText editTextNo = (EditText) findViewById(R.id.editTextNo) ;
        int no = Integer.parseInt(editTextNo.getText().toString()) ;

        EditText editTextName = (EditText) findViewById(R.id.editTextName) ;
        String name = editTextName.getText().toString() ;

        EditText editTextPhone = (EditText) findViewById(R.id.editTextPhone) ;
        String phone = editTextPhone.getText().toString() ;

        CheckBox checkBoxOver20 = (CheckBox) findViewById(R.id.checkBoxOver20) ;
        boolean isOver20 = checkBoxOver20.isChecked() ;

        String sqlInsert = ContactDBCtrct.SQL_INSERT + 
                " (" +
                Integer.toString(no) + ", " +
                "'" + name + "', " +
                "'" + phone + "', " +
                ((isOver20 == true) ? "1" : "0") +
                ")" ;

        db.execSQL(sqlInsert) ;
    }

    // 코드 계속 ...
}

2.7 데이터 삭제하기. (DELETE)

삭제하는 코드 또한 개선된 방법을 적용합니다.

[STEP-7] "MainActivity.java" - 데이터 삭제하기.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        Button buttonClear = (Button) findViewById(R.id.buttonClear) ;
        buttonClear.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                delete_values() ;
            }
        });

        // 코드 계속  ...
    }

    // ... 코드 계속

    private void delete_values() {
        SQLiteDatabase db = dbHelper.getWritableDatabase() ;

        db.execSQL(ContactDBCtrct.SQL_DELETE) ;

        EditText editTextNo = (EditText) findViewById(R.id.editTextNo) ;
        editTextNo.setText("") ;

        EditText editTextName = (EditText) findViewById(R.id.editTextName) ;
        editTextName.setText("") ;

        EditText editTextPhone = (EditText) findViewById(R.id.editTextPhone) ;
        editTextPhone.setText("") ;

        CheckBox checkBoxOver20 = (CheckBox) findViewById(R.id.checkBoxOver20) ;
        checkBoxOver20.setChecked(false) ;
    }
}

3. 예제 실행 화면

예제의 실행 화면은 [안드로이드 데이터베이스(DB) 프로그래밍 3 -SQLiteDatabase 사용 예제]의 예제와 동일합니다.

예제 실행 화면


각 필드에 값을 입력하고 "SAVE" 버튼을 누르면, 입력된 값들이 데이터베이스에 저장됩니다. 그리고 앱을 다시 실행하면, 데이터베이스에 저장된 값이 화면에 표시되는 것을 확인할 수 있습니다.

데이터베이스 저장 및 저장된 데이터 표시


4. 참고.

.END.


ANDROID 프로그래밍/DB , , , , , , ,

  1. Blog Icon

    비밀댓글입니다

  2. Blog Icon
    인썸니아

    정독하고 갑니다. 꾸벅~

  3. 감사합니다. 꾸벅~

  4. 이 블로깅 역시 잘 읽었습니다.
    감사합니다.

  5. 감사합니다.

  6. Blog Icon
    지나가는이

    정말 좋은 강좌 잘 읽었습니다~
    책보다 나은거 같네요^^
    그런데 마지막 개선된 부분에서 저장하기 부분이 조금 이상하네요~
    계약자클래스에서 SQL_INSERT 를 정의해놓고
    개선된 부분에선 해당 계약자 부분에서 선언된걸 쓰지를 않고 있네요 ㅎㅎ
    요거 좀 내용 수정 바랍니다^^
    잘읽었습니당

  7. 부족한 글임에도, 좋은 말씀 해주셔서 감사합니다.
    그리고 본문의 내용이 부실함을 지적해주신 것도 정말 감사드립니다.
    좀 더 꼼꼼하게 살펴서, 최대한 깔끔한 내용 만들 수 있도록 노력하겠습니다.

    감사합니다.

  8. Blog Icon
    하수

    혹시 sqlite db 파일에 여러 테이블 중에서 원하는 테이블이름을 얻어올 수 있는 방법이 있을까요??

  9. db 파일에 어떤 테이블이 만들어져 있는지 확인하고 싶다는 질문인 것 같은데요.

    그런 경우, 간단하게 sqlite3 프로그램에서 ".tables" 명령을 실행하여 테이블 리스트를 조회할 수 있습니다.

    그런데 만약 sqlite3 프로그램을 사용하지 않고 쿼리를 통해 테이블 리스트를 조회하려면, 아래와 같은 쿼리를 쓰시면 됩니다.

    SELECT name FROM sqlite_master
    WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'
    ORDER BY 1

    좀 더 자세한 내용을 확인하고 싶으시면 "http://www.sqlite.org/cli.html"의 내용을 참고하시길 바랍니다.

    감사합니다.

  10. Blog Icon
    초보개발자

    SQLiteDataBase 정말 잘 읽고 갑니다. 설명도 너무 잘해주시고 이해할 수 있게 흐름도 너무 잘 이끌어 주셔서 덕분에 공부하고 갑니다!!

  11. 도움이 되신 것 같아서, 보람을 느끼게 되네요.
    많은 내용 담을 수 있도록 노력하겠습니다.
    감사합니다.

  12. Blog Icon
    환경론자

    정독 후 댓글 올립니다.

    정말 큰 도움이 되었습니다.

    다시 한 번 개념들이 정리가 되네요.

    차분하고 상냥한 설명 감사드립니다.

  13. 도움이 되셨다니 다행입니다.
    내용을 보다가 도움드릴 내용이 있으면, 언제든 질문글 남겨주세요.
    감사합니다.

  14. Blog Icon
    Gisys

    안녕하세요, 좋은 글 잘 읽었습니다. 지금 디비 설계 중에, 경위도 좌표를 db로 만들어서 원할 때 경위도 좌표를 불러오는 식으로 db를 설계하고 싶은데 예제는 제가 사용자 데이터를 입력하는 예제여서 질문드립니다. 혹시 미리 경위도 좌표를 저장 후, db로 불러오려면 어떻게 해야할까요?

  15. 보통 경도, 위도의 경우 소수점으로 관리하기 때문에, 코드 상에서 double 타입으로 정의할 텐데요. SQLite에서는 REAL이라는 데이터 타입을 사용하시면 됩니다.
    (https://sqlite.org/datatype3.html)

    본문의 예제가, DB에 데이터를 저장하는 코드로만 작성되어 있지는 않습니다.
    [STEP-5]의 코드를 보시면 SELECT문을 사용하여 DB에 저장된 데이터를 읽어오는 코드가 있는데, 해당 부분을 참고하시면 큰 문제없이 DB로부터 데이터를 읽어오는 코드를 만드실 수 있을 것 같네요.

    물론, 본문에서는 REAL 형식으로 저장된 데이터를 double로 가져오는 부분이 없으므로 예제를 참고하여 만드셔야합니다.

    작업하시다가 잘 이해가 안되거나, 문제가 생기면 다시 글 남겨주세요~

    감사합니다.

  16. Blog Icon
    GODISGOOD

    전 예제는 잘 됐는데 이번꺼는 안되네요...ㅠ.ㅠ
    맞게 코드 쳤는데 세이브할려고 하면
    자꾸 앱 작동이 중지가 됩니다...ㅠㅠㅠㅠㅠㅠㅠ

  17. 에러 메시지를 같이 적어주시면 도움을 드리기가 용이할 것 같습니다.
    그리고 본문의 예제 코드와, 작성하신 코드를 천천히 비교해보시길 바랄게요.

    예제의 내용이 100% 완벽할 순 없지만, 나름 검증을 하고 정리하였기 때문에 기본 동작에는 문제가 없을 것이라 생각되네요.

    다시 한번 살펴보시고, 문제점을 찾기 힘드시면 글 남겨주세요.

    감사합니다.

  18. Blog Icon
    wjd

    글 너무 잘보고있습니다. 제가 로그인에 필요한 DB와 어플 안에서 점수를 만들거라서 점수의 DB를 같이 놔두면 insert에서 오류가 납니다 DB를 두개 두는 방법은 없나요? 혹은 테이블을 두개 두고싶습니다 방법을 알려주세요 부탁드려요!

  19. 질문을 조금 더 구체적으로 정리해주시면 좋을 것 같은데요.

    "로그인에 필요한 DB와 점수의 DB를 같이 놔두면 insert에서 오류가 난다"고 하셨는데, DB를 따로 두는 것과 insert에서 오류가 난다는 것은 관계가 없습니다. 아마 코드를 잘 못 작성하신 게 아닌가 생각이 드네요.

    테이블을 포함한 DB 구조에 대한 설계는 같은 요구사항이라도 개발자에 따라 그 설계 결과가 다르기 때문에 "이렇게 하세요!!" 라고 말씀드리기가 어려운 문제입니다.

    적절한 이론 학습과 시행착오, 경험을 통해 자신만의 노하우를 쌓아갈 수 밖에 없습니다. 아니면 주변의 경력자로부터 도움을 받는 방법도 좋은 선택 중 하나겠죠.

    일단 "wjd"님이 만들고자 하는 앱의 요구사항이 어떻게 되는지 구체적으로 파악할 수 없지만, 두루뭉실하게 조언을 드리자면..
    하나의 DB에 로그인 정보에 관한 테이블과 점수 테이블을 각각 만드시면 될 것 같습니다. 물론, 하나로 만들어도 문제가 없을 것 같지만, 학습과 경험을 위해서 따로 테이블을 두시면 될 것 같네요.

    일단 여기서부터 출발해보세요. 진행하시다가 막히는 부분이 있으면 다시 글 남겨주시면 최대한 도움드리겠습니다.

    물론, 질문의 내용이 구체적일수록 답변의 내용 또한 좀 더 자세하게 작성될 수 있습니다.

    감사합니다.

  20. Blog Icon
    GODISGOOD

    ㅠㅠ 네네 하나하나 다 비교하면서 확인했습니다.
    다시 만들어 보기까지 했는데도... 안되네요..ㅠㅠㅠ
    에러메세지도 안뜨고 컴파일도 제대로 됐는데 앱을 실행시키자 마자 바로 앱작동이 중지되었습니다. 그러면서 죽어버리니 문제를 알 수도 없어 너무 답답합니다..ㅠㅠㅠ

  21. 일단 제대로 실행되지 않는 이유를 파악하기 위해서는 정확한 에러 메시지를 확인할 수 밖에 없습니다.
    안드로이드 스튜디오에서 앱을 실행시킨 다음, 로그캣을 통해 출력되는 메시지를 확인해 주세요. 그 중 빨간색으로 표시되는 메시지가 있는데, 해당 내용을 확인하신 다음 질문글에 올려주세요.

    본문의 예제에 대한 실행은 확인했지만, 혹시라도 빠진 내용이나, 버전 또는 환경에 따른 실행 차이가 있을 수 있으므로, 에러 메시지를 확인해야 정확한 원인을 파악할 수 있을 것 같습니다.

    감사합니다.

  22. Blog Icon

    비밀댓글입니다

  23. 음, 질문의 내용으로만 보아, DB를 다루어본 경험이 많지 않으신 것 같습니다. DB에 관한 내용을 학습한 다음, DB 관련 프로그래밍을 하는 것이 조금 더 도움이 되실 것 같은데요.

    먼저, 로그인 테이블을 만들고 점수 테이블을 만든다고 하셨는데, 올려주신 코드를 보면 두 테이블 간의 연관성이 전혀 보이지 않는 것 같습니다.
    간단하게 얘기하자면, 점수 테이블 저장된 점수가 누구의 점수인가... 하는 연결점.
    사실 이런 내용은 이렇게 답글만으로 설명드리기 어려운 부분이라, 여러 이론서와 예제 또는 강의를 통해 습득할 수 밖에 없을 것 같습니다.

    그리고 dbmanager를 하나 둔다고 하셨는데, dbmanager가 뭘 의미하는지 모르겠습니다. 본문에서는 dbmanager라는 용어를 사용하지 않고 있는데요. "wjd"님께서 말하는 dbmanager라는 것이 계약클래스를 의미하는지, 본문의 DBHelper를 말하는지, 아니면 SQLiteDatabase 객체에 대한 참조를 말하는건지. 조금 명확히 구분할 필요가 있다고 생각합니다.

    또한 "QR 코드 값을 DB에 반영하고 다른 액티비티에서 표현해도 같은 점수가 뜨는가"라는 질문에 대한 답변은, "그렇게 만들면 됩니다." 입니다. DB에 저장하고, 그 값을 다른 액티비티에서 읽어오면 당연히 반영된 값이 뜨겠지요.

    음, 올려주신 질문들이 사실, 너무너무 기초적인 내용들이라 이렇게 답글만으로 설명하기엔 한계가 너무 많네요.
    그리고 제가 올려드린 답글 또한 크게 도움이 되지 않을 것 같기도 하구요.

    일단, 기초적인 내용을 먼저 학습하시길 바랍니다. 무작정 만들어가며 배우는 것도 좋은 방법이지만, 어느 정도 기초를 쌓은 다음, 구현을 하셔도 늦지 않습니다.

    여러 책과 강좌, 또는 주변 사람들의 도움을 받아서 기초적인 내용에 대한 학습을 먼저 진행하시길 바랄게요.

    도움을 드리기에 한계가 있음을 양해바랍니다.

    감사합니다.

  24. Blog Icon
    GODISGOOD

    하나의 데이터 베이스안에 여러개의 table을 생성하고 싶으면 어떻게 해야하나요 ??ㅠ,ㅠ
    너무 귀찮게 해드리는 것같아서 죄송해유 ..ㅠㅠㅠㅠ

    저는 이런 방식으로 만들었거든요

    String sqlCreateTbl = "CREATE TABLE IF NOT EXISTS CONTACT_T (" +
    "NO "+"INTEGER NOT NULL," +
    "NAME "+"TEXT," +
    "PHONE"+"TEXT," +
    "OVER20"+"INTEGER" +")";


    String sqlCreateTbl2 = "CREATE TABLE IF NOT EXISTS CONTACT_B (" +
    "NO "+"INTEGER NOT NULL," +
    "NAME "+"TEXT," +
    "PHONE"+"TEXT," +
    "OVER20"+"INTEGER" +")";

    db.execSQL(sqlCreateTbl);
    db.execSQL(sqlCreateTbl2);


    컴파일 상으로는 잘 생성됐다고 말하기는 하는데
    막상 두번째 테이블 그니깐 CONTACT_B 안에 레코드를 짚어넣을려고 하니까 앱이 죽어버리네요 ㅠㅠㅠㅠ



    SQLiteDatabase db = dbHelper.getWritableDatabase();

    db.execSQL("DELETE FROM CONTACT_B");

    String sqlinsert = "INSERT INTO CONTACT_B VALUES('9','JEE','001-000','0');";

    db.execSQL(sqlinsert);

    이런식으로 INSERT 시도하는데 뭐가 문제인지 모르겠습니다..ㅠㅠ
    아무래도 테이블이 생성되지 않은 것 같은데... ㅠㅠㅠㅠ 도와주세유

  25. 하나의 DB 안에 여러 개의 테이블을 생성하려면 CREATE 문을 여러 개 써서 만들면 됩니다. 질문에 올려주신 것처럼 말이죠.

    그리고 두 번째 테이블(CONTACT_B)에 값을 넣을 때 죽어버린다고 하셨는데, 그 때 출력되는 에러 메시지를 같이 올려주시는 게 좋을 것 같습니다.
    에러 메시지 없이 문제점을 유추하기는 좀 힘든 점이 많습니다.

    그래도 에러가 날 수 있는 부분을 살펴보자면,
    올려주신 코드를 소스에서 그대로 복사한 내용이라면, 테이블을 만들 때 사용한 SQL문이 잘못된 것 같습니다.

    본문의 SQL 문을 보면 각 필드 이름 뒤에 스페이스(Space)가 하나씩 들어가 있는데요, 질문글에 올려주신 코드는 "PHONE"과 "OVER20" 뒤에 스페이스가 빠져 있네요.

    다음과 같은 문장이 되어야 하는데..
    "CREATE TABLE IF NOT EXISTS CONTACT_T (NO INTEGER NOT NULL, NAME TEXT, PHONE TEXT, OVER20 INTEGER)"

    아래처럼 "PHONE"과 "OVER20" 부분이 잘 못 만들어지는 것이죠.
    "CREATE TABLE IF NOT EXISTS CONTACT_T (NO INTEGER NOT NULL, NAME TEXT, PHONETEXT, OVER20INTEGER)"


    SQL 문장을 만든 다음, execSQL() 함수로 실행하기 전 디버그 메시지로 한번 출력해보세요. 어디가 문제인지를 좀 더 명확하게 확인할 수 있습니다.

    수정해보시고, 추가적으로 궁금한 점이 있으면 질문글 남겨주세요.

    감사합니다.

  26. Blog Icon
    GODISGOOD

    ㅠㅠㅠㅠ 말씀하신대로 뒤에 스페이스를 짚어넣었는데 그건 문제가 안되는 것 같습니다...
    왜냐면 첫번째 table 만들 때 도 동일하게 띄어쓰기를 하지 않았거든요...
    디버깅을 어떻게 하는지를 잘 몰라서... 일단 android monitor에 나온 걸 봤는데요

    12-07 13:21:31.492 12839-12839/com.example.asus.lasttest E/SQLiteLog: (1) no such table: CONTACT_B
    12-07 13:21:31.493 12839-12839/com.example.asus.lasttest W/System.err: android.database.sqlite.SQLiteException: no such table: CONTACT_B (code 1): , while compiling: DELETE FROM CONTACT_B
    12-07 13:21:31.493 12839-12839/com.example.asus.lasttest W/System.err: #################################################################
    12-07 13:21:31.493 12839-12839/com.example.asus.lasttest W/System.err: Error Code : 1 (SQLITE_ERROR)
    12-07 13:21:31.493 12839-12839/com.example.asus.lasttest W/System.err: Caused By : SQL(query) error or missing database.
    12-07 13:21:31.493 12839-12839/com.example.asus.lasttest W/System.err: (no such table: CONTACT_B (code 1): , while compiling: DELETE FROM CONTACT_B)

    테이블을 찾을 수 없다고 뜨네요...

  27. 에러 메시지의 내용을 보면, 아래와 같습니다.

    "no such table: CONTACT_B (code 1): , while compiling: DELETE FROM CONTACT_B"

    의미 그대로, DELETE 문장을 수행하려고 하는데, CONTACT_B 라는 테이블이 없어서 에러가 나는 것이죠.
    테이블이 제대로 만들어지지 않았으므로 CREATE 문장을 잘 보셔야 합니다.

    제가 위에서 적어드린 답변에서, execSQL() 함수 실행 전에 디버그 메시지로 출력해보라고 했는데, 혹시 해보셨나요?

    만약 안해보셨다면....
    음, 간단하게 아래 코드를 실행해 보세요. db.execSQL() 함수 바로 위에 아래 코드를 붙여넣고 실행해 보세요.

    System.out.println("##################################") ;
    System.out.println(sqlCreateTbl2) ;
    System.out.println("##################################") ;

    그리고 android monitor에서 "###.." 사이에 출력되는 SQL 문장을 확인해보세요.
    그럼 CREATE 문에 무엇이 잘못되어 있는지 확인하실 수 있습니다.

    감사합니다.

  28. Blog Icon
    GODISGOOD

    12-07 16:22:50.055 18971-18971/com.example.asus.lasttest W/System.err: #################################################################
    12-07 16:22:50.055 18971-18971/com.example.asus.lasttest W/System.err: Error Code : 1 (SQLITE_ERROR)
    12-07 16:22:50.055 18971-18971/com.example.asus.lasttest W/System.err: Caused By : SQL(query) error or missing database.
    12-07 16:22:50.055 18971-18971/com.example.asus.lasttest W/System.err: (no such table: CONTACT_D (code 1): , while compiling: INSERT INTO CONTACT_D VALUES('7','dEE','001-001','0');)
    12-07 16:22:50.055 18971-18971/com.example.asus.lasttest W/System.err: #################################################################


    확인해 보니까 이부분인것 같네요...ㅠ

  29. CONTACT_D 는 갑자기 어디서 나온건가요?
    그리고 제가 위에 올려드린 답글은 확인하신 건가요?

  30. Blog Icon

    비밀댓글입니다

  31. 네. 확인하셨군요.
    sqlCreateTbl2 에 저장된 CREATE 문이 어떻게 출력되는지 올려주지 않으셔서 확인 안하신 줄 알았어요.
    CREATE 문은 문제 없나요?