리팩터링은 차갑다

오늘, 리팩터링에 실패했다. 아니 어쩌면 어제, 모르겠다.

Jinhwan Kim
7 min readMar 16, 2024

약 3일 정도, 진행중인 개발 프로젝트에 정리 겸 고도화를 목적으로 하는 리팩토링을 진행했고, 안타깝지만 실패라는 결론을 내렸다.

뜬금 없이 왠 리팩터링

리팩터링에 대해 짧게 설명하면 현재 프로그램의 전체 실행 로직이 바뀌지 않는 선에서 프로그램의 코드 구성과 어쩌면 성능을 개선하기 위한 목적의 코드 수정. 이라고 거창하게 표현할 수 있다.

완벽하진 않겠지만 큰 문제 없이 기능 추가를 비롯해 잘 돌아가고 있던 개발 프로젝트에 왜 리팩터링을 하게 되었는가.

이는 당연하게도 애옹킴의 허접한 코딩 실력에 기인한다.

우선 프로젝트에는 코드와 파일들이 너무 많았다. 그런데 여기에는 실제로 프로그램에 쓰이는 코드들과 쓰이지 않는 “스니펫", “디버그", ”백업용" 코드들이 섞여 있었고, 이 맥락들을 지속적이지 않고 뜨문 뜨문 진행되는 프로젝트에서 개발하는 사람 혼자서 기억하는 것에는 문제가 있다고 판단했다.

이 수백 ~ 수천줄의 코드들은 각각 다른 시간에 다른 목적을 위해 만들고 추가 되었고, 그러다보니 동일한 문제를 푸는 과정에서도 로직이 조금씩 달랐다. 예를 들면 데이터 조작을 위해 한 코드에서는 data.table을 사용했고, 다른 코드에서는 dplyr를 사용했다. 일단 돌아만 가면 OK라는 기조가 문제를 점점 크게 만들었다고 볼 수 있다.

각 코드 안에서도 “원하는 결과를 만들어낸다” 를 제외하면, 함수나 변수의 명명법을 비롯하여 입력과 출력, 연산 과정 등이 전부 따로따로 였다. 전부 한 사람이 개발한 코드임에도 불구하고 다 다른 이유는 코드를 만드는 시점과 배경 상황이 달랐기 때문 !

그래서 R Shiny 리팩터링

은 이렇게 했다. 라는 제목으로 글을 작성했다면 더 좋을 뻔 했지만.

아무튼 리팩터링의 방향은 거창했다.

Shiny의 module을 사용해 혼재되어 있는 코드를 정리하고, 운이 좋으면 그 과정에서 성능의 향상도 기대한다.

혼재되어 있는 코드를 정리하기가 우선이었던 이유는 프로젝트의 “지속가능성” 이다. 여기서 지속가능성은 이 프로젝트를 천년만년 끌고가자 라기보단, 이후에 내가 아닌 다른 사람이 개발에 참여해서도 이어질 수 있게 하는 것. 즉 프로젝트에 대체 가능성을 만들기 위함이다.

그런데, 지금 프로젝트는 다른 사람은 커녕 몇달 후의 내가 개발에 참여해도 맥락 유지를 위해 어느 정도의 시간이 필요했다.

두번째로는 새로운 개발 과정 중 이전에 돌아가던 기능에서 오류가 발생하는 경우가 있어, (물론 이를 방지하기 위해 처음부터 잘 짜는 것이 최선이지만 여러가지 핑계로 인해 어려운 상황) 이를 조금 더 나은 기술인 “테스트 기법”을 활용해 검증하고 싶었는데 이를 위해서는 프로그램을 “구조에 맞게" 변경하는 것이 필요했다.

즉, 코드의 내용 뿐 아니라 파일의 연동과 배치를 고려한 “아키텍쳐"를 갈아 엎는 것도 리팩터링의 방향에 포함되었다.

리팩터링은 차갑다.

글을 작성하면서 온라인을 좀 돌아다녀보니 리팩터링을 통해 프로그램의 “더 나은 아름다움”을 기대하는 사람도 있었다. 또한 어떤 사람들은 (멋진 사람들) 프로그램의 “성능 향상”도 기대한다. 그렇지만 이렇게 우아한 목적과 다르게 나는 “지속가능성”“테스트” 이 2가지를 주요 목적으로 정한 이유는 “비용”이다.

요즘 뜨는 devin을 포함해 내가 참여하지 않고도 프로젝트가 진행되거나, 프로젝트 진행에 상대적으로 불필요한 과정인 잘못된 오류를 수정하는 것 (테스트를 통해 오류를 자동으로 검증할 수 있다면)을 스킵할 수 있다면 아껴진 시간을 사용해 더 많은 결과물을 만들어 낼 수 있다.

그래서 열심히 리팩터링을 진행했고 결론은 조졌다 !!

왜 실패했을까

