Yong

nx 통합 monorepo 구축 기록

통합 모노레포란

https://nx.dev/concepts/integrated-vs-package-based

  • root에 있는 하나의 package.json에서 모든 의존성을 관리하는 방식

특징

  • 이미 툴링, 의존성이 결정되었기 때문에 새로운 프로젝트를 추가하는 것이 쉬움.
  • 기존 프로젝트를 통합 모노레포로 옮기기는 어려움.
  • 하나의 의존성만 관리하기 때문에 관리비용이 적음.
  • 모든 의존성이 한곳에 있기 때문에 다른 의존성을 가진 프로젝트가 많아지면 오히려 관리가 복잡해질 수 있음.

목표

  • react Ui library 프로젝트 추가
  • nextjs 프로젝트 추가
  • eslint, prettier 설정
  • CD/CI

구축

nx workspace 생성

1
npx create-nx-workspace

원하는 옵션을 선택해 워크스페이스를 생성한다.

ui-component 프로젝트 생성

vscode의 nx console 플러그인으로 생성.

importPath도 지정할 수 있다

airbnb eslint 설정

  1. eslint-config-airbnb 설치
1
npx install-peerdeps --dev eslint-config-airbnb
  1. eslint-config-airbnb-typescript 설치
1
npm install eslint-config-airbnb-typescript --save-dev
  1. airbnb eslint 설정

root의 .eslintrc.json에 다음과 같이 추가

1
2
3
4
5
6
7
"extends": [
"airbnb-typescript", // 추가
"plugin:@nx/react-typescript",
"next",
"next/core-web-vitals",
"../../.eslintrc.json"
],

이제 린트를 돌려보면 다음과 같은 에러가 발생함.

https://nx.dev/recipes/tips-n-tricks/eslint

공홈에 nextjs는 다음과 같은 방법으로 해결하라고 되어있음.

nextjs의 .eslintrc.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
// We set parserOptions.project for the project to allow TypeScript to create the type-checker behind the scenes when we run linting
"parserOptions": {
"project": ["apps/org/tsconfig(.*)?.json"]
},
"rules": {
"@typescript-eslint/await-thenable": "error"
}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

react library의 .eslintrc.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
// We set parserOptions.project for the project to allow TypeScript to create the type-checker behind the scenes when we run linting
"parserOptions": {
"project": ["lib/ui-component/tsconfig.*?.json"]
},
"rules": {
"@typescript-eslint/await-thenable": "error"
}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

다시 린트를 돌려보면 다음과 같은 에러 발생한다

1
Parsing error: ESLint was configured to run on `<tsconfigRootDir>/apps/org/jest.config.ts` using `parserOptions.project`

아무리 봐도 jest.config.ts가 include 되어있는데 자꾸 include 하란다.
그래서 tsconfig.json에 있는 exclude 목록에서 jest.config.ts를 제거해보니 정상동작함.
아무래도 include, exclude 둘다 되어있어서 충돌이 난게 아닐까 싶다.

성공!

prettier 설정

eslint의 prettier 설정으로 대체함.

  1. eslint-plugin-prettier

eslint에서 prettier를 설정할 수 있게 해줌.
nx에서 prettier가 있어서 충돌나니 prettier 제거해준 후 설치

1
2
npm remove prettier
npm install --save-dev eslint-plugin-prettier
  1. eslint-config-prettier

plugin:prettier/recommended 설정과 함께 prettier와 중복되는 eslint rule을 자동으로 꺼줌.

root의 .eslintrc.json에서 설정

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
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nx", "prettier"],
"extends": ["prettier", "plugin:prettier/recommended"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{
"sourceTag": "*",
"onlyDependOnLibsWithTags": ["*"]
}
]
}
]
}
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nx/typescript"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nx/javascript"],
"rules": {}
}
],
"rules": {
"prettier/prettier": "error"
}
}
Continue
React 재조정(Reconciliation)
  • state나 props가 갱신되면 새로운 react 엘리먼트 트리 반환
  • n개의 엘리먼트에 대해 O(n) 복잡도 - 휴리스틱 알고리즘 사용
    • 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다
    • 개발자가 key prop를 통해, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할지 표시

비교 알고리즘

2개 트리 비교시, 루트부터 비교
이후의 동작은 루트 엘리먼트의 타입에 따라 다름

