본문 바로가기

개발자 생활

요즘 웹 개발 1편 — 경계가 나뉘던 시절과, 한 덩어리로 묶는 요즘

반응형

부제: 예전엔 SPA+Vite+Spring, 지금은 Next를 만지며

요즘 웹 쪽 트렌드가 궁금해서, 모던 웹 기술 스택을 공부하려고 작은 풀스택 베이스를 깎고 있다. Next.js를 한 번 해보고 싶어서 시작했는데, 막상 보니 스택이 꽤 밀려 있다는 느낌이 들었다. JS는 예전에 Express.js 정도만 떠올랐고, 최근에는 거의 자바만 해 왔다.

이번 시리즈는 회사에서 겪었던 조합지금 손에 익히는 조합을 겹쳐 보면서 “왜 이렇게 잡았지?”를 남기려 한다. 1편은 앱 경계 — 분리된 구조와 한 덩어리 구조의 차이부터 시작한다.

용어

  • CRA (Create React App): 옛날 React 보일러. webpack으로 개발 서버·빌드를 한 번에 잡는 그림.
  • Vite: 개발은 가볍게, 빌드는 보통 Rollup 쪽으로 묶는 차세대 번들러에 가깝다.
  • pnpm: 패키지를 디스크에 한 번 받고 링크로 재사용하려는 패키지 매니저.
  • Next.js: React에 라우팅·서버에서 돌릴 코드 자리까지 얹은 풀스택 프레임워크. (한 프로젝트 안에 프론트와 서버가 같이 들어오기 쉽다 — 한 건물에서 주방과 홀이 함께 있는 그림에 가깝다.)
  • MSA (Micro Service Architecture): 기능을 작은 서비스로 나눠 독립 배포하려는 아키텍처 방향.

1) 회사에서는 CRA + npm에서 Vite + pnpm으로 갈아탔다

대표 이미지 (앱 경계: '분리와 한 덩어리')

앱이 커질수록 빌드가 느려졌다. 당연한 면도 있다. 패키지 설치에도 시간이 많이 걸렸다. 그래서 시간을 줄이려고 도구를 바꿨다.

“그래서 왜 빨라진 건데?”까지는… 속까지는 잘 모르겠다. 체감만 “빨라졌다”다.

Vite는 개발 때 ESM (ECMAScript Modules, 자바스크립트 표준 모듈 시스템) 기반으로 가볍게 돌린다는 설명이 흔하고, pnpm은 패키지를 한 번만 받아 링크로 재사용해 설치를 빠르게 한다고 알려져 있다. 나는 시간이 줄었다는 결과만 확실히 기억한다. 그리고 SSR (Server Side Rendering)은 쓰지 않았다. 계속 SPA (Single Page Application)였다.

잠깐 개념: Vite가 빠른 이유

CRA는 개발 서버를 시작하기 전에 전체 소스를 한 번 번들링한다. 앱이 커질수록 cold start가 수십 초로 늘어나는 이유다.

Vite는 다르다. 의존성(node_modules)만 esbuild로 미리 묶고, 소스 파일은 브라우저가 요청하는 순간에만 ESM으로 변환해 넘긴다. 앱 크기와 무관하게 시작 시간이 짧게 유지된다. HMR (Hot Module Replacement)도 변경된 모듈만 교체해서 빠르다.

잠깐 개념: pnpm이 빠른 이유

npm·yarn은 패키지를 설치할 때 프로젝트 node_modules에 실제 파일을 복사한다. 같은 패키지가 프로젝트마다 중복 저장된다.

pnpm은 패키지를 전역 저장소에 한 번만 받아 두고, 각 프로젝트의 node_modules는 그 파일을 가리키는 링크(hardlink)만 만든다. 복사할 파일이 없으니 설치가 빠르고 디스크도 덜 쓴다.

잠깐 개념: SPA와 SSR

  • SPA: 첫 로딩 뒤 화면 전환을 브라우저 안 JS가 많이 담당한다.
  • SSR: 서버가 HTML을 만들어 내려주고 그 위에 JS가 붙는 그림. SEO (Search Engine Optimization, 검색 엔진 최적화)·초기 로딩 이야기와 자주 같이 나온다.

2) 프론트와 백은 갈랐고, MSA를 지향했다

React 쪽과 Spring Boot API는 레포도 따로, 런타임도 따로였다. 팀은 MSA를 지향했고, 서비스 경계가 당연하게 머릿속에 있었다.

프론트와 백이 갈려 있으면 배포 타이밍도 따로 돌아간다. API 스펙이 바뀌면 프론트가 맞춰서 따라가야 하고, 그 사이 타입이 어긋나는 건 사람이 잡아야 했다. 공유 타입 같은 건 없었고, Swagger 문서를 보면서 수동으로 맞추는 식이었다.

잠깐 개념: MSA가 프론트에 주는 그림

서비스가 나뉘면 HTTP+JSON이 통로가 된다. 프론트는 여러 서비스 중 하나의 클라이언트에 불과하다. 독립 배포가 가능해지는 대신, 서비스 간 계약을 맞추는 비용이 생긴다. 이 장단은 뒤 편들에서 더 이어진다.


3) 지금 손에 익히는 쪽은 Next + React + pnpm, 한 레포에 가깝다

'CRA에서 Vite로' 속도 전환 장면

지금 만지고 있는 학습용 레포는 Next.js + React + pnpm 조합이다. 스크립트는 next dev / next build로 단순하다.

회사에서는 프론트·백이 항상 갈려 있었는데, 한 레포에 프론트·서버 성격이 같이 들어가는 형태는 나한테는 이번이 처음이다.

잠깐 개념: Next.js가 “한 덩어리”인 이유

Next는 같은 프로젝트 안에서 UI와 서버 쪽 코드를 한 toolchain으로 다루기 쉽게 설계됐다. 타입·규약이 레포 안에서 이어지기 좋다는 게 내가 기대하는 지점이다.


4) 거친 비교 한 장

왼쪽이 회사에서 겪었던 구조다. React SPA와 Spring Boot API가 HTTP+JSON으로 이어져 있고, 레포도 빌드도 따로였다. 오른쪽이 지금 손에 익히는 구조다. Next.js 한 레포 안에 UI와 서버 코드가 같이 들어간다.

다이어그램


5) 트레이드오프 한 줄

'한 레포에 프론트·서버가 함께' 디테일 컷

어느 쪽이 낫다는 이야기가 아니다. MSA·분리 구조는 조직이 크고 서비스별 독립 배포가 필요할 때 힘을 발휘하지만, 서비스 간 통신이나 배포 파이프라인이 늘어나는 비용도 따른다. 반대로 한 덩어리 구조는 타입과 규약이 레포 안에서 이어지기 쉬운 대신, 규모가 커지면 그 안에서 복잡도가 쌓인다.

지금은 학습 목적이니, 한 레포에서 흐름이 끊기지 않는 단순함이 더 맞는다고 판단했다.


경계를 잡았으니, 다음은 그 경계를 넘나드는 상태 관리와 API 계약 이야기다. 2편에서는 회사에서 썼던 MobX-State-Tree·REST·any 조합과, 지금 손에 익히는 TanStack Query / tRPC / Zod 조합을 겹쳐 본다.


참고/더 읽기

반응형