개요

  • no config 가 유행이지만 적용할 체계에 맞게 튜닝하려면 모든 옵션을 꿰고 있어야할 것이다.

전체 설정

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
const path = require("path");

module.exports = {
// 모드 설정
// production 일 경우 실환경용 플러그인을 활성화한다.
// https://webpack.js.org/configuration/mode/#mode-production
mode: "production", // "production" | "development" | "none"

// 어플리케이션 구조에 따라 웹팩이 빌드를 시작할 진입점 설정
entry: "./app/entry", // string | object | array

// 웹팩으로 빌드될 파일에 대한 출력 설정
output: {
// 경로 설정 (절대경로)
path: path.resolve(__dirname, "dist"), // string

// 파일명 설정
// https://webpack.js.org/configuration/output/#template-strings
// [id] [name] [fullhash] [contenthash] 등을 사용하여 구성 가능
filename: "[name].js", // string

// public 경로 설정
// 앱 내부에서 사용하는 asset 에 대해 기본 경로 지정 (이미지 등 public dir)
publicPath: "auto", // string

// 웹팩으로 라이브러리를 만드는 경우 사용하는 설정
// 기본값은 undefined
// https://github.com/webpack/webpack/tree/master/examples/multi-part-library
library: {
// 라이브러리 타입 정의
type: "var", // "umd2" | "commonjs-module" | "commonjs2" | "commonjs" | "amd" | "amd-require" | "system" | "this" | "var" | "assign" | "global" | "window" | "self" | "jsonp" | "module"

// 노출할 라이브러리의 이름 설정
name: undefined, // string | string[]


/* 라이브러리 고급설정 */
// 노출 되어야하는 엔트리 모듈 설정
export: undefined, // string | string[]

// UMD 래퍼에 추가할 코멘트
auxiliaryComment: "comment", // { amd: "comment", commonjs: "comment", commonjs2: "comment", root: "comment" },

// umd build 에서 amd define 함수에 이름 설정
umdNamedDefine: undefined,
},

// 빌드의 고유값 설정
// 이는 같은 HTML 에 대해 충돌을 방지한다.
uniqueName: "기본값은 package.json 파일의 name 속성",

// 여러 웹팩 설정을 사용 시에 확인할 이름 설정
name: undefined, // string


/* 고급 출력 설정 */
// 청크파일에 대한 파일명 설정
// long term cache 시에는 [contenthash].js
chunkFilename: "[id].js", // string | (pathData, assetInfo) => string

// 에셋 모듈에 대한 파일명 설정
assetModuleFilename: "[hash][ext][query]", // string

// 웹 어셈블리 모듈에 대한 파일명 설정
webassemblyModuleFilename: "[hash].module.wasm", // string

// 소스 맵 파일명 설정
// devtool: source-map 인 경우만 동작
sourceMapFilename: "[file].map[query]", // "sourcemaps/[file].map"

// 웹팩 devtool 에 대한 템플릿 설정
devtoolModuleFilenameTemplate: "webpack://[namespace]/[resource-path]?[loaders]", // string | (info) => string

// 웹팩 devtool 에 대한 템플릿 지정 (충돌 방지용)
devtoolFallbackModuleFilenameTemplate: undefined, // string | (info) => string

// 웹 에서 JSONP 로 청크를 로드하는 경우 CORS 설정
crossOriginLoading: false // "use-credentials" | "anonymous" | false

// import 함수명 (polyfill 사용시 변경)
// dynamic-import-polyfill 의 경우 __import__
importFunctionName: "import", // string

// import meta 명 (polyfill 사용시 변경)
importMetaName: "import.meta", // string


/* 전문가용 출력 설정 1 (위험) */
// 번들에 pathinfo 정보 추가 (production 에서 비활성화)
pathinfo: true, // boolean

// script tag 에 charset=utf-8 속성 추가
// 모던 브라우저에서 deprecated 되었지만 호환성을 위해 웹팩에서 기본으로 추가
charset: true, // string

// chunk 파일 타임아웃 설정
chunkLoadTimeout: 120000, // number (ms)

// 생성된 애셋을 디스크에 쓰기 전에 비교하여 일치할 경우 덮어쓰지 않음
compareBeforeEmit: true, // boolean

// require 시에 에러 발생을 추적할지 설정 (퍼포먼스 이슈로 비활성화가 기본값)
strictModuleExceptionHandling: false, // boolean

// devtools 의 소스 네임스페이스
devtoolNamespace: output.uniqueName, // string

// 출력 환경 설정
environment: {
// 화살표 함수 지원
arrowFunction: true,
// 123n 과 같은 bigInt 지원
bigIntLiteral: false,
// const 지원
const: true,
// destructing 연산자 지원
destructuring: true,
// import() 지원
dynamicImport: false,
// for of 문 지원
forOf: true,
// import / export 지원
module: false
},

// umd 와 같은 라이브러리의 경우 어느 전역 개체에 마운트할 지 설정
globalObject: "self", // string,

// 번들을 IIFE 로 감싸 isolation 을 줄지 설정
iife: true, // boolean

// 모듈 유형의 자바스크립트 파일로 생성할지 설정
// experiments.outputModule: true 로 실험 기능을 켜야하며 사용시 iife: false 로 설정된다.
module: false, // boolean

// 스크립트 타입 설정
// output.module 이 true 일 경우 이 값도 module 로 설정됨
scriptType: false, // boolean | "module" | "text/javascript"


/* 전문가용 출력 설정 2 (위험) */
// 청크 파일을 로드 방법 설정
// web: jsonp, worker: importScritps, sync node.js: require, async node.js: async-node
chunkLoading: "jsonp", // "jsonp" | "import-scripts" | "require" | "async-node" | false

// 청크 파일을 등록할 전역변수 설정
chunkLoadingGlobal: "webpackChunkwebpack", // string

// 엔트리포인트에서 사용할 수 있는 청크 로딩 타입 설정
// 웹팩에 의해 자동으로 설정됨
enabledChunkLoadingTypes: ["jsonp", "import-scripts"], // string[]

// 엔트리포인트에서 사용할 라이브러리 타입 설정
enabledLibraryTypes: [], // string[]

// 엔트리포인트에서 사용할 wasm 로딩 타입 설정
enabledWasmLoadingTypes: ["fetch"], // string[]

// 청크 포맷 설정
// web: array-push, worker: array-push, node.js: commonjs
chunkFormat: "array-push",

// HMR manifest 파일명 설정 (비권장)
hotUpdateMainFilename: "[runtime].[fullhash].hot-update.json", // string

// HMR 청크의 파일명 설정
hotUpdateChunkFilename: "[id].[fullhash].hot-update.js", // string

// HMR 청크를 로드할 시 JSONP 함수명 설정
hotUpdateGlobal: "webpackHotUpdatewebpack", // string

// 출력의 각 라인 앞에 붙을 prefix 를 설정
sourcePrefix: undefined, // string

// 사용할 해싱 알고리즘 설정
hashFunction: "md4", // string

// 해시를 생성할 때 사용할 인코딩 설정
hashDigest: "hex", // string

// 사용할 해시의 prefix 길이 설정
hashDigestLength: 20, // number

// 해시 솔트 설정 (해시 관련 이슈 발생시)
hashSalt: undefined, // string | Buffer.

// 워커 내에서 청크 로딩 방식 설정
workerChunkLoading: "import-scripts",

// 워커 내에서 wasm 로딩 방식 설정
workerWasmLoading: "fetch",
},

// 사용할 모듈 설정
module: {
// 모듈 규칙 설정
rules: [
{
// 조건
test: /\.jsx?$/,

// 포함할 경로 (exclude 보다 사용을 권장)
include: [
path.resolve(__dirname, "app")
],

// 제외할 경로 (test 보다 높은 우선순위)
exclude: [
path.resolve(__dirname, "app/demo-files")
],

// 어디서 import 되는지에 따라 모듈을 사용할지 설정
// 파일에 따라 raw-loader, babel-loader 등 import 방식을 다르게 쓰는 경우 사용한다.
issuer: { or: [ /\.css$/, path.resolve(__dirname, "app") ] },


/* 고급 조건 설정 */
// 모듈의 리소스와 일치하는지 테스트 (test, include 와 동일)
resource: /\.css$/,

// 하위 컴파일러 이름과 일치하는지 테스트
compiler: /html-webpack-plugin/,

// dependency 타입이 일치하는지 테스트
dependency: "esm", // import-style dependencies
dependency: "commonjs", // require-style dependencies
dependency: "amd", // AMD-style dependency
dependency: "wasm", // WebAssembly imports section
dependency: "url", // new URL(), url() and similar
dependency: "worker", // new Worker() and similar
dependency: "loader", // this.loadModule in loaders

// package.json 의 정보와 일치하는지 테스트
descriptionData: {
type: "module"
},

// 리소스의 mimetype 이 일치하는지 테스트
mimetype: "text/javascript",

// resource 와 같지만 리소스명이 변경된 경우 무시
realResource: /\.css$/,

// 리소스의 Fragment 가 일치하는지 테스트
resourceFragment: "#blah",

// 리소스의 쿼리스트링이 일치하는지 테스트
resourceQuery: "?blah"

// 적용할 로더를 설정
// use: [ { loader } ] 의 shortcut
loader: "babel-loader",
// 로더 옵션을 설정
options: {
presets: ["es2015"]
},

// 여러 로더를 한 번에 설정
use: [
"htmllint-loader",
{
loader: "html-loader",
options: {}
}
],

// 일치하는 모듈의 타입을 설정
// 설정 시 defaultRules 및 기본 import 기능은 우회된다.
// https://webpack.js.org/configuration/module/#ruletype
type: "javascript/auto", // 'javascript/auto' | 'javascript/dynamic' | 'javascript/esm' | 'json' | 'webassembly/sync' | 'webassembly/async' | 'asset' | 'asset/source' | 'asset/resource' | 'asset/inline'


/* 고급 액션 설정 */
// 로더 순서 설정
// 미설정시 normal loader 로 호출된다.
enforce: "pre", // "pre" | "post"

// 모듈 타입에 따른 제네레이터 설정
generator: {
dataUrl: {
encoding: "base64", // "base64" | false
mimetype: undefined,
},
// output.assetModuleFilename 를 override 하며 asset, asset/resource 타입의 경우만 동작
filename: "",
},

// 모듈 타입에 따른 파서 설정
parser: {
amd: false, // disable AMD
commonjs: false, // disable CommonJS
system: false, // disable SystemJS
harmony: false, // disable ES2015 Harmony import/export
requireInclude: false, // disable require.include
requireEnsure: false, // disable require.ensure
requireContext: false, // disable require.context
browserify: false, // disable special handling of Browserify bundles
requireJs: false, // disable requirejs.*
node: false, // disable __dirname, __filename, module, require.extensions, require.main, etc.
node: {...}, // reconfigure [node](/configuration/node) layer on module level
worker: ["default from web-worker", "..."] // Customize the WebWorker handling for javascript files, "..." refers to the defaults.
},

// 모듈별 리졸브 설정
resolve: {
// 해당 key 를 리졸브 할시 script.js 로 대체
alias: {
key: "script.js",
},

// package.json 의 type: "module" 인 경우 파일 확장자와 파일명을 명시해야한다.
fullySpecified: true,
},

// 스코프를 벗어나 사이드이펙트를 발생시키는지 명시적으로 설정
// package.json 의 sideEffects 를 override
sideEffects: false, // boolean
},
{
// 일치하는 하나의 규칙만 사용
oneOf: [
// ... (rules)
]
},
{
// 중첩된 규칙 모두 사용
rules: [
// ... (rules)
]
},
],


/* 고급 모듈 설정 */
// 이 모듈에서 파싱하지 않을 경로 설정
noParse: [
/special-library\.js$/
],

// 동적 요청에 대한 모듈 컨텍스트 기본 설정
// 곧 deprecated 될 예정으로 사용 비권장
unknownContextRequest: ".",
unknownContextRecursive: true,
unknownContextRegExp: /^\.\/.*$/,
unknownContextCritical: true,
exprContextRequest: ".",
exprContextRegExp: /^\.\/.*$/,
exprContextRecursive: true,
exprContextCritical: true,
wrappedContextRegExp: /.*/,
wrappedContextRecursive: true,
wrappedContextCritical: false,
},

// 모듈 리졸브 설정
// (로더 리졸브 시에는 사용되지 않음)
resolve: {

// 모듈을 찾을 디렉토리
// 상대 경로일 경우 현재 디렉토리와 부모 디렉토리까지 확인
modules: ["node_modules"],

// 사용할 확장자
// 이름이 같고 확장자만 다를 경우 첫 번째 확장자를 사용
extensions: [".wasm", ".mjs", ".js", ".json"],

// 특정 모듈을 더 쉽게 리졸브하기 위해 별칭 설정
alias: {
// e.g. "module/path/file" -> "new-module/path/file"
"module": "new-module",

// e.g. "only-module" -> "new-module", "only-module/path/file" -> "new-module/path/file" 는 불가
"only-module$": "new-module",

// e.g. "module" -> "./app/third/module.js", "module/file" 은 에러
"module": path.resolve(__dirname, "app/third/module.js"),

// e.g. "module/file" -> "./app/third/file"
"module": path.resolve(__dirname, "app/third"),

// e.g. "./app/module.js" -> "./app/alternative-module.js"
[path.resolve(__dirname, "app/module.js")]: path.resolve(__dirname, "app/alternative-module.js"),
},

/* 고급 리졸브 설정 */
conditionNames: ["myCompanyCondition", "..."],
// conditions used for the "exports" and "imports" field in description file
roots: [path.resolve(__dirname, "app/root")],
// locations where to resolve server-relative requests (starting with "/")
// This behavior is only applied when the request doesn't resolve as absolute path
fallback: { "events": path.resolve(__dirname, "events.js") },
// Similar to alias, but only applied when the normal resolving fails
mainFields: ["main"],
// properties that are read from description file
// when a folder is requested
restrictions: [ /\.js$/, path.resolve(__dirname, "app") ],
// To successful resolve the result must match these criteria
cache: true, // boolean
// enable safe caching of resolving
// this is safe as it tracks and validates all resolving dependencies
unsafeCache: true,
unsafeCache: {},
// enables unsafe caching for resolved requests
// this is unsafe as there is no validation
// but performance improvement is really big
plugins: [
// ...
],
// additional plugins applied to the resolver


/* 전문가용 리졸브 설정 */
symlinks: true, // boolean
// follow symlinks to new location
descriptionFiles: ["package.json"], // (default)
// files that are read for package description
aliasFields: ["browser"],
// properties that are read from description file
// to alias requests in this package
exportsFields: ["exports"], // (default)
// fields in description file that are used for external module request
importsFields: ["imports"], // (default)
// fields in description file that are used for internal request
mainFiles: ["index"],
// files that are used when resolving in a directory and no mainField applies
fullySpecified: true, // boolean
// Input request is already full specified (it includes filename and extension)
// Module requests are still resolved as usual
preferRelative: true, // boolean
// Try to resolve module requests also a relative request
enforceExtension: false,
// if true request must not include an extension
// if false request may already include an extension
cachePredicate: ({ path, request }) => true,
// predicate function which selects requests for caching
cacheWithContext: false, // (default)
// include context information in cache key
// This must be set to true when custom plugins resolve depending on
// those information
useSyncFileSystemCalls: false, // (default)
// use sync fs calls instead of async fs calls
byDependency: { commonjs: { extensions: [".js", "..."] } },
// change resolving depending on issuer dependency
},

// 웹팩 퍼포먼스 힌트 표시 설정
performance: {
// 힌트설정
hints: "warning", // "warning" | "error" | false

// 경고를 내보낼 최대 에셋 크기
maxAssetSize: 250000, // number

// 경고를 내보낼 최대 엔트리 크기
maxEntrypointSize: 250000, // number

// 퍼포먼스 힌트를 계산할 파일 필터 설정
assetFilter: (assetFilename) => {
return !(/\.map$/.test(assetFilename));
}
},

// 브라우저 devtools 에 대한 소스맵 스타일 설정
// 설정에 따라 빌드 성능에 영향을 미칠 수 있다.
// https://webpack.js.org/configuration/devtool/#devtool
devtool: false, // enum, 위 링크 참조

// 설정에서 엔트리 및 로더를 확인하기 위한 기본 홈 경로 (절대경로)
context: __dirname, // string

// 번들이 실행되어야할 환경 설정
// web 이 기본이며 browserslist 환경에서는 browserslist 가 기본이다.
// https://webpack.js.org/configuration/target/#string
target: "web", // enum

// 번들링시 해당 모듈의 종속성을 제거한다.
// 주로 외부 라이브러리 종속성 제거에 사용된다.
// https://webpack.js.org/configuration/externals/#combining-syntaxes
externals: undefined, // string | [string] | object | function | RegExp

// externals 타입 설정
externalsType: "var", // 기본값은 output.library.type

// 특정 대상에 대한 externals 프리셋을 활성화한다.
externalsPresets: {
electron: false,
electronMain: false,
electronPreload: false,
electronRenderer: false,
node: false,
nwjs: false,
web: true,
webAsync: true,
},

// 경고를 무시할 패턴 설정
ignoreWarnings: undefined, // RegExp | (WebpackError, Compilation) => boolean | {module?: RegExp, file?: RegExp, message?: RegExp}

stats: "errors-only",
stats: {
// lets you precisely control what bundle information gets displayed
preset: "errors-only",
// A stats preset


/* 고급 전역 설정 */
all: false,
// switch all flags on or off
colors: true,
// switch colors on and off
context: __dirname,
// all paths will be relative to this directory
ids: true,
// include module and chunk ids in the output

env: true,
// include value of --env in the output
outputPath: true,
// include absolute output path in the output
publicPath: true,
// include public path in the output

assets: true,
// show list of assets in output

/* 고급 에셋 설정 */
assetsSort: "size",
// sorting of assets
assetsSpace: 50,
// number of asset lines to display
cachedAssets: false,
// show assets that are caching in output
excludeAssets: /\.png$/,
// hide some assets
groupAssetsByPath: true,
// group assets by their path in the output directory
groupAssetsByExtension: true,
// group assets by their extension
groupAssetsByEmitStatus: true,
// group assets depending if they are cached, emitted or compared
groupAssetsByChunk: true,
// group assets by how they relate to chunks
groupAssetsByInfo: true,
// group assets by meta information like immutable, development, etc.
relatedAssets: true,
// show assets that are related to other assets, like SourceMaps, compressed version, etc.
performance: true,
// show performance hints next to assets and modules

entrypoints: true,
// show entrypoints list
chunkGroups: true,
// show named chunk group list
/* 고급 청크 그룹 설정 */
chunkGroupAuxiliary: true,
// show auxiliary assets for entrypoints/chunk groups
chunkGroupChildren
// show child chunk groups for entrypoints/chunk groups
chunkGroupMaxAssets: 5,
// collapse chunk group assets lists when this limit has been reached

chunks: true,
// show list of chunks in output


/* 고급 청크 설정 */
chunksSort: "size",
// sort chunks list
chunkModules: true,
// show modules contained in each chunk
chunkOrigins: true,
// show the origin of a chunk (why was this chunk created)
chunkRelations: true,
// show relations to other chunks (parents, children, sibilings)
dependentModules: true,
// show modules that are dependencies of other modules in that chunk

modules: true,
// show list of modules in output


/* 고급 모듈 설정 */
modulesSpace: 50,
// number of modules lines to display
nestedModules: true,
// show nested modules (when concatenated)
cachedModules: true,
// show modules that were cached
orphanModules: true,
// show modules that are not referenced in optimized graph anymore
excludeModules: /\.css$/,
// hide some modules
reasons: true,
// show the reasons why modules are included
source: true,
// include the Source Code of modules (only in JSON)


/* 전문가용 모듈 설정 */
modulesSort: "size",
// sort modules list
groupModulesByPath: true,
// group modules by their resource path
groupModulesByExtension: true,
// group modules by their extension
groupModulesByAttributes: true,
// group modules by attributes like if the have errors/warnings/assets
// or are optional
groupModulesByCacheStatus: true,
// group modules depending if they are built, code was generated or if
// they are cacheable in general
depth: true,
// show depth in the module graph of modules
moduleAssets: true,
// show assets emitted by modules in module list
runtimeModules: true,
// show runtime modules in the modules list

/* 고급 최적화 설정 */
providedExports: true,
// show exports provided by modules
usedExports: true,
// show which exports are used by modules
optimizationBailout: true,
// show information why optimizations bailed out for modules

children: true
// show stats for child compilations

logging: true,
// show logging in output
loggingDebug: /webpack/,
// show debug type logging for some loggers
loggingTrace: true,
// show stack traces for warnings and errors in logging output

warnings: true,
// show warnings

errors: true,
// show errors
errorDetails: true,
// show details for errors
errorStack: true,
// show internal stack trace for errors
moduleTrace: true,
// show module trace for errors
// (why was causing module referenced)

builtAt: true,
// show timestamp in summary
errorsCount: true,
// show errors count in summary
warningsCount: true,
// show warnings count in summary
timings: true,
// show build timing in summary
version: true,
// show webpack version in summary
hash: true,
// show build hash in summary
},

devServer: {
proxy: { // proxy URLs to backend development server
'/api': 'http://localhost:3000'
},
contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
compress: true, // enable gzip compression
historyApiFallback: true, // true for index.html upon 404, object for multiple paths
hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
https: false, // true for self-signed, object for cert authority
noInfo: true, // only errors & warns on hot reload
// ...
},

// 실험 기능 설정
experiments: {
// wasm 모듈을 비동기로 설정
// https://github.com/WebAssembly/esm-integration
asyncWebAssembly: true,

// deprecated (webpack4)
syncWebAssembly: true,

// ES module 허용
// output.libraryTarget 을 module 로 설정
outputModule: true,
// Allow to output ESM

// top-level await 를 허용
topLevelAwait: true,
}

// 빌드시 사용할 플러그인 설정
plugins: [
// ...
],

// 최적화 설정
optimization: {
// 청크 아이디를 생성할 때에 사용할 알고리즘
// production: "deterministic", development: "named", fallback: "natural"
// https://webpack.js.org/configuration/optimization/#optimizationchunkids
chunkIds: "deterministic", // false | "natural" | "named" | "size" | "total-size" | "deterministic"

// 모듈 아이디를 생성할 때에 사용할 알고리즘
// production: "deterministic", development: "named", fallback: "natural"
moduleIds: "deterministic", // false | "natural" | "named" | "deterministic"

// exports 명을 mangle 할지 설정
// production: "deterministic", fallback: false
mangleExports: "deterministic", // false | "deterministic" | "size"

// 출력 파일을 압축할지 설정
// production: true, fallback: false
minimize: true, // boolean

// 사용할 압축 플러그인 설정
minimizer: [],


/* 고급 최적화 */
// concatenate multiple modules into a single one
// production: true, fallback: false
concatenateModules: true, // boolean

// 빌드 에러가 있어도 출력을 내보낼지 설정
// production: false, fallback: true
emitOnErrors: false, // boolean

// 이미 로드된 청크에 포함되어있을 경우 청크를 다운로드하지 않게 플래그 설정
// production: true, fallback: false
flagIncludedChunks: true, // boolean

// 사용하지 않는 exports 에 대해 내부 그래프 분석 수행 설정
// production: true, fallback: false
innerGraph: true, // boolean

// 동일한 모듈을 포함하는 청크를 병합하게 설정
mergeDuplicateChunks: true, // boolean

// 웹팩 process.env.NODE_ENV 설정
// mode 값을 바라보고, mode: "none" 일 경우 false 와 동일
nodeEnv: "production", // string | boolean

// 레코드 생성시 상대경로를 사용할지 설정
// recordsPath, recordsInputPath, recordsOutputPath 사용시에 자동으로 활성화
portableRecords: false, // boolean

// 모듈에서 export * from 구문에 대해 효율적인 코드를 생성하게 설정
providedExports: true, // boolean

// 사용하지 않는 exports 를 제거
// production: true, fallback: false
usedExports: true, // boolean | "global"

// 파일 내용에 기반하여 contenthash 계산
// production: true, fallback: false
realContentHash: true, // boolean

// 모듈이 이미 상위 청크에 포함되어 있을경우 감지하여 제거
// 빌드 성능을 위해서는 비활성화하는 것이 좋다.
removeAvailableModules: false, // boolean

// 빈 청크파일 제거
removeEmptyChunks: true,

// 런타임 청크 설정
// 다중 엔트리의 경우 "single" 로 변경 후 런타임 청크를 공유할 수 있다.
runtimeChunk: false, // object | string | boolean

// exports 를 중복으로 사용할 때에 사이드이펙트가 없는 모듈 건너뛰기
// optimization.providedExports 가 활성화되어야 사용 가능
// production: true, fallback: "flag"
sideEffects: true,

splitChunks: {
cacheGroups: {
"my-name": {
// define groups of modules with specific
// caching behavior
test: /\.sass$/,
type: "css/mini-extract",

/* 고급 셀렉터 */
chunks: "async",
minChunks: 1,
enforceSizeThreshold: 100000,
minSize: 0,
minRemainingSize: 0,
usedExports: true,
maxAsyncRequests: 30,
maxInitialRequests: 30,

/* 고급 이펙트 설정 */
maxAsyncSize: 200000,
maxInitialSize: 100000,
maxSize: 200000,
filename: "my-name-[contenthash].js",
idHint: "my-name",
name: false,
hidePathInfo: true,
automaticNameDelimiter: "-",
}
},

fallbackCacheGroup: {
automaticNameDelimiter: "-"
minSize: 20000,
maxAsyncSize: 200000,
maxInitialSize: 100000,
maxSize: 200000,
}


/* 고급 셀렉터 설정 */
chunks: "all",
// select which chunks should be optimized
usedExports: true,
// treat modules as equal only when used exports are equal
minChunks: 1,
// minimum number of chunks a module must be in
enforceSizeThreshold: 100000,
// ignore when following criterias when size of modules
// is above this threshold
minSize: 20000,
// size of modules must be above this threshold
minRemainingSize: 20000,
// when modules are removed from a single chunk
// the size of the modules that are remaining
// must be above this threshold
maxAsyncRequests: 30,
maxInitialRequests: 30,
// number of parallel requests for a single on demand loading
// resp. entrypoint but be above this threshold

/* 고급 이펙트 설정 */
maxAsyncSize: 200000,
maxInitialSize: 100000,
maxSize: 200000,
// when size of modules in the new chunk is above this
// threshold, split it further
filename: "[contenthash].js",
// give the new chunk a different filename
name: false, // false | string | (module, chunks, key) => string
// give the new chunk a different name
// when an existing name is used, chunks are merged
// non-splitChunks chunks can only be selected, when they are
// a parent or sibling chunk of all selected modules
hidePathInfo: true,
// hide path info when splitting via "maxSize"
automaticNameDelimiter: "-",
// use this separator to separate original name from path info
// when splitting via "maxSize"


/* 전문가용 설정 */
defaultSizeTypes: ["javascript", "..."]
// when using numbers for sizes measure these size types
// minSize: { javascript: 10000 } allows to be more specific
}
},


/* 고급 설정 */
loader: { /* ... */ },
// add custom API or properties to loader context
resolveLoader: { /* same as resolve */ }
// separate resolve options for loaders
node: {
// Polyfills and mocks to run Node.js-
// environment code in non-Node environments.
global: true, // boolean
// replace "global" with the output.globalObject
__filename: "mock", // boolean | "mock" | "eval-only"
__dirname: "mock", // boolean | "mock" | "eval-only"
// true: includes the real path
// "mock": includes a fake path
// "eval-only": only defines it at compile-time
// false: disables all handling
},
recordsPath: path.resolve(__dirname, "build/records.json"),
recordsInputPath: path.resolve(__dirname, "build/records.json"),
recordsOutputPath: path.resolve(__dirname, "build/records.json"),
// store ids into a file to make the build even more deterministic


/* 고급 캐시설정 */
// 캐시 설정
// 개발 모드에서는 cache: true 이며 { type: "memory" } 와 동일
// 프로덕션 모드에서는 비활성화
// https://webpack.js.org/configuration/other-options/#cache
cache: false, // boolean | object
cache: {
type: "filesystem", // "memory" | "filesystem"

// 캐시 기본 폴더 설정
cacheDirectory: "node_modules/.cache/webpack", // string

// 캐시 경로 설정
cacheLocation: path.resolve(cache.cacheDriectory, cache.name), // string

// 무효화를 위한 캐시 의존성 추가
buildDependencies: {
defaultWebpack: [ "webpack/lib" ],
// 최신 웹팩 설정에 대한 캐시 의존성을 설정하려면 아래 설정 권장
// config: [ __filename ],
},

// 캐시에서 사용할 해시 알고리즘 설정
hashAlgorithm: "md4", // string

// 캐시명 설정
// 여러 웹팩 설정별로 독립된 캐시를 가져야할 때 변경할 수 있다.
name: `${config.name}-${config.mode}`, // string

// 파일시스템에 캐시를 저장할 시점 설정
// pack: 컴파일러가 idle 상태일 경우 단일 파일에 데이터 저장
store: "pack", // "pack"

// 파일 캐시를 무효화하기 위한 버전 설정
version: "", // string

// store: pack 인 경우 캐시를 저장할 주기 설정
idleTimeout: 10000, // number (ms)

// store: pack 인 경우 캐시를 초기화할 시간 설정
idleTimeoutForInitialStore: 0, // number (ms)
},

// 파일시스템 스냅샷을 생성하고 무효화하는 방법 설정
snapshot: {
// package.json 에서 관리되는 경로
managedPaths: [ path.resolve(__dirname, "node_modules") ], // string[]

// immutable 하여 스냅샷일 필요가 없는 경로
// path.resolve(__dirname, ".yarn/cache")
immutablePaths: [], // string[]

// 모듈 빌드시의 스냅샷 설정
module: {
// 타임스탬프를 비교하여 무효화 확인
timestamp: true,
// 해시 비교로 무효화 확인
// timestamp 보다 무겁지만 자주 변경되지 않음
hash: true,
},

// 리졸브시 스냅샷 설정
resolve: {
timestamp: true,
hash: true,
},

// 캐시를 사용시 빌드 종속성 리졸브시 스냅샷
resolveBuildDependencies: {
timestamp: true,
hash: true,
},

// 캐시 사용시 빌드 종속성 스냅샷
buildDependencies: {
timestamp: true,
// CI 환경에 적합
hash: true,
},
},

// watch 설정
watch: true, // boolean

// watch option 설정
watchOptions: {
// 파일 변경시에 지연시간 설정
aggregateTimeout: 200, // number (ms)

// watch 를 하지 않을 경로 설정
ignored: /node_modules/, // RegExp | string | [string, RegExp]

// poll 방식으로 watch 할지 설정
// 주로 nfs 사용으로 파일시스템에서 변경을 감지할 수 없을 경우
poll: false, // boolean | number (ms)
},

/* 고급 빌드 설정 */
// 인프라 수준 로깅 설정
infrastructureLogging: {
level: "info", // "none" | "error" | "warn" | "info" | "log" | "verbose"
debug: undefined // true | string | RegExp | (name) => boolean | [string, RegExp, (name) => boolean]
},

// 병렬 처리할 모듈의 수 제한
// 성능을 미세하게 조정하거나 안정적인 결과를 얻는 데에 사용 가능
parallelism: 100, // number

// 통계 및 힌트를 포함하여 분석 도구에서 사용할 수 있게 프로필 제한
// 더 나은 결과를 위해 parallelism: 1 로 설정해야한다.
profile: true, // boolean

// 첫 오류 발생시 종료 설정
// 웹팩은 HMR 사용 시에 브라우저 콘솔, 터미널에 오류를 기록하지만 번들링을 게속하는데 이를 방지한다.
bail: false, // boolean

// 여러 웹팩 설정에 대한 빌드 의존성 설정
dependencies: ["name"],
}

