Refactoring

높은 품질의 코드는 유지 관리 비용을 절감하고, 오류를 줄이며, 개발자의 만족도를 향상시킨다. 높은 품질의 코드를 얻는 가장 일반적인 방법이 바로 "리팩토링"이다.

리팩토링이란

리팩터링은 '기능을 변경하지 않고 코드의 가독성과 유지보수가 쉽도록 코드를 변경하는 것'

  • 리팩터링을 수행하려면 리팩터링 대상을 식별하는 스킬리팩터링 단계를 명시적으로 가진 문화, 리팩터링을 돕는 도구조합이 필요하다.

리팩터링을 해야하는 이유

  • 코드를 더 빠르게 만들기 위해

  • 더 작은 코드를 만들기 위해

  • 코드를 더 일반적이거나 재사용 가능하게 하기 위해

  • 코드의 가독성을 높이고 유지보수를 용이하게 하기 위해

좋은 코드란 사람이 읽기 쉽고, 유지보수가 용이하며, 의도한 대로 잘 동작하는 코드

더 자세히 풀어보자면

  • 코드를 작성하는 시간보다 코드를 읽고 이해하는데 더 많은 시간을 보내게 된다.

    • 복잡한 분야에서 작업하기에 이해 하지 못하고 변경하게되면 치명적인 장애가 발생할 수 있기 때문

  • 코드베이스의 가독성을 높이면 새로운 기능을 구현하기 위한 시간을 확보할 수 있게된다. (경제적인 이유)

  • 유지보수가 용이해지면 버그 발생이 줄어들고 수정이 쉬워진다.

  • 좋은 코드베이스는 생각하기 편하기 때문

    • 디버깅이 지옥인 이유는 우리가 코드를 읽을 때 코드가 하는 일에 대해 머릿속으로 해석하게되는데 머릿속에서 한 번에 많은 것을 기억해야 할수록 더 지치게 된다.

개발 작업 절차

  1. 탐색(Explore): 무언가 만들어야 할지 확신이 안서거나 또는 문제를 해결할 수 있을지조차 모르는 경우엔 항상 실험부터 시작해야 한다. 무언가를 신속하게 구현하면 고객이 무엇을 필요로하는지 함께 확인할 수 있게된다.

  2. 명세화(Specify): 무엇을 만들지 알게 되면 그것을 명세화한다. 최적의 경우 이것은 자동화된 테스트의 형태가 된다.

  3. 구현(Implement): 코드를 구현

  4. 테스트(Test): 코드가 2단계의 명세(사양)를 따르는지 확인

  5. 리팩터링(Refactor): 코드를 전달하기 전에 다음 사람이 쉽게 작업할 수 있는지 확인(다른 사람이 내가 될 수 있음)

  6. 전달(Deliver): 가장 일반적으로 PR(pull request) 또는 특정 브랜치로 push하는 작업

레거시 시스템에서의 리팩터링

  • 대규모 레거시 시스템에서 리팩터링을 시작할 때 모든 것을 중단한 후 코드베이스 전체를 먼저 리팩터링할 필요없이 일상 업무에 결합할 수 있는 좋은 방법은 "우선 변경하기 쉽게 만든 후 변경하라 - 켄트 백"

  • 새로운 것을 구현할 때마다 새 코드를 쉽게 추가할 수 있게 리팩터링을 먼저 한다.

규칙

일반적으로 코드 스멜은 리팩터링 대상을 설명하는데 쓰인다. 하지만 이것들은 모호해서 주니어들이 내면화하기 어렵기 때문에 코드 스멜을 대체할 구체적인 규칙을 제공한다.

  • 다섯 줄 제한: 메서드는 사용하는 데이터 구조를 탐색하는데 필요한 것보다 더 많은 코드로 구성돼서는 안된다.

  • 호출 또는 전달, 한가지만 할 것: 함수 내에서 객체에 있는 메서드를 호출하거나 객체를 인자로 전달할 수 있지만 둘은 섞어 사용해서는 안된다.

  • if 문은 함수의 시작에만 배치: if 문이 있는 경우 해당 if 문은 함수의 첫 번째 항목이어야 한다.

  • if 문에서 else를 사용하지 말 것: 프로그램에서 이해하지 못하는 타입(형)인지를 검사하지 않는 한 if문에서 else를 사용하지 않기

  • switch를 사용하지 말 것: default 케이스가 없고 모든 case에 반환 값이 있는 경우가 아니라면 switch를 사용하지 않기

  • 인터페이스에서만 상속받을 것: 상속은 클래스나 추상 클래스가 아닌 오직 인터페이스를 통해서만 받는다.

  • 순수 조건 사용: 조건문의 조건식에서는 변수에 값을 할당하거나 예외를 발생시키거나 I/O와 상호작용해서는 안된다.

  • 구현체가 하나뿐인 인터페이스를 만들 지 말 것: 구현체가 하나뿐인 인터페이스를 사용하지 말기

  • getter와 setter를 사용하지 말 것: 부울이 아닌 필드에 setter나 getter를 사용하지 않기

  • 공통 접사를 사용하지 않기: 코드에는 공통 접두사나 접미사가 있는 메서드나 변수가 없어야 한다.

리팩토링 패턴

  • 메서드 추출: 한 메서드의 일부를 가져와 고유한 메서드로 추출

  • 클래스로 타입 코드 대체: 열거형을 인터페이스로 변환하고 열거형의 값을 클래스로 만든다.

  • 클래스로의 코드 이관: 기능을 클래스로 옮기기 때문에 클래스로 타입 코드 대체는 클래스로의 코드 이관으로 자연스럽게 이어진다.

  • 메서드 인라인화: 프로그램의 가독성에 더 이상 도움을 주지 않는 메서드를 제거

  • 메서드 전문화: 메서드에 불필요하고 문제가 있는 일반성을 제거

  • 삭제 후 컴파일하기: 인터페이스와 클래스의 전체 사용 범위를 알고 있는 경우 사용하지 않는 메서드를 인터페이스와 클래스에서 제거

  • 유사 클래스 통합: 일련의 상수 메서드에서 서로 다른 두 개 이상의 클래스를 통합

  • if 문 결합: 동일한 본문을 가진 if문이 이어진 경우 if 문의 분기들을 결합해서 중복을 줄임

  • 전략 패턴의 도입: if문을 사용한 분기를 클래스를 인스턴스화하는 것으로 대체

  • 구현에서 인터페이스 추출: 클래스에 대한 종속성을 인터페이스로 바꿈

  • getter와 setter 제거하기: 기능을 데이터에 더 가깝게 옮겨 getter와 setter를 제거

  • 데이터 캡슐화: 변수와 관련된 불변속성을 지역화하고 응집력을 더 명확히 한다.

  • 순서 강제화: 특정 순서대로 작업을 수행하는 것을 컴파일러가 보장하게 한다.

Last updated