안드로이드 액션바에 뒤로 가기 버튼 추가하기

 

 

 

실행 화면

 

 

 

 

 

 

 

 

 

 

 

 

▲ 1. AndroidManifest.xml 파일을 연다.

 

 

 

 

 

▲ 2. 뒤로가기 버튼을 추가할 activity에서 android:parentActivityName 속성을 추가한다.

 

속성 값으로는 뒤로 가기 버튼을 눌러서 이동될 activity의 name을 주면 된다.

 

 

ex)

<activity                                 <- 뒤로 가기 버튼을 추가할 액티비티

   android:name=                     <- 해당 액티비티의 이름

   android:parentActivityName     <- 뒤로 가기 버튼으로 이동할 엑티비티 이름

/>

 

 

 

 

 

2019/05/15 - [Android] - 안드로이드 메뉴 만들기

 

안드로이드 메뉴 만들기

안드로이드 액션바에 메뉴 아이템 추가하기 실행 화면 ▲ 1. res - [New] -> [Android Resource Directory] 클릭 ▲ 2. Resource type에서 menu 선택 후 OK 버튼 클릭 ▲ 3. 생성 된 menu 디렉터리에서 [New]..

godog.tistory.com

 

▲ 메뉴를 추가하고 해당 속성을 사용하여도 둘 다 적용되므로 메뉴에 뒤로가기 버튼을 구현하고 싶을 경우 간단히 한 줄로 구현할 수 있다.

 

안드로이드 액션바에 메뉴 아이템 추가하기

 

 

 

실행 화면

 

 

 

 

 

 

 

 

 

 

 

▲ 1. res - [New] -> [Android Resource Directory] 클릭

 

 

 

 

▲ 2. Resource type에서 menu 선택 후 OK 버튼 클릭

 

 

 

 

▲ 3. 생성 된 menu 디렉터리에서 [New] -> [Menu resource file] 클릭

 

 

 

 

 

▲ 4. 레이아웃을 만들 때처럼 Palette에서 item을 끌어다가 간단히 추가

 

 

 

 

▲ 5. 

id : 자바 코드에서 각 아이템을 클릭하였을 때 동작을 지정하기 위해서 필요

- title : 아이콘이 아닌 목록으로 표시할 때 보일 메뉴 명

- icon : 표시할 아이콘 이미지

- showAsAction : always를 클릭하면 항상 아이콘이 보인다. (아이콘이 없을 경우 title로 표시)

 

 

 

icon 이미지 파일을 직접 만들경우 이미지 리소스의 크기는 아래 글을 참조하면 될 것 같다.

2019/05/15 - [Android] - 안드로이드 이미지 리소스 크기

 

 

 

▲ 6. 

showAsAction

- never : 메뉴를 [ ... ] 안으로 감춤

- ifRoom : 공간이 남을 경우 [ ... ] 밖으로 표시

- always : 항상 [ ... ] 밖으로 표시

 

 

 

 

 

▲ 7.

- 이제 메뉴를 보여줄 엑티비티의 .java 파일로 간다.

 

- 클래스 안의 커서를 두고 Ctrl + O 를 누른다.

 

- onCreateOptionsMenu 를 찾아서 추가

 

 

 

 

 

▲ 8.  7번 과정 처럼 onOptionsItemSelected 도 추가

 

 

 

 

 

 

▲ 9. 

- onCreateOptionsMenu 메소드에서는 .inflate를 사용하여서 연결하고자하는 menu 리소스 파일과 연결해 준다.

 

- onOptionsItemSelected 메소드에서는 item의 지정하였던 id를 이용하여서 각각의 메뉴가 선택되었을 때 동작할 코드를 구현하면 된다.

 

 

 

 

 

▲ 10. 실행 후 메뉴 아이템을 선택하면 Toast 메시지를 확인할 수 있다.

안드로이드에서는 ImageView 사용하여 이미지 리소스를 보여줄 수 있다. 여기서 Drawable이나 Bitmap을 사용하여서 이미지 크기를 조절할 수도 있다.

 

 

 

- 밀도에 따른 Drawable 파일 만들기

밀도 : 안드로이드는 각종 기기에 사용하므로, 화면 크기에 따라 밀도가 달라진다.

 

그래서 밀도에 따른 이미지를 구분하여 파일을 만든 후 이미지 리소스를 넣는다면 이보다 작은 밀도를 가진 기기에서는 이미지가 축소되고, 큰 기기에서는 확대되어서 보이게 된다.

 

 

