데브코스 프론트엔드 5기/VanillaJS를 통한 자바스크립트 기본 역량 강화 1

231006 [Day14] VanillaJS를 통한 자바스크립트 기본 역량 강화 1 (3)

코딩하는 키티 2023. 10. 9. 13:28

 

컴포넌트 방식으로 생각하기(w. To do list)

 To-do 리스트를 추상화 하기

  • UI를 선언적으로 표현하기 위해 TO-DO 리스트를  Header, TodoFrom, TodoList, App의 4가지로 추상화
  • Header, TodoFrom, TodoList, App 하나하나가 컴포넌트이다.
    • 컴포넌트의 재사용 가능, 기능 추가시 간편

 

1) TodoList 컴포넌트 만들기

 

명령형 코드 방식

//params.$target - 해당 컴포넌트가 추가가 될 DOM 엘리먼트
//params.initialState - 해당 컴포넌트의 초기 상태

function TodoList(params) {
  const $todoList = document.createElement('div');
  const $target = params.$target;
  $target.appendChild($todoList);

  this.state = params.initialState;
  
  this.render = () => {
    // 명령형
    let html = '';

    for(let i=0;i<this.state.length;i++){
      html += `<li>${this.state[i].text}</li>`
    }
    html += `<ul>${html}</ul>`;
    $todoList.innerHTML = html;
  }

  this.render();
}

 

함수형 코드 방식(+ es6) 

// object destructuring
function TodoList({$target, initialState}) {
  const $todoList = document.createElement('div');
  $target.appendChild($todoList);

  this.state = params.initialState;

  this.render = () => {
    $todoList.innerHTML = `
      <ul>
        ${this.state.map(({text}) => `<li>${text}</li>`).join('')}
      </ul>
      `

      //this.state = [{text:'자바스크립트공부하기'},{text: '....'}]
      
      //map을 돈 이후에는 아래처럼 만들어진다.
      /*
      * this.state.map(todo=>`<li>${todo.text}</li>`)
      * [{text:'<li>자바스크립트공부하기</li>','<li>....</li>'}]
      *	['<li>자바스크립트공부하기</li>', '<li>....</li>']
      * join('')
      * <li>자바스크립트공부하기</li><li>....</li>
      */
    
  }

  this.render();
}

 

 

 2) TodoList를 화면에 불러오기

- main.js을 작성한다. (new를 통해 TodoList를 생성한다)

const data = [
  {
    text: '자바스크립트 공부하기'
  },
  {
    text: '자바스크립트 복습하기'
  }
];

const $app = document.querySelector('.app')

new TodoList({
  $target: $app,
  initialState: data
});

 

index.html 파일에 js파일들을 불러온다.

<html>
    <head>
        <title>KDT</title>
    </head>
    <body>
        <main class="app"></main>
        <script src="./src/TodoList.js"></script>
        <script src="./src/main.js"></script>
    </body>
</html>

 

script 코드 위치는 중요하다

  • 먼저 불러와야 되는 파일이 먼저 와야 한다.
  • 파일이 head쪽에 있으면 그 스크립트가 불러올 때까지 사용자는 빈 화면만 보임
  • body안 아래에 위치하면 내용이 불러와진다.(조금 더 나은 경험을 준다.)

 

 

3) TodoForm 컴포넌트 만들기

  • TodoForm의 input에 작성한 데이터가 TodoList로 들어가야한다.
  • 하지만, 파라미터에 TodoList를 넣고 직접 참조하는 방식은 두 함수간의 의존성이 강하게 생김
    •  => 파라미터로 onSubmit 함수를 받는다.
function TodoForm({$target, onSubmit}) {
  const $form = document.createElement('form');

  $target.appendChild($form);

  let isInit = false;

  this.render = () =>{
    $form.innerHTML = `
      <input type="text" name="todo" />
      <button>Add</button>
    `
    if(!isInit) { 
      $form.addEventListener('submit', e => {
        e.preventDefault(); // form action을 막는다.

        const $todo = $form.querySelector('input[name=todo]');
        const text = $todo.value;

        $todo.value = '';
        if(text.length>1){ // 글자수 2이상일때
          $todo.value= '';
          onSubmit(text);
        }

      })
      isInit = true;
    }
  }

  this.render();
}

 

// main.js
new TodoForm({
  $target: $app,
  onSubmit: (text) => {
    const nextState = [...todoList.state, {
      text
    }]
    todoList.setState(nextState)
  }
})

main에서는 todoList의 상태를 변경해주는 코드를 작성하여 TodoForm에서는 TodoList와의 의존성을 최소화 한다.

 

 

4) Header 컴포넌트 만들기

function Header({$target, text}){
  const $header = document.createElement('h1');

  $target.appendChild($header);

  this.render = () => {
    $header.textContent = text
  }

  this.render();
}

 

5) App.js로 화면에 그려지는 요소 관리하기

