Realm이란?
오픈소스이며 모바일에 최적화된 라이브러리이다. 객체지향 데이터베이스로, iOS, Android, React Native등 다양한 플랫폼에서 사용할 수 있으며 로컬에 저장된다.
UserDefault는 간단한 데이터만 저장하는 용도이다(id, pw, 간단한 설정과 같은..) 큰 용량을 저장하기엔 무리가있다.
ORM이 아닌 *데이터 컨테이너 모델을 사용하고 데이터 객체는 Realm에 객체로 저장된다.
*컨테이너가 뭘까.
컨테이너란 OS상에 논리적인 구획(컨테이너)을 만들고, 어플리케이션을 작동시키기 위해 필요한 라이브러리나 어플리케이션등을 하나로 모아, 별도의 서버인 것처럼 사용할 수 있게 만든것이다.
OS의 리소스를 논리적으로 분리시키고 여러개의 컨테이너가 공유하여 사용한다. 컨테이너는 오버헤드가 적기 때문에 가볍고 고속으로 작동한다.
기존 데이터베이스에선 쿼리문을 통해 테이블에서 데이터를 읽어오던 방식이며, 가져온 데이터를 바로 애플리케이션에 사용할 수 없고, 가공하는 과정이 필요했다. 이런 과정을 잘못하면 코드가 복잡해진다.
그에 반해 Realm의 핵심 개념은 가벼운 객체 컨테이너이다. 다른 데이터베이스 처럼 쿼리, 필터링, 상호연결이 가능하고 저장된다.
Realm은 객체형태로 데이터를 읽어오고 객체형태로 바로 DB에 저장을 가능하게한다. 그래서 Realm객체는 라이트 오브젝트이며 반응형이다.
사용방법
Realm Install
코코아팟을 통해 설치하거나, SPM을 사용하여 설치할 수 있다.
코코아팟
pod 'RealmSwift'
SPM
https://github.com/realm/realm-swift
Realm Object 생성
테이블을 생성한다.
final class MemoData: Object {
@objc dynamic var id = "" // 기본키
@objc dynamic var date = Date()
@objc dynamic var image: Data? = nil
@objc dynamic var memo = ""
override static func primaryKey() -> String? {
return "id"
}
}
- 테이블 내에 저장하고 싶은 데이터들에 맞는 데이터 타입을 확인하고 테이블을 구현해주는 과정이 필요하다.
- Object를 상속받아야 하기 때문에 class로 구성되며 @objc dynamic키워드가 필요하다.
- primaryKey는 Realm 객체를 식별하기 위해 사용되는 고유 식별자이다.
- 이렇게 해두면 id의 속성이 기본키로 지정된다.
- 오늘의 메모는 여러개일 수 있지만 각각의 메모는 id를 가지고있어야하기 때문이다.같은 id의 메모를 추가하려면 에러가 발생한다. ( 이 예제에선 같은 id를 추가할 확률은 걱정하지 않아도 된다)
- 곧 id속성은 MemoData 객체를 고유하게 식별할 수 있는 유일한 값이며 중복은 없어야한다.
- id값을 사용하여 객체를 조회, 수정, 삭제할 수 있다.
이후 모든 CRUD를 하기위해 Realm객체를 생성해야한다.
let realm = try! Realm()
Realm 저장.
// 새로운 객체를 저장
func save(memoData: MemoData, image: UIImage){
let newData = MemoData()
newData.id = UUID().uuidString
netData.date = memoData.date
newData.memo = memoData.memo
newData.image = image.jpegData(compressionQuality: 0.7)
try! realm.write {
realm.add(newData)
}
}
새로운 객체(newData)의 경우 본인의 데이터 구조에 맞게 생성하면 되지만, id는 UUID().uuidString형식으로 생성하면 된다.
UUID가 뭔지는 다른 포스팅에서 다루고있다.
https://clamp-coding.tistory.com/413
데이터 가져오기
여기선 MemoData(MemoData테이블)을 가져온다.
func fetchAll() -> Results<MemoData> {
let results = realm.objects(MemoData.self)
return results
}
여기선 id를 기준으로 가져오고있다. filter를 하는등 다양한 쿼리를 사용할 수 있다.
func getData(withId id: String) -> MemoData? {
let existingData = realm.objects(MemoData.self).filter { $0.id == id }.first
return existingData
}
업데이트
func update(memoData: MemoData, image: UIImage) {
// 기존 데이터 업데이트
guard let existingData = getData(withId: memoData.id) else {
return
}
do {
try realm.write {
existingData.date = phomemoDatatoData.date
existingData.memo = memoData.memo
existingData.image = image.jpegData(compressionQuality: 0.8)
}
} catch {
print("????")
}
}
여기에선 기존의 memoData를 가져와서 이미지를 업데이트하는 방법의 업데이트를 하고있다.
삭제
func delete(memoData: MemoData) {
do {
try realm.write {
realm.delete(memoData)
}
} catch {
print("Error deleting memoData: \(error)")
}
}
}
메모 데이터 객체를 받아와 해당메모데이터를 삭제하는 코드를 포함한다.
중요한 점⭐️⭐️
Realm으로 읽어온 Object객체(여기선 MemoData)는 데이터를 바로 반영하고 업데이트한다.
Realm으로 읽어온 데이터가 있다고 해보자. Memo테이블에서 하나만 가져온다.
let data = RealmManager.shared.fetchAll().first ?? MemoData()
이 데이터를 이렇게 수정할 수 없다.
data.memo = "New memo1"
위에서 설명했듯 객체형태로 데이터를 읽어온다, 또 바로 데이터를 반영하고 업데이트한다.
여기서 읽어온 data라는 객체는 Realm으로 읽어온 객체이며, Realm테이블과 연결되어있다.
데이터의 변경은 Realm을 통해 수정해야하며, 수정이 일어나는 즉시, 데이터베이스에 저장된 정보도 변경된다.
곧 "읽어온 객체 = 데이터베이스에 저장된 객체" 가 된다는 말이다.
위에서 생성한 update 함수는 Image를 업데이트하는 함수이다.
data.memo 를 수정하기 위해선 memo를 수정하는 함수를 만들거나,
가벼운 수정을 원한다면 이렇게할 수 있다.
try! realm.write{
data.memo = "New memo"
}
}
Realm Studio
Realm Studio를 사용해 데이터의 변경을 GUI방식으로 실시간 체크가 가능하다.
- Mac의 앱스토어에 접속하여 Realm Browser를 검색 후 다운받는다.
- Realm을 구현한 소스에서 Realm데이터가 저장된 위치정보를 받는다. » print(Realm.Configuration.defaultConfiguration.fileURL!)
- 2에서 얻은 파일 주소로 이동 후 파일을 실행한다.
1) 터미널 실행
2) $cd FileURL로 FileURL의 상단 폴더까지 이동
3) open . 으로 해당 위치의 폴더을 창으로 이동
4) 저장된 Realm 파일을 오픈
또는 finder를 이용해 경로를 찾아가도 쉽게 찾을 수 있다.