diff --git "a/doeun/05.\354\213\261\352\270\200\355\206\244_\355\214\250\355\204\264" "b/doeun/05.\354\213\261\352\270\200\355\206\244_\355\214\250\355\204\264"
new file mode 100644
index 0000000..7c3906d
--- /dev/null
+++ "b/doeun/05.\354\213\261\352\270\200\355\206\244_\355\214\250\355\204\264"
@@ -0,0 +1,462 @@
+# 싱글톤 패턴 (Singleton Pattern)
+
+> "클래스의 인스턴스가 딱 하나만 만들어지도록 하고, 그 인스턴스에 대한 전역 접근을 제공한다." - GoF
+
+## 📌 싱글톤이란?
+
+싱글톤 패턴은 **클래스의 인스턴스가 단 하나만 존재하도록 보장하고, 어디서든 그 인스턴스에 접근할 수 있게 하는 디자인 패턴**이다.
+
+처음 들으면 "전역 변수 좀 고급스럽게 만드는 건가?" 싶은데, 맞기도 하고 틀리기도 하다.
+
+> **핵심: "인스턴스를 하나로 제한" + "전역 접근점 제공"**
+
+## 🎯 구조
+
+```mermaid
+classDiagram
+ class Singleton {
+ -static instance: Singleton
+ -constructor()
+ +static getInstance(): Singleton
+ +someMethod()
+ }
+
+ class ClientA {
+ +doSomething()
+ }
+
+ class ClientB {
+ +doSomething()
+ }
+
+ class ClientC {
+ +doSomething()
+ }
+
+ ClientA --> Singleton : getInstance()
+ ClientB --> Singleton : getInstance()
+ ClientC --> Singleton : getInstance()
+```
+
+**핵심 2요소:**
+
+1. **private constructor**: 외부에서 `new`로 생성하지 못하게 막는다
+2. **static getInstance()**: 인스턴스가 없으면 만들고, 있으면 기존 걸 돌려준다
+
+## 🔧 기본 구현
+
+### 가장 단순한 형태
+
+```javascript
+class Singleton {
+ static instance;
+
+ constructor() {
+ if (Singleton.instance) return Singleton.instance;
+ Singleton.instance = this;
+ }
+}
+
+const a = new Singleton();
+const b = new Singleton();
+console.log(a === b); // true
+```
+
+`constructor`에서 이미 인스턴스가 있으면 기존 걸 리턴한다. 몇 번을 `new`해도 같은 객체가 나온다.
+
+### 클로저를 활용한 방식
+
+```javascript
+const Singleton = (() => {
+ let instance;
+
+ function createInstance() {
+ return {
+ config: {},
+ getConfig(key) {
+ return this.config[key];
+ },
+ setConfig(key, value) {
+ this.config[key] = value;
+ }
+ };
+ }
+
+ return {
+ getInstance() {
+ if (!instance) {
+ instance = createInstance();
+ }
+ return instance;
+ }
+ };
+})();
+
+const a = Singleton.getInstance();
+const b = Singleton.getInstance();
+console.log(a === b); // true
+```
+
+IIFE + 클로저로 `instance`를 외부에서 접근 못하게 완전히 숨긴다. 첫 번째 방식보다 캡슐화가 강하다.
+
+### ES 모듈 방식 — JS에서 가장 자연스러운 싱글톤
+
+사실 이게 가장 중요한 포인트인데, **ES 모듈은 최초 import 시 한 번만 평가되고 이후에는 캐싱된 결과를 돌려준다.**
+
+```javascript
+// config.js
+const config = {
+ apiUrl: process.env.API_URL ?? 'http://localhost:3000',
+ env: process.env.NODE_ENV ?? 'development',
+};
+
+export default config;
+```
+
+이걸 여러 파일에서 `import config from './config'` 해도 전부 같은 객체를 참조한다. 클래스로 싱글톤을 구현할 필요 없이, **모듈 자체가 싱글톤 역할을 한다.**
+
+```mermaid
+flowchart LR
+ A[moduleA.js] -->|import config| C[config.js
최초 1회 평가]
+ B[moduleB.js] -->|import config| C
+ D[moduleC.js] -->|import config| C
+ C -->|같은 객체 반환| A
+ C -->|같은 객체 반환| B
+ C -->|같은 객체 반환| D
+```
+
+즉, JavaScript에서는 클래스 기반 싱글톤보다 **모듈 패턴이 사실상 표준**이다.
+
+## 🛠 실전 예제
+
+### Axios 인스턴스
+
+프론트엔드에서 가장 흔하게 볼 수 있는 싱글톤이다.
+
+```javascript
+// api/client.js
+import axios from 'axios';
+
+const apiClient = axios.create({
+ baseURL: import.meta.env.VITE_API_URL,
+ timeout: 10000,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+});
+
+apiClient.interceptors.request.use((config) => {
+ const token = localStorage.getItem('accessToken');
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ return config;
+});
+
+apiClient.interceptors.response.use(
+ (response) => response,
+ (error) => {
+ if (error.response?.status === 401) {
+ // 토큰 만료 처리
+ }
+ return Promise.reject(error);
+ }
+);
+
+export default apiClient; // 어디서 import해도 같은 인스턴스
+```
+
+인터셉터 설정이 한 곳에 모여있고, 앱 전체에서 동일한 설정으로 API를 호출한다. 이게 여러 인스턴스로 흩어져 있으면 인터셉터 관리가 지옥이 된다.
+
+### 로거 (Logger)
+
+```javascript
+// logger.js
+const LOG_LEVELS = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 };
+
+const logger = (() => {
+ const logs = [];
+ let level = LOG_LEVELS.INFO;
+
+ const format = (lvl, message) => {
+ const timestamp = new Date().toISOString();
+ return `[${timestamp}] [${lvl}] ${message}`;
+ };
+
+ return {
+ setLevel(newLevel) {
+ level = LOG_LEVELS[newLevel] ?? LOG_LEVELS.INFO;
+ },
+ debug(msg) {
+ if (level <= LOG_LEVELS.DEBUG) {
+ const entry = format('DEBUG', msg);
+ logs.push(entry);
+ console.log(entry);
+ }
+ },
+ info(msg) {
+ if (level <= LOG_LEVELS.INFO) {
+ const entry = format('INFO', msg);
+ logs.push(entry);
+ console.log(entry);
+ }
+ },
+ warn(msg) {
+ if (level <= LOG_LEVELS.WARN) {
+ const entry = format('WARN', msg);
+ logs.push(entry);
+ console.warn(entry);
+ }
+ },
+ error(msg) {
+ if (level <= LOG_LEVELS.ERROR) {
+ const entry = format('ERROR', msg);
+ logs.push(entry);
+ console.error(entry);
+ }
+ },
+ getLogs() {
+ return [...logs];
+ }
+ };
+})();
+
+export default logger;
+```
+
+**사용:**
+```javascript
+import logger from './logger';
+
+logger.setLevel('DEBUG');
+logger.info('앱 시작'); // [2024-03-15T...] [INFO] 앱 시작
+logger.debug('디버그 정보'); // [2024-03-15T...] [DEBUG] 디버그 정보
+logger.error('에러 발생!'); // [2024-03-15T...] [ERROR] 에러 발생!
+
+console.log(logger.getLogs()); // 전체 로그 이력
+```
+
+로거가 여러 개면 로그도 뿔뿔이 흩어진다. 앱 전체의 로그가 하나의 스트림으로 모여야 의미가 있다.
+
+### 상태 관리 Store
+
+이미 쓰고 있을 확률이 높다.
+
+```javascript
+// Zustand
+import { create } from 'zustand';
+
+const useAuthStore = create((set) => ({
+ user: null,
+ isAuthenticated: false,
+ login: (user) => set({ user, isAuthenticated: true }),
+ logout: () => set({ user: null, isAuthenticated: false }),
+}));
+```
+
+Zustand, Redux, Jotai 같은 상태 관리 라이브러리의 store도 결국 싱글톤이다. 앱 전체에서 하나의 상태 트리를 공유하니까.
+
+### 캐시 매니저
+
+```javascript
+// cache.js
+const cache = (() => {
+ const store = new Map();
+
+ return {
+ get(key) {
+ const item = store.get(key);
+ if (!item) return null;
+
+ if (item.expiry && item.expiry < Date.now()) {
+ store.delete(key);
+ return null;
+ }
+ return item.value;
+ },
+
+ set(key, value, ttlMs = null) {
+ store.set(key, {
+ value,
+ expiry: ttlMs ? Date.now() + ttlMs : null,
+ });
+ },
+
+ delete(key) {
+ store.delete(key);
+ },
+
+ clear() {
+ store.clear();
+ },
+
+ get size() {
+ return store.size;
+ }
+ };
+})();
+
+export default cache;
+```
+
+**사용:**
+```javascript
+import cache from './cache';
+
+// 5분 TTL로 캐싱
+cache.set('user:123', userData, 5 * 60 * 1000);
+
+// 다른 모듈에서
+const cached = cache.get('user:123');
+if (cached) {
+ // 캐시 히트
+}
+```
+
+캐시가 여러 개면 캐시가 아니다. 같은 키로 조회했는데 다른 결과가 나오면 의미가 없으니까.
+
+## ❓ 전역 변수랑 뭐가 다른데?
+
+솔직히 "의도적으로 제한을 건 전역 상태"가 싱글톤이다.
+
+| | 전역 변수 | 싱글톤 |
+|---|---|---|
+| 인스턴스 수 제한 | ❌ 없음 | ✅ 하나로 제한 |
+| 초기화 시점 제어 | ❌ 선언 시 즉시 | ✅ 첫 접근 시 (lazy) |
+| 캡슐화 | ❌ 다 열려있음 | ✅ 클로저/private으로 보호 |
+| 교체 가능성 | ❌ 어려움 | ✅ 인터페이스 뒤에 숨길 수 있음 |
+
+다만, 싱글톤을 무분별하게 쓰면 전역 변수랑 다를 바 없는 상태가 된다. 이게 싱글톤이 욕을 많이 먹는 이유다.
+
+## ⚠️ 싱글톤의 문제점
+
+### 1. 테스트가 어렵다
+
+```javascript
+function calculateDiscount(price) {
+ const config = AppConfig.getInstance(); // 강한 결합
+ const rate = config.get('DISCOUNT_RATE');
+ return price * rate;
+}
+```
+
+함수 시그니처만 보면 `price`만 넣으면 될 것 같은데, 실제로는 `AppConfig`에 의존한다. 테스트할 때 설정값을 바꾸고 싶어도, 싱글톤이 이미 초기화되어 있으면 까다롭다.
+
+### 2. 숨겨진 의존성
+
+```javascript
+// 이 함수가 뭘 의존하는지 시그니처만 봐서는 알 수 없다
+function processOrder(order) {
+ const logger = Logger.getInstance(); // 숨어있음
+ const config = AppConfig.getInstance(); // 숨어있음
+ const cache = Cache.getInstance(); // 숨어있음
+ // ...
+}
+```
+
+파라미터로 받으면 명시적인데, 싱글톤은 함수 내부에 숨어있다. 코드를 읽는 사람이 의존성을 놓치기 쉽다.
+
+### 3. SSR에서 요청 간 상태 공유 버그
+
+```mermaid
+flowchart LR
+ A[요청 A - 유저 김철수] -->|getInstance| S[Singleton
user: 김철수]
+ B[요청 B - 유저 이영희] -->|getInstance| S
+ S -->|응답| A
+ S -->|응답 - 김철수 정보 노출!| B
+```
+
+서버 사이드 렌더링 환경에서는 요청마다 독립된 상태가 필요한데, 싱글톤은 프로세스가 죽을 때까지 살아있다. **요청 A의 유저 정보가 요청 B에 노출되는** 치명적인 버그가 생길 수 있다.
+
+### 4. 단일 책임 원칙 위반
+
+싱글톤 클래스는 "자기 본래의 역할" + "인스턴스가 하나임을 보장하는 역할" 두 가지를 동시에 담당한다. 인스턴스 관리 책임이 클래스 자체에 들어가 있는 셈이다.
+
+## ✅ 대안: 의존성 주입
+
+싱글톤의 "하나만 존재" 자체가 나쁜 게 아니라, **접근 방식**이 문제다. 의존성을 외부에서 넣어주면 같은 목적을 달성하면서 단점을 피할 수 있다.
+
+### Before — 싱글톤 직접 참조
+
+```javascript
+function calculateDiscount(price) {
+ const config = AppConfig.getInstance(); // 강한 결합
+ const rate = config.get('DISCOUNT_RATE');
+ return price * rate;
+}
+```
+
+### After — 의존성 주입
+
+```javascript
+function calculateDiscount(price, config) {
+ const rate = config.get('DISCOUNT_RATE');
+ return price * rate;
+}
+
+// 프로덕션
+calculateDiscount(10000, prodConfig);
+
+// 테스트 — 자유롭게 교체
+calculateDiscount(10000, { get: () => 0.5 });
+```
+
+인스턴스를 하나만 만드는 건 **호출하는 쪽에서 제어**하면 된다. 클래스 스스로 "나는 하나만 존재해야 해"라고 강제할 필요는 없다.
+
+## 🤔 언제 사용할까?
+
+```mermaid
+flowchart TD
+ A[인스턴스가 2개 이상이면
버그가 나는가?] -->|Yes| B[상태 변경이 없거나
매우 제한적인가?]
+ A -->|No| E[싱글톤 불필요
일반 모듈로 충분]
+ B -->|Yes| C[앱 전역에서
접근이 필요한가?]
+ B -->|No| F[주의해서 사용
상태 관리 복잡해질 수 있음]
+ C -->|Yes| D[싱글톤 사용 ✅]
+ C -->|No| G[모듈 스코프로 충분]
+```
+
+### ✅ 사용하면 좋은 경우
+
+- **설정 관리**: 앱 전체가 같은 설정을 봐야 할 때
+- **로깅**: 로그가 하나의 스트림으로 모여야 할 때
+- **캐시**: 하나의 캐시 저장소를 공유해야 할 때
+- **커넥션 풀**: 리소스를 효율적으로 관리해야 할 때
+
+### ❌ 사용하지 않아도 되는 경우
+
+- **모듈로 충분할 때**: JS에서는 `export default`만으로 싱글톤 효과를 얻을 수 있다
+- **상태가 자주 바뀌는 경우**: 전역 mutable state는 디버깅 지옥의 시작
+- **테스트가 중요한 코드**: DI로 대체하는 편이 낫다
+- **SSR 환경**: 요청 간 상태 격리가 필수인 경우
+
+## 📊 장단점 정리
+
+### 장점
+
+1. **인스턴스 하나 보장**: 리소스 낭비 방지, 상태 일관성 유지
+2. **전역 접근점**: 어디서든 동일한 인스턴스에 접근 가능
+3. **지연 초기화(Lazy)**: 필요한 시점에 생성할 수 있음
+4. **메모리 효율**: 무거운 객체를 한 번만 생성
+
+### 단점
+
+1. **테스트 어려움**: 전역 상태에 의존하면 모킹이 까다로움
+2. **숨겨진 의존성**: 코드만 봐서 의존 관계를 파악하기 어려움
+3. **단일 책임 위반**: 본래 역할 + 인스턴스 관리를 동시에 담당
+4. **동시성 이슈**: SSR 등에서 요청 간 상태가 섞일 수 있음
+5. **오버엔지니어링**: JS에서는 모듈로 충분한 경우가 대부분
+
+## 💬 마무리
+
+싱글톤은 디자인 패턴 중 가장 단순하다. 그래서 남용하기도 쉽다.
+
+JavaScript에서는 **모듈 시스템이 이미 싱글톤처럼 동작**하기 때문에, 클래스로 직접 구현할 일은 생각보다 많지 않다. axios 인스턴스, Zustand store, 설정 파일 — 이미 쓰고 있는 것들이 전부 싱글톤이라는 걸 인식하는 게 더 중요하다.
+
+**싱글톤을 쓸지 말지보다, "이 상태가 정말로 전역이어야 하는가?"를 먼저 질문하자.** 대부분은 스코프를 좁히는 쪽이 답이다.
+
+---
+
+## 📚 참고
+
+- Head First Design Patterns 5장
+- GoF Design Patterns