가수면

리팩토링 본문

웹 개발/웹 개발

리팩토링

니비앙 2023. 8. 22. 22:42

리팩토링 핵심

1. 기본 원칙

목표

  • 코드의 가독성 향상
  • 복잡성 감소
  • 유지보수성 향상
  • 확장성 향상

지향점

  • 누구나 잘 알아볼 수 있는가 (성능 최적화 신경쓰지 않고 코드를 다루기 쉽게 만드는 것(가독성)에 집중.)
  • 한 가지 역할만 수행하는가
  • 재사용 가능한 단위인가
  • 기능의 원하는 부분만 수정할 때 용이한가
  • 너무 미래 지향적인 코드 x (점진적 확장)
  • 매개 변수로 불리언 타입 받아 분기 처리하는 함수 지양
  • 되도록 매개 변수 없는 함수 지향
  • 모듈 간 독립성 유지 - 변수(어떤 데이터) / 함수(어떤 일) / 모듈(어떤 책임) 명확한 분리

리팩토링 중 피해야 할 행동

  • 기능의 변경이나 추가:
    기능 변경이나 추가는 리팩토링 과정에서의 핵심 목표가 아닙니다. 리팩토링은 코드의 내부 구조를 개선하는 것에 초점을 맞추어야 합니다.
  • 성능 개선을 목표로 한 리팩토링:
    성능 최적화는 종종 코드의 가독성이나 복잡성을 증가시킬 수 있기 때문에 리팩토링의 주요 목표로 삼지 않습니다. 물론, 리팩토링 후 성능이 개선되는 부수적인 효과는 있을 수 있습니다.
  • 리팩토링 과정에서 발견한 버그 수정:
    리팩토링 과정에서 버그를 발견하면, 그 버그를 고치기 전에 리팩토링을 먼저 완료해야 합니다. 이렇게 하는 이유는 리팩토링과 버그 수정을 구분하여, 각각의 변경 사항이 어떤 영향을 미치는지 명확히 파악하기 위함입니다.
  • 버전의 업데이트:
    리팩토링은 기존의 기능을 변경하거나 추가하지 않기 때문에, 보통 버전 업데이트를 수반하지 않습니다.

2. 명명 및 스타일 가이드

  • 변수: 형용사+명사
  • 함수: 동사+명사
  • boolean변수: be동사 + 주어 + 보어(명사, 대명사, 형용사 등)
  • 클래스/객체/모듈 안에서 속성처럼 쓰이는 경우 (긴 계산이 아니라, 속성값과 유사한 경우) 명사로도 사용 가능

3. 함수형 리팩토링

계산한 데이터를 영원히 간직할 필요 없이 일시적으로 한번만 계산해야한다면 변환 함수를 사용하는 것이 더 좋음

변환 함수 사용 시 주의할 점

  • 불변성 유지
// const reading = { customer: "ivan", quantity: 10, month: 5, year: 2017 };

export function acquireReading() {
  return reading;
}

const rawReading = acquireReading();
rawReading.quantity = 20;  // 데이터를 직접 변경

위처럼 데이터를 복사하지 않고 변경하게 되면 원본 데이터가 변경되기 때문에 이후 reading이나 acquireReading를 사용하는 다른 곳에서는 원본이 아닌 변경된 데이터를 사용하게 되어 문제가 발생할 수 있다.

  • 파생된 값 동기화

불변성을 주의하여 깊은 복사해 사용하는 경우에도 주의해야할 점이 있다.

변환 함수의 경우 호출 시에만 계산해서 영원히 변수에 저장해둔다는 특징이 있다.

// const original = { customer: "ivan", quantity: 10, month: 5, year: 2017 };

function enrichReading(original) {
  const result = JSON.parse(JSON.stringify(original));
  result.baseCharge = calculateBaseCharge(result);
  result.taxableCharge = Math.max(
    0,
    result.baseCharge - taxThreshold(result.year),
  );
  return result;
}

const enrichedReading = enrichReading(rawReadingData);
enrichedReading.quantity = 20;
console.log(enrichedReading.baseCharge);
console.log(enrichedReading.taxableCharge);

예를 들어 위처럼 변환 함수를 사용하여 기존 객체에 새로운 속성을 추가하는 경우 이 함수가 실행된 시점에서만 계산되어 저장되기 때문에 'enrichedReading.quantity = 20;'으로 값을 바꿔주더라고 console에는 quantity가 20으로 바뀌어 동적으로 계산된 값이 아닌 10이었던 예전 값을 출력하게 된다.

이처럼 변환 함수의 경우 값을 변경하면 파생된 값들이 더 이상 올바르지 않게된다는 문제가 발생한다.

그렇기에 변환 함수는 읽기 전용으로 사용될 때만 사용하는 것이 좋다.

이 문제를 해결하기 위해서는 2가지 방법이 있다.

  • 함수를 재호출해 업데이트 시키기
enrichedReading.quantity = 20;  // 값 변경
const updatedReading = enrichReading(enrichedReading);

console.log(updatedReading.baseCharge);  // 재계산된 값
console.log(updatedReading.taxableCharge);   // 재계산된 값
  • 변환 함수가 아닌 클래스를 만들어 get으로 불러오기

4. 객체 지향 리팩토링

장점

  • 소프트웨어와 아키텍쳐가 복잡해질수록 좀 더 고립되고 독립된 모듈로 만들어나갈 수 있음
  • 함수를 다 쪼개다보면 여러 함수들이 만들어지게되고 자칫 복잡해질 수도 있는데 클래스로 만들어두면 직관적인 걸 유지하면서 캡슐화까지할 수 잇음
  • 함수를 다 쪼개서 한 함수 안에서 호출하다보면 매개변수를 인자로 넘겨주게 되는데 클래스를 사용하면 생성자에서 한 번만 받아서 내부적으로 사용할 수 있어 코드가 깔끔해짐
  • 확장성
  • 좀 더 명시적으로 의도를 나타낼 수 잇음

주의할 점

  • 상속 때문에 확장성이 떨어지거나 유지보수성이 떨어지는 경우가 있음. 상속 쓰기 전에 다른 방법은 없는지 고민
  • 클래스도 복사한 값이나 새로운 인스턴스로 반환하지 않으면 외부에서 접근해서 값 변경 가능
  • 클래스가 제공하는 메서드 중 절반이 다른 클래스를 위임하고 있다면,.모듈사이 거래가 많으면 리팩토링 대상
  • 위임 숨기기
const person = new Person('Tom', new Department('aManager', '999'));
console.log(person.name);
console.log(person.department.manager);
console.log(person.department.chargeCode);

캠슐화한 클래스 내부에서 위임을 해서 사용하고 있다면 이처럼 외부에 노출시키는 건 좋지 않음

외부에서 접근시키지 않도록 하려면 private(#)를 사용

5. 기타

  • 실패하는 케이스도 테스트 코드 작성(이 경계를 벗어나면 어떠한 일이 벌어지는지에 대한 테스트)
  • 매개 변수 직접 수정하지 말고 let으로 할당해 계산
  • 함수를 한번만 선언해서 호출하는 것과 필요할 때마다 인스턴스를 생성하는 것에서 비용적인 차이가 있을 수 있음

'웹 개발 > 웹 개발' 카테고리의 다른 글

CSS 애니메이션  (0) 2023.09.26
mkcert를 이용해 로컬 환경 https로 실행시키기  (0) 2023.09.04
모노레포 (MonoRepo) 설정  (0) 2023.08.18
ESLint 설정  (1) 2023.07.19
rgba와 opacity  (1) 2023.04.18
Comments