로컬서버와 테스트 시 일반적으로 localhost 또는 127.0.0.1로 테스트를 진행하지만, 안드로이드 에뮬레이터와 테스트 시에는 10.0.2.2 로 설정을 해야한다.

 

안드로이드 에뮬레이터 네트워킹 설정참고

https://developer.android.com/studio/run/emulator-networking?hl=ko

사이드 바 열고/닫기
Windows Ctrl + B
MacOS : Cmd + B


빠른 열기(파일이나 기호 탐색)
Windows Ctrl + P
MacOS : Cmd + P


모든 명령 표시 (에디터의 모든 명령에 접근)
Windows Ctrl + Shift + P
MacOS : Cmd + Shift + P


편집 닫기
Windows Ctrl + W
MacOS : Cmd + W

찾기 탐색
Windows Ctrl + F
MacOS : Cmd + F


찾기(검색)/바꾸기(대체)
Windows Ctrl + H
MacOS : Cmd + Alt(Opt)+ F 

줄 위로 이동할수
Windows Alt(Opt) + Up
MacOS : Alt + Up

줄 아래로 이동할수
Windows Alt(Opt) + Down
MacOS : Alt + Down

아래 줄 복사
Windows Alt(Opt) + Shift + Down
MacOS : Alt + Shift + Down

위에 줄 복사
Windows Alt(Opt) + Shift + UP
MacOS : Alt + Shift + UP

들여쓰기
MacOS : Tab
Widows : Tab

비교적 간단한 데이터를 저장하기위해 안드로이드 개발 시 SharedPreferences를 사용한다.
설정에 따라서 SharedPreferences 파일은 프레임워크에서 관리하며 비공개이거나 공개로 설정할 수 있다.


비공개로 설정할 경우 일반적인 방법으로는 저장된 데이터를 확인할수는 없지만, 루팅을 통해 관리자 권한을 획득한 경우 저장된 데이터를 확인할 수있다. 개인정보(카드번호, 인증 비밀번호) 등 중요한 데이터가 저장하여야 하는경우에는 SharedPreferences는 적합하지 않다.

Android SDK 23부터 androidx.security를 통해 EncryptedSharedPreferences를 제공한다.

https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences

 

EncryptedSharedPreferences  |  Android Developers

androidx.constraintlayout.core.motion.parse

developer.android.com

 

기본적인 사용방법은

MasterKey masterKey = new MasterKey.Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build();

