Skip to content

Latest commit

 

History

History
202 lines (155 loc) · 12.6 KB

2021-08-13.md

File metadata and controls

202 lines (155 loc) · 12.6 KB

Todo list v2 2일차

오늘은 기본적인 데이터 저장/불러오기 구조를 배웠다.

거기에 대해서 정리하면 좋겠지만, 우선 지금 코드에 대해서 이해하는 과정이 필요해 보여서 todolist에 대한 정리를 해보려고 한다.

ps. 밑에 새로운 개념들과 데이터 저장에 대해서도 다루었다.


목차

1.� Todo list의 기본적인 진행 구조

2. 파일 시스템 구현

3. 여러 개념들...

4. 마무리


전체적인 구조

  • 어플리케이션 클래스를 만들고, 그 안에 필요한 내용 (유저 목록, 리스트 목록, 리스트 내의 아이템 목록...) 을 만들어주는 클래스들과 함수들을 만들어 나간다. 이번 TIL에선 테스트 구조를 이용하여 이런 과정을 짚어보겠다.

기본 진행


const app = TodoApplication.create();

  • 우선 TodoApplication 클래스의 create() 함수로 객체 app 을 만들며 시작한다. (이때 create 처럼 특정 기능을 해주는 함수를 헬퍼 함수라고 한다.)

const jpham = app.createUser('jpham005', 'jpham004', '');

  • 객체 appcreateUser 함수로 유저를 만든다.
    •   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 를 쓰는 이유는

        1. 외부에서 만들면 비슷한 기능과 이름의 함수가 너무 많아지고

        2. constructor 에선 한가지 함수에 한가지 기능만 담게 하기 위함이다.

    • app 객체의 users 키의 값인 객체에 newUser 객체의 id 라는 key 값을 새로운 key 로 사용하여 newUser(유저 정보 일체) 를 저장한다.

    • 마지막으로 newUser 를 반환하며 함수를 끝낸다.

      • 앱의 객체에 할당도 하고 반환도 하는 이유는 앱과 실사용자 모두가 정보를 갖게 하기 위함이다.

const jphamList = app.createList('jpham todo list', jpham);

  • 여기서부턴 app.createUser() 와 흐름이 비슷하다. 같은 구조로 listsitems 를 만드는것 뿐 이다.

  • createList 에선 이미 중복되는 리스트가 있는지 검사하지는 않는다. 실제로 같은 이름의 리스트가 많으면 혼란스러울 수 있겠지만, 앱 차원에서 막아야 할 이유도 딱히 없다고 느껴진다. (같은 이름의 리스트 여러개를 만들고 랜덤으로 할 일을 정할 순 있겠다...)

  • 마찬가지로 class TodoList 를 이용하여 실질적인 리스트 생성을 한다.

    • 만드는 항목을 좀 살펴보자면,

      1. 유저나 아이템과는 별개의 리스트의 id (결국 유저, 리스트, 아이템 모두가 고유 id 를 가짐으로써, 앱에서 모든 데이터를 다룰 수 있다.)

      2. 후술할 stringify 작업을 위해 아이템 대신 itemIds 배열을 만든다.

      3. 그 외엔 리스트 이름, 리스트 소유주. 만든 시간 이 들어간다.

    • owner 값으로 TodoUser 클래스에서 생성된 객체가 들어가야 하므로 instanceof 를 사용한걸 볼 수 있다.


const todo1 = app.createItem(jphamList, 'Do javascript TIL', '');

  • createItem 에선 아이템을 만드는 과정에 리스트에 아이템을 넣어주는 과정이 추가된다.
    따라서 addItem 이라는 함수를 추가로 만들어야 한다.

  • 리스트와 마찬가지로 리스트 자체가 아닌 리스트의 id 를 받는다.

  • validate 함수를 볼 수 있는데, 검사하는 과정을 분리해서 아이템을 수정하는 updateItem 같은 함수에서는 list 인지 검사하지 않도록 해준다.

    • 또한 추후에 나올 세이브/로드 시 validate 자체를 생략할 수 있다. 이 또한 static 을 사용하는 이유이다.
  •   addItem(item) {
          this.itemIds.push(item.id);
      }
    • 여기서 thisadditemlist.addItem(item); 과 같이 사용되므로, list 가 되는데, listcreateItem 에서 파라미터로 받은 이미 생성된 리스트가 되겠다. 거기에 push 하기 때문에 리스트에 아이템이 추가되는 것 이다.

app.updateItem(itemId, params)

  • updateItem(itemId, params) 함수를 실행시켜 고유한 id 를 가진 아이템을 찾아 (id값을 줘야하는 이유.) params 내용을 업데이트 한다.

  • appitems 에 아이템 객체가 들어가 있기 때문에, updateItem 에서 this.items[itemId] 로 객체를 특정하고, 해당 객체의 내용을 업데이트 할 수 있게 된다.


app.removeItem(todo2.id);

  • 함수 내의 상수 itemapp 객체에서 itemId 로 찾아준다. item 이 정해지면 item 내의 listIdlist 또한 찾을 수 있다.

  • 이 후로는 list (TodoList 로 생성된) 내의 removeItem 메서드 (이름은 같지만 명백히 다른 함수다!) 로 리스트에서도 제거해주고, delete 연산자로 app 에서도 제거하면서 끝낸다.

  • TodoList 클래스의 removeItem 메서드는 filter() 메서드를 이용한다. 파라미터로 받은 item 객체의 idlist 내의 itemIds 에서 찾아 불일치 하는 요소들만 남겨 배열로 리턴한다.


app2.removeList(jphamList.id);

  • 리스트를 지우는 경우는 한 단계만 더 처리를 하면 된다.
    • 리스트에 포함된 아이템들도 모두 지워줘야 하기 때문에, forEach() 를 사용해서 모든 아이템들을 지운다.

