Toaster 리팩터링 중 느낀점

1. 함수의 인수는 3개를 넘지 말자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const generateToast = (messageType) => {
const [type, title, message] = messageType;
const $newToast = document.createElement("div");
$newToast.style.bottom = "0";
$newToast.classList.add("toast", type);

$newToast.innerHTML = `
<h4 class="toast-title">${title} ${
[...document.querySelectorAll("body .toast")].length
}</h4>
<div class="toast-message">
<svg width="24" height="24">
<use xlink:href="#${type}" />
</svg>
<p>${message}</p>
</div>
<a class="toast-close">&times;</a>`;

return $newToast;
};
  • 배열 스트럭처링으로 매개변수를 함수 내부에서 나눠서 배치하였다.

2. 함수를 사용하는 이유를 고민하자.

1
2
3
4
5
6
7
8
9
10
11
12
const addToast = (messageType) => {
const $newToast = generateToast(messageType);

$body.appendChild($newToast);
lineUpToast();

setTimeout(() => {
$newToast.remove();

lineUpToast();
}, 3000);
};
  • setTimeout 함수를 따로 함수로 빼서 사용해보면 어떨까 생각해보았지만, 만약 함수로 들어가게 된다면 오히려 그 함수를 이해하기 위해 더 큰 노력을 쏟아야 할 수 있으므로 가치 판단을 하여 함수로 넣을지 말지를 고민하자.

Autocomplete 리팩터링 중 느낀점

1. setState 함수는 함수 내부의 데이터를 변경만 하는 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let state = {
countryCode: [],
};

const setState = (newState) => {
state = { ...state, ...newState };
render();
};

const init = () => {
setState({ countryCode });
};

document
.querySelector(".autocomplete-toggle-button")
.addEventListener("click", () => {
$suggester.classList.toggle("hide");
$searchInput.focus();

autocomplete.init();
});
  • 이벤트 핸들러는 모델에서 return 해준 함수만 사용할 수 있도록 하여 관심사를 분리해준다.

  • 또한, 이벤트 핸들러가 setState 함수를 사용하여 데이터를 직접 변경하는 것은 옳지 않다. 위와 같이 init 함수를 통해서 사용하는 것으로 한다. 왜냐하면 setState 함수는 데이터 변경에만 관여하는 함수이지 다른 기능을 하는 함수가 아니기 때문이다.

2. 함수 역할의 분리

1
2
3
4
5
6
7
8
9
10
11
12
const findCountry = (inputValue) => {
const regExp = new RegExp(inputValue, "i");

const newCountryCode = countryCode
.filter(([, country]) => country.match(regExp))
.map(([code, country]) => [
code,
country.replace(regExp, `<strong>${country.match(regExp)}</strong>`),
]);

setState({ countryCode: newCountryCode });
};
  • 이전 코드에서는 findCountry 함수가 하는 역할을 setState 함수내에 있어 이를 역할에 맞게 구분해주었다.

  • 또한, 배열 디스트럭처링을 사용하여 가독성을 높였다.

([, country]) 처럼 매개변수로 배열 디스트럭처링 사용할 때, 앞의 매개변수를 사용하지 않는다면, 빈칸으로 두어도 된다.

3. 함수의 가독성을 높이자.

1
2
3
4
const selectCountry = (target) => {
$toggleButtonSpan.innerHTML = target.firstElementChild.innerHTML;
$toggleButtonSpan.classList.add("country");
};
  • selectCountry 함수의 매개변수가 이벤트가 발생한 요소를 전달해주도록 코드를 작성하여 더욱 직관적으로 작성했다.

Carousel 리팩터링 중 느낀점

1. state로 관리해야하는 데이터와 그렇지 않은 데이터

1
2
3
4
5
6
7
let state = {
currentSlide: 1,
};

let isTransitioned = true;
const DURATION = 500;
const newImages = [images[images.length - 1], ...images, images[0]];
  • 이번 과제를 하면서 어떤 데이터를 state로 관리해야할지 결정하는 것이 힘들었다. 짝 코딩을 하면서 나름 이유와 컨벤션을 정하였다.

state로 관리해야하는 데이터

  1. 사용자의 액션에 의해서 변경되는 데이터

  2. 실제 화면의 렌더링에 영향을 끼치는 데이터

  3. 서버에 저장해야할 필요가 있는 데이터

위 3가지 항목에 해당된다면 해당 데이터를 state로 관리하는 것이 옳다고 판단하였다.

그에 대한 근거는 다음 예시를 통해 알아보자.

1
2
3
4
5
6
7
const completeTransition = () => {
setState({ isTransitioned: true });
};

const completeTransition = () => {
isTransitioned = true;
};
  • 모델안의 completeTransition 함수는 state 데이터를 수정하고 렌더링을 해주는 코드였다. 하지만 위 데이터는 화면에 보여지는 데이터도 아니고, 사용자의 액션에 의해 직접적으로 변경되었다기 보다는 사용자가 액션을 발생 시켰을 때 개발자가 눈속임으로 화면을 보여주기 위함이기 때문이다.

즉, completeTransition 데이터를 사용자에게 직접적으로 보여주지도 않고 서버에 전송해야할 데이터도 아니고 실제 화면에 렌더링에 영향을 끼치는 데이터가 아니기 때문에 state로 관리 하지 않았다.

2. Model과 Controller 패턴의 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const carousel = () => {
const carouselSlider = (() => {

...

return {
init,
setStyle,
prev,
next,
completeTransition,
}
})();

window.addEventListener('DOMContentLoaded', carouselSlider.init);
}

carousel(document.querySelector('.carousel'), [images...])
  • addEventListener가 사용할 수 있는 데이터는 Model에서 반환해준 함수만 사용할 수 있다. 이러한 구조를 만듦으로서 사용자는 자신이 어떤 함수를 사용해야하는지 명시적으로 알 수 있고 다른 함수에 대한 고민을 하지 않아 코드의 이해도를 높일 수 있다.

3. DOMContentLoaded와 load 이벤트 핸들러

1
2
3
window.addEventListener("DOMContentLoaded", carouselSlider.init);

window.addEventListener("load", carouselSlider.setStyle);
  • 이미지가 로드된 다음에 이미지의 width 값과 속성을을 설정해줘야지만 제대로 동작하므로 위와 같이 로드되는 시점에 따라 코드를 구분하였다.

  • 또한 요구사항에서 이미지가 로드되어 carousel의 width가 정해진 후 opacity를 1로 바꿔줘야 하므로 로드 시점에 따라 이벤트 핸들러를 구분하는 것이 옳다.