리팩터링: CHAPTER 02

정성준 (Seongjun Chung)·2023년 4월 12일
0

리팩터링2

목록 보기
2/3

리팩터링 원칙을 읽으며

리팩터링의 정의

특정한 방식에 따라 코드를 정리하는 것만이 리팩터링이다.
누군가 "리팩터링하다가 코드가 깨져서 며칠이나 고생했다"라고 한다면, 십중팔구 리팩터링한 것이 아니다.

이 부분은 좀 개인적으로 찔렸던 부분이다. 사실 생각해보면 리팩터링의 정의 자체를 단순히 성능개선이나 코드 품질개선등의 개념으로 생각하고 있었던 것 같다.

v2(리액트)프로젝트에서 로그인을 후 자신이 방문했던 스페이스 목록을 불러오는 페이지에 대한 구현을 한 적이 있는데, 해당 스페이스들은 2가지의 종류(방문했던 남의 스페이스, 내가 만든 스페이스)로 나뉘어지고 해당 종류별로 필터를 할 수 있는 기능이였다.

해당 부분을 리액트쿼리로 구현했었는데 다른 키값을 사용하지만 같은 같은 queryFn을 사용하다보니 해당 부분을 하나의 쿼리로 합치려는 작업을 리팩터링 명목으로 진행한 적이 있는데 생각보다 코드는 더 난잡해지고 복잡해지는 부분이 있었다.
쿼리결과가 분리돼있음에 따라 오히려 더 명확했던 부분이 하나의 쿼리로 합쳐져서 유저가 선택한 상태에 따라 다른 쿼리키를 참조해 데이터를 가져오다보니 생각보다 원하던대로 동작도 안하는 부분이 있어서 진짜 오랜 삽질 후에 다시 롤백하는 것이 더 올바르다고 판단했다.

어쩌면 여기서 얘기하는 리팩터링이라는 개념에 대해선 코드가 좀 길어지더라도 더 명확하고 수정하기 쉬운 코드는 내가 처음에 짰던 코드가 아닌가 싶기도 하다. 무작정 더 짧은 코드가 되어야한다고 생각하고 코드를 개선해보고자 했던 리팩터링이였는데 오히려 반대의 행위를 하고 있었던 것 같다는 생각이 들었다.

리팩터링의 목적은 코드를 이해하고 수정하기 쉽게 만드는 것이다. 프로그램 성능은 좋아질 수도, 나빠질 수도 있다.

리팩터링하는 이유

  • 리팩터링하면 소프트웨어 설계가 좋아진다.
  • 리팩터링하면 소프트웨어를 이해하기 쉬워진다.
  • 리팩터링하면 버그를 쉽게 찾을 수 있다.
  • 리팩터링하면 프로그래밍 속도를 높일 수 있다.

언제 리팩터링해야 할까?

거의 한 시간 간격으로 리팩터링한다.

3의 법칙
1. 처음에는 그냥 한다.
2. 비슷한 일을 두 번째로 하게되면, 일단 계속 한다.
3. 비슷한 일을 세 번째 하게 되면 리팩터링한다.
'스트라이크 세 번이면 리팩터링하라'로 기억하자

이 개념을 잘 참고해서 현재 진행중인 마이그레이션 프로젝트에 적용해봐야겠다.

그래서 나는 리팩터링 시간을 일정에 따로 잡아두지 않고, 대부분의 리팩터링을 다른 일을 하는 중에 처리한다.
보기 싫은 코드를 발견하면 리팩터링하자.

사실 이 부분을 읽고 의심이 좀 드는 부분이 있었다. 분명 나나 팀원들은 스프린트에 해당하는 기능별 브랜치를 따서 작업하고 있는데 해당 기능 브랜치에서 다른 코드의 리팩터링 코드를 적용하는 것이 올바른 행위일까에 대한 의구심이였다.
오히려 기능은 구현하고 또 다른 브랜치에 리팩터링을 진행하는 것이 더 명확하지 않을까라는 생각이였다.

리팩터링 커밋과 기능 추가 커밋을 분리해야한다는 조언에 동의하지 않는다.
리팩터링은 기능 추가와 밀접하게 엮인 경우가 너무나 많기 때문에 굳이 나누는 것은 시간 낭비일 수 있다.
리팩터링 커밋을 분리한다고 해서 무조건 좋은 것은 아님을 명심...!

