주녁, DevNote
article thumbnail

1. 개요

모던 자바스크립트의 구조를 학습하고 컴포넌트 기반의 코드를 작성한다.

2. 목표

순수 Javascript로 작성된 Todo List 어플리케이션을 작성한다.

(이 글은 Next Step에서 진행한 js-todo-list-step을 따라 작성되었습니다.)

 

전체 코드는 여기 에서 보실 수 있습니다.


3. 여정

현재 store는 아래와 같은 문제점이 있다.

  • store를 사용하기 위해 컴포넌트들이 알아야할 정보가 너무 많다. (store 변경에 취약하다, 의존적이다)
  • store를 호출하면서 중복되는 코드를 재사용할 수 없다.
  • 어플리케이션을 재시작하면 정보가 초기화된다(휘발성이 있다, 실제 DB를 이용하고 있지 않다.)

하나씩 문제를 해결해보도록 하자.

 

 

3.1. Store 의존성 감소

기존 컴포넌트에서는 아래와 같은 형태로 Store를 호출해서 사용했다.

<javascript />
// TodoInput 컴포넌트 createTodoItem(content) { const newTodoItem = { id: String(Date.now()), user: store.getState().selectedUser, // Store를 통한 조회 content: content, completed: false }; store.dispatch(createTodo(newTodoItem)); // store를 통한 요청 }

 

이런 경우에는 store가 어떤 상태를 가지고 있는지,

어떤 연산자를 필요로 하는지, 컴포넌트가 모두 알아야한다.

또한, 연산자가 추가/변경될때마다 같이 변경될 가능성도 포함하고 있다.

 

따라서 필자는 아래와 같이

reducer를 직접 다루는 creator가 store를 직접 사용하도록 했다.

<javascript />
createTodoItem(event) { const newTodoItem = { id: String(Date.now()), user: getSelectedUser(), // creator.js로 코드 응집 content: content, completed: false }; createTodo(newTodoItem); // creator.js로 코드 응집 }
<javascript />
// creator.js(변경 전) const createTodo = (todo) => { return { type: CREATE_TODO, payload: { todo }, } } // creator.js(변경 후) const createTodo = (todo) => { store.dispatch({ // store를 직접 다루도록 변경 type: CREATE_TODO, payload: { todo }, }); }

 

이렇게 creator가 reducer를 직접 다룸으로써

 

 

컴포넌트는 자신이 필요한 creator만 알고 있으면

store가 누구인지, 몇개인지, 어떤 연산을 하는지 몰라도 된다.

 

사실, 우리는 한가지 문제를 더 해결했다는 사실을 아는가?

 

3.2. Store 코드 재사용

creator에서 reducer를 직접 사용함으로써

다양한 상태들을 불러서 의미를 부여하고 재사용할 수 있다.

<javascript />
// user/creator.js // 유저 목록 조회 const getUserList = () => { const { users } = store.getState(); return users; } // 선택한 유저 조회 const getSelectedUser = () => { const { selectedUser } = store.getState(); return selectedUser; } // 유저 존재여부 확인 const isUserExist = (userName) => { const { users } = store.getState(); return users.find(user => user.name === userName); }

 

기존 코드를 보면 더 명확히 알 수 있다.

 

<javascript />
// 기존 const { users, selectedUser } = store.getState(); // 변경 const users = getUserList(); const selectedUser = getSelectedUser();

기존에는 store의 상태에서 어떤 정보를 담고있는지 명확히 알아야했고,

store.getState()의 호출만으로는 어떤 정보가 조회/사용됬는지 알기 어려웠다.

 

이를 재사용하고 명확하게 추적할 수 있게된 것이다.

 

자, 이제 마지막으로 휘발성을 제거해보자!

 

3.3. Store 휘발성 제거

(원래대로라면 API를 이용해서 DB에 저장하고 불러와야 하지만

그 부분은 추후 시리즈에서 진행해보도록 하자.)

 

이 시리즈 초창기에 작성했던 Store 코드로 돌아가야 한다.

<javascript />
import { observable } from "./observer.js"; export const createStore = (reducer) => { // 스토어를 생성한다. // 스토어는 상태를 관리하고, 상태를 변경하는 기능을 가진다. // reducer 가 실행될 때 // 반환하는 객체 State 를 Observable 로 만든다. const state = observable(reducer()); const dispatch = (action) => { // 액션을 실행한다. // 액션을 실행하면 reducer 가 실행되고, // reducer 가 반환하는 새 객체를 state 에 할당된다. const newState = reducer(state, action); for (const [key, value] of Object.entries(newState)) { if(!state[key]) continue; state[key] = value; } } // state 를 변경할 수 없도록 한 frozenState 를 만든다. const frozenState = {}; Object.keys(state).forEach(key => { Object.defineProperty(frozenState, key, { get: () => state[key] }); }); // frozenState 를 반환하는 getState 함수를 만든다. const getState = () => frozenState; return { getState, dispatch }; }

 

코드는 길지만 흐름은 단순하다.

다시 되짚어보면 

초기상태 지정 → dispatch(action) / getState() 의 흐름으로 흘러간다.

그렇다면, 초기상태를 불러올 때 localStorage에서 불러오도록,

저장할때 들고있는 state와 함께 localStorage에도 저장하도록 하면 된다!

(코드의 변경사항이 많지 않다)

 

<javascript />
... /** 변경된 부분 **/ const initialState = reducer(); const storedState = JSON.parse(localStorage.getItem("state")); const state = observable(storedState || initialState); const dispatch = (action) => { // 액션을 실행한다. // 액션을 실행하면 reducer 가 실행되고, // reducer 가 반환하는 새 객체를 state 에 할당된다. const newState = reducer(state, action); for (const [key, value] of Object.entries(newState)) { if(!state[key]) continue; state[key] = value; } ... /** 변경된 부분 **/ // 변경된 state를 localStorage에 저장한다. localStorage.setItem("state", JSON.stringify(state)); }

실행한 결과는 아래와 같이

F12 → 응용 프로그램 → 로컬 저장소에서 확인할 수 있다.

이제 새로고침을 하거나 재시작해도 Todo List가 유지되는 것을 확인할 수 있다.


4. 마무리

지금까지 모던 자바스크립트 형태로 작성한 TodoList 어플리케이션을 작성해보았다.

 

고생했다! 이제야 프론트엔드의 첫 시리즈가 끝났다.

이러한 컴포넌트 형태가 발전해서 캐로셀이 되고, 무한 스크롤이 되는 것이다.

이런 형태가 모이고 모여서 발전한 형태가 React이고 Vue인것이다.

 

다음 시리즈에는 더욱 발전한 프로젝트를 진행해보도록 하자!

profile

주녁, DevNote

@junwork

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!