노트북을 열고.

[성장회고]나의 첫 프론트엔드 개발 도전기 본문

휘갈기는 글

[성장회고]나의 첫 프론트엔드 개발 도전기

ahndy84 2023. 1. 27. 22:44
입사하시게 되면 프론트엔드 개발도 가능하실까요?
가능합니다.
저희는 리액트를 사용하고 있어요.
혹시 리액트에 대해선 좀 알고 계실까요?
(하아. 예전에 문득 들어보긴 한거 같은데.. 먹는 거 아니었나?)
글쎄... 잘은 모르지만 해보면 할 수 있어요.

 


이전 직장에서 줄곧 PHP기반 레거시 시스템 고도화 업무를 다뤄왔습니다. 사용자 페이지를 작성하는 일이든 그 뒤에 비즈니스 로직을 구현하는 일이든 뭐든 가리지 않고 뚝딱뚝딱 해치어 왔으니 그때까진 두 영역 간에 차이는 없다고 봐도 무방했죠. 늘 그래왔듯 프론트엔드영역이란 결국 HTML로 정적인 요소를 배치하고 자바스크립트를 통해 동적인 조작이나 데이터패치를 해오는 정도로 활용해 왔기에 프론트엔드란 마치 제 아이폰 케이스처럼 우리의 비즈니스를 사용자관점에서 보기 좋게 감싸는 껍데기(?) 정도로 치부해 왔습니다.

이게 생각이 났어요.

 

돌이켜보면 프론트엔드영역이라 볼 수 있는 뷰 페이지를 작성하는 건 늘 피곤한 일이었습니다. 각각에 보여주고자 하는 UI를 배치하고 특정 엘리먼트영역에 값을 바인딩하고 정의된 CSS스타일을 입히는 업무는 다이나믹하지 않고 때론 지치고 지루하니까요. 아는 만큼 보이는 법마냥 딱 그 정도의 시각에서 업무를 바라보았기에 데이터를 직접 드리븐하는 백엔드영역보단 프론트엔드영역은 좀처럼 손이 가진 않고 굳이(?) 해야만 할 때 해오던 그런 업무영역이었습니다.

내가 임마! php도 하고, jsp도 하고, 제이쿼리도 하고, 마 다 했어!

이곳 프로덕트의 프론트엔드는 react.js로 구현되어 있다는 정도만 듣고 내심 뭐가 됐든 제가 그간 업무에서 프론트엔드를 한 번도 다뤄오지 못한 것도 아니고 일단 해보면 안 될 게 없다는 고 정주영 회장님과 같은 포부와 패기로 이곳에서 백엔드뿐 아니라 프론트엔드업무도 도맡아 맡겠구나라는 마음의 준비만 장전한 채 새직장에서의 업무개시를 준비하고 있었습니다.

 

쉽지 않은 요즘 것들


그러고보면 당시에 전 프론트엔드계의 흥선대원군과 같은 존재였을지도 모릅니다. 최근까지 그렇게 힙하다는 Go, Kotlin, Rust 같은 언어들의 릴리즈업데이트소식은 꾸준히 찾아보긴 했어도 프론트엔드의 세상은 아직도 jQuery 만능주의로 돌아가고 있다고 생각했으니까요. 아닌 게 아니라 지금으로부터 약 8년 전 재직 중인 회사의 동료 한분께서 줄기차게 우리도 신규프로젝트에 당시 각광받기 시작한 Angular.JS를 한번 도입해 보자는 제안에 옆에 조용히 듣고 있던 과장님께서

앙굴라? 제이쿼리로 다 할 수 있는데 그걸 왜 혀? 바빠 죽겠는디.

 

그 한마디로 그 자리에 모인 개발자 구성원 모두를 평정했던 순간이 떠오릅니다. (물론 지금도 당시 과장님의 발언이 틀렸다고 생각하진 않습니다. 어디까지나 도구는 그저 도구일 뿐이죠.) 꼭 리액트라서가 아니라 최근까지 각광받고 있는 프론트엔드 라이브러리에 대해 한번 살펴보고 싶었습니다. 사용자 페이지에선 제가 알고 있는 $("...")라는 시그니처 하나만으로 Dom으로 구성된 html의 모든 요소들을 모든 조작이 가능할텐데 왜 자꾸 새롭다 하는 것들이 나와서 우리를 불편하고 헷갈리게 하는지.

 

