본문 바로가기
핌팩토링

2. 리팩터링 원칙

by AsCE_hyunseung 2022. 2. 13.

2.1 리팩터링 정의

리팩터링

  • 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법
  • 특정한 방식에 따라 코드를 정리하는 것만이 리팩터링이다.
  • 동작을 보존하는 작은 단계들을 거쳐 코드를 수정하고, 이러한 단계들을 순차적으로 연결하여 큰 변화를 만들어내는 일이다.
  • 리팩터링하는 동안에는 코드가 항상 정상 작동하기 때문에 전체 작업이 끝나지 않았더라도 언제든 멈출 수 있다.

리팩터링과 성능 최적화의 공통점

  • 프로그램의 전반적인 기능은 그대로 유지한다

리팩터링과 성능 최적화의 차이점

  • 리팩터링은 코드를 이해하고 수정하기 쉽게 만드는것(성능 관점 X)
  • 성능 최적화는 오로지 속도 개선에만 신경 쓴다.

2.2 두 개의 모자

  • 목적이 기능 추가냐 리팩터링이냐를 명확히 구분해야한다.
  • 항상 내가 어떤 모자를 쓰고있는지와, 그에 따른 미묘한 작업 방식의 차이를 분명하게 인식해야한다.

2.3 리팩터링하는 이유

리팩터링하면 소프트웨어 설계가 좋아진다.

  • 아키텍처를 충분히 이해하지 못한 채 단기 목표만을 위해 코드를 수정하다 보면 기반 구조가 무너지기 쉽다.
  • 중복 코드를 제거하면 모든 코드가 언제나 고유한 일을 수행함을 보장할 수 있으며, 이는 바람직한 설계의 핵심이다.

리팩터링하면 소프트웨어를 이해하기 쉬워진다

  • 컴퓨터에게 시키려는 일과 이를 표현한 코드의 차이를 최대한 줄여야한다.
  • 프로그래밍은 결국 내가 원하는 바를 정확히 표현하는 일이다.
  • 기억할 필요가 있는 것들은 최대한 코드에 담자

리팩터링하면 버그를 쉽게 찾을 수 있다.

  • 리팩터링하면 코드가 하는 일을 깊이 파악하게 되면서, 새로 깨달은 것을 곧바로 코드에 반영하게 된다.

리팩터링하면 프로그래밍 속도를 높일 수 있다.

  • 리팩터링하는 데 시간이 드니 전체 개발 속도가 떨어질까봐 걱정할 수도 있다.
  • 그러나 내부 설계에 심혈을 기울이면 소프트웨어의 지구력이 높아져서 빠르게 개발할 수 있는 상태를 더 오래 지속할 수 있다.
  • 리팩터링하면 기존 코드의 설계를 얼마든지 개선할 수 있으므로, 설령 프로그램 요구사항이 바뀌더라도 설계를 지속해서 개선할 수 있다.

2.4 언제 리팩터링해야 할까?

  • 삼진 리팩터링을 따르자
  1. 처음에는 그냥 한다
  2. 두 번째에도 그냥 한다
  3. 비슷한 일을 세 번째 하게 되면 리팩터링한다.

준비를 위한 리팩터링: 기능을 쉽게 추가하게 만들기

  • 리팩터링하기 가장 좋은 시점은 코드베이스에 기능을 새로 추가하기 직전이다.
  • 이 시점에 현재 코드를 살펴보면서, 구조를 살짝 바꾸면 다른 작업을 하기가 훨씬 쉬워질 만한 부분을 찾는다.
  • 복사 붙여넣기 방식으로 처리하면 나중에 새 기능을 약간 변형한 버전을 만들어야 할 대 번거로울 수 있다.
  • 버그를 잡을때도 오류를 일으키는 코드를 한 곳으로 합치는 편이 훨씬 편하다.

이해를 위한 리팩터링: 코드를 이해하기 쉽게 만들기

  • 코드를 수정하려면 먼저 그 코드가 하는 일을 파악해야한다.
  • 리팩터링하면 머리로 이해한 것을 코드에 옮겨 담을 수 있다.
  • 그 다음 테스트를 해보면 내 생각이 맞았는지 확인 할 수 있다.
  • 코드를 분석할 때 리팩터링을 해보면, 리팩터링을 하지 않았을때의 이해 수준 보다 더 깊은 수준까지 이해하게 된다.

쓰레기 줍기 리팩터링

  • 간단히 수정할 수 있는 것은 즉시 고치고, 시간이 좀 걸리는 일은 짧은 메모만 남긴 다음, 하던 일을 끝내고 나서 처리한다.