function App({$target, initialState}) {
  new Header({ $target, text: 'Simple Todo List'});

  new TodoForm({
    $target,
    onSubmit: (text) => {
      const nextState = [...todoList.state, {
        text
      }]
      todoList.setState(nextState);
    }
  })

  const todoList = new TodoList({
    $target,
    initialState,
  })

}

main에서는 App.js를 불러온다.

const $app = document.querySelector('.app');

new App({
  $target: $app,
  initialState
})

 

 

Client Side에서 데이터를 저장하기

Cookie

  • 쿠키는 브라우저에 저장되는 작은 문자열로 RFC 6265 명세에서 정의한 HTTP 프로토콜의 일부
  • 다른 저장 방법에 비해 가장 오래된 방식

 

1) Cookie 추가하기

document.cookie = 'KDT=is_fun';

 

2) Cookie 읽어오기

const cookies = document.cookie;

쿠키는 ; 문자열로 끝나기 때문에 모든 쿠키 정보를 배열에 담고 싶으면 split() 메서드를 이용

 

3) Cookie 유효기간 넣기

유효기간은 GMT 시간을 기준으로 하기 때문에 new Date().toGMTString() 으로 구할 수 있다.

// 하루뒤 만료되는 쿠키 설정하기
const date = new Date();
date.setDate(date.getDate() +1);

document.cookie = 'user=euna; expires=`${date.toGMTString()}`';
//document.cookie = 'user=kitty; expires=Wed, 18 Aug 2021 01:30:36 GMT";

// max-age 이용하기
// 생성시점 기준으로 유효기간 설정 가능
document.cookie = 'user=kitty; max-age=60'; // 1분 뒤

유효기간이 없으면 브라우저가 꺼지면 같이 삭제된다.

 

4) 주의 사항

  • HTTP 요청시 헤더에 쿠키가 같이 나가기 때문에 쿠키 사이즈가 크면 HTTP 요청 크기도 커짐
  • 사이즈에 제한이 있음
  • 여러가지 보안 취약점 주의

 

Local Storage

  • key, value 기반 Local에 데이터를 저장할 수 있음
  • 도메인 기반으로 Storage가 생성이 된다(도메인만 같다면 여러 탭 내 같은 Storage 공유가능)
  • 삭제하거나 Storage를 날리지 않는 한 삭제되지 않음

 

1) 값 저장(3가지 방식)

window.localStorage.name = 'kitty';
window.localStorage['name'] = 'kitty';
window.localStorage.setItem('name', 'kitty');

setItem을 쓰는 것을 권장(property를 수정하는 방식은 length, toString 같은 내장 함수들을 덮어 씌울 수 있음)

 

2) 불러오기

const storedName = localStorage.getItem('name');

 

3) 삭제하기

localStorage.removeItem('name');
localStorage.clear(); // 전체 삭제

localStorage에는 string만 넣을 수 있기 때문에 넣을 때는 JSON.stringfy로, 꺼낼 땐 JSON.parse로 파싱하는 게 좋음

* object를 넣었을 시 Object(텍스트)가 들어감

const user = {
	name: 'kitty',
    position: 'base',
    song: 'love'
}

//저장
localStorage.setItem('user', JSON.stringify(user))

//불러오기
const storedName = JSON.parse(localStorage.getItem('name'))

 

Session Storage

  • 전체적으로 Local Storage와 같다.
  • 브라우저를 닫으면 저장된 내용이 초기화됨.

 

To-do 앱에 웹스토리지 추가하기

  • TodoList에서 직접 로컬스토리지에 접근하여 가져오는 것은 사용자가 임의로 로컬스토리지를 변경할 위협이 있기에 try-catch문으로 따로 함수를 작성하는 것이 좋다. (안그러면 로딩자체가 안됨)
  • setItem() 함수에서는 key를 바탕으로 로컬 스토리지에 item을 저장하고, 에러 발생시 콘솔에 찍어준다.
  • getItem() 함수에서는 키와 기본값을 받아 key를 통해 로컬스토리지에서 value를 받아올 수 있으면 상태를 업데이트 하고 아니라면 기본 값([])을 상태로 지정하여 화면에 불러올 수 있도록 한다.
const storage = (function(){
  const setItem = (key) => {
    try{
      storage.setItem(key, value);
    }catch(e) {
      console.log(e);
    }
  }

  const getItem = (key, defaultValue) => {
    try{
      const storedValue = storage.getItem(key);

      if(storedValue) return JSON.parse(storedValue);
      return defaultValue;

    }catch(e){
      console.log(e);
      return defaultValue;
    }
  }
})(window.localStorage)

 

main.js 에서 함수를 불러온다.

const initialState = storage.getItem('todos', []);

 

App.js의 TodoForm 생성에서 setItem()을 호출한다.

new TodoForm({
    $target,
    onSubmit: (text) => {
      const nextState = [...todoList.state, {
        text
      }]
      todoList.setState(nextState);

      storage.setItem('todos', JSON.stringify(nextState));
    }
  })