리액트에 대하여.


Virtual Dom으로 동작된다.

화면에서 사용자가 이걸 누르면 이 부분은 요렇게 바뀌구요. 저 부분은 없는데 생겨나야 해요

 

........ 네?

사용자 화면에서 동적인 요소가 복잡하게 가미된 기능구현을 요구받을 때마다입니다. 매번 고민해 왔던 것은 HTML페이지에 구성된 각각에 DOM 요소들이 서로 의존하며 개별적으로 동작과정을 과연 어떻게 구조화된 설계로 가져가야 하는가였습니다. 트리구조를 기반으로 하는 DOM요소들은 자신의 상위 DOM을 의존하고 있기에 상위 DOM 요소에서 연산이 일어나면 결국 그 하위의 DOM 요소들도 똑같이 연산이 반복해서 일어나게 되죠.

DOM이라는 요소로 구조화된 HTML 페이지

페이지 렌더링이 불필요하게 늘어날 뿐만 아니라 이를 최적화하기 위한 방안도 개발자의 시각에서 직접 풀어야 하고 그 과정 또한 어렵고 경우에 따라 생각지 못한 유지보수 비용이 발생할 수도 있습니다. 되도록이면 페이지가 렌더링 되는 과정도 절자지향적이기보다는 결과에 기반하여 최종 결과를 미리 알고 `알아서 잘` 연산이 이루어지면 더 효율적일 겁니다. 없었는데 잠시 내 곁에 있다가 다시 없어지는 것이라면, 처음부터 그냥 없었던 것이 우리가 인생을 살아가는데 좀 더 편할 수도 있겠죠.

[500일의 섬머]를 보면서 그런 생각을 했어요.

리액트에선 바로 이러한 과정을 생략해 줍니다.
어떻게 가능하냐면 바로 리액트 내부에서 각 페이지(혹은 컴포넌트)로 구성된 DOM방식의 HTML문서마다 동일한 가상의 DOM을 두고 그것이 렌더링과 관련된 연산을 스스로 감지하여 최종적으로 연산된 결과의 DOM을 페이지에 반영해 줍니다. 이 방식을 통해 페이지 내부에 각각의 계층으로 의존하는 DOM 요소 간 연산과정을 하나로 묶어 미리 최적화된 형태로 작업을 해주게 되죠.

 

마! 이 얼마나 살기 좋은 세상이고.

 

Component를 기반으로 한다.
과거에 간혹 1000라인이 넘어가는 JSP페이지를 유지보수를 해야 할 때면 문득 그 파일 히스토리에 들어가 컨트리뷰터들을 확인해 보곤 합니다. 자조와 원망스러운 감정을 주마등에 실려 보내고 기도를 한 후 코드분석에 들어가곤 했는데요. 과거에 사용자페이지영역에 해당하는 코드를 바르게 유지관리해 가는 건 사실 좀처럼 쉽지 않은 일이기도 합니다.

앞서 설명드린 Dom 트리구조상 각각에 조작되는 Dom 요소들이 가지고 있는 의존성과 함께 각각에 배치된 요소들은 결국 각각의 계층적 엘리먼트로 귀속되기 때문에 그 부분만 따로 조각조각 잘라내어 관리할 수도 노릇이기 때문입니다. 그러나 리액트에선 그것을 가능하게 해 줍니다.

import React from 'react';
import ReactDOM from 'react-dom';
 
class MyComponent extends React.Component{
    render(){
        return(       
              <div>           
                <h1>Hello</h1>
                <h1>This is a Component</h1>
              </div>
        );
    }
}
ReactDOM.render(
    <MyComponent/>, document.getElementById('content')
);

