프롤로그: 2016년, 개발자들의 절망
2016년 10월, 한 개발자가 Medium에 글을 올렸습니다.[^111-1]
"How it feels to learn JavaScript in 2016"
이 글은 폭발적으로 퍼졌어요. 왜냐하면 모든 JavaScript 개발자가 공감했거든요.
글은 이렇게 시작합니다. 신입 개발자가 선배에게 묻습니다: "jQuery로 웹사이트 만들고 싶은데 어떻게 시작해요?"
선배가 대답합니다: "jQuery요? 2016년에요? React 써야죠."
"React가 뭔데요?"
"React는 Facebook이 만든 라이브러리예요. UI를 선언적으로..."
"잠깐만요, 그럼 HTML은 어디에 쓰는 거예요?"
"HTML은 안 써요. JSX를 써요."
"JSX요?"
"네, JSX는 JavaScript 안에 HTML을 쓰는 건데..."
"JavaScript 안에 HTML을요?!"
"네, 그리고 Babel로 트랜스파일해야 해요."
"Babel이요?"
"ES6를 ES5로 변환하는 거예요."
"그게 뭔데요?"
"아, 그리고 webpack도 필요해요."
"webpack요?"
"모듈 번들러예요. 설정 파일이 좀 복잡하긴 한데..."
이 대화는 끝없이 계속됩니다.[^111-1]
이것이 **"JavaScript Fatigue"**였습니다.
1막: 설정 파일 지옥 (2014-2016)
악몽의 시작
2014-2016년, React 앱을 만들려면 이렇게 해야 했습니다:
npm install react react-dom
npm install --save-dev webpack webpack-cli webpack-dev-server
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react
npm install --save-dev eslint eslint-loader babel-eslint eslint-plugin-react
npm install --save-dev style-loader css-loader file-loader
npm install --save-dev html-webpack-plugin clean-webpack-plugin
Bash
복사
그리고 webpack.config.js:[^112-1][^113-1]
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash].js'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
hot: true,
open: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{ loader: 'babel-loader' },
{ loader: 'eslint-loader' }
]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
inject: false,
hash: true,
template: './src/index.html',
filename: 'index.html'
})
]
};
JavaScript
복사
.babelrc:[^113-1][^114-1]
{
"presets": [
["@babel/env", {
"useBuiltIns": "entry",
"targets": {
"browsers": "> 0.25%, not dead"
}
}],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread"
]
}
JSON
복사
.eslintrc.json:[^115-1][^116-1]
{
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"browser": true,
"es6": true,
"jest": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"airbnb"
],
"plugins": [
"react"
],
"rules": {
"max-len": ["error", { "code": 100 }],
"indent": ["error", 2],
"quotes": ["error", "single"]
}
}
JSON
복사
그리고 package.json의 scripts:[^117-1]
{
"scripts": {
"start": "NODE_ENV=development webpack-dev-server --mode=development --hot",
"build": "webpack --mode=production",
"lint": "eslint src/**/*.js",
"test": "jest"
}
}
JSON
복사
이것이 "Hello World"를 띄우기 위한 최소 설정이었습니다.[^111-1][^118-1]
왜 이렇게 됐을까?
각 도구는 각자의 역사가 있었어요:[^119-1]
Webpack (2012): Tobias Koppers가 만들었습니다. 당시 JavaScript 모듈 시스템이 혼란스러웠거든요. CommonJS, AMD, UMD... Webpack은 "모든 것을 번들링하자"는 철학이었죠.
Babel (2014): Sebastian McKenzie가 17살에 만들었습니다. 이름은 "6to5"였어요. ES6를 ES5로 변환하는 게 목적이었죠. 나중에 Babel로 이름을 바꿨습니다.
ESLint (2013): Nicholas Zakas가 만들었습니다. JSLint와 JSHint는 너무 독단적이었어요. ESLint는 "규칙을 커스터마이즈하자"는 철학이었죠.
각각은 훌륭했습니다. 문제는? 전부 다 배우고 설정해야 했다는 거예요.[^111-1][^118-1]
도구의 폭발
2015-2016년, 새로운 빌드 도구가 매달 나왔습니다:[^111-1]
•
Grunt → Gulp → Broccoli
•
Browserify vs RequireJS vs SystemJS
•
npm vs Bower vs yarn
•
Mocha vs Jasmine vs Jest
•
Karma vs Testem
6개월마다 "최신 베스트 프랙티스"가 바뀌었습니다.[^111-1]
튜토리얼을 따라하면? 이미 deprecated됐어요. 새 프로젝트를 시작하면? "이거 써야 해, 저거 써야 해" 조언이 난무했죠.
2막: Create React App - 첫 번째 희망 (2016년 7월)
Dan Abramov의 선물
2016년 7월, Facebook의 Dan Abramov가 발표했습니다:[^120-1]
"Create React App"
npx create-react-app my-app
cd my-app
npm start
Bash
복사
끝.
webpack 설정? 없습니다. Babel 설정? 없습니다. ESLint 설정? 없습니다.
전부 숨겨져 있었어요. 개발자는 그냥 코드만 쓰면 됐죠.[^120-1]
하지만...
Create React App(CRA)은 훌륭했지만, 한계가 있었습니다:[^120-1]
1. 커스터마이징의 어려움
설정을 바꾸고 싶으면? npm run eject를 실행해야 했어요. 그러면 모든 설정 파일이 튀어나왔습니다. 되돌릴 수 없었죠.
2. SSR은 여전히 불가능
CRA는 클라이언트 사이드 앱만 만들 수 있었어요. SSR? 직접 구현해야 했습니다. 여전히 복잡했죠.
3. 라우팅, 데이터 페칭
React Router 직접 설정, API 호출 직접 작성... CRA는 React만 줬을 뿐이었습니다.
개발자들은 여전히 SSR을 원했습니다. SEO 때문이었죠.
3막: 2016년 10월 25일, 게임 체인저의 등장
Zeit의 대답
2016년 10월 25일, Zeit(현재 Vercel)의 CEO Guillermo Rauch가 발표했습니다:[^121-1][^122-1]
"Next.js - A minimalistic framework for server-rendered React applications"
철학:[^122-1]
Zero Config
File-system Based Routing
Server Rendering by Default
Automatic Code Splitting
Plain Text
복사
얼마나 간단했냐면:[^121-1][^122-1]
npm install next react react-dom
Bash
복사
pages/index.js:
export default function Home() {
return <h1>Hello World</h1>
}
JavaScript
복사
npm run dev
Bash
복사
끝입니다. SSR이 작동했어요. 라우팅도 자동이었죠.[^121-1][^122-1]
새 페이지를 만들고 싶으면? pages/about.js 파일만 추가하면 됐습니다. /about 라우트가 자동으로 생겼어요.[^122-1]
Guillermo Rauch는 누구였나?
Guillermo는 아르헨티나 출신이었어요.[^123-1] 그에게 오픈소스는 "아르헨티나에서 세계로 나가는 티켓"이었죠.
그는 socket.io를 만든 사람이기도 했습니다. 실시간 웹에 대한 통찰이 있었어요.
2015년, 그는 Zeit를 창업했습니다. 미션은 간단했죠: "클라우드를 모든 사람이 접근 가능하게 만들자."[^123-1]
Guillermo가 본 문제:[^122-1][^123-1]
React는 훌륭합니다.
하지만 SSR 설정이 너무 복잡해요.
webpack 설정만 500줄이에요.
개발자들은 비즈니스 로직에 집중해야 하는데,
도구 설정에 시간을 낭비하고 있어요.
Plain Text
복사
Next.js의 접근법은 **"관습(convention)이 설정(configuration)을 이긴다"**였습니다.[^122-1][^123-1]
Ruby on Rails의 영향을 받았죠. "파일을 특정 위치에 두면 자동으로 작동한다."
4막: 같은 날 오후, Vue 진영의 반응
Chopin 형제의 결단
2016년 10월 25일 오후.[^124-1][^125-1]
프랑스의 Alexandre Chopin과 Sébastien Chopin 형제가 Next.js 발표를 봤습니다.
그들은 생각했죠: "Vue에도 이런 게 필요해."[^124-1][^125-1]
몇 시간 후, 그들은 프로젝트를 시작했습니다.[^125-1]
이름은 Nuxt.js.
Next.js = React
Nuxt.js = Vue
Plain Text
복사
발표 글에서 이렇게 썼어요:[^125-1]
"2016년 10월 25일, zeit.co 팀이 Next.js를 발표했습니다. 발표 몇 시간 후, Vue.js로 같은 방식의 서버 렌더 앱을 만들자는 아이디어가 명백해 보였습니다: Nuxt.js가 탄생했습니다."
Nuxt.js는 Next.js의 철학을 그대로 가져왔습니다:[^125-1][^126-1]
•
File-system based routing
•
Server-side rendering by default
•
Automatic code splitting
•
Zero config
차이는? Vue.js 기반이었다는 것뿐이죠.
왜 이렇게 빨랐나?
Chopin 형제는 Vue 커뮤니티의 핵심 멤버였어요.[^126-1] 그들은 Vue 개발자들이 겪는 고통을 알고 있었죠.
Vue는 React보다 배우기 쉬웠지만, SSR 구현은 여전히 어려웠습니다. Vue SSR 가이드는 70페이지나 됐어요.
Next.js가 React 생태계에 준 충격을 보고, 그들은 즉시 행동했습니다.[^124-1][^125-1]
2018년 1월, Nuxt 1.0이 릴리즈됐습니다.[^127-1]
5막: 한편, 또 다른 접근 - Gatsby (2015-2017)
Kyle Mathews의 다른 선택
2015년 5월, Kyle Mathews는 다른 문제를 풀고 있었습니다.[^128-1][^129-1]
그는 React로 블로그를 만들고 싶었어요. 하지만 React만으로는 불가능했죠. 그래서 직접 도구를 만들었습니다.
이름은 Gatsby.[^128-1][^129-1]
Gatsby의 차별점
Gatsby는 Next.js나 Nuxt와 달랐습니다:[^129-1][^130-1]
Next.js:
•
런타임에 서버에서 렌더링
•
동적 콘텐츠에 강함
Gatsby:
•
빌드 타임에 전부 렌더링
•
정적 HTML 파일 생성
•
CDN에 배포
Kyle의 통찰:[^130-1][^131-1]
대부분의 웹사이트는 사실 정적입니다.
블로그? 정적이에요.
문서 사이트? 정적이죠.
마케팅 사이트? 대부분 정적입니다.
그런데 왜 매 요청마다 서버에서 렌더링하나요?
빌드할 때 한 번만 렌더링하면 되잖아요!
Plain Text
복사
GraphQL이라는 무기
Gatsby의 독특한 점은 GraphQL 데이터 레이어였습니다.[^130-1][^131-1]
// pages/index.js
import { graphql } from 'gatsby'
export default function Home({ data }) {
return (
<div>
<h1>{data.site.siteMetadata.title}</h1>
{data.allMarkdownRemark.edges.map(({ node }) => (
<article key={node.id}>
<h2>{node.frontmatter.title}</h2>
<p>{node.excerpt}</p>
</article>
))}
</div>
)
}
export const query = graphql`
query {
site {
siteMetadata {
title
}
}
allMarkdownRemark {
edges {
node {
id
frontmatter {
title
}
excerpt
}
}
}
}
`
JavaScript
복사
데이터가 어디서 오든 (Markdown, WordPress, Contentful, API...) GraphQL로 통일됐어요.[^130-1][^131-1]
2017년, Gatsby 1.0
2017년 7월, Gatsby 1.0이 릴리즈됐습니다.[^131-1]
이때부터 Gatsby는 폭발적으로 성장했어요. 왜냐면:[^131-1][^132-1]
•
엄청나게 빨랐습니다. 정적 파일이니까요.
•
SEO가 완벽했습니다. 이미 HTML이 있으니까요.
•
무료 호스팅이 쉬웠습니다. Netlify, GitHub Pages에 그냥 올리면 됐죠.
•
플러그인 생태계. 500개 이상의 플러그인이 생겼습니다.[^132-1]
2018년, Gatsby Inc.가 설립됐고, $3.8M 시드 펀딩을 받았습니다.[^133-1]
6막: 메타프레임워크 시대의 개막 (2016-2017)
"메타프레임워크"라는 개념
Next.js, Nuxt.js, Gatsby... 이들의 공통점은 뭘까요?
프레임워크 위의 프레임워크였습니다.
•
React 위의 Next.js
•
Vue 위의 Nuxt.js
•
React 위의 Gatsby
이들을 "메타프레임워크"라고 부르기 시작했어요.[^122-1][^126-1][^130-1]
왜 성공했을까?
1. Zero Config 철학[^122-1][^123-1]
개발자들은 설정 파일 작성에 지쳤어요. 메타프레임워크는 "그냥 작동한다"를 제공했죠.
2. Best Practices를 기본값으로[^122-1][^123-1][^130-1]
•
Code splitting: 자동
•
Lazy loading: 자동
•
Image optimization: 자동 (Gatsby)
•
Prefetching: 자동 (Next.js)
3. 점진적 채택 가능[^122-1][^123-1]
기존 React/Vue 지식을 그대로 쓸 수 있었어요. 러닝 커브가 낮았죠.
4. 강력한 후원자[^123-1][^125-1][^133-1]
•
Next.js: Vercel (당시 Zeit)
•
Nuxt.js: NuxtLabs (2018년 설립)
•
Gatsby: Gatsby Inc. (2018년 설립)
오픈소스지만 회사가 있었어요. 지속 가능한 개발이 보장됐죠.
에필로그: 여전히 남은 문제들
2017년 말, 메타프레임워크 시대가 활짝 열렸습니다.
하지만 문제는 여전했어요:
1. Isomorphic의 한계
여전히 같은 코드를 클라이언트와 서버에서 돌려야 했습니다.[^122-1]
// 이건 여전히 안 됐어요
import db from 'database'
export default function Page() {
const data = db.query('SELECT * FROM posts') // ❌ 클라이언트에서 실행 불가
return <div>{data}</div>
}
JavaScript
복사
2. renderToString의 블로킹
React의 서버 렌더링은 여전히 동기적이었어요.[^122-1]
// Next.js 내부
const html = ReactDOMServer.renderToString(<App />)
JavaScript
복사
전체 컴포넌트 트리가 렌더링될 때까지 기다려야 했죠. BigPipe처럼 스트리밍? 불가능했습니다.
3. 데이터 페칭의 Waterfall[^122-1]
// Next.js getServerSideProps
export async function getServerSideProps() {
const posts = await fetchPosts() // 1초
const user = await fetchUser() // 1초
return { props: { posts, user } }
}
// 총 2초 기다림
JavaScript
복사
병렬로 할 수는 있었지만, 개발자가 직접 해야 했어요.
4. 여전히 복잡한 빌드[^122-1][^130-1]
Zero-config라고는 하지만... Next.js나 Gatsby 프로젝트가 커지면 빌드 시간이 10분, 20분씩 걸렸습니다.
하지만 희망은 있었습니다.
2018년, React 팀은 조용히 새로운 프로젝트를 시작했어요.
코드명: "Fizz"
BigPipe의 아이디어를 React로 가져오려는 시도였죠.
그리고 2019년, 더 야심찬 프로젝트가 시작됩니다.
코드명: "Flight"
2013년에 포기했던 질문을 다시 던졌어요: "같은 코드를 양쪽에서 돌리는 게 정답일까?"
React Server Components의 이야기가 시작되는 순간이었습니다.
다음 장 예고:
2018년, Facebook React 팀에서 이상한 실험이 진행되고 있었습니다.
"컴포넌트 전체를 서버에서만 실행하면 어떨까?"
"가상 DOM을 직렬화해서 와이어로 보내면?"
"클라이언트는 그걸 받아서 hydrate하면?"
BigPipe + React = Fizz서버 전용 컴포넌트 = Flight
그리고 이 두 프로젝트가 합쳐져...
2023년, Next.js 13 App Router의 충격이 옵니다.
참고 문헌 표기:
[^111-1]: Jose Aguinaga, "How it feels to learn JavaScript in 2016", Medium (2016)
[^112-1]: Shalitha Senanayaka, "Setting up React app from scratch with Webpack, Babel and ESLint", Medium (2021)
[^113-1]: Michele Nasti, "Typescript, Babel, Webpack, ESLint: my configuration" (2019)
[^114-1]: Stack Overflow, "webpack babel loader with eslint loader"
[^115-1]: Robin Wieruch, "How to use ESLint in Webpack 5"
[^116-1]: Camilo Rincon, "Setting up ESLint & Prettier With Webpack in VSCode", DEV (2022)
[^117-1]: Ultimate Courses, "Modern JavaScript with Babel, Webpack and ESLint"
[^118-1]: Tutorialspoint, "Advanced JavaScript Tooling: Webpack, Babel, and ESLint"
[^119-1]: webpack Documentation, "Build Performance"
[^120-1]: Create React App Official Documentation
[^121-1]: Open Collective, "How funding open source gives Vercel access to world-class talent" (2021)
[^122-1]: Acquired FM, "Building Web Apps with Just English and AI (with Vercel CEO Guillermo Rauch)"
[^123-1]: LinkedIn, "The History of Next.js: A Tale of Bridging the Gap in React Ecosystem" (2023)
[^124-1]: Naturaily, "What Is Nuxt? Discover The SSR Framework" (2025)
[^125-1]: CloudCannon, "The 'SPAs are awesome' era"
[^126-1]: Bejamas, "What is Nuxt: Review and Features"
[^127-1]: StaticMania, "Next.js vs Nuxt.js: Differences - Which Is Better in 2025?"
[^128-1]: StaticMania, "Gatsby Framework: The Ultimate React-Based Framework"
[^129-1]: Gaël Billon, "Gatsby, le générateur de site statique basé sur React et GraphQL" (2021)
[^130-1]: Webstacks, "What is the GatsbyJS Framework?"
[^131-1]: InfoQ, "Q&A with Kyle Mathews, Creator of React-Based Static Site Generator Gatsby" (2017)
[^132-1]: Common Ninja's Blog, "The Ultimate Guide to Gatsby.js"
[^133-1]: Changelog, "How $3.8M in seed funding started Gatsby as an open source company featuring Kyle Mathews, CEO of Gatsby" (Founders Talk #59)