_sharedPreferences = EncryptedSharedPreferences.create(
        context,
        "secret_shared_pref",
        masterKey,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

 

AES256방식에   암호화 키를 생성하여 생성한 암호화 키로 저장되는 데이터의 AES256_SIV(key) ,AES256_GCM (data)방식으로 키와 데이터를 각각 암호화한다. 

 

(암호화 키 생성 시 keystore를 사용하기 때문에 별도의 키입력을 하지 않으며, 생성된 암호화 키는 컨테이너에 저장되어 저장된 암호화키는 외부에서 추출하기 어렵다.)

https://developer.android.com/training/articles/keystore?hl=ko 

 

SharedPreferences와 동일하게 앱 삭제 시 저장된 데이터도 함꼐 삭제되며, 저장된 데이터는 아래와 같이 암호화되어 저장된다.

 

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="__androidx_security_crypto_encrypted_prefs_key_keyset__">12a90125a94b1b6aa4f97a630764baecf0e5f4c046a82c8b7947dabc06e71c5170a654d27c1903ffe2da75f84fd1dd011f37c917e9b8ce21d6b4673bbec9127ea066e6adb177f34bc98ff7259331f0d854b78d318226ed62dca1b38ec2dd32598a9357432e0e524a91e9a27393370337acbc5611f8808a6d8bcb724506dcb0e5513e6d6c0cb93d9af1f22cde840c93f3c213adce4bf1cd38aea6464724a1f0bf0bc7153ea84df9b5f4f4344b1a4408b191c8fc03123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e4165735369764b6579100118b191c8fc032001</string>
    <string name="AT+SCLGWnuSnzmogArPsMQJbMe92S7STOQ==">AR4mc1e8vYohj66Q91oCIoaMmklTD+DDPStaqibMtsDXatXDJJeskjW7eW9WNTGR</string>
    <string name="__androidx_security_crypto_encrypted_prefs_value_keyset__">128801163831f1d036fdc9a33da123fee29b5cda21a49d89684f5fac67e3d54a634de5d8ca990106d0535f0ce74e824f8352e027a37a7512a189a67c183781bf6ce08894b3a622bda2632205c5e5f64389cbff75556bd05d0f8dcd8dc495b9cff09681a7e1e18bae625b7d1513e7301c4ed2aa1e592b97df72474ee9437f75e20e1a019803d7cb3cd9f78f1a4408d7e699f101123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d4b6579100118d7e699f1012001</string>
    <string name="AT+SCLEaHEykuXI9WqXm7wB0ZOTyX/dPWI4=">AR4mc1euqAo6fPFARiDB0ACt92fHt3w1P+64D76rk9Dlyv4ya/hKWgQ5dHy6COIN</string>
</map>

 

 

EncryptedSharedPreferences 을 사용하기 위해서는 Security 라이브러리를 추가 해야함.

(build.gradle의 dependencies추가)

 

implementation "androidx.security:security-crypto-ktx:1.1.0-alpha03"

 

EncryptedSharedPreferences 공통 Class로 사용하기 위한 샘플 소스

 

import android.content.Context;
import android.content.SharedPreferences;

import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;

import java.io.IOException;
import java.security.GeneralSecurityException;

public class SharedPreferenceUtil {

    private static SharedPreferenceUtil sharedInstance;
    private Context _context = null;
    private MasterKey masterKey = null;

    private SharedPreferences _sharedPreferences = null;
    private SharedPreferences.Editor _shEditor = null;

    private final static String SECRET_PREFERENCE = "secret_shared_prefs";


    public static synchronized SharedPreferenceUtil getInstance(Context context) throws GeneralSecurityException, IOException {
        if (null == sharedInstance) {
            sharedInstance = new SharedPreferenceUtil(context);
        }
        return sharedInstance;
    }

    public SharedPreferenceUtil(Context context) throws GeneralSecurityException, IOException {
        MasterKey masterKey = new MasterKey.Builder(context)
                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
                .build();

        _sharedPreferences = EncryptedSharedPreferences.create(
                context,
                SECRET_PREFERENCE,
                masterKey,
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
        );
    }

    public void putString(String key, String data) {
        _shEditor.putString(key, data);
        _shEditor.commit();
    }

    public void putInt(String key, int data) {
        _shEditor.putInt(key, data);
        _shEditor.commit();
    }

    public void putFloat(String key, float data) {
        _shEditor.putFloat(key, data);
        _shEditor.commit();
    }

    public void putLong(String key, long data) {
        _shEditor.putLong(key, data);
        _shEditor.commit();
    }

    public void putBoolean(String key, boolean data) {
        _shEditor.putBoolean(key, data);
        _shEditor.commit();
    }

    public void clear() {
        if (null != _shEditor) {
            _shEditor.clear();
            _shEditor.commit();
        }
    }

    public String getString(String key, String defValue) {
        return _sharedPreferences.getString(key, defValue);
    }

    public int getInt(String key, int defValue) {
        return _sharedPreferences.getInt(key, defValue);
    }

    public float getFloat(String key, float defValue) {
        return _sharedPreferences.getFloat(key, defValue);
    }

    public long getLong(String key, long defValue) {
        return _sharedPreferences.getLong(key, defValue);
    }

    public boolean getBoolean(String key, boolean defValue) {
        return _sharedPreferences.getBoolean(key, defValue);
    }
}

 

프로젝트 진행 중 실 시간으로 시간을 날짜 시간을 표기 해달라는 고객 요청사항이 있어 Thread와 Handler를 사용하여 TextView에 표시해야곘다는 생각을 했다. 

 

구글링을 하다가 TextClock으로 손쉽게 현재 날짜 시간을 표기할수 있다는것을 알게되었다.

(https://developer.android.com/reference/android/widget/TextClock)

 

API level 17부터 지원되기 시작한 기능으로 TextView를 상속받아 구현되었다.

<TextClock
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:format12Hour="yyyy년 MM월 dd일 EE요일"
    android:textSize="18dp" />

<TextClock
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:format12Hour="hh:mm a"
    android:textSize="45dp" />

format 안에 내용은

yyyy : 년

MM : 월

dd : 일

EE : 요일

hh : 시간

mm : 분

a : 오전/오후를 나타낸다

android:format12Hour="hh:mm a"
android:format24Hour="hh:mm"

format12Hour은 12시간 시간표기방식, format24Hour은 24시간 시간표기 방식을 설정할수 있다.

요일과 오전/오후의 경우는 시스템의 local언어에 따라 다르게 표기된다.

(개발 단말의 언어가 한국어인 경우는 기본적으로 위와 같이 한글로 요일과 오전/오후가 표기 되지만 영어인 경우는 영어로 요일과 AM/PM으로 표기 된다.)

 

예전에 시간 갱신하는 로직을 일일히 다 구현해 줘야하는 번거로움이 있었는데, TextClock사용으로 이러한 번거로움을 줄일수 있었다.

Node js는 chrome v8 javascript 엔진으로 빌드된 JavaScript 런타임 환경을 말한다.
※ 기본적으로 HTML, CSS, JavaScript으로만으로도 웹개발이 가능하지만, 효율적인 웹 개발을 위해 Node js를 사용한다.

Node js 설치 시 LTS(Long Term Supported) 버전과, 최신 버전이 존재한다.

보통 LTS버전은 짝수 버전 (ex:12.x.x,14.x.x,16.x.x 등) 최신버전은 홀수 버전(ex:11.x.x,13.x.x,15.x.x 등)으로 되어 있다.
LTS 버전은 장기적으로 안정되고 신뢰성이 높으며 안정적 지원이 보장된 버전이다. 주로 안정적인 유지/보수가 필요한 서버보안등에 사용된다.
최신 버전의 경우는 웹에서 사용되는 최신의 기술들을 사용할수 있지만, 안정적이지 않아 업데이트가 빈번하게 발생하는 등의 안전성 문제가 발생할수있어 테스트 용으로 사용된다.
실제 개발 시에는 LTS 버전 사용을 권장하며, 보통 개발 시 한개의 Node js만 설치하여 사용하는 것이 아니라 프로젝트 별로 여러 개의 Node js 버전을 사용하기 때문에 nvm(Node Version Manager)을 사용한다.

 nvm(Node Version Manager) : node js를 버전별로 설치하여 필요한 버전으로 node js 버전을 변경할수 있음

프로젝트 빌드 환경에 따라 nvm을 이용하여 Node js 버전을 변경해가며 사용한다.

Node js를 사용하면, npm(Node Package Manager)을 사용할 수 있다.

※ npm (Node Package Manager) : node js 개발 시 전 세계의 개발자들이 만든 다양한 기능(패키지, 모듈)을 관리, 개발 시 필요한 기능을 설치하여 쉽게 사용할수 있다.

npm을 사용함으로 관리 효율증가와 손쉬운 기능 고도화가 가능하지만 구성이 복잡해지고, 기능에대한 학습의 난이도가 증가하는 Trade-off(상충관계)가 발생한다.

안드로이드 프로젝트 빌드 중 Execution failed for task ':app:validateSigningDebug'에러가 발생하였다.

 

이유는  프로젝트 내에 debug.keystore가 존재하지 않아서 발생하였다.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:validateSigningDebug'.
> Keystore file '/Users/Documents/gitlab/app-frontend/android/app/debug.keystore' not found for signing config 'debug'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 13s

error Failed to install the app. Make sure you have the Android development environment set up: https://reactnative.dev/docs/environment-setup. Run CLI with --verbose flag for more details.
Error: Command failed: ./gradlew app:installDebug -PreactNativeDevServerPort=8081
Warning: Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01

debug.keystore는 /Users/.android/debug.keystore 에 존재하며, 로컬에 존재하는 debug.keystore를 해당경로로 옮겨주니 정상적으로 빌드가 되었다.

프로젝트 진행 중 gitlab 에 React-Native로 되어 있는 소스를 빌드 후 실행 중 

Error: error:0308010C:digital envelope routines::unsupported

오류가 발생하였다.

 

Failed to construct transformer:  Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:133:10)
    at stableHash (/Users/Documents/gitlab/app-frontend/node_modules/metro-cache/src/stableHash.js:19:8)
    at JsTransformer.getCacheKey (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/JSTransformer/worker.js:478:7)
    at getTransformCacheKey (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/DeltaBundler/Transformer/getTransformCacheKey.js:39:29)
    at new Transformer (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/DeltaBundler/Transformer.js:147:28)
    at /Users/Documents/gitlab/app-frontend/node_modules/metro/src/Bundler.js:54:29
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
  library: 'digital envelope routines',
  reason: 'unsupported',
  code: 'ERR_OSSL_EVP_UNSUPPORTED'
}
[Tue Nov 29 2022 13:52:10.710]  BUNDLE  ./index.js 

