1129 - 1205
1129
리액트 수업 3일 차
Jest 실습
- jest 명령어를 수행하면 확장자명이
test.js
이거나__test__
디렉토리 내에 있는 모든 js 파일을 테스트한다. npm t 파일이름
으로 특정 파일만 테스트를 수행할 수 있다. 여기서 t는 test의 축약어로 npm에서 제공해주는 기능이다.
export const addClass = (node, ...classNames) => {
if (node && "classList" in node) {
node.classList.add(...classNames);
}
return node;
};
- 메서드 체이닝을 사용할 수 있도록 코딩하기 위해 node 객체를 반환한다.
- classList 프로퍼티를 node가 보유한 경우 classList에 classNames를 추가한다. 참고로 add 메서드에 목록으로 class 추가가 가능하다.
DOM 접근
test(`문서 특정 노드에 'anything' 클래스 이름 설정이 가능하다.`, () => {
document.body.innerHTML = /* html */ `
<div id="app"></div>
`;
const appNode = document.getElementById("app");
let addedClassName = "anything";
addClass(appNode, addedClassName);
expect(appNode.classList.contains(addedClassName)).toBeTruthy();
});
- Jest가 돌아가는 Node.js 환경은 getElementById라는 메서드를 제공하지 않는다는 문제가 있다.
- 우선 jest.config 파일에
testEnvironment: 'jsdom'
옵션을 넣어주고,document.body.innerHTML
에 값을 할당하여 테스트를 진행하면 테스트가 가능하다. - 이처럼 jest는 DOM 테스트에 취약하다는 단점이 있는데, jest-dom 라이브러리를 추가하여 DOM을 테스트하기 편하게 도와주는 함수를 제공받을 수 있다.
jest-dom 설치
dom에 접근하여 테스트하기 편리하도록 지원해주는 메서드를 사용하기 위해서는 jest-dom 라이브러리를 추가 설치해야한다.
npm i -D @testing-library/jest-dom
jest.setup.cjs 파일 세팅
require("@testing-library/jest-dom");
- import 방식으로 외부 모듈을 받아오는 방법도 있지만, 공식 문서에 확장자 명을 작성하라는 말이 있었기에 mjs 확장자로 하면 오류가 발생하여 cjs 확장자로 수정하고 require 방식으로 변경했다.
jest.config.mjs
export default {
// 각 테스트 전에 테스트 프레임워크를 구성하거나 설정하기 위해 일부 코드를 실행하는 모듈의 경로 목록
setupFilesAfterEnv: ["./jest.setup.cjs"],
};
eslint와 jest-dom 사용 플러그인
npm i -D eslint-plugin-jest-dom
.eslintrc.cjs
module.exports = {
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:jest/recommended",
"plugin:jest-dom/recommended",
],
plugins: ["react", "jest", "jest-dom"],
};
React
React 라이브러리 로드
<script src="//unpkg.com/react/umd/react.development.js" crossorigin></script>
<script src="//unpkg.com/react-dom/umd/react-dom.development.js" crossorigin><script>
웹 앱 개발을 위해서는 React 뿐만 아니라 react-dom 라이브러리도 함께 사용해야 한다.
React element
- React 앱의 가장 작은 단위로, 브라우저 엘리먼트와는 달리 React 엘리먼트는 일반 객체이며 쉽게 생성할 수 있다.
- React DOM은 React 엘리먼트와 일치하도록 DOM을 업데이트한다.
- 가상 돔 노드트리를 생성한다.
- React element 객체의 중요한 속성으로는 type과 props가 있다.
React.createElement()
React.createElement(type, [props], [...children]);
- 인자로 주어지는 타입에 따라 새로운 React 엘리먼트를 생성하여 반환한다.
- type 인자로는 태그 이름 문자열(‘div’등), React 컴포넌트 타입, React Fragment 타입 중 하나가 올 수 있다.
- JSX로 작성된 코드는
React.createElement()
를 사용하는 형태로 변환된다.
props
- props는 컴포넌트의 입력값이다. 부모 컴포넌트로부터 자식 컴포넌트로 전달되는 데이터이다.
- 읽기 전용이므로 어떤 방식으로든 수정해서는 안 된다.
props.children
- 모든 컴포넌트에서 props.children를 사용할 수 있다.
- props.children은 컴포넌트의 여는 태그와 닫는 태그 사이의 내용을 포함한다.
ReactDOM.render()
ReactDOM.render(element, container[, callback])
- React 엘리먼트를 container DOM에 렌더링하고 현재 ReactComponent 루트 인스턴스에 대한 참조를 반환한다. (무상태 컴포넌트는 null을 반환)
- 그러나 이 반환 값을 사용하는 것은 레거시이다. React 신규 버전이 컴포넌트를 비동기로 렌더링하는 경우가 있기 때문에 피해야 한다. ReactComponent 인스턴스의 참조가 필요하다면 권장하는 해결책은 루트 엘리먼트에 콜백 ref를 붙이는 것이다.
- React 엘리먼트가 이전에 container 내부에 렌더링 되었다면 해당 엘리먼트는 업데이트하고 최신의 React 엘리먼트를 반영하는 데 필요한 DOM만 변경한다.
- 처음 호출할 때 기존의 DOM 엘리먼트를 교체하며 이후의 호출은 React의 DOM diffing 알고리즘을 사용하여 더욱 효율적으로 업데이트 한다.
- 컨테이너 노드를 수정하지 않고 컨테이너의 하위 노드만 수정한다. 그렇기 때문에 자식 노드를 덮어쓸 필요 없이 기존의 DOM 노드에 컴포넌트를 추가할 수 있다.
- 추가적인 콜백함수가 제공된다면 컴포넌트가 렌더링되거나 업데이트된 후 실행된다.
사용 예시
const contentsElement = React.createElement(
"div",
{
className: "contents",
lang: "en",
},
headlineElement,
descriptionElement
);
ReactDOM.render(contentsElement, document.getElementById("root"));
- contestsElement에 React element가 할당되고, 이를 root DOM에 렌더링한다.
- root 내부에
<div class="contents" lang= "en"></div>
가 생성되고, 내부에 headlineElement와 descriptionElement React element가 렌더링된다. - 여기서 주의해야 할 점은 props 중 className은 class 어트리뷰트로 렌더링된다는 것이다.
ReactDOM.createPortal(child, container)
- portal을 생성한다.
- portal은 DOM 컴포넌트 구조의 외부에 존재하는 DOM 노드에 자식을 렌더링하는 방법을 제공한다.
React.Fragment
- React 요소는 하나의 루트 요소만 가질 수 있어서 루트에 두 개 이상의 요소를 렌더링해야 하는 경우 문제가 발생한다.
- 쉽게 해결할 수 있는 방법은 div 요소를 만들어 묶는 것인데, 이 방법보다 더 좋은 방법은 React가 제공하는 Fragment 컴포넌트를 활용하는 것이다.
- 래퍼 없이 여러 엘리먼트를 렌더링할 수 있는 컴포넌트
- render() 메서드 안에서 추가적인 DOM 엘리먼트를 생성하지 않아도 여러 엘리먼트를 반환할 수 있다.
- 축약형인
<></>
문법으로도 동일하게 사용할 수 있다.
사용 예시
ReactDOM.render(
React.createElement(
React.Fragment,
null,
contentsElement,
translationButtonElement
),
document.getElementById("app")
);
- contentsElement와 translationButtonElement를 app id를 가진 돔 요소 내부에 렌더링 하기 위해 React.Fragment를 사용하였다.
renderToString()
ReactDOMServer.renderToString(element)
- ssr 측에서 사용
- React 엘리먼트의 초기 HTML을 렌더링한다. React는 HTML 문자열을 반환한다.
- 빠른 페이지 로드를 위해 초기 요청 시에 서버에서 HTML을 생성하여 마크업을 보내거나 SEO를 위해 사용할 수 있다.
JSX
- XML 문법을 사용하는 ECMAScript를 확장한 문법 (비표준 기술)
- JSX는 React 엘리먼트를 생성한다.
- 이 코드를 리액트 문법으로 바꿔줘야 하기 때문에 컴파일러가 필요하다. 주로 Babel 사용
- html 주석을 사용할 수 없고
{/* */}
JSX 주석을 사용해야함 - html 에서 script 태그를 이용해 바로 JSX를 사용하기 위해서는 script type을 text/babel이나 text/jsx로 지정해줘야 한다.
JSX에 표현식 사용하기
- JSX의 중괄호 안에는 유효한 모든 JavaScript 표현식을 넣을 수 있다. (인터폴레이션, 보간법)
function formatName(user) {
return user.firstName + " " + user.lastName;
}
const user = {
firstName: "Harper",
lastName: "Perez",
};
const element = <h1>Hello, {formatName(user)}!</h1>;
- 가독성을 좋게 하기 위해 JSX를 여러 줄로 나눌 때 필수는 아니지만, 자동 세미콜론 삽입을 피하기 위해 괄호로 묶는 것을 권장한다.
- JSX도 표현식이다. 컴파일이 끝나면, JSX 표현식이 정규 JavaScript 함수 호출이 되고 JavaScript 객체로 인식된다.
- 즉, JSX를 if 구문 및 for loop 안에 사용하고, 변수에 할당하고, 인자로서 받아들이고, 함수로부터 반환할 수 있다.
- JSX는 HTML보다 JavaScript에 가깝기 때문에, HTML 어트리뷰트 이름 대신 카멜케이스 프로퍼티 명명 규칙을 사용한다.
- 예를 들어, class는 className이 되고, tabindex는 tabIndex가 된다.
JSX의 XSS (cross-site-scripting) 공격 방지
- React DOM은 JSX에 삽입된 모든 값을 렌더링하기 전에 이스케이프 하므로, 애플리케이션에서 명시적으로 작성되지 않은 내용은 주입되지 않는다.
- 모든 항목은 렌더링 되기 전에 문자열로 변환된다.
JSX는 객체를 표현
- Babel은 JSX를
React.createElement()
호출로 컴파일한다. - 다음 두 예시는 동일하다.
const element = <h1 className="greeting">Hello, world!</h1>;
const element = React.createElement(
"h1",
{ className: "greeting" },
"Hello, world!"
);
엘리먼트 렌더링
렌더링 된 엘리먼트 업데이트하기
- React 엘리먼트는 불변객체이다. 엘리먼트를 생성한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없다.
- 엘리먼트는 하나의 프레임과 같이 특정 시점의 UI를 보여준다.
- 이 내용을 바탕으로 하면 UI를 업데이트하는 유일한 방법은 새로운 엘리먼트를 생성하고 ReactDOM.render()로 전달하는 것이다.
- 주의할 점은 실제 대부분의 React앱은 ReactDOM.render()를 한 번만 호출한다.
변경된 부분만 업데이트하기
- React DOM은 해당 엘리먼트와 자식 엘리먼트를 이전의 엘리먼트와 비교하고 DOM을 원하는 상태로 만드는데 필요한 경우에만 DOM을 업데이트한다.
- 매초 전체 UI를 다시 그리도록 엘리먼트를 만들더라도 React DOM은 내용이 변경된 노드만 업데이트한다.
Components와 Props
- 개념적으로 컴포넌트는 JavaScript 함수와 유사하다.
- props라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환한다.
함수 컴포넌트와 클래스 컴포넌트
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
- 이 함수는 데이터를 가진 하나의 props 객체 인자를 받은 후 React 엘리먼트를 반환하므로 유효한 React 컴포넌트이다.
- JavaScript 함수이기 때문에 함수 컴포넌트라고 호칭한다.
<Welcome />
과React.createElement(Welcome)
은 같다.
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
- class를 사용하여 컴포넌트를 정의할 수 있다.
- React 관점에서 볼 떄 위 두 가지 유형의 컴포넌트는 동일하다.
컴포넌트 렌더링
React 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있다.
const element = <Welcome name="Sara" />;
- React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달하는데 이 객체를 ‘props’라고 한다.
이를 render 함수로 호출하게 되면
- 사용자 정의 컴포넌트를 엘리먼트로 하여
ReactDOM.render()
를 호출한다. - React는 {name: ‘Sara’}를 props로 하여 Welcome 컴포넌트를 호출한다.
- Welcome 컴포넌트는 결과적으로 지정한 엘리먼트를 반환한다.
- React DOM은 반환된 엘리먼트와 일치하도록 DOM을 효율적으로 업데이트한다.
컴포넌트 이름은 항상 대문자로 시작해야 한다
React는 소문자로 시작하는 컴포넌트를 DOM 태그로 처리한다.
Props
- 함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안 된다.
- 모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 한다.
- props로 숫자를 전달하고 싶은 경우는 보간법을 이용해야 한다.
- props의 이름은 사용될 context가 아닌 컴포넌트 자체의 관점에서 짓는 것을 권장한다.
reference
React – 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리
1130
4 Sum
var fourSum = function (nums, target) {
const answer = [];
const len = nums.length;
nums.sort((a, b) => a - b);
for (let i = 0; i < len - 3; i++) {
for (let j = i + 1; j < len - 2; j++) {
let lt = j + 1;
let rt = len - 1;
while (lt < rt) {
let sum = nums[i] + nums[j] + nums[lt] + nums[rt];
if (sum === target) {
answer.push([nums[i], nums[j], nums[lt], nums[rt]]);
while (nums[lt] === nums[lt + 1]) lt++;
while (nums[rt] === nums[rt - 1]) rt--;
lt++;
rt--;
} else if (sum < target) {
lt++;
} else {
rt--;
}
}
while (nums[j] === nums[j + 1]) j++;
}
while (nums[i] === nums[i + 1]) i++;
}
return answer;
};
- 예전에 풀었던 3Sum 문제와 상당히 유사한 문제였다.
- 두 개의 for 문을 돌면서 내부의 lt, rt를 조절하여 문제를 해결하였다.
- 문제를 풀다보니 이전에 풀었던 문제가 생각보다 많이 헷갈린다는 생각이 들어서 복습 위주로 문제를 풀어봐야겠다고 느꼈다.
Count Binary Substrings
var countBinarySubstrings = function (s) {
s += " ";
let answer = 0;
let prev = 0;
let cur = 1;
for (let i = 1; i < s.length; i++) {
if (s[i] === s[i - 1]) {
cur++;
} else {
answer += Math.min(prev, cur);
prev = cur;
cur = 1;
}
}
return answer;
};
- 연속되는 부분 문자열 중, 0과 1의 숫자가 같은 개수를 구하는 문제였다. 단, 0과 1은 연속되어야 한다.
- 인덱스를 1부터 끝까지 순회하며 이전과 비교해서 같을 때 다를 때로 구분하였다.
- 같을 때는 cur 변수를 증가시켜 몇 번 연속 중인지를 나타냈고, 다른 경우 새로운 수가 시작하므로 이전 연속된 수와 비교해서 더 작은만큼 정답에 더해줬다.
- 그리고 이전 변수에 현재 변수의 값을 저장하고 cur을 1로 초기화했다.
- 여기서 마지막에 같은 경우를 대비해 문자열 마지막에 빈 문자열을 집어넣어 코딩했다.
1201
모의 코딩테스트 회고
코딩 테스트를 진행하며 모든 문제를 맞추긴 했지만, 문제가 애매하게 나온 문제도 있었고 강사님의 코드를 보고 이해하면 좋을 것 같다는 생각이 들어 글을 작성하게 되었다.
주사위 쌓기
function solution(dice) {
let opp = [5, 3, 4, 1, 2, 0];
let answer = 0;
for (let i = 0; i < 6; i++) {
let start = dice[0][i];
let sum = 0,
maxN = 0;
for (let j = 0; j < dice.length; j++) {
let side = dice[j].indexOf(start);
let op = opp[side];
let end = dice[j][op];
if (start !== 6 && end !== 6) maxN = 6;
else if (start != 5 && end !== 5) maxN = 5;
else maxN = 4;
sum += maxN;
start = end;
}
answer = Math.max(answer, sum);
}
return answer;
}
- 내 로직과 큰 다른점은 나는 맵을 이용해 인덱스의 반대편 인덱스를 구했지만, 강사님 코드는 배열의 기본 인덱스를 활용했다는 점이다.
- 이 외 로직은 전반적으로 비슷했다. 현재 시작면 숫자와, 반대편 숫자를 계속 갱신해나가며 문제를 해결했다.
안전 영역
function solution(board) {
let answer = 0;
const len = board.length;
const dx = [-1, 0, 1, 0];
const dy = [0, 1, 0, -1];
let ch;
function DFS(x, y, k) {
ch[x][y] = 1;
for (let i = 0; i < 4; i++) {
const nx = x + dx[i];
const ny = y + dy[i];
if (
nx >= 0 &&
nx < len &&
ny >= 0 &&
ny < len &&
ch[nx][ny] === 0 &&
board[nx][ny] > k
) {
ch[nx][ny] = 1;
DFS(nx, ny, k);
}
}
}
for (let k = 0; k < 100; k++) {
ch = Array.from(Array(len), () => Array(len).fill(0));
let cnt = 0;
for (let i = 0; i < len; i++) {
for (let j = 0; j < len; j++) {
if (ch[i][j] === 0 && board[i][j] > k) {
cnt++;
DFS(i, j, k);
}
}
}
answer = Math.max(answer, cnt);
if (cnt === 0) break;
}
return answer;
}
- 섬나라 아일랜드 문제와 비슷한 DFS 문제였다.
- 높이가 총 100까지 존재하므로 100번까지 순회하면서 안전지역을 만날 때마다 카운트를 증가시키고 주변 안전지역을 체크하였다.
- 모든 구간이 안전지역이 존재하지 않는 경우 반복문을 종료한다.
결혼식
function solution(times) {
let answer = 0;
let cnt = 0;
const T_line = [];
times.forEach((time) => {
T_line.push([time[0], "s"]);
T_line.push([time[1], "e"]);
});
T_line.sort((a, b) =>
a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0
);
T_line.forEach((v) => {
if (v[1] === "s") cnt++;
else cnt--;
answer = Math.max(answer, cnt);
});
return answer;
}
- T_line에는 들어온 시간과 나간 시간을 문자열로 표기하여 배열을 삽입한다.
- T_line을 시간별로 오름차순, 시간이 같다면 나간 사람 먼저 빼고 해당 시간대 별 최대 인원 수를 구한다.
- 여기서 주의할 점은 나간 사람을 먼저 빼고 인원을 구해야 한다. 그렇지 않으면 해당 시간에 나가는 인원은 제외하고 구해야 하는데 포함해서 구하게 된다.
- 이 방법으로 해결하면 O(n)으로 문제를 해결할 수 있다.
삼각형 돌리기
function solution(grid, clockwise) {
const answer = [];
const n = grid.length;
if (clockwise) {
for (let i = 0; i < n; i++) {
let line = "";
for (let j = 0; j <= i * 2; j++) {
line += grid[n - 1 - Math.floor(j / 2)][i * 2 - j];
}
answer.push(line);
}
} else {
for (let i = 0; i < n; i++) {
let line = "";
for (let j = 0; j <= i * 2; j++) {
line +=
grid[n - 1 - i + Math.floor((j + 1) / 2)][
n * 2 - 2 - i * 2 + (j % 2)
];
}
answer.push(line);
}
}
return answer;
}
- 우테코에 출제되었던 7번 문제
- 거의 내 코드의 절반의 길이를 이용해서 문제를 해결할 수 있었다.
- 핵심은 line에 더하는 값을 한 줄로 해결하는 것. 인덱스를 잘 활용해야 한다.
1202
리액트 수업 4일 차
Babel
babel polyfill
Array.prototype.find 메서드나 Promise는 IE에서 지원하지 않는다. 이를 하위 브라우저에서 사용하기 위해서는 babel-polyfill이 필요하다.
- babel-polyfill이 아닌 core-js를 사용하기를 babel 공식 사이트에서는 권장하고 있다.
- core-js와 regenerator-runtime를 함께 사용해야 한다.
babel standalone의 문제점
- 코드를 모듈화하면 JSX를 해석하지 못한다
- 스크립트 type을 module로 지정하면 babel standalone이 무엇을 컴파일해야 할 지 모른다.
- data-type으로 module을 지정해도 babel이 기본적으로 commonjs 방식으로 컴파일하기 때문에 require 문법을 브라우저가 이해하지 못해 오류가 발생한다.
- .babelrc 파일에서 presets 부분에 modules를 false로 지정하여 바벨이 모듈은 컴파일 하지 않도록 한다.
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
]
],
- 이렇게까지 해도 결국 경로 문제가 발생한다.
- babel standalone은 하나의 파일만 컴파일할 수 있어서 문제가 있다.
- babel로 컴파일링 되면 어떻게 변환되는지 보여주는 사이트에서 적합
babel/cli
- 결론은 babel/cli 를 사용하는 것이 더 낫다.
- babel/cli는 폴더를 폴더로 컴파일한다.
- 추가로 preset과 plugin-transform-react-jsx라는 플러그인을 함께 설치해줘야지만 바벨이 JSX를 해석할 수 있다.
- html에서 컴파일 된 폴더를 로드해서 사용해야 한다.
- 바벨은 컴파일러지 모듈 번들러는 아니기에 추후에 웹팩을 사용할 때는 다른 방식으로 사용해야한다.
- rimraf는 지우기 귀찮으니 커맨드 하나로 컴파일 된 폴더를 지울 수 있게 해준다. rm -rf 명령어는 맥에서만 되기에 크로스 브라우징을 위해서 설치
eslint 커맨드라인 vs 스토어 설치
- 어찌보면 기본적인 것인데 순간 헷갈려 질문했다.
- npm으로 설치한 것은 실행해야 동작, 스토어에서 설치하여 실행 시 기본 관찰. 이때 .eslintrc 파일을 자동으로 확인하여 실행한다.
사진 포맷 확장자 선택 기준
- 맥의 레티나 디스플레이를 위해 2배수 이미지도 고려해야 한다.
- 일반 사진은 JPG
- 심플한 모양의 사진은 svg
- 투명도가 필요한 사진은 png
defaultProps
- React 모든 컴포넌트(함수, 클래스)는 props의 기본 값을 .defaultProps로 설정한다.
Logo.defaultProps = {
textColor: "#242CF8",
dotColor: "#FF6B00",
label: "MORE.",
size: ratio.height,
};
Virtual DOM
실제 DOM에 직접 조작하는 것이 아니라, 변경 요청이 발생할 때 가상 DOM의 이전/이후 구조를 비교해 변경 된 부분만 실제 DOM에 업데이트한다. 실제 DOM을 조작하지 않아 UI 반응 속도를 높일 수 있다.
- 잦은 DOM 조작은 비용이 많이 들고 속도가 느려진다.
- React는 가상 DOM을 사용하여 성능을 향상시키는 방식을 채택했다.
- 가상 DOM 컴포넌트를 지속적으로 관찰하여 상태 변경을 감지하려 시도한다.
- 가상 DOM 이전, 이후 비교는 재조정 알고리즘을 사용하여 효율적으로 처리한다.
- 비교 결과 차이가 발생하면 실제 DOM에 반영해 UI를 업데이트 한다.
Virtual DOM 트리 추상화
가상 돔은 실제 돔 구조를 추상화한 표현
예를 들어 HTML 문서 구조를 가상 DOM으로 표현한다는 것은 JS 객체로 구현할 수 있다.
<ul class="what-is-virtual-dom">
<li>가상 DOM은 실제 DOM을 추상화 하여 표현한 것을 말합니다.</li>
<li>가상 DOM 트리에서 무언가 변경되면 새로운 가상 DOM 트리가 생성됩니다.</li>
</ul>
{
type: 'ul',
props: { className: 'what-is-virtual-dom' },
children: [
{
type: 'li',
props: null,
children: '가상 DOM은 실제 DOM을 추상화 하여 표현한 것을 말합니다.'
},
{
type: 'li',
props: null,
children: '가상 DOM 트리에서 무언가 변경되면 새로운 가상 DOM 트리가 생성됩니다.'
},
]
}
hyperscript 모듈
DOM 구조가 복잡해질수록 Virtual DOM 구조 또한 복잡해진다. 가상 DOM 구조를 손쉽게 생성할 수 있도록 h 함수를 작성한다.
function h(type, props = {}, ...children) {
return { type, props, children };
}
JSX 프라그마
- React는 가상 돔을 보다 이해하기 쉽고, 사용하기 편리하도록 JSX를 사용한다. JSX 문법은 Babel과 같은 컴파일러를 통해 가상 DOM을 생성하는 함수(React.createElement) 코드로 변경된다.
- @babel/plugin-transform-react-jsx 플러그인을 사용하면 JSX 문법을 특정 함수로 처리하여 컴파일 할 수 있다.
- 예를 들어 가상 돔을 생성하는 h() 함수를 JSX 문법을 해석하는 함수로 설정할 수 있다.
- 컴파일러에 지시하는 것을 뜻하는 @jsx 프라그마를 사용하여 JSX 문법을 해석해 특정 함수로 변경하도록 설정한다.
/** @jsx h */
// JSX 문법을 h 함수로 해석하라는 지시
createElement 모듈
- 가상 돔 트리 구조를 해석해 실제 돔을 생성하는 모듈을 작성
function createElement(vNode) {
// vNode 타입이 문자 값이라면
if (typeof vNode === "string") {
// 텍스트 노드를 생성해 반환
return document.createTextNode(vNode);
}
// vNode.type에 해당되는 요소 노드를 생성 (예: 'div')
const elementNode = document.createElement(vNode.type);
// props 객체의 모든 필드를 반복해 설정
setProps(elementNode, vNode.props);
// 이벤트 바인딩
bindEvents(elementNode, vNode.props);
// vNode.children 순환
vNode.children
// 포함하는 vChildNode를 createElement()를 사용해 노드 생성 (재귀 호출)
.map((vChildNode) => createElement(vChildNode))
// DOM 노드 집합을 순환
// 개별 자식 DOM 노드를 루트 노드의 마지막 자식 노드로 삽입
.forEach((childNode) => elementNode.appendChild(childNode));
// DOM 요소 노드 반환
return elementNode;
}
1203
투포인터 문제 풀이
Minimum Size Subarray Sum
var minSubArrayLen = function (target, nums) {
let answer = nums.length + 1;
let sum = 0;
let lt = 0;
for (let rt = 0; rt < nums.length; rt++) {
sum += nums[rt];
while (sum >= target) {
answer = Math.min(answer, rt - lt + 1);
sum -= nums[lt++];
}
}
return answer === nums.length + 1 ? 0 : answer;
};
- 연속되는 배열의 합이 target보다 크거나 같다는 조건을 충족할 때, 배열의 최소 길이를 구하는 문제
- 워낙 많이 풀어 본 유형의 문제라 수월하게 해결했다.
Arithmetic Slices
var numberOfArithmeticSlices = function (nums) {
let answer = 0;
let lt = 0;
let tmp;
for (let rt = 1; rt < nums.length; rt++) {
if (nums[rt] - nums[rt - 1] === tmp) {
if (rt - lt + 1 >= 3) {
answer += rt - lt - 1;
}
} else {
tmp = nums[rt] - nums[rt - 1];
lt = rt - 1;
}
}
return answer;
};
- 등차 수열을 개수를 구하는 문제
- 등차 수열은 변수에 저장해 둔 값이랑 앞 뒤 차가 다르면 깨지게 되므로, 깨지면 다시 시작하고 그렇지 않다면 해당 인덱스에서 구할 수 있는 등차수열 개수를 정답에 더해 반환했다.
1205
우테코 프리코스 2주차
1주차 과제와 전반적으로 비슷했지만 다른 점이 있다면 DOM API에 대해 리마인드 할 수 있는 시간이었다. appendChild나 createElement 같은 메서드를 사용하며 이전에 공부했던 내용을 다시 한 번 복습할 수 있었다.
기존에는 클래스 패턴보다는 생성자 함수 패턴을 사용해서 코드를 작성했는데, 과제를 진행하면서는 많이 사용해보지 않은 패턴을 적용해보자고 생각하여 클래스 패턴을 이용하려고 노력했다.
화면에 개행을 하는 부분에서 시간을 가장 많이 투자했다. join을 실행할 때 이스케이프 처리를 해주면 바로 적용이 될 줄 알았는데, 예상과는 다르게 동작하지 않았습니다. 때문에 <br />
을 삽입하여 문제를 해결했다. 총 시간은 3시간 정도 걸린 것 같다.