항해99 플러스 프론트엔드 5기 2주차
김영우 학습 메이트 님의 강의
코드 리뷰란?
- 다른 사람이 작성한 코드를 함께 읽고, 서로 피드백을 주고받는 활동
장점
- 성장 가속화
- 버그 예방
- 코드 퀄리티 향상
- ??
- 협업 역랑 강화
리뷰 잘하는법 받는법
받는 사람: 의도 설명, 감사히 받기
주는 사람: 제안형 표현, 함께 고민
코드리뷰는 피드백이 아닌 선물입니다.
지난주 과제에 대한 데모
데모를 보면서 준일 코치님의 생각 하는 방식과, 쓰시는 방법을 보면서
모든 코드는 머리속에 생각하고있는걸 logical 한 스텝으로 하나하나 edge case생각하며 쓰는것
내가 모르는 나라의 언어로 내가 생각한 말을 그대로 의미도 같게 번역을 하고싶으면
사전도 찾고 번역도 돌리고 하다가, 그 언어만의 규칙이나 문법이 있을테니
그걸 찾아서 적용해가면서, 맞는지 안맞는지 물어보고 사용해보고
점점 내것으로 만들어가는 것이라는걸 깨달았습니다.
What is Virtual DOM?
When the app starts, the entire UI is represented as a Virtual DOM.
as the state and props change, react re-renders the affected UIs in the virtual DOM, but not in the actual DOM itself.
React uses Diffing algo to compare the actual DOM vs virtual DOM.
Instead of rerendering the entire UI, react determines the best possible way to effectively apply changes to actual DOM.
React makes those changes to the actual DOM.
앱이 실행될때, 모든 UI는 virtual DOM으로 그려지는데
상태나 props가 바뀌면서, 리액트는 virtual DOM에 변경점이 있는 UI를 re-render 해줍니다. real DOM은 냅두고
리액트는 Diffing algo를 사용해서 actual DOM과 virtual DOM의 차이점을 알아내고
전체 리렌더링대신 그 차이점만 리렌더링하려고 리액트가 제일 효과적인 방법을 찾는다고 합니다
그 방법과 차이점을 실제 DOM에 그린다고 합니다.
-> 전부 다 리렌더링 안하고 차이점만 렌더링 하려고 가상 공간 (layer?)를 만들어 사 용하는 것 ?
virtual DOM은 선언적으로 코드를 짜게 해줍니다.
UI를 어떻게 업데이트 시켜야하는지에 대해서 쓰는게 아니라
UI가 어떻게 생겨야 하는지에 대해서만 쓰면 리액트가 알아서 해준답니다.
createElement
가상돔을 돔으로 변환하는 함수
createVNode
에서 nested array들이 있는데, flatten 하려면
Array.prototype.flat() 으로 하면된다
1
const flatChildren = children.flat(Infinity)
Infinity로 deeply nested array를 flatten 할 수있다.
특정 타입 없애기
1
2
3
4
5
6
7
8
9
10
11
function noBoolean(children) {
return children
.flat(Infinity)
.filter((child) => child != null && typeof child !== "boolean");
}
export function createVNode(type, props, ...children) {
const flatArray = noBoolean(children);
return {type, props, children: flatArray};
}
false (boolean)을 이렇게 recursive 하게 없애려면 이렇게 해야한다
normalizeVNode
1
2
3
4
5
6
7
8
9
10
11
if (typeof vNode.type === "function") {
const node = vNode.type({ ...vNode.props, children: vNode.children });
return normalizeVNode(node);
} else {
return {
...vNode,
children: vNode.children
.flatMap(normalizeVNode)
.filter((item) => Boolean(item)),
};
}
여기서 잘못했던건 첫줄에 vNode === ‘function’으로 했던것.
vNode는 객체이고 그냥은 function이였을때 되는것. 당연하게..
type은 실제로 컴포넌트를 정의할때 쓰는 함수
1
type: MyComponent // 실제함수
createElement
진짜 DOM을 만들어주는 역할
normalizeVNode처럼 return “” 인줄 알았으나, 아니였고 대신 createTextNode 함수를 써야했습니다..
to return a new Text node whose data is data and node document is this.
마찬가지로 String(vNode)가 아닌 createTextNode(String(vNode))를 하면된다
updateAttributes($el, props) HTML요소에 attribute 넣는 역할
eventManager
익명함수 남발하지말기 - 성진님
버튼이나 div 요소에 click event를 register하고, 나중에 한번에 같이 처리하려고
왜?
버튼 하나하나에 addEventListener()를 붙히면 inefficient 하니 root에 이벤트를 등록하고 누가 눌럿는지 확이해서, 거기에 해당하는 handler만 실행하려고 = Event Delegation
1
2
3
4
export function addEvent(element, eventType, handler) {
if (!eventMap[eventType]) eventMap[eventType] = [];
eventMap[eventType].push({element, handler});
}
어떤 element에 어떤 event(Type)를 어떤 함수로 연결할지 저장하는 코드
1
2
3
4
5
6
7
8
export function removeEvent(element, eventType, handler) {
if (!eventMap[eventType]) return;
eventMap[eventType] = eventType[eventType].filter(
(e) => e.element !== element || e.handler !== handler,
);
if (eventMap[eventType].length === 0) delete eventMap[eventType];
}
element, eventType, handler 조합을 삭제, 해당 eventType에 연결된 이벤트가 없으면 eventMap안에서도 삭제.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export function setupEventListeners(root) {
Object.entries(eventMap).forEach(([eventType, handlers]) => {
if (rootListener[eventType]) return;
root.addEventListener(eventType, function (e) {
handlers.forEach(({element, handler}) => {
if (e.target === element) {
handler(e);
}
});
});
rootListener[eventType] = true;
});
}
eventMap에 등록된 모든 eventType에 대해서, root에 한번만 등록, 등록후는 true로 중복 방지
근데 이제 event handler가 제거되면 더 이상 호출되지 않아야하는데
처음 부터 click했을때 존재하지 않았음.
그래서 Map으로 root자체를 key로 썻서
click기준으로만 listener register 상태 추적 했던걸, container기준으로 Map사용했고 container마다 별도 listener register가능해지게.
- container 기준 등록된 타입이 있는지 확인,
- 해당 container에 click type listener 가 없으면
- listener 붙이고
- click event발생하면 정확히 그 버튼이 클릭됐을때만 handler 실행.
Map을 사용해서 container 기준으로 이벤트 등록을 추적하면, 테스트마다 다른 container가 생겨도 정확하게 이벤트 리스너가 등록되고, 핸들러도 정확하게 호출되며, removeEvent도 정상 작동합니다.
renderElement
기존 createElement의 updateAttributes에서는 이벤트 핸들러가 직접 DOM으로 붙었는데
그걸 바로 하지않고 저장 해두고 (eventMap에) 나중에 setupEventListener(root) 호출할때 한번만 root.addEventListener 로 처리 하게
1
2
3
4
5
6
7
if (key.startsWith("on") && typeof value === "function") {
const eventType = key.slice(2).toLowerCase();
addEvent($el, eventType, value);
continue;
}
onMouseover 같은 JSX에서 오는 이벤트 핸들러를 무시하기 위해.