기본 값 확인

  • 옵션 별 기본 값은 문서상으로 알 수 없는 경우가 있었다.
  • 아래 소스에서 확인 가능했다.
    • webpack/lib/config/defaults.js
    • webpack/test/Defaults.unittest.js

개요

  • 앵귤러 같기도 하고 코드를 머리를 써서 읽어야한다는 것 때문에 최대한 안 쓰고 싶다.
  • 하지만 async/await 로 동시성 제어가 힘드므로 이 쪽이 답인 것 같다.

소스

  • 데이터 배열에서 5개씩 끊어서 병렬 요청한다.
  • delay, catchError, delayWhen 의 조건만 다르게 하여 사용하면 된다.
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
import { EMPTY, from, of } from "rxjs";
import {
catchError,
delay,
delayWhen,
finalize,
map,
mergeMap,
retry,
tap,
toArray
} from "rxjs/operators";

// 슈도코드임
const fetchObservable = (data) => {
return from(
new Promise((resolve) => {
setTimeout(() => {
resolve({
data
});
}, 300);
})
);
};

const concurrency = 5;
const fetchConcurrently$ = from(YOUR_DATA).pipe(
mergeMap((token) => {
return fetchObservable(YOUR_DATA).pipe(
map(({ data }) => data),
delay(1000),
retry(1),
catchError(() => EMPTY)
);
}, concurrency),
map((data) => {
return data.id;
}),
toArray(),
delayWhen(() => Promise.resolve()),
finalize(() => console.log("done"))
);

