주녁, DevNote
article thumbnail

개요

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

목표

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

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

 

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


여정

User는 TodoList를 가져야한다

TodoList는 사람이 해야할 일을 작성한 것이다.

즉, User는 TodoList를 소유해야 한다.

 

마침내, 처음 작성했던 User 관련 컴포넌트와

Todo 컴포넌트를 연결하는 순간이다.

 

먼저 유저를 선택할 수 있도록 기능을 추가하자.


UserList에 User 선택기능 추가

User를 선택하려면

User 목록을 가지고 있는 UserList에 이벤트를 붙여야한다.

// UserList.js
setEvent () {
    this.clickCreateButton();
    this.clickDeleteButton();
    this.clickUserItem(); // 추가
}

clickUserItem() {
    this.addEvent('click', '#user-list .ripple', (event) => {
        const userName = event.target.dataset.username;
        this.selectUser(userName);
    });
}

selectUser(userName) {
    const { selectedUser } = store.getState();
    if (selectedUser.name === userName) { return; }
    store.dispatch(selectUser(userName)) // 새로운 Reducer 연산이 필요하다
}

 

이벤트가 발생하면 선택된 User는 어디에 저장되는가?

이를 저장할 새로운 상태와 연산이 필요하다.

 

// reducer.js
let initialState = {
    users: [],
    todos: [],
    filter: FILTER_TYPE.ALL,
    selectedUser: "-", // 새로운 상태 추가
}

const reducer = (state = initialState, action = {}) => {
    switch (action.type) {
        ...
        case SELECT_USER: // 새로운 연산 추가
            return {
                ...state,
                selectedUser: action.payload.name,
            }

 

이렇게하면 아래와 같이

선택된 유저를 불러와 Rendering 할수 있다

// UserList.js

const setActiveUser = (isSelected) => {
    return isSelected ? 'active' : '';
}
const UserListItem = (user, isSelected) => {

    return `
        <button class='ripple ${setActiveUser(isSelected)}' data-username="${user.name}">
            ${user.name}
        </button>
    `
}

template () {
    const { users, selectedUser } = store.getState();
    return `
        <div id="user-list">
            ${ users && users.map( (user) => 
                UserListItem(user, selectedUser === user.name)
            )}
	...

유저를 선택할 수 있게 된 모습

다음은 Todo에 User 상태를 매칭시켜 주도록 하자


TodoList에 User 상태 추가

User별로 Todo를 분류하려면 Todo마다 User가 매칭되어야 한다.

(현재 Todo 상태를 정의하는 컴포넌트는 TodoInput 뿐이다.)

// TodoInput.js
createTodoItem(content) {
    const newTodoItem = {
      id: String(Date.now()),
      user: store.getState().selectedUser, // 추가
      content: content,
      completed: false
    };
    store.dispatch(createTodo(newTodoItem));
  }

위와 같이 현재 선택된 User를 불러와 저장하도록 하자.

 

필자는 기존 상태(users, todos)를 활용하기 위해서 

selecetedUser 이름을 저장하고 필터링하는 방식을 채택했다.

 

이렇게 하면 전체 목록을 불러오는 과정에서

selecetedUser 이름으로 필터링만 껴넣어주면 큰 코드 변경 없이

기능을 추가할 수 있다. 

(TodoCount에도 같은 방법을 적용해주어야 한다!)

// TodoList.js

const selectedUserTodoList = () => {
    const { todos, selectedUser, filter } = store.getState();
    const selectedUserTodos = todos.filter(todo => todo.user === selectedUser);
    return filteredTodoList(filter, selectedUserTodos);
}

template () {
        const todos = selectedUserTodoList()

        // 컴포넌트의 내용을 반환
        // join을 하지 않으면 ','까지 같이 출력된다
        return `
            <ul class="todo-list">
                ${renderTodoList(todos)}
            </ul>
        `;
    }

User 222의 할 일 목록
User 333의 할 일 목록

 

선택된 유저상태를 다루는 방법은 여러가지가 될수 있다.

조금 더 나아가 생각해보면 이 부분에는 분명 개선의 여지가 있다.

전체 리스트를 필터링하는 방법은 두가지 단점이 있다고 생각한다.

  • Rendering마다 전체 리스트를 조회하는 과정이 필요하다. (시간이 더 걸린다)
  • 로그인이 필요한 경우라면, 전체 유저 목록(users)를 가져올 수 없다.

따라서, 이 글을 읽는 독자 여러분들은 이 부분에서 각자의 방법으로 작성해보길 바란다!


마무리

이번 시간에는 User - TodoItem간의 관계를 정의하고 기능을 구현해보았다.

 

구현하면서 느꼈을지도 모르지만

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

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

다음 시간에는 store를 리팩토링해보도록 하자.


 

profile

주녁, DevNote

@junwork

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