CS Repository/리팩터링

[리팩터링] 리팩터링 가이드

조금씩 차근차근 2025. 8. 23. 15:12

읽기 쉬운 코드는 생산성을 크게 올려주기에, 읽기 어려운 코드에 적극적으로 리팩토링을 도입해야 한다.

 

그렇다면 뭐가 읽기 어려운 코드일까?

 

지금부터 "코드 냄새"라고 불리는 아래 내용들을 하나씩 짚어볼 것이다.

  • 기이한 이름
  • 중복 코드
  • 긴 함수
  • 긴 매개변수 목록
  • 전역 데이터
  • 가변 데이터
  • 뒤엉킨 변경
  • 산탄총 수술
  • 기능 편애
  • 데이터 뭉치
  • 기본형 집착
  • 반복되는 switch문
  • 반복문
  • 성의 없는 요소
  • 추측성 일반화
  • 임시 필드
  • 메시지 체인
  • 중개자
  • 내부자 거래
  • 거대한 클래스
  • 서로 다른 인터페이스의 대안 클래스들
  • 데이터 클래스
  • 상속 포기
  • 주석

기이한 이름

  • '컴퓨터 과학에서 어려운 건 딱 두 가지, 캐시 무효화와 이름 짓기다.'
  • 이름이 이상하면, 코드를 이해하기 어렵다.
  • 코드는 단순하고 명료하게.
  • 마땅한 이름이 떠오르지 않는다면 설계에 더 근본적인 문제가 숨어 있을 가능성이 높다.
    • 도메인 용어가 충분히 나오지 않았다는 의미이다.

필요한 리팩터링

  • 함수 선언 바꾸기 (6.5절)
  • 변수 이름 바꾸기 (6.7절)
  • 필드 이름 바꾸기 (9.2절)

중복 코드

  • 똑같은 코드 구조가 여러 곳에서 반복된다면 하나로 통합하여 더 나은 프로그램을 만들 수 있다.

필요한 리팩터링

  • 함수 추출하기(6.1절)
    • 한 클래스에 딸린 두 메소드가 똑같은 표현식을 사용하고 있는 경우
  • 문장 슬라이드하기(8.6절)
    • 코드가 비슷하긴 한데 완전히 똑같지는 않다면
    • 비슷한 부분을 한 곳에 모아 함수 추출하기가 가능한지 살펴본다.
  • 메서드 올리기(12.1절)
    • 같은 부모로부터 파생된 서브클래스들에 코드가 중복되어 있을 경우

긴 함수

  • 오랜 기간 잘 활용되는 프로그램들은 하나같이 짧은 함수로 구성됐다.
    • 짧은 함수들로 구성된 코드베이스를 얼핏 훑으면 연산하는 부분이 하나도 없어 보인다.
  • 함수 이름을 잘 지어두면 본문 코드를 볼 이유가 사라진다.
    • 그러니 훨씬 적극적으로 함수를 쪼개야 한다.
    • 주석을 달아야 할 만한 부분은 무조건 함수로 만들어라.

필요한 리팩터링

  • 함수 추출하기
  • 임시 변수를 질의 함수로 바꾸기
    • 임시변수의 수를 줄일 때
  • 매개변수 객체 만들기
    • 매개변수의 수를 줄일 때
  • 객체 통째로 넘기기
    • 매개변수의 수를 줄일 때
  • 함수를 명령으로 바꾸기
    • 위 방법을 다 써도, 여전히 임시 변수와 매개변수가 너무 많을 때
  • 조건문 분해하기
    • 조건문 제거하기
  • 조건부 로직을 다형성으로 바꾸기
    • switch문이 여러개면
  • 반복문 쪼개기
    • 추출한 반복문 코드에 적합한 이름이 떠오르지 않을 경우
      • 두 가지 이상의 일을 하고 있을 가능성 높음

긴 매개변수 목록

  • 매개변수가 길면 함수가 뭘 쓰는지 이해하기 어려워진다.

필요한 리팩터링

  • 매개변수를 질의 함수로 바꾸기
    • 다른 매개변수에서 값을 얻어올 수 있는 매개변수가 있을 때
  • 객체 통째로 넘기기
    • 원본 데이터 구조 그대로 전달할 수 있을 때
  • 매개변수 객체 만들기
    • 항상 함께 전달되는 매개변수가 있을 때
  • 플래그 인수 제거하기
    • 함수의 동작 방식을 정하는 플래그 역할의 매개변수가 있을 때

전역 데이터

  • 전역 변수는 해롭다.
    • 디버깅하기 어렵다.
    • 이 값의 변경이 어디에 영향을 끼칠지 파악하기 어렵다.

필요한 리팩터링

  • 변수 캡슐화하기

가변 데이터

  • 이 데이터의 변경이 예상치 못한 결과나 골치 아픈 버그로 이어지게 될 수 있다.
  • 참고) 사이드 이펙트
    • 값의 변경으로 다른 객체에 예상하지 못한 결과가 일어나는 것
  • 그래서 함수형 프로그래밍에서는
    • 데이터는 절대 변하지 않고
    • 데이터를 변경하려면 반드시 (원래 데이터는 그대로 둔 채) 변경하려는 값에 해당하는 복사본을 만들어서 반환한다.