error: TypeError: Cannot read properties of undefined (reading 'transformFile')
    at /Users/Documents/gitlab/app-frontend/node_modules/metro/src/Bundler.js:91:34
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/Bundler.js:14:24)
    at _next (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/Bundler.js:34:9)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

 

오류 내용으로 검색해보니 node v17이상에서는 digital envelope routines이 지원되지 않아서 발생하는 문제라고 되어있었다.

 

nvm으로 노드 버전을 v12.22.12 으로 변경 후(프로젝트의 ReadMe의 환경 설정에 노드 버전이 12.x.x버전) node_modules을 제거후 다시 npm install을 해보았다.

 

동일한 Error: error:0308010C:digital envelope routines::unsupported 오류가 계속 발생하여 반나절 정도 삽질을 하였다.

삽질 중 nvm 의 default버전이 v18.12.1로 되어 있어 있는데, 에러가 이부분에 연관된 것이 아닌가라는 의심이 들어 혹시나 하는 마음에 nvm default 버전을 변경해 보았다.

 

nvm alias default 12.22.12

변경 후 nvm list명령어로 nvm defalut가 정상적으로 변경되었음을 확인하였다.

 

node_module을 제거 후 새로운 터미널에서 다시 npm install 후 실행을 하니 정상적으로 앱이 실행되었다.

 

 

