SW/DevOps

DevOps : 리팩토링 기법 (1)

얇은생각 2019. 12. 12. 07:30
반응형

코드 리팩토링

 

설명변수로 변환

설명변수로 변환하는 방법에 대해서 알아보겠습니다. if문 등 조건문의 복잡한 표현을 나누는 가장 쉬운 방법은 작게 분리한 설명 변수를 사용하는 방법이 있습니다.

 

 

제어 플래그 삭제

상태를 기록하고 처리 흐름을 제어하기 위한 boolean type 변수를 제어 플래그라고 합니다. 이 제어 플래그를 지나치게 사용하면 처리 흐름을 파악하기 어려워져서 전체를 파악하기 어렵습니다. 그럴 경우 break, continue, return 등을 사용해 조건문의 의미와 제어 흐름을 명확하게 합니다.

플래그명도 flag, state보다는 found, initialized, debug, error, done, aborted, interrupted 등의 이름이 더 좋습니다. 배열을 객체로 변환하는 방법에 대해서 알아보겠습니다. 배열 안의 요소들이 서로 다른 의미를 가진 경우 객체를 통해 보다 명확히 사용하는 것이 좋습니다.

 

 

메서드

메서드에 대해서 알아보겠습니다. 메서드는 15줄 이내로 작성하도록 하고 넘을 경우 분리가 가능한지 살펴보는 것이 좋습니다. 복잡하고 긴 소스코드는 대부분 응집도가 낮고 결합도가 높은 편으로 소스 품질을 떨어뜨리고 유지보수를 저해하는 주요 원인 중 하나입니다.

단지 개발하기 편하기 위해 차례대로 실행하는 절차지향적으로 만든 메서드에 다른 객체, 또는 다른 메서드에 위임해야 하는 기능들을 모두 포함하게 되면 특정 기능을 파악하고 유지하기 불가능할 정도로 모든 기능이 거미줄처럼 엉키게 됩니다.

샘플 코드는 고객 식별자를 url로 받아 해당 고객의 전체 은행 계좌별 잔액 등을 목록으로 보여주고 있는데 네모 부분의 반복문에서 계좌번호가 유효한지 판단하는 부분이 로직입니다. 하지만 HTTP 요청 처리, DB로부터 데이터 획득, 결과 데이터를 JASON 등의 반환 등의 여러 기능이 한 메서드에 있어서 로직 파악, 테스트 코드 작성 모두 어렵습니다.

 

 

메서드 추출 기법

메서드가 길 경우 그룹화 가능한 코드 단위를 추출해서 별도의 메서드로 만든 후 호출하는 방식으로 변경할 수 있습니다.

shouldFollowPasswordRules 메서드는 입력받은 패스워드가 지정된 특수문자를 포함하는지 결과를 체크하는 메서드입니다. 이후 1번 메서드라고 하겠습니다. 리팩토링 기법 중 메서드 추출을 통해 패스워드 체크 로직을 화면 우측처럼 hasSpecialCharactor로 분리하였습니다. 이후 2번 메서드라고 하겠습니다.

코드 라인도 줄었지만 1번 메서드는 입력 받을 때마다 특수문자 체크를 2번 메서드를 호출하면 되니 코드가 단순해집니다. 즉, 체크할 특수문자가 변경되어도, 1번 메서드는 상세히 알 필요 없이 호출만 하면 되고, 특수 문자 체크를 담당하는 2번 메서드만 변경이 일어납니다.

객체지향의 장점 중 하나인 캡슐화를 통해 코드의 영향이 최소화됩니다. 추가적으로 코드 단위를 짧게 하면 소스코드가 스스로를 설명하는 형태로 바뀜으로 메서드명이 주석을 대신할 수 있습니다.

 

 

메서드 객체로 대체하는 방법

전달하는 파라미터 목록이 점점 많아지는 경우가 자주 발생할 수 있습니다. 단위당 전달하는 파라미터는 4개 이하로 제한하도록 하고, 그 이상은 메서드 객체로 대체하는 방법을 사용하도록 합니다.

화면 좌측의 compute 메서드는 메서드 추출 기법을 적용하기에 좋지만 이 코드엔 여러 개의 로컬 변수가 있습니다. 메서드 추출 기법을 쓰면 추출한 메서드의 파라미터 여러 개를 넘겨줘야 합니다.