필요한 리팩터링

  • 변수 캡슐화하기
    • 정해놓은 함수를 거쳐야만 값을 수정할 수 있도록
  • 변수 쪼개기
    • 하나의 변수에 용도가 다른 값들을 저장하느라 값을 갱신하는 경우 사용
    • 용도 별로 독립 변수에 저장하게 하기
  • 문장 슬라이드하기, 함수 추출하기
    • 갱신 로직이 조회 로직과 함께 있을 경우
    • 무언가를 갱신하는 코드로부터 부작용이 없는 코드를 분리한다.
  • 질의 함수와 변경 함수 분리하기
    • API를 만들 때
    • 꼭 필요한 경우가 아니라면 사이드 이펙트가 있는 코드를 호출할 수 없도록
  • 세터 제거하기
    • 무분별한 세터가 남용될 여지가 있을 경우
  • 여러 함수를 클래스로 묶기
    • 변수를 갱신하는 코드들의 유효범위를 제한해야 할 때
  • 여러 함수를 변환 함수로 묶기
    • 변수를 갱신하는 코드들의 유효범위를 제한해야 할 때
  • 참조를 값으로 바꾸기
    • 구조체처럼 내부 필드에 데이터를 담고 있는 변수일때
    • 내부 필드 수정을 직접 하지 말고, 구조체를 통째로 교체해라.

뒤엉킨 변경

  • 수정할 곳이 한군데가 아닐 때
    • 지원해야 할 데이터베이스가 추가될 때마다 함수 세 개를 바꿔야 할 때
    • 금융 상품이 추가될 때마다 또다른 함수 네 개를 바꿔야 할 때
  • SRP가 제대로 지켜지지 않을 때 나타난다.
    • 하나의 모듈이 서로 다른 이유들로 인해 여러 가지 방식으로 변경되는 일이 많을 때

필요한 리팩터링

  • 단계 쪼개기
    • 순차적으로 실행되는게 자연스러운 맥락인 경우
    • 다음 맥락에 필요한 데이터를 특정한 데이터 구조에 담아 전달하기
  • 함수 옮기기
    • 전체 처리 과정 곳곳에서 각기 다른 맥락의 함수를 호출하는 빈도가 높을 경우
    • 각 맥락에 해당하는 적당한 모듈들을 만들어서 관련 함수들을 모은다.
  • 함수 추출하기
    • 여러 맥락의 일에 관여하는 함수가 있다면
  • 클래스 추출하기
    • 모듈이 클래스일 경우

산탄총 수술

  • 뒤엉킨 변경과 비슷하면서도 정반대
  • 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때

필요한 리팩터링

  • 함수 옮기기
    • 변경되는 대상들을 한 곳에 모아두기
  • 필드 옮기기
    • 변경되는 대상들을 한 곳에 모아두기
  • 여러 함수를 클래스로 묶기
    • 비슷한 데이터를 다루는 함수가 많을 경우
  • 여러 함수를 변환 함수로 묶기
    • 데이터 구조를 변환하거나 보강(enrich)하는 함수를 묶을 때
  • 단계 쪼개기
    • 묶은 함수들의 출력 결과를 묶어서 다음 단계의 로직으로 전달할 수 있다면
  • 함수 인라인하기
    • 어설프게 분리된 로직을 일단 합칠 때
  • 클래스 인라인하기
    • 어설프게 분리된 로직을 일단 합칠 때

기능 편애

  • 어떤 함수가 자신이 속한 모듈의 함수나 데이터보다, 다른 모듈의 함수나 데이터와 상호작용할 일이 더 많은 경우

필요한 리팩터링

  • 함수 옮기기
    • 해당 모듈로 옮겨주기
  • 함수 추출하기
    • 함수의 일부에서만 "기능 편애"가 발생할 떄
    • 함수가 사용하는 모듈이 다양할 때

데이터 뭉치

  • 항상 비슷한 데이터 서너 개가 함께 나열될 때
  • 판별법
    • 값 하나를 지워보자.
      • 나머지 데이터만으로는 의미가 없다면
      • 객체로 환생하길 갈망하는 데이터 뭉치다.

필요한 리팩터링

  • 클래스 추출하기
    • 하나의 객체로 묶기
  • 매개변수 객체 만들기
    • 메서드 시그니처에 있는 파라미터 합치기
  • 객체 통째로 넘기기
    • 메서드 시그니처에 있는 파라미터 합치기

기본형 집착

  • 화폐, 좌표, 구간 등을 기본형으로 사용하지 마라.
    • 문자열화된 자료형과 다를 바가 없다.

필요한 리팩터링

  • 기본형을 객체로 바꾸기
  • 타입 코드를 서브클래스로 바꾸기
  • 조건부 로직을 다형성으로 바꾸기
  • 클래스 추출하기
  • 매개변수 객체 만들기