fetchConcurrently$.subscribe((ids) => console.log(ids));

테스트

  • 테스트 시에 jest 에서 done callback 을 적절히 호출해주자.

여담

  • marble 테스트도 작성해보고 싶었는데 레퍼런스로 삼을만한 문서를 못 찾아서 아쉽다.
  • 매번 of 와 from 을 헷갈리는데, 전자는 하나씩이고 후자는 덩어리다.

Chrome dev summit 2020

Performance (특히 CLS)와 Privacy 가 행사 전반에 녹아있었다. 가장 중요했던 건 이제 Web Vitals 수치가 SEO 에 반영이 된다는 것이다.

State of speed tooling

Field vs Lab

  • Lighthouse 지표가 올해 리뉴얼 되었는데 FCP, TBT, CLS 지표를 주로 보면 된다.
  • 이 값이 Core Web Vitals 지표로 사용된다.
  • 실험실 데이터와 필드 데이터가 있는데, 실험실 데이터로 향상을 시킨 뒤 필드 데이터로 확인하면 된다.
  • Third party 에 대한 측정도 추가되었다.
  • Thrid party facades 를 통해 iframe 등도 lazy load 해야한다.

Fixing common Web Vitals issues

  • CLS를 측정하고 개선하는 방안에 대해 설명하였다.
  • GoogleChrome/web-vitals 로 측정해라.
  • LayoutShiftAttribution API