코딩을 못하니까 !!! 로 한줄로 표현할 수 있지만, 이는 일종의 “실버 불렛”이지만, ‘코딩을 잘하는 사람이 아니면 리팩터링 하지마라’로 해석될 수도 있는 만큼 건강한 회고와는 조금 거리가 있다.

실패를 만드는 것은 쉽고, 실패를 인정하는 것은 어렵지만. 이를 돌아보는 이유는 이후에 실패의 가능성을 줄이기 위함인 만큼 어떤 문제가 실패를 하게 했고, 이를 방지하기 위해 어떤 것을 해볼 수 있는지 생각해보겠다.

그림에서 모듈은 다른 프로그래밍 언어로 치면 OOP의 개념과 조금 유사하다 (R은 기본적으로 OOP가 아닌 pipe와 tidy를 위히산 FP(함수형 프로그래밍)의 개념을 더 많이 차용한다)

실패라고 판단 내린 과정을 그림으로 표현해보면 다음과 같다.

프로젝트의 코드 배치를 바꾸는 것이나 모듈 형태로 코드를 변경하는 것에는 기술적인 문제가 없었다. 분명 어느 정도까지는 리팩토링이 잘 진행되었다. 정말로

그런데 어느 순간 눈 떠보니 분리된 코드들은 작동하지 않았거나, 정상 작동을 위해 여러가지 수정이 또 다시 필요했고, 분리되지 않은 (기존에 작동 중인) 코드 또한 문제를 일으키고 있었다.

설상가상으로 추가 해야할 신규 기능들도 대기하고 있었다.(이게 의사 결정에 가장 큰 이유)

이후 점점 맥락과 기억들이 overflow되는 것과 마감기한이 다가오는 것을 보다 못한 서렌..

실패한 이유를 돌아보면 “너무 많은 것을 한꺼번에 하려고 했다”라는 하나의 문장으로 귀결된다.

바론 먹고, 용도 먹고, 미드 타워도 밀고, 사이드 주도권도 얻으려다가 와장창 되버리는 그 녀석들처럼(…) 코드 컨벤션도 바꾸면서 배치도 바꾸고 성능도 올리고 아키텍쳐도 바꾸려고 하다보니 결과적으로는 이도 저도 안된 것 아닐까

만약 신규 기능이 없었다면, 시간이 널널했다면, 코드의 전체 덩어리가 작았다면 결과가 달랐을까? 라는 생각을 해봤지만 이런 상황에서도 잘 해내야 진정한 R의 고수라고 할 수 있지 않을까.

앞으로의 리팩터링

제일 좋은 것은 리팩터링을 해야하는 상황을 만들지 않게 처음 부터 잘 만드는 것이지만 이론상으로나 가능 하고.

다행히 이는 바꿔 말하면 하나에만 우선 집중한다면 하나를 완성해낼 수 있다고도 볼 수 있겠다. 역시 나는 멀티태스킹이 어려운 사람이다.

컨벤션만 맞추거나, 구조만 바꾸거나, 성능만 올리거나… 일단 하나를 마치고 난 이후에 다른 나머지를 이어 진행하면 충분히 가능하다.

또 하나 배운 것은 개발과 문서화는 동시에 진행되어야겠다는 것이다.

여기서 문서화는 이 프로그램이 어떤 기능을 가지고 있고, 이를 어떻게 작동시키며 어떤 결과가 나와야 한다는 일종의 “사용자" 관점에서의 문서를 의미한다.

이 “매뉴얼"을 만드는 것은 상당히 번거로운 작업이지만 사용자 경험에 크리티컬한 역할을 하기 때문에 필요하다는 것에는 동의하고, 나는 보통 프로젝트가 완료된 이후에 만들곤 한다.

그런데 프로젝트에 참여하는 개발자를 위한 매뉴얼이 있으면 좋겠다는 생각이 들었던 것은 코드를 작성하면서 어떤 맥락으로 이 코드를 작성했는지 헷갈림이 꽤 자주 나타났기 때문이다. 물론 이를 주석으로도 표기할 수 있겠지만 보통 주석은 전반적인 프로젝트를 설명하기 보단 특정 부분에 한정하여 설명하는 것이 더 효과적인 방법이라고 생각하기 때문.

개발자를 위한 문서화는 이 프로그램이 어떤 아키텍쳐를 가지고 있는 지, 어떤 코드들로 구성되어 있고, 함수의 명명법이나 컨벤션 등은 어떤 것을 사용하는 지를 README처럼 정리해두는 것으로 보면 이해가 쉬울 것 같다.

추가로, 왜 이것들을 알지 못했는가 에 대해서 변명을 하자면 R과 Shiny가 가지고 있는 언어의 태생적인 한계라고나 할까, 아무튼 얘네들은 짧고 빠른 결과물을 내는 프로젝트가 주 목적이고 이처럼 장기적으로 크게 크게 하는 프로젝트는 많이 사용되지 않는 “Prototype”형 언어에 가깝기 때문.

아무튼 이번에는 실패했지만, 조만간 성공했다는 썰을 풀 수 있길 기대한다. ㄲㅂ

--

--