현업 프론트엔드 개발 자문

프론트엔드 개발 구조에 대해 자문받고 정리한 내용

최근에 프론트엔드 쪽을 다시 보고 있다.

React를 시작으로 Vue도 찾아보고 SPA, CSR, SSR, 상태 관리 같은 것도 같이 보다 보니 개별적으로는 대충 알겠는데 서로 연결해서 생각하려니까 조금 꼬였다.

특히 React와 Next.js의 관계가 그랬다. 둘 다 프론트엔드에서 많이 보이는데 정확히 어디까지가 React고 어디부터 Next.js인지 애매했다. SPA와 CSR도 비슷하게 느껴졌고 Redux도 이름은 많이 봤는데 왜 굳이 필요한지는 잘 와닿지 않았다.

그래서 그냥 실제로 프론트엔드 프로젝트를 하고 있는 친구한테 물어봤다.

문서만 계속 찾아보는 것보다 직접 프로젝트를 만들어본 사람이 어떤 기준으로 기술을 나누는지 들어보는 게 빠를 것 같았다.

아래 내용은 친구가 해준 말을 그대로 옮긴 건 아니다. 이야기를 듣고 나서 내가 이해한 내용을 다시 정리했다.


일단 HTML CSS JavaScript

결국 웹의 기본은 여기서 시작한다.

HTML이 구조를 만들고 CSS가 스타일을 담당한다. JavaScript는 동작을 처리하고 데이터를 가져오거나 화면을 바꾸는 데 사용한다.

너무 기본적인 내용이라 이미 알고 있다고 생각했는데 React를 다시 보면서 오히려 이 부분을 한번 짚고 넘어가는 게 도움이 됐다.

JavaScript만 가지고도 DOM을 직접 조작해서 웹 페이지를 만들 수 있다.

JavaScript -> DOM 직접 조작

작은 페이지라면 이걸로도 충분하다.

문제는 규모가 커졌을 때다.

버튼 하나 눌렀을 때 특정 DOM을 바꾸고, 다른 데이터가 바뀌면 또 다른 부분을 수정하고, 그 결과에 맞춰 다른 UI까지 바꿔야 한다.

처음에는 별거 아닌데 이런 코드가 계속 쌓이면 어느 순간부터 어디를 수정해야 하는지 찾는 것 자체가 일이 된다.

React나 Vue를 쓰는 이유도 결국 이런 문제와 연결되어 있었다.


React와 Vue

React와 Vue를 보면서 가장 먼저 이해한 건 컴포넌트라는 개념이었다.

화면을 하나의 큰 덩어리로 만들어 놓는 대신 필요한 단위로 쪼개서 관리한다.

그리고 DOM을 직접 찾아가서 수정하는 것보다는 상태가 변경됐을 때 어떤 UI가 나와야 하는지를 코드로 표현하는 쪽에 가깝다.

State -> UI

처음 React를 볼 때는 이게 별거 아닌 것처럼 보였는데 생각보다 방향 자체가 달랐다.

예전에는

“이 값이 바뀌었으니까 이 DOM을 수정해야 한다.”

라는 식으로 생각했다면 React에서는

“상태가 이 값이면 UI는 이렇게 생겨야 한다.”

에 가까웠다.

React와 Vue가 세부적으로 어떻게 다른지는 더 공부해야겠지만, 일단 이 차이를 이해하고 나니 왜 이런 라이브러리나 프레임워크가 등장했는지는 어느 정도 이해가 됐다.

그리고 React는 보통 UI 라이브러리라고 하고 Vue는 프레임워크에 가까운 것으로 설명한다.

처음에는 둘 다 그냥 프론트엔드 프레임워크라고 생각했는데, 실제로 프로젝트를 구성할 때 기본적으로 제공하는 범위에서 차이가 있다는 정도로 이해했다.


React에서 이벤트 처리

React를 쓴다고 브라우저 이벤트가 사라지는 건 당연히 아니다.

버튼을 누르면 브라우저에서 클릭 이벤트가 발생하고 키보드를 누르면 키보드 이벤트가 발생한다.

차이는 그걸 코드에서 다루는 방식이다.

JavaScript에서는 직접 addEventListener()를 붙일 수 있지만 React에서는 컴포넌트 안에서 이벤트 핸들러를 연결하고 그 결과로 상태를 변경하는 방식이 일반적이다.

Browser Event -> Event Handler -> State Update -> UI Update

결국 브라우저에서 이벤트가 발생한다는 사실 자체는 똑같다.

React가 그 위에서 UI와 상태를 관리하기 편한 형태를 제공한다고 보는 게 이해하기 쉬웠다.


React만 알면 되는 줄 알았는데

