Single Quotes 사용: (작성자 생각: JSON과 구별하기 위함인가?🤷)
일반적인 문자열 리터럴에서는 싱글 쿼트('
)를 사용하는 것이 권장된다. 더블 쿼트("
)도 사용할 수 있지만, 싱글 쿼트를 사용하면 일반적인 코드 스타일과 일관성을 유지할 수 있다.
Before
const greeting = "Hello, world!";
After
const greeting = 'Hello, world!';
문자열에 싱글 쿼트가 포함되어 있는 경우, 이스케이프(\'
) 대신 템플릿 리터럴을 사용하는 것이 가독성을 높인다.
Before
const message = 'It\'s a beautiful day!';
After
const message = `It's a beautiful day!`;
템플릿 문자열 사용:
템플릿 문자열은 백틱(`)으로 감싸고, 문자열 내에서 동적 데이터 삽입과 여러 줄 문자열 작성을 간단히 처리할 수 있다.
복잡한 문자열 연결 작업은 템플릿 문자열로 대체하는 것이 더 명확하고 가독성이 좋다.
Before
const message = 'Hello, ' + name + '! You have ' + count + ' messages.';
After
const message = `Hello, ${name}! You have ${count} messages.`;
줄 바꿈(Line Continuation) 금지:
문자열을 백슬래시(\
)를 사용해 줄 바꿈하면 코드의 가독성이 떨어지고, 공백과 관련된 에러가 발생할 가능성이 있다. 예를 들어, 백슬래시 뒤에 공백이 있으면 에러를 초래한다.
줄 바꿈이 필요한 경우, 문자열 연결(+
)을 사용하거나 템플릿 문자열을 활용한다.
Before
const longString = 'This is a very long string that spans multiple lines. \
It can cause tricky errors if there is any trailing whitespace.';
After
// 문자열 연결
const longString = 'This is a very long string ' +
'that spans multiple lines without issues.';
// 템플릿 리터럴 사용
const longString = `This is a very long string
that spans multiple lines cleanly.`;
// 템플릿 문자열로 여러 줄 처리
function arithmetic(a, b) {
return `Here is a table of arithmetic operations:
${a} + ${b} = ${a + b}
${a} - ${b} = ${a - b}
${a} * ${b} = ${a * b}
${a} / ${b} = ${a / b}`;
}
숫자 리터럴 표기법:
JavaScript에서는 숫자를 다양한 진법(Decimal, Hexadecimal, Octal, Binary)으로 표현할 수 있다. 숫자 리터럴의 접두사는 소문자로 통일해야 한다.
진법 | 표기법 예시 | 설명 |
---|---|---|
Decimal | 42 | 10진수 (기본) |
Hex | 0x2a | 16진수, 접두사 0x |
Octal | 0o52 | 8진수, 접두사 0o |
Binary | 0b101010 | 2진수, 접두사 0b |
const decimal = 42; // 10진수
const hex = 0x2a; // 16진수
const octal = 0o52; // 8진수
const binary = 0b101010; // 2진수
선행 0 금지:
숫자 리터럴에 선행 0(leading zero)를 사용하는 경우 혼란을 초래할 수 있다. 예전에는 선행 0이 있는 숫자가 8진수로 해석되었기 때문이다(ECMAScript 5 이전).
ECMAScript 6 이상에서는 선행 0을 사용하는 숫자 리터럴은 문법 오류로 간주된다. 따라서 선행 0은 반드시 0x
, 0o
, 0b
와 같이 올바른 접두사가 뒤따라야 한다.
Before
// 예전 JavaScript에서 선행 0은 8진수로 해석되었다.
const octal = 052; // 42 (8진수)
After
// 올바른 8진수 표현
const octal = 0o52;
암시적 강제 변환(Implicit coercion) 지양하기:
암시적 강제 변환은 코드의 의도를 불분명하게 만들고, 예상치 못한 오류를 초래할 수 있다.
Before
if (!!foo) {...} // BAD:불필요한 암시적 강제 변환
while (!!foo) {...}
After
if (foo) {...} // GOOD: 간결하게 작성
while (foo) {...}
문자열 명시적 변환에 String()
또는 Boolean()
함수를 사용하기:
const bool = Boolean(false); // false
const str = String(123); // "123"
const bool2 = !!str; // true
const str2 = `result: ${bool2}`; // "result: true"
!!
연산자를 사용하여 값을 명시적으로 boolean으로 변환열거형 값을 불리언으로 변환하지 않기:
열거형(Enum)은 기본적으로 숫자 값과 매핑되며, 첫 번째 값은 0
으로 할당된다.
Boolean(enumValue)
나 !!enumValue
를 사용하면:
0
)은 falsy로 평가Before
enum SupportLevel {
NONE,
BASIC,
ADVANCED,
}
const level: SupportLevel = SupportLevel.NONE;
let enabled = Boolean(level); // 잘못된 방식: `NONE`이 `false`로 평가됨
After
enum SupportLevel {
NONE,
BASIC,
ADVANCED,
}
const level: SupportLevel = SupportLevel.NONE;
let enabled = level !== SupportLevel.NONE; // 명시적 비교
숫자형 변환에 Number() 사용하기:
숫자 값을 파싱할 때는 Number()
를 사용하고, 결과가 유효한지 명시적으로 확인해야 한다:
isNaN
: 숫자가 NaN인지 확인isFinite
: 숫자가 유한한 값인지 확인Before
const x = +y; // 가독성이 낮고, 실수하기 쉬움
const n = parseInt(someString, 10); // 에러 발생 가능
const f = parseFloat(someString); // 에러 발생 가능
After
let f = Number(someString); // 숫자로 변환
if (!isFinite(f)) throw new Error('Invalid number'); // 유효성 확인
if (isNaN(f)) handleError(); // NaN인지 확인
f = Math.floor(f); // 정수로 변환
Number('')
,Number(' ')
,Number('\t')
는NaN
대신0
을 반환한다.
Number('Infinity')
,Number('-Infinity')
는Infinity
와-Infinity
를 반환한다.
지수 표기법, 예를들어Number('1e+309')
,Number('-1e+309')
은 무한대로 오버플로될 수 있다. 이러한 경우 특별한 처리가 필요할 수 있다.
모든 제어 흐름문(if, else, for, while, 등)은 항상 중괄호로 코드 블록을 감싸야 한다.
단, if 문이 한 줄로 끝나는 경우에는 중괄호 생략이 허용된다.
Before
if (x)
doSomething(); // 중괄호 없이 여러 줄 코드 작성 시 가독성 문제 발생
After
if (x) {
doSomething();
}
for (let i = 0; i < 10; i++) {
console.log(i);
}
if (x) x.doSomething(); // 한 줄 코드인 경우에만 중괄호 생략 가능
제어문 내부에서 변수 할당은 지양해야 한다.
이는 할당(=)과 비교(==)를 혼동할 가능성을 줄이기 위함이다.
단, 의도적으로 할당하는 경우 이중 괄호를 사용해 의도를 명확히 표시한다.
Before
if (x = someFunction()) {
// 할당이 조건문 내부에 있어 혼동 가능
}
After
x = someFunction();
if (x) {
// x를 사용
}
while ((x = someFunction())) {
// 할당이 의도적임을 알 수 있음
}
배열을 반복 처리할 때는 다음과 같은 방식을 우선적으로 사용한다:
for ... of
: 배열의 값을 직접 반복Array.prototype.forEach
: 반복 동작을 콜백 함수로 처리for
루프: 인덱스가 필요한 경우에만 사용for (const value of someArr) {
console.log(value);
}
for (let i = 0; i < someArr.length; i++) {
const value = someArr[i]; // 인덱스가 필요한 경우
console.log(i, value);
}
for (const [i, value] of someArr.entries()) {
console.log(i, value);
}
for ... in
은 배열이 아닌 객체 순회용이며, 배열의 인덱스를 문자열로 반환하기 때문에 비직관적이므로 배열에 사용하지 말자.
객체를 반복 처리할 때는 for ... in
보다 Object.keys, Object.values, Object.entries를 권장한다.
for (const key of Object.keys(obj)) {
console.log(key);
}
for (const value of Object.values(obj)) {
console.log(value);
}
for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}
for (const key in obj) {
if (!obj.hasOwnProperty(key)) continue;
doWork(key, obj[key]);
}
코드를 작성할 때 불필요한 괄호 사용을 피하되, 읽기 쉽고 오해의 소지가 없도록 작성하라.
선택적인 그룹화 괄호는 작성자와 검토자가 그룹화 괄호 없이는 코드가 오해받을 가능성이 전혀 없고, 코드의 읽기가 더 쉬워지지 않을 것에 동의하는 경우에만 생략한다. (모든 독자가 연산자 우선순위 표 전체를 암기하고 있다고 가정하는 것은 합리적이지 않다.)
delete
, typeof
, void
, return
, throw
, case
, in
, of
, yield
다음에 나오는 전체 표현식 주위에 불필요한 괄호를 사용하지 말자.
Before
return (a + b);
throw (new Error('Error message'));
After
return a + b;
throw new Error('Error message');
예외는 언어의 중요한 부분이므로 예외적인 경우가 발생할 때마다 사용해야 한다.
사용자 정의 예외는 함수에서 추가적인 오류 정보를 전달하는 좋은 방법을 제공한다.
new
를 사용한 인스턴스화 하기:
throw Error()
보다는 throw new Error()
를 사용. 이는 다른 객체 인스턴스화 방식과 일관성을 유지한다.
Before
throw Error('Foo is not a valid bar.');
After
throw new Error('Foo is not a valid bar.');
Error
만 throw
하기:
일반적으로 JavaScript에서는 throw
키워드를 사용해 예외를 발생시킬 때 Error
객체를 던진다.
임시 오류 처리 방식(오류 컨테이너 참조 유형 전달이나 오류 속성이 있는 객체 반환 등)보다 예외를 throw
하는 것을 권장한다.
Before
throw 'oh noes!';
new Promise((resolve, reject) => void reject('oh noes!'));
Promise.reject();
Promise.reject('oh noes!');
After
throw new Error('oh noes!');
class MyError extends Error {}
throw new MyError('my oh noes!');
new Promise((resolve) => resolve());
new Promise((resolve, reject) => void reject(new Error('oh noes!')));
Promise.reject(new Error('oh noes!'));
Error
객체만이 의미 있는 스택 추적 정보를 제공한다.오류를 잡을 때 코드는 발생한 모든 오류가 Error
인스턴스라고 가정해야 한다.
function assertIsError(e: unknown): asserts e is Error {
if (!(e instanceof Error)) throw new Error("e is not an Error");
}
try {
doSomething();
} catch (e: unknown) {
// All thrown errors must be Error subtypes. Do not handle
// other possible values unless you know they are thrown.
assertIsError(e);
displayError(e.message);
// or rethrow:
throw e;
}
과도하게 방어적인 코드를 작성하지 말자:
대부분의 코드에서 throw
는 Error
객체를 던지기 때문에, 항상 모든 경우의 타입(string, number 등)을 방어적으로 처리하는 코드는 불필요한 반복적 방어가 된다.
문제를 일으킬 가능성이 적은 상황에서 "모든 경우"를 처리하려는 과도한 방어는 코드 복잡성을 높이고 유지보수를 어렵게 만든다.
만약 사용하는 API가
Error
가 아닌 다른 타입(예: 문자열)을 던지는 비정상적인 동작을 하는 경우에 한하여 방어적 코드를 작성하고 주석을 추가하여 문제의 출처를 명확히 설명하자.
빈 catch 블록 피하기:
Before
try {
shouldFail();
} catch (e) {
// 아무것도 하지 않음
}
After
try {
// 작업 수행
} catch (e: unknown) {
// 왜 아무것도 하지 않는지 주석으로 설명
// 예: 특정 예외는 무시해도 되는 상황
}
모든 switch
문에 default
그룹을 포함하기:
switch
문에는 항상 default
그룹을 포함해야 하며, 이 그룹은 항상 마지막에 위치한다.
빈 그룹도 괜찮다. 코드가 없는 경우라도 주석으로 의도를 명시하라.
모든 비어 있지 않은 case
그룹은 명시적으로 종료하기:
case
블록이 비어 있지 않다면, 반드시 명시적으로 종료해야 한다. 종료 방법은 break
, return
, 또는 throw
를 사용하는 것이다.
명시적으로 종료하지 않으면 다음 case
블록으로 실행이 넘어가는 fall-through로 의도치 않은 동작을 유발할 수 있다.
물론 여러 조건을 같은 방식으로 처리하고 싶을 때 의도적인 묶음 처리는 사용해도 된다.
switch (x) {
case 1:
doSomething();
break; // 명시적으로 종료
case 2: // fall-through! => 다음 case로 실행이 넘어감
case 3:
doSomethingElse();
break;
default:
// nothing to do.
}
삼중 등호(===
, !==
) 사용하기:
JavaScript의 동등 비교 연산자(==
, !=
)에서 발생하는 타입 강제 변환(type coercion)에 따른 예측 불가능한 동작을 방지하기 위해 항상 삼중 등호(===
, !==
)를 사용할 것을 권장한다.
==
는 두 값이 다른 타입일 경우, 비교를 시도하기 위해 한쪽 값을 다른 타입으로 변환한다.
이러한 변환은 예상치 못한 결과를 초래할 수 있다.
또한, 타입 강제 변환을 수행하는 ==
는 JavaScript 엔진이 더 많은 작업을 처리해야 하므로 느리다.
삼중 등호는 타입을 체크한 뒤 바로 값을 비교하므로 성능이 더 효율적이다.
예외: null
비교에서만 ==
와 !=
허용
단, null
과 undefined
를 동시에 비교해야 할 때는 예외적으로 ==
와 !=
를 사용할 수 있다.
if (foo === null || foo === undefined) {
console.log('foo is null or undefined');
}
if (foo == null) {
console.log('foo is null or undefined'); // true
}
TypeScript의 타입 단언(type assertions)과 null 불가능성 단언(non-nullability assertions)을 신중하게 사용할 것을 권장한다.
타입 단언 시, as
문법을 사용하라:
<Foo>
와 같은 앵글 브래킷 문법은 혼란을 줄 수 있으므로 지양한다.
as
를 사용하면 멤버 접근 시 명확성을 높이기 위해 괄호가 추가되기 때문이다.
Before
const x = (<Foo>z).length;
const y = <Foo>z.length; // 오해의 소지가 있음
After
const x = (z as Foo).length; // 괄호가 명확성을 보장
타입 단언과 null 불가능성 단언은 반드시 필요한 경우에만 사용하라:
타입 단언 (x as SomeType
)과 null 불가능성 단언 (y!
)은 컴파일러에게 특정 값이 어떤 타입임을 강제로 알리는 기능이다.
하지만 런타임에서는 실제로 타입이나 null 체크를 수행하지 않는다. 따라서:
Before
const x: unknown = "hello";
console.log((x as number).toFixed(2)); // 런타임 에러 발생!
After
타입 체크나 null 체크를 명시적으로 작성하라.
if (typeof x === "number") {
console.log(x.toFixed(2)); // 안전한 실행
}
타입 단언이나 null 불가능성 단언을 반드시 사용해야 할 경우, 주석을 통해 해당 단언이 안전하다고 판단한 이유를 기록한다. (명백한 경우에는 주석이 생략 가능하다.)
// x는 Foo 타입임이 보장된다 (어떤 이유 때문인지 명시).
(x as Foo).foo();
// y는 null이 될 수 없음을 알고 있다 (해당 상황 설명).
y!.bar();
이중 단언문 (Double Assertions)
TypeScript는 일반적으로 "더 구체적이거나 더 일반적인 타입"으로의 단언만 허용한다.
안전하다고 확신하는 경우, 이중 단언문을 사용할 수 있다.
중간 타입으로 any
나 {}
대신 unknown
을 권장한다.
Before
(x as any as Foo).fooMethod();
After
(x as unknown as Foo).fooMethod();
객체 리터럴에는 타입 어노테이션을 사용하여 에러를 사전에 방지하라:
객체 리터럴에서는 타입 단언 (as Foo
) 대신 타입 어노테이션 (: Foo
) 사용을 권장한다.
타입 어노테이션을 사용하면 인터페이스 변경 시 잘못된 필드가 자동으로 감지된다.
Before
interface Foo {
bar: number;
baz?: string;
}
const foo = {
bar: 123,
bam: 'abc', // 오류가 발생하지 않음!
} as Foo;
After
interface Foo {
bar: number;
baz?: string;
}
const foo: Foo = {
bar: 123,
bam: 'abc', // bam이 Foo에 정의되지 않았다고 에러 발생
};
try
블록에 포함된 코드는 최소화하는 것이 좋다:
어떤 코드가 예외를 던질 가능성이 있는지 명확하게 드러나도록 하기 위함이다.
Before
try {
const result = methodThatMayThrow(); // 예외를 던질 가능성 있음
use(result); // 예외를 던지지 않음
} catch (error: unknown) {
// ...
}
use(result)
는 예외를 던지지 않지만 try
블록 안에 포함되어 있어 독자에게 혼란을 줄 수 있다.let result;
try {
result = methodThatMayThrow(); // 예외를 던질 가능성이 있는 코드만 포함
} catch (error: unknown) {
// ...
}
use(result); // 예외를 던지지 않는 코드는 `try` 블록 밖으로 이동
methodThatMayThrow()
만 예외를 던질 가능성이 있음을 명확히 알 수 있다.try {
use(methodThatMayThrow()); // 코드가 간단하므로 별도로 나눌 필요 없음
} catch (error: unknown) {
// ...
}
루프 안에 try
블록을 반복적으로 선언하면 성능 문제가 발생할 수 있다:
이 경우, try 블록의 범위를 루프 전체로 확장하는 것이 낫다.
Before
for (const item of items) {
try {
processItem(item); // 매번 `try` 블록이 재생성됨
} catch (error: unknown) {
// ...
}
}
After
try {
for (const item of items) {
processItem(item); // `try` 블록이 한 번만 생성됨
}
} catch (error: unknown) {
// ...
}
데코레이터는 클래스, 메서드, 필드, 또는 파라미터에 메타데이터를 추가하거나, 동작을 수정할 수 있도록 도와주는 특수한 문법이다.
데코레이터는 @ 기호로 시작하며, 함수 형태로 정의된다.
@Component({
selector: 'my-component',
template: '<p>My Component</p>',
})
class MyComponent {}
새로운 데코레이터를 정의하지 말고, 프레임워크 제공 데코레이터만 사용하라:
데코레이터는 TypeScript에서 실험적(Experimental)으로 도입되었다.
하지만 JavaScript 표준화 기구인 TC39의 최종 표준안과는 다르게 동작하는 부분이 있다.
실험적 기능이기 때문에 이미 발견된 몇 가지 버그들이 수정되지 않을 가능성이 있다.
이런 이유로 새로운 데코레이터를 정의하거나 사용하는 것이 위험할 수 있다.
프레임워크에서 제공하는 공식 데코레이터만 사용해야 한다.
데코레이터는 빈 줄 없이 바로 적용 대상 앞에 위치시켜 가독성과 유지보수성을 높여라:
Before
/** JSDoc comments */
@Component({...})
class MyComponent {} // 빈 줄이 있어서 잘못된 예
After
/** JSDoc comments */
@Component({...})
class MyComponent {} // 빈 줄 없이 바로 연결
class MyComp {
@Input() myField: string; // 같은 줄
@Input()
myOtherField: string; // 다음 줄로 감싸기
}
래퍼 클래스(wrapper class)는 JavaScript의 원시 타입(String, Boolean, Number)을 객체로 감싸는 기능을 제공한다.
예를 들어, String, Boolean, Number는 각각 원시 타입을 객체처럼 다룰 수 있게 해 준다.
const s = new String('hello'); // 문자열 'hello'를 String 객체로 감쌈
const b = new Boolean(false); // 불리언 false를 Boolean 객체로 감쌈
const n = new Number(5); // 숫자 5를 Number 객체로 감쌈
원시 타입을 감싸는 래퍼 클래스를 직접 인스턴스화하지 말자:
래퍼 클래스를 인스턴스화하면 원시 값이 아니라 객체가 생성된다.
이로 인해 의도와 다른 동작을 초래할 수 있다.
원시 타입을 객체로 감싸는 것은 메모리와 성능 측면에서 불필요한 오버헤드를 발생시킨다.
원시 타입 자체는 가볍고 빠르게 동작하도록 설계되었다.
Before
const b = new Boolean(false);
console.log(b); // Boolean {false}
console.log(b == true); // true
console.log(b ? 'true' : 'false'); // 'true'
new Boolean(false)는 불리언 객체를 반환하며, 객체는 항상 true로 평가된다.
즉, 실제 값이 false임에도 true로 평가되는 문제가 발생한다.
const s1 = 'hello';
const s2 = new String('hello');
console.log(s1 === s2); // false (문자열 vs 객체)
console.log(s1 == s2); // true (느슨한 비교로 동등하다고 평가)
After
const s = 'hello'; // 원시 문자열
const b = false; // 원시 불리언
const n = 5; // 원시 숫자
const s = String('hello'); // 문자열로 변환
const b = Boolean(0); // 불리언으로 변환
const n = Number('42'); // 숫자로 변환
모든 문장을 세미콜론으로 명시적으로 끝내야 한다.
이렇게 하면 잘못된 세미콜론 삽입으로 인한 버그를 방지하고 ASI 지원이 제한된 도구(예: clang-format)와의 호환성을 보장한다.
const enum을 사용해서는 안 된다:
const enum은 컴파일 결과에서 사라지므로, JavaScript로 작성된 코드나 다른 모듈에서 이 열거형을 참조할 수 없다. 만약 const enum을 사용한 TypeScript 코드가 JavaScript 모듈과 함께 사용될 경우, 예상치 못한 오류가 발생할 수 있다.
const enum은 값이 인라인되기 때문에 디버깅 과정에서 열거형 이름을 볼 수 없다. 이는 디버깅 정보를 잃게 만들고, 유지보수를 어렵게 한다. 또한 열거형의 값이 여러 곳에 인라인으로 삽입되면, 열거형 값이 변경될 때 컴파일된 JavaScript의 모든 참조를 다시 컴파일해야 한다.
const 열거형은 모듈의 JavaScript 사용자에게 열거형을 보이지 않게 하는 최적화와 관련된 별도의 언어 기능이며, JavaScript 표준이 아니다. 따라서 다른 환경에서 호환되지 않을 가능성이 있다.
enum:
enum Color {
Red,
Green,
Blue,
}
console.log(Color.Red); // 출력: 0
컴파일 후 JavaScript:
var Color;
(function (Color) {
Color[Color["Red"] = 0] = "Red";
Color[Color["Green"] = 1] = "Green";
Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));
console.log(Color.Red); // 출력: 0
const enum:
const enum Color {
Red,
Green,
Blue,
}
console.log(Color.Red); // 출력: 0
컴파일 후 JavaScript:
console.log(0); // `Color.Red`가 상수로 대체됨
debugger
문은 프로덕션 코드에 포함되어서는 안 된다.
debugger는 JavaScript에서 디버깅을 위해 사용되는 명령어로, 코드 실행을 중단하고 디버깅 세션을 시작할 수 있도록 돕는다.
이 명령어는 주로 브라우저의 개발자 도구나 Node.js의 디버깅 모드에서 실행될 때 유용하게 사용된다.
function debugMe() {
debugger; // 디버거가 활성화된 상태에서 실행 시, 코드가 중단된다.
console.log("This will not run unless debugger is removed.");
}
debugger
는 코드 실행을 중단시키므로, 사용자가 웹 애플리케이션을 사용할 때 예기치 않게 페이지가 멈추거나 동작이 중단될 수 있다.
프로덕션 환경에서는 디버깅을 위한 코드 실행 중단이 사용자 경험을 방해하거나 서비스의 가용성에 악영향을 미칠 수 있다.
debugger
문이 코드에 남아 있으면, 프로덕션 환경에서도 디버깅을 할 수 있는 상태로 남게 된다. 이는 보안 위험을 초래할 수 있다.
공격자는 브라우저의 개발자 도구를 통해 디버거를 활성화하고 애플리케이션의 상태를 추적하거나 악용할 수 있다.
특히, 민감한 데이터를 처리하는 애플리케이션에서는 이러한 디버깅 코드가 보안 취약점을 유발할 수 있다.
배포 후 디버깅은 실제 코드에 debugger를 삽입하는 대신, 로그 파일이나 에러 추적 시스템을 활용하여 디버깅을 수행하는 것이 좋다.
with
키워드 사용 금지:
코드를 이해하기 어렵게 만들고 ES5 이후로 strict 모드에서 금지되었다.
eval
이나 Function(...string)
생성자를 사용 금지(코드 로더 제외).
이러한 기능은 잠재적으로 위험하며 엄격한 콘텐츠 보안 정책을 사용하는 환경에서는 제대로 작동하지 않는다.
const userInput = "alert('Hacked!')";
eval(userInput); // 위험: 사용자가 악의적인 코드를 삽입할 수 있음
const userInput = "alert('Malicious code')";
const fn = new Function(userInput);
fn(); // 위험: 악의적인 코드 실행
표준화된 ECMAScript 및 Web Platform 기능만 사용하자:
비표준 ECMAScript나 웹 플랫폼 기능으로 간주되는 것들은 다음과 같다.
내장 타입을 수정하지 마라:
JavaScript에는 내장된 타입들, 예를 들어 Array
, Object
, String
, Number
, Boolean
, Function
등이 있다.
내장 타입을 수정하면, 기존 코드나 외부 라이브러리가 의도한 대로 동작하지 않을 수 있다. 예를 들어, Array
에 새로운 메서드를 추가하면, 다른 코드에서 기존 메서드들과 충돌하거나 예기치 않은 결과를 초래할 수 있다.
Array.prototype
에 newMethod
를 추가한다고 가정했을 때, 다른 라이브러리가 같은 이름의 메서드를 추가하거나, 예상치 못한 방식으로 동작할 수 있다.
표준 JavaScript 동작을 변경하는 것은 코드의 호환성 문제를 유발할 수 있다. 예를 들어, 새로운 자바스크립트 버전이나 다른 환경에서는 다르게 동작할 가능성이 있으며, 이는 예상치 못한 버그를 초래할 수 있다.
내장 타입을 수정하면 디버깅을 어렵게 만들 수 있다. 수정된 메서드는 JavaScript 엔진의 표준 동작을 변경하므로, 문제가 발생할 경우 이를 추적하기가 힘들어진다.
글로벌 객체에 심볼을 추가하지 마라:
글로벌 객체는 JavaScript 실행 환경에서 전역적으로 접근 가능한 객체를 의미한다. 브라우저에서는 window
, Node.js에서는 global
객체가 해당된다. 이 규칙은 글로벌 객체에 심볼이나 속성을 추가하지 말고, 추가가 필요하다면 꼭 필요한 경우에만 하라는 것이다.
글로벌 객체는 애플리케이션 내 모든 코드에서 접근 가능하다. 여기에 심볼이나 속성을 추가하면, 전역 변수의 이름이 충돌할 가능성이 높아지며, 이는 코드의 예기치 못한 동작을 초래할 수 있다. 특히, 외부 라이브러리나 다른 개발자와 협업할 때 문제가 될 수 있다.
글로벌 객체에 불필요한 심볼이나 속성이 많이 추가되면, 코드의 복잡성이 증가하고, 성능에도 영향을 미칠 수 있다. 이는 특히 대규모 애플리케이션에서 문제가 될 수 있다.
글로벌 객체에 의도치 않은 속성이 추가되면, 디버깅할 때 혼란을 초래할 수 있다. 어떤 코드에서 해당 심볼을 수정하거나 참조할 수 있기 때문에, 문제가 발생했을 때 추적이 어렵다.
글로벌 객체에 심볼을 추가할 때는 반드시 그 이유를 명확히 하고, 외부에서 이 심볼에 접근하거나 수정할 가능성을 최소화해야 한다.
GitHub - google/gts: ☂️ TypeScript style guide, formatter, and linter.
Typescript Google Code Style Part 1
Typescript Google Code Style Part 2
Typescript Google Code Style Part 3