자바스크립트에서 피해야할 코드 작성법

이라운·2023년 1월 7일
85

Say bye bye to bad javascript practices

Freddy Manrique의 Say bye bye to bad javascript practices
⚠️ 글을 읽고 작성자 편한대로, 이해한 대로, 기억하고 싶은 부분만 번역했습니다. 믿지 마시고, 되도록이면 위의 원문을 봐주세요.
⚡️ 해당 이모지가 있는 부분은 원문에 없는 작성자의 사족입니다.

✒️ 느낀 점

면접을 준비하며 자바스크립트 공부에 박차를 가하고(가할려고 준비중)인데 공부할 수록 자바스크립트란 참 도전정신을 불러일으키는 언어인것 같다. 이런 자바스크립트를 사용할 때, 피해야할 작성법에 대한 글이 있기에 허겁지겁 번역했다. 찔리는... 부분도 있었지만 이제라도 알게 됐으니...! 앞으로는 유의하면서 작성해야겠다.

🔤 번역

시작하며

멋진 프로그래밍의 세계에 첫 발을 내딛을 때, 우리는 프로그래밍이 수백만의 사람들한테 어떤 영향을 끼치는지 알게 된다. 프로그래밍 덕분에 우리의 삶은 단 몇 번의 클릭만으로 훨씬 쉬워졌다. (마치 마법같다)

프로그래밍은 새로운 슈퍼파워라고도 할 수 있지만, 스파이더맨 피터 파커에게 삼촌 벤이 말했던 것처럼 "큰 힘에는 큰 책임이 따른다". 프로그래밍의 세계에서의 책임은, 우리가 작성하는 코드가 쉽게 테스트될 수 있어야 하며 오랜 시간 뛰어난 유지보수성을 가져야 한다는 것이다.

몇 가지의 소소한 코드 작성 습관은 계속적으로 코드와 제품에 부정적인 영향을 줄 수 있다. 나는 이와 같은 이슈를 경험해봤다. 그와 같은 안좋은 코드 작성 습관을 공유하고, 그것이 왜 안좋은지를 설명하는 것은 반드시 필요한 작업이다.

1. let, const 가 아닌 var 를 사용하는 것

무조건 let, const 를 사용하는 습관을 들여야 한다.

  • 중괄호 간의 스코프가 더욱 명확해진다.
  • 전역 객체를 생성하지 않는다.
  • 재선언을 하게 되면 에러를 발생시킨다.
// ❌ with var
var name = '이름이다'
if (name === '이름이다') {
  var name = 'rawoon'
}
console.log(name) // 'rawoon'

// ✅ with let and const
const name = '이름이다'
if (name === '이름이다') {
  let name = 'rawwon'
}
console.log(name) // '이름이다'

2. 코드를 설명하는 주석을 적는 것

주석은 우리가 소프트웨어를 만들 때 기본적인 부분으로 읽고 있는 코드에 대한 조금이라도 더 나은 이해를 할 수 있도록 도와준다. 하지만 코드가 단계별로 무엇을 수행하는지 적는 경우가 많은데, 읽기 쉽도록 코드를 작성하고 주석은 맥락을 제공하는 형태로 작성해야 한다.

프로처럼 코멘트를 적는 팁은 아래와 같다.

  • 반복적인 주석을 피하자. 무엇을 하는지 적는 것이 아닌, 왜 이렇게 했는지를 적자
  • 변수/함수/클래스에 대해 설명해 줄 수 있는 변수 이름을 짓는 것이 주석으로 설명을 적는 것보다 낫다.
  • 필요하지 않는 한, 요약해서 적는다.
  • 동일한 언어와 스타일의 주석을 적도록 노력한다.
  • 시간이 지나면서 주석은 코드만큼 유지보수 되지 않는다.

3. === 가 아닌 == 를 쓰는 것

보기에는 비슷하지만 둘은 다른 기능을 가진다는 것을 알아야 한다. == 는 추상적 같음 비교이고 ===는 엄격한 같음 비교이다.

추상적 같음 비교 연산자(==)는 비교대상이 비슷한지를 보기 때문에 불편한 놀라움을 선사해줄 수 있다.

[] == 0 // true 😱

⚡️ 여기서 이 밈을 놓칠 수 없지

엄격한 같음 비교 연산자(===)는 비교대상이 정확히 일치하는지 검사한다.

// ❌ ==
const number = 1
const string = '1'

console.log(number == string) // true

// ✅ ===
const number = 1
const string = '1'

console.log(number === string) // false

4. '?' optional chaining 연산자 사용을 잊는 것

optional chaining 연산자 (?.) 는 체인의 각 참조가 유효한지 명시적으로 검증하지 않고, 연결된 객체 체인 내에 깊숙이 위치한 속성 값을 읽을 수 있다.

존재하지 않는 프로퍼티에 접근하려 할 때 이 연산자를 사용하면 에러가 발생하지 않도록 해준다. 예를 들어 포켓몬의 정보를 가지고 있는 포켓몬 객체를 가지고 있다고 해보자.