개인적으로는 이 부분에서 생각이 좀 바뀌었다.

처음 React를 공부할 때는 React 문법을 어느 정도 익히면 프론트엔드 개발도 대부분 할 수 있다고 생각했다.

그런데 실제 프로젝트 이야기를 들어보니 React 자체는 전체에서 보면 한 부분이었다.

프로젝트를 만들다 보면 라우팅이 필요하고 서버에서 데이터를 받아와야 한다. 상태를 관리해야 하고 API 요청도 해야 한다. TypeScript를 사용한다면 타입도 관리해야 하고 CSS나 UI 컴포넌트 문제도 따로 생긴다.

대충 보면 이런 것들이 붙는다.

HTML / CSS / JavaScript -> React -> Routing / State / API / TypeScript / UI / Performance

물론 모든 프로젝트에서 전부 사용하는 건 아니다.

필요한 걸 가져다 쓰는 것이다.

예를 들어 전역에서 공유해야 하는 상태가 많으면 Redux 같은 상태 관리 도구를 사용할 수 있고, 서버에서 받아온 데이터를 관리하는 데는 React Query나 SWR 같은 도구를 사용할 수 있다.

HTTP 요청도 마찬가지다.

그냥 fetch()를 써도 되고 Axios 같은 라이브러리를 사용할 수도 있다.

이런 걸 보다 보니 React 문법보다 오히려 주변 기술을 알아야 할 게 더 많겠다는 생각이 들었다.


SPA는 React가 아니다

이건 처음에 꽤 헷갈렸던 부분이다.

SPA를 처음 봤을 때는 React 같은 기술의 한 종류인 줄 알았다.

그런데 SPA는 Single Page Application의 약자고 특정 라이브러리를 말하는 게 아니었다.

웹 애플리케이션을 어떤 방식으로 구성하느냐에 대한 개념에 가깝다.

기존 웹에서는 페이지를 이동하면 서버에 요청하고 서버가 새로운 HTML을 보내주는 방식이 일반적이었다.

Browser -> Server -> HTML -> Browser

SPA에서는 처음에 HTML과 JavaScript를 받은 다음 브라우저에서 JavaScript가 화면 전환을 처리할 수 있다.

Browser -> HTML + JavaScript -> Application -> 화면 변경

그래서 주소가 바뀌더라도 매번 완전히 새로운 HTML 페이지를 받는 게 아니라 현재 실행 중인 애플리케이션에서 필요한 UI만 바꾸는 방식이 가능하다.

여기서 React를 사용한다고 무조건 SPA가 되는 건 아니다.

React는 UI를 만드는 기술이고 SPA는 애플리케이션을 구성하는 방식에 대한 개념이다.

이걸 분리해서 생각하니까 CSR과 SSR도 조금 덜 헷갈렸다.


CSR과 SSR

CSR은 Client Side Rendering이고 SSR은 Server Side Rendering이다.

CSR에서는 브라우저에서 JavaScript를 실행하면서 UI를 구성한다.

Browser -> HTML -> JavaScript -> React -> UI

SSR은 서버에서 HTML을 만들어서 브라우저로 전달하는 방식이다.

Browser -> Server -> Data -> HTML -> Browser

처음에는 SSR이 CSR보다 발전된 방식인 줄 알았다.

그런데 꼭 그런 건 아니었다.

어떤 방식이 더 좋은지가 아니라 서비스에 따라 선택하는 문제였다.

초기 화면을 어떻게 보여줄지, 검색 엔진에 어떤 식으로 노출되어야 하는지, 서버에서 어느 정도 처리를 할지 같은 조건에 따라 달라진다.

그래서 CSR과 SSR을 놓고 어느 하나가 무조건 우위에 있다고 생각하면 안 된다고 했다.


Next.js는 React의 다른 이름이 아니었다

Next.js도 처음에는 좀 애매했다.

React에 SSR 기능을 붙여주는 도구 정도로 생각하고 있었다.

실제로 SSR도 지원하지만 그것만 하는 건 아니다.

React를 기반으로 웹 애플리케이션을 만들 때 필요한 여러 기능을 하나의 프레임워크 안에서 제공한다.

라우팅이나 서버 기능, 렌더링 방식, 빌드와 최적화 같은 부분도 같이 다룬다.

그래서 단순히

React + SSR = Next.js

라고 생각하면 부족하다.

좀 더 정확하게는 React를 기반으로 웹 애플리케이션을 구성하기 위한 프레임워크라고 보는 게 맞을 것 같다.

이 부분은 React와 Next.js를 실제로 같이 사용해보면 더 명확해질 것 같다.


서버에서 데이터를 가져오는 것도 생각보다 복잡하다