계획된 리팩터링과 수시로 하는 리팩터링

  • 리팩터링은 프로그래밍과 구분되는 별개의 활동이 아니다.
  • 뛰어난 개발자는 새 기능을 추가하기 쉽도록 코드를 수정하는 것이 그 기능을 가장 빠르게 추가하는 길일 수 있음을 안다.
  • 버전 관리 시스템에서 리팩터링 커밋과 기능 추가 커밋을 분리?

오래 걸리는 리팩터링

  • 주어진 문제를 몇 주에 걸쳐 조금씩 해결해가는 편이 효과적일 때가 많다.

코드 리뷰에 리팩터링 활용하기

  • 코드 리뷰는 개발팀 전체에 지식을 전파하는 데 좋다.
  • 코드 리뷰를 하면 다른 사람의 아이디어를 얻을 수 있다는 장점도 있다.
  • 리팩터링은 다른 이의 코드를 리뷰하는 데도 도움된다.
  • 리팩터링은 코드 리뷰의 결과를 더 구체적으로 도출하는 데에도 도움된다.

관리자에게는?

  • 프로 개발자의 임무는 효과적인 소프트웨어를 최대한 빨리 만드는 것이다.
  • 그 임무를 빠르게 달성하는 방법은 리팩터링이다.

리팩터링하지 말아야 할 때

  • 내부 동작을 이해해야 할 시점에 리팩터링해야 효과를 제대로 볼 수 있다.
  • 리팩터링할지 새로 작성할지를 잘 결정하려면 뛰어난 판단력과 경험이 뒷받침돼야 한다.

2.5 리팩터링 시 고려할 문제

  • 특정한 기술, 도구, 아키텍쳐를 언제 어디에 적용할지 판단하려면 손익을 제대로 이해해야한다.

새 기능 개발 속도 저하

  • 리팩터링의 궁극적인 목적은 개발 속도를 높여서 더 적은 노력으로 더 많은 가치를 창출하는 것이다.
  • 코드베이스가 건강하면 기존 코드를 새로운 방식으로 조합하기 쉬워서 복잡한 새 기능을 더 빨리 추가할 수 있다.
  • 리팩터링의 본질은 오로지 경제적인 이유로 하는 것이다.

코드 소유권

  • 코드 소유권이 나뉘어 있으면 리팩터링에 방해가 된다.
  • 코드의 소유권을 팀에 두는 것이다. 팀원이라면 누구나 팀이 소유한 코드를 수정할 수 있게 한다.
  • 코드 소유권을 느슨하게

브랜치

  • 마스터에서 기능별로 브랜치를 따서 작업하는 방식은 독립 브랜치로 작업하는 기간이 길어질수록 작업 결과를 마스터로 통합하기가 어려워진다는 단점이 있다.
  • 마스터를 개인 브랜치로 리베이스하거나 머지한다. (메이비 컨플릭?)
  • 여러 기능 브랜치에서 동시에 개발이 진행될 때는 이런 식으로 해결할 수 없다.

머지

  • 단 방향성이다, 브랜치만 바뀌고 마스터는 그대로다

통합

  • 양방향 처리다, 마스터를 개인 브랜치로 가져와서 작업한 결과를 다시 마스터에 올린다.

지속적 통합(CI)

  • 기능별 브랜치의 통합 주기를 짧게 관리해야한다.
  • 모든 팀원이 하루에 최소 한 번은 마스터와 통합한다.
  • 머지의 복잡도를 낮춘다.
  • 지속적 통합을 위해서는 마스터를 건강하게 유지하고, 거대한 기능을 잘게 쪼개는 법을 배우고, 각 기능을 끌 수 있는 기능 토글을 적용하여 완료되지 않은 기능이 시스템 전체를 망치지 않도록 해야한다.

테스팅

  • 리팩터링의 두드러진 특성은 프로그램의 겉보기 동작은 똑같이 유지된다는 것이다.
  • 실수를 저지르더라도 재빨리 해결하면 문제가 되지 않는다.
  • 핵심은 오류를 재빨리 잡는 데 있다.
  • 실제로 이렇게 하려면 코드의 다양한 측면을 검사하는 test suite가 필요하다.
  • 자가 테스트 코드는 리팩터링을 할 수 있게 해줄 뿐만 아니라, 새 기능 추가도 훨씬 안전하게 진행할 수 있도록 도와준다

레거시

  • 레거시 시스템을 파악할 때 리팩터링이 굉장히 도움된다.
  • 대규모 레거시 시스템을 테스트 코드 없이 명료하게 리팩터링하기는 힘들다.
  • 테스트 보강으로 해결할 수 있다.
  • 테스트를 갖추고 있더라도 복잡하게 얽힌 레거시 코드를 아름다운 코드로 단번에 리팩터링하는 데는 낙관적이지 않다.
  • 이럴때는 서로 관련된 부분끼리 나눠서 하나씩 공략하는 것이 효과적일 수 있다.