1
2
3
4
5
6
7
8
9
const connection = navigator.connection.effectiveType;
const { width, height } = window.screen;
const debugInfo = { connection, widght, height };
sendToAnalytics({ cls, debugInfo });

// Ohter sources of info:
// window.scrollY
// LayoutShiftAttribution (part of the Layout Instability API)
// Debugging info specific to your app (e.g. tokens)

UX patterns optimized for Core Web Vitals

Prevent CLS

  • Placeholder 를 놓아 CLS 를 막아야한다는 내용이였다.
  • Click 해서 내용이 추가되는 경우도 마찬가지이다.

Exploring the future of Core Web Vitals

  • Web Vitals Changelogs 확인하고 피드백 주면 좋겠다.

Core Web Vitals and SEO

  • 첫째 날의 가장 중요한 섹션이지 않을까 싶다. 21년 5월부터 랭킹에 적용된다.
  • Loading, Interactivity, Stability 가 중요하다.
  • SEO 에는 필드 데이터를 사용한다.
    • Field data: RUM, CrUX 랑 비슷하고, last ~month 사이의 기간동안 측정된 결과이다.
    • Lab test data: 테스트 데이터

Ranking factors

  • Ranking factor 는 위와 같다. Google 검색결과의 페이지 환경 이해를 참조하자.
  • Search Console 에서 Core Web Vitals report 데이터를 확인 가능하다.
  • 회사명 같은 특정한 경우는 속도가 Ranking factor 에 주는 영향력이 낮지만, best editor 처럼 여러 검색 결과가 나오는 경우에는 page experience 가 표시에 더 많은 영향을 준다.
  • Great relevant content > Page experience
  • Page experience 가 좋으면 유저의 이탈율이 24% 감소한다. (The Science Behind Web Vitals)

Beyond fast

  • 동적으로 로드되는 영역에 대해 content-visibility을 줘서 placeholder 를 구현할 수 있다.
1
2
3
4
.content {
content-visibility: auto;
content-intrinsic-size: 0 500px;
}

font metrics override descriptors

  • 운영체제와 브라우저에 따라 폰트가 다르게 보이는 것과 swap 폰트 로드로 인해 레이아웃이 변경되는 구조를 막을 수 있다.
  • ascent-override, descent-override, line-gap-override Chrome87 부터 가능하다.
  • Back Forward Cache 가 Chrome87 에서 Same Origin 에 대해서도 적용되었다. (November 17, 2020)
  • chrome은 strict multi-process architecture 라 적용하는데 좀 걸렸다.
  • portal 을 18년 소개 후부터 미는데… 브라우저 지원이 없어서 아직까진 잘 모르겠다.
  • 포탈을 쓰면 스토리지 제한이 없어서 두 사이트 간 content 공유가 쉽다.
  • quicklink 도 짧게 소개했다. newegg 사는 50% 컨버젼 상승과 4x faster navigation 을 달성했다고 한다.

Enable and debug cross-origin isolated

  • 헤더가 추가된다.
    • Cross-Origin-Opener-Policy: same-origin 코옵
    • Cross-Origin-Embedder-Policy: required-corp 코엡
  • Why you need “cross-origin isolated” for powerful features 문서와 Demo 참조하는 게 좋다.
  • COOP 는 no-openner 속성과 비슷하며 제어하는 위치의 차이이다.
  • COEP 는 CORP 와 함께 사용하여 리소스 로드를 제어할 수 있다. 서비스워커보다 상위에서 실행된다.
  • 요약하면 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
# 타 도메인 리소스
Cross-Origin-Resource-Policy: cross-origin
# 또는 CORS 열고 img[crossorigin] 추가

# 타 도메인 아이프레인
Cross-Origin-Resource-Policy: cross-origin

# 내 사이트
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
# 또는 a[no-openner] 추가

Introducing the Privacy Budget

  • 브라우저 fingerprint 를 방지하기 위한 proposal
  • Privacy Sandbox
  • Less entropy, User-Agent Client Hints 로 개선하여 특정하지 못 하게 할 것이다.

A more private way to measure ad conversions

  • 쿠키를 심어서 arbitrary information까지 추적이 가능한 걸 개선하려한다.
  • conversiondestination, impressiondata, reportingorigin 가 추가된다.
  • 브라우저 자체에 고유값이 저장되며 chrome://conversion-internals 에서 디버깅 가능하다.
  • A more private way to measure ad conversions, the Event Conversion Measurement APIDemo 를 보는 게 이해가 쉬웠다.

Sign-up form best practices

  • 한 판을 정리해놓아서 이 것만 보면 된다.
  • 이름 검사에 정규식을 사용할 때 유니코드 기반을 사용해야한다. /[\p{L} ]+/u
  • 개인적으로 듣기 제일 좋았다.

SMS OTP form best practices

1
2
3
4
5
6
7
<input 
type="text"
inputmode="numeric"
autocomplete="one-time-code"
pattern="\d{6}"
required
>
  • 한 판을 정리해놓아 이 것만 보면 된다.
  • type="number" 가 아닌 type="text", inputmode="numeric" 을 사용해야한다.
  • OTP 문자 마지막 라인에는 @${bound-domain} #${OTP} 처럼 넣어주면 브라우저에서 파싱이 가능하다. (e.g. @gracefullight.dev #123456)
  • Web OTP API를 사용하여 서버에서 바로 받아서 사용할 수도 있다.