dom 엘리먼트의 타입이 다른 경우

  • <a><img> 등 태그가 바뀌면 기존 트리를 버리고 완전히 새로 트리를 구축
  • 트리를 버릴 때 이전 dom 노드 모두 파괴
  • 새트리가 만들어질때 새 dom 노드들이 dom에 삽입
  • 이전 트리와 연관된 state 모두 사라짐

dom 엘리먼트의 타입이 같은 경우

  • 두 엘리먼트의 속성 확인해 동일한 내역을 유지하고 변경된 속성들만 갱신
  • dom 노드 처리가 끝나면 react는 이어서 노드의 자식들을 재귀적으로 처리
Continue
클로저(closure)

클로저(Closure)

클로저는 함수와 함수가 선언된 렉시컬 스코프의 조합이다.

렉시컬 스코프

함수가 어디에서 호출했는지가 아니라 어디에 선언되었는지에 따라 스코프가 결정되는 것을 말한다.

어디에서 호출되었는지에 따라 스코프가 결정되는 것을 동적 스코프라고 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
var x = 1;

function A() {
var x = 10;
B();
}

function B() {
console.log(x);
}

A(); // ?
B(); // ?

위의 예제에서 javascript는 선언할 때 스코프가 결정되므로 B 함수에서 사용하는 x는 선언할 때 스코프를 따라 가장 상위의 var x = 1;를 사용한다. A 함수를 호출했을 때, x가 새로 선언되어도 B 함수 스코프에 영향을 주지 않는다.

클로저의 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}

var myFunc = makeFunc();
//myFunc변수에 displayName을 리턴함
//유효범위의 어휘적 환경을 유지
myFunc();
//리턴된 displayName 함수를 실행(name 변수에 접근)

위 예제에서 makeFunc() 함수가 종료되었음에도 name변수가 사라지지 않고 makeFunc의 리턴 함수인 displayName이 호출되었을 때 여전히 참조 가능 한 것을 알 수 있다.

javascript는 참조를 사용하는 곳이 모두 없어지지 않으면 참조를 제거하지 않는다.

그래서 makeFunc()함수가 종료되어도 렉시컬 스코프에 의해 클로저가 형성되어 name의 참조 사용이 유지된다.

참조:

https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures

Continue
javascript - 자바스크립트 데이터 타입

자바스크립트의 타입

  • 기본 타입
    • number, string, boolean, null, undefined
  • 참조 타입
    • Object, Array

기본 타입

숫자, 문자열, 불린, null, undefined가 있다. 기본 타입의 특징은 그 자체가 하나의 값을 나타낸다.

숫자

C언어는 int, long, float, double 같은 여러 숫자 타입이 존재하지만, 자바스크립트는 number라는 하나의 숫자형만 존재한다. 자바스크립트에서는 모든 숫자를 64비트 부동 소수점 형태로 저장하기 때문이다. C언어의 double과 유사하다.

특징

  • 정수형이 따로 없다
  • 모든 숫자를 64비트 부동 소수점 형태
  • 모든 숫자를 실수로 처리하므로 나눗셈 연산에 주의해야 한다.

문자열

문자열은 작은 따옴표(‘)나 큰 따옴표(“)로 생성할 수 있다.

특징

  • C언어에서의 char 타입처럼 문자 하나만을 별도로 나타내는 데이터 타입은 없다.
  • 한 번 정의된 문자열은 변하지 않는다.
1
2
3
4
5
6
7
var str = 'test';
console.log(str[0], str[1], str[2], str[3]);
// (출력값) test

str[0] = 'T';
console.log(str);
// (출력값) test

불린값(boolean)

자바스크립트에는 true와 false를 나타내는 boolean 타입이 있다.

null과 undefined

null과 undefined는 ‘값이 비어있음’을 나타낸다. 자바스크립트 환경 내에서 기본적으로 할당되지 않는 변수는 undefined 타입이고 값도 undefined이다. 즉 undefined는 타입이자, 값을 나타낸다.

undefined가 아직 값을 할당하지 않아서 비어있는 값이라면, null은 개발자가 명시적으로 값이 없다는 것을 나타낼 때 사용한다. 그래서 undefined는 타입도 undefined지만 null 값을 넣은 변수의 타입은 object이다.

1
2
3
4
var nullVar = null;

