프론트엔드

[Next JS] 슬기롭게 서버컴포넌트 사용하기 3 - 아키텍처

CandDoIt 2025. 3. 20. 00:44

적극적으로 서버 컴포넌트를 활용하자!

 

https://younghun123.tistory.com/3 - 서버컴포넌트는 어떻게 사용해야 할까?

 

안녕하세요! 저번 포스팅에서는 서버 컴포넌트를 어떻게 활용해야 하는지에 대해서 고민했습니다.

서버 컴포넌트는 waterfall 방식으로 동작하기 때문에 사용자와 복잡하게 상호작용하는 UI를 만들기 어렵습니다.

 

그렇다면, 어떻게 컴포넌트를 디자인해야할까요? 필자는 보통 세가지의 원칙을 가지고 컴포넌트를 디자인합니다.

1. 비즈니스와 뷰의 관심사 분리

2. 컴포넌트 분리

3. 클린 아키텍처에 대한 고민

첫번째, 비즈니스와 뷰의 관심사를 분리할 수 있습니다.

프론트엔드 애플케이션에서 분리할 수 있는 관심사는 크게 Business와 View입니다. View 계층에서는 오로지 데이터를 시각화합니다.

 

View를 담당하는 계층은 컴포넌트입니다. 컴포넌트는 정해진 props만 받아 항상 일정한 UI를 제공합니다. 컴포넌트는 외부 인프라(API)들과는 독립적이어야 합니다. 컴포넌트가 정해진 데이터만 받는 객체라고 보장할 수 있다면, 개발자는 데이터를 보여주는 로직에 더 집중할 수 있습니다.

위 그림과 같이 컴포넌트입장에서 사전에 약속된 데이터의 흐름만 보장 받는다면, 최상위 컴포넌트에서 데이터를 받아 하위 컴포넌트에 데이터를 내릴 수 있습니다. 이렇게 관심사를 분리할 경우 비즈니스 요구사항의 변화에도 안정적인 애플리케이션을 설계할 수 있습니다. 또한 테스트측면에서도 데이터 관련 비즈니스 로직과 UI 테스트를 독립적으로 실행할 수 있어 개발자 경험 또한 향상됩니다.

위 그림은 비즈니스 관점에서 관심사를 또 다시 나눈 예시입니다. 애플리케이션이 복잡해질 수록 API의 수는 증가합니다.

API의 응답값을 그대로 받아서 보여줄 수 있다면 가장 이상적이겠지만, 브라우저에서 접하는 인터페이스가 복잡할 수록 프론트엔드의 비즈니스 로직 또한 복잡해집니다. 즉, 대부분의 경우 API 응답값을 그대로 받아서 보여줄 수 없습니다. 백엔드에서 알아서 잘 보내주면 되지 않을까라고 생각할 수 있지만 보통 프론트엔드 개발자가 요구하는데로 다 맞춰주는 이상적인 팀은 존재하지 않습니다. 어떻게 하면 백엔드 개발자에게 받은 데이터를 잘 가공해서 컴포넌트에 전달할 수 있을까요?

 

1. MVVM 패턴 참고

Model - View Model -View 패턴입니다. 해당 아키텍처에서 각 계층은 각 맡은 책임이 다릅니다.

 

model

DAO(데이터 접근 계층)를 참조합니다. 프론트엔드 애플리케이션 관점에서 DAO는 백엔드이므로 model은 백엔드 API와 직접 관련된 코드를 작성하는 계층입니다. 

view model

Model 계층에서 받은 데이터를 View에 맞게 가공하며, 상태 관리와 이벤트 처리를 담당하는 계층입니다. View와 Model 사이의 느슨한 결합을 도와 유지보수성을 높입니다.

View

UI를 구성하는 계층으로, 사용자 인터페이스를 렌더링하고 ViewModel에서 제공하는 데이터를 구독하여 화면에 표시합니다.

 