What’s new in DevTools

  • Gird 지원 추가
  • Location 변경 기능 추가, Timezone, Language
  • Idle Detection API 추가, Kiosk 에서 비활성화시 사용 가능
  • Font-face 에서 fallback 을 확인 가능, Disable Local font 추가
  • animation 수정 가능
  • element screenshot
  • element hover 시 accessibility 확인 필드 추가
  • warning 등이 Issues 탭으로 나와서 가이드까지 추가 됨
  • WebAuthn 탭으로 디버깅 가능
  • CSS Overview 탭 추가
  • Media 디버깅 패널 추가

Extending CSS with Houdini

  • Houdini.how에 Worklet 확인 가능
  • CSS.paintWorklet.addModule 로 바로 가져와서 쓸 수 있다고 강조했다.
  • Static gradient worklet 가 예뻐보였다.

Debugging WebAssembly with modern tools

  • emcc 로 c++ 디버깅 옵션을 주면 c++ 소스를 바로 디버깅도 가능하다.
  • 추후 memory inspection, custom formatters, profiling, code coverage 기능을 추가할 예정이다.
  • google map 에 어떤 wasm 기술이 적용되었는지 알려주면 좋을텐데 아쉬웠다.

Transitioning to modern JavaScript

6x slower

  • 모던 자바스크립트는 최신 버전을 말하는 게 아니라, All modern browsers 가 사용할 수 있는 스크립트를 말한다.
  • ES2017 은 유저 중 94% 가 다 사용할 수 있으므로 더 높은 버전 사용시에는 이 버전을 타켓으로 해야한다. (ES2021은 70%)
  • 더 낮은 브라우저에 대한 지원이 필요할 경우 ESnext > ES2017 > ES2015 처럼 ES2017 을 한번 더 말아서 따로 제공해야한다. 필요 없는 코드가 너무 많이 들어가기 때문이다.
  • 달마다 80PB 가 필요없는 레거시 자바스크립트 로드에 사용된다고 한다.
  • EStimator 를 사용하여 어떤 코드를 변경해야할지 확인할 수 있다.
  • Publish, ship, and install modern JavaScript for faster applications

Next-level web apps on desktop

  • PWA 로 추가될 내용들이였다. 기능 설명 위주였다.
  • navigator.runOnOsLogin
  • Window placement window.getScreens(), internal/external
  • Tabbed application mode & Display override manifest.display_override: "tabbed"
  • Notification triggers TimestampTrigger
  • Link capturing manifest.capture_links, manifest.url_handlers
  • File type handling manifest.file_handlers, launchQueue
  • Badging (이게 드디어 나온다.)
  • Digital goods API

New logic to detect PWA offline support

  • 현재는 오프라인 체크시 서비스워커에 fetch event 가 정의되어있는지만 확인하는데 이제는 offine 시에 200 이 반환되어야한다.
  • chrome88 에서 check-offline-capability 플래그를 활성화하여 테스트 가능하다.
  • chrome89 에서 console.warn 을 발생시킬 것이다.
  • offline.html 파일을 강제시키는 bug fix 느낌이다.

What’s new for web apps in Play

  • bubblewrap/cli 사용하여서 TWA 를 만드는 방법
  • PaymentRequest 로 PWA 와 구글 결제 연동 가능

Extending Workbox: custom strategies and plugins

  • Workbox v6 이 되면서 Lifecycle hook 과 기존 strategy 를 확장 가능하게 변경되었다.
  • 한 판을 정리해놓아 이 것만 보면 된다.

Structured data for developers

  • Dynamic structred data 가 가능하다.
1
2
3
4
5
6
7
8
9
const s = document.createElement('script');
s.setAttribute('type', 'application/ld+json');

fetch(`/api/events/structured-data?id=${id}`)
.then((r) => r.text())
.then((data) => {
s.textContent = data;
document.head.appendChild(s);
});
  • 테스트는 Rich Results Test 에서 가능하다.
  • schema-dts, react-schemaorg 패키지도 있다.

Making the web more visual with Web Stories

  • Web story 를 만드는 방법에 대해 나온다.
  • stories.googleGoogle의 웹 스토리 사용 설정하기 참조하자.
  • nws.ai, wp.stories.google, makestories.io 에서 만들거나 amp story 로 가능하다.
  • 구글 검색에도 표시되어서 언젠가는 만들어야할 것 같다.

The web ahead

The web ahead

  • Wasm: The bridge that helps from lagacy application to that better world.
  • Write your code once for all browsers.
  • 원반 언제 다 볼 수 있을까 갈수록 기본기가 중요해지는 것 같다.

블루투스 차단

블루투스 기기가 자동 연동될 때 쉘에서 직접 차단을 할 수 있다.

맥 어드레스 확인

option + shift 후에 블루투스 상태창을 클릭하면 MAC 주소(Media Access Control Address)를 알 수 있다.

주소 차단

블루투스 정보는 com.apple.Bluetooth.plist 에서 관리한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
# 설정파일을 xml 형식으로 변환한다.
sudo plutil -convert xml1 /Library/Preferences/com.apple.Bluetooth.plist

# 변환한 파일을 열어 아래 라인에 맥 어드레스를 추가한다.
sudo vi /Library/Preferences/com.apple.Bluetooth.plist
2794 <key>IgnoredDevices</key>
2795 <array>
2796 <string>47-02-87-10-0f-34</string>
2797 <string>74-3a-4e-e3-5d-68</string>
2798 </array>

# 다시 바이너리로 바꾼다.
sudo plutil -convert binary1 /Library/Preferences/com.apple.Bluetooth.plist

이제 더 이상 모르는 기기로부터의 연결을 요청받지 않아도 된다.

참조

빅서로 업데이트 후부터 알 수 없는 맥 어드레스의 기기가 자동 연결되면서 매직 키보드와 트랙패드의 연결이 유실되는 현상이 있어서 무시하는 법을 찾기 시작하였는데, 비슷한 버그가 발생하는 것 같다.

  • Big Sur Bluetooth automatically connects

DNS Cache

쿠버네티스 환경에서는 Node.js 이미지를 올릴 시 종종 아래와 같은 IP 반환 에러메세지가 보인다.
이는 서비스 연결조차 불가능하게 만들어 운영에 지장을 주었다.

1
Error: getaddrinfo EAI_AGAIN your-service

먼저 Node.js 레벨에서부터 확인을 시작하였다.

UV_THREAD_POOL_SIZE

Node.js Man 을 보면 다음과 같은 주의사항이 있다.

Though the call to dns.lookup() will be asynchronous from JavaScript’s perspective, it is implemented as a synchronous call to getaddrinfo(3) that runs on libuv’s threadpool. This can have surprising negative performance implications for some applications, see the UV_THREADPOOL_SIZE documentation for more information.

연관된 UV_HTREADPOOL_SIZE 문서는 다음과 같다.

Asynchronous system APIs are used by Node.js whenever possible, but where they do not exist, libuv’s threadpool is used to create asynchronous node APIs based on synchronous system APIs.

스레드 풀을 사용하는 API 는 다음과 같다.

  • all fs APIs, other than the file watcher APIs and those that are explicitly synchronous
  • asynchronous crypto APIs such as crypto.pbkdf2(), crypto.scrypt(), crypto.randomBytes(), crypto.randomFill(), crypto.generateKeyPair()
  • dns.lookup()
  • all zlib APIs, other than those that are explicitly synchronous

이 메소드들을 사용할 때는 병렬 요청과 부하에 신경을 써야하며 UV_THREADPOOL_SIZE 사이즈를 Node.js 기본값인 4에서 적절히 증가시켜줘야한다.

dns.lookup

dns.lookup가 비동기인척하는 동기 메소드임을 확인할 수 있었다.
보통 이 메소드를 직접 사용하는 경우는 거의 없다.

axios 등 http 연결을 하는 라이브러리에서 Hostname 을 IP 로 변경하기 위해 사용한다.

java 는 30s¹, php 는 120s²의 도메인 캐시를 기본으로 제공하지만 Node.js 에서는 그런 것이 없다.

Node.js 커뮤니티에서는 native dns lookup cache 기능이 제안 되었지만, dns.resolve4dns.resolve6 에 서버에서 반환하는 ttl 값을 사용할 수 있게 추가되어 이걸 사용하여 DNS 캐싱을 하게 권장되었다.

alpine

alpine 이미지에는 musl 을 사용하므로 다음과 같은 이슈가 발생할 수 있으나 해당 서비스는 그렇지 않았다.

  • docker-alpine#dns
  • musl#dns

kube-dns

남은 건 쿠버네티스 환경이었다. kube-dns 대신 dnsmasq 를 사용하자 제안이 있었고, DNS intermittent delays of 5s 이슈와 공식 문서의 NodeLocal DNS Cache Daemonset 으로 이 문제를 해결할 수 있어보였다.

클러스터에서 데몬셋을 통해 캐싱된 DNS 를 리졸브하는 완전한 해결책이였으나 클라우드에는 접근 권한이 없어 이슈 내용을 공유할 수 밖에 없었다.

그렇다면 어플리케이션에서 해결할 방법을 찾아야했다.

cacheable-lookup

Node.js 의 HTTP 모듈에서는 lookup 속성을 지원하며 이는 기본값으로 dns.lookup 을 사용한다.
결국 Node.js(HTTP -> dns.lookup) -> Alpine(getaddrinfo) -> K8S(socket) 의 어느 구간이라도 캐싱을 하면 되는 것이다.

dns.lookup 대신에 dns.resolve4 를 사용하며 Map 기반으로 캐시키를 관리하는 라이브러리로 Cachable Lookup 을 찾을 수 있었다.

axios 라이브러리는 lookup 속성을 지원하지 않으므로 http.globalAgent 에 다음과 같이 추가해야했다.

1
2
3
4
import CacheableLookup from "cacheable-lookup";

const cacheable = new CacheableLookup();
cacheable.install(http.globalAgent);

전역 agent 를 오염시키는 느낌이라 아예 HTTP 라이브러리를 got 으로 변경하였다.
got 에서는 dnsCache: true 를 주어 이 기능을 쉽게 활성화 할 수 있었다.

