[잔뿌리 호기심]은 잔뿌리처럼 메인 주제에서 뻗어 나온 개인적인 궁금증을 다룹니다.
프론트엔드와 소프트웨어 아키텍처
글이 꽤나 깁니다. 자신이 아래에 해당하거나 얻어갈 항목에 흥미롭다고 생각하시는 경우 읽어보시길 추천합니다.
글을 읽으시면 얻어갈 게 있는 경우.
- 프론트엔드 개발 경력이 길지 않고, 소프트웨어 아키텍처에 대해서 개념만 알고 계신 경우.
- 프론트엔드 개발을 하긴 하는데, 소프트웨어 아키텍처를 적용하고 있지 않다고 생각하시는 경우.
- MVC, MVVM … 다양한 패턴이 있다는건 아는데 프론트엔드에서 쓰긴 어렵던데? 라는 생각을 하시는 경우.
글을 읽으시면 얻어갈 수 있는 부분.
- 프론트엔드 소프트웨어 아키텍처의 대략적인 흐름
- 프론트엔드 개발자에게 아키텍처란 무엇일까? ( 아키텍처의 실체 )
- 가까운 미래의 프론트엔드 아키텍처 흐름 예측
좋은 개발자
여러분이 바라는, 바랬던 미래의 모습은 무엇인가요?
모두 나름의 목표가 있겠지만 그 중간에는 ‘좋은 개발자’ 혹은 ‘잘 하는 개발자’ 라는 키워드가 끼어있지 않을까 싶습니다.
그렇다면 좋은 개발자, 잘하는 개발자는 무엇을 의미할까요?
‘좋은 개발자란 무엇일까요?’ 라는 질문은 한 마디로 정의하기엔 어려운 질문인 것 같습니다.
특히나 저에겐 아직 너무나 멀리 느껴지는 타이틀인데요, 그나마 좋은 개발자가 무엇인지는 설명하기 어렵지만 예시를 들 순 있을 것 같습니다.
우리가 흔히 좋은 개발자라고 말하는 사람의 예시가 있다면 백엔드의 김영한님, 프론트는… 그냥 제가 좋아하는 정재남님 정도 뽑겠습니다.
그 외에도 로버트.C.마틴이나 마틴 파울러처럼 엄청나게 유명한 월클개발자들도 있겠죠?
저희는 저희보다 잘 하는 개발자, 좋은 개발자들의 글을 읽고 그들의 생각을 조금이나마 복제하여 내 것으로 만들고 더 나은 개발자로 나아가고자 노력하곤 합니다.
잘 하는 사람의 모습을 모방하고 자신의 스타일로 변화해서 새로운 나의 스타일을 만들어 내는 것. 이것이 제가 말하는 지적 허영심의 목표이기도 합니다.
제가 읽었던 많은 글에서 좋은 개발자는 좋은 코드를 통해 좋은 프로그램을 만들기 위해 노력한다고 합니다.
좋은 코드
여기서 좋은 코드란 무엇일까요?
이 질문은 되게 쉬운 질문일 것 같은데요, 뭐 SOLID 원칙을 지키는 코드! 클린코드 관점에서 동작 뿐만 아니라 가독성 또한 뛰어난 코드라고도 할 수 있을 것 같습니다.
여기에 오늘의 주제인 하나의 키워드가 더 추가되는데요, 바로 ‘좋은 아키텍처’ 또한 좋은 코드를 만드는 요소 중 하나입니다.
실제로 저는 MVC 패턴을 사용해서 프로젝트를 진행해 본 적이 있는데요,
그럼에도 불구하고 저에겐 ‘소프트웨어 아키텍처’ 라는 단어가 너무나 전문적이고 멀게만 느껴졌습니다.
특히 ‘아키텍처’ 라는 단어가 주는 폭력성때문에 아직 제 수준에서는 다룰 수 없을 것 같다는 느낌을 강하게 받았거든요.
아키텍처라는 단어의 뜻은 건축 혹은 구조물 설계? 정도로 해석할 수 있는데요, 우리에게 건축이란 매우 견고해야하고, 동시에 작업에 들어가기 전에 완성되어야 하는 일 입니다. 그렇다 보니 ‘소프트웨어 아키텍처’라는 단어가 마치 계획단계에서 완성된 프로그램의 피쳐를 하나하나 완벽하게 파악하고 이에 적합한 구조를 설계해야한다는 것으로 느껴지죠.
반면 저희가 실제 개발에 들어가면 당장 개발하던 피쳐가 언제 사라질 지, 언제 새로운 피쳐가 생길지, 새로운 기획이 추가될 지 모른다는 것을 경험적으로 너무나도 잘 알고 있습니다.
당장 프로젝트 플래닝에서 한 개의 피쳐에 들어갈 MD 도 정확히 산출해내지 못하는데, 프로젝트 전반에 대해 불확실한 미래를 정확하고 견고하게 계획해내라는 의미처럼 받아들여지니 ‘소프트웨어 아키텍처’ 라는 단어가 저희에겐 너무나도 어려운 존재로 남아버리는 것이죠.
아키텍처란?
그렇다면 소프트웨어 아키텍처란 무엇을 의미할까요? 참 좋은 비유가 있는 것 같아서 가져왔는데요, 코드작성을 옷 정리에 비유하는 예제입니다. 우리가 옷을 정리하는 이유는 무엇일까요? 공간효율성을 확보해서 원하는 옷을 빨리 찾기 위해서가 아닐까 합니다.
옷이 하나뿐일땐 특별한 정리가 필요하지 않습니다. 오히려 옷걸이나 옷장을 들이면 공간 효율성만 떨어질 뿐이죠. 하지만 옷이 늘어날수록 옷걸이나 옷장이 필요하게 됩니다.
옷이 너무 늘어나면 단순히 섹션만 나눠져있는 옷장을 구매합니다. 마구잡이로 늘어놓았던 옷을 정리해두니 옷이 한쪽에 모여서 그래도 조금 나아진 것 같습니다. 하지만 옷이 더욱 늘어나니 새로운 옷은 어디에 넣어야할 지, 어디에 내 옷이 있는지 파악하기가 여전히 어렵습니다.
이번엔 옷은 옷대로, 양말은 양말대로 정리할 수 있는 옷걸이와 서랍이 있는 옷장을 새로 구매합니다. 드디어 저희가 원하던 옷장의 기능을 완전히 수행하는 옷장을 구매했습니다! 양말은 양말칸에, 셔츠는 옷걸이에. 옷들을 잘 정리해서 공간 효율성을 늘렸고, 옷마다 모아두어서 원하는 옷을 더욱 편하고 빠르게 찾을 수 있습니다!
소프트웨어 아키텍처는 이 옷장과 매우 유사합니다. 타고난 정리꾼이 아니라면 코드가 늘어날수록 원하는 코드가 어디에 있는지 찾기 어렵고 점점 방대해집니다. 특정 위치에 서랍이 있고 옷걸이가 설치된 옷장을 구매한다면 옷정리가 훨씬 더 체계적이고 쉬워지는 옷장과 같이, 이미 존재하는 규칙과 패턴에 맞춰서 개발한다면 편하고 이해하기 쉬운 코드가 작성될 것입니다. 우리는 여기서 우리의 코드작성을 도와주는 요소인 특정한 규칙을 소프트웨어 아키텍처 혹은 패턴이라고 부릅니다.
프론트엔드 아키텍처의 역사 ( feat. MV*)
이후 나오는 아키텍처의 흐름은 생각보다 지루하고 읽기 힘드실 수 있습니다.
결론만 보고싶으시다면 여기서 마지막 부분으로 넘어가셔도 무방합니다.
아키텍처는 하나의 규칙 혹은 패턴이라고 설명드렸습니다. 규칙을 만들기 위한 첫번째 조건은 분류입니다.
많은 프론트엔드 개발자들은 깔끔한 코드를 작성할 하나의 규칙을 만들기 위해 고민한 끝에 프론트엔드 아키텍처의 클래식. MVC 패턴이라는 구조를 만들어냅니다.
MVC 패턴
MVC 패턴이 등장하기 이전의 프론트엔드 아키텍처에서의 문제점은 많은 화면이 존재하고, 각각의 화면에서 사용자의 동작에 따라 데이터에 접근해야하기 때문에 코드가 방대해지고, 복잡해진다는 문제를 가지고 있었습니다.
이 문제를 해결하기 위해 MVC 패턴은 프론트엔드에서 보여지는 요소들을 Model, View, Controller 라는 세 가지 구분으로 나누어 명확한 책임을 부여한다면 해결할 수 있겠다는 발상에서 시작한 아키텍처입니다.
- 컴포넌트
- 뒤이은 설명에 앞서 계속해서 나올 컴포넌트라는 단위에 대해서 설명드리자면, 사전적 의미로는 하나의 기능을 담당하는 재사용 가능한 독립된 모듈을 의미합니다. 조금 더 추상적으로 설명드리자면 서비스라는 하나의 완성품을 만들기 위한 하나의 레고 조각이라고 생각하시면 편할 것 같습니다.
- Model - 데이터
- 프론트엔드는 특정한 데이터를 가공하여 사용자들에게 제공합니다. 데이터는 단순히 타이머가 될 수도 있고, API를 통해 받아온 정보들이 될 수도 있습니다. 이러한 데이터들을 담당하는 컴포넌트들을 Model 단으로 정의합니다.
- View - 화면
- 저는 어떤 훌륭한 서비스가 있더라도 사용자가 보고 사용할 수 없다면 좋은 서비스라고 부를 순 없다고 생각합니다.
프론트엔드의 꽃, 보여지는 화면에 해당하는 컴포넌트들을 View 단으로 구분합니다. 일반적으로 View 단은 최종적으로 사용자에게 전달되는 HTML 과 CSS 등의 결과물이 포함됩니다. - Controller - 컨트롤러
- MVC 패턴의 꽃. 컨트롤러입니다.
컨트롤러는 View에서 발생하는 모든 사용자이벤트를 캐치하고, Model의 데이터를 가공하여 다시 View 에 제공하는 역할을 수행합니다.
앞서 MVC 패턴 이전의 프론트엔드 아키텍처에선 각각의 View 에서 직접 Model 에 접근하여 수정했기 때문에 코드가 방대해지고 복잡해지는 문제가 발생했다고 소개해드렸었습니다. 이러한 문제를 해결하고자 View 에서 발생하는 모든 사용자의 동작을 Controller 에서 받고 Contorller 에서 Model 에 접근하여 수정하는 중앙 집중 방식을 생각하게 된 것입니다.
- MVC의 역사
- 극 초창기의 MVC패턴은 PHP나 루비 온 레일즈에서 사용되는것과 유사하게 DB를 Model로, [HTML, CSS, JS] 를 모두 통틀어 View로, DB에 접근하고 HTML을 반환하는 백엔드 영역을 Controller 로 취급했습니다. 하지만 JQuery 의 등장과 함께 AJAX 라는 기술로 인해 현재 프론트엔드 아키텍처와 유사하게 자리잡을 수 있었습니다. AJAX를 통해 받아온 데이터가 Model에 해당하고, HTML과 CSS를 View, AJAX 데이터를 가공하여 화면을 바꾸는 Javascript가 Controller 의 역할을 부여받은 것이죠.
여기서 아키텍처 관점에서 MVC 패턴을 정리해보자면, 아키텍처를 만들기 위한 첫번째 요소는 분류 라고 말씀드렸었습니다. MVC 패턴이 아키텍처(옷장)로서 분류(정리)하고자 했던 포인트는 컨트롤러라는 존재를 통해 데이터와 화면을 분리하여 의존성을 최소화하고자 했던 것입니다.
자료를 찾아보다보면 ‘MVC의 단점이 View 와 Model의 높은 종속성이다.’ 라는 자료도 있고,
이와 상충되게 Controller에 기능이 집중되어 Controller와 View의 종속성이 높아진다는 자료도 있습니다.
이는 어느 한쪽이 잘못된 자료라기보단, 해당 프로젝트의 구현방법에 따라 달라질 수 있는 부분이기에 둘 다 발생할 수 있는 요소라고 이해하시면 좋겠습니다.
MVVM 아키텍처
MVC 패턴을 대표하는 프레임워크가 JQuery였다면, MVVM 을 대표하는 프레임워크는 Angular입니다. JQuery에서 SPA 프레임워크 대격변의 시대로 넘어가던 때를 생각해보면 좋습니다.
MVC 패턴을 사용해보지 않고 구조만 들어본다면 매우 효율적인 패턴으로 보여집니다. 하지만 실제 서비스에 도입해 본 개발자들은 입을모아 DOM 조작의 불편함과 유지보수성, jQuery와 MVC패턴의 구조적 불협화음으로 인해 한계를 느꼈습니다.
이를 조금 더 아키텍처 측면에서 개선한 프레임워크가 바로 MVVM 패턴과 Angular입니다.
Angular는 jQuery의 매번 DOM 조작을 해줘야하는 불편함과 반복적인 행동에 의한 컨트롤러의 비대함을 선언적 방식을 통해 개선했습니다. 또한 Model과 View를 ViewModel이라는 요소를 활용한 양방향 데이터 바인딩을 통해 View와 ViewModel(Contorller)의 결합도를 낮췄습니다. 흔히 Angular 의 장점을 얘기할 때 ‘양방향 바인딩’이라는 키워드가 나 오는 이유도 여기에 있습니다.
- Model
- MVVM 패턴에서의 Model 또한 보여져야 할 데이터를 다루는 점은 유사합니다. 하지만, 다른 점은 MVC에서의 Model 에 대한 변경은 Controller 에 의해 이뤄지는 반면, MVVM의 Model의 변경은 ViewModel에 바인딩되어 이뤄진다는 점입니다.
- View
- MVVM 패턴에서의 View의 가장 큰 차이점은 더이상 Controller의 지시에 의존적인 수동적인 요소가 아니라는 점 입니다. 물론 Controller 가 사라졌으니 당연한 게 아니냐 하실 수 있겠습니다만, 이 또한 ViewModel이라는 요소에 바인딩되어 Contorller의 변경 지시가 내려질 때 까지 기다리는 존재가 아닌 바인딩을 통해 자동적으로 변화하는 요소가 되었습니다.
- ViewModel ( Controller )
- 드디어 MVVM 패턴의 꽃 ViewModel입니다. ViewModel은 이름에서 알 수 있다시피 View와 Model을 연결해주는 역할을 수행합니다. 양 측에 모두 바인딩되어 ‘양방향 바인딩’을 구현합니다. 따라서 이전의 Controller는 이벤트에 따라 View와 Model 데이터들을 수동적으로 변경해줘야하는 로직이 존재했던 반면, ViewModel은 바인딩을 통해 구독형식으로 자동으로 바뀔수 있도록 합니다.
- MVVM의 역사
- 매번 DOM 요소를 적절하게 변화시키느라 비즈니스로직에 집중하지 못했던 프론드엔드 개발자들은 MVVM과 Angular 의 등장으로 인해 변곡점을 맞이합니다.
아키텍처의 첫번째 요소가 분류였던 점을 기억하시나요? 개발자들은 거대한 하나의 규칙이 생기자, 조금 더 세부 규칙을 원하게 됩니다. 드디어 우리에게 많이 친숙한 개념이 등장하는데요, 더이상 Page라는 거대한 하나의 단위가 아니라, Page를 여러개의 모듈로 나누는 Component 패턴이 등장하게 됩니다.
Container - Presenter 패턴
이제 더이상 Page라는 하나의 큰 요소가 아닌 재사용할 수 있는 여러개의 컴포넌트를 구성하고 조립하여 사용하고자 발전했습니다.
하지만 재사용이 가능해야하는 Component에 비즈니스 로직이 포함되는 순간, 재사용성이 급격하게 감소하게 되는 문제를 해결하기 위해 Container 컴포넌트와 Presenter 컴포넌트로 분리하게 됩니다.
- Container
- 컴포넌트마다 담당해야하는 비즈니스로직들이 존재합니다. 그러한 비즈니스 로직들만을 담당하는 컴포넌트를 Container 컴포넌트라고 칭합니다.
- Presenter
- 반면, 비즈니스로직을 담당하지 않고 단순히 데이터를 뿌려주기만 하는 컴포넌트를 구성하여 Presenter 컴포넌트라고 칭합니다.
서비스가 커지면서 Conatiner - Presenter 패턴을 확장시켜 나간 결과, 또 다시 반가운 키워드 ‘Props Drilling’ 문제를 접하게 됩니다. 흔히 리액트에서 Props Drilling 이 안티패턴으로 평가되는 이유는 리랜더링 이슈 때문입니다.
반대로 아키텍처 차원에서 Props Driling 이 안티패턴인 이유는, 컴포넌트에 사용하지도 않는 Props가 단순히 내려줘야 하기 때문에 불필요한 종속성이 추가되기 때문입니다.
따라서 Props Drilling 문제를 해결하기 위해 FLUX 패턴이 등장합니다.
FLUX 패턴
앞선 다양한 패턴들이 가지고있는 특징은 양방향 혹은 일대다 관계에 놓여있다는 점이었습니다. 양방향 바인딩은 비즈니스로직에 집중할 수 있는 이점을 가져다 주었지만, 동시에 문제가 생길 경우 어디서 문제가 발생했는 지 찾기 어려운 디버깅 이슈를 가져다 주었습니다. 일대다 관계는 관심사 분리를 통해 재사용성을 늘려주었지만, 종속성 문제와 복잡성 증대라는 문제를 가져왔습니다. 이 문제를 해결하기 위해 Facebook 팀은 FLUX 패턴이라고 하는 단방향흐름 아키텍처를 제안하게 됩니다. 하지만 여전히 실체는 없는 하나의 개념 제안에 멈췄습니다.
- Redux
- 기존의 Props Drilling 에 대한 문제를 정확히 조준한 Redux 의 등장은 전역상태관리라는 새로운 개념을 탄생시킵니다. Redux 는 앞선 FLUX 패턴을 이용한 구현체로서 Store, Dispatch, Reducer 라는 개념을 더욱 명확히 정리하여 대중에게 소개했습니다.
- Store
- 첫번째 요소인 Store는 이름에서 알 수 있듯이, 다양한 상태를 저장하는 객체를 담당합니다.
- Dispatch
- 두번째 요소인 Dispatch 는 액션을 호출하는 역할을 담당합니다. 액션을 Reducer에 전달하는 중간다리 역할을 담당한다고 이해하시면 좋을 것 같습니다.
- Reducer
- 세번째 요소인 Reducer는 상태가 어떻게 변경되어야 할 지 정의하는 함수에 해당합니다. Reducer는 순수함수로서 존재해야 하며, 입력이전 상태를 기반으로 새로운 상태를 반환해야 합니다.
- Redux의 FLUX 패턴
- 초기 FLUX 패턴은 단순히 단방향 흐름에 대한 아이디어 제시였다면, Redux는 Store, Dispatch, Reducer의 흐름을 통해 단방향 상태 관리 흐름을 생성하여 예측 가능하고 일관된 방법을 통해 상태관리를 구현했습니다.
하지만 Redux의 FLUX 패턴에게도 단점이 존재했으니, 우리가 잘 알고있는 Redux의 단점 ‘높은 러닝커브’와 ‘비대한 보일러 플레이트’ 입니다.
이를 해결하기 위해 대 상태관리 라이브러리 시대가 열리게 되니 그 중 가장 유명한 새로운 패턴인 Atomic 패턴이 등장합니다.
Atomic 패턴
[ Recoil, Zustand, Jotai … ] Atomic 패턴은 전역 상태관리라는 관점은 동의하나 Dispatch, Reducer 와 같은 복잡한 구조엔 회의적인 생각을 가진 개발자들에 의해 전역 상태관리를 편리하게 사용하고자 하는 일념으로 생성된 패턴입니다.
비교적 간단한 문법으로 전역상태를 사용하고 변경할 수 있도록 발전했습니다.
이후엔 전역상태로서 존재하는 대다수의 데이터가 AJAX 데이터라는 점에서 착안하여, 전역상태관리 또한 너무 복잡하다는 의견을 통해 탄생한 라이브러리가 MVC패턴을 확대하여 적용한 React-Query 입니다.
그래서 프론트엔드 개발자에게 소프트웨어 아키텍처가 무슨 의미인데?
여기까지 읽으셨다면, '과거 역사에 대해선 알겠지만, 그래서 도대체 프론트엔드 개발자에게 소프트웨어 아키텍처가 뭔데?'
라는 질문을 계속해서 가지고 계실 것 같습니다.
제가 소프트웨어 아키텍처에 대해서 고민하게 된 이유는 이전에 Vanilla JS MVC 프로젝트를 경험한 이후로는 제가 뭔가 ‘아키텍처’ 라고 부를 만큼 뭔가 거창한 하나의 규칙을 따르고있지 않아서 라고 생각했기 때문입니다. 아마 이 글을 읽으시는 많은분들도 저와 비슷한 거북함을 느끼고 계실 것이라 생각합니다.
최근 프론트엔드는 React 를 주류로 한 NextJS 가 완전히 장악한 것으로 보여집니다. 실제 다양한 Vue를 사용하는 회사 입장에서는 구인을 위해, 조직 구성원은 이직을 위해 React나 Next 도입을 적극적으로 시도하고 있구요.
여기서 다시 한번 소프트웨어 아키텍처에 대한 설명을 되돌아 볼까요?
옷정리를 비유하여 우리의 코드작성을 도울 수 있는 하나의 규칙을 아키텍처 라고 소개해드렸습니다.
그렇다면 도대체 프론트엔드나 Next에서 옷장처럼 코드 작성을 도와주는 하나의 규칙이 어떤게 있을까요?
눈치 채셨나요? 맞습니다. Next 의 App 라우터 폴더구조 패턴 또한 하나의 규칙, 하나의 아키텍처, 패턴으로 바라볼 수 있는 것입니다.
라우터 폴더구조 뿐만 아니라 다양한 상수들을 매번 선언하는것은 불편하니 Constant 폴더에 모아두는 과정, API요청을 보내는 함수들을 매번 생성하는 과정이 귀찮으니 추상화해서 분리하는 과정 모두 각자 나름의 아키텍처를 만들어가는 과정인 것이죠.
프론트엔드 개발자에게 아키텍처란 방향성이다
프론트엔드에서의 아키텍처는 특정한 실체가 있는 것이 아닌 하나의 방향성입니다.
하나의 규칙을 정해야, 어디에 기능을 개발하고 저장해야 할 지 쓸데없는 고민에 들어가는 리소스를 최소화 할 수 있습니다. 만약 이런 규칙이 정해지지 않았고, 개발 조직의 규모가 커진다면 이 쓸데없는 고민에 들어가는 리소스는 급격히 거대해지겠죠. 이런 불필요한 리소스 누수를 규칙으로서 방지하고자 아키텍처를 고민하는 것 아닐까 싶습니다.
아키텍처는 잘 짜여진 하나의 완벽한 구조가 될 수도 있지만, 우리가 평소에 폴더를 나누는 과정 또한 아키텍처 라고 부를 수 있습니다.
그러니 ‘아키텍처’나 ‘패턴’이라는 거창한 이름에 지레 겁먹고 우리 주니어 개발자들에게는 너무 머나먼 키워드라고 무서워 할 필요는 없을 것 같습니다.
프론트엔드 아키텍처의 가까운 미래
그간의 프론트엔드 아키텍처의 발전 흐름을 살펴보면 의외로 하나의 공통된 주제가 반복적으로 등장합니다.
바로 비즈니스로직과 인터페이스의 분리 입니다.
MVC, MVVM 외에도 MVP, MVI 등 다양한 패턴이 시도되고 있지만 모두가 바라보는 방향은 비즈니스로직과 인터페이스 구현부의 분리입니다.
비단 아키텍처 패턴 뿐만 아니라 최근 가장 핫한 라이브러리 중 하나인 TailwindCSS 또한 비즈니스 로직과 인터페이스 구현부를 분리하여 작성할 때 더욱 높은 생산성을 기대할 수 있습니다.
이러한 비즈니스 로직과 인터페이스 분리에 대한 아키텍처 패턴의 모범답안은 아직 완성되지 않은 것으로 보이며, 앞으로도 꾸준히 발전 해 나가지 않을까 조심스럽게 예상해봅니다.