절대적이지는 않지만 애플리케이션을 View, Business의 관심사로 나누자면 view와 view model은 비즈니스에 속합니다. MVVM패턴을 참조해 프론트엔드 애플리케이션을 설계한다면 서버 컴포넌트를 설계하는데 api 데이터로부터 자유로워질 수 있습니다.

 

2. BFF 패턴 참고 (next js server action)

Backend For Frontend 패턴은 말 그대로 프론트엔드를 위한 백엔드 서버를 두는 시스템입니다.

백엔드 API 서버에서 데이터를 보내면 BFF 서버가 데이터를 받아 View 계층에 데이터를 전달하는 구조입니다. 프론트엔드를 위해 서버를 하나 더 두는게 부담이 되지않을까 생각할 수 있습니다. 하지만 최근 서버사이드까지 지원하는 프레임워크인 Next js의 등장으로 부담이 줄어들었습니다. 특히 'use server'키워드를 통해 작성할 수 있는 서버 액션 함수의 등장으로 BFF에대한 접근성이 많이 향상되었습니다.

두번째, 컴포넌트를 분리할 수 있습니다.

View 계층을 담당하는 컴포넌트에서도 관심사의 분리를 실현할 수 있습니다. 대표적으로 아토믹 디자인 패턴이 있습니다.

컴포넌트를 원자단위로 분리해서 각 계층은 원자(atom)부터 시작해 점차 복잡한 요소로 조합되는 구조를 가집니다. 더이상 쪼개질 수 없는 원자 단위로 시작해서 각 계층은 전 계층의 컴포넌트들로 구성됩니다. 다음 계층의 컴포넌트를 디자인할 때 전 계층의 컴포넌트를 재활용할 수 있는 장점이 있습니다.

 

아토믹 디자인 패턴을 적용하면 서버 컴포넌트클라이언트 컴포넌트를 명확하게 구분하기 쉬워집니다.

서버 컴포넌트는 쿼리(Query), 클라이언트 컴포넌트는 커맨드(Command)에 적합한데, 컴포넌트를 작은 단위로 쪼개면 서버 컴포넌트의 역할을 더욱 적극적으로 활용할 수 있습니다. 서버 컴포넌트로 구성된 UI는 사용자에게 더 빠르게 렌더링됩니다. 같은 페이지라도 서버 컴포넌트로 분리할 수 있는 부분이 있다면, 해당 콘텐츠가 먼저 사용자에게 전달됩니다. 즉, 클라이언트 컴포넌트가 필요한 기능이 있더라도, 사용자가 체감하는 대기 시간을 줄여 더 쾌적한 사용자 경험을 제공할 수 있습니다.

세번째, 관심사의 분리를 나아가 클린 아키텍처를 고민할 수 있습니다.

사실 해당 부분은 서버 컴포넌트의 활용보다는 관심사의 분리 측면에서 더 고도화된 아키텍처에 대한 주제입니다. 하지만 아주 관련이 없지는 않습니다. 이렇게 관심사의 분리를 고민하기 시작하면, 자연스럽게 프론트엔드에서도 객체지향의 필요성을 느낍니다. 실제로 타입스크립트는 인터페이스와 클래스를 지원하기 때문에 추상화를 통한 관심사의 분리를 실현할 수 있습니다.

클린 아키텍처에서는 의존성의 방향이 바깥에서 안으로 향합니다. 즉, 비즈니스 로직을 담고 있는 도메인 로직이 외부 의존성에 영향을 받지 않고 독립적으로 유지됩니다. 덕분에 클린 아키텍처는 도메인 로직이 복잡한 서비스에 특히 적합합니다. 명확한 도메인 구조를 바탕으로 비즈니스 로직에 집중할 수 있어 유지보수성과 확장성이 뛰어납니다. 프론트엔드에서 클린아키텍처를 고민한다면 어떻게 나눌 수 있을까요?

 

1. 도메인 계층 (Domain Layer)