데이터베이스

  • 이 기법도 전체 변경 과정을 작고 독립된 단계들로 쪼개는 것이 핵심이다.
  • 데이터베이스 리팩터링은 프로덕션 환경에 여러 단계로 나눠서 릴리스하는 것이 대체로 좋다는 점에서 다른 리팩터링과 다르다.

2.6 리팩터링, 아키텍쳐, 애그니

  • 리팩터링이 아키텍처에 미치는 실질적인 효과는 요구사항 변화에 자연스럽게 대응하도록 코드 베이스를 잘 설계 해준다.
  • 코딩 전에 아키텍처를 확정지으려 할 떄의 대표적인 문제는 소프트웨어 요구사항을 사전에 모두 파악해야한다는 것이다.
  • 이것을 해결하는 방법은 향후 변경에 유연하게 대처할 수 있는 유연성 메커니즘을 소프트웨어에 심어두는 것이다.

유연성 메커니즘

  • ex. 함수 정의할때 범용적으로 쓰려고 매개변수 추가할때, 여기서의 매개변수가 유연성 메커니즘이다.
  • 리팩터링을 활용하면 다르게 접근할 수 있다. 현재까지 파악한 요구사항만을 해결하는 소프트웨어를 구축한다.
  • 소프트웨어의 복잡도에 지장을 주지 않는 메커니즘은 마음껏 추가하지만, 복잡도를 높일 수 있는 유연성 메커니즘은 반드시 검증을 거친 후에 추가한다.
  • 리팩터링을 미루면 훨씬 힘들어진다는 확신이 들 때만 유연한 메커니즘을 추가한다.
  • 이런 식으로 설계하는 방식을 애그니라고 부른다.

애그니

  • you aren’t going to need it (필요 없을거다)

2.7 리팩터링과 소프트웨어 개발 프로세스

  • 리팩터링의 첫 번째 토대는 자가 테스트 코드다.
  • 팀으로 개발하면서 리팩터링을 하려면 각 팀원이 다른 사람의 작업을 방해하지 않으면서 언제든지 리팩터링할 수 있어야한다.
  • 자가 테스트 코드, 지속적 통합, 리팩터링이라는 세 기법은 서로 강력한 상승효과를 발휘한다.
  • 애그니로 인해 리팩터링을 더욱 쉽게 할 수 있다. 추측에 근거한 수많은 유연성 메커니즘을 갖춘 시스템보다는 단순한 시스템이 변경하기가 훨씬 쉽기 때문이다.

2.8 리팩터링과 성능

  • 리팩터링하면 소프트웨어가 느려질 수도 있는 건 사실이다. 하지만 그와 동시에 성능을 튜닝하기는 더 쉬워진다.

빠른 소프트웨어를 작성하는 방법

  1. 시간 예산 분배 방식 설계를 여러 컴포넌트로 나눠서 컴포넌트마다 자원 예산을 할당한다.
  2. 관심을 기울이는 것
  3. 의도적으로 성능 최적화에 돌입하기 전까지는 성능에 신경 쓰지 않고 코드를 다루기 쉽게 만드는데 집중한다.

프로그램을 잘 리팩터링해두면 이런 식의 최적화에 두 가지 면에서 도움이 된다.

  1. 성능 튜닝에 투입할 시간을 벌 수 있다.
  2. 프로그램의 성능을 더 세밀하게 분석할 수 있다.

결론

  • 단기적으로 보면 리팩터링 단계에서는 성능이 느려질 수 있지만, 최적화 단계에서 코드를 튜닝하기 훨씬 쉬워지기 때문에 결국 더 빠른 소프트웨어를 얻게 된다.

2.9 리팩터링의 유래

  • 리팩터링은 소프트웨어 개발 프로세스 전반의 핵심 요소다.

2.10 리팩터링 자동화

  • 리팩터링을 자동화하는 가장 어설픈 방법은 소스 코드의 텍스트를 직접 조작하는 것이다.
  • 정적 타입 언어라면 훨씬 안전하게 구현할 수 잇는 리팩터링 수가 늘어난다.
  • 대부분의 리팩터링이 믿을만하더라도 중간에 꼬인 부분이 없는지 테스트로 확인하는 것이 바람직하다.

'핌팩토링' 카테고리의 다른 글

7. 캡슐화  (0) 2022.04.27
6. 기본적인 리팩터링  (0) 2022.03.26
4. 테스트 구축하기  (0) 2022.03.12
3. 코드에서 나는 악취  (0) 2022.02.28
1. 리팩터링 첫번째 예시  (0) 2022.02.02