결론

  • 언어 수준에서의 DNS 캐시는 짧게나마 필요해보였다.
  • deno 에선 reqwest 모듈 위에 HTTP 를 올려놓았는데, trust-dns 란 DNS 캐시 모듈을 활성화하는 옵션은 들어가있지 않았다. 따라서 쿠버네티스 환경에서 같은 오류를 뱉을지 테스트해보고 싶다.

참조

  • What does getaddrinfo do

뉴런

  • 한 사람의 뇌에는 평균 860억 개의 뉴련이 있다.
  • 뉴런 간의 연결은 약 100~1,000조 개 정도이다.
  • 뉴런 하나가 약 7,000개 연결을 갖고있다.
  • 가지돌기: 데이터 수신
    • 새로운 패턴이 만들어지면 돌기가 새로 만들어지거나 발달하여 다른 뉴런의 축색 말단에 가까이 가게 된다.
  • 축삭: 데이터 전송, 전선
  • 축삭말단: 데이터 전달
  • 미엘린: 전선 피복
    • 축삭을 따라 흐르는 신호가 손실되지 않고 빠르게 전달되는데에 도움을 준다.
    • 잘 쓰지 않는 경로의 미엘린이 부실하거나 손상되면 뉴런에서 전달하는 신호도 약해지고 느려진다.
  • 시냅스: 뉴런 사이의 연결부
    • 전기 신호를 화학 물질로 바꿔 전달
    • 시냅스의 길이는 20~40 나노미터이며 전기 신호보다 느리긴하지만 공간이 짧아 빠른 통신 가능
    • 학습과 연습이 지속되어 해당 경로가 필요해지면 발신부에서 나오는 신경 전달 물질과 이 물질을 감지하는 수용체를 늘린다.
  • 뉴런이 새로운 것을 배운다는 것은 새로운 경로의 생성과 강화를 의미한다.

구조

뇌의 3층 구조

  • 전두엽: 감정, 운동, 지능
  • 두정엽: 공간, 감각
  • 후두엽: 시각
  • 측두엽: 언어
  • 뇌의 10% 만 사용한다는 설이 상식처럼 자리잡고 있지만, 사실 뇌의 100% 를 모두 사용하고 있다. (fMRI, EGG)
  • 뉴런이 많다고 똑똑한 것은 아니다.
  • 복잡한 일에는 많은 뉴런이 사용되어야하고, 단순한 일에는 적은 뉴런이 사용되어야 효율적인 결과가 나온다.
  • 단순한 일에 많은 뉴런을 사용하면 과적합(overfitting)이고, 복잡한 일에 적은 뉴런을 사용하면 저적합(underfitting)이 된다.