console.log(typeof nullVar === null); // (출력값) false
console.log(nullVar === null); // (출력값) true

특징

  • undefined, null는 값이 비어있다는 의미
  • undefined는 아직 값을 할당하지 않은 것, null은 일부러 값이 비어있음을 명시할 때
  • undefined는 타입이자 값
  • null의 타입은 object

참조 타입 (객체)

자바스크립트에서 기본 타입을 제외한 모든 값은 객체이다. 배열, 함수, 정규표현식 등도 모두 객체다. 즉 참조 타입은 객체 타입이다.

자바스크립트 객체는 이름(key): 값(value) 형태의 프로퍼티들을 저장하는 컨테이너이다. 이것은 컴퓨터 과학 분야에서 해시(Hash)라는 자료구조와 유사하다.

객체 생성

객체를 생성하는 방법은 3가지가 있다.

  • Object() 생성자 함수
1
2
3
4
5
6
7
8
var foo = new Object();

foo.name = 'foo';
foo.age = 30;
foo.gender = 'male';

console.log(typeof foo); // (출력값) object
console.log(foo); // (출력값) { name: 'foo', age: 30, gender: 'male' }
  • 객체 리터럴 방식 이용
1
2
3
4
5
6
7
8
var foo = {
name: 'foo',
age: 30,
gender : 'male'
};

console.log(typeof foo);
console.log(foo);
  • 생성자 함수
1
2
3
4
5
6
7
8
var Foo = function(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
};

var f = new Foo('foo', 30, 'male');
console.log(f.name); // (출력값) foo

객체 프로퍼티 읽기/쓰기/갱신

객체의 프로퍼티 접근법은 2가지가 있다.

  • 대괄호([]) 표기법
  • 마침표(.) 표기법

프로퍼티 읽기

1
2
3
4
5
6
7
var foo = {
name : 'foo',
major : 'compute science'
};

console.log(foo.name); // (출력값) foo
console.log(foo['name']); // (출력값) foo

프로퍼티 갱신

1
2
3
4
5
6
7
var foo = {
name : 'foo',
major : 'compute science'
};

foo.major = 'electronics engineering';
console.log(foo['major']); // (출력값) electronics engineering

프로퍼티 동적 생성

객체 생성 후에도 동적으로 프로퍼티를 추가할 수 있다.

1
2
3
4
5
6
7
var foo = {
name : 'foo',
major : 'compute science'
};

foo.age = 30;
console.log(foo.age); // (출력값) 30

접근하는 값이 있으면 갱신되고 없으면 동적으로 생성해 추가된다.

대괄호 표기법만 사용해야 하는 경우

접근하려는 프로퍼티가 표현식이거나 예약어일 경우 대괄호 표기법만 사용한다.

1
2
3
4
5
6
7
8
var foo = {
name : 'foo',
major : 'compute science',
};

foo['full-name'] = 'foo bar';
console.log(foo.full-name); // (출력값) NaN
console.log(foo.['full-name']); // (출력값) foo bar

이 예제에서 프로퍼티 이름에 -연산자가 포함되어 있어 마침표 표기법으로 접근할 수 없다.

NaN (Not a Number)

자바스크립트에서 수치 연산을 해서 정상적인 값을 얻지 못할 때 출력되는 값이다.

객체 참조값

객체를 왜 참조 타입이라고 부를까?

객체의 모든 연산이 실제 값이 아닌 참조값으로 처리되기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
var objA = {
val : 40
};
var objB = objA;

console.log(objA.val); // (출력값) 40
console.log(objB.val); // (출력값) 40

objB.val = 50;
console.log(objA.val); // (출력값) 50
console.log(objB.val); // (출력값) 50

변수 objB에 objA를 할당하면 값을 할당하는 것이 아닌 참조값이 저장되기 때문에 값을 바꾸면 참조하고 있는 모든 객체의 값이 바뀐다.

객체 비교

동등 연산자(==)를 사용하여 객체를 비교할 때도 프로퍼티 값이 아닌 참조값을 비교한다.

1
2
3
4
5
6
7
8
9
10
var a = 100;
var b = 100;

var objA = { value: 100 };
var objB = { value: 100 };
var objC = objB;

console.log(a == b); // (출력값) true
console.log(objA == objB); // (출력값) false
console.log(objA == objC); // (출력값) true

호출 방식의 차이