메서드 객체로 대체한다면 우선 리팩토링할 메서드 PriceCalculator를 새로 만듭니다. 많은 지역 변수 파라미터를 새로 만든 클래스의 private 필드로 바꿉니다. 같은 클래스 메서드는 모두 이 필드에 접근할 수 있으므로 파라미터는 더 이상 전달할 필요가 없습니다.

간단한 파라미터를 가지게 되어 이행, 테스트, 재사용이 쉬운 코드로 바뀝니다. 추가적으로 Java8의 동작 파라미터화도 참고하면 좋습니다.

 

 

점점 복잡해지는 소스 코드

소스코드는 시간이 지남에 따라 버그 수정, 요구사항 추가, 변경 등의 이유로 점점 복잡해지는 특성이 있습니다. 그래서 코드를 유지보수 가능한 형태로 만들려면 복잡도에 제한을 두고 반드시 측정해야 합니다.

복잡도를 산출하는 가장 일반적인 방법은 코드가 지나가는 가능한 경로의 수를 세어보는 것으로, 코드 조각을 지나가는 경로가 많을수록 복잡도는 증가합니다. 실행 흐름이 나뉘어 진행되는 조건문 같은 분기점 개수를 세어보면 경로 개수를 명확히 알 수 있습니다.

if, switch case, 단항 연산자, and, or, class, 패키지, 전체 코드 베이스도 분기점이 될 수 있습니다. 실행 흐름이 분기되는 분기점 개수를 4개로 제한하는 것이 좋습니다.

 

 

분기문 중첩 해소

분기문 중첩 해소에 대해서 알아보겠습니다. 분기문의 경우 계속 중첩될 경우 코드가 우측으로 화살표 모양으로 계속 들어가면서 점점 로직을 파악하기 힘들게 바뀌어 갑니다. error exception 처리나 반환 처리를 미리 할 수 있다면 화면 우측처럼 바꿀 수 있습니다.

 

 

분기문 분해 알고리즘

분기문 분해 알고리즘 교체에 대해서 알아보겠습니다. 메서드 내부에서 사용하는 알고리즘이 알기 어려울 경우 알고리즘을 추출 및 교체하는 기법을 사용할 수 있습니다. 메서드 분기점은 4개 이하로 하는 것이 좋습니다.

통합 코드 문자열을 입력받아 국가 이름을 가져오는 메서드를 중간에 호출합니다. 국가의 이름을 다루는 메서드는 switch문을 통해 처리합니다. 참고로 예외 처리는 임시로 처리하였습니다.

getCountryName 메서드는 테스트 케이스를 나라별로 만들어야 하고, 점차 복잡해질 가능성이 높습니다. 미국 항목을 추가하면서 코드를 복사 붙여넣기 할 때 자칫 break문이 빠지면 베트남은 미국 이름을 return하게 됩니다. getCountryName의 switch문을 맵을 사용하여 리팩토링 후 복잡도를 낮출 수 있습니다.

 

 

분기문 분해 enum

앞에서 만든 COUNTRY_MAP을 좀 더 리팩토링해보면 Country map은 나라 목록을 관리하고, CurrencyInfo는 enum 형태로 나라별 숫자 코드, 나라 이름으로 나눌 수 있겠습니다. 분기문 분해 다형성에 대해서 알아보겠습니다. 더 나아가 객체 지향의 특징 중 하나인 다형성을 이용해 조건문을 대체할 수도 있습니다.

이렇게 하면 나중에 새로운 국가가 추가되더라도 새로운 국가 정보만 추가하면 되는 것이 장점인 반면 여러 클래스가 생겨서 복잡해지는 단점도 있습니다. 리팩토링을 적용 시 항상 프로젝트 상황에 따라 적절히 선택해야 합니다.

반응형

'SW > DevOps' 카테고리의 다른 글

DevOps : 리팩토링 기법 (3)  (0) 2019.12.14
DevOps : 리팩토링 기법 (2)  (0) 2019.12.13
DevOps : 리팩토링 개념과 필요성  (0) 2019.12.11
DevOps : SonarQube 사용법  (0) 2019.12.10
DevOps : SonaQube 특징, 개요, 기능  (0) 2019.12.09