도메인 계층은 비즈니스 로직을 담당합니다. 해당 계층에서는 도메인을 객체로 구현하고, 비즈니스 규칙과 관련된 코드를 작성합니다.

 

2. 유즈 케이스 계층 (Use Case Layer)

유즈 케이스 계층은 애플리케이션 로직을 담당합니다. 해당 계층에서는 명령(Command)과 조회(Query)의 책임을 분리하는 CQRS 패턴을 적용해 외부 인프라와의 결합을 느슨하게 유지할 수 있습니다.

 

3. 인프라 계층 (Infrastructure Layer)

인프라 계층은 외부 시스템과의 연결을 담당합니다. 사용자 유즈 케이스 계층과의 연결에는 어댑터 패턴을 적용해 느슨한 결합을 유지합니다. 프론트엔드 애플리케이션에서 인프라 계층에 해당하는 요소는 바로 브라우저, API, 서드파티 라이브러리와 관련된 코드입니다.

 

https://bespoyasov.me/blog/clean-architecture-on-frontend/

 

Clean Architecture on Frontend

In this post, I describe what the clean architecture is, how to use it with JS/TS code bases and if it's even worth it.

bespoyasov.me

해당 포스팅에서는 프론트엔드에서 클린아키텍처를 어떻게 설계하면 좋을지에대한 고민을 소개합니다. 클린 아키텍처는 어댑터 패턴과 도메인 주도 개발(DDD)에 대한 이해가 선행되어야 하므로 처음 접하면 직관적으로 이해하기 어렵습니다.

또한 코드의 볼륨이 많아지고 프로젝트 구조가 복잡해지기 때문에 비즈니스 로직이 복잡한 서비스가 아니면 고민해봐야 할 아키텍처입니다.

그렇지만 소프트웨어의 변하지않는 본질인 변화에 유연한 아키텍처임은 확실합니다. 해당 포스팅에서는 도메인을 클래스가 아닌 인터페이스로 설계하고 도메인 속성에 대한 개방 폐쇄 원칙을 지키지 않습니다. 이외에도 비즈니스 로직을 독립적인 함수로 만드는 등 전통적인 클린 아키텍처와는 조금 다른 부분이 있습니다. 소프트웨어에 정답은 없으므로 전통적인 방칙을 지킬 필요가 없지만, 읽으면서 해당 방식이 더 좋은가에 대한 고민은 필요할 것 같습니다. 다음에 기회가 된다면 위 포스팅을 주제로 글을 작성해보고 싶습니다.

마치며

돌이켜보면 저는 Next js로 프로젝트를 처음 시작할 때 클라이언트 컴포넌트만 사용했습니다. 프로젝트를 구경하는 선배에게 들은 가장 기억에 남는 피드백은 "서버 컴포넌트가 너무 없는게 아쉽다."였습니다. 이후로 어떻게하면 서버 컴포넌트를 잘 활용할 수 있을까에 대해 고민했고, 이는 애플리케이션 아키텍처와 사용자 경험 개선에 대한 고민으로 이어졌습니다. 정답도 아니고 저는 아직 능숙하지도 않습니다. 하지만 제가 치열하게 고민했던 내용에대해 공유하면 Next js 시작을 망설이는 개발자에게 조금이나마 도움이 되지않을까라는 생각으로 포스팅을 작성하게 되었습니다.

 

참조

클린아키텍처 - 로버트 C 마틴

객체지향의 사실과 오해 - 조영호

https://atomicdesign.bradfrost.com/chapter-2/#the-part-and-the-whole

 

Atomic Design Methodology | Atomic Design by Brad Frost

Learn how to create and maintain digital design systems, allowing your team to roll out higher quality, more consistent UIs faster than ever before.

atomicdesign.bradfrost.com

https://bespoyasov.me/blog/clean-architecture-on-frontend/

 

Clean Architecture on Frontend

In this post, I describe what the clean architecture is, how to use it with JS/TS code bases and if it's even worth it.

bespoyasov.me