밀도 단위 메뉴(홈) 아이콘  액션바/상태바 아이콘 
mdpi 1px(픽셀) = 1dp 48 x 48 px 24 x 24 px
hdpi 1.5px(픽셀) = 1dp  72 x 72 px  36 x 36 px 
xhdpi 2px(픽셀) = 1dp 96 x 96 px 48 x 48 px 
xxhdpi 3px(픽셀) = 1dp  144 x 144 px  72 x 72 px 
xxxhdpi 4px(픽셀) = 1dp 192 x 192 px 96 x 96 px 

( ※ dp = dpi/160, 기기마다 해상도의 크기가 다르므로 단위로 dp를 사용해야 동일하게 보인다. )

 

 

 

▲ 1. res 디렉터리에서 [New] -> [Android Resource Directory] 클릭

 

 

 

 

 

▲ 2. Resource type을 drawable로 선택 후 목록에서 Desity(밀도)를 선택한 후 >> 버튼을 클릭

 

 

 

 

 

▲ 3. 밀도를 선택한다. 현재 나오는 기기들의 밀도를 생각하여 선택해준다.

 

 

 

 

 

▲ 4. 이제 추가 할 이미지를 복사 후 drawable 디렉터리를 선택한 후 붙여넣기 한다.

 

 

 

 

 

▲ 5. 그러면 추가한 xhdpi 디렉터리를 확인할 수 있고, 이 디렉터리로 이미지 리소스를 넣어주면 된다.

 

 

안드로이드 스튜디오 자동 import 설정하기

 

 

 

 

 1. [File] -> [Settings...] 으로 간다.

 

 

 

 

 

 

 2. [Editor] -> [General] -> [Auto import] 로 간다.   (or 검색창의 Auto import를 검색)

 

 

 

 

 

 

 3. 각 XML 이나 Java에서 자동으로 improt 될 필요가 있다고 생각하는 부분을 체크해준 후 OK 해준다.

 

 

 

 

 

+)

자동으로 import 되기 때문에 최종적으로 작업이 끝났을 경우 사용하지 않는 것들이 import 되어있을 경우가 많다. 

 

import문들을 보면 사용하지 않는 것은 회색으로 처리되어 있으니, 작업이 끝난 후 최종적으로 한 번 import문들을 정리해 주는 것이 좋다.

안드로이드 코드 난독화

 

컴파일된 자바 파일을 디컴파일하면 코드가 노출된다. 디컴파일이란 간단히 말하여 컴파일의 반대 과정이다.

 

 

이러한 디컴파일로 인하여 코드의 노출을 방지하기 위해서 디컴파일해도 코드를 읽기 어렵게 만들기 위한 일종의 코드 보안 기술이다.

 

 

그래서 앱 출시 전 난독화를 하는 것이 좋은데 이는 안드로이드 스튜디오에서 기본적으로 지원하는 난독화 프로그램 프로가드(Proguard) 사용할 수 있다.

 

 

 

 

 

 

 

▲ 1. 모듈 수준의 그레이들 파일을 열어준다.

 

 

 

 

▲ 2. mainfyEnabled의 값으로 true를 준다.

 

 

 

 

 

 3. Sync Now를 클릭한다.

 

 

 

이것으로 코드의 난독화를 하게 된다.

 

 

 

 

 

 

+)

 안드로이드에서는 debug 빌드와 release 빌드를 할 수 있다.

 

debug 빌드 : 코딩 중 <Run>(▶) 버튼으로 연결된 기기나 에뮬레이터로 실행하였을 경우

 

release 빌드 : 작업이 끝나 최종적으로. apk 파일을 만들 경우

 

기본적으로는 release 부분만이 선언되어있지만 이를 복사하여서 dubug문을 만들면 debug 빌드 시에도 코드 난독화가 일어나게 할 수 있다.

 

 

 

이번 글에서는 이전 글 "안드로이드 DB(데이터베이스) 생성" 이후의 내용입니다.

 

2019/04/21 - [Android] - 안드로이드 DB(데이터베이스) 생성

 

이전 글에서 데이터베이스 파일을 생성하였다면 이제 그 데이터베이스 파일에 데이터를 추가하고 삭제하는 방법을 알아보겠습니다.

 

 

/* 실행 결과 */

 

 

 

이전 글에 이어서 작성하는 것으로, 메인 자바 외에 DBHelper나 레이아웃(. xml) 파일은 이전 글에서 확인해 주세요.

 

 

