해당 글은 https://blog.bitsrc.io/what-are-code-smells-and-how-clean-code-can-help-typescript-version-990697a87f46 글을 번역한 글입니다.
저자의 글을 확인하고싶으시면 여기를 클릭해주세요.
위키피디아에 따르면, 악취는 심각한 문제를 야기할수있는 소스코드를 뜻합니다.
간단하게 정의하자면, 깨끗하지않고 잘못된 프로그래밍의 결과라고 할 수 있습니다.
악취는 참을수 있을정도의 작은 악취부터 프로그램 개발을 더욱더 어렵고 비효율적으로 만드는 악취까지 다양한 범주로 존재합니다.
이번 글에서 저는 자바스크립트와 타입스크립트에 보편적으로 존재하는 악취에대해 초점을 맞추려고합니다.
타입스크립트 컴파일러는 자동으로 변수의 타입을 추론할수있습니다.(typeof
, instanceof
등등).
이러한 컴파일러 기능 덕분에 재선언과 non-null 선언은 필요없습니다.
나쁜 예시
function getName(x?:string|UserName){
if(x){
console.log("Getting name for " + x!) //Noncompliant
if(typeof x === "string")
return (x as string) //Noncompliant
else
return (x as UserName).name //Noncompliant
}
return "NoName"
}
좋은 예시
function getName(x?:string|UserName){
if(x){
console.log("Getting name for " + x) //Noncompliant
if(typeof x === "string")
return x //Noncompliant
else
return x.name //Noncompliant
}
return "NoName"
}
삼항 연산자는 가독성이 뛰어나고 사용하기 쉽기때문에 JavaScript에서 널리 사용됩니다. 그러나 때떄로 우리들은 삼항연산자를 남용합니다. 중첩된 연산을 별도의 문장으로 표현하려면 다른 줄을 사용하는것이 좋습니다.
나쁜 예시
function getReadableStatus(job){
return job.isRunning() ? "Running" : job.hasErrors() ? "Failed" : "Succeeded"
}
좋은 예시
function getReadableStatus(job){
if(job.isRunning()){
return "Running"
}
return job.hasErrors() ? "Failed" : "Succeeded"
}
Javascript의 많은 배열 함수들은 기존 배열은 그대로 둔 채 새로운 배열을 반환시킵니다. reverse
와 sort
함수는 이러한 배열 함수들에 포함되지않습니다. 그들은 새로운 배열을 반환시키는것 외에도 기존 배열들을 변경하기도하는데 아마 의도하고 사용하지않았을 가능성이 높습니다.
이 특징은 반환된 배열이 할당될때 문제를 야기하는데 코드를 유지보수할때 원래 값이 변경되었다는 사실을 간과할 수 있습니다.
나쁜 예시
var a = a.reverse()
var d = c.sort()
좋은 예시
var b = [...a].reverse()
a.reverse()
c.sort()
'a == b
이고 b == c
이면 a == c
이다'라는게 추이적 관계입니다. 이러한 상황에서 a
에 c
를 할당하는건 좋지않습니다. 이미 동일하기 때문입니다.
나쁜 예시
a = b
b = c
b = a
좋은 예시
a = b
c = a
이 조언은 당연한거라 별로 할 말이 없습니다. 여러분들은 타입이 안전하지않은 레거시 라이브러리에만 any
타입을 써야합니다. 그 외 99%경우에는any
타입을 사용하지않을 수 있을겁니다.
유니온 타입(Union Type)과 인터섹션 타입(Intersection Type)은 사용함에 있어서 편리하지만 가독성과 유지보수에는 힘듭니다. 그렇기떄문에 particular union 또는 intersection이 여러 곳에서 사용된다면, 타입 별칭을 사용해보세요.
나쁜 예시
function foo(x:string|null|number) {
...
}
function bar(x:string|null|number) {
...
}
function zoo():string|null|number {
return null
}
좋은 예시
type MyType = string | null | number
function foo(x:MyType){
...
}
function bar(x:MyType){
...
}
function zoo():MyType{
return null
}
정규표현식 내에 많은 공백은 일치해야하는 공간이 몇 개인지 구별하기 어렵게만듭니다. 하나의 공간만 사용하고 수량 한정 기호를 이용하여서 얼마나 많은 공간이 예상되는지 표시하는것이 더 좋습니다.
나쁜 예시
const pattern = /Hello, world!/
좋은 예시
const pattern = /Hello, {3}world!/
delete
연산자를 이용하여 어떠한 object에서 속성을 제거할 수 있습니다. 배열도 object이기때문에 배열에도 delete
연산자를 사용할 수 있다. 그러나 만약 사용하게된다면 삭제 내용을 반영하도록 인덱스/키가 이동되지않기때문에 배열에는 구멍이 나있을겁니다.
특정 인덱스에서 요소를 제거하는 적절한 방법은 다음과 같습니다.
Array.prototype.splice
- 배열에서 요소를 추가/삭제할 수 있습니다.Array.prototype.pop
- 배열의 마지막 요소를 추가/삭제할 수 있습니다.Array.prototype.shift
- 배열의 첫번째요소를 추가/삭제할 수 있습니다.나쁜 예시
var myArray = ['a','b','c','d']
delete myArray[2]
console.log(myArray)
// => myArray = ['a','b',undefined,'d']
좋은 예시
var myArray =['a','b','c','d']
const removedFromMyArray = myArray.splice(2,1)
console.log(removedFromMyArray)
// => ['a','b','d']
특별한 경우를 제외하고는 가독성을 위해 동일한 모듈에서 import하는건 한 곳에 병합하세요.
나쁜 예시
import { B1 } from 'b'
import { B2 } from 'b'
좋은 예시
import { B1,B2 } from 'b'
Javascript와 Typescript에는 제가 다룬것 외에도 많은 '악취나는 코드'가 많습니다.
만약 Typescript내에 어떤 또다른 '악취나는 코드'가 있는지 알고싶으시면 여기를 클릭하여 확인해보세요!