[클린 코드 JS] 변수 다루기

yoon Y·2022년 9월 7일
0

클린 코드

목록 보기
2/3

var보다 let/const를 지향해야하는 이유

1. 중복 선언/할당

var

  • var는 선언, 재할당을 중복으로 할 수 있다.
  • var가 중복 선언이 될 경우 (원래 오류가 나야하지만) 중복 선언이 무시되고 할당으로만 작동한다.

let/const

  • let/const는 중복 선언 시 에러가 뜬다.
  • let은 재할당 가능, const는 불가능하다.
  • let은 선언 먼저 한 후 나중에 할당해도되지만, const는 변수 재할당을 할 수 없기 때문에 선언 시에 바로 할당까지 해야한다

선언 중복 시 에러가 나지 않는다면 새로 선언하려는 변수가 이미 있는 변수인지 확인하기가 어려워 의도하지 않은 대로 동작할 수 있다.
var < let/const

변수의 값이 변경될 가능성이 있는 경우 예상치 못한 에러가 생길 가능성이 높다.
var < let < const


2. 호이스팅

코드 작성 순서와 런타임 때 작동하는 것은 다르다. 런타임에 예상못한 동작들이 발생된다.

  • 런타임 시에 선언과 할당이 분리되는 것.
  • 런타임 시에 선언이 최상단으로 끌어올려 지는 것.

var와 함수 선언식은 호이스팅과 동시에 할당된다.

  • var는 호이스팅 시 undefined로 초기 할당된다. 그래서 선언문보다 위쪽에서 실행시킬 수 있다.
  • 함수 선언식을 사용하면 호이스팅 시 바로 함수 선언문이 할당된다. 그래서 선언문보다 위쪽에서 실행시킬 수 있다.

호이스팅 시 할당될 때의 문제점

  • 보여지는 코드대로 동작하지 않기 떄문에 런타임시 예측하지 못한 상황들이 발생할 가능성이 높다.
  • 개발자가 작성한 순서에 따라 동작하지 않아서 혼란스럽게 된다.

해결책
함수나 변수 선언 시 const, let을 써서 선언해야 실수를 줄일 수 있다.


3. block level, function level scope

블록 단위 스코프(let/const)여야 외부 변수의 오염을 막을 수 있다.
함수 레벨(var)의 경우 블록 안에서 변경을 해도 전역 변수에 영향을 미치게 된다.

예시1.
if조건이 맞을 때만 새로운 변수를 선언해서 출력해야하는데 같은 이름의 전역변수가 변경되어 버렸다.

var global = '전역';

If (global === '전역') {
   var global = '지역';
  
   console.log(global); // 지역
}

console.log(global); // 지역

예시2.
블록 밖과 안의 변수가 별개로 선언되게 됨.
블록 스코프가 사람이 생각한 대로 작동할 수 있게 됨.

let global = '전역';

If (global === '전역') {
   let global = '지역';
  
   console.log(global); // 지역
}

console.log(global); // 전역

4. let보다도 const를 사용하는 게 좋다

  • 변수의 값이 변경될 가능성이 있는 경우 예상치 못한 에러가 생길 가능성이 높기 때문에 let보다는 되도록 const를 사용하는 게 좋다.
  • const로 선언된 변수가 객체, 배열 값을 갖더라도, 재할당은 불가능하지만 내부 프로퍼티 변경은 할 수 있다.



전역 공간을 사용하면 안되는 이유

  • 전역===최상위 (window(브라우저), global(nodeJS))
  • 전역 객체에는 webApi들이 포함되어 있다.
  • 모듈 기능이 없는 경우(webpack x, type="module"x), 모든 파일이 한 스코프에 담기게 된다.
  1. 전역 공간을 사용하면 윈도우 객체에 사용자 정의 변수가 포함되게 된다.
    • 파일(모듈)을 나눠도 스코프가 나뉘지 않기 때문에, 모든 파일에서 접근할 수 있게 된다.
    • 여러 모듈에서 같은 이름의 별개의 변수를 사용할 수 없고 모든 파일에서 공유하게된다.
      → 변경된 지점을 찾기가 어려워 디버깅이 어렵다.
  1. 기본적으로 정의된 브라우저 WebApi의 값을 변경해도 언어 자체에서 에러가 뜨지 않는다.