프론트엔드에서는 서버와 데이터를 주고받는 일이 많다.

사용자 정보를 하나 가져온다고 하면 처음에는 그냥 이렇게 생각했다.

React -> HTTP Request -> Server -> Response -> UI

그런데 실제로는 여기서 끝나지 않는다.

요청하는 동안 로딩을 보여줘야 할 수도 있고 실패했을 때 처리도 해야 한다. 받은 데이터를 얼마나 오래 사용할지도 생각해야 하고 다시 요청해야 할 수도 있다.

캐싱도 필요할 수 있다.

Request -> Loading -> Success / Error
                       |
                       -> Cache / Refetch / Retry

그래서 서버에서 가져온 데이터와 컴포넌트 내부에서 사용하는 상태를 굳이 같은 방식으로 관리하지 않는 경우가 많다고 한다.

React Query나 SWR을 사용하는 이유도 여기에서 이해할 수 있었다.

예전에는 그냥 데이터를 변수에 넣으면 되는 거 아닌가 싶었는데, 서버 데이터는 데이터 자체보다 그 데이터를 어떻게 가져오고 유지할지가 더 귀찮은 문제였다.


Axios REST GraphQL은 같은 종류가 아니다

이것도 처음에는 전부 API 통신과 관련된 기술이라 한꺼번에 묶어놨다.

그런데 역할이 다르다.

Axios는 HTTP 요청을 보내기 위한 클라이언트 라이브러리다.

REST는 API를 설계하는 방식이고 GraphQL은 클라이언트가 필요한 데이터를 질의할 수 있도록 만든 API 방식이다.

그래서 예를 들어 REST API를 사용한다면 다음과 같이 구성할 수 있다.

React -> React Query -> Axios -> REST API -> Server

GraphQL을 사용한다면 중간에 GraphQL Client 등을 둘 수도 있다.

React -> GraphQL Client -> GraphQL API -> Server

결국 Axios와 React Query를 같은 역할이라고 보면 안 된다.

이런 식으로 기술 이름만 외우는 게 아니라 각각 어느 문제를 해결하는지를 보는 게 중요하다는 생각이 들었다.


Redux는 모든 상태를 넣는 곳이 아니다

Redux는 여러 컴포넌트에서 공유해야 하는 상태를 관리할 때 사용하는 도구다.

예를 들어 여러 화면에서 같은 상태를 사용한다면 중앙에서 관리할 필요가 생길 수 있다.

Component A / Component B / Component C -> Store

그런데 이것도 모든 상태를 Redux에 넣는다는 의미는 아니다.

컴포넌트 안에서만 사용하는 값도 있고 여러 컴포넌트가 같이 사용하는 값도 있다.

서버에서 받아온 데이터는 또 성격이 다르다.

Local State -> Component 내부에서 사용하는 상태
Global State -> 여러 Component가 공유하는 상태
Server State -> Server에서 가져온 상태

특히 서버 상태는 캐싱이나 재요청 같은 문제가 있어서 React Query나 SWR처럼 서버 상태 자체를 관리하기 위한 도구를 사용하는 경우가 있다.

그래서 Redux를 하나의 거대한 저장소처럼 생각하기보다는 “이 상태가 어떤 종류인가?”부터 판단하는 게 맞는 것 같다.


TypeScript

TypeScript는 JavaScript에 정적 타입 시스템을 추가한 언어다.

React와 같이 사용하는 경우가 많고 Props나 API 응답처럼 구조가 정해진 데이터를 타입으로 표현할 수 있다.

React + TypeScript

처음에는 JavaScript에 타입 문법이 조금 추가된 정도라고 생각했다.

그런데 프로젝트가 커지면 이야기가 달라진다.

특히 다른 사람이 만든 코드나 API 데이터를 다루다 보면 어떤 값이 들어오는지 코드만 보고 확실하지 않은 경우가 생긴다.

이런 부분을 타입으로 명확하게 만들어두면 개발하면서 확인해야 할 정보가 줄어든다.

물론 TypeScript를 쓴다고 모든 문제가 해결되는 건 아니지만 규모가 커질수록 타입 정보가 있는 쪽이 관리하기 편하다는 건 이해할 수 있었다.


CSS는 React가 담당하는 게 아니다

React를 공부하다 보면 이것도 은근히 헷갈린다.

React가 UI를 만든다고 해서 스타일링까지 해주는 건 아니다.

React는 UI 구조와 상태 변화 쪽이고 CSS는 스타일을 담당한다.

필요하면 CSS를 직접 작성할 수도 있고 Tailwind CSS나 Bootstrap 같은 도구를 사용할 수도 있다.

UI 컴포넌트가 필요하면 별도의 UI 라이브러리를 사용할 수도 있다.