세부구조

  • 전두엽 Frontal lobe
    • 몸의 수의운동, 자세 유지
      • 일차운동영역 Primary motor area
      • 전운동영역 Premotor area
      • 보조운동영역 Supplementary motor area
    • 운동성 언어기능: 발성, 글자쓰기
      • 브로카영역 Broca area
    • 고차 뇌 기능: 동기부여, 주의집중, 계획, 도덕성
      • 이마연합영역 Frontal association area
  • 두정엽 Parietal lobe
    • 체성감각기능: 촉각, 무게, 물리적인 감각
      • 일차체성감각영역 Primary somatosensory area
      • 이차체성감각영역 Secondary somatosensory area
    • 고유감각기능
      • 자기 신체의 각 부분에 대한 위치 정보에 대한 감각
  • 측두엽 Temporal lobe
    • 청각
      • 청각피질 Auditory cortex
    • 언어의 이해
      • 베르니케 영역 Wernicke`s area
      • 베르니케 영역 이상시 환자는 소리를 들을 수 있고 말도 할 수 있으나 이해하지 못하고 의미도 모름 (실독증, 실어증)
    • 학습과 기억기능
  • 후두엽 Occipital lobe
    • 시각중추
      • 일차시각영역 Primary visual area
      • 시각연합영역 Visual association area
  • 해마 Hippocampus
    • 사실을 학습하고 기억하게 함
    • 뇌의 다른 부위로 신호를 전달하는데 중요한 신경섬유 역할
    • 시상하부의 기능을 조절
  • 뇌활 (뇌궁) Fornix
    • 해마와 시상을 연결하는 활 모양의 구조
  • 시상 Thalamus
    • 후각을 제외한 외부 운동과 감각신호를 대뇌 피질로 가기 전 중계하는 감각의 관문
    • 수면과 각성의 조절, 의식
    • 운동기능의 조절
  • 시상하부 Hypothalamus
    • 자율신경계 조절
    • 뇌하수체의 기능을 조절
    • 체온 조절, 음식물 섭취와 수분대사 조절
    • 생체 리듬 조절, 수면 주기

퍼셉트론

뉴런에서 신호를 받는 과정은 4단계로 나눌 수 있다.

  1. 다른 뉴런들에게 신호를 받기
  2. 신호의 합이 충분히 큰지 확인하고 활성화하기
  3. 신호를 먼 곳으로 전달하기
  4. 신호를 다른 뉴런에 전달하기
  • 1단계는 가지돌기에서 일어나며 신호 여러 개를 하나로 모아주는 시그마 연산과 같다.
  • 2단계는 역치를 넘는지 확인하고 활성화를 시킨다. 활성화는 1, 비활성화는 0이므로 이를 만들어주는 활성화 함수 (계단 함수)가 필요하다.
1
2
3

→ ( ∑ ) → ( 활성화함수 ) →

  • 3단계는 미엘린의 발달 여부에 따라 신호의 강도가 달라진다. 발달 정도를 나타내는 가중치인 w'를 곱해준다. 잘 발달된 경우 1에 가까운 값을 주면 된다.
1
2
3

→ ( ∑ ) → ( 활성화함수 ) → ( × w' ) →

  • 4단계는 축색 말단에서 시냅스를 통해 다른 뉴런으로 전달한다. 시냅스의 발달, 가지돌기 가시 크기, 시냅스에서의 화학물질 양, 수용체의 민감도에 따라 신호의 효율이 달라진다. 이 값을 3단계와 동일하게 가중치인 w"를 곱해준다.
1
2
3
4
5
6
7

→ ( ∑ ) → ( 활성화함수 ) → ( × w' ) → ( × w") →



→ ( ∑ ) → ( 활성화함수 ) → ( × W ) →

이 연결을 여러 개로 구성한다면

1
2
3
4
5

→ ( ∑ ) → ( 활성화함수 ) → ( × W₁ ) ↘
→ ( ∑ ) → ( 활성화함수 ) → ( × W₂ ) → ( ∑ ) → ( 활성화함수 ) → ( × W₄ ) →
→ ( ∑ ) → ( 활성화함수 ) → ( × W₃ ) ↗

이를 가운데 부분만 떼어내 퍼셉트론 모델을 만들 수 있다.

1
2
3
→ ( × W₁ ) ↘
→ ( × W₂ ) → ( ∑ ) → ( 활성화함수 ) →
→ ( × W₃ ) ↗

이 인공뉴런을 기본 단위로 지능을 갖게 되는데 여기서 학습을 통해 정해지는 값은 W 이다.
각각의 W 값이 0이되면 연결 소멸, 0보다 크면 연결 생성, 값이 커지면 연결 강화, 기존보다 작아지면 연결 약화가 된다. 학습을 통해 이 값이 최적화 되면 이후 학습한 일을 언제든지 수행 가능하다.

강화학습

  • agent: 학습하고 행동을 취하는 주체
  • environment: 환경
  • reward: 보상
  • state: 상태
  • policy: 방침, 상황을 파악해 행동하는 것
  • 강화학습이 가장 많이 쓰이는 분야는 자율주행이다.
  • 차는 에이전트이며 차도라는 환경에 나와 차선, 사고 등의 마이너스 보상 (패널티)를 받으면 방침을 수정해 도로에서 해야할 것과 하지 말아야할 것을 경험하며 방침을 수정해 결과적으로 도로를 안전하게 주행이 가능하다.
  • 보상과 패널티를 잘 조정해야 원하는 결과가 나온다.
  • 시행착오를 하면서 방침을 찾아갈 수도 있지만, Q 함수에 현재 상태와 하고싶은 일인 action을 정해 물어보고 현재 상태에서 그 일이 얼마나 유익한지를 받아 학습할 수 있다.
  • 物理エンジンくん 채널에 이런 영상들이 많다.

평생학습

  • life-long learning 또는 continual learning 으로 불린다.
  • 현재의 뉴럿넷은 새로운 업무를 하면 이전 학습은 완전히 잊어버린다.
  • 뇌는 연결이 만들어질 때 수신 뉴런에서 가지돌기 가시가 나온 후 발신 뉴런의 축산 말단에 닿아 새로운 연결을 만들어 기존 돌기의 변동성을 떨어뜨려 변하지 않게 함으로써 기존 경로를 보호한다.

EWC

  • Elastic Weight Conditioning
  • 이전 학습에서 중요한 연결은 고정해 놓고 다른 연결만을 이용해 학습하는 것
  • 고정된 숫자의 뉴런으로 무한한 일을 해내는 것은 불가능하므로 사람처럼 새로운 일을 배울 때마다 새로운 인공 뉴런을 추가해 효과적으로 처리하는 방법인 Progressive Neural Network가 고안되었다.

인지

의미

  • 기계가 사물을 인식하는 방법은 숫자로 나타내는 것이다.
  • 사물을 숫자로 나타내는 것을 벡터화 또는 인코딩, 변한된 결과를 벡터 또는 임베딩이라 한다.
  • One-hot representation: 1, 0으로 사물을 나타낸다.
    • [1, 0, 0], [0, 1, 0] 처럼 항목별로 나누기에 비효율적
  • Distributed representation: 여러 개의 요소들의 숫자를 통해 사물을 나타낸다.
    • [1, 1, 0], [0.5, 0.5, 0] 처럼 여러 요소를 저장
  • 단어를 숫자로 잘 표현된다면 사물들의 벡터에서 단어 사이의 거리는 동일해야한다.
    • MAN - WOMAN === UNCLE - AUNT
    • KING - KINGS === QUEEN - QUEENS
  • 이를 단어에서 해결하기 위해 아래 방법들이 나왔고, 이는 문장에서 자주 같이 나오는 단어들끼리 비슷하게 표현하는 방법이다.
    • Skip-gram
    • Cbow
    • Glove
  • 유사한 단어는 가깝게 모이고 다른 단어들은 멀리 떨어지며 단어 벡터에 의미가 담기게 된다.

시각

  • 망막에서 이미지가 들어오면 막대세포에서 빛, 원뿔세포에서 색 정보를 따로 받아 시신경을 통해 뇌로 전달한다.
  • LGN -> V1 -> V2 -> V3 -> LOC 순으로 통과하면서 물리적, 인지적 한계를 넘는 처리를 한다.
    • 각막의 혈관 제거
    • 수정체에서 180° 뒤집혀서 들어오는 상을 다시 처리
    • 황반의 몰려있는 원뿔세포로 인해 생기는 맹점 처리
    • 원근 처리
  • LGN 에서는 공간 및 대조, V1, V2 에서는 물체 윤곽, 색, 움직임, 마지막엔 물체 간의 그룹화, 연관, 시각에 대한 의미적 내용을 통합한다.
  • 시각은 한 번에 완성되는 것이 아니라 빛, 색, 윤곽, 깊이감 등이 점차 통합 되어 완성된다.

CNN

  • Convolutional Neural Network
  • 98년에 발표된 Lenet-5 가 그 기원이다.
    • 두 개의 convolution layer: 이미지의 다양한 특징 추출
    • 두 개의 multi-layer perception, fully connected layer: 시각 정보 통합을 통해 정보 추상화와 의미적 정보 도출
    • 하나의 결과 출력 레이어: 추상화되고 의미적인 정보를 이용해 사물 결정
  • 머핀과 치와와를 구별하기 힘들어하지만 근래에는 1,000개가 넘는 레이어를 구성해 극복하려하고 있다.

청각

  • 소리는 매질의 진동이며 소리에서 가장 필요한 정보는 무엇어디이다.
  • 진동이 고막에서 물리적인 진동으로 변하고, 귓속뼈를 통해 증폭돼 달팽이관으로 전달된다.
  • 전달된 진동은 달팽이관 내부 유모세포를 흔들어 전기신호를 발생시키고 이 신호가 청각 신경을 통해 뇌로 전달돼 소리를 듣는다.
  • 달팽이관의 초입은 고주파를 받고 중심부에서는 저주파를 받는다. 주파수별로 처리함으로 모든 청신경이 켜지지 않고 효율적인 정보처리가 가능하다.
  • 대역별로 받은 신호는 청각 신경을 통해 뇌의 1차 청각 피질에서 대역별로 처리한다.
  • 청각피질도 주파수별로 구획화되어있다.
  • '무엇’에 대한 정보는 측두엽에 위치한 베르니케 영역과 브로카 영역을 순차적으로 통과하면서 음소 모델과 언어 모델을 이용해 이해한다.
  • '어디’에 대한 정보는 두정엽에서 귀에 도착한 타이밍의 차이, 주파수에 따른 소리 크기 차이, 도플러 효과 등과 같은 방법을 통해 방향과 거리를 알아낸다.
  • 이 두 정보는 시각적 인지에 의존하므로 시각과 청각을 통합해 사용하는 멀티모달 연구가 진행 중이다.
  • 고주파보다 저주파 대역의 변화에 민감해 저주파 대역에서 많은 정보를 뽑고 고주파 대역에서 적게 뽑으면 인지적으로 적합한 정보를 추출할 수 있는데, 이는 멜 주파수 필터로 가능하다.
  • 무엇: Speech Recognition, Sound Classification
  • 어디: Sound Localization

기억

기억은 뉴런들의 연결 강화 또는 약화이다.

내포적 기억

  • Implicit Memory
  • 어떻게, 몸으로 익힌 기억
  • 기저핵과 소뇌에서 주로 담당
  • 절차 기억
    • Procedural Memory
    • 몸이 하는 기억
  • 예비화 기억
    • Priming Memory
    • 과거의 경험에 의해 특정 자극이 들어왔을 때 기억의 민감도가 증가하는 기억

명시적 기억

  • Explicit Memory
  • 구체적으로 설명 가능한 기억
  • 고등적인 일 처리인 대화, 언어, 시각적 정보, 청각적 정보 등을 처리하는데에 필수
  • 대뇌피질에 저장되며 특히 전두엽이 많이 사용된다.
  • 일화 기억
    • Episodic Memory
    • 인상적인 날을 기억한다.
  • 의미 기억
    • Semantic Memory
    • 객관적인 지식에 대한 기억

단기 기억

  • RAM
  • 작업 기억, Working Memory
  • 단순한 전기 신호로 저장

RNN

  • Recurrent Neural Network
  • 단기기억을 모방
  • 뉴럴넷이 연속으로 연결돼 있고 매 뉴럴넷마다 입력을 받는 구조
  • 1, 2, ⋯, t-1, t, t+1 의 순차 구조에서 t-1 에서 처리된 결과와 t 에서의 입력이 함께 새로운 결과를 만들어 주는 모델
  • 하나의 셀을 지날 때마다 모든 정보를 누적하므로 최근 이전 다섯 셀 이내의 정보만을 잘 기억해낸다.

LSTM

  • Long Short Term Memory
  • RNN 의 짧고 비효율적인 단기 기억을 해결하기 위해 제안
  • 정보의 컨베이어 벨트가 있고 매번 필요한 정보를 넣고 필요 없는 정보를 빼서 효율적으로 정보를 관리한다.

장기 기억

  • HDD
  • 물리적인 연결을 통해 저장
  • 정보에 따른 고유 경로가 활성화되면서 머리 속에서 떠오른다.
  • 전전두엽의 단기기억이 주로 수면 중에 해마와 편도체에서 내부 반복해 대뇌피질에 기억 관련 연결을 굳혀놓는다.

DNC

  • Differentiable Neural Computer
  • deepmind/dnc
  • 정보를 외장 메모리에 차곡차곡 저장하고 필요할 때 불러 쓰는 방식
  • Q&A에 주로 사용한다.
  • Neural Turing Machine, Memory Network 등도 비슷한 방식이다.

효율적 기억

  • 사람은 매초 4,000억 bits 즉 50GB의 정보를 쏟아받지만 필요한 정보만 선택해 실제로 2,000 bits만 사용한다.
  • 이는 부주의맹(Inattention Blindness)라 하며 우리가 정보를 2억분의 1로 필터링하는 비결이다.
  • 기계도 필요한 정보에 집중하는 Attention을 사용한다. 이 개념은 이미지 캡셔닝, 번역기에 활용된다.
    • 원문에서 문맥적 의미가 비슷한 부분에 집중해 이 정보를 중점적으로 확인한다.

Next

  • 직관과 추론: 감정
  • 심리, 도덕: 트롤리 문제
  • 원샷 학습: 한 번의 예시만으로 학습
  • 메타 학습
    • 학습법의 학습
    • MANN, Matching Network, MAML
  • 전이 학습: 비슷한 것을 배운 지식을 새로운 것을 배울 때 활용해 빠르게 학습

용어

  • 뉴럴넷: Neural Network
  • 베이지안 네트워크: Bayesian Network
  • SVM: Support Vector Machine
  • 오버피팅: overfitting, 과적합
  • MFCCs: Mel Frequency Cepstral Coefficients
  • 스펙트로그램: 주파수를 대역별로 시각화한 것
  • Memory consolidation: 기억의 응고, 장기기억화
  • 신경: Nerve 각 기관계를 연결하여 하나의 유기체로서 신체 활동의 조절과 조정을 담당하는 신경조직
  • 중추신경계: Central nervous 몸 가장 가운데에 있는 신경으로, 신경정보들을 통합, 조정하는 중앙처리장치
  • 척수: Spinal cord
  • 신경세포: Nerve cell, Neu 신경계를 이루는 구조적, 기능적 단위
  • 말초신경: Peripheral nerve 감각과 운동자극을 중추신경으로 연결하는 통로
  • 대뇌: Cerebrum 중추신경계의 중추, 운동, 감각, 언어, 기억 및 고등 정신 기능 수행
  • 시상: Thalamus 많은 신경핵들로 이루어진 대부분이 감각 신경이 지나가는 통로
  • 소뇌: Cerebellum 움직임을 제어하는데에 주요한 역할
  • 연수: Medulla oblongata 뇌간의 한 부분으로 호흡, 순환, 운동, 뇌신경 기능을 담당하는 뇌줄기의 하부구조
  • 시냅스: Synapse 두 긴셩세포가 연결되어 신호가 전달 되는 공간
  • 수막: Meninx 경질막, 거미막, 연질막으로 구성된 세층의 막으로 물리적으로 뇌와 척수를 보호
  • 대뇌피질: Cerebral cortex 뇌 바깥쪽 회백질, 신경세포가 모여있음
  • 백질: White matter 뇌 안쪽, 신경 세포에서 나가는 회로가 모여있음
  • 뇌신경: Cranial nerve 신경이 뇌에서 나가면 뇌신경,척수에서 나가면 척수신경

활성 객체

  • Activation Object
  • 함수가 호출되면 활성 객체가 만들어진다. 이는 숨겨진 데이터 구조로 호출된 함수의 반환 주소와 실행에 필요한 정보를 저장하고 호출된 함수에 바인딩한다.
  • C와 같은 언어에서는 활성 객체가 스택에 저장되고 함수가 종료되고 반환되면 스택에서 제거하지만 자바스크립트에서는 활성 객체를 다른 객체와 동일하게 힙에 저장한다.
  • 함수가 종료된다고 활성 객체를 자동으로 비활성화하지 않는다.
  • 활성 객체는 해당 객체에 대한 참조가 있는 한 계속 살아있으며 다른 객체와 마찬가지로 가비지 컬렉터에 의해 처리된다.

활성 객체 내부 정보

  • 함수 객체에 대한 참조
  • caller 함수의 활성 객체에 대한 참조 (이 참조는 return 이 실행흐름을 caller 로 돌릴 때 사용된다.)
  • 함수 호출이 끝난 뒤 실행을 재개하기 위해 필요한 정보 (대개 함수 호출문 바로 다음 명령어의 주소)
  • 파라미터에 의해 초기화되는 함수 매개변수
  • undefined로 초기화된 함수 변수들
  • 함수가 복잡한 표현식을 계산하기 위해 임시로 사용하는 변수들
  • 함수 객체가 메소드로서 호출되었을 때 사용할 수 있는 this 참조

함수

함수 객체

  • 함수 객체는 다른 일반적인 객체와 마찬가지로 속성을 갖고 있으며 변경 가능하다.
  • 공유되는 변경 가능한 함수 객체가 취약점으로 사용될 수 있다. (프로토타입 오염 등)
  • 함수 객체는 prototype 이란 속성을 가지고 있고, prototype은 Object.prototype에 대한 delegation link와 constructor 속성을 가진 객체의 참조를 가진다.
    • constructor 속성은 함수 객체에 대한 역참조를 가지고 있다.
  • 함수 객체는 Function.prototype 에 대한 델리게이션 링크를 가지고 있어 applycall을 상속받는다.
  • 함수 객체에는 두 가지 숨겨진 속성이 있는데 다음과 같다.
    • 함수 실행 코드에 대한 참조
    • 함수 객체가 생성되는 시점에 활성화된 활성 객체에 대한 참조 클로져를 사용할 수 있게 해준다.

함수 호출

  • 파라미터 표현식 계산
  • 함수의 매개변수와 변수를 저장할 수 있는 충분한 크기의 활성 객체 생성
  • 호출된 함수 객체에 대한 참조를 새로운 활성 객체에 저장
  • 전달받은 파라미터를 새로운 활성 객체의 매개변수에 저장
    • 빠진 파라미터는 undefined로 간주, 남는 파라미터는 버림
  • 활성 객체의 모든 변수 값을 undefined로 할당
  • 함수 호출 명령어의 바로 다음 명령어를 활성 객체의 next instruction 필드 값으로 할당
  • 새로운 활성 객체의 caller 필드 값에 현재 활성 객체를 할당
    • 실제 호출 스택이 아닌 활성 객체의 연결된 목록이다.
  • 새로운 활성 객체를 현재 활성 객체로 지정
  • 호출된 함수 실행

꼬리 재귀

  • 파라미터 표현식 계산
  • 현재 활성 객체가 충분히 크다면 (일반적으로 충분히 큼)
    • 현재 활성 객체를 새로운 활성 객체로 사용
  • 아닌 경우
    • 함수 매개변수와 변수를 저장할 수 있는 충분한 크기의 활성 객체 생성
    • 새로운 활성 객체의 caller 필드 값에 현재 활성 객체 저장
    • 새로운 활성 객체를 현재 활성 객체로 지정
  • 전달받은 파라미터를 새로운 활성 객체의 매개변수에 저장
    • 빠진 파라미터는 undefined로 간주, 남는 파라미터는 버림
  • 활성 객체의 모든 변수 값을 undefined로 지정
  • 호출된 함수를 실행

활성 객체를 재사용하므로 재귀 함수 호출을 반복문만큼 빠르게 처리 가능하다.
try 블록의 꼬리 호출은 최적화되지 않는다.

  • try는 실행 흐름을 caller의 catch문으로 변경해야하는데, 이러면 활성 객체가 최적화 될 수 없음
  • 내부에서 새로운 변수를 가지고 있다면 활성 객체가 최적화 될 수 없음

꼬리 호출 예시

1
2
3
4
5
6
; recursion
call func
return

; tail recursion
jump func
1
2
3
4
5
6
7
8
9
(function loop() {
// do some
if (done) {
return;
}

// do more
return loop();
}());
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
// ❌
function factorial(n) {
if (n < 2) {
return 1;
}

return n * factorial(n - 1);
}

// ❌
const value = func();
return value;

// ❌
return function () {};

// ✔️
function factorial(n, result = 1) {
if (n < 2) {
return result;
}

return factorial(n - 1, n * result);
}
// ✔️
return (function () {}());

문자열

0~65535 사이의 크기를 가지는 Immutable unsigned int16 배열이다.

유니코드

  • 모든 언어를 16bit 로 표시하는 것이 목적이였으나 후에 21bit로 바뀌었다.
  • 자바스크립트는 16비트로 표현하려던 시절에 설계되어서 간극이 있다.
  • 자바스크립트는 문자를 받아 코드 유닛코드 포인트 둘로 나눈다.
  • 유니코드는 1,114,112개의 코드 포인트를 정의하고 있으며 한 평면당 65,536 코드포인트, 총 17개의 평면으로 나뉜다.
  • 원래의 평면은 BMP (Basic Multilingual Plane)라고 불리우며 나머지 16개의 평면은 나중에 추가되었다.
  • BMP에 있는 코드 포인트는 하나의 코드 유닛으로 식별가능하므로 사용이 쉽지만 그 외의 문자는 써로게이트 페어를 사용해야한다.

써로게이트 페어

  • 써로게이트 페어는 두 개의 코드 유닛으로 구성되는데, 자바스크립트에서는 1024개의 상위 써로게이트 코드유닛과 1024개의 하위 써로게이트 코드유닛이 있다.
    • 상위 써로게이트 코드유닛: 0xD800 ~ 0xDBFF
    • 하위 써로게이트 코드유닛: 0xDC00 ~ 0xDFFF
  • 계산식은 SurrogatePair = ((High - 0xD800) * 0x400) + (Low - 0xDC00) + 0x10000;이다.
  • 따라서 😃는 0x1F603 이며 D83D + DE03 이다.
    • "\uD83D\uDE03" === "\u{1F603}"
    • String.fromCharCode(55357, 56835) === String.fromCodePoint(128515);
  • 비트 연산자로도 계산이 가능한데, 코드 포인트에서 0x10000 (65,536)을 빼고 상위 10bit에 0xD800 을 더한 값과 하위 10bit에 0xDC00을 더하면 된다.
  • 비트 연산의 계산식은 다음과 같다.
1
2
3
4
SurrogatePair = SurrogatePair - 0x10000;
High = 0xD800 + (SurrogatePair >> 10);
Low = 0xDC00 + (SurrogatePair & 0x3FF);
return String.fromCharCode(High, Low);

전세계 사람들과 대화하기 위해 필요한 문자 대부분을 BMP에서 찾을 수 있지만, 유니코드 프로그램을 만들 경우에는 써로게이트 페어를 항상 염두해야한다. Unicode-aware 로 적혀지는 듯 하다.

정규화

  • 유니코드 문자에서는 악센트나 기타 문자를 수정할 수 있는 조합 및 수정 문자, 쓰기 방향 제어 문자 등이 포함되어있다.
  • 동일한 문자처럼 보이더라도 실제로는 다를 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 예시 1
"S\u0307" === "Ṡ"
"S\u0307\u0323" === "Ṩ"

let s1 = "S\u0307\u0323"; // Ṩ, S + 윗 점 + 아랫 점
let s2 = "S\u0323\u0307"; // Ṩ, S + 아랫 점 + 윗 점

s1 === s2; // false
s1.normalize() === s2.normalize(); // true

// 예시 2
let umlaut = "\u00FC"; // ü
let u_diaeresis = "u\u0308"; // u + 분음부호 ü

umlaut === u_diaeresis; // false
umlaut.normalize() === u_diaeresis.normalize(); // true

항상 WeakMap, WeakSet을 어떤 방식으로 쓸까 많이 고민되었다.
참조가 없는 경우 메모리를 반환하는데에 이점이 있는데, 여러 활용방안이 있었다.

WeakMap

Cache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 📁 cache.js
let cache = new WeakMap();

// calculate and remember the result
function process(obj) {
if (!cache.has(obj)) {
let result = /* calculate the result for */ obj;

cache.set(obj, result);
}

return cache.get(obj);
}

// 📁 main.js
let obj = {
/* some object */
};

let result1 = process(obj);
let result2 = process(obj);

// ...later, when the object is not needed any more:
obj = null;

Sealer

값을 봉인하고 box를 반환해 box object 전체가 와야만 내부값을 알 수 있게 해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function sealerFactory() {
const weakMap = new WeakMap();
return {
sealer(obj) {
const box = Object.freeze(Object.create(null));
weakMap.set(box, object);
return box;
},

unsealer(box) {
return weakMap.get(box);
},
};
}

WeakSet

Circular references

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
// Execute a callback on everything stored inside an object
function execRecursively(fn, subject, refs = null) {
if (!refs) {
refs = new WeakSet();
}

// Avoid infinite recursion
if (refs.has(subject)) {
return;
}

fn(subject);
if (typeof subject === 'object') {
refs.add(subject);
for (let key in subject) {
execRecursively(fn, subject[key], refs);
}
}
}

const foo = {
foo: 'Foo',
bar: {
bar: 'Bar',
},
};

foo.bar.baz = foo; // Circular reference!
execRecursively((obj) => console.log(obj), foo);

참조

  • MDN WeakSet
  • The Modern JavaScript Tutorial - WeakMap
  • How JavaScript Works

qs 모듈과 querystring 모듈 비교

대부분의 포스트에서는 qs.stringifyqs.parse 는 확장된 쿼리스트링 변환이 가능하다고 나온다.
기본 모듈인 querystring.stringify, querystring.parse 대비 쿼리스트링의 중첩을 가능하게한 모듈인데, 다른 변경점이 하나 더 있다.

rfc3986

rfc3986!, ', (, ), * 문자에 대해 추가적으로 엔티티화 한다.
자바스크립트에서 이 스펙을 준수하려면 다음과 같이 encodeURIComponent 함수를 합성해야한다.
노드에서도 이는 마찬가지이며 querystring 모듈은 이 스펙을 준수하지 않았다.

1
2
3
4
5
function fixedEncodeURIComponent(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
return '%' + c.charCodeAt(0).toString(16);
});
}

하지만 qs 모듈은 이 스펙을 기본으로 준수한다.

RFC3986 used as default option and encodes ’ ’ to %20 which is backward compatible.
In the same time, output can be stringified as per RFC1738 with ’ ’ equal to ‘+’.

따라서 언어 간 호환성 및 표준을 맞추기 위해는 qs 모듈을 사용하는 것이 마음이 편하다.

참조

  • ljharb/qs
  • MDN encodeURIComponent
  • Node.js querystring