전역 상태 관리의 이해 + 프론트엔드 앱의 테스트/배포 흐름 경험
목표
- 전역 상태 관리
- 전역 상태의 개념과 필요성을 설명할 수 있다. (props drilling vs 전역 상태)
- useContext로 간단한 전역 상태를 직접 구성해 볼 수 있다.
- Zustand, Recoil, Redux 등 대표 상태관리 라이브러리의 특징과 차이를 말할 수 있다.
- 어떤 상황에서 어떤 상태 관리 전략을 선택할지 판단할 수 있다.
- 테스트 및 배포 흐름
- React 컴포넌트 테스트의 기본 구조(vitest, jest, testing-library/react)를 이해하고 예제 테스트를 작성할 수 있다.
- Vercel, Netlify, Cloudflare Pages의 차이점과 장단점을 이해할 수 있다.
- 실제로 만든 앱을 클라우드 플랫폼에 배포해 보고 동작 확인까지 할 수 있다.
참고 키워드
- 상태 관리:
- useContext, useReducer
- Zustand → 간결한 구조
- Recoil → atom 중심 구조
- Redux → reducer + store 구조
- 테스트/배포:
- vitest, jest, @testing-library/react
- GitHub → Vercel 자동 배포
- Netlify CLI, netlify.toml 등
공유 방식
- 상태 관리 도구 중 하나를 골라 간단한 예제 (Todo, 테마 토글 등) 만들어 공유
- 테스트는 단일 컴포넌트라도 render + expect 구문 써서 해보기
- 각자 Vercel / Netlify / Cloudflare 중 하나를 골라 배포해 보기
- 링크 공유 (가능하면 커스텀 도메인도 도전)
전역 상태의 개념과 필요성