하나의 HTML문서마다 구성된 Dom 요소들을 별도의 의미를 부여한 컴포넌트로 구성할 수 있습니다. 조회 목록요소나 버튼 UI 혹은 특정 header나 footer영역 등 특정 태그로 나뉘는 모든 요소들을 파일단위가 아닌 하나의 컨포넌트(함수)로 떼어 정의하고 그것을 가져다가 페이지 어느 곳이든 자유롭게 배치해 놓을 수 있죠. 

이렇게 자바스크립트로 정의된 dom요소들을 JSX(Javascript XML)라고 불리웁니다. 미리 정의된 공통화된 DOM 요소들을 자유롭게 떼었다 붙였다 재사용할 수 있기에 결과적으로 코드의 라인수가 줄어들면서 코드의 가독성이 좋아질 뿐 아니라 기존 전체 페이지단위로 관리가 되어오던 것들이 더 세밀한 컴포넌트기반로 관리가 되니 전반적인 유지보수의 품질도 높일 수 있습니다. 이렇게 각각에 컴포넌트 간 느슨한 결합을 통해 자연스럽게 관심사의 분리원칙(Seperation Of Concerns)도 이루어가게 되죠.

 

판도라의 상자


이외 더 많은 것들이 있겠지만 현재 이 두 가지만으로도 요즘 것들 중에 하나인 리액트를 사용함으로써 가져올 수 있는 실리는 자명했습니다. 사용자경험측면은 차치하더라도 이것만으로도 기존에 지루하고 반복적인 페이지작성 업무의 생산성과 효율성을 또렷이 높일 수 있다는 기대를 해 볼 수 있으니까요. 새 회사 입사직전 약 2주간의 쉬는 기간 동안 리액트의 공식문서에 나온 기본적인 개념을 찬찬히 탐독하고 인프런에서 리액트 초급자 강의를 수강하였습니다. 강의에 나온 서비스예제들을 직접 따라 만들어보며 이것저것 바꿔 보기도 하고 직접 컴포넌트를 정의하여 기존 서비스예제에 붙여보는 과정을 통해 리액트에 대한 전반적인 동작과 기본개념들을 다져갈 수 있었습니다.

 

입사첫날, 새로운 맥북을 하사 받고 개발환경을 세팅하고 직전에 익혀온 리액트에 대한 감을 가지고 MVP FE프로젝트 디렉터리를 이제 막 열어본 찰나였습니다.

어?...

 

감히 무엇이 맞고 틀리다는 걸 감별할 수 있는 입장이 되진 못할지라도 적어도 순수한 초보 리린이의 시각에 본 우리 프로젝트 구조의 생김새는 앞서 공부한 리액트 도입으로 가져올 수 있는 두 가지의 특징들을 찾아볼 수 없었습니다. 이벤트가 발생될 때마다 필요하지 않는 페이지렌더링은 반복해서 일어나고 페이지 저마다 동일한 함수들이 여기저기 중복해서 정의되어 있습니다.

많은 이유들이 있었습니다. 이제 막 시작한 개발 빌드업과정에서 FE프로젝트의 개발리소스의 부재로 결국 외주개발의 힘을 빌리게 되었습니다. 협업과정에서 갖가지 소통비용과 그로 인한 촉박한 상황들이 발생되면서 MVP개발스펙의 목표는 완수하였으나 그로 인한 상당 부분의 기술부채가 쌓인 상태였습니다. 이후에 사실상 방치되어 온 품질의 개간과정을 필요로 하고 있는 시점이 바로 지금 제가 입사 후 이것을 훑어보고 있는 지금 이 순간이었던 것입니다.

다른 한편으론 더 나은 상황이라 생각했습니다. 제가 기초적으로, 그러나 본질적으로 알고 있는 지식들을 적용하여 그로 인한 효과를 체감하며 학습해 볼 수 있는 상황이었으니까요. 그동안 외주리소스가 주도해 왔던 프론트엔드 개발의 의존도를 온전히 우리의 것으로 가져올 수 있는 상황이라 판단되었기 때문입니다. 분주하게 회의를 진행하고 난 뒤 난잡하고 어지럽게 펼쳐진 문서들을 하나씩 정리하듯 두 팔에 소매를 걷어붙이고 본격적으로 다음의 두 가지 작업을 착수하였습니다.

 

