1. ToggleSideNav

1
2
3
4
5
6
7
8
const isOpen = {
get local() {
return JSON.parse(localStorage.getItem("isOpen"));
},
set local(value) {
localStorage.setItem("isOpen", JSON.stringify(value));
},
};
  • 접근자 프로퍼티 사용하면 함수를 사용하지 않아도 응집도를 높힐 수 있다.
1
2
3
4
5
6
7
8
9
10
window.addEventListener("DOMContentLoaded", () => {
if (isOpen.local === null) isOpen.local = false;

$nav.classList.toggle("active", isOpen.local);
});

window.addEventListener("load", () => {
document.body.style.visibility = "visible";
document.body.classList.remove("preload");
});
  • DOMContentLoaded 에서 CSS 속성을 설정해준 뒤 load 이벤트가 발생했을 때, body의 preload 클래스를 제거해줘야 요구사항대로 깜빡임 없이 작동한다. 왜냐하면 preload 클래스가 있으면 모든 자식요소의 transition 이벤트가 무효화된다.

2. tictactoe

  • 즉시실행함수로 클로저를 구현한다. ⇒ 정보은닉가능, 응집도 높이고, 관심사 분리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const people = {
a: [],
b: [],
};

// 승자를 가려내는 로직
Object.entries(people).forEach(([key, value]) => {
winning.some((w) =>
w.every((v, index) => v === value.sort((a, b) => a - b)[index])
);
});

// 리팩터링 후
const items = Array(9).fill(null);

// 승자를 가려내는 함수
const getWinner = () => {
const winning = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];

for (let i = 0; i < winning.length; i++) {
const [a, b, c] = winning[i];
if (items[a] && items[a] === items[b] && items[a] === items[c]) {
return items[a];
}
}
return null;
};
  • people 객체 안에서 플레이어가 선택한 요소를 배열로 관리하니깐 depth가 깊어져서 가독성도 떨어지고 유지보수가 좋지 않다.
    • 요소를 클릭할 때 마다 getWinner() 함수 조건문을 판단한다. 그래서 items 배열에 0번째 인덱스부터 차례대로 player “O” or “X”가 삽입된다. 그런 다음에 승리의 경우의 수를 담은 winning 2차원 배열을 순회하면서 items 배열이 winning 배열과 같은지 비교해주고 있다.
      예를 들면, 처음에 X가 시작하면 X가 누른 요소의 data-id가 items의 index에 ‘X’가 할당되고 그 다음 ‘O’가 누른 요소의 data-id가 items의 index에 ‘O’가 할당된다. 그 때마다 반복문 돌면서 items 배열에 winning 배열의 인덱스에 모두 같은 player가 있는지 확인하여 승리 여부를 확인한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 리팩터링 전
let current = true;
const people = {
O: [],
X: [],
};
let isWin = false;
let winner = '';

// 리팩터링 후
const state = {
player: 'O',
people: {
O: [],
X: [],
},
get isWin() {
const winning = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];

return getCombinations(this.people[this.player], 3).some(c => winning.some(w => w.every((v, index) => v === c.sort((a, b) => a - b)[index])));
},
get winner() {
return this.isWin ? this.player : ' ';
},
get length() {
return Object.values(this.people).reduce((a, c) => a + c.length, 0);
},
};

// 2번째 리팩터링 후
let player = 'X';
let items = Array(9).fill(null);

const getWinner = () => {
...
};

const setPlayer = nextPlayer => {
...
};
  • 관리해야할 상태가 많아지면 부수효과 발생할 가능성이 높아지므로, 상태 최소화를 해주었다.
    • 리팩터링 후 상태값은 줄고 접근자 프로퍼티를 사용하여 로직의 기준을 설정해주었다. 함수를 사용하는 것 대신 접근자 프로퍼티를 쓰게되면 매개변수에 대한 고민이 줄어들어 사용하였다.
    • 하지만 접근자 프로퍼티를 사용하기 위해서는 데이터를 객체안에서 보관해야 하고 그에 따라 접근 방식이 마침표 표기법과 대괄호 표기법으로 depth가 길어져 가독성이 떨어지는 것을 느꼈고 굳이 state로 관리해줄 필요가 없다고 판단하여 state를 제거하여 변수로 나타내고 접근자 프로퍼티는 함수로 나타내었다.

3. Acordion

  • $container = document.querySelector(’.acordian1’) 이다. querySelector가 꼭 document, window에만 붙을 수 있는 것이 아니라 node에 사용 가능한 메서드이다.
1
2
3
4
5
6
7
8
9
10
11
12
// 리팩터링 전
const getHeight = (e) => {
const $item = $container.querySelector(".active > ul > li");
const height = getComputedStyle($item).getPropertyValue("height");
const count = e.target.closest(".active").querySelector("ul > li").length;

return +height.replace("px", "") * count + "px";
};

// 리팩터링 후
const getHeight = ($article = $container.querySelector("article")) =>
$article.querySelector("ul").scrollHeight + "px";
  • ul 요소의 scrollHeight 값을 구하면 overflow : hidden 처리된 요소까지 포함한 높이를 구할 수 있으므로 간결하게 사용할 수 있다.

오늘 배운 점

  1. 접근자 프로퍼티를 사용하여 객체의 응집도를 높이고 가독성을 키울 수 있다는 것을 배웠다.
  2. Array.fill() vs Array.from() 의 차이를 알게 되었다.
1
2
3
4
5
6
arr.fill(value[, start[, end]])

// EX)
[1, 2, 3].fill(4); // [4, 4, 4]
[1, 2, 3].fill(4, 1); // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2); // [1, 4, 3]
  • Array.fill()은 ()안의 요소를 배열에 시작부터 끝 인덱스까지 채운다.
1
2
3
4
5
6
Array.from(arrayLike[, mapFn[, thisArg]])

mapFn : 배열의 모든 요소에 대해 호출할 맵핑 함수

// EX)
Array.from([1, 2, 3], x => x + x); // [2, 4, 6]
  • Array.from()은 배열로 변환하고자 하는 유사배열 객체 또는 Iterable 객체를 얕게 복사하여 새로운 Array 객체를 만든다.
  • 주로 이중배열을 작성할 때 사용한다.