반복되는 switch문

  • switch statement를 피해라.
    • switch expression 하곤 다름

필요한 리팩터링

  • 조건부 로직을 다형성으로 바꾸기

반복문

  • 반복문은 파이프라인에 비해 가독성이 떨어진다.

필요한 리팩터링

  • 반복문을 파이프라인으로 바꾸기

성의 없는 요소

  • 아무런 역할을 갖지 않는 클래스, 함수들

필요한 리팩터링

  • 함수 인라인하기
  • 클래스 인라인하기
  • 계층 합치기

추측성 일반화

  • '나중에 필요할 거야'라는 생각으로 당장은 필요 없는 모든 종류의 역할과 특이 케이스 처리 로직을 작성해둔 경우
    • 코드를 이해하거나 관리하기 어려워진다.

필요한 리팩터링

  • 계층 합치기
    • 하는 일이 거의 없는 추상 클래스
  • 함수 인라인하기
    • 쓸데없이 위임하는 코드
  • 클래스 인라인하기
    • 쓸데없이 위임하는 코드
  • 함수 선언 바꾸기
    • 본문에서 사용되지 않는 매개변수
  • 죽은 코드 제거하기
    • 테스트코드 말고는 사용하는 곳이 없는 함수나 클래스

임시 필드

  • 특정 상황에서만 값이 설정되는 필드를 가진 클래스
  • 하지만 객체를 가져올 때는 당연히 모든 필드가 채워져 있으리라 기대하는 게 보통이다.

필요한 리팩터링

  • 클래스 추출하기
  • 함수 옮기기
  • 특이 케이스 추가하기
    • 임시 필드들이 유효한지를 확인한 후 동작하는 조건부 로직이 있을 때
    • 필드들이 유효하지 않을 때를 위한 대안 클래스를 만들어두기

메시지 체인

  • 우리가 메소드를 호출할 때,
    • A.getB().getC().getD() 이것보다
    • DGenerator.get(A) 가 있는게 좋을 수 있다.

필요한 리팩터링

  • 위임 숨기기

중개자

  • 중개자는 잘 쓰면 결합도를 낮춰주지만, 지나치면 문제가 된다.

필요한 리팩터링

  • 중개자 제거하기
  • 함수 인라인하기

내부자 거래

  • 모듈 사이에 결합도가 높아졌을 경우
    • 모듈 사이에 주고받는 데이터가 너무 많을 경우

필요한 리팩터링

  • 함수 옮기기
  • 필드 옮기기
  • 위임 숨기기
    • 공통 부분을 정식으로 처리하는 제 3의 모듈을 만들기.
  • 서브클래스를 위임으로 바꾸기
  • 슈퍼클래스를 위임으로 바꾸기

거대한 클래스

  • 한 클래스가 너무 많은 일을 하려고 하는 경우

필요한 리팩터링

  • 클래스 추출하기
  • 슈퍼클래스 추출하기
  • 타입 코드를 서브클래스로 바꾸기

서로 다른 인터페이스의 대안 클래스들

  • 설계 단계에서 동일한 책임을 가진 클래스들이 서로 다른 인터페이스를 갖는 경우

필요한 리팩터링

  • 함수 선언 바꾸기
  • 함수 옮기기
  • 슈퍼클래스 추출하기

데이터 클래스

  • 게터/세터로만 구성된 클래스
  • 단계 쪼개기로 나온 중간 데이터 구조는 예외다.
    • 이는 본질적으로 Immutable하게 설계한다.

필요한 리팩터링

  • 레코드 캡슐화하기
  • 세터 제거하기
  • 함수 옮기기
  • 함수 추출하기

상속 포기

  • 상속을 받는데, 필요없는 데이터를 포함해서 상속받는 경우

필요한 리팩터링

  • 상속 메커니즘에서 벗어나기
    • 서브클래스를 위임으로 바꾸기
    • 슈퍼클래스를 위임으로 바꾸기

주석

  • 주석을 달면 안 된다고 말하는게 아님.
    • 뭘 할지 모를 때는 주석을 다는게 좋다.
    • 현재 진행 상황뿐만 아니라 확실하지 않은 부분에 주석을 남긴다.
    • 코드를 지금처럼 작성한 이유를 설명할 때 주석을 남긴다.

필요한 리팩터링

  • 함수 추출하기
    • 특정 코드 블록이 하는 일에 주석을 남기고 싶다면
  • 함수 선언(이름) 바꾸기
    • 이미 추출되어 있는 함수임에도 여전히 설명이 필요하다면
  • 어서션 추가하기
    • 시스템이 동작하기 위한 선행 조건을 명시하고 싶다면

 

본 글은 마틴 파울러의 리팩터링을 참고하여 작성되었습니다.

 

리팩터링 2판 - 예스24

개발자가 선택한 프로그램 가치를 높이는 최고의 코드 관리 기술마틴 파울러의 『리팩터링』이 새롭게 돌아왔다.지난 20년간 전 세계 프로그래머에게 리팩터링의 교본이었던 『리팩토링』은,

www.yes24.com