레거시에서 느낀 모던한 프론트엔드 개발 방식의 소중함
꽃이 진 뒤에야 비로소 봄이었음을 알았습니다

10년이 훌쩍 넘은 레거시 코드를 다뤄볼 기회가 생겼다. PHP와 jquery 기반의 서비스였는데 최근 변경 사항이 무려 13년 전, 14년 전으로 확인되는 선사시대의 레거시 였다. 13~14년 전이면 ES2015가 나오기도 전이고 현대적인 프론트엔드 개발의 대표주자들인 뷰와 앵귤러(*앵귤러JS 말고) 그리고 현재 가장 높은 점유율을 보이고 있는 리액트가 나오기 이전 시기였다. 이런 시대적 요건은 DB의 구조와 그를 다루는 프론트엔드 설계가 요즘의 상태 기반 개발 방식과는 전혀 다를 수 밖에 없음을 의미했다.

요구사항
요구사항은 기존의 D&D를 지원하지 않고 메뉴와 2단계 깊이의 서브메뉴 설정만 가능한 메뉴 설정에서 D&D를 지원하며 메뉴와 5단계 깊이의 서브메뉴 설정이 가능한 메뉴 설정으로 변경해달라는 것이었다. 요구사항만 보았을 때에는 코드와 데이터 구조를 확인하지 못한 상태였기에 낯선 php와 jquery에 대한 걱정만 한 채 그런가보다 하고 있었으나 실제로 코드와 데이터를 접하고 나서는 설계 사항부터 점검해야하는 대공사가 되겠구나 직감했다.
데이터구조가 D&D와 다계층 구조에 친화적이지 못하다는 점과 이 데이터 구조를 수정할 수 없다는 제약사항이 있었는데 이런 전제 조건을 바탕으로 클라이언트에서 위 요구사항을 충족시키기 위해서는 온 몸을 비틀어야한다(새로운 접근이 필요하다)는 결론이 도출되었다.
방치 되어있던 레거시
기존에는 php를 통한 서버 사이드 렌더링과 jquery를 활용한 직접 DOM 조작으로 개발되어 있었다. php를 통한 서버 사이드 렌더링의 특징 탓인지 프론트엔드는 서버에 강하게 의존성을 두고 있었는데 이 때문에 새로운 기능에 대한 확장성이 거의 전무한 상태였다.
특히나 문제였던 건 데이터 구조였는데 D&D 또는 다계층 구조가 전혀 고려되지 않은 채 플랫 구조로 구성되어 있었고 메뉴의 순서와 계층의 깊이 그리고 특정 계층에 속해있음을 결정하는 플래그까지 오직 한 데이터 값에 의해 결정이 되어있었다. 이는 기존 2단계 메뉴 구조에서는 크게 문제될 것 없지만 기능을 확장하기에는 유연하지 못했고 요구사항의 기능들을 수행하기에는 너무 경직된 구조였다.

D&D가 고려되지 않았다
기존에는 D&D 기능과 순서변경 기능이 존재하지 않는 메뉴설정이었다. 추가 버튼을 누르면 새로운 자식창을 통해 정보를 입력받아 부모의 code를 토대로 새로운 메뉴를 만들고 이를 정해진 규칙에 따라 렌더링하는 것이 전부였다. 그렇게 만들어진 새로운 메뉴 데이터들을 DB에 존재하는 기존 메뉴 데이터를 제거하고 통째로 교체하는 것이 메뉴 설정 로직이었다.
이런 구조의 가장 큰 문제라고 하면 사용자의 인터렉션에 따라 동적으로 계속해서 변경되는 D&D와는 전혀 맞지 않다는 점이었다. 제약 사항 중에는 꼭 저장 버튼을 눌러 DB에 저장해야한다라는 사항이 있었는데 이는 드롭된 후 저장로직을 실행할 수 없다는 말이었으며 그렇다면 변경된 데이터를 임시로 저장해둘 수 있는 상태가 필요하다는 의미이기도 했다.
…물론 드롭될 때마다 저장로직을 실행한다면 서버 자원을 계속해서 크게 사용해야한다는 점과 사용자 경험 측면에서도 좋지 못하다는 단점이 동반될테니 애초에 제약 사항이 없었더라도 저장 타이밍을 변경할 수는 없었다.
MVVM을 떠올렸다
머릿 속에 떠오르는 해법은 상태를 활용하는 방법이었다. 정확히는 MVVM을 따라가보자는 생각이었는데, View Model(클라이언트 상태)를 기준으로 View(UI)와 Model(데이터, 비즈니스 로직)를 업데이트할 수 있도록 해보자 생각했다.
View Model을 업데이트하면 View가 업데이트되고 (이 과정에서 CSR이 수행된다.)
Model(서버와의 통신, 비즈니스 로직 실행 등)을 통해 DB에 업데이트
위 방식은 제법 모던한 프론트엔드의 개발 방식과 닮아있었다.
기존에는 서버 사이드에서 모든 것을 처리하다보니 클라이언트 측의 사용자 인터렉션에 따른 변경을 핸들링하는 것에 제약이 심한 편이었는데 백과 프론트의 책임과 역할을 명확하게 나누니 더 다양한 사용자 경험을 고려할 수 있게 되었다.
클라이언트 사이드 렌더링으로 전환
CSR이 항상 완벽하지 않다는 건 인지하고 있지만 현재 프로젝트의 상황을 생각했을 때 서버 사이드 렌더링을 고수할 필요가 전혀없었다.
첫째로 어드민 페이지이기 때문에 SEO 성능과는 무관하다는 점이었고
둘째로는 사용자 인터렉션으로 인한 변경사항이 수시로 적용 되어야 한다는 점이었으며
셋째로는 요구사항을 위해서는 DB의 데이터 구조를 가지고서 개발할 수 없다는 점이었다.
그렇기에 아래와 같이 전체적인 구조를 변경하기로 하였다.
기존의 데이터 구조를 클라이언트에 받아온 후 D&D에 적합하도록 계층 구조화 하여 상태로 저장한다.
PHP는 메뉴리스트의 렌더링에 관여하지 않도록 하며 JS를 통해 상태를 기반으로 렌더링하도록 한다.
모든 변경사항이 반영되고 난 후 저장 로직을 실행하게 되면 계층 구조의 상태를 DB 내 데이터구조에 호환되도록 플랫구조로 변경한 후 저장한다.
이런 설계로 구조를 변경하게 되면 더 깊은 계층을 확장해야할 경우에도, 더 얕은 계층으로 제한해야할 경우에도 손쉽게 적용가능할 터였다. 결과는 성공적이었다. 레거시의 데이터 구조와도 호환되었으며 요구사항 또한 충족했다.
| 구분 | 기존 방식 | 수정할 방식 |
| 데이터 구조 | 플랫 구조 | 계층 구조 |
| 데이터 처리 | PHP를 통해 DB에 직접 접근해 데이터를 직접 HTML로 렌더링 | PHP를 통해 DB에 직접 접근해 데이터를 가져온 후 클라이언트에서 계층 구조로 변경 후 상태로 관리하며 상태를 통해 렌더링 |
| 업데이트 방식 | DOM에서 변경된 데이터를 기반으로 submit된 값을 DB에 업데이트 | 이벤트를 통해 변경된 상태를 DB의 데이터 구조에 맞게 다시 플랫 구조로 변환하여 DB에 업데이트 |