React -> UI 구조
CSS -> Style
CSS Framework -> Styling
UI Library -> Component

애니메이션도 마찬가지다.

CSS로 처리할 수도 있고 필요한 경우 별도의 라이브러리를 사용할 수도 있다.

결국 프론트엔드라는 이름 안에 생각보다 여러 분야가 섞여 있었다.


성능은 일단 측정부터

React에서 상태가 변경되면 렌더링이 다시 발생한다.

State Update -> Render -> Reconciliation -> DOM Update

그렇다고 렌더링이 발생하는 것 자체를 성능 문제로 보면 안 된다.

실제로 느린 부분이 있는지를 먼저 봐야 한다.

큰 리스트를 한꺼번에 그리거나, 컴포넌트가 너무 복잡하거나, 입력 이벤트가 지나치게 자주 발생하거나, 이미지가 큰 경우처럼 실제 병목이 생기는 지점은 다양하다.

그에 따라 메모이제이션이나 코드 스플리팅, Lazy Loading, Virtualization, 이미지 최적화 등을 사용할 수 있다.

입력 이벤트라면 Debouncing이나 Throttling이 필요할 수도 있다.

Measure -> Bottleneck 확인 -> 원인 파악 -> 필요한 최적화 적용

이 부분은 다른 개발에서도 비슷하다고 생각한다.

최적화 기법을 알고 있다는 이유만으로 먼저 적용할 필요는 없다. 실제로 느린 부분이 어디인지부터 확인하는 게 순서다.


JSP와 비교해보니

JSP를 어느 정도 알고 있어서 비교하니까 이쪽은 이해가 빨랐다.

JSP는 서버에서 HTML을 만들어서 브라우저에 전달하는 방식으로 볼 수 있다.

Browser -> Java Server -> JSP -> HTML -> Browser

React SPA에서는 브라우저에서 JavaScript가 실행되고 React가 UI를 구성한다.

Browser -> HTML + JavaScript -> React -> UI

Next.js는 React를 기반으로 하면서 서버에서 렌더링하는 방식도 사용할 수 있다.

Browser -> Next.js Server -> React -> HTML -> Browser

물론 실제 동작은 이것보다 복잡하지만 큰 방향만 놓고 보면 HTML을 어디에서 만들고 어떤 부분을 브라우저에서 처리하는지를 비교해보는 게 이해하기 편했다.

예전에 JSP를 보면서 서버에서 HTML을 만들어 보내는 구조를 익혔기 때문에 SSR이라는 개념도 완전히 새로운 느낌은 아니었다.


자문을 받고 나서

이번에 자문을 받고 나서 가장 크게 달라진 건 각각의 기술을 보는 기준이었다.

처음에는 React, Vue, Next.js를 전부 비슷한 종류의 기술이라고 생각했다.

그런데 하나씩 역할을 나눠보니까 애초에 서로 같은 자리에 있는 기술이 아니었다.

React는 UI를 만드는 쪽이고 Redux는 상태 관리에 사용한다. React Query나 SWR은 서버 상태를 관리하는 데 사용할 수 있다.

Axios는 HTTP Client고 REST와 GraphQL은 API를 구성하는 방식이다.

SPA는 애플리케이션 구조에 대한 개념이고 CSR과 SSR은 렌더링 방식이다.

Next.js는 React를 기반으로 웹 애플리케이션을 만들기 위한 프레임워크다.

이 정도로 나눠놓으니 처음보다 훨씬 덜 복잡했다.

전체를 아주 단순하게 그려보면 이런 느낌이다.

Browser -> HTML / CSS / JavaScript -> React -> UI / State -> API -> Server
                                      |
                                      -> Routing / Server State / TypeScript / UI Library

실제 프로젝트가 이렇게 딱 떨어지는 구조로 만들어지는 건 아니다.

어떤 프로젝트는 React만 사용하기도 하고, 어떤 프로젝트는 Next.js를 사용한다. 상태 관리도 Redux가 필요한 경우가 있고 굳이 필요하지 않은 경우도 있을 것이다.

결국 기술 이름 자체보다 어떤 문제를 해결하기 위해 사용하는지를 보는 게 중요하다는 생각이 들었다.

아직 프론트엔드 프로젝트를 직접 깊게 만들어본 단계는 아니라서 실제로 사용해보면 지금 생각과 달라지는 부분도 있을 것 같다.

그래도 적어도 지금까지 따로따로 보고 있던 React, SPA, CSR, SSR, 상태 관리, API 같은 것들이 어느 위치에서 연결되는지는 어느 정도 잡힌 것 같다.

일단 지금은 이 정도로 정리하고 넘어가려고 한다.