-
정의:부모 컴포넌트에서 여러 자식 컴포넌트를 거쳐 자손 컴포넌트로 데이터를 전달할 때, 중간 컴포넌트들이 해당 데이터를 실제로 사용하지 않더라도 전달해야 하는 상황을 말한다.
-
문제점:코드가 복잡해지고 가독성이 떨어지며, 유지보수가 어려워질 수 있다. 특정 데이터를 사용하지 않는 컴포넌트에서도 props를 관리해야 하므로 불필요한 작업이 발생한다.
-
해결 방법:Context API 또는 전역 상태 관리 라이브러리 (Redux, Zustand 등)를 사용하여 props drilling 문제를 해결할 수 있다.
-
정의:애플리케이션 전체에서 공유 가능한 상태를 중앙에서 관리하는 방식이다.
-
장점:props drilling 문제를 해결하고, 필요한 컴포넌트에서 쉽게 데이터에 접근할 수 있도록 합니다. 특히 복잡한 애플리케이션에서 유용하다.
-
단점:상태 관리가 복잡해질 수 있고, 잘못 사용하면 성능 문제가 발생할 수 있다. 또한, 불필요하게 전역 상태를 사용하면 코드의 가독성이 저하될 수 있다.
-
예시:
- Redux: 상태, 액션, 리듀서 등을 사용하여 복잡한 애플리케이션의 상태를 관리.
- Zustand: 가볍고 사용하기 쉬운 전역 상태 관리 라이브러리.
- Context API: React에서 제공하는 기능으로, 간단한 전역 상태 관리에 유용.
데이터 전달 방식
|
중간 컴포넌트 거쳐 전달
|
중앙에서 관리
|
문제점
|
코드 복잡도 증가, 유지보수 어려움
|
복잡한 애플리케이션에서 관리 어려움
|
장점
|
간단한 컴포넌트 구조에서 유용
|
복잡한 컴포넌트 구조에서 효율적
|
해결 방법
|
Context API, 전역 상태 관리 라이브러리
|
전역 상태 관리 라이브러리
|
useContext로 간단한 전역 상태를 직접 구성해 볼 수 있다.
(리액트 공식문서를 참고하였습니다.)
https://ko.react.dev/reference/react/useContext
useContext – React
The library for web and native user interfaces
ko.react.dev
Context API란?
- Context API는 React version 16부터 사용 가능한 리액트의 내장 API이다.
- 앱에서 컴포넌트에게 props를 사용하지 않고 필요한 데이터(state)를 쉽게 공유할 수 있게 해 준다. 따라서 앱의 모든 컴포넌트에서 사용할 수 있는 데이터(state)를 전달할 때 유용하다.
Context API는 주로 어디에 사용하나?
- Context API를 통해 전달하는 데이터의 종류
- 테마 데이터 (다크 모드, 라이트 모드)
- 사용자 데이터 (현재 인증된 사용자)
- 언어 혹은 지역 데이터
- Context API는 자주 업데이트할 필요가 없는 데이터에 사용.
- Context API에서 State값을 변경하면, Provider로 감싼 모든 자식 컴포넌트들이 리렌더링 되므로 전역 상태 관리를 위한 도구가 아닌, 데이터를 쉽게 전달하고 공유하기 위한 목적으로 사용하는 것이 적합하다.
- 따라서 Context는 리액트에서 컴포넌트를 위한 전역 변수의 개념이다.
Context API 사용 방법
- createContext 메서드를 사용하여 context 생성한다.
- 생성한 context를 대상 컴포넌트에 값을 내려주기 위해서 Provider로 대상 컴포넌트를 감싼다.
- Provider의 프로퍼티인 value에 전달할 데이터를 넣는다.
- Provider의 value에 담은 데이터를 전달할 때는, 2가지 방식으로 전달이 가능하다. Consumer 컴포넌트 또는 useContext라는 훅을 이용하는 법이다.
예제 코드
버튼을 누르면 다크 모드, 라이트 모드를 바꾸는 코드를 예시로 들어보자. 코드는 다음과 같다.
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext('light');
export default function MyApp() {
const [theme, setTheme] = useState('light');
return (
<>
<ThemeContext value={theme}>
<Form />
</ThemeContext>
<Button onClick={() => {
setTheme(theme === 'dark' ? 'light' : 'dark');
}}>
Toggle theme
</Button>
</>
)
}
function Form({ children }) {
return (
<Panel title="Welcome">
<Button>Sign up</Button>
<Button>Log in</Button>
</Panel>
);
}
function Panel({ title, children }) {
const theme = useContext(ThemeContext);
const className = 'panel-' + theme;
return (
<section className={className}>
<h1>{title}</h1>
{children}
</section>
)
}
function Button({ children, onClick }) {
const theme = useContext(ThemeContext);
const className = 'button-' + theme;
return (
<button className={className} onClick={onClick}>
{children}
</button>
);
}
https://codesandbox.io/p/sandbox/q997jx?file=%2Fsrc%2FApp.js%3A19%2C2
https://codesandbox.io/p/sandbox/q997jx?file=%2Fsrc%2FApp.js%3A19%2C2
codesandbox.io
(예시 화면)
📌 컴포넌트 구성 & 동작 원리
import { createContext, useContext, useState } from 'react';
> 훅을 쓰기 위해서는 상단에 import문을 꼭 써주자!
1. ThemeContext 생성
const ThemeContext = createContext('light');
- createContext는 전역 상태를 만들 때 사용하는 React 내장 함수.
- 'light'는 초기 기본값이지만, 실제로는 value를 통해 동적으로 관리된다.
2. 최상위 컴포넌트: MyApp
export default function MyApp() {
const [theme, setTheme] = useState('light');
...
}
- theme 상태를 선언. 기본값은 'light'
- setTheme을 통해 테마를 토글 할 수 있음.
이 theme를..
<ThemeContext value={theme}>
ThemeContext의 value에 넣어주기!
3. Form 컴포넌트
function Form({ children }) {
return (
<Panel title="Welcome">
<Button>Sign up</Button>
<Button>Log in</Button>
</Panel>
);
}
- Form은 Panel을 렌더링 하고, Button 두 개를 자식으로 넘김.
4. Panel 컴포넌트
function Panel({ title, children }) {
const theme = useContext(ThemeContext);
const className = 'panel-' + theme;
...
}
- useContext(ThemeContext)로 현재 테마 상태를 받아옴.
- className을 테마에 따라 동적으로 적용.
5. Button 컴포넌트
function Button({ children, onClick }) {
const theme = useContext(ThemeContext);
const className = 'button-' + theme;
...
}
- 버튼도 테마에 따라 스타일이 바뀜.
- onClick이 있으면 실행함 → 이걸로 테마 토글 기능 구현.
Zustand, Recoil, Redux 등 대표 상태관리 라이브러리의 특징과 차이
- 상태 관리 라이브러리 비교
- 라이브러리특징
Zustand 코드 간결, 러닝커브 낮음 Recoil atom 단위, 비동기 지원 좋음 Redux 가장 전통적, 복잡하지만 강력함 - 상황별 선택 기준
- 규모 작음 👉 useContext or Zustand
- 컴포넌트 복잡, 비동기 많음 👉 Recoil
- 대규모 프로젝트 👉 Redux
공식 사이트 링크 >
Zustand
zustand-demo.pmnd.rs
Recoil
A state management library for React.
recoiljs.org
Redux - 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너. | Redux
자바스크립트 앱을 위한 예측 가능한 상태 컨테이너.
ko.redux.js.org
zustand의 예제 >
npm install zustand
위 명령어로 zustand를 설치할 수 있다.
그리고 zustand에는 "store"라는 것을 써서 상태를 관리할 수 있다.
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
updateBears: (newBears) => set({ bears: newBears }),
}))
보통 상태를 관리하는 코드를 따로 파일에 관리하는 것이 정설(?)이다.
zustand의 상태관리를 사용하고 싶다면 코드의 상단에 import 문을 사용하고, 스토어를 만들어낸다.
이 스토어에는 기본형, 객체, 함수 등을 넣을 수 있다. set함수는 상태를 병합하는 함수이다.
그리고 이렇게 요소를 세팅하고 이후 내가 원하는 코드에 바인딩해 주면 끝이다.
(예시 코드)
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} bears around here...</h1>
}
function Controls() {
const increasePopulation = useStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
Recoil의 예제 >
npm install recoil
위 명령어로 recoil을 설치할 수 있다.
recoil은 Atoms이라는 개념을 사용한다. atoms는 상태의 단위이며 업데이트 및 구독이 가능하다.
atom이 업데이트되면 각각 구독된 컴포넌트는 새로운 값을 반영하여 다시 렌더링 된다.
atom는 런타임에서 생성될 수도 있다. 동일한 atom이 여러 컴포넌트에서 사용되는 경우 모든 컴포넌트는 상태를 공유한다.
atoms는 atom 함수를 사용해서 생성하게 된다.
const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});
Atoms는 디버깅, 지속성 및 모든 atoms의 map을 볼 수 있는 특정 고급 API에 사용되는 고유한 키가 필요하다. 두 개의 atom이 같은 키를 갖는 것은 오류이기 때문에 키값은 전역적으로 고유하도록 해야 한다. React 컴포넌트의 상태처럼 기본값도 가진다.
컴포넌트에서 atom을 읽고 쓰려면 useRecoilState라는 훅을 사용한다. React의 useState와 비슷하지만, 상태가 컴포넌트 간에 공유될 수 있다는 차이가 있다.
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return (
<button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
Click to Enlarge
</button>
);
}
버튼을 클릭하면 버튼의 글꼴 크기가 1만큼 증가하며, fontSizeState atom을 사용하는 다른 컴포넌트의 글꼴 크기도 같이 변화한다.
function Text() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return <p style={{fontSize}}>This text will increase in size too.</p>;
}
Selectors
Selector는 atoms나 다른 selectors를 입력으로 받아들이는 순수 함수이다. 상위의 atoms 또는 selectors가 업데이트되면 하위의 selector 함수도 다시 실행이 된다. 컴포넌트들은 selectors를 atoms처럼 구독할 수 있으며 selectors가 변경되면 컴포넌트들도 다시 렌더링이 된다.
Selectors는 상태를 기반으로 하는 파생 데이터를 계산하는 데 사용되는데, 최소한의 상태 집합만 atoms에 저장하고 다른 모든 파생되는 데이터는 selectors에 명시한 함수를 통해 효율적으로 계산함으로써 쓸모없는 상태의 보존을 방지한다.
Selectors는 어떤 컴포넌트가 자신을 필요로 하는지, 또 자신은 어떤 상태에 의존하는지를 추적하기 때문에 이러한 함수적인 접근방식을 매우 효율적으로 만드는 함수이다.
Selectors는 selector함수를 사용해 정의한다.
(예시 코드)
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
get 속성은 계산될 함수이다.
전달되는 get 인자를 통해 atoms와 다른 selectors에 접근할 수 있다. 다른 atoms나 selectors에 접근하면 자동으로 종속 관계가 생성되므로, 참조했던 다른 atoms나 selectors가 업데이트되면 이 함수도 다시 실행된다.
redux의 예제 >
# NPM
npm install @reduxjs/toolkit
# Yarn
yarn add @reduxjs/toolkit
위 명령어로 redux을 설치할 수 있다. (더 정확히는 redux의 toolkit를 설치하는 셈. 공식 문서에서는 redux toolkit를 설치해서 사용하기를 권장하고 있다.)
리덕스 툴킷은 리덕스를 만들기 위해서 필수적으로 여기는 패키지와 함수들을 포함한다.
기본 예제
어떠한 앱의 상태 전부는 하나의 저장소(store) 안에 있는 객체 트리에 저장된다.
상태 트리를 변경하는 방법은 무엇이 일어날지 서술하는 객체인 액션(action)을 보내는 것이다. 액션이 상태 트리를 어떻게 변경할지 명시하기 위해 우리가 리듀서(reducers)를 작성해야 한다.
예시 코드 >
import { createStore } from 'redux'
/**
* 이것이 (state, action) => state 형태의 순수 함수인 리듀서입니다.
* 리듀서는 액션이 어떻게 상태를 다음 상태로 변경하는지 서술합니다.
*
* 상태의 모양은 당신 마음대로입니다: 기본형(primitive)일수도, 배열일수도, 객체일수도,
* 심지어 Immutable.js 자료구조일수도 있습니다. 오직 중요한 점은 상태 객체를 변경해서는 안되며,
* 상태가 바뀐다면 새로운 객체를 반환해야 한다는 것입니다.
*
* 이 예제에서 우리는 `switch` 구문과 문자열을 썼지만,
* 여러분의 프로젝트에 맞게
* (함수 맵 같은) 다른 컨벤션을 따르셔도 좋습니다.
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// 앱의 상태를 보관하는 Redux 저장소를 만듭니다.
// API로는 { subscribe, dispatch, getState }가 있습니다.
let store = createStore(counter)
// subscribe()를 이용해 상태 변화에 따라 UI가 변경되게 할 수 있습니다.
// 보통은 subscribe()를 직접 사용하기보다는 뷰 바인딩 라이브러리(예를 들어 React Redux)를 사용합니다.
// 하지만 현재 상태를 localStorage에 영속적으로 저장할 때도 편리합니다.
store.subscribe(() => console.log(store.getState())))
// 내부 상태를 변경하는 유일한 방법은 액션을 보내는 것뿐입니다.
// 액션은 직렬화할수도, 로깅할수도, 저장할수도 있으며 나중에 재실행할수도 있습니다.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
상태를 바로 변경하는 대신, 액션이라 불리는 평범한 객체를 통해 일어날 변경을 명시한다.
그리고 각각의 액션이 전체 애플리케이션의 상태를 어떻게 변경할지 결정하는 특별한 함수인 리듀서를 작성한다.
보통의 Redux 앱에는 하나의 루트 리듀서 함수를 가진 단 하나의 저장소가 있다.
앱이 커짐에 따라 루트 리듀서를 상태 트리의 서로 다른 부분에서 개별적으로 동작하는 작은 리듀서들로 나눌 수 있다. React 앱을 하나의 루트 컴포넌트에서 시작해서 여러 작은 컴포넌트의 조합으로 바꾸는 것과 동일하고 생각하면 된다.
이런 아키텍처는 규모가 작은 앱에서 너무 과하다. 그러나 크고 복잡한 앱에서는 이 패턴의 확장성이 엄청난 도움이 된다. 액션에 따른 모든 변경을 추적할 수 있기 때문에, 매우 강력한 개발자 도구를 가능하게 해주기도 한다.
(이 밖에도 많은 개념들이 존재함... 즉, 커닝 커브가 큰 라이브러리임)
https://youtu.be/QZcYz2NrDIs?si=9EoESpWahSvKgr8k
(옛날 영상이지만 쉽게 설명해 주셨음.)
리액트의 컴포넌트 테스트
1. 왜 테스트 코드를 작성을 해야 될까?
현업에서는 바쁘다는 이유로 테스트코드를 작성하지 않는 경우가 많다. (나 또한 작성해 본 적은 한 번도 없다..)
- 어플리케이션의 코드품질 및 안정성 향성
- 유지보수성 향상
- 테스트 코드가 완전하게 커버되어 있다면 프로젝트 신뢰성이 높아지며 사용자에게 안정적인 어플리케이션을 제공함.
2. React Testing Library
- React Testing Library는 사용자 관점에서 테스트를 수행하며, 애플리케이션의 실제 사용자와 유사한 방식으로 상호작용한다. 이는 사용자의 행동에 따른 애플리케이션의 동작을 테스트하는 데 매우 유용하다. 또한, React Testing Library는 컴포넌트의 내부 구조에 의존하지 않으므로, 컴포넌트의 내부 구현이 변경되어도 테스트를 재작성하지 않아도 되는 이점이 있다.
- 가상 DOM(Virtual DOM)을 사용하여 테스트를 수행한다. 가상 DOM은 실제 DOM과 유사한 인터페이스를 제공하지만, 메모리 상에 존재하는 가짜 DOM이다. 가상 DOM을 사용하여 컴포넌트를 렌더링 하고, 이를 기반으로 테스트를 수행한다.
공식 문서 >
https://testing-library.com/docs/react-testing-library/intro/
React Testing Library | Testing Library
React Testing Library builds on top of DOM Testing Library by adding
testing-library.com
https://jestjs.io/docs/tutorial-react
Testing React Apps · Jest
At Facebook, we use Jest to test React applications.
jestjs.io
Vitest
Next generation testing framework powered by Vite
vitest.dev
npm으로 설치하기 >
npm install --save-dev @testing-library/react @testing-library/dom
설치는 위 명령어를 쓰면 된다.
참고로 이 리액트 테스팅 라이브러리랑 궁합이 맞는 친구들이 있는데, 하나는 jest이고, 나머지는 vitest가 있다.
jest는 테스트 종류 중 유닛 테스트와 통합 테스트를 위해서 사용되고, 리액트 환경에서 이루어진다. (그래서 리액트 테스팅 라이브러리와 함께 사용해서 테스트 코드를 짠다.)
JEST
Jest는 페이스북에서 만든 자바스크립트 테스팅 프레임워크이다. React의 테스트를 위해 쓸 수 있지만 이외의 다양한 환경에서도 쓸 수 있다.(바벨, 타입스크립트, 노트, 리액트 등) 또한 라이브러리를 설치하면 별도의 설정 없이 바로 쓸수 있다.(필요한 경우도 존재)
브라우저가 아닌 CLI환경에서 테스트 코드를 실행시키는 특징이 있다.
그리고 이 jest는 CRA. 즉, Create react app을 이용해서 프로젝트를 만들 시에 작동된다.
근데 문제는 이 cra가 이제 지원을 하지 않기 때문에 권장하는 방식은 아니다.
그래서 또 하나인 vitest도 알아보자.
Vitest
Vitest는 JavaScript 및 TypeScript 프로젝트를 위한 모던 테스트 러너 및 테스트 프레임워크이다. Jest처럼 테스트 실행뿐만 아니라 모킹(mocking)과 스냅샷(snapshot)을 지원하며, Jest와 호환되는 API를 제공하고 있어서 Jest 사용자들이 쉽게 전환할 수 있도록 설계되었다.
특징
- 빠른 실행 속도: Vite의 모듈 서버와 esbuild를 활용하여 테스트 파일과 의존성을 빠르게 로드하고 실행한다.
- Jest와 호환: Vitest는 Jest의 API와 호환되는 설정과 명령어를 제공하여 Jest 사용자들이 손쉽게 전환할 수 있게 한다. 하지만 완벽한 호환성은 보장하지 않는다.
- 내장된 커버리지 지원: Istanbul을 통한 코드 커버리지 리포트를 내장 지원하여 추가 설정 없이 코드 커버리지를 측정할 수 있다.
- TypeScript와 Vue 지원: TypeScript 및 Vue 파일을 별도의 플러그인이나 설정 없이 직접 테스트할 수 있다.
병렬 테스트 실행: 테스트를 병렬로 실행하여 전체 테스트 시간을 단축시킬 수 있다. - Vitest는 특히 Vite, Vue, React, Svelte 등을 사용하는 프로젝트에 적합한 테스트 도구로, 개발자들이 더 빠르고 효율적으로 테스트 환경을 구축하고 관리할 수 있도록 도운다.
코드
간단한 코드 예제를 비교해 보자.
// Counter.jsx
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
지금 여기에 카운터를 1씩 올리는 간단한 코드가 있다. 이 코드를 테스팅해 본다고 가정해 보자.
jest로 테스트한다고 했을 때에는 코드가 다음처럼 테스팅 코드를 작성해 볼 수 있다.
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('버튼 클릭 시 숫자가 증가해야 한다', () => {
render(<Counter />); // 1️⃣ Counter 컴포넌트를 가상 DOM에 렌더링
const button = screen.getByText('증가'); // 2️⃣ 버튼 요소 찾기
const heading = screen.getByRole('heading'); // 3️⃣ 숫자 표시하는 <h1> 찾기
expect(heading).toHaveTextContent('0'); // 4️⃣ 처음엔 0인지 확인
fireEvent.click(button); // 5️⃣ 버튼 클릭 이벤트 발생
expect(heading).toHaveTextContent('1'); // 6️⃣ 클릭 후 1인지 확인
});
처음에 import를 써서 테스팅 라이브러리를 가져온다. 이후 테스팅할 컴포넌트도 import 해준다.
이후 test 함수를 선언한다. 이 함수는 Counter라는 컴포넌트를 랜더링 해준다.
각각 버튼 요소와 숫자를 표시하는 요소를 찾는다. 이를 변수에 넣는다.
이 변수를 expect 함수에 할당하고 테스팅하게 된다.
그다음. vitest로 테스트한다고 했을 때에는 코드가 다음처럼 테스팅 코드를 작성해 볼 수 있다.
설치 >
(이미 리액트 테스팅 라이브러리를 설치했다는 가정)
npm install -D vitest
vite.config.js 설정
(미리 vite.config.js에 설정해야 함.)
// vite.config.js
export default {
test: {
environment: 'jsdom',
},
};
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
import { describe, it, expect } from 'vitest';
describe('Counter 컴포넌트', () => { // 1️⃣ 테스트 그룹 (설명용)
it('버튼 클릭 시 숫자가 증가해야 한다', () => { // 2️⃣ 실제 테스트 단위
render(<Counter />); // 3️⃣ Counter 컴포넌트 렌더링
const button = screen.getByText('증가'); // 4️⃣ 버튼 찾기
const heading = screen.getByRole('heading'); // 5️⃣ <h1> 찾기
expect(heading).toHaveTextContent('0'); // 6️⃣ 초기값 0인지 확인
fireEvent.click(button); // 7️⃣ 버튼 클릭
expect(heading).toHaveTextContent('1'); // 8️⃣ 숫자 1로 바뀌었는지 확인
});
});
처음에 import를 써서 테스팅 라이브러리를 가져온다. 이후 테스팅할 컴포넌트도 import 해준다.
vitest을 쓰기 위해서도 import을 해준다.
이후 describe 함수를 이용해서 테스트해준다.
실제 테스트의 단위는 it이고, 이 안에도 Counter 함수가 렌더링 된다.
이후 코드는 위 jest랑 유사하다.
🔥 정리
실행 명령어 | npx jest | npx vitest |
환경 설정 | 따로 필요 X | vite.config.js에 test: { environment: 'jsdom' } 필요 |
문법 | test 사용 | describe, it 사용 (Jest도 사용 가능) |
속도 | 상대적으로 느림 | 매우 빠름 (Vite 기반) |
(참고할만한 영상 >
https://www.youtube.com/watch?v=TBFogFU7cls
배포 흐름
지금까지 전역 상태 관리와 테스트 코드에 대한 이야기를 했다.
마지막으로 "배포"에 대한 것을 다뤄보자.
지금까지는 로컬에서 돌아갔다. 이걸 배포해서 사람들도 사용하게 만들어보자.
배포를 하는 플랫폼은 많다.
vercel,netlify,cloudflare page,github page 등등.. 많다.
각 배포 플랫폼 특징 >
Vercel | Next.js 최적화, 쉬운 자동 배포 |
Netlify | 세팅 유연, CLI 지원 |
Cloudflare Pages | 빠른 배포, 글로벌 퍼포먼스 |
vercel은 next.js에 최적화가 되어있다. (왜냐하면 vercel이 next.js를 만들어서...) 또한 서버리스를 지원해 준다.
빌드 속도도 빠른 편이고 부분 유료이긴 하지만, 기본적으로 무료로 잘 이용할 수 있다. UI도 깔끔한 편.
배포도 되게 쉽다. (근데 리액트 배포에서 삐걱거리는 요소가 많다는 소문(?)을 들은 적이 있다.)
배포는 깃허브랑 연동해서 간단하게 push 되며, 자동으로 업데이트해 준다.
Vercel: Build and deploy the best web experiences with the AI Cloud – Vercel
Vercel gives developers the frameworks, workflows, and infrastructure to build a faster, more personalized web.
vercel.com
netlify도 배포할 수 있는 플랫폼이다. jamstack 전반을 지원한다.
다양한 플러그랑 CLI를 지원해 주고 세팅이 유연하다. 빌드 속도도 빠른 편이다.
배포도 되게 쉽다. 보통 나는 리액트를 이곳에 배포한다. next.js는 vercel에 배포한다.
배포는 깃허브랑 연동해서 간단하게 push 되며, 자동으로 업데이트해 준다.
https://app.netlify.com/teams/kss2002/projects
Netlify
app.netlify.com
Cloudflare Pages는 Cloudflare에서 제공하는 서버리스 기반의 정적 웹사이트 호스팅 서비스다.
깃허브에 연동해서 코드 배포가 가능하다.
Cloudflare Pages를 이용하면 글로벌 네트워크를 통해서 빠르게 웹사이트를 제공할 수 있다.
- 정적 웹사이트 호스팅: HTML, CSS, JavaScript, 이미지 등 정적 파일을 저장하고 제공합니다.
- 서버리스 기반: Cloudflare Workers를 기반으로 구축되어 확장성이 뛰어나고 유지 관리가 용이합니다.
- GitHub/GitLab 연동: GitHub 또는 GitLab 저장소와 연동하여 자동 배포 기능을 제공합니다.
- 글로벌 CDN: 전 세계에 분산된 Cloudflare의 CDN을 통해 빠른 로딩 속도를 제공합니다.
- 무료 플랜: 무료 플랜에서도 충분히 많은 기능을 사용할 수 있습니다.
- 커스텀 도메인 지원: 사용자 정의 도메인을 연결하여 사용할 수 있습니다.
- 개발자 친화적인 환경: 개발자 친화적인 인터페이스와 기능을 제공합니다.
Cloudflare Pages
Build your next application with Cloudflare Pages
pages.cloudflare.com
(공식 사이트)
요약
항목> Vercel / Netlify / Cloudflare Pages
장점 | 🔹 Next.js 최적화🔹 GitHub 연동 쉬움🔹 빠른 자동 배포🔹 간편한 미리보기 링크 | 🔹 다양한 프레임워크 지원🔹 Netlify Functions (서버리스) 지원🔹 CLI/설정 파일 세팅 유연 | 🔹 무료 CDN 속도 최강🔹 GitHub 연동 간편🔹 Cloudflare Workers로 글로벌 확장성 |
단점 | 🔸 Free 티어 제한 🔸 트래픽 커지면 요금 빠르게 증가 | 🔸 빌드 속도 느릴 수 있음🔸 요금제가 세분화되어 비용 예측 어려움 | 🔸 빌드 시간 느릴 수 있음🔸 일부 고급 기능 부족 (ex. A/B 테스트) |
추천 상황 | 🔥 Next.js 앱 + 빠른 배포 필요할 때 | 🔥 다양한 프레임워크 + 서버리스 연동할 때 | 🔥 글로벌 CDN + 서버리스 연동이 목표일 때 |
배포 난이도 | 매우 쉬움 (Vercel 사이트 접속 → GitHub 연동 끝) | 쉬움 (CLI 활용 시 좀 더 유연) | 쉬움 (사이트 접속 → GitHub 연동 끝) |
도메인 >
기본적으로 사이트를 빌드하면, 뒤에 네트리파이나 버셀 같은 것이 붙을 수밖에 없다.
예시 >
https://kss-portfolio-react.netlify.app/
포트폴리오 사이트 만들기 😜 React-Site
1. 꿈을 설계하고 디자인하다. 나는 공간을 만드는 것을 좋아한다. 어려을 때부터 나만의 공간을 만드는 것을 좋아했고 나만의 다락방을 좋아했다. 단 한 사람이라도 내가 만든 공간 속에서 영감
kss-portfolio-react.netlify.app
https://learn-next-puce-kss2002.vercel.app/
Home
learn-next-puce-kss2002.vercel.app
이런 식으로 뒤에 붙는다.
이걸 도메인을 사서 자신만의 사이트의 이름으로도 바꿀 수 있다.
보통은 이 도메인을 "가비아"에서 많이 사서 연동한다.
(네트리파이의 경우 domain-management에 가서 도메인이랑 dns를 등록해 주면 된다.)
https://unhosted.tistory.com/81
Netlify에 가비아에서 구매한 도메인 연결하기
netlify에서 호스팅을 하고 가비아에서 구매한 도메인을 연결하는 과정을 공유하려고 합니다. 먼저 netlify는 무료로 호스팅이 가능한 사이트입니다. 호스팅 하게 되면 도메인은 {사용자 지정 도메
unhosted.tistory.com
'카카오_구름 > 리액트' 카테고리의 다른 글
리액트 심화 커리큘럼 2주차 : React 비동기 처리와 데이터 패칭 라이브러리 (0) | 2025.06.20 |
---|---|
리액트 심화 커리큘럼 1주차 : Next.js App Router에서의 렌더링 전략 이해 (6) | 2025.06.06 |
4기 카카오 프론트 전체 스터디 - 리액트 4주차 (SPA 구조 + Next.js & TS 프리뷰) (2) | 2025.05.17 |
4기 카카오 프론트 전체 스터디 - 리액트 3주차 (HTTP 요청 + 주요 Hooks 정리) (0) | 2025.05.11 |
4기 카카오 프론트 전체 스터디 - 리액트 2주차 (조건부 렌더링, 리스트, 스타일링, 폼 다루기) (2) | 2025.05.01 |