리액트 네이티브리액트라는 자바스크립트 라이브러리를 사용해 자바스크립트를 이용해서 모바일 앱을 만드는 프레임워크를 말하며, 리액트 네이티브 코드는 해당 플렛폼의 네이티브 컴포넌트로 컴파일됩니다.

 

리액트 네이티브의 장점으로는

 

첫번째, 페이스북에서 지원하고 있는 오픈 소스라는점과 많은 개발자들이 의욕적으로 참여하고 있다는 점입니다.

아마존, 마이크로소프트,인스타그램, AirBnB, 블룸버그, 테슬라 등 다수의 기업들이 리액트 네이티브에 투자하거나 개발에 사용하고 있습니다.

 

두번째, 리액트 네이티브를 사용하는 개발자는 자바스크립트를 사용하여 네이티브의 뷰를 만들수 있고, 플랫폼 별로 컴포넌트에 접근할 수 있습니다. 즉, 자바스크립트 하나의 언어로 앱 전체를 만들 수 있고, 크로스 플랫폼을 지원하는데 있어 많은 코드를 재사용할 수 있어 소요되는 시간과 비용을 줄일수 있습니다.

 

세번째, 아이오닉, 코르도바 등의 하이브리드 프레임워크와 비교하여 네이티브 컴포넌트와 API를 사용하여 렌더링하기 때문에 속도와 성능면에서 유리 합니다.

 

리액트 네이티브의 단점으로는 JSX(Javascript XML)라는 리액트에만 존재하는 개념에 대해 학습이 필요하며, 네이티브에 대한 지식도 어느정도 필요합니다. 또한 프레임워크 구조가 자바 스크립트 스레드와 네이티브 스레드를 연결하여 동작 시키는 하이브리드 앱 방식으로 순수 네이티브 개발보다는 성능 면에서 떨어 질수 있습니다.

 