망가뜨리기


여 하나 썰고

첫 번째. 페이지 렌더링 최적화
우선적으로는 각 페이지 로드가 발생되는 부분을 진단하고 불필요하게 중복해서 렌더링이 발생되지 않도록 Memoization을 적용해 주었습니다. Memoization이야말로 작은 변경점으로 react로 구현된 페이지의 성능을 드라마틱하게 최적화할 수 있는 방법이기 때문입니다.


이로 인한 성능의 개선점은 다음과 같았습니다.

부모 컴포넌트가 렌더링 될 때마다 자식컴포넌트까지 연쇄적인 렌더링이 일어나지 않습니다.
부모 컨포넌트로부터 받은 props나 state에 변경이 일어나도라도 쓸데없이 하위 컴포넌트까지 렌더링이 일어나지 않습니다.

 

두 번째. 컴포넌트 구조화
저마다 파편적으로 정의된 컴포넌트들을 취합하여 하나의 공통화된 표준 컴포넌트로 정의하고 프로젝트 패키지 컨텍스트를 좀 더 명확하게 구분되도록 변경했습니다. 파일업로드, 수정, 삭제와 같은 공통화시킬 수 있는 기능들을 단일 UI컴포넌트로 새롭게 정의하여 그 기능을 필요로 하는 어느 페이지에서도 인스턴스처럼 쉽게 불러와 사용되어질 수 있도록 하였습니다.

페이지와 UI 그리고 기능에 대한 영역까지 컴포넌트단위로 분리가 가능하기 때문에 과거에 JS와 HTML이 서로 덕지덕지 뒤섞여 존재했던 파일들이 굉장히 깔끔하게 정리 정돈할 수 있게 되었습니다. /page라는 이름으로 정의된 패키지 내부에는 더 이상 공통 UI요소로 정의된 컴포넌트 파일들이 존재해 있지 않고 별도의 정의한 /components 패키지 내부에 만들어 관리되도록 하였습니다.

세 번째. monorepo로 구성하기
고객을 대상으로 하는 상용서비스. 그 고객들에게 관리해 주는 고객관리서비스, 시스템전반을 관리하는 시스템관리서비스 이렇게 총 3개의 시스템단위에 프로젝트가 존재했습니다. 앞서 말한 리액트를 사용함으로써 얻을 수 있는 이익인 컴포넌트 재사용성의 이익을 극대화해 가기 위해선 위 3개의 서비스프로젝트를 좀 더 유연하고 확장성 있게 유지관리할 필요가 있었습니다. 동일한 성격의 컴포넌트가 각 서비스 프로젝트마다 중복해서 정의되고 관리되는 건 개발 생산성에 있어 관리포인트의 중복이 발생되고 그건 유지보수관점에서 꽤 지불하기 아까운 비용이니까요.

Lerna 라는 패키지도구를 활용하여 3개의 개별적인 서비스 프로젝트를 하나의 Monorepo 프로젝트로 구성하였습니다.

각각의 프로젝트마다 프로젝트 리포지토리로 관리되어 오던 것을 하나의 프로젝트내부에 속해 있는 각각에 패키지 단위로 나누고 각 프로젝트에 공통적으로 재사용할 수 있는 UI컴포넌트들은 공통요소로 구성된 새로운 패키지로 구성하였습니다. 이제 UI를 구성할 때 매번 동일한 컴포넌트들을 각각에 프로젝트에서 정의할 필요가 없어졌습니다. 패키지별 의존성도 하나의 노드모듈에서 관리되고 버전까지도 일원화가 가능해졌습니다. 이전보다 훨씬 더 모던해지고 훨씬 더 편안한 환경에서 프론트엔드개발을 유지관리해갈 수 있게 되었습니다.

 

