1
1
# clean-code-javascript
2
2
3
- * Updated date 2017.1.16
4
- * 현재 원문의 [ 00a5068 ] ( https://github.com/ryanmcdermott/clean-code-javascript/commit/00a50681b1c0a24e9c8c08d8872fee9c76da17d5 )
3
+ * Updated date 2017.1.31
4
+ * 현재 원문의 [ ac6a51d ] ( https://github.com/ryanmcdermott/clean-code-javascript/commit/ac6a51d640064ec4ac6cada7af477df8967dd193 )
5
5
까지 반영되어 있습니다.
6
6
7
7
## 읽기에 앞서
24
24
5 . [ 클래스(Classes)] ( #클래스classes )
25
25
6 . [ 테스트(Testing)] ( #테스트testing )
26
26
7 . [ 동시성(Concurrency)] ( #동시성concurrency )
27
- 8 . [ 에러 처리(Error Handling)] ( #에러-처리error-Handling )
27
+ 8 . [ 에러 처리(Error Handling)] ( #에러-처리error-handling )
28
28
9 . [ 포맷팅(Formatting)] ( #포맷팅formatting )
29
29
10 . [ 주석(Comments)] ( #주석comments )
30
+ 11 . [ 번역(Translation)] ( #번역translation )
30
31
31
32
## 소개(Introduction)
32
33
![ 코드를 읽을 때 소리 지르는 숫자로 소프트웨어 품질을 추정하는 유머 사진] ( http://www.osnews.com/images/comics/wtfm.jpg )
@@ -107,7 +108,7 @@ saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeReg
107
108
``` javascript
108
109
const address = ' One Infinite Loop, Cupertino 95014' ;
109
110
const cityZipCodeRegex = / ^ [^ ,\\ ] + [,\\ \s ] + (. +? )\s * (\d {5} )? $ / ;
110
- const [, city , zipCode ] = address .match (cityZipCodeRegex);
111
+ const [, city , zipCode ] = address .match (cityZipCodeRegex) || [] ;
111
112
saveCityZipCode (city, zipCode);
112
113
```
113
114
** [ ⬆ 상단으로] ( #목차 ) **
@@ -173,6 +174,8 @@ function paintCar(car) {
173
174
** [ ⬆ 상단으로] ( #목차 ) **
174
175
175
176
### 기본 매개변수가 short circuiting 트릭이나 조건문 보다 깔끔합니다
177
+ 기본 매개변수는 종종 short circuiting 트릭보다 깔끔합니다. 기본 매개변수는 매개변수가 ` undefined ` 일때만
178
+ 적용됩니다. ` '' ` , ` "" ` , ` false ` , ` null ` , ` 0 ` , ` NaN ` 같은 ` falsy ` 한 값들은 기본 매개변수가 적용되지 않습니다.
176
179
177
180
** 안좋은 예:**
178
181
``` javascript
@@ -195,14 +198,25 @@ function createMicrobrewery(name = 'Hipster Brew Co.') {
195
198
매개변수의 개수를 제한 하는 것은 함수 테스팅을 쉽게 만들어 주기 때문에 중요합니다. 만약 매개변수가 3개 이상일 경우엔
196
199
테스트 해야하는 경우의 수가 많아지고 각기 다른 인수들로 여러 사례들을 테스트 해야합니다.
197
200
198
- 인자가 한 개도 없는 것이 이상적인 케이스이고 1개나 2개 정도의 인자는 괜찮습니다.
199
- 하지만 3개는 피해야하고 그 이상이라면 줄이는 것이 좋습니다.
201
+ 1개나 2개의 인자를 가지고 있는 것이 가장 이상적인 케이스입니다.
202
+ 그리고 3개의 인자는 가능한 피해야합니다. 그것보다 더 많다면 통합되어야합니다.
200
203
만약 당신이 2개 이상의 인자를 가진 함수를 사용한다면 그 함수에게 너무 많은 역할을 하게 만든 것입니다.
201
204
그렇지 않은 경우라면 대부분의 경우 상위 객체는 1개의 인자만으로 충분합니다.
202
205
203
206
JavaScript를 사용할 때 많은 보일러플레이트 없이 바로 객체를 만들 수 있습니다.
204
207
그러므로 당신이 만약 많은 인자들을 사용해야 한다면 객체를 이용할 수 있습니다.
205
208
209
+ 함수가 기대하는 속성을 좀더 명확히 하기 위해서 es6의 비구조화(destructuring) 구문을 사용할 수 있고
210
+ 이 구문에는 몇가지 장점이 있습니다.
211
+
212
+ 1 . 어떤 사람이 그 함수의 시그니쳐(인자의 타입, 반환되는 값의 타입 등)를 볼 때 어떤 속성이 사용되는지
213
+ 즉시 알 수 있습니다.
214
+ 2 . 또한 비구조화는 함수에 전달된 인수 객체의 지정된 기본타입 값을 복제하며 이는 사이드이펙트가
215
+ 일어나는 것을 방지합니다. 참고로 인수 객체로부터 비구조화된 객체와 배열은 복제되지 않습니다.
216
+ 3 . Linter를 사용하면 사용하지않는 인자에 대해 경고해주거나 비구조화 없이 코드를 짤 수 없게 할 수 있습니다.
217
+
218
+
219
+
206
220
** 안좋은 예:**
207
221
``` javascript
208
222
function createMenu (title , body , buttonText , cancellable ) {
@@ -212,17 +226,16 @@ function createMenu(title, body, buttonText, cancellable) {
212
226
213
227
** 좋은 예:**
214
228
``` javascript
215
- const menuConfig = {
229
+ function createMenu ({ title, body, buttonText, cancellable }) {
230
+ // ...
231
+ }
232
+
233
+ createMenu ({
216
234
title: ' Foo' ,
217
235
body: ' Bar' ,
218
236
buttonText: ' Baz' ,
219
237
cancellable: true
220
- };
221
-
222
- function createMenu (config ) {
223
- ...
224
- }
225
-
238
+ });
226
239
```
227
240
** [ ⬆ 상단으로] ( #목차 ) **
228
241
@@ -403,8 +416,8 @@ function showManagerList(managers) {
403
416
404
417
** 좋은 예:**
405
418
``` javascript
406
- function showList (employees ) {
407
- employees .forEach (employee => {
419
+ function showEmployeeList (employees ) {
420
+ employees .forEach (( employee ) => {
408
421
const expectedSalary = employee .calculateExpectedSalary ();
409
422
const experience = employee .getExperience ();
410
423
@@ -500,7 +513,7 @@ function createTempFile(name) {
500
513
```
501
514
** [ ⬆ 상단으로] ( #목차 ) **
502
515
503
- ### 사이드 이펙트를 피하세요
516
+ ### 사이드 이펙트를 피하세요 (part 1)
504
517
함수는 값을 받아서 어떤 일을 하거나 값을 리턴할때 사이드 이팩트를 만들어냅니다.
505
518
사이드 이팩트는 파일에 쓰여질 수도 있고, 전역 변수를 수정할 수 있으며, 실수로 모든 돈을 다른 사람에게 보낼 수도 있습니다.
506
519
@@ -541,6 +554,44 @@ console.log(newName); // ['Ryan', 'McDermott'];
541
554
```
542
555
** [ ⬆ 상단으로] ( #목차 ) **
543
556
557
+ ### 사이드 이펙트를 피하세요 (part 2)
558
+ 자바스크립트에서는 기본타입 자료형은 값을 전달하고 객체와 배열은 참조를 전달합니다.
559
+ 객체와 배열인 경우를 한번 살펴봅시다. 우리가 만든 함수는 쇼핑카트 배열에 변화를 주며
560
+ 이 변화는 구매목록에 어떤 상품을 추가하는 기능 같은 것을 말합니다.
561
+ 만약 ` 장바구니 ` 배열을 사용하는 어느 다른 함수가 있다면 이러한 추가에 영향을 받습니다.
562
+ 이것은 좋을 수도 있지만, 안좋을 수도 있습니다. 안좋은 예를 한번 상상해봅시다.
563
+
564
+ 유저가 구매하기 버튼을 눌러 ` 구매 ` 함수를 호출합니다. 이는 네트워크 요청을 생성하고 서버에 ` 장바구니 ` 배열을 보냅니다.
565
+ 하지만 네트워크 연결이 좋지않아서 ` 구매 ` 함수는 다시한번 네트워크 요청을 보내야 하는 상황이 생겼습니다.
566
+ 이때, 사용자가 네트워크 요청이 시작되기 전에 실수로 원하지 않는 상품의 "장바구니에 추가" 버튼을 실수로 클릭하면 어떻게됩니까?
567
+ 이런 일이 발생하고 네트워크 요청이 시작되면 ` 장바구니에 추가 ` 함수 때문에 실수로 변경된 ` 장바구니 ` 배열을 서버에 보내게 됩니다.
568
+
569
+ 가장 좋은 방법은 ` 장바구니에 추가 ` 는 항상 ` 장바구니 ` 배열을 복제하여 수정하고 복제본을 반환하는 것입니다.
570
+ 이렇게하면 장바구니 참조를 보유하고있는 다른 함수가 다른 변경 사항의 영향을 받지 않게됩니다.
571
+
572
+ 이 접근법에대해 말하고 싶은 것이 두가지 있습니다.
573
+
574
+ 1 . 실제로 입력된 객체를 수정하고 싶은 경우가 있을 수 있지만 이러한 예제를 생각해보고 적용해보면 그런 경우는
575
+ 거의 없다는 것을 깨달을 수 있습니다. 그리고 대부분의 것들이 사이드 이펙트 없이 리팩토링 될 수 있습니다.
576
+ 2 . 큰 객체를 복제하는 것은 성능 측면에서 값이 매우 비쌉니다. 운좋게도 이런게 큰 문제가 되지는 않습니다.
577
+ 왜냐하면 이러한 프로그래밍 접근법을 가능하게해줄 [ 좋은 라이브러리] ( https://facebook.github.io/immutable-js/ ) 가 있기 때문입니다.
578
+ 이는 객체와 배열을 수동으로 복제하는 것처럼 메모리 집약적이지 않게 해주고 빠르게 복제해줍니다.
579
+
580
+ ** Bad:**
581
+ ``` javascript
582
+ const addItemToCart = (cart , item ) => {
583
+ cart .push ({ item, date: Date .now () });
584
+ };
585
+ ```
586
+
587
+ ** Good:**
588
+ ``` javascript
589
+ const addItemToCart = (cart , item ) => {
590
+ return [... cart, { item, date : Date .now () }];
591
+ };
592
+ ```
593
+ ** [ ⬆ 상단으로] ( #목차 ) **
594
+
544
595
### 전역 함수를 사용하지 마세요
545
596
전역 환경을 사용하는 것은 JavaScript에서 나쁜 관행입니다. 왜냐하면 다른 라이브러리들과의 충돌이 일어날 수 있고,
546
597
당신의 API를 쓰는 유저들은 운영환경에서 예외가 발생하기 전까지는 문제를 인지하지 못할 것이기 때문입니다. 예제를 하나 생각해봅시다.
@@ -617,7 +668,7 @@ const programmerOutput = [
617
668
618
669
const totalOutput = programmerOutput
619
670
.map (programmer => programmer .linesOfCode )
620
- .reduce ((acc , linesOfCode ) => acc + linesOfCode, 0 );
671
+ .reduce ((acc , linesOfCode ) => acc + linesOfCode, INITIAL_VALUE );
621
672
```
622
673
** [ ⬆ 상단으로] ( #목차 ) **
623
674
@@ -729,7 +780,7 @@ JavaScript는 타입이 정해져있지 않습니다. 이는 당신의 함수가
729
780
``` javascript
730
781
function travelToTexas (vehicle ) {
731
782
if (vehicle instanceof Bicycle) {
732
- vehicle .peddle (this .currentLocation , new Location (' texas' ));
783
+ vehicle .pedal (this .currentLocation , new Location (' texas' ));
733
784
} else if (vehicle instanceof Car) {
734
785
vehicle .drive (this .currentLocation , new Location (' texas' ));
735
786
}
@@ -863,7 +914,7 @@ class BankAccount {
863
914
864
915
// getter/setter를 정의할 때 `get`, `set` 같은 접두사가 필요하지 않습니다.
865
916
set balance (amount ) {
866
- if (verifyIfAmountCanBeSetted (amount)) {
917
+ if (this . verifyIfAmountCanBeSetted (amount)) {
867
918
this ._balance = amount;
868
919
}
869
920
}
@@ -899,7 +950,7 @@ const Employee = function(name) {
899
950
900
951
Employee .prototype .getName = function getName () {
901
952
return this .name ;
902
- }
953
+ };
903
954
904
955
const employee = new Employee (' John Doe' );
905
956
console .log (` Employee name: ${ employee .getName ()} ` ); // Employee name: John Doe
@@ -909,13 +960,15 @@ console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined
909
960
910
961
** 좋은 예:**
911
962
``` javascript
912
- const Employee = function (name ) {
913
- this .getName = function getName () {
914
- return name;
963
+ function makeEmployee (name ) {
964
+ return {
965
+ getName () {
966
+ return name;
967
+ },
915
968
};
916
- };
969
+ }
917
970
918
- const employee = new Employee (' John Doe' );
971
+ const employee = makeEmployee (' John Doe' );
919
972
console .log (` Employee name: ${ employee .getName ()} ` ); // Employee name: John Doe
920
973
delete employee .name ;
921
974
console .log (` Employee name: ${ employee .getName ()} ` ); // Employee name: John Doe
@@ -1139,17 +1192,9 @@ class Shape {
1139
1192
}
1140
1193
1141
1194
class Rectangle extends Shape {
1142
- constructor () {
1195
+ constructor (width , height ) {
1143
1196
super ();
1144
- this .width = 0 ;
1145
- this .height = 0 ;
1146
- }
1147
-
1148
- setWidth (width ) {
1149
1197
this .width = width;
1150
- }
1151
-
1152
- setHeight (height ) {
1153
1198
this .height = height;
1154
1199
}
1155
1200
@@ -1159,12 +1204,8 @@ class Rectangle extends Shape {
1159
1204
}
1160
1205
1161
1206
class Square extends Shape {
1162
- constructor () {
1207
+ constructor (length ) {
1163
1208
super ();
1164
- this .length = 0 ;
1165
- }
1166
-
1167
- setLength (length ) {
1168
1209
this .length = length;
1169
1210
}
1170
1211
@@ -1175,21 +1216,12 @@ class Square extends Shape {
1175
1216
1176
1217
function renderLargeShapes (shapes ) {
1177
1218
shapes .forEach ((shape ) => {
1178
- switch (shape .constructor .name ) {
1179
- case ' Square' :
1180
- shape .setLength (5 );
1181
- break ;
1182
- case ' Rectangle' :
1183
- shape .setWidth (4 );
1184
- shape .setHeight (5 );
1185
- }
1186
-
1187
- const area = shape .getArea ();
1188
- shape .render (area);
1189
- });
1190
- }
1219
+ const area = shape .getArea ();
1220
+ shape .render (area);
1221
+ });
1222
+ }
1191
1223
1192
- const shapes = [new Rectangle (), new Rectangle (), new Square ()];
1224
+ const shapes = [new Rectangle (4 , 5 ), new Rectangle (4 , 5 ), new Square (5 )];
1193
1225
renderLargeShapes (shapes);
1194
1226
```
1195
1227
** [ ⬆ 상단으로] ( #목차 ) **
@@ -1520,7 +1552,7 @@ Gang of four의 [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Pattern
1520
1552
"그럼 대체 상속을 언제 사용해야 되는 건가요?"라고 물어 볼 수 있습니다. 이건 당신이 직면한 문제 상황에 달려있지만
1521
1553
조합보다 상속을 쓰는게 더 좋을 만한 예시를 몇 개 들어 보겠습니다.
1522
1554
1523
- 1 . 당신의 상속관계가 "is-a" 관계가 아니라 "has-a" 관계일 때 (동물->사람 vs. 유저->유저정보)
1555
+ 1 . 당신의 상속관계가 "is-a" 관계가 아니라 "has-a" 관계일 때 (사람->동물 vs. 유저->유저정보)
1524
1556
2 . 기반 클래스의 코드를 다시 사용할 수 있을 때 (인간은 모든 동물처럼 움직일 수 있습니다.)
1525
1557
3 . 기반 클래스를 수정하여 파생된 클래스 모두를 수정하고 싶을 때 (이동시 모든 동물이 소비하는 칼로리를 변경하고 싶을 때)
1526
1558
@@ -1596,7 +1628,7 @@ describe('MakeMomentJSGreatAgain', () => {
1596
1628
1597
1629
date = new MakeMomentJSGreatAgain (' 1/1/2015' );
1598
1630
date .addDays (30 );
1599
- date . shouldEqual (' 1/31/2015' );
1631
+ assert . equal (' 1/31/2015' , date );
1600
1632
1601
1633
date = new MakeMomentJSGreatAgain (' 2/1/2016' );
1602
1634
date .addDays (28 );
@@ -1617,7 +1649,7 @@ describe('MakeMomentJSGreatAgain', () => {
1617
1649
it (' handles 30-day months' , () => {
1618
1650
const date = new MakeMomentJSGreatAgain (' 1/1/2015' );
1619
1651
date .addDays (30 );
1620
- date . shouldEqual (' 1/31/2015' );
1652
+ assert . equal (' 1/31/2015' , date );
1621
1653
});
1622
1654
1623
1655
it (' handles leap year' , () => {
@@ -2027,3 +2059,16 @@ const actions = function() {
2027
2059
};
2028
2060
```
2029
2061
** [ ⬆ 상단으로] ( #목차 ) **
2062
+
2063
+ ## 번역(Translation)
2064
+
2065
+ 다른 언어로도 읽을 수 있습니다:
2066
+
2067
+ - ![ br] ( https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png ) ** Brazilian Portuguese** : [ fesnt/clean-code-javascript] ( https://github.com/fesnt/clean-code-javascript )
2068
+ - ![ cn] ( https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png ) ** Chinese** : [ alivebao/clean-code-js] ( https://github.com/alivebao/clean-code-js )
2069
+ - ![ de] ( https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png ) ** German** : [ marcbruederlin/clean-code-javascript] ( https://github.com/marcbruederlin/clean-code-javascript )
2070
+ - ![ kr] ( https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png ) ** Korean** : [ qkraudghgh/clean-code-javascript-ko] ( https://github.com/qkraudghgh/clean-code-javascript-ko )
2071
+ - ![ ru] ( https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png ) ** Russian** : [ BoryaMogila/clean-code-javascript-ru/] ( https://github.com/BoryaMogila/clean-code-javascript-ru/ )
2072
+
2073
+ ** [ ⬆ 상단으로] ( #목차 ) **
2074
+
0 commit comments