오늘은 기본적인 데이터 저장/불러오기 구조를 배웠다.
거기에 대해서 정리하면 좋겠지만, 우선 지금 코드에 대해서 이해하는 과정이 필요해 보여서 todolist에 대한 정리를 해보려고 한다.
ps. 밑에 새로운 개념들과 데이터 저장에 대해서도 다루었다.
1.� Todo list의 기본적인 진행 구조
2. 파일 시스템 구현
3. 여러 개념들...
4. 마무리
- 어플리케이션 클래스를 만들고, 그 안에 필요한 내용 (유저 목록, 리스트 목록, 리스트 내의 아이템 목록...) 을 만들어주는 클래스들과 함수들을 만들어 나간다. 이번 TIL에선 테스트 구조를 이용하여 이런 과정을 짚어보겠다.
- 우선 TodoApplication 클래스의 create() 함수로 객체 app 을 만들며 시작한다. (이때 create 처럼 특정 기능을 해주는 함수를 헬퍼 함수라고 한다.)
- 객체 app 의 createUser 함수로 유저를 만든다.
-
createUser(username, password, image) { if (this.findUser(username) !== undefined) { throw Error('username is already occupied'); } const newUser = TodoUser.create(username, password, image); this.users[newUser.id] = newUser; return newUser; }
-
우선 findUser 함수로 객체 app 의 key인 users 객체에 중복되는 username 이 없는지 검사한다.
-
findUser(username) { return Object.values(this.users).find(el => el === username); }
-
username 을 파라미터로 받아서 객체 users 의 키값인 배열에 username 이 이미 존재하는지 찾는다. find() 는 만족하는 요소가 없으면 undefined 를 반환한다.
-
때문에 createUser 함수의 if문 에서는 반환된 값을 undefined 와 비교한다.
-
-
다음으로, TodoUser 클래스의 create 함수로 객체 newUser 를 생성한다.
-
이때, 파라미터로 username, password, image 만 전달하고, id 는 자체적으로 생성한다.
-
클래스로 객체를 생성할때 constructor 는 필수로 들어가야 한다.
-
static create 를 쓰는 이유는
-
외부에서 만들면 비슷한 기능과 이름의 함수가 너무 많아지고
-
constructor 에선 한가지 함수에 한가지 기능만 담게 하기 위함이다.
-
-
-
app 객체의 users 키의 값인 객체에 newUser 객체의 id 라는 key 값을 새로운 key 로 사용하여 newUser(유저 정보 일체) 를 저장한다.
-
마지막으로 newUser 를 반환하며 함수를 끝낸다.
- 앱의 객체에 할당도 하고 반환도 하는 이유는 앱과 실사용자 모두가 정보를 갖게 하기 위함이다.
-
-
여기서부턴
app.createUser()
와 흐름이 비슷하다. 같은 구조로 lists 와 items 를 만드는것 뿐 이다. -
createList 에선 이미 중복되는 리스트가 있는지 검사하지는 않는다. 실제로 같은 이름의 리스트가 많으면 혼란스러울 수 있겠지만, 앱 차원에서 막아야 할 이유도 딱히 없다고 느껴진다. (같은 이름의 리스트 여러개를 만들고 랜덤으로 할 일을 정할 순 있겠다...)
-
마찬가지로
class TodoList
를 이용하여 실질적인 리스트 생성을 한다.-
만드는 항목을 좀 살펴보자면,
-
유저나 아이템과는 별개의 리스트의 id (결국 유저, 리스트, 아이템 모두가 고유 id 를 가짐으로써, 앱에서 모든 데이터를 다룰 수 있다.)
-
후술할 stringify 작업을 위해 아이템 대신 itemIds 배열을 만든다.
-
그 외엔 리스트 이름, 리스트 소유주. 만든 시간 이 들어간다.
-
-
owner 값으로 TodoUser 클래스에서 생성된 객체가 들어가야 하므로
instanceof
를 사용한걸 볼 수 있다.
-
-
createItem 에선 아이템을 만드는 과정에 리스트에 아이템을 넣어주는 과정이 추가된다.
따라서 addItem 이라는 함수를 추가로 만들어야 한다. -
리스트와 마찬가지로 리스트 자체가 아닌 리스트의 id 를 받는다.
-
validate 함수를 볼 수 있는데, 검사하는 과정을 분리해서 아이템을 수정하는 updateItem 같은 함수에서는 list 인지 검사하지 않도록 해준다.
- 또한 추후에 나올 세이브/로드 시 validate 자체를 생략할 수 있다. 이 또한 static 을 사용하는 이유이다.
-
addItem(item) { this.itemIds.push(item.id); }
- 여기서 this 는 additem 이
list.addItem(item);
과 같이 사용되므로, list 가 되는데, list 는 createItem 에서 파라미터로 받은 이미 생성된 리스트가 되겠다. 거기에 push 하기 때문에 리스트에 아이템이 추가되는 것 이다.
- 여기서 this 는 additem 이
-
updateItem(itemId, params) 함수를 실행시켜 고유한 id 를 가진 아이템을 찾아 (id값을 줘야하는 이유.) params 내용을 업데이트 한다.
-
app 의 items 에 아이템 객체가 들어가 있기 때문에, updateItem 에서
this.items[itemId]
로 객체를 특정하고, 해당 객체의 내용을 업데이트 할 수 있게 된다.
-
함수 내의 상수 item 을 app 객체에서 itemId 로 찾아준다. item 이 정해지면 item 내의 listId 로 list 또한 찾을 수 있다.
-
이 후로는 list (TodoList 로 생성된) 내의 removeItem 메서드 (이름은 같지만 명백히 다른 함수다!) 로 리스트에서도 제거해주고, delete 연산자로 app 에서도 제거하면서 끝낸다.
-
TodoList 클래스의 removeItem 메서드는
filter()
메서드를 이용한다. 파라미터로 받은 item 객체의 id 를 list 내의 itemIds 에서 찾아 불일치 하는 요소들만 남겨 배열로 리턴한다.
- 리스트를 지우는 경우는 한 단계만 더 처리를 하면 된다.
- 리스트에 포함된 아이템들도 모두 지워줘야 하기 때문에,
forEach()
를 사용해서 모든 아이템들을 지운다.
- 리스트에 포함된 아이템들도 모두 지워줘야 하기 때문에,
-
유저의 경우에는 유저가 가진 리스트를 지워주는 작업을 하고, 유저를 지우면 된다.
- 물론 리스트를 삭제하려면 아이템도 삭제해줘야 한다.
-
Object.values
와filter()
를 이용해서 list 객체의 owner 객체의 id 키 값을 파라미터로 받는 userId 와 비교해서 간단히 해당 유저의 리스트를 찾을 수 있다.
- 이제부턴 위에서 작업했던 값 들을 저장/불러오기를 하기 위한 작업에 대한 작동 구조를 간단히 살펴보겠다.
-
save() 에서 데이터들을 toString() 을 이용해서 JSON 문자열 형태로 바꿔주고 저장한다. (node.js에서 지원한다.)
-
app 의 toSting() 에선
-
container 배열을 선언해서 app의 각 키 값들을 요소로 넣는다.
-
data 를 선언하고
map
을 이용해서 containers 배열의 각 요소에 콜백 함수를 실행한다. -
(여기서부터 콜백 내용, 결과적으로 app 의 모든 객체에 실행된 결과가 배열의 요소 단위로 data에 저장된다.) entities 에는 객체의 모든 키의 값들이 할당된다. (이때 키의 값들은 각 클래스로 만들어진 객체가 된다. 때문에 toString 메서드를 각 객체로부터 불러올 수 있다.).
-
data 에는(처음 선언한 data와 다르다.) entities 배열의 각 요소마다 toString 메서드를 실행시켜 JSON 문자열 형태로 바뀐 데이터를 할당한다.
-
최종적으로 전체 데이터를 반환하게 되고,
map
으로 순회하며 데이터가 배열로 저장된다. (users, lists, items 단위로 배열의 한 요소씩 차지하는 형태.)
-
-
각 클래스마다 있는 toString() 은 단순하게 모든 데이터를 JSON 문자열 형태로 변환해서 반환한다.
-
fs.js 파일에서 정해놓은 함수를 이용하여 저장한다. fs.js는 처음 보는 개념이긴 하지만 아직까진 굉장히 직관적인 내용이라 따로 설명하지 않겠다.
- 정확히 반대되는 작업을 수행한다. 핵심은
parse()
로 데이터를 해석 해준다는것.
- class 로 객체를 생성할 때, 함수를 정의 해 두면 그 함수를 객체의 메서드로 사용할 수 있다.(어떻게 보면 당연하지만 consturctor 나 static 에 들어가지 않은 형태라 class 기본 문법 페이지를 다시 참조했었다.)
- 객체에서 속성을 제거해주는 연산자이다. expression 에
object.property
같은 속성 참조를 해주면 된다.
- 객체에서 key 에
[]
을 이용하면 표현식을 사용할 수 있다. 이를 computed property name 이라고 한다.
-
callback 은 각 요소를 시험 할 함수. true 면 요소를 남기고, false 면 버린다.
-
때문에 TodoList에선 filter() 의 안에 화살표 함수로 콜백을 표현했다.
- 시간이 얼마가 걸리든 상관없이 약속된 결과를 만들어내는 '제작 코드' 가 준비되었을 때, 모든 소비 코드가 결과를 사용할 수 있게 해준다.
-
간단히 설명하자면, async 는 함수 앞에 위치하여, 함수의 반환값을 항상
promise
가 되게 한다. -
await 는 async 함수 안에서만 동작하는데, 프로미스가 처리(settled) 될때까지 기다렸다가 이후 결과가 반환되게 해준다.
- callback 함수에 따라서 새로운 배열 요소를 생성하는 메서드다.
- JSON 문자열 데이터를 해석해준다.
- node.js 를 시작하는 구문이라고 생각하면 된다. 정해져있는 것.
- JavaScript 값이나 객체를 JSON 문자열로 변환한다.
- 풀어서 설명해둔 글이 있어 링크로 대체한다.
-
[내용](링크)
구조를 사용하면 링크를 걸 수 있었는데, 링크에#이동할 헤드
(실제로 #을 몇개 사용했는지는 상관없다) 를 사용하면 문서 내 링크를 만들 수 있다. -
생각해보니 마크다운도 처음 써 보는 물건인데 아무런 정리가 없었다...
-
8월 13일부로 깃헙에선 token을 써야 커밋이나 기타 등등의 일을 할 수 있게 되었다. 원체 깃헙에 대해 잘 모르는데 갑자기 오류가 떠서 좀 당황했지만 역시 구글에 물어보니 알아서 깃헙 블로그 페이지로 연결해 줘서 빠르게 토큰 만들고 커밋할 수 있었다.
-
하는 법 : 프로필 - 설정 - 개발자 설정 - 토큰 만들기 - 만료일 및 권한 범위 지정 - 토큰 값 복사 (중요!) - 맥에선 keychain 수정
이번 TIL은 마침 바쁜 주말과 기나긴 코드리뷰로 꽤나 늦어졌다. TIL 의 통상적 내용과는 맞지 않을 수 있겠지만, 놓친 개념들도 꽤 많이 발견했고 유익했다고 생각한다. 그래도 좀 더 발전하여 이런 기나긴 TIL 을 작성하지 않아도 되기를...