기본 타입은 값에 의한 호출 방식이나, 참조 타입은 참조에 의한 호출 방식이다.

함수를 호출할 때 인자로 기본 타입의 값을 넘길 경우, 호출된 함수의 매개변수로 복사된 값이 전달된다. 그래서 함수 내부에서 매개변수의 값을 변경해도 호출된 변수의 값이 변경되지는 않는다.

이에 반해 함수를 호출할 때 인자로 참조 타입인 객체를 넘길 경우, 그 객체 참조값이 넘어가게 되어 함수 내부에서 매개변수의 값을 변경할 수 있게된다.

프로토타입

자바스크립트의 모든 객체는 자신의 부모 역할을 하는 객체와 연결되어 있다. 이러한 부모 객체를 프로토타입 객체라고 부른다.

ECMAScript 명세서에는 자바스크립트의 모든 객체는 자신의 프로토타입을 가리키는 [[Prototype]]이라는 숨겨진 프로퍼티를 가지고 있다고 한다.

객체 리터럴 방식으로 생성된 객체의 경우 Objsect.prototype 객체가 프로토타입 객체가 된다.

배열

다른 언어들과 다르게 자바스크립트 배열은 크기를 지정하지 않아도 되며, 어떤 위치에 어느 타입의 데이터를 저장해도 된다.

배열 리터럴

배열을 생성하는데 사용하는 표기법이다.

1
var colorArr = ['orange', 'yellow', 'blue'];

배열 요소 생성

아무 인덱스 위치에 값을 동적으로 넣을 수 있다. 대신 앞의 인덱스가 정의되지 않았다면 undefined가 기본값으로 들어가며 생긴다. 하지만 그렇다고 실제 메모리도 생긴 배열처럼 할당되진 않는다.

1
2
3
4
5
6
var emptyArr = [];

emptyArr[0] = 100;
emptyArr[3] = 'eight';
console.log(emptyArr);
// (출력값) [100, undefined, undefined, "eight"]

배열의 length 프로퍼티

배열은 모두 length 프로퍼티가 있다. length 프로퍼티는 배열 내에 가장 큰 인덱스에 1을 더한 값이다.

length 프로퍼티는 명시적으로 값을 변경할 수도 있다.

1
2
3
4
5
6
7
8
9
var arr = [0, 1, 2];
console.log(arr.length); // (출력값) 3

arr.length = 5;
console.log(arr); // (출력값) [0, 1, 2, undefined, undefined]

arr.length = 2;
console.log(arr); // (출력값) [0, 1]
console.log(arr[2]); // (출력값) undefined

이렇게 length를 수정하면 length가 가리키는 위치가 변경된다.

length를 할당한 원소보다 안으로 설정하면 length를 벗어나는 값은 삭제되기 때문에 접근하면 undefined가 나온다.

배열의 프로토타입

객체와 배열의 큰 차이점은 배열만의 표준 메소드 존재 여부이다. push(), pop() 같은 메소드는 객체에서는 사용할 수 없다. 하지만 나머지는 Array.prototype도 결국 Object.prototype이 부모이기 때문에 큰 차이가 없다.

배열 요소 삭제

배열도 객체이므로 delete연산자로 삭제할 수 있다.

1
2
3
4
var arr = ['zero', 'one', 'two', 'three'];
delete arr[2];
console.log(arr); // (출력값) ['zero', 'one', undefined, 'three']
console.log(arr.length); // (출력값) 4

delete는 해당 요소의 값을 undefined로 설정할 뿐 원소 자체를 삭제하지는 않는다.

Array() 생성자 함수

Array() 생성자 함수는 인자 개수에 따라 동작이 다르다.

  • 인자가 1개이고 숫자일 때 : 호출된 인자를 length로 갖는 빈 배열 생성
  • 그외의 경우 : 호출된 인자를 요소로 갖는 배열 생성
1
2
var foo = new Array(3);       // [undefined, undefined, undefined]
var bar = new Array(1, 2, 3); // [1, 2, 3]

기본 타입의 표준 메소드

자바스크립트는 숫자, 문자열, 불린값에 대해 각 타입별로 호출 가능한 표준 메소드를 정의하고 있다.

기본 타입의 경우 객체가 아닌데 어떻게 메소드를 호출할 수 있을까?