리액트 네이티브가 코르도바, 아이오닉과 다른점은 코르도바, 아이오닉 두 프레임워크는 HTML과 CSS를 이용해 만든 웹뷰를 패키징하여 네이티브앱을 만들지만, 리액트 네이티브는 자바스크립트를 컴파일해 네이티브한 코드로 만들어 줍니다. 이러한 방식은 자마린(Xamarin)과 같지만, 자마린은 개발언어가 C#이고 리액트 네이티브는 자바스크립트라는 점에서 차이점이 있습니다. 

 

'용어정리' 카테고리의 다른 글

[용어정리] CI/CD : 지속적 통합/배포  (0) 2021.06.29

CI(Continuous Integration)지속적 통합
개발 후 모든 소스를 통합 시 특정코드에 결함이 있거나 통합된 후 소스에 서로 다른 부분이 반영 되어 문제가 발생 할수 있므로 직접 테스트하여, 문제를 찾아내야 한다.
이러한 테스트를 하는 과정은 많은 리소스(시간과 인력)가 필요함으로 리소스 절약을 위해 지속적 통합을 할 필요가 있다.

CD(Continuos Deploymet/Delivery) 지속적 배포
소프트웨어를 소트웨어의 최종 결과를 최종 사용자에게 제공하여 실행 가능하도록 하는 단계를 말한다.
예를 들면 모바일의 경우는 앱스토어에 올려 배포하는 행위, 서버의 경우는 배포파일을 배포하고 서버에 올리는 행위를 말한다.
코딩을 마칠때 마다 이 행위는 지속적으로 진행 하여야 한다.


CI/CD는 코딩이 완료된 후 배포 되기까지의 과정을 자동화 한다는 의미이다.
CI/CD 툴의 종류는 설치형(젠킨스), 클라우드형(Circle Ci, Travis Ci, Team City)이 있으며, git lab, bitbucket 등에서도 CI/CD를 제공하여 준다.

클라우드형의 경우는 사용자 편의성을 제공한다는 면에서 장점이 있고, 젠킨스의 경우는 커스터마이징하여 사용자 상황에 맞게 사용할수 있다는 장점이 있기 떄문에 각 상황에 맞는 CI/CD 툴을 사용하는것이 좋다.

'용어정리' 카테고리의 다른 글

리액트 네이티브 (React-Native)  (0) 2022.03.03

IOS에서 DB를 사용하는 방법은 Sqlite 와 Realm이 있다.

Realm은 속도가 빠르고, 높은 용량을 요구하여 대용량 데이터를 다룰 때 사용하는 방법이다.

Sqlite는 그 반대로 내장된 라이브러리라서 가볍게 사용하기 좋다. 

 

Swift로 간단히 데이터를 저장하기 위해 Sqlite의 사용법을 알아보도록 한다.

 

1. Swift에 sqlite import를 해준다. 

import SQLite3

 

2. 변수 및 테이블 명 (전역 변수로 선언해 준다.)

    var db:OpaquePointer?
    let TABLE_NAME : String = "DaDaTable"

 

2. Table 생성

func createTable(){
        let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("DSDatabase.sqlite")
        
        
        if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
            print("table not exsist")
        }
        
        let CREATE_QUERY_TEXT : String = "CREATE TABLE IF NOT EXISTS \(TABLE_NAME) (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, subline, date TEXT)"

        print(CREATE_QUERY_TEXT)
        if sqlite3_exec(db, CREATE_QUERY_TEXT, nil, nil, nil) != SQLITE_OK {
            let errMsg = String(cString:sqlite3_errmsg(db))
            print("db table create error : \(errMsg)")
        }
    }

 

3. 데이터 삽입

