모던 프론트엔드 개발 워크플로우
2026년 7월 10일
현대의 프론트엔드 개발 워크 플로우는 어떻게 될까. 개발의 흐름을 이해해보자.
프론트엔드 개발은 ① 린트 → ② 컴파일(빌드) → ③ 런타임의 3단계를 거친다. 핵심 철학은 "에러를 최대한 앞 단계에서 잡는 것" 그리고 "네트워크 왕복(핑퐁)을 최대한 줄이는 것".
0. 에러를 잡는 위치가 중요한 이유
린트 ──────── 컴파일 ──────── 런타임
←── 에러 찾기 쉬움 에러 찾기 어려움 ──→
- 오른쪽(런타임)으로 갈수록 에러를 발견하기 어렵다.
- 왼쪽(린트)으로 갈수록 에러를 찾기 쉽다.
- 예: 기능이 100가지(테스트 케이스 100개)면, 런타임에서 검증하려면 100개를 일일이 실행해봐야 한다. 코드 수정할 때마다 100개를 다 테스트하는 건 매우 번거롭다.
- 코드 작성 단계에서 미리 알 수 있다면 에러를 훨씬 수월하게 잡는다.
- TypeScript를 통해 린트·컴파일 단계에서 에러 체킹을 하면 에러가 약 90% 줄어든다.
자바 비유: Expected Error(예상한 에러) vs Unexpected Error(예측 못 하는 에러). 언익스펙티드 에러는 실행해봐야 발생 여부를 알 수 있어 해결이 어렵다. 이걸 앞 단계에서 잡자는 게 핵심. (프론트엔드만의 얘기가 아니라 모든 언어 공통)
1. 린트(Lint) 단계
코드를 실행하지 않고 정적 분석(static analysis) 으로 문제를 잡는 단계.
1-1. 린터 (Linter) — ESLint
- 코드를 정적 분석 단계에서 검사해 에러가 있을지 없을지 체크한다.
- ESLint를 설치하는 이유가 바로 이것.
1-2. 포매터 (Formatter) — Prettier
-
코드 스타일을 팀 규격에 맞게 자동 정렬해주는 도구.
-
코드 리뷰 때 흔히 다투는 것들을 자동화: 들여쓰기(인덴트), 줄바꿈, 따옴표(큰따옴표 vs 작은따옴표),
if문 중괄호 위치 등. -
저장하는 단계에서 무조건 팀 규격에 맞게 수정해준다.
-
트레일링 콤마(Trailing Comma): 객체/배열 마지막 요소 뒤에 붙는 콤마. 넣을지 말지도 Prettier로 결정.
const obj = { a: 1, b: 2, // ← 트레일링 콤마 } -
(참고) 백엔드 쪽은 이런 포맷팅을 지원하는 포매터가 별로 없는 걸로 알려져 있고, 프론트엔드는 Prettier로 가능.
1-3. ESLint와 Prettier의 역할 분리 ⭐
- 원래 ESLint도 포맷팅 기능을 지원했었다.
- 그런데 작년(2024년) 12월경, ESLint가 "포맷팅 기능은 아예 제공하지 않겠다. 포맷팅이 필요하면 Prettier를 써라. 우리는 린터 역할만 하겠다"며 역할을 완전히 분리했다.
- 결론: ESLint = 린터, Prettier = 포매터. 이 구분을 확실히 잡고 가자.
2. TypeScript (린트 단계에서도 동작)
2-1. 강타입 vs 약타입
| 구분 | 강타입(Strong Type) | 약타입(Weak Type) |
|---|---|---|
| 대표 언어 | Java | JavaScript |
| 타입 결정 시점 | 컴파일 시 | 런타임 시 |
| 특징 | 변수마다 타입 지정, 타입 안 맞으면 에러 | 타입이 없는 건 아니지만 실행할 때 결정됨 |
- Java는 컴파일러로 고수준 언어 → 기계어로 변환하며, 컴파일 단계에서 문법·타입 이상 여부를 확인한다.
- JavaScript는 처음부터 타입이 결정되는 게 아니라 실제 실행(런타임)할 때 타입이 결정된다.
2-2. 암시적 형변환 (Implicit Type Conversion)
"1" + 3 // 결과: "13" (문자열!)
- 문자열 + 숫자 → 숫자가 문자열로 암시적 형변환되어
"13"(문자열)이 나온다. - 의도치 않은 결과가 나올 수 있다.
- 이건 에러가 아니라 버그다. (실행은 진짜로 된다 → 그래서 더 위험)
- 이런 예기치 않은 버그를 런타임보다 앞 단계에서 잡고 싶어서 TypeScript를 사용한다.
2-3. TypeScript 패키지의 두 가지 구성 ⭐
npm install typescript 하면 패키지 안에 두 가지가 들어 있다:
| 구성 | 약자 | 역할 |
|---|---|---|
| TSC | TypeScript Compiler | 컴파일러 (TS → JS 변환) |
| TS Server | TypeScript Server | 백그라운드 실행, 린트 단계에서 타입 체킹 |
- TS Server가 백그라운드에서 실행되며 VSCode / Cursor와 연계해, 작성한 코드에 에러가 있는지 실시간으로 체크한다.
- 코드 편집기에 뜨는 빨간 밑줄이 바로 TS Server가 코드를 실행하지 않고 정적 분석으로 타입 이슈를 파악해 그어주는 것.
- (참고) Java·Kotlin에서 편집기에 밑줄이 그어지는 것도 컴파일러가 아니라 뒤에서 도는 체커 서버가 실시간으로 해주는 것. → 컴파일 단계와 린트 단계는 엄밀히 다르다.
TypeScript도 컴파일러로 JS로 변환하는 게 맞지만, 린트 단계에서도 동작한다는 점이 핵심.
3. 컴파일(빌드) 단계
사용 도구: 트랜스파일러(Transpiler) + 번들러(Bundler) (이 외에도 용어가 많지만 대표 두 가지)
⭐ 미리 알아둘 핵심(6장에서 상세): 두 도구는 동등한 병렬 관계가 아니다. 실무 구조에서는 번들러가 빌드 전체를 지휘하는 "오케스트레이터" 이고, 번들러가 파일마다 트랜스파일러를 불러서 변환을 시킨다.
3-1. 컴파일러 vs 트랜스파일러
- 컴파일러(Compiler): 보통 고수준 언어 → 저수준 언어(기계어)로 변환. 단, 넓게 보면 고수준 → 다른 고수준 언어 변환도 컴파일러라 부른다.
- 트랜스파일러(Transpiler): 컴파일러의 일종. 고수준 언어 → 다른 고수준 언어 변환만을 콕 집어 지칭하는 말.
- 예: TypeScript → JavaScript 변환 = 트랜스파일러
3-2. 왜 변환이 필요한가? (브라우저가 못 알아듣는 것들)
프론트엔드 코드에는 보통 이런 게 섞여 있다:
- 최신 JavaScript 문법
- React의 JSX
- TypeScript
→ 이 코드를 그대로 브라우저에서 실행하면 안 된다. 에러가 나는 지점:
| 항목 | 문제 | 이유 |
|---|---|---|
| TypeScript 타입 정의 | 에러 | 브라우저가 TS를 인식 못 함 |
| 최신 JS 문법 (예: 배열 메서드) | 구형 브라우저에서 동작 안 함 | 예전 브라우저가 모름 |
| JSX | 인식 못 함 | HTML이 아니며, JS 엔진은 HTML/JSX를 모름 |
실제로 우아한형제들·라인 등에서도 최신 JS 문법 쓰다가 구형 브라우저에서 장애 난 사례가 종종 있었다.
→ 빌드 과정을 통해 브라우저가 이해하는 언어로 변환한다. (Java가 .java/Kotlin을 JVM이 이해하는 형태로 빌드하는 것과 동일한 개념)
3-3. 타겟 브라우저 지정 — Browserslist
-
무작정 변환하면 안 되고, 어떤 브라우저까지 지원할지 컴파일러에 알려줘야 한다.
-
Browserslist 문법으로 최소 지원 브라우저를 지정.
Chrome 64 Edge 79 Firefox 67 Opera 51 Safari 12 -
그러면 트랜스파일러가 해당 문법에 맞게 변환해준다.
3-4. 변환 결과 — 3가지 처리 방식 ⭐
① TypeScript 타입 → 그냥 제거(떼어내기)만 하면 됨
- 타입은 JS 실행에 영향을 주지 않으므로, 컴파일 시 타입만 제거하면 브라우저에서 실행된다.
"타입 영역 vs 값(값/런타임) 영역" 구분 ⭐
- 타입 영역: 타입 관련 코드
- 값 영역: 런타임에서 사용하는 코드
- 옛날 TypeScript: 타입 영역 + 값 영역 둘 다 제공했다.
- 예: 과거 JS에 옵셔널 체이닝(
?.)이 없을 때, TS가 옵셔널 체이닝을 제공하고 컴파일 시 JS 문법으로 변환해줬다.- 지금 TypeScript: 기조 변경 → "값 영역은 건드리지 말자. 값 영역은 JS만, TS는 타입 영역만 건드리자."
- 그래서 지금
infer등 TS가 제공하는 키워드는 다 타입 영역 관련 코드.- 특징: 컴파일 시 그냥 제거되면 끝. (그래서 "타입은 제거만 하면 된다")
- (옛 표현) "TypeScript는 JavaScript의 슈퍼셋이다" → 더 깊이 들어가면 타입 영역/값 영역 구분이 필요.
② 최신 JS 문법 → 폴리필(Polyfill) 삽입
- 최신 문법이라 구버전에서 지원하는 문법으로 변환할 수가 없는 경우.
- 폴리필: 구현 코드(작은 조각)를 JS 파일 안에 직접 넣어서 구형 브라우저에서도 실행되게 만드는 코드.
③ JSX → JavaScript 문법으로 변환
- JSX는 HTML이 아니다. JS로 변환되어야 한다.
- 역사:
- React 1~2 버전 시절엔
React.createElement(...)식으로 직접 작성 → 어떤 HTML로 그려질지 눈에 잘 안 들어옴(가독성 ↓). - Meta 개발자들이 고민 끝에 HTML과 유사한 선언형 UI 문법(JSX) 을 만들고, 컴파일 단계에서 JSX → JS로 변환하는 컴파일러를 제공.
- React 1~2 버전 시절엔
- 옛날엔 비표준이었지만 지금은 거의 표준처럼 사용됨.
- React뿐 아니라 Vue, Svelte에서도 JSX를 (또는 유사 컴파일 문법을) 이용해 UI를 정의. 단, 컴파일러에 의해 감싸지는 바깥 함수는 프레임워크/라이브러리마다 다르다.
4. 번들링(Bundling)
4-1. 왜 번들링을 하는가? — 네트워크 왕복 줄이기
컴퓨터 각 모듈 속도 (빠른 순):
CPU > 메모리 > 디스크 IO > 네트워크 IO
→ 프론트엔드 개발자는 특히 네트워크 IO에 집중해야 한다.
문제 상황 (HTML 로딩 과정):
- 브라우저가 HTML 요청 → 응답 받음
- HTML을 한 줄씩 읽다가 JS, CSS, 이미지 등을 추가 요청
- → 서버와 여러 번 왔다 갔다(핑퐁) 함
비유: 부산에 물 100병 가지러 가는데, 기왕 내려간 김에 최대한 많이 가져오는 게 이득. 핑퐁 횟수를 줄이는 게 네트워크 애플리케이션 입장에서 이득.
HTTP/1.1 한계: 동시 요청 가능한 커넥션이 6~8개뿐. → 리소스가 100개면 8개씩 잡아도 약 12~13번 왕복해야 함. 너무 번거롭다.
→ 한 번 요청할 때 최대한 다 가져오자 = 번들링
4-2. 번들링이란
- 클린 코드는 함수/클래스 단위로 파일을 잘게 쪼개라고 하지만 → 네트워크 입장에선 안 좋다.
- 쪼개진 JS, CSS, 이미지 등 리소스를 하나로 합쳐 한 번의 커넥션으로 가져오게 만드는 것.
4-3. 이미지·파일은 어떻게 합치나? — Base64 인코딩
- JS는 복사-붙여넣기로 합치면 되고, CSS도 가능. 그런데 이미지·파일은?
- Base64 인코딩 방식으로 파일/이미지를 문자열로 변환해서 JS 파일 안에 넣는다.
- (참고) 멀티파트 파일 업로드 구현 시에도 파일을 그대로가 아니라 문자열로 변환해 처리. DB 저장 시에도 Base64로 넣고 리치 텍스트로 가져오는 식으로 활용.
5. 최적화 (빌드 시 함께 수행)
5-1. 맹글링(Mangling) — 변수/함수명 단축
왜? 웹사이트는 보안에 취약하다.
- 앱은 코드를 까보기 어렵지만, 브라우저는 개발자 도구로 JS·HTML·CSS 코드를 다 볼 수 있다.
- 코드를 친절하게(주석, 이해하기 쉬운 함수명) 짤수록 보안에 취약 → 해커가 리버스 엔지니어링으로 취약점을 찾기 쉬워짐.
또 다른 이유 — 용량:
- 문자 한 글자의 크기:
- ASCII 코드: 0~127 숫자로 문자 표현, 1바이트 (2⁷)
- 하지만 보통 유니코드 사용(한글 변수명 등 가능) → 글자 하나가 2바이트
- 클린 코드로 변수명·함수명을 길게 하면 클린 코드엔 좋지만 네트워크 용량이 커진다.
→ 맹글링: 변수/함수명을 짧게 줄여서 ⓐ 보안 취약점 ⓑ 용량 문제를 동시에 해결.
5-2. 압축(Compress) — 공백/탭 제거
- 스페이스조차 2바이트.
- 탭 하나 = 보통 스페이스 2개 또는 4개 = 4~8바이트.
- 들여쓰기·줄바꿈용 공백/탭을 제거 → 용량 절감.
- 맹글링 + 압축을 함께 적용하면 우리가 실제 네이버 등에서 보는 빽빽하게 줄여진 코드가 된다.
5-3. 트리 쉐이킹(Tree Shaking) — 안 쓰는 import 제거
- import 해놓고 실제로 안 쓰는 파일들은 번들링되면 용량만 커진다.
- "나무 흔들기": 나무를 흔들어 썩은 열매(안 쓰는 코드) 를 떨어뜨리는 것에 비유.
- 실선 = import 후 실제 사용 / 점선 = import 했지만 미사용 → 점선을 제거.
5-4. 코드 스플리팅(Code Splitting) — 다시 분리
번들링이 무조건 좋은 건 아니다:
- JS 100개가 1MB인데 이미지 1개가 100MB라면 → 번들하면 101MB. 그 이미지는 나중에 불러와도 되는데 굳이 묶을 필요 없다.
- 네이버처럼 서비스가 많은 경우(메일·카페·블로그·쇼핑·뉴스): 메일만 쓸 건데 카페·블로그·쇼핑 코드까지 다 들고 오면 너무 무겁다.
→ 코드 스플리팅: 번들링하되 URL 경로 기준으로 다시 분리. 해당 지면에서만 필요한 JS만 묶어서 요청하도록 나눈다.
네트워크의 핵심 고민: "왔다 갔다(요청)를 얼마나 줄일까." 프론트는 이걸 극한으로 고민해야 웹 애플리케이션이 좋아지고, 취업·면접에서 최적화 경험을 정량적으로 어필할 수 있다. (서버 개발자도 운영체제·네트워크 관점의 최적화 경험을 설명할 수 있어야 함)
6. 빌드 파이프라인 — 누가 무엇을 실행하나 (도구 총정리) ⭐ [보강]
3·4·5장(트랜스파일 / 번들링 / 최적화)은 전부 "빌드" 안에서 일어난다. 그런데 이걸 누가 하느냐? 하나의 도구가 다 하는 게 아니라 번들러가 지휘자(오케스트레이터) 가 되어 나머지를 불러 쓴다.
6-1. 역할 분담 — 누가 무엇을
| 단계 | 담당 | 무슨 일을 / 어떻게 |
|---|---|---|
| ① 트랜스파일 | 트랜스파일러(컴파일러) | 번들러가 파일마다 호출해서 시킴. 호출 통로 = 로더(loader, Webpack) / 플러그인(plugin, Vite·Rollup). TS 타입 제거, JSX→JS, 최신 문법 변환, 폴리필 주입이 전부 여기서. |
| ② 번들링 | 번들러 본체 | 모듈 합치기, import 의존성 해석, 이미지·파일 Base64 인라인, 청크 생성 |
| ③ 최적화 - 트리쉐이킹·코드스플리팅 | 번들러 본체 | 의존성 그래프(import 관계)를 번들러만 알고 있으므로 번들러가 직접 수행 |
| ③ 최적화 - 맹글링·압축(minify) | 압축기(minifier) | 번들러가 별도 도구에 위임 |
한 줄 요약: 번들러 = 지휘자(번들링·트리쉐이킹·코드스플리팅 직접 + 트랜스파일·압축은 외주).
┌─────────── 번들러 (지휘자) ───────────┐
│ ① 파일마다 트랜스파일러 호출 (로더/플러그인)│
│ ② 모듈 합치기 = 번들링 (본체 작업) │
│ ③ 트리쉐이킹·코드스플리팅 (본체 작업) │
│ 맹글링·압축 → 압축기에게 위임 │
└────────────────────────────────────┘
6-2. 전통 조합 vs 요즘 조합
전통(2015~): Webpack(번들러) + Babel(트랜스파일) + Terser(압축)
→ 3개 도구가 따로, 전부 JS로 작성됨, 느림
요즘(2024~): Rust 기반 단일 엔진으로 "통합"되는 추세
→ 역할(트랜스파일/번들/압축)은 그대로, 한 도구 안에서 처리
- 트렌드: Babel → SWC / esbuild / Oxc 로 교체, Terser → esbuild / SWC / Oxc 로 교체. 핵심 동기는 속도(Rust·Go 기반).
6-3. 최신 도구 현황 (2026년 기준) ⭐ [보강]
Vite
- 원래 번들러가 2개였다: 개발 땐
esbuild, 프로덕션 빌드 땐Rollup→ 두 파이프라인이 달라 미묘한 불일치가 있었음. - Vite 8(2026년 3월 정식 출시) 부터 Rolldown 이라는 단일 Rust 번들러로 통합. 개발/프로덕션 파이프라인이 하나로 합쳐짐.
- 파싱·변환(트랜스파일)·압축은 Oxc 라는 도구가 담당. (Rolldown 자체는 2026년 5월 1.0 정식)
- 정리: Vite(빌드 툴) → Rolldown(번들러) → Oxc(파서·변환·압축) 한 팀.
Next.js
- Next.js 16(2025년 10월) 부터 Turbopack 이 개발·프로덕션 빌드 모두의 기본 번들러로 승격(Rust 기반, 증분 빌드).
- 트랜스파일은 SWC 가 담당.
- 기존 Webpack 설정을 쓰던 프로젝트는
next dev --webpack/next build --webpack으로 명시하면 계속 Webpack 사용 가능.
기억 포인트: 강의의 "트랜스파일러 + 번들러 2개" 분류는 개념적으로 정확하고, 실제 구현은 번들러가 트랜스파일러·압축기를 불러 쓰는 구조라는 한 겹을 더 얹으면 완성. 그리고 요즘은 그 여러 도구가 Rust 단일 엔진(Rolldown+Oxc / Turbopack+SWC) 으로 합쳐지는 중.
7. 런타임(Runtime) 단계
빌드까지 끝낸 코드를 실제 배포해 브라우저에서 실행/테스트/QA 하는 단계.
7-1. SSR / MSA / BFF
- SSR(Server Side Rendering): SEO를 위해 프론트엔드를 위한 서버를 두는 것. (꼭 해야 하는 건 아님 — 선택)
- MSA(Microservices Architecture): 서버를 도메인 단위로 나눔.
- 문제: "전시 서버"에 리뷰 데이터를 요청하면 "저는 전시 담당이고 리뷰는 리뷰 서버에 따로 요청하세요"라고 함.
- API 어그리게이션(Aggregation): 프론트가 여러 MSA 서버를 각각 찔러서 받은 응답들을 합쳐주는 작업. 보통 "프론트에서 하라"고 한다.
- BFF(Backend For Frontend) 패턴: MSA는 도메인 단위로만 나눠 서비스용 서버가 없으므로, BFF 서버를 두고 거기서 MSA 응답을 합치는 작업을 한다.
- Next.js 서버를 BFF로 활용 가능. 혹은 백엔드 개발자가 만들어줄 수도.
- 단, API 어그리게이션까지 프론트(Next.js)가 하면 트래픽을 다 감내해야 해서 사실상 서버처럼 구축해야 함 → Docker/Kubernetes, 로드 밸런서 등 필요. DB만 안 만질 뿐 거의 서버 역할.
- 프론트엔드 경쟁력을 위해 BFF를 알아두면 큰 도움.
- Next.js를 SSR에 쓸 때 "렌더러 역할만 할지 / API 어그리게이션까지 할지" 결정하는 것도 중요.
7-2. Web Vitals 지표 ⭐
왜? "성능을 최적화했다"고만 하면 "정확히 뭐가 어떻게 좋아졌냐"에 답하기 어렵다. → 정량적 수치화가 필요. (서버는 스트레스 테스트로 트래픽 감당량을 수치화하듯, 웹은 Web Vitals로 측정)
| 지표 | 의미 | 기준 |
|---|---|---|
| LCP (Largest Contentful Paint) | 지면에서 가장 큰 요소(가장 오래 걸리는 리소스)가 렌더링되는 데 걸리는 시간 | 2.5초 이내 = 좋음, 2.5~4초 = 개선 필요, 4초 이상 = 나쁨 |
| INP (Interaction to Next Paint) | 사용자 입력에 대한 전체적인 반응 속도 | 200ms 이내 = 좋음, 200~500ms = 개선 필요, 500ms 초과 = 나쁨 |
| CLS (Cumulative Layout Shift) | 레이아웃이 밀리는 정도(누적 레이아웃 시프트) | 로드된 줄 알고 클릭하려는데 배너가 늦게 떠서 엉뚱한 걸 클릭하는 현상 등 |
⭐ 최신 변경점 (꼭 알아둘 것): 강의에서 설명한 FID(First Input Delay) 는 2024년 3월에 Google이 INP(Interaction to Next Paint)로 공식 대체했다.
- FID: "렌더링 후 첫 입력까지의 지연"만 측정 → 한계가 있었음. (100ms 이내 = 좋음, 하이드레이션 문제 시 나빠짐)
- INP: 페이지 전 생애에 걸친 모든 상호작용의 반응성을 측정 → 더 정확.
- 면접에서 최신 기준을 물으면 "FID가 INP로 바뀌었다"는 점을 같이 언급하면 좋다. (개념·원인 진단은 FID 설명 그대로 유효 — 하이드레이션 문제가 반응성을 떨어뜨린다는 논리)
면접/어필 팁: "라이트하우스 점수를 X점 → Y점으로 올렸다"는 크게 도움이 안 된다. 정확히 뭘 고쳤는지 안 보이기 때문. 대신 "LCP가 낮아서 ~한 액션을 취했다", "CLS 개선을 위해 스켈레톤 로딩을 도입했다" 처럼 구체적·정량적 지표로 설명하는 연습을 하자.
8. [Q&A] 명령형 vs 선언형 / 프레임워크 vs 라이브러리 ⭐
8-1. 핵심 개념 — IOC (Inversion of Control, 제어의 역전)
- 스프링에서 나오는 개념. 제어권이 누구에게 있느냐의 차이.
| 구분 | 라이브러리(Library) | 프레임워크(Framework) |
|---|---|---|
| 제어권 | 개발자(나) 에게 있음 | 프레임워크에 있음 |
| 실행 주체 | 내가 짠 플로우대로 내가 코드를 실행 | 내가 작성한 코드를 프레임워크가 실행해줌 |
8-2. React는 라이브러리, Next.js는 프레임워크
-
React (라이브러리):
- 상태가 변경되면 React가 JSX(설계도)를 받아 렌더링해줌 → 프레임워크적 특징도 있다.
- 개발자는 DOM을 직접 제어하지 않고 JSX로 설계도(정의서)만 준다. 실제로는 React가 버추얼 돔(Virtual DOM) 을 만들어 비교하고, 변경된 것만 찾아 렌더링.
- 그런데 왜 라이브러리? React를 프레임워크 없이 쓰려면
index.js에 루트 컴포넌트를 선언하고 렌더링하는 코드를 직접 작성해야 한다. 즉 실행 시점을 내가 제어하므로 라이브러리.
(옛날) 명령형 방식:
document.createElement로 div 만들고 직접 찾아서 넣어주던 방식(jQuery 등). (지금) 선언형 방식:return으로 JSX 설계도를 주면 React가 그려줌. -
Next.js (프레임워크):
- 이런 렌더링 제어 코드조차 없다.
app폴더에 정해진 위치에 두기만 하면 알아서 라우팅·실행됨. → 제어권이 프레임워크에 있음.
- 이런 렌더링 제어 코드조차 없다.
-
스프링(비유):
- 일반 자바:
main함수를 만들고 거기서 코드를 실행하도록 직접 작성. - 스프링: 서블릿을 스프링 문법에 맞게 특정 폴더에 두고 정의해놓으면 스프링 프레임워크가 그대로 실행해줌.
- 일반 자바:
9. [Q&A] PoC와 AI 활용
9-1. PoC (Proof of Concept)
- 특정 기능이 될지 안 될지 모르는 것의 기술적 검토를 위해 하는 것.
- 가볍게 만들어보고 검증하는 것. 성능 측정·디자인·레이아웃 등 모든 걸 다 PoC할 필요 없이 필요한 것만 하면 된다.
9-2. AI 활용 (AI Native)
- "AI 네이티브 = 바이브 코딩(이해 안 하고 AI에 다 시키기)"으로 오해하기 쉽다.
- 그렇게 하지 말 것. 설계도/아키텍처/베스트 프랙티스 코드 등을 스킬로 만들어두고, AI에 주되 내가 코드를 이해하려는 방식으로 활용하자. 필요할 때마다 정의해서 넣어보고 질문하는 게 좋다.
10. [Q&A] Next.js 배포 방법 (2가지) ⭐
Next.js를 쓴다고 꼭 SSR을 하는 건 아니다. (Docker/어댑터는 일단 빼고 생각) 배포는 두 가지 방법으로 정리된다.
| 방식 | Static Export | Node.js 배포 |
|---|---|---|
| 생성 시점 | 빌드 단계에서 HTML/CSS/JS 에셋을 미리 생성 | 런타임에 요청 들어올 때마다 HTML 생성 |
| 방식 | 이미 만들어진 정적 파일 배포 | 온디맨드(On-demand) 방식 |
| 배포처 | CloudFront + S3에 올리기만 하면 됨 | 서버 사이드 렌더링용 (Docker 필요) |
- 둘 다 스텝을 맞춰놓고 하나는 SSR, 하나는 Static Export로 배포해봐도 된다.
10-1. TanStack Start (대안)
- 최근 "SSR 안 할 거면 Next.js는 너무 무겁고 과하다"는 의견이 많다.
- 배민(우아한형제들) 에서 TanStack Start를 사용 중. 아직 완전 정식 단계는 아니지만 많이 쓰이고 있고, 차세대 준비에 활용 중.
- 공유는 React 관련 함수·공통 훅 단위로 같이 공유해서 사용 가능.
📌 한눈에 보는 전체 흐름 요약
[1] 린트 단계
├─ ESLint (린터) → 정적 분석으로 에러 체크
├─ Prettier (포매터) → 코드 스타일 자동 정렬
└─ TypeScript(TS Server) → 실시간 타입 체킹 (편집기 밑줄)
↓
[2] 컴파일(빌드) 단계 ── 지휘자: 번들러
│
├─ 트랜스파일 (번들러가 트랜스파일러 호출)
│ ├─ TS 타입: 제거만
│ ├─ 최신 JS 문법: 폴리필 삽입
│ └─ JSX: JS로 변환
│ └ 도구: Babel → (요즘) SWC / esbuild / Oxc
│
├─ 번들링 (번들러 본체): 리소스 하나로 합치기 (이미지=Base64)
│
└─ 최적화
├─ 트리 쉐이킹: 안 쓰는 import 제거 (번들러 본체)
├─ 코드 스플리팅: URL 경로별 재분리 (번들러 본체)
└─ 맹글링+압축(minify): 이름 단축·공백 제거 (압축기에 위임)
└ 도구: Terser → (요즘) esbuild / SWC / Oxc
[번들러 도구] 전통: Webpack
요즘: Vite8→Rolldown(+Oxc) / Next16→Turbopack(+SWC)
↓
[3] 런타임 단계
├─ 배포 (Static Export / Node.js 온디맨드)
├─ SSR / MSA / BFF / API 어그리게이션
└─ Web Vitals (LCP / INP / CLS)로 정량 측정 ※ FID는 INP로 대체됨
관통하는 두 철학
- 에러는 최대한 앞 단계(린트·컴파일)에서 잡는다. (→ 에러 약 90% 감소)
- 네트워크 왕복(핑퐁)을 최대한 줄인다. (번들링·최적화의 존재 이유)