const pokemon = {
  name: 'Pikachu',
  info: {
    ability: 'Mega volt'
  }
}
console.log(pokemon?.info?.ability) // Mega volt
console.log(pokemon.info.attack) // Error
console.log(pokemon?.info?.attack) // undefined (no error)

정의되지 않은 프로퍼티에 접근하려고 하면, 예를 들어 'attack' 과 같은, 자바스크립트는 에러를 뱉어낼 것이고 우리의 어플리케이션은 멈출 것이다. optional chaining 연산자를 사용하면 자바스크립트는 undefined 라고 알려줄 것이지만 에러를 발생시키지는 않을 것이다. 간혹 우리의 제어를 벗어나는 이와 같은 에러를 고려한다면, 장기적으로 유익하다.

5. 마법의 문자열 또는 마법의 숫자를 쓰는 것

마법의 문자열 또는 마법의 숫자는 직접적으로 코드에 사용되는 숫자와 문자열으로서 정확한 맥락은 없지만 뚜렷한 목적을 가지고 작성된 것을 의미한다. 이와 같은 마법의 문자열과 숫자를 변수로 빼서 사용하는 것이 더욱 유익하다. 그렇지 않을 경우 이해하기도 어렵고 디버깅도 힘들어진다.

// ❌ with magic number
const validatePassword = (password) => {
  if (password.length > 7) {
    console.log('Your password is amazing')
  }
}

// ✅ without magic number
const MIN_PASSWORD_SIZE = 7;

const validatePassword = (password) => {
  if (password.length > MIN_PASSWORD_SIZE) {
    console.log('Your password is amazing')
  }
}

6. 부적절한 API 호출 제어

async/await 에서 반드시 try/catch 를 사용하여 에러를 다루어야한다.

프로미스(Promise 객체)에서 에러를 다루지 않으면 우리의 서비스는 결국 폭발할 것이다. 그리고 확신하건데 그 누구도 그와 같은 상황을 원하지 않을 것이다.

async function downloadImage(img = 'pokemon.png') {
  let response await fetch(img) 
  if (!response.ok) { 
    throw new Error(`HTTP error! status: ${response.status}`) 
  } 
  return await response.blob() 
  
// ❌ 
const pokemonImage = await download Image()

// ✅
let pokemonImage 
try ( 
  pokemonImage = await download Image() 
} catch { 
  pokemonImage = DEFAULT_IMG
}

// ✅
const pokemonImage = await downloadImage().catch(() => DEFAULT_IMG)

7. 매개변수로 하나의 객체를 넘기는 것

함수를 선언할 때 하나의 객체에서의 여러 값을 사용해야 될 경우, 복수의 매개변수를 넘기는 것이 하나의 객체를 넘기는 것보다 더 좋다. 왜냐하면

  • 함수를 읽는 처음의 과정에서부터 함수가 필요로 하는 매개변수를 파악하기 용이하다.
  • 함수를 더욱 쉽게 테스트할 수 있도록 해주고 이와 같은 2가지의 장점만으로도 코드의 유지보수성을 높일 수 있다.
  • 쓸모없는 파라미터를 보관하지 않아도 되기에 어플리케이션의 성능에도 도움이 된다.
  • 만약 타입스크립트를 사용한다면, 매개변수를 interface 로 정의하는 것이 에러를 피하거나 자동완성을 통한 타입체크를 가능하게 하기 때문에 유익하다.
// ❌
const getPokemon = (Pokemon) => {
  console.log(`The pokemon is $(Pokemon.pokemonName) the weigth is $(Pokemon.pokemonWeight)`)
}

// ✅ 
const getPokemon = (pokemonName, pokemonWeight) => {
  console.log(`The pokemon is $(pokemonName) the weigth is $(pokemonWeight)`)
}

8. 단축어의 힘을 잊는 것

우리는 변수가 존재하는지, 값이 있는지, null 인지 undefined 인지를 확인해야 될 경우가 모두 있다. 이를 위해 간혹 '무척이나 긴' 코드를 작성하는 경우가 있다.

이럴 때 단축어는 긴 문장을 더욱 쉽게 해결하고 읽을 떄도 더욱 빠르게 이해할 수 있도록 해준다.

// ❌
if (x !== "" && x !== null && x !== undefined) {...}
// ✅ 
if (!!x) {...}

위의 두 줄의 코드는 정확하게 같은 일을 수행할 수 있다.

결론

클린코드를 작성하는 것은 우리 모두의 책임이다. 내 경험에 따르면 유지보수성이 뛰어나고 쉽게 읽을 수 있는 코드는 팀과 너 자신의 수 많은 시간을 아낄 수 있도록 해줄 것이다.

우리는 쓰기보다는 더욱 많은 시간을 읽기에 사용한다는 것을 잊지말자.

단어

fundamental: 근본적인, 중요한

profile
Programmer + Poet = Proet

5개의 댓글

comment-user-thumbnail
2023년 1월 14일

예시 중에 매개변수는 잘못된예와 잘된 예가 바뀐 것 같네요 ^^

1개의 답글
comment-user-thumbnail
2023년 1월 16일

Me too same problem .. I am also looking for a solution :(
https://www.hyveehuddle.net/

답글 달기