/* 메인 레이아웃(activity_main.xml) */

package com.tistory.godog.dbtest;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText editText, editText2;
    ListView listView, listView2;

    DBHelper dbHelper;
    SQLiteDatabase db = null;
    Cursor cursor;
    ArrayAdapter adapter, adapter2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = findViewById(R.id.editText);
        editText2 = findViewById(R.id.editText2);
        listView = findViewById(R.id.listView);
        listView2 = findViewById(R.id.listView2);

        dbHelper = new DBHelper(this, 4);
        db = dbHelper.getWritableDatabase();    // 읽기/쓰기 모드로 데이터베이스를 오픈


    }


    public void listUpdate(View v) {
        cursor = db.rawQuery("SELECT * FROM tableName", null);
        startManagingCursor(cursor);    //엑티비티의 생명주기와 커서의 생명주기를 같게 한다.

        adapter = new ArrayAdapter(this,
                android.R.layout.simple_list_item_1);
        adapter2 = new ArrayAdapter(this,
                android.R.layout.simple_list_item_1);

        while (cursor.moveToNext()) {
            adapter.add(cursor.getString(0));
            adapter2.add(cursor.getString(1));
        }

        /*
        cursor.moveToFirst();
        cursor.moveToPrevious();
        cursor.moveToPosition(2);
        */

        listView.setAdapter(adapter);
        listView2.setAdapter(adapter2);
    }



    public void insert(View v) {
        String name = editText.getText().toString();
        String info = editText2.getText().toString();
        db.execSQL("INSERT INTO tableName VALUES ('" + name + "', '" + info + "');");

        Toast.makeText(getApplicationContext(), "추가 성공", Toast.LENGTH_SHORT).show();

        editText.setText("");
        editText2.setText("");
    }

    public void delete(View v) {
        String name = editText.getText().toString();
        db.execSQL("DELETE FROM tableName WHERE name = '" + name + "';");
    }

}

 

이전 과 달리 insert, delete, listUpdate 함수들이 추가되었다.

 

이들은 각각 버튼 '추가' '삭제' 'UPDATE'를 클릭하였을 때 실행된다.

 

 

insert 함수와 delete 함수를 보면 정작 데이터베이스의 데이터를 추가, 삭제하기 위해서 실행하는 문은 db.execSQL 한 줄이면 된다.

 

 

 

- insert 함수 

  db.execSQL("INSERT INTO tableName VALUES ('" + name + "', '" + info + "');");

execSQL은 SQL문을 실행시켜주는 기능을 한다.

 

위 문장은 tableName 테이블의 (name, info) Values를 insert(삽입) 한다는 의미이다.

 

 여기서 tableName 테이블의 이름으로 이는 저번 테이블을 CREATE(생성) 할 때 정한 테이블의 이름이다. 그러니 테이블을 생성할 때 의미의 맞게 테이블의 이름을 준 후 사용하면 된다.

 

 

※  ""(큰 따옴표), ''(작은 따옴표)을 어찌 사용해야 할지 혼란스러운 분들을 위해 간단한 팁을 드리자면 ""(큰 따옴표)는 안드로이드(자바)에서 사용되는 것이고, ''(작은따옴표)는 SQL문에서 사용되는 것이라 생각하면 편하다.

 

 위 문을 예로 들면 ""(큰 따옴표)만을 이어보면

"INSERT INTO tableName VALUES ('name', 'info');"   같은 문이 된다.

 

이 SQL문을 실행하는 것인데, 여기서 name과 info가 ''(작은따옴표)의 감싸 져 있는데 이들이 저장될 변수명 들이기 때문이다.

 

그러면 위 코드에서 ""(큰 따옴표)로 나눈 것도 name과 info가 변수명이기 때문이라는 것을 알 수 있다. 그러니 이는 평소 변수들을 문자열로 출력할 때처럼 +로 이어준 것뿐이라는 것이다.

 

 

정 어렵다면 우선 위 "INSERT INTO tableName VALUES ('name', 'info');"처럼 SQL문을 먼저 작성해본 후 여기서 변수만을 ""로 안 감싸도록 분리해서 한 문자열로 만들도록 하면 쉬울 것이다.

 

 

 

 

- delete 함수

db.execSQL("DELETE FROM tableName WHERE name = '" + name + "';");

delete 함수도 insert 함수랑 마찬가지이다.

 