임시 변수 제거하기

임시 변수란?

  • 어느 스코프 안에서 전역 변수처럼 활용되는 변수 (함수가 커지면 그 안의 변수도 전역변수처럼 동작될 수 있다.)
  • 어느 스코프 안에서 그 스코프 안의 어떤 곳에서도 접근하고, 변경할 수 있는 변수.

임시 변수의 문제점

  • 임시 변수가 생기는 순간 수정할 가능성이 생긴다.
  • 로직이 명령형에 가까워진다.
  • 함수는 하나의 역할만 하게 해야되는데 임시변수가 있으면 수정, 추가를 유혹받게 되어 유지보수가 어려워진다.
  • 어디서 어떻게 잘못되었는지 디버깅이 힘들다.

해결책

  1. 함수를 쪼갠다. (함수가 작아지면 접근할 가능성도 낮기 때문)
  2. 임시변수를 제거한다. (crud의 가능성을 최소화한다)
  3. 바로 반환한다.
  4. 고차 함수를 사용한다.(map, filter, reduce)
  5. 선언형 프로그래밍으로 바꿔보는 연습을 한다.

[예시1] 변수 crud없이 바로 리턴하기

변수의 값을 변경할 수 있는 가능성을 제거한다.
임시 변수, 객체, 배열이 생기는 순간 수정할 가능성이 생기는 것.

bad - 수정이 가능한 버전

function getElements() {
  const result = {}; // 임시 객체
  
  result.title = document.querySelector('.title');
  result.text = document.querySelector('.text');
  result.value = document.querySelector('.value');
  
  return result;
}

good - 수정 불가능한 버전

변수를 조작할 여지를 아예 주지 않는다.

function getElements() {
  return {
    title: document.querySelector('.title');
    text: document.querySelector('.text');
    value: document.querySelector('.value');
  }
}

[예시2] 수정불가한 함수를 이용해 새로운 기능 만들기

함수 내부에서 let으로 선언한 변수에 접근해 수정할 가능성이 있다.
추가적인 스펙이 있을 경우 함수 자체를 수정하면 그 함수를 수정하는 많은 곳에서 문제가 생길 수 있다. 이미 만들어둔 함수를 변경 불가능하게 바로 리턴하는 방법으로 작성 한 후 추가 수정 사항은 리턴 값을 가지고 추가적인 조작을 해서 사용하는 게 좋다.

bad - 수정이 가능한 버전

function getDateTime(targetDate) {
  let month = targetDate.getMonth();
  let day = targetDate.getDate();
  let hour = targetDate.Hours();
  
  month = month >= 10 ? month : '0' + month;
  day = day >= 10 ? day : '0' + day;
  hour = hour >= 10 ? hour : '0' + hour;
  
  return {
  	month,
    day,
    hour
  }
}

good - 수정 불가능한 버전

function getDateTime(targetDate) {
  const month = targetDate.getMonth();
  const day = targetDate.getDate();
  const hour = targetDate.Hours();
  
  return {
  month: month >= 10 ? month : '0' + month;
  day: day >= 10 ? day : '0' + day;
  hour: hour >= 10 ? hour : '0' + hour;
  }
}

good - 기능 추가 버전

  • 추가적인 기능이 필요할 때에는 이미 만들어둔 함수들을 사용해서 껍데기를 추가하고 벗기면서 유지보수를 하는 것이다.
  • 이미 만들어진 함수를 수정하는 것은 사이드이펙트가 일어날 문제가 있다.
  • 수정 방지를 위해 변경될 가능성이 있는 변수 선언을 최소화한다.
function getDateTime(targetDate) {
  const currentDateTime = getDateTime(new Date());
  
  return {
  month: currentDateTime.month + '분 전';
  day: currentDateTime.day + '분 전';
  hour: currentDateTime.hour + '분 전';
  }
}

[예시3] 임시 변수를 선언한 후 할당&연산을 반복한 후 리턴하는 방법.

  • 매우 혼란하다.
  • 최종값이 무엇인지 예측하기 어렵다.
  • 명령형에 가까운 코드이다.
function getSomeValue(number) {
  let temp;
  
  temp += number;
  
  if(temp > 10) {
    temp = 0;
  }
}
profile
#프론트엔드

0개의 댓글