-
UserDefaults을 이용하여 로컬에 데이터 저장하기(1)데이터 저장/데이터 저장 in 로컬 : UserDefaults 2022. 8. 17. 14:23
To do List 앱을 사용하는데, 다음날 앱을 켰더니 작성했던 내용이 사라지면 안되지 않는가
이는 사용자로서는 당연한 얘기인데, 개발자로서는 중요한 작업 항목이다.
App에서 사용자가 만드는 것이 있다면 App은 그 데이터를 사용자가 지우기 전까지 저장하고 있어야 한다.
App이 데이터를 저장하는 곳은 크게 2가지라고 보면 될 것 같다.
첫번째는 로컬 저장소, 즉 개인이 사용하는 기기 자체 저장소이고,
두번째는 App을 만든 회사가 가지고 있는 Server일 것이다.
(cloud는 데이터를 저장할지의 여부를 선택할 수 있고, 개인이 관리하는 것이니 여기서 다룰 내용은 아니다.)
자, 그럼 이제 여기서 다룰 내용은 첫번째인 데이터를 로컬에 저장하는 방법인데, 그 방법도 여러 가지가 있는 모양이다.
가장 기본적인 UserDefaults, 또 CoreData, 또 라이브러리를 이용하는 Realm 등.
이번엔 그 중에서도 UserDefauls를 사용하는 방법이다.
UserDefaults는 Xcode에서 제공하는 클래스인데, 반영구적인 저장이면서 사용방법이 간단하기는 하나,
데이터 전체를 한 번에 저장하고, 전체를 불러온다는 점에서 Database처럼 사용할 경우 비효율적인 측면이 있다고 한다.
따라서 아래처럼 단순한 정보 저장에 적합하다고 한다.
https://blog.naver.com/soojin_2604/222377014758
(좋은 정보 감사합니다.)
그럼에도 이번에는 사용 방법을 되새김하기 위해 배열을 저장하고 불러오도록 하겠다.
이를 위해 정해야 할 것은 크게 3가지.
1. 무엇을 저장하고, 불러올 것인가
2. 언제 저장할 것인가
3. 언제 불러올 것인가
1. 무엇을 저장하고 불러올 것인가
예제는 약 먹는 시간을 나타내는 알람인데, 각 항목을 alramList 배열로 저장하고, 테이블 뷰에 하나씩 나타나도록 했다.
다시 어플을 켤 때에도 이 목록이 나타나도록 이 배열을 저장하고 불러올 것이다.
2. 언제 저장할 것인가
자신이 사용하는 앱에 따라 저장하는 타이밍을 조절할 수도 있겠지만
그럼에도 내용이 바뀌는 순간마다 저장하는 것이 가장 안전성이 높을 것이라 예상한다.
따라서 배열의 내용이 바뀌는 곳을 꼼꼼히 파악해서 저 함수를 넣어줘야 한다.
예제에서는 저장하는 함수(saveLocalData())를 따로 만들고 저장이 필요할 때마다 이 함수를 호출할 것이다.
"+" 버튼을 눌러 배열이 추가될 때도 저 함수를 호출해야 하고, 배열의 내용이 삭제 될 때도 저 함수가 호출되어야 한다.
나는 이런 수고를 덜고자, 아래와 같이 변수에 didSet옵저버를 추가하는 코드로 작성했다.
var alramList = [NewAlram]() { didSet { saveLocalData() tableView.reloadData() } }
이러면 추가를 하든, 수정을 하든, 삭제를 하든 변수가 변할 때면 저장 함수가 호출될 것이다.
이게 좋은 방법인지 아닌지는 아직 판단할 수 없다;;;
그리고 마찬가지로 TableView도 reload되도록 해주었다.
3. 언제 불러올 것인가
'불러온다'는 개념을 명확히 할 필요가 있다.
이는 저장하는 프로세스에 달려있을 것인데,
만약 내가 무엇인가 바꿨을 때 저장소의 데이터를 바꾸고, 이를 코드의 배열에 반영한다면 불러오는 과정이 필요할 것이고,
반대로, 내가 무엇인가 바꿨을 때 코드의 배열을 바꾸고 이를 저장소의 데이터에 반영한다면 불러오는 과정이 불필요할 것이다.
후자라면, '불러온다'는 것은 앱이 시작될 때의 데이터 로드뿐일 것이다.
예제에서는 이 후자를 다루므로 데이터를 로드하는 함수(loadLocalData())를 viewdidLoad에 작성했다.
override func viewDidLoad() { super.viewDidLoad() loadLocalData() }
이렇게 하면
첫음 어플 킬 때, UserDefaults에 "alramList"라는 항목이 없으므로 return
알람 추가 시, 배열의 옵저버가 saveLocalData() 저장 함수를 호출하고, TableView를 reload
알람 변경/삭제 시도 마찬가지
어플 종료 후 재 실행시, UserDefaults에 "alramList"라는 data를 불러와 배열 생성 -> save / reload
자 그럼 이제 저장하는 코드를 작성해 보자.
func saveLocalData() { let data = alramList.map { [ "id": $0.id, "date": $0.date, "isOn": $0.isOn ] } let userDefaults = UserDefaults.standard userDefaults.set(data, forKey: "alramList") }
우선 userDefaults에 저장할 객체로서 data를 만들어 줘야 하는데, data는 위 1번에서 말한 alramlist를 넣을 것이다.
userDefaults는 key-value로 저장할 수 있으므로 map을 이용해 각 key를 갖는 배열로 매핑시켜준다.
이렇게 하고 data를 print해보면
저장된 내용을 보니, Array 안에 Dictionary들이 저장됐다. (기억을 되뇌며, Array는 순서가 있고, Dictionary는 순서가 없다.)
다음은 만든 data를 set을 이용해 userDefaults에 "alramList"라는 key로 저장한다.
그럼 바로 불러오는 코드도 작성해보자
func loadLocalData() { let userDefaults = UserDefaults.standard guard let data = userDefaults.object(forKey: "alramList") as? [[String: Any]] else { return } alramList = data.compactMap { guard let id = $0["id"] as? String else { return nil } guard let date = $0["date"] as? Date else { return nil } guard let isOn = $0["isOn"] as? Bool else { return nil } return NewAlram(id: id, date: date, isOn: isOn) } }
우선 저장할 때 정했던 key가 "alramList"인 value를 불러올 것인데,
여기서 저장한 것의 타입에 따라 명령어를 선택해야 한다.
위와 같은 경우는 하위 dictionary를 생각해 object를 썼는데, 이제 보니 array를 써도 될 것이다.
이때 저장했던 방법을 떠올리며 1차 타입캐스팅을 해주도록 한다.
최종적으로 이들을 alramList 배열에 넣는 작업은 compactMap을 사용한다.
저장할 때는 map, 불러올 때는 compactmap!
클로져 안에서 리스트 내용 NewAlram들을 return 해주면 된다.
흠, 옵저버로 했더니 불러올 때 세이브가 다시 실행되게 되는구나.
편하긴 하지만 효율적인 방법은 아닐 수 있겠어.
그래도 프로세스를 익힌다는 관점에서!
'데이터 저장 > 데이터 저장 in 로컬 : UserDefaults' 카테고리의 다른 글
UserDefaults을 이용하여 로컬에 데이터 저장하기(2) - Encoder & Decoder (0) 2022.08.18