Realm에서 특정 데이터를 삭제할때 지금까지는 전체 데이터를 fetch해와서 필터링하는 방법을 사용해왔다.
한마디로 비효율적이고 귀찮은 방법을 사용했던 것이다.
그런데 찾아보니 PK를 통해 삭제하는 더 쉽고 간편한 방법이 있어서 소개하려고 한다.
데이터를 필터링해서 id를 비교 - 이전에 사용하던 방식
앱에서 즐겨찾기 버튼의 동작을 구현하고 있었다.
즐겨찾기 버튼을 탭할때, 현재 즐겨찾기가 되어있지 않다면 즐겨찾기에 추가하고,
현재 즐겨찾기가 되어있다면 즐겨찾기에서 해제하는 로직을 구현해야했다.
realm에서 데이터를 삭제하는 등의 동작을 Repository 패턴을 사용해서 다음과 같이 정의해주었다.
func deleteItem(data: StarItem) {
do {
try realm.write {
realm.delete(data)
}
} catch {
print("realm 데이터 삭제 실패")
}
}
`StarItem`이라는 Realm Object를 매개변수로 받아서, 해당 데이터를 삭제하는 방식이다.
참고로 `StarItem`에는 primaryKey로써 id라는 칼럼 하나만 존재하는 상황이다.
그리고 즐겨찾기 로직을 실제로 구현한 곳의 코드는 다음과 같다.
// ViewModel
input.starButtonTapped
.bind(with: self) { owner, _ in
let data = Array(owner.repository.fetchAll())
let existingData = data.filter {
$0.id == owner.id.value
}
if existingData.count > 0 {
owner.repository.deleteItem(data: existingData.first ?? existingData[0])
isStared.accept(false)
} else {
owner.repository.createItem(id: owner.id.value)
isStared.accept(true)
}
}
.disposed(by: disposBag)
`starButton`이라는 버튼을 탭했을때의 로직을 ViewModel에서 작성해두었다.
코드의 흐름을 설명하면 다음과 같다.
- `fetchAll`을 통해 Realm에 저장된 모든 데이터를 가져온다.
- 가져온 데이터에서 id와 현재 아이템의 id를 비교해서 일치하는 데이터를 담는 배열을 만든다.
- 해당 배열의 count가 1개 이상이라면 현재 아이템이 realm에 있다는 뜻이므로, 데이터를 삭제한다.
- 그렇지 않다면 현재 아이템이 realm에 없다는 뜻이므로, 데이터를 추가한다.
나는 여기서 의문이 들었다.
"realm에 이 데이터가 있다면 삭제" 이 로직을 위해 전체 데이터를 불러오고, 별도의 배열에 담고..
이런 과정이 비효율적이라고 생각하고, 다른 방법이 없는지 궁금했다.
PK값을 통해 삭제 - 더 간편한 방식
기존에 사용하던 방식처럼 모든 데이터를 불러오는 방식이 아니라,
PK를 기준으로 특정 아이템을 삭제할 수 있다.
먼저 Repository에 구현되어있는 코드는 다음과 같다.
func isBookmarked(id: String) -> Bool {
return realm.object(ofType: BookmarkedRecipeObject.self, forPrimaryKey: id) != nil
}
func delete(by id: String) {
if let object = realm.object(ofType: BookmarkedRecipeObject.self, forPrimaryKey: id) {
do {
try realm.write {
realm.delete(object)
}
} catch {
print("realm 데이터 삭제 실패")
}
}
}
delete메서드는 `BookmarkedRecipeObject`라는 realm object를 삭제하는 기능이다.
isBookmarked 메서드는 `BookmarkedRecipeObject`에 특정 아이템이 있는지 확인하는 기능이다.
기존의 코드와의 차이점은 id라는 매개변수를 받는다는 점과, 내부에서 PrimaryKey에 해당 id값을 대응해서 일치하는 object를 찾는다는 점이다.
즉, 특정 아이템이 realm에 존재하는지 여부를 id를 통해 확인하고, 존재한다면 삭제하는 로직인것이다.
이 메서드를 실제 사용하는 곳의 코드는 다음과 같다.
// Reactor
case .bookmarkButtonTapped:
let isCurrentlyBookmarked = repository.isBookmarked(id: currentState.recipe.id)
if isCurrentlyBookmarked {
repository.delete(by: currentState.recipe.id)
} else {
repository.save(currentState.recipe)
}
return .just(.setBookmarked(!isCurrentlyBookmarked))
위의 코드는 ViewModel의 코드이고, 이 코드는 Reactor의 mutate 메서드 안의 코드라는 차이점이 있지만..
그냥 bookmarkButton이 탭됐을때의 동작이라고 생각하면, 즐겨찾기 로직과 정확히 일치하는 로직이다.
코드의 흐름은 다음과 같다.
- 먼저 Repository의 isBookmarked메서드를 통해 해당 id를 PK로 가진 칼럼이 object에 존재하는지 확인한다.
- 만약 존재한다면, 해당 id에 해당하는 아이템을 삭제한다.
- 그렇지 않다면 아이템을 추가한다.
확실히 기존에 사용하던 방식보다 더 쉽고 간편한 코드로 구현할 수 있었다.
사실 기존의 코드에서 이미 realm에 데이터가 존재하는지 여부도 Repository에서 isStared 정도의 메서드로 구현해놨다면, 실제 구현부에서의 코드는 단순해졌을지 모른다.
그러나 여기서의 핵심은 PK를 기반으로 realm에 존재하는지 여부를 확인할 수 있다는 점이다.
이제 모든 데이터를 불러와서 비교하는 식의 비효율적인 코드는 작성하지 않아도 된다.
'iOS > Swift' 카테고리의 다른 글
[iOS/Swift] DI와 DIP는 다르다! - 의존성 주입 알아보기 (1) | 2025.03.18 |
---|---|
[iOS/Swift] 책임분리를 위한 설계 - DTO와 Entity에 대해 (0) | 2025.03.15 |
[iOS/Swift] Typed throws - Swfit6에서의 에러처리 패턴 개선사항 (0) | 2025.02.16 |
[iOS/Swift] MVVM의 한계에 대한 고찰 - Massive View Model 해결법 (0) | 2025.02.11 |
[iOS/Swift] ARC - Swift의 메모리 관리 기법 그런데 이제 weak을 곁들인 (0) | 2025.02.08 |