개요
모던 자바스크립트의 구조를 학습하고 컴포넌트 기반의 코드를 작성한다.
목표
순수 Javascript로 작성된 Todo List 어플리케이션을 작성한다.
(이 글은 Next Step에서 진행한 js-todo-list-step을 따라 작성되었습니다.)
전체 코드는 여기에서 보실 수 있습니다.
여정
버튼을 누르면 이벤트 호출
버튼을 누르면 이벤트가 발생해야 한다.
잊고 있었겠지만 Component.js에는 이벤트 관련 함수가 두개나 작성되어 있다.
이벤트를 추가하는 addEvent()와 모든 이벤트를 등록하는 setEvent()다.
// Component.js 중
addEvent (eventType, selector, callback) {
// 컴포넌트의 이벤트를 추가한다.
this.$target.addEventListener(eventType, event => {
if(!event.target.closest(selector)) return false;
callback(event);
});
}
setEvent () {
// 컴포넌트의 모든 이벤트를 등록한다.
// 모든 이벤트를 this.$target 에 등록하여 사용하면 된다. (이벤트 버블링)
/* ex)
const { deleteItem, toggleItem } = this.$props;
this.addEvent('click', '.deleteBtn', ({target}) => {
deleteItem(Number(target.closest('[data-seq]').dataset.seq));
});
this.addEvent('click', '.toggleBtn', ({target}) => {
toggleItem(Number(target.closest('[data-seq]').dataset.seq));
});
*/
}
이를 활용해서 UserList에 이벤트를 등록하면 아래와 같다.
// UserList.js
setEvent () {
this.addEvent('click', '.user-create-button', (event) => {
// 유저 생성 버튼 클릭 시 발생하는 일
});
this.addEvent('click', '.user-delete-button', (event) => {
// 유저 삭제 버튼 클릭 시 발생하는 일
});
}
createUser(...) { // 유저 생성 로직 }
deleteUser(...) { // 유저 삭제 로직 }
굳이 이벤트 발생 함수와 실제 처리 함수를 분리한 것은
검증 로직과 비즈니스 로직(유저 생성, 삭제)은 분리되어야 하기 때문이다.
이제 유저 생성/삭제 로직을 작성해보자.
유저 생성
우선 이벤트 발생 시 기본적인 검증로직을 작성해준 뒤
비즈니스 로직을 호출해준다.
// UserList.js 중..
setEvent () {
this.addEvent('click', '.user-create-button', (event) => {
const userName = prompt('추가하고 싶은 이름을 입력해주세요')
if (!userName) {
alert('이름을 반드시 입력해야 합니다.');
return false;
}
if (userName.length < 2) { // 제시된 제약조건
alert('사용자 이름은 2글자 이상이어야 합니다.');
return false;
}
if(this.isUserExist(userName)){
alert('이미 존재하는 이름입니다.');
return false;
}
this.createUser(userName);
});
...
}
getUsers() { // 전체 유저 조회
return store.getState();
}
isUserExist(userName) { // 유저가 이미 존재하는지 확인
const { users } = this.getUsers();
return users.find(user => user.name === userName);
}
createUser(userName) { // 유저 생성
store.dispatch(createUser(userName));
}
그 다음은 비즈니스 로직에서 store에 요청을 보내는 dispatch() 함수를 호출한다.
여기서 createUser()는 reducer의 action이다.
현재 우리는 유저 이름만 알고있으니 이름을 매개변수로 전달한다.
reducer의 형식은 { type, payload } = action 형태이다.
전달하는 내용에 맞춰 함수를 작성한다.
// actions.js
// 타입을 정의하는 변수이므로 추후에는 코드 소개는 생략한다
const GET_USERS = 'GET_USERS'
const CREATE_USER = 'CREATE_USER'
export {
GET_USERS,
CREATE_USER,
}
// creator.js
import {
GET_USERS,
CREATE_USER,
DELETE_USER,
} from './actions.js'
const getUsers = (users) => {
return {
type: GET_USERS,
payload: {
users,
},
}
}
const createUser = (name) => {
return {
type: CREATE_USER,
payload: {
name,
},
}
}
export { getUsers, createUser }
그리고 이를 reducer에서는 어떻게 처리할 것인지 정의해준다.
// reducer.js
import {
GET_USERS,
CREATE_USER,
DELETE_USER,
} from "./user/actions.js";
let initialState = {
users: [],
}
const reducer = (state = initialState, action = {}) => {
switch (action.type) {
case GET_USERS:
return {
...state,
...action.payload,
}
case CREATE_USER:
return {
...state,
users: [...state.users, action.payload],
}
default:
return state;
}
}
중요한 점은 전달된 객체의 상태를 변경해서는 안되며,
상태가 바뀐다면 새로운 객체를 반환해야 한다는 점이다.
자 이제 결과를 확인해보자
하지만 여러번 눌러본 사람이라면 알겠지만
이벤트 콜백이 1, 2, 3개 점점 증가한다.
이 때 이벤트 전파 방지함수를 호출하면 이를 해결할 수 있다.
물론 Component의 addEvent 구현 안에 집어넣어도 된다. (필자는 집어넣기로 했다)
// UserList.js
setEvent () {
this.addEvent('click', '.user-create-button', (event) => {
...
event.stopImmediatePropagation();
});
...
}
유저 삭제
유저 삭제도 생성과 동일한 과정을 거쳐 작성한다.
// UserList.js
setEvent () {
...유저 생성...
this.addEvent('click', '.user-delete-button', (event) => {
const userName = prompt('삭제하고 싶은 이름을 입력해주세요')
if (!userName) {
alert('이름을 반드시 입력해야 합니다.');
return false;
}
if (userName.length < 2) { // 제시된 제약조건
alert('사용자 이름은 2글자 이상이어야 합니다.');
return false;
}
if(this.isUserNotExist(userName)){
alert('존재하지 않는 이름입니다.');
return false;
}
this.deleteUser(userName);
});
}
isUserNotExist(userName) {
return !this.isUserExist(userName);
}
deleteUser(userName) {
if(this.isUserNotExist(userName)){ return false; }
store.dispatch(deleteUser(userName));
}
// creator.js
...
const deleteUser = (name) => {
return {
type: DELETE_USER,
payload: {
name,
},
}
}
export { getUsers, createUser, deleteUser }
// reducer.js
const reducer = (state = initialState, action = {}) => {
switch (action.type) {
...
case DELETE_USER:
return {
...state,
// 유저이름이 동일하면 필터링하여 반환
users: state.users.filter(user => user.name !== action.payload.name),
}
...
}
}
마무리
자 드디어 Component를 하나 완성했다.
그러나, 아직 기뻐하긴 이르다.
Todo리스트 작성, 필터링 등 다양한 Component가 남았다.
다음 시간에는 TodoList를 생성/삭제하는 로직을 작성해보도록 하자.
'Frontend' 카테고리의 다른 글
모던 자바스크립트 TodoList - Todo Component (3) (0) | 2023.05.08 |
---|---|
모던 자바스크립트 TodoList - Todo Component (2) (0) | 2023.05.06 |
모던 자바스크립트 TodoList - Todo Component (1) (0) | 2023.05.02 |
모던 자바스크립트 TodoList - Store & Reducer (0) | 2023.04.17 |
모던 자바스크립트 TodoList - Component & Observer (0) | 2023.04.16 |