처음의 구조에서 이것저것 만져가며 점진적인 리펙토링이 진행이 되어갔습니다. 물론 아직까지 FE영역에 대한 배경지식이나 경험이 부족하기에 나름에 주관에 따라 변경된 것들이 많고 경우에 따라 기존의 것에 비해 오히려 더 안 좋게 망가져 버린 건 없는지 내심 고민되는 지점도 생겨났습니다. 지금도 FE 개발업무를 진행해 오면서 컴포넌트의 구조를 설계해 가는 과정에서 또렷한 확신이 서지 않는 때가 많습니다. 예컨대 memoization이라는 기법은 리액트뿐만 아니라 보편적으로 사용되고 있는 프로그래밍기법인데 반해 이것을 사용했을 때 고려해야 할 사항은 전혀 없는지가 궁금합니다. 결국 이러한 캐싱이 되는 비용이 커질수록 서비스품질 전반에 걸친 리스크가 존재할 수 있을 것이라는 생각을 가져보게 됩니다. 이 부분와 관련하여 공식문서에서 따로 언급된 부분을 찾지 못했는데 한번 유심히 더 살펴볼 예정입니다.

 

돌이켜 보며


실무 수준에서 프론트엔드개발의 첫 삽을 떠 볼 기회를 얻게 되었습니다. 과거 백엔드영역과 프론트엔드영역 간에 기술적인 구분을 나누는 것에 조금 새삼스러움을 여겨왔던 저로썬 이 영역에서의 첫 발을 담그는 데엔 여러번의 시행착오를 거치는 순간도 있었습니다. 첫 기능개발일감 QA단계에선 생각보다 너무나 많은 에러케이스가 발견되어 개발브런치에서 과감히 롤백을 단행되기도 했었습니다.

무지가 지식보다 더 강한 자신감을 갖게 만든다. -찰스 다윈

첫 프론트엔드 기능개발을 워밍업 하면서 과거엔 느끼지 못했던 사용자경험에 대해 직접 체감하기 시작했습니다. 이벤트 발생시점마다 페이지 전체가 리렌더링 되면서 아주 잠깐에 깜박임이나 지연들이 감지가 되고 그것들이 로드되는 데 걸리는 시간을 관심있게 측정해 가게 되었습니다. 백엔드영역의 경우 데이터를 실제적으로 주도하는 영역이다 보니 그 시각에서 보는 프론트엔드 영역의 세계는 상대적으로 얕게 비추어 보였습니다. 과거에는 퍼블리셔라 불리우는 업무영역에서 CSS와 HTML와 같은 정적인 페이지를 기획하고 그려왔고 그것이 프론트엔드 영역의 전부라 봐도 무방했었으니까요. 다양한 사용자디바이스환경이 등장하고 그 사용자가 기대하는 요구 수준도 높아진 만큼 프론트엔드영역에서 고민해야할 기술적인 경험과 지식은 날로 높아져가고 있다는 사실 또한 이번 '보직전환' 경험에서 얻은 통찰이었습니다.

 

마침내


MVP개발단계를 지나 실무적 경험에 대한 예열을 마치고 새로운 프론트엔드 기술스택을 기준으로 본격적인 프로덕트 개발을 착수를 했습니다. Next.js 프레임워크와 타입스크립트를 기반으로 초기보다 더 안정적이고 견고한 수준의 프로덕트를 만들어 갔고 마침내, 2023년 1월 2일 우리의 서비스가 처음 세상에 런칭했습니다.

대치동, 목동 선생님들과 1:1 수학 온라인 클리닉, CLUS

👉 https://www.clus.co.kr 👈

 

이 과정에 오기까지 우리가 얻은 성과는 다음과 같습니다

1. 외주와의 기술적 의존성으로부터 완전히 자유로워졌습니다.

2. 주니어 새 동료의 합류로 드디어 팀단위의 프론트엔드 개발조직이 세워졌습니다.

3. MVP단계를 거쳐 스프린트단위로 일감을 세우고 실행해갈 수 있는 업무기반이 만들어졌습니다.