과거를 경험해야 비로소 알 수 있는 것들
나처럼 리액트를 통해 프론트엔드 개발을 접하고 공부해왔던 이들이라면 PHP나 jquery에 대해 막연한 공포감이 있을 것으로 안다. 사실 나도 그런 공포감을 가지고 있었고 여전히 가지고 있다.
하지만 이 공포감은 어쩌면 아래에 대한 두려움 아닐까?
- 절차적 프로그래밍
- 프론트엔드/백엔드가 명료하게 구분되어 있지 않음
- 경직되어있는 구조를 수정/개선해야 한다는 부담감
사실 그럼에도 배포되지 못했다
요구사항을 충족했다. 기능도 동작했다. UI/UX에 대한 이슈가 조금 남긴 했지만 충분히 개선가능하다고 생각했다.
하지만 결과적으로 배포되지 못했다. 이유는 오픈소스 프로젝트의 특성 상 기존 사용자들의 혼란이 불가피한 건 피하고 싶다는 게 주요 골자였다. 아이러니하게도 레거시만 다루는 입장에서도, 이런 레거시를 처음 접하는 입장에서도 쌍방향으로 서로에게 공포감을 가지고 있었던 셈이다.
내가 얻은 것들
배포되지는 못했지만, 이 경험을 통해 얻은 건 많았다.
첫째, 프레임워크 이전에 개발 패러다임을 이해하게 되었다.
MVVM이나 상태 관리는 그저 관성적으로 사용하는 것이었다. 하지만 레거시를 다룸으로써 왜 이런 방식들이 필요했는 지 조금은 더 이해하게 되었다. 역시 이런 건 말이나 글보단 경험이 이해를 돕는다.
둘째, 제약 속에서의 설계 능력이 향상되었다.
DB 구조를 바꿀 수 없다는 제약과 낯선 기술이 오히려 생각이라는 걸 하게 만들었다. 문제를 해결하기 위해 기존에 사용하던 도구의 특징을 떠올려보고 그에 맞게 설계하고 개선하는 경험을 맛보게 해주었다.
마치며…
낯선 개발 방식을 접하면 누구나 막막하고 머릿 속은 하얗게 변해버린다. (안 그러면 부럽다.)
하지만 이걸 개선시켜나가는 경험들은 내가 가진 지식을 한층 더 끌어올려주는 좋은 계기가 되곤 한다.
그렇기에 겁내거나 도망치지 말자.
기회라고 생각하자. 실제로 정말 기회가 되기도 한다.


![[트러블슈팅] 서버를 터뜨린 쟈그마한 함수](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1748357643167%2Ffc68d237-0bfe-4f79-8e28-15fd98c5710d.png&w=3840&q=75)
![[기술 서적 후기 및 요약] 쏙쏙 들어오는 함수형 코딩](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1743129459556%2F14a7d741-d22e-4622-98e4-1857e449821e.png&w=3840&q=75)
![[Daily Flow 1.] Validator 리팩토링](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1741161717995%2Fa62693a8-0f24-4dc9-8d80-cefe541ed663.png&w=3840&q=75)