단, SQL문에서 DELETE를 사용하여 삭제해준다. 그리고 WHERE은 if문 같은 것으로

 

조건은 name ='name'; 으로 editText1에서 name을 적은 후 삭제 버튼을 클릭하면 이미 기존 테이블에 저장되어 있는 name 데이터와 같은 것이 있으면 삭제되도록 한다.

 

 

 

 

 

- listUpdate 함수

여기서는 rawQuery 메서드를 사용하는데 SELECT 명령을 사용하기 위해서는 execSQL이 아닌 rawQuery를 사용해야만 한다.

 

 

rawQuery는 리턴 값으로는 Cursor 객체를 주며 이는 행을 가리키게 된다.

 

name info
aa 123
bb 234

             ↓ getString(0)            ↓getString(1)

cursor->

 

 

cursor는 행을 가리키므로 cursor를 아래로 이동[movoToNext()]을 while문으로 돌리고

 

cursor.getString(0) 는 cursor가 가리키는 행에서 첫 번째 열(name 열) 데이터를 가져오고

마찬가지로 cursor.getString(1) 는 cursor는 두 번째 열(info 열) 데이터를 가져온다.

 

 

그리고 이를 리스트 어뎁터의 추가하여 리스트 뷰로 출력하도록 만든 것이다.

 

 

여기서는 rawQuery와 Cursor 객체를 사용하여서 데이터베이스에서 데이터들을 가져오는 방법을 이해하였다면 된다.

 

이전 '안드로이드 DB(데이터베이스) 생성'

2019/04/21 - [Android] - 안드로이드 DB(데이터베이스) 생성

 

글에서 SQLiteOpenHelper를 상속받는 클래스 속 onUpgrade() 메소드의 관하여 알아보겠다.

 

 

 

 

/* DBHelper.java */

package com.tistory.godog.dbtest;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

    static final String DATABASE_NAME = "test.db";
    //static final int DATABASE_VERSION = 2;

    public DBHelper(Context context, int version) {
        super(context, DATABASE_NAME, null, version);
    }
/*
    public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }
*/

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE tableName ( name TEXT, info TEXT);");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS tableName");
        onCreate(db);
    }

}

 

- onUpgrade()는 생성자에서 버전을 업그레이드하면 실행된다.

 

 

- 버전을 높일 때는 보통 db에 새로운 열 테이블을 추가하거나 변경 사항이 있을 때이므로 기존의 테이블은 삭제해준 후 다시 생성하여야 한다.

 

 

ex) 만약 위 프로그램을 한번 실행한 후 db파일이 생성되었으면 그 후 다시 실행하였을 때는 db파일이 있으므로 onCreate가 실행되지 않는다. 

 그러므로 한번 실행 후 onCreate에서 INTEGER인 열을 하나 추가하고 다시 실행하여도 테이블은 변하지 않는다.

 하지만 이 때 버전을 더 높은값(업그레이드)을 준다면 다시 onUpgrade가 실행되어 기존의 테이블을 지운 후 onCreate를 실행하여서 새로운 테이블을 생성하도록 만든다.

 

 

- 이 때 주의할 점은 기존의 테이블은 삭제한 후 새로운 테이블은 만드는 것이므로 기존 테이블의 데이터는 다 삭제가 된다.

 그러므로 기존의 데이터를 보존하고 싶다면 테이블을 삭제하기 전 데이터들을 저장하고, 테이블을 다시 만든 후 값들을 다시 저장해주면 된다.

 

 

 

 

안드로이드 DB(데이터베이스)를 사용하는 방법을 알아 보겠슴다.

 

데이터베이스를 이용하는 곳이 많은 만큼 중요한 것이고, 가독성을 높이기 위해서 여러 글로 나누어서 작성하려고 합니다.

 

우선은 안드로이드 DB(데이터베이스) 1편  'DB 생성'입니다.

 

 

 

 

 

실행 화면

 

 

 

 

/* 메인 레이아웃(activity_main.xml) */

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/editText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10"
            android:hint="이름"
            android:inputType="textPersonName" />

        <EditText
            android:id="@+id/editText2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10"
            android:hint="정보"
            android:inputType="textPersonName" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="insert"
            android:text="추가" />

        <Button
            android:id="@+id/button2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="delete"
            android:text="삭제" />
    </LinearLayout>

    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="listUpdate"
        android:text="Update" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <ListView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1">


        </ListView>

        <ListView
            android:id="@+id/listView2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1" />
    </LinearLayout>