그동안 줄곧 백오피스로 대변되는 인터널 서비스 백엔드 개발업무을 담당해 오면서 실제 이용고객을 대상으로 하는 익스터널 서비스, 그중에서도 프론트엔드의 세계가 궁금했습니다. 맨 끝자락에서 그곳을 대칭하는 반대쪽 끝자락까지 걸어가 경험해보고 싶었습니다. 풀스택개발자라기엔 좀 거창하고 과분한 표현이겠고 그저 양 영역을 넘나들면서 참견질하고 한번 우당탕탕 망가뜨려보고 신규기능도 구현해보고자 한 욕심이 늘 자리하고 있었습니다. 그렇게 프론트엔드 개발리소스가 절실했던 이곳에서 마침 발을 담가 본 프론트엔드의 실무 개발경험은 어쩌면 제가 운이 타고난 것도 있고 그저 무지에서 비롯된 용감함을 믿고 맡겨준 이곳 동료들이 있었기에 가능했습니다.

 

태풍이 지나가고


서비스가 런칭되는 시점에 또 한명의 프론트엔드개발 동료가 합류하고 이제 프론트엔드는 어엿한 조직을 이루며 우리가 기획한 서비스를 능수능란하게 확장해갈 수 있는 전력을 갖추게 되었습니다. MVP단계를 넘어 현재의 서비스가 새롭게 구축되기까지 6개월의 기간동안 프론트엔드영역을 리드를 담당해 오며 마주하는 러닝커브의 피로감, 쫒겨오는 일정 등 글로는 다 표현못한 우여곡절이 있었지만 그 기억은 그저 지나 간 기억으로 흘려 보낸 채, 입사 당시에 급히 해결해가야 했던 이슈, 런칭스펙의 프론트엔드개발 담당에서의 제 역할과 소임은 이쯤에서 마무리하고 다시 백엔드영역으로 커리어를 회귀하고자하는 시점에 와있습니다.

이곳에 합류하기 전까진 스스로가 개발자로서의 성장 정체기라 느꼈던 때로 기억됩니다. 새로운 도전이 필요할 때에 합류하게 된 이곳에서 뭐가 되었든 당장에 필요로 하는 업무영역에서 주도적으로 쓸모있게 기여해 가고자 했습니다. 한 번도 경험해보지 못한 영역에 대한 두려움은 곧 드디어 경험해 볼 수 있는 기대감과 설레임으로 걷어내기 충분했습니다. 기꺼이 새로운 업무영역에 참여했고 그렇게 묵직하게 리드해 갔습니다.

 

내가 꿈꾸는 시니어로 성장하고 있을까?

가끔씩 북상하는 태풍 앞에 과거보단 조금 더 담대하고 의연하게 대응하고 대처할 수 있는 용기를 얻을 수 있었습니다. 꼭 기술적인 역량에 국한된 것이 아니라 무엇이 되었든 성장의 정체기가 문득 찾아올 때 그것을 어떻게 마주하고 극복해 갈 수 있는 태도와 그로부터 오는 성취에 대한 경험 말이죠.

어렸을 적, 라이프 오브 파이라는 영화가 떠오릅니다. 파이라는 이름의 어린 소년과 호랑이가 거센 폭풍우를 만나 표류되어 온갖 산전수전을 겪으면서 마침내 득도 수준에 이르게 되는데 거기서 그는 이런 독백을 합니다.

당장 할 수 있는 일에 집중하는 것에서 생존은 시작된다.


어차피 불가항력의 태풍을 마주해야 한다면 지금 내가 해야 하는 일을 찾아내 최선을 다해 가다 보면 어느새 태풍은 저만치 지나가 있습니다. 살다가 또 다른 태풍을 마주할 날을 기대하며 그렇게 더 나은 개발자로 성장해 갑니다.

'휘갈기는 글' 카테고리의 다른 글

[퇴사회고]책상을 정리한 뒤  (425) 2022.05.07
2020 회고 <다시 대항해시대2>  (784) 2021.01.01
2019 회고 <실수에 관하여>  (882) 2019.12.30
2019 돛을 올리며 (2018 회고록)  (406) 2019.01.01
첫 글  (426) 2018.06.16
Comments