아니나 다를까 내가 의심했던 부분에 대한 답이 나온 것이 좀 신기했다. 사실 많은 사람들이 나처럼 프로젝트를 진행하고 있었을텐데, 이 부분에선 오히려 좀 강하게 얘기하고 있는 것 같다. 그만큼 리팩터링이라는 행위 자체가 프로젝트에 도움이 되는 부분이 많다는 것을 반증하는 것이 아닐까라는 생각이 들었다.

사실 생각해보면 기능별 브랜치로 코드를 구현하다보면 생각보다 이슈간에 스위칭 비용이 생각보다 크다는 것은 느끼고 있었다. 그런 부분에 있어서 그런 스위칭 비용등의 리소스를 판단했을 때 기능 브랜치에도 리팩터링 코드를 포함시키는 것이 더 효율적인 부분이 되는 것을 뜻하는 것 같다.

프로 개발자의 역할은 효과적인 소프트웨어를 최대한 빨리 만드는 것이다.

리팩터링하지 말아야 할 때

  • 지저분한 코드를 발견해도 굳이 수정할 필요가 없다면 리팩터링하지 않는다.
  • 리팩터링하는 것보다 처음부터 새로 작성하는 게 쉬울 때도 리팩터링하지 않는다.

내가 볼 때 사람들이 빠지기 쉬운 가장 위험한 오류는 리팩터링을 '클린 코드'나 '바람직한 엔지니어링 습관' 처럼 도덕적인 이유로 정당화하는 것이다.
리팩터링의 본질은 코드베이스를 예쁘게 꾸미는 데 있지 않다.
오로지 경제적인 이유로 하는 것이다.(개발 시간 단축)

기능별 브랜치의 통합 주기를 2-3일 단위로 짧게 관리해야 한다고 주장하는 사람이 많다.
나와 같은 사람들은 더 짧아야 한다고 주장한다. 이 방식을 지속적 통합(CI)라고 한다.
CI에 따르면 모든 팀원이 하루에 최소 한 번은 마스터와 통합한다.

이 부분은 현재 일하는 방식에 바로 적용해볼 수 있는 부분이라고 생각했다. 기존 레거시 코드를 마이그레이션하면서 좀 더 나은 방향으로 CI/CD를 경험할 수 있는 기회라고 생각한다. (개발팀내 기존 레거시 프로젝트에서는 리베이스만을 사용하도록하는 원칙이 있었어서 CI와 브랜치 히스토리 관리가 너무 힘들었다. 충돌이라도 생기면 지옥이 따로 없...)

테스팅

리팩터링하기 위해서는 (대부분의 경우에) 자가 테스트코드를 마련해야 한다는 뜻...!

CI에 통합된 테스트는 XP(CI + 리팩터링)의 권장사항이자 지속적 배포(CD)의 핵심이기도 하다.

코딩 전에 아키텍처를 확정지으려 할 때의 대표적인 문제는 소프트웨어 요구사항을 사전에 모두 파악해야 한다는 것이다.

사실 현재 프론트엔드, 백엔드 개발자할 것 없이 모두가 TDD를 위한 테스트코드를 도입하고 싶어하지만(사실 반반으로 나뉘는 것 같기도 하다. 나는 해보고 싶다. 혼자 공부라도 좀 해볼 예정) 마이그레이션 기간 자체가 워낙 타이트하기도 하고 바쁘게 스프린트단위로 개발하다보니 도저히 도입할 시간이 나오질 않고 있는 것 같다.

책에서 얘기하기를 시간이 없다면 더욱더 테스트코드가 필요하다고 하는데 현업에선 생각보다 쉽지 않는 부분이 있는 것 같다... 내가 언젠간 도입해보리...(사실 이미 마이그레이션 프로젝트에는 jest를 사용 가능한 상황이기도 해서 혼자 함수 테스팅 용도로라도 사용해보기 시작해봐야겠다고 생각 중..)

성능의 대하여

성능에 대한 흥미로운 사실은, 대부분 프로그램은 전체 코드 중 극히 일부에서 대부분의 시간을 소비한다는 것이다. 그래서 코드 전체를 고르게 최적화한다면 그중 90%는 효과가 거의 없기 때문에 시간낭비인 셈이다.

사실 우리의 프로젝트 또한 대부분의 성능을 Play화면(Phaser엔진과 rtc 등)이 대부분의 비중을 차지하고 있다보니 해당 부분의 코드를 만질때도 특별히 조심하면서 좋은 코드를 짜려고 노력해야겠다는 생각을 많이 했다. 현재 마이그레이션의 코드를 설계하면서 기존 레거시에서 많은 부분을 개선한 팀원들의 노고가 대단하다고 생각한다.

profile
ZEP에서 프론트엔드 개발을 하고 있습니다.

0개의 댓글