최근 한 페이지를 리팩토링하면서 예상하지 못한 어려움을 겪었다. 새로운 기능을 추가하는 작업이 아니라 기존 구조를 정리하는 작업이었는데, 가장 어려웠던 건 새로운 코드를 작성하는 것이 아니라 기존 코드를 삭제하는 일이었다.
처음에는 사용하지 않는 코드들을 정리하면 끝날 줄 알았다. 하지만 막상 삭제를 시작하니 생각보다 쉽지 않았다.
- 다른 페이지에서도 import하고 있었고
- 특정 도메인 안에서 만든 전용 훅이 다른 도메인의 기능에서 함께 사용되고 있었고
- util과 컴포넌트의 의존성이 여기저기 퍼져 있었고
- import cycle도 여러 곳에서 발견됐다.
코드를 삭제하려고 할 때마다 이거 정말 지워도 괜찮은 걸까? 라는 생각이 계속 들었다.
예를 들면 이런 코드였다.
import type { QueryGridRow } from "@/pages/channels/settings/Query/components/Grid/hooks/useQueryColumns"
const DescriptionCell = ({ data }: { data: QueryGridRow }) => {
return <span className="block truncate">{data.description}</span>
}위는 전역에서 사용하는 공통 컴포넌트였는데, 왜인지 특정 도메인의 타입을 import 하고 있었다. Query라는 도메인의 기능을 걷어내면서 공통 컴포넌트 레이어까지 건드려야 했던 것이다.
그 경험을 하면서 문득 이런 생각이 들었다.
좋은 코드라는 건, 어쩌면 삭제하기 쉬운 코드도 포함하는 것이 아닐까?
삭제가 어렵다는 건 무엇 때문이었을까?
보통 좋은 구조를 이야기할 때는 확장하기 쉽다, 재사용하기 쉽다, 유지보수하기 쉽다 같은 이야기를 많이 하고, 나 또한 그렇게 생각해왔다.
그런데 이번 리팩토링에서는 오히려 반대 상황을 더 많이 마주했다. 기능을 추가하는 것보다 기능을 제거하거나 구조를 단순하게 만드는 일이 훨씬 어려웠다.
곰곰이 생각해 보니 삭제가 어려웠던 이유는 코드의 양 때문이라기보다, 어디까지 영향을 주는지 확신할 수 없었기 때문이었던 것 같다.
import cycle을 보며 든 생각
리팩토링을 하면서 import cycle도 여러 번 발견했다. 예전에는 순환 참조니까 고쳐야 한다 정도로만 생각했던 것 같다.
그런데 이번에는 조금 다르게 보였다. 순환 참조가 생겼다는 건 두 모듈이 서로를 알고 있다는 뜻이고, 한쪽을 수정하거나 삭제하려고 하면 다른 쪽도 함께 신경 써야 하는 경우가 많았다.
물론 모든 import cycle이 나쁜 구조라고 말할 수는 없겠지만, 적어도 내가 경험한 코드에서는 삭제하기 어려운 코드와 함께 나타나는 경우가 많았다. 그래서 import cycle 자체보다, 의존성이 지나치게 얽혀 있다는 신호가 아니었을까 하는 생각이 들었다.
삭제가 쉬웠던 코드도 있었다
반대로 비교적 쉽게 정리할 수 있었던 코드들도 있었다. 특정 Feature를 제거하면서 관련 폴더만 삭제해도 다른 곳에 거의 영향을 주지 않는 경우였다.
그런 코드들은 삭제 범위를 비교적 쉽게 예상할 수 있었고, 혹시 문제가 생기더라도 어디를 확인해야 하는지 감이 왔다.
돌이켜보면 코드가 적어서가 아니라 경계가 비교적 명확했기 때문이었던 것 같다.
앞으로는 이런 질문을 하게 될 것 같다
예전에는 새로운 기능을 만들면서 확장하기 쉬운 구조인가?, 코드가 위에서 아래로 잘 읽히나? 를 먼저 생각했다. 그런데 이번 리팩토링 이후에는 질문이 하나 더 생겼다.
6개월 뒤 이 기능을 삭제해야 한다면 어떨까?
이 질문을 떠올리면 Feature의 경계를 어디까지 둘지, Shared에 넣어도 되는 코드인지, 다른 모듈과 너무 강하게 연결되는 건 아닌지 한 번쯤 더 고민하게 될 것 같다.
아직 이것이 항상 맞는 기준이라고는 생각하지 않는다. 다만 적어도 내가 리팩토링하면서 느낀 어려움은 이 질문 하나로 조금은 줄어들 수 있지 않을까 하는 생각이 들었다.
마치며
예전에는 좋은 코드를 떠올리면 추가하기 쉽고, 확장하기 쉽고, 재사용하기 쉬운 코드를 먼저 생각했다. 그런데 이번 리팩토링을 하면서 삭제하기 쉬운 코드도 좋은 코드의 중요한 조건 중 하나가 아닐까 하는 생각이 들었다.
언젠가는 대부분의 기능이 변경되거나 사라질 수도 있다. 그때 불안하지 않게 코드를 지울 수 있다면, 그 구조는 꽤 잘 만들어진 구조라고 말할 수 있지 않을까.
아직 하나의 경험만으로 결론을 내릴 수는 없지만, 앞으로는 기능을 추가하는 것만큼 삭제하기 쉬운 구조인지도 함께 고민하는 개발자가 되려고 한다.