func insert(_ title : String,_ subline : String, _ date : String ){
        var stmt : OpaquePointer?
        
        let INSERT_QUERY_TEXT : String = "INSERT INTO \(TABLE_NAME) (title, subline, date) Values (?,?,?)"

        if sqlite3_prepare(db, INSERT_QUERY_TEXT, -1, &stmt, nil) != SQLITE_OK {
            let errMsg = String(cString: sqlite3_errmsg(db)!)
            print("error preparing insert:v1 \(errMsg)")
            return
        }
        
        let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
        
        if sqlite3_bind_text(stmt, 1, title, -1, SQLITE_TRANSIENT) != SQLITE_OK{
            let errMsg = String(cString : sqlite3_errmsg(db)!)
            print("failture binding name: \(errMsg)")
            return
        }

        if sqlite3_bind_text(stmt, 2, subline, -1, SQLITE_TRANSIENT) != SQLITE_OK{
            let errMsg = String(cString : sqlite3_errmsg(db)!)
            print("failture binding name: \(errMsg)")
            return
        }

        
        if sqlite3_bind_text(stmt, 3, date, -1, SQLITE_TRANSIENT) != SQLITE_OK{
            let errMsg = String(cString : sqlite3_errmsg(db)!)
            print("failture binding name: \(errMsg)")
            return
        }
        
        if sqlite3_step(stmt) != SQLITE_DONE {
            let errMsg = String(cString : sqlite3_errmsg(db)!)
            print("insert fail :: \(errMsg)")
            return
        }
    }

 

4. 데이터 검색

func selectValue(){
        
        let SELECT_QUERY = "SELECT * FROM \(TABLE_NAME)"
        var stmt:OpaquePointer?
        
        
        if sqlite3_prepare(db, SELECT_QUERY, -1, &stmt, nil) != SQLITE_OK{
            let errMsg = String(cString: sqlite3_errmsg(db)!)
            print("error preparing insert: v1\(errMsg)")
            return
        }
        
        while(sqlite3_step(stmt) == SQLITE_ROW){
            let id = sqlite3_column_int(stmt, 0)
            let title = String(cString: sqlite3_column_text(stmt, 1))
            let subline = String(cString: sqlite3_column_text(stmt, 2))
            let date = String(cString: sqlite3_column_text(stmt, 3))
        
            print("read value id : \(id) title : \(title) subline : \(subline) date : \(date)")
        }
  
    }

 

5. 업데이트

    func update(_ index:String, _ title : String,_ subline : String, _ date : String){
        let UPDATE_QUERY = "UPDATE \(TABLE_NAME) Set title = '\(title)', subline = '\(subline)', date= '\(date)' WHERE id == \(index)"
        var stmt:OpaquePointer?
        print(UPDATE_QUERY)
        if sqlite3_prepare(db, UPDATE_QUERY, -1, &stmt, nil) != SQLITE_OK{
            let errMsg = String(cString: sqlite3_errmsg(db)!)
            print("error preparing update: v1\(errMsg)")
            return
        }
        
        if sqlite3_step(stmt) != SQLITE_DONE {
            let errMsg = String(cString : sqlite3_errmsg(db)!)
            print("update fail :: \(errMsg)")
            return
        }
        
        sqlite3_finalize(stmt)
        print("update success")
               
    }

 

6. 데이터 삭제

func delete(_ index:String){
//        readValues()
        let DELETE_QUERY = "DELETE FROM \(TABLE_NAME) WHERE id = \(index)"
        var stmt:OpaquePointer?
        
        print(DELETE_QUERY)
        if sqlite3_prepare_v2(db, DELETE_QUERY, -1, &stmt, nil) != SQLITE_OK{
            let errMsg = String(cString: sqlite3_errmsg(db)!)
            print("error preparing delete: v1\(errMsg)")
            return
        }
        
        if sqlite3_step(stmt) != SQLITE_DONE {
            let errMsg = String(cString : sqlite3_errmsg(db)!)
            print("delete fail :: \(errMsg)")
            return
        }
        sqlite3_finalize(stmt)
                
    }

간단히 데이터를 저장하기 위해선 sqlite가 적합하다. 

대용량 데이터를 처리하기 위해선 Realm을 사용하는데 이 후 정리하는 시간을 갖도록 하겠다.

+ Recent posts