리팩토링이란 무엇인가?
리팩토링(Refactoring)은 소프트웨어를 보다 쉽게 이해할 수 있도록 만드는 작업입니다. 이해하기 쉬운 코드가 수정하기도 쉽기 때문에 수정하기 쉬운 코드를 만든다는 또 하나의 목적 역시 자연스럽게 달성됩니다. 리팩토링은 들어가는 리소스를 최소화하기 위해서 외부 동작의 변화 없이 소프트웨어의 구조를 바꾸는 것을 중심으로 진행이 됩니다.
왜 리팩토링을 해야 하는가?
리팩토링이 적절하게 진행되면 소프트웨어가 이해하기 쉬워지고 수정도 용의해 집니다. 여기서 얻을 수 있는 다양한 이익이 리팩토링을 해야하는 이유가 됩니다.
이해하기 쉽고 수정하기 쉬워지니 개발자의 개발 속도가 자연스럽게 빨라집니다. 그리고 이해하기 쉽기 때문에 버그가 있다면 훨씬 쉽게 발견할 수 있습니다. 리팩토링을 마친 다음 뿐만 아니라 코드의 구조를 수정하면서 버그를 발견할 가능성이 더 높을 수 도 있겠네요.
언제 리팩토링을 해야 하는가?
사실 현업에서 납기에 쫓기는 상황이라면 리팩토링을 하기가 쉽지 않을 수도 있습니다. 하지만 리팩토링을 하는 것은 오히려 작업시간을 단축시킬 수 있는 방법입니다. 따라서 기본적으로 리팩토링은 별도의 시간을 내는 것이 아니라 틈틈히 계속 진행되어야 합니다. 또한 다음과 같은 경우에 리팩토링을 진행하는 것이 권장됩니다.
비슷한 것을 세 번째로 하게 되면 리팩토링을 해야 하는 신호입니다. 이것을 삼진규칙이라고 합니다.. 기능을 추가할 때도 리팩토링을 하는 것이 좋습니다. 기능을 추가할 때 어떤 부분을 수정해야 할지 더 쉽게 판단할 수 있기 때문이죠. 버그를 수정할 때도 리팩토링을 할 필요가 있습니다. 이미 언급한 것처럼 리팩토링을 하는 도중에 버그가 발견될 수도 있고 이해하기 쉬운 코드에서 버그를 찾기기 훨씬 쉬우니까요. 그리고 팀원들과 함께 code review를 할 때 리팩토링을 할 필요가 있습니다. 리팩토링을 진행하는 과정을 공유하면서 팀 전체에 지식이 공유될 수도 있고 다양한 관점으로 보기 때문에 디자인이나 코드의 장단점을 더 잘 파악할 수 있습니다.
관리자에게 리팩토링을 어떻게 설명해야 할까?
만약 프로그래밍 경험이 없는 관리자라면 이미 동작하고 있는 코드를 수정하는 것이 불필요하게 느껴질 수도 있습니다. 하지만 리팩토링은 전체 작업 속도를 높일 수 있는 비결입니다. 이 점을 관리자에게 잘 전할 수 있어야 합니다.
우선 팀원들이 함께 기술적 검토를 하는 것이 버그를 줄이고 개발 속도를 빠르게 하는 중요한 방법이라는 것을 알립니다. 그리고 새로운 기능을 추가해야 할 때 디자인 상 변경이 쉽지 않다면 먼저 리팩토링을 하고 그 다음에 기능을 추가하는 것이 훨씬 빠릅니다. 이 점을 가능한 구체적으로 설명하는 것이 관리자를 이해시키는데 도움이 될 것입니다.
코드 속의 나쁜 냄새(bad smell)
비효율적이고 이해하기 쉽지 않은 코드를 인지했을 때 나쁜 냄새(bad smell)가 있다고 이야기 합니다. 어떨 때 우리는 bad smell을 느끼고 리팩토링을 시작해야 할까요?
|중복된 코드가 있을 때(Duplicated Code)
가장 단순한 경우는 서로 다른 두 메소드 안에서 같은 코드가 있을 때입니다. 이 때는 중복되는 코드를 하나의 메소드로 빼낼 필요가 있겠죠?
갖은 수퍼클래스를 가진 두 클래스에서 같은 코드가 나타난 경우도 리펙토링 대상입니다. 수퍼클래스에 반복되는 내용을 정의하고 상속을 받는다면 훨씬 깔끔한 구조가 되겠죠?
|메소드가 지나치게 길 때(Long Method)
메소드는 가능한 길이가 짧을 수록 널리 활용되고 또 마지막까지 활용될 가능성이 높아집니다. 메소드는 하나의 input에 하나의 output이 나오는 것이 이상적입니다. 또한 메소드의 이름은 그 기능을 명확히 정의하는 것으로 지어서 내용을 보지 않더라도 기능을 파악할 수 있도록 짓습니다. 뭔가 주석을 달아야겠다는 생각이 든다면 대신 단일한 기능을 가진 메소드로 분리해 내는 것을 먼저 고민해보세요.
|클래스가 지나치게 클 때(Large Class)
하나의 클래스가 지나치게 많은 인스턴스 변수를 갖는다면 중복된 코드가 존재할 확률이 높습니다. 이 중복된 코드는 리팩토링을 통해서 수정을 할 필요가 있겠죠?
|클래스가 충분히 역할을 하지 않을 때(Lazy Class)
클래스를 생성하면 그것을 유지하고 이해하는데 (개발자의 심적 물적) 비용이 계속 들어가게 됩니다. 만약 하나의 클래스가 그 비용을 투자할만큼의 일을 처리하고 있지 않다면 이 클래스는 정리대상이 됩니다.
|필요하지 않는 것을 처리하기 위해 작성된 코드가 있을 때(추측성 일반화: Speculative Generality)
필요하지 않는 기능을 위한 코드라면 남겨둘 필요가 없습니다.
|임시 필드가 있을 때(Temporary Field)
임시 필드란 이런 상황에서 볼 수 있습니다. 하나의 객체에서 instance variable이 특정 환경에서만 set 되는것과 같은 경우입니다.. 우리는 한 객체의 변수들이 모두 필요하다고 가정하고 보기 때문에 이 코드는 매우 이해하기 어려워집니다. 이런 코드는 리팩토링이 필요하겠죠.
|메시지 체인이 있을 때(Message Chains)
만약 클라이언트가 어떤 객체를 얻기 위해서 다른 객체에 물어보고 다른 객체는 또 다시 다른 객체에 물어보고... 이런 경우를 메시지 체인이라고 합니다. 이런 구조는 리팩토링을 해주어야하겠죠.
|미들 맨이 있을 때(Middle Man)
클래스의 인터페이스를 보았을 때 대부분 다른 클래스로 위임을 하고 있다면 이런 클래스를 미들맨이라고 부릅니다. 이 미들맨은 리팩토링이 필요한 신호이고 제거의 대상이 됩니다.
|클래스들이 지나치게 친밀할 때(부적절한 친밀 Inappropriate Intimacy)
클리스가 서로 지나치게 친밀해서 결합도가 높다면 서로의 사적인 부분을 파고 드느라 너무 많은 시간을 소모하게 됩니다. 이런 경우 리팩토링이 필요합니다.
|다른 인터페이스를 가진 대체 클래스가 있을 때(Alternative Classes with Different)
이런 경우는 개발자가 같은 기능을 가진 다른 클래스가 있다는 것을 몰랐을 때 발생하게 됩니다. 이 경우 리팩토링을 해서 중복을 제거할 필요가 있습니다.
|불완전한 라이브러리 클래스일 때(Incomplete Library Class)
라이브러리의 작성자가 우리가 필요한 기능을 가지고 있지 않거나 추가하는 것을 거부했을 경우입니다. 많은 라이브러리들이 일정 시간이 지나면 사용자들의 필요를 충족시키는 것을 멈추기 때문에 발생합니다. 이 경우 해결책은 라이브러리를 수정하는 것이지만 만약 라이브러리가 수정을 허용하지 않는다면 이 방식으로 해결할 수가 없게 됩니다. 그렇다면 리팩토링이 필요해 지겠죠.
|데이터 클래스가 있을 때(Data Class)
필드와 그 필드에 대한 get/set 메소드만 있는 클래스가 있다면 리팩토링이 필요합니다.
|클래스 상속 구조가 잘못 되었을 때(거부된 유산: Refused Bequest)
클래스의 상속구조가 잘못 되었다면 수정할 필요가 있겠죠?
|주석이 잔 뜩 붙어 있는 코드를 보았을 때
주석이 잔뜩 있다는 것은 무엇인가 개발자가 서투른 부분이 있다는 신호일 경우가 많습니다. 이 경우 자세히 보고 리팩토링이 필요한지 확인할 필요가 있습니다.
주석은 무엇을 해야 할 지 모를 때나 어떤 것을 왜 그렇게 했는지 표시할 때 사용하는 것이 좋습니다.
리펙토링에 익숙해지기
|리펙토링 목표를 잡는데 익숙해져라
코드에서 bad smell이 난다면 그 문제를 해결해야겠다고 결심을 하고 그 문제를 항해 전진해야 합니다.
|확실하지 않을 때는 멈추어라
리펙토링을 하다가 제대로 되고 있는지 모르겠다면 일단 멈출 필요가 있습니다. 만약 제대로 되지 않는 상태에서 멀리까지 가버린다면 더 멀리 돌아가게 됩니다. 우선 멈추고 확실해 질 때 다시 시작하세요.
|왔던 길로 되돌아가기(Backtrack)
제대로 실행되는 것 중 가장 최근의 것으로 되돌리세요. 그리고 변경을 하나씩 하고 변경할 때마다 테스트를 실행하세요.
|듀엣(Duets)
리펙토링을 혼자서 하기보다는 다른 사람과 함께 하는 것이 더 효율적인 경우가 많습니다.
좀 더 실질적인 리펙토링 기준
와 리펙토링 할 때 정말 고려해야 할 부분이 많네요. 이걸 다 기억하면 좋겠지만 쉽지는 않을 것 같습니다. 만약 아직 리팩토링에 익숙하지 않으시다면 우선적으로 아래의 세 가지를 염두에 두고 코딩을 하고 또 리팩토링을 할 것을 추천합니다.
|가독성이 높은 코드를 짜라
가독성이 높은 코드라면 대부분 하나의 모듈이 분명한 하나의 역활을 하고 있을 것입니다. 그래야 심플하고 명확한 코드가 되어 이해가 쉬워지니까요.
| Coupling(결합도)를 없애라
Coupling이라는 것은 간단히 이야기 하면 하나의 모듈의 어떤 부분을 바꿀 때 다른 모듈도 함께 바꾸어야 하는 상황을 의미합니다. 프로그래머들은 단순하고 명확한 모듈을 선호합니다. 하나의 모듈의 일은 가능한 하나의 모듈에서 끝날 수 있도록 코드를 짤 필요가 있습니다.
|하나의 함수/메소드는 하나의 역할만 하게 하라
함수가 너무 복잡하고 여러 개의 기능을 가지게 된다면 활용성이 떨어지고 이해하기 어려운 코드가 됩니다. 이 경우 함수를 여러 개로 쪼개고 하나의 함수는 하나의 인풋과 아웃풋을 가지도록 하고 그 함수들을 호출해서 조합하는 역할을 하는 함수로 다시 정의하는 것이 좋습니다.
이 세 가지만 잘 하더라도 충분히 좋은 코드가 될 수 있습니다.
아마도 리팩토링에 대해서 배워야 할 것들이 훨씬 많을 것 같네요. 오늘은 리팩토링의 대략적인 의미나 필요성을 파악하는 것으로 글을 마치겠습니다. :)
Reference
https://www.slideshare.net/ddayinhwang9/refactoring-60121460
임시필드: http://blog.ploeh.dk/2015/09/18/temporary-field-code-smell/
Alternative Classes with Different: https://sourcemaking.com/refactoring/smells/alternative-classes-with-different-interfaces
https://sourcemaking.com/refactoring/smells/incomplete-library-class
[출처] 리팩토링이란 무엇인가?|작성자 AIdev
'기타 정보 > 소프트웨어 공학' 카테고리의 다른 글
[자료구조] 배열 - 정리 및 연습문제 (0) | 2021.04.21 |
---|---|
[자료구조] 자료구조란 무엇인가? - 정리 및 연습문제 (0) | 2021.04.20 |
제어장치 - 제어장치의 구성과 명령어 수행 과정 (0) | 2021.04.20 |
처리장치 - 제어단어의 이해와 마이크로 연산의 제어단어 변환 (0) | 2021.04.20 |
프로그래밍 언어의 구문의 표현 - BNF, EBNF, 구분 도표 표현법 (0) | 2021.04.20 |
프로그래밍 언어의 요구사항과 설계 원칙 (0) | 2021.04.20 |
프로그래밍 언어의 평가 기준 (0) | 2021.04.20 |
1. 소프트웨어 공학이란? - 소프트웨어 공학의 정의와 개발과정 (0) | 2019.12.27 |