</LinearLayout>

 

주 내용은 데이터베이스이므로 대충 그냥 리스트뷰를 2개 사용하여서 각각 출력하도록 만들었다.

 

하지만 데이터베이스를 사용할 때 보통 같이 쓰는 것 중 하나가 바로 커스텀 리스트 뷰인데 이에 관하여서는

2019/04/14 - [Android] - 안드로이드 리사이클 러뷰(RecyclerView) (1)

 

해당 글을 참조하여서 만들어보는 것을 추천한다.

 

 

 

 

 

 

 

/* 메인 자바 코드 ( MainActivity.java ) */

package com.tistory.godog.dbtest;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText editText, editText2;
    ListView listView, listView2;

    DBHelper dbHelper;
    SQLiteDatabase db = null;
    Cursor cursor;
    ArrayAdapter adapter, adapter2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = findViewById(R.id.editText);
        editText2 = findViewById(R.id.editText2);
        listView = findViewById(R.id.listView);
        listView2 = findViewById(R.id.listView2);

        dbHelper = new DBHelper(this, 3);
        db = dbHelper.getWritableDatabase();    // 읽기/쓰기 모드로 데이터베이스를 오픈


    }

}

 

- 우선은 데이터베이스 생성만을 알아보는 것이므로 메인에서는 변수 선언과 초기화 말고 별거 없다.

 

- 18라인 : 만들어야 할 클래스 파일.

 

- 19라인 : SQLiteDatabase에는 SQL 명령을 생성, 삭제, 실행하고 기타 일반적인 데이터베이스 관리 작업을 수행하는 메소들이 있다.

 

- 20라인 : Cursor는 안드로이드에서 DB에서 가져온 데이터를 쉽게 처리하기 위해서 제공하는 2차원 테이블을 가지는 인터페이스이다.

 

 

 

 

 

 

이제 18라인에서 만들어야 한다고 했던 자바 파일을 만들기 위해서 자바 파일만을 하나 생성해준다.

/* DBHelper.java */

package com.tistory.godog.dbtest;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

    static final String DATABASE_NAME = "test.db";
    //static final int DATABASE_VERSION = 2;

    public DBHelper(Context context, int version) {
        super(context, DATABASE_NAME, null, version);
    }
/*
    public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }
*/

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE tableName ( name TEXT, info TEXT);");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS tableName");
        onCreate(db);
    }

}

 

- 파일을 생성하였으면 SQLiteOpenHelper를 상속받아준다.

 

- SQLiteOpenHelper는 데이터베이스 생성 및 버전 관리를 관리하도록 도와주는 클래스이다.

 

 

+ 상속을 받아준 후 클래스 안에서 "Ctrl+i"를 눌러서 모두 추가해 준 후,

 "Alt + Insert"->"Constructor"로 생성자 하나를 추가해주면 기본적인 세팅은 끝난다.

 

 

- 주석된 부분을 보면 본래 생성자를 볼 수 있다. 이는 선언한 생성자와 많이 매개변수가 다른데 별 의미는 없다. 단, 데이터베이스 파일을 하나만 사용할 경우 위처럼 상수로 데이터베이스 이름과 버전을 선언해두고 인수로 줌으로써 에러를 줄이고 수정을 용이하게 만든다.

(+ 보통 파일 내에서 테이블 단위로 나눌 수 있으므로 db파일을 하나만 사용한다. )

 

 

- onCreate : 데이터베이스가 처음 생성될 때 호출된다. 그러므로 테이블의 생성이 일어난다.

 

※ execSQL :  Select 명령 외에 SQL 명령들을 실행한다. 

 

- 23라인 :  괄호 안을 보면 ( name TEXT, info TEXT)로 되어있다. 이는 ( '열 항목명' '데이터 형', )이다. 숫자 데이터 형을 만들려면 "(num INTEGER)" 이렇게 만들면 된다.

 

 

- onUpgrade

2019/04/21 - [Android] - 안드로이드 DB(데이터베이스) onUpgrade

 

 

 

여기까지 데이터베이스 생성 및 관리를 도와주는 SQLiteOpenHelper를 상속받은 클래스 파일을 만들었으면 데이터베이스를 만들 준비는 끝났다.

 

 

 

다음 글에서는 이어서 데이터베이스의 데이터를 추가, 삭제하는 방법을 알아보겠다.

 

+ Recent posts