기본 타입의 값들이 메소드를 호출한 경우, 이들 기본값은 메소드 처리 순간에 객체로 변환된 다음 각 타입별 표준 메소드(charAt())를 호출한다. 그 메소드가 끝나면 다시 기본값으로 복귀하게 된다.

1
console.log("test".charAt(2)); // (출력값) 's'
Continue
Javascript - 스코프(scope)

스코프란?

  • 변수나 함수같은 참조 대상 식별자를 찾아내기 위한 규칙
  • 이 규칙대로 자바스크립트는 식별자를 찾음

종류

  • 전역 스코프: 코드 어디에서든지 참조 가능
  • 지역 스코프: 함수 코드 블록이 만든 스코프로, 함수 자신과 하위 함수에서만 참조 가능

자바스크립트 스코프의 특징

  • 함수 레벨 스코프: 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효
    • 보통은 블록 레벨 스코프: 코드 블록 내에서만 유효한 스코프
    • ECMAScript 6에 도입된 let은 블록 레벨 스코프

함수 스코프 종류

  • 렉시컬 스코프
    • 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 상위 스코프가 결정됨.
  • 동적 스코프
    • 함수를 어디에 호출했는지에 따라 상위 스코프가 결정됨.

암묵적 전역

  • 선언을 안해도 스코프 체인을 통해 window로 접근해 프로퍼티를 동적 생성함.

스코프체인

  • 일단 자신이 속한 스코프에서 찾고 그 스코프에 식별자가 없으면 상위 스코프에서 다시 찾아감.
Continue
angular - injector 원리

2개의 injector 계층

  • ModuleInjector 계층
    • @NgModule() 이나 @Injectable()를 사용하여 ModuleInjector를 구성함.
  • ElementInjector 계층

ModuleInjector

2가지 구성 방법이 있음.

  • @Injectable() 에서 providedIn을 사용하고 @NgModule()에 참조
  • @NgModule()의 providers 배열에 추가
    • ModuleInjector는 NgModule.providers 및 NgModule.imports 속성으로 구성됨.
    • NgModule.imports를 재귀적으로 따라가 도달할 수 있는 모든 providers 배열을 flatten 시킴.
    • lazy loading하는 NgModules가 있을 때 Child ModuleInjector들이 생김.

Platform injector

  • root ModuleInjector 위에 PlatformModule로 구성된 ModuleInjector와 NullInjector() 가 더 있음.
  • platformBrowserDynamic() 은 PlatformModule에 의해 구성된 injector 생성함.
  • 다음 부모 injector는 NullInjector이고 tree에 top임.
  • service을 찾다가 NullInjector를 만나면 null을 return 함.

ElementInjector

  • 각 component(and directive)는 dom element를 위한 암묵적으로 ElementInjector를 만든다.
  • ElementInjector에는 provider, viewProvider, 자기 자신(component or directive) 등이 포함되어 있다.
  • component에서 services를 provide했을때, ElementInjector를 통해 해당 컴포넌트에서 그 service가 사용 가능하다.
  • 해당 component가 destroy 되면 service도 destroy된다.

의존성을 찾는 규칙

컴포넌트나 디렉티브에서 provide된 토큰을 찾을 때, angular는 2가지 기준으로 찾기 시작한다.

  • ElementInjector 계층 구조에서 먼저 찾고
  • ModuleInjector 계층 구조에서 찾는다

순서

  1. component가 의존성 요청한다.
  2. angular는 그 component의 ElementInjector에서 의존성을 찾는다.
  3. 만약 그 component의 injector에 provider가 없다면 부모 component의 ElementInjector에 요청한다.
  4. 상위 ElementInjector가 부족할 때까지 계속 올라간다.
  5. 못찾으면 시작된 component로 돌아가서 ModuleInjector 계층 구조를 탐색한다.
  6. 또 못찾으면 에러를 발생시킨다.

만약 중복으로 provider에 등록했다면, angular는 처음에 찾은 걸 사용하고 다른 provider에 있는건 보지 않는다.

Resoltion modifiers

Angular의 찾는 행위는 modifier에 의해 수정될 수 있다.

  • @Optional - 없어도 에러 내지 않음.
  • @Self - 해당 컴포넌트의 ElementInjector만 찾아봄.
  • @SkipSelf - 부모의 ElementInjector부터 찾기 시작하게 함.(본인 생략)
  • @Host - HostComponent(의존성을 요청한 컴포넌트)까지만 찾도록 함.
    • 정확히 해당 component가 속해있는 뷰까지만 찾음.