app2.removeUser(jpham.id);

  • 유저의 경우에는 유저가 가진 리스트를 지워주는 작업을 하고, 유저를 지우면 된다.

    • 물론 리스트를 삭제하려면 아이템도 삭제해줘야 한다.
  • Object.valuesfilter() 를 이용해서 list 객체의 owner 객체의 id 키 값을 파라미터로 받는 userId 와 비교해서 간단히 해당 유저의 리스트를 찾을 수 있다.


파일 시스템 구현

  • 이제부턴 위에서 작업했던 값 들을 저장/불러오기를 하기 위한 작업에 대한 작동 구조를 간단히 살펴보겠다.

await app.save();

  • save() 에서 데이터들을 toString() 을 이용해서 JSON 문자열 형태로 바꿔주고 저장한다. (node.js에서 지원한다.)

  • apptoSting() 에선

    1. container 배열을 선언해서 app의 각 키 값들을 요소로 넣는다.

    2. data 를 선언하고 map 을 이용해서 containers 배열의 각 요소에 콜백 함수를 실행한다.

    3. (여기서부터 콜백 내용, 결과적으로 app 의 모든 객체에 실행된 결과가 배열의 요소 단위로 data에 저장된다.) entities 에는 객체의 모든 키의 값들이 할당된다. (이때 키의 값들은 각 클래스로 만들어진 객체가 된다. 때문에 toString 메서드를 각 객체로부터 불러올 수 있다.).

    4. data 에는(처음 선언한 data와 다르다.) entities 배열의 각 요소마다 toString 메서드를 실행시켜 JSON 문자열 형태로 바뀐 데이터를 할당한다.

    5. 최종적으로 전체 데이터를 반환하게 되고, map 으로 순회하며 데이터가 배열로 저장된다. (users, lists, items 단위로 배열의 한 요소씩 차지하는 형태.)

  • 각 클래스마다 있는 toString() 은 단순하게 모든 데이터를 JSON 문자열 형태로 변환해서 반환한다.

  • fs.js 파일에서 정해놓은 함수를 이용하여 저장한다. fs.js는 처음 보는 개념이긴 하지만 아직까진 굉장히 직관적인 내용이라 따로 설명하지 않겠다.


const app2 = await TodoApplication.load();

  • 정확히 반대되는 작업을 수행한다. 핵심은 parse() 로 데이터를 해석 해준다는것.

여러 개념들...


class 에서의 메서드

  • class 로 객체를 생성할 때, 함수를 정의 해 두면 그 함수를 객체의 메서드로 사용할 수 있다.(어떻게 보면 당연하지만 consturctorstatic 에 들어가지 않은 형태라 class 기본 문법 페이지를 다시 참조했었다.)

delete expression 연산자

  • 객체에서 속성을 제거해주는 연산자이다. expressionobject.property 같은 속성 참조를 해주면 된다.

computed property name

  • 객체에서 key[]을 이용하면 표현식을 사용할 수 있다. 이를 computed property name 이라고 한다.

arr.filter(callback(element[, index[, array]])[, thisArg])

  • callback 은 각 요소를 시험 할 함수. true 면 요소를 남기고, false 면 버린다.

  • 때문에 TodoList에선 filter() 의 안에 화살표 함수로 콜백을 표현했다.


  • 시간이 얼마가 걸리든 상관없이 약속된 결과를 만들어내는 '제작 코드' 가 준비되었을 때, 모든 소비 코드가 결과를 사용할 수 있게 해준다.

  • 간단히 설명하자면, async 는 함수 앞에 위치하여, 함수의 반환값을 항상 promise 가 되게 한다.

  • await 는 async 함수 안에서만 동작하는데, 프로미스가 처리(settled) 될때까지 기다렸다가 이후 결과가 반환되게 해준다.


arr.map(callback(currentValue[, index[, array]])[, thisArg])

  • callback 함수에 따라서 새로운 배열 요소를 생성하는 메서드다.

JSON.parse(text[, reviver])

  • JSON 문자열 데이터를 해석해준다.

const fs = require('fs/promises');

  • node.js 를 시작하는 구문이라고 생각하면 된다. 정해져있는 것.

  • JavaScript 값이나 객체를 JSON 문자열로 변환한다.

Node.js 의 buffer, stream, binary data

  • 풀어서 설명해둔 글이 있어 링크로 대체한다.

마크다운 목차

  • [내용](링크) 구조를 사용하면 링크를 걸 수 있었는데, 링크에 #이동할 헤드 (실제로 #을 몇개 사용했는지는 상관없다) 를 사용하면 문서 내 링크를 만들 수 있다.

  • 생각해보니 마크다운도 처음 써 보는 물건인데 아무런 정리가 없었다...


git token

  • 8월 13일부로 깃헙에선 token을 써야 커밋이나 기타 등등의 일을 할 수 있게 되었다. 원체 깃헙에 대해 잘 모르는데 갑자기 오류가 떠서 좀 당황했지만 역시 구글에 물어보니 알아서 깃헙 블로그 페이지로 연결해 줘서 빠르게 토큰 만들고 커밋할 수 있었다.

  • 하는 법 : 프로필 - 설정 - 개발자 설정 - 토큰 만들기 - 만료일 및 권한 범위 지정 - 토큰 값 복사 (중요!) - 맥에선 keychain 수정


마무리


이번 TIL은 마침 바쁜 주말과 기나긴 코드리뷰로 꽤나 늦어졌다. TIL 의 통상적 내용과는 맞지 않을 수 있겠지만, 놓친 개념들도 꽤 많이 발견했고 유익했다고 생각한다. 그래도 좀 더 발전하여 이런 기나긴 TIL 을 작성하지 않아도 되기를...