Provider vs viewProvider

@Component에서 provider와 viewProvider property를 사용할 수 있다.

  • provider: component 메타데이터에 삽입됨.
    • child component도 똑같이 provide된 토큰을 공유해 사용할 수 있다.
  • viewProvider: view에서 사용가능하도록 한다.
    • ng-content로 들어온 component에서는 provide되지 않는다.
      • ng-content는 component의 view 안에 속하지 않기 때문
    • view 안에 명시된 component는 똑같이 공유 가능하다.
    • component class도 view이기 때문에 사용 가능하다.

참조

https://angular.io/guide/hierarchical-dependency-injection

Continue
virtual dom vs incremental dom

virtual dom

  • 모든 컴포넌트는 매 렌더링 시마다 새로운 돔 트리를 생성
  • react는 새로운 돔 트리를 기존 돔 트리와 비교한 뒤, 변경 사항을 브라우저의 돔에 적용

장점:

  • 렌더링 함수를 구현할 때 어떤 프로그래밍 언어든 사용 가능하므로, 컴파일할 필요가 없음. React는 주로 JSX를 사용하지만, plain javascript를 사용할 수도 있음.
  • 렌더링된 컴포넌트의 결과를 얻을 수 있어서 테스트나 디버깅에 사용할 수 있음.

Incremental dom

  • 구글에서 만듬
  • key idea: 모든 컴포넌트들은 각각 자체 렌더링 엔진이 되는 코드로 컴파일됨. 이 엔진들은 DOM 트리를 데이터가 바뀔 때 올바른 위치에 업데이트하거나 만듬.
  • 구글의 2가지 목표
    • tree shakable: dom 트리에서 사용하지 않는 node 제거
    • low memory footprint: 적은 메모리 사용량

tree shakable이 되는 이유

프레임워크 자체(런타임)에서 컴포넌트를 바로 해석하지 않고, 컴포넌트는 특정 코드(명령)들을 참조한다. 만약 참조하지 않고 있으면 아예 사용되지 않는다. 이 행동은 번들에서가 아니라 컴파일 시점에서 하기때문에 사용하지 않는 코드를 제거할 수 있다.

반면 virtual dom 은 런타임 때 컴포넌트가 해석된다. 컴파일 시점에 필요한지 필요하지 않은지 알 수 없기 때문에 항상 브라우저로 보내야한다.

적은 메모리 사용량을 갖는 이유

virtual dom은 매 렌더링 마다 전체 트리를 새로 만들지만,

incremantal dom은 dom이 바뀌지 않았을 때는 렌더링하는데 memory가 전혀 필요하지 않다. 오직 dom node가 추가되거나 제거될때만 필요하다. 크기도 dom 트리 전체가 아니라 dom 변경 사이즈만큼만 할당된다.

참조:

https://d2.naver.com/helloworld/59361

https://blog.nrwl.io/understanding-angular-ivy-incremental-dom-and-virtual-dom-243be844bf36

Continue
Test Runner vs Test Framework

Test Software

  • Test Runners: Karma
  • Testing Frameworks: Mocha
  • Assertion Libraries: Chai
  • Testing Plugins: Sinon

Test Runners: Karma

  • 가짜 서버 띄움
  • 가짜 서버로부터 파생된 데이터를 사용하여 다양한 브라우저에서 실행하는 테스트 실행기
  • 실제 테스트를 실행하기 위해 Mocha같은 Test Framework가 필요함

Testing Framework: Mocha

  • it, beforeEach, describe 같은 메소드 지원
  • test runner보다는 낮지만 assertion library 보다는 높은 추상화 수준

Assertion Libraries: Chai

  • 실제 테스트인 부분을 구현
  • 조건을 검사해 validation함
  • Jasmine같은 Testing Framework는 assertion library가 내장되어 있음.

Test Plugins: Sinon

  • Chai와 함께 더 다양한 테스트 셋을 구성할 수 있도록 도와주는 역할
  • Sinon 을 통해 Mock, 가짜 서버, stub을 만들 수 있음.
  • 소스 내부로 들어가 확인할 수 있는 기능들이 있음.
Continue
Home Archives Tags About Search