[문제 해결] → 소프트웨어 / [판단 근거] → 데이터 / [실행 능력] → 인프라 / [수호] → 보안 / [성장] → 관리 이 다섯 가지 관점에서 HBM(High Bandwidth Memory)과 HBF(High Bandwidth Flash)를 체계적으로 분석한다.

 


1. 개요

AI 워크로드가 트릴리온 파라미터 모델 시대로 진입하면서, GPU의 연산 속도를 메모리가 따라잡지 못하는 Memory Wall 문제가 AI 인프라의 핵심 병목으로 부상했다. 이를 해결하기 위해 등장한 것이 HBM(DRAM 기반 초고대역 메모리)과 HBF(NAND 기반 초고대역 플래시)이다.

구분                               HBM (High Bandwidth Memory)                                                HBF (High Bandwidth Flash)
기반 매체 DRAM 3D NAND Flash
핵심 가치 초고속 대역폭 (최대 3.3 TB/s) 대용량 + 준-HBM급 대역폭
주요 세대 HBM → HBM2 → HBM2E → HBM3 → HBM3E → HBM4 1세대 (16-Hi, 512GB/stack 목표)
최적 워크로드 AI Training + Inference AI Inference (Read-intensive)
표준화 JEDEC JESD270-4 (2025.04) OCP 표준화 진행 중 (2026~)
양산 시점 HBM4: 2026 상반기 양산 개시 샘플 2026 하반기, 상용 2027 초
주요 플레이어 SK하이닉스, 삼성, Micron SK하이닉스, Sandisk, 삼성

2. [문제 해결] → 소프트웨어 관점

2.1 Memory Wall이라는 문제의 본질

소프트웨어 관점에서 Memory Wall은 단순한 하드웨어 병목이 아니라, AI 서비스의 응답 지연(Latency)과 처리량(Throughput)을 직접 결정하는 소프트웨어 성능 문제다. LLM 추론 시 KV Cache가 GPU 메모리에 상주해야 하는데, HBM 용량이 부족하면 모델 파라미터를 여러 GPU에 분산(Model Parallelism)해야 하고, 이때 GPU 간 통신 오버헤드가 발생한다.

2.2 HBM이 해결하는 문제

  • Training 단계: 수천 개의 GPU 코어가 동시에 메모리에 접근해야 하므로, HBM4의 2048-bit 인터페이스와 32개 독립 채널이 병렬 접근성을 극대화한다.
  • Inference 단계: Batch 처리 시 파라미터 로딩 → 연산 → 결과 반환의 파이프라인에서, HBM의 초저지연이 실시간 응답을 가능하게 한다.

2.3 HBF가 해결하는 문제

  • Inference에서의 용량 문제: HBM4 단일 스택이 최대 64GB인 반면, HBF는 단일 스택으로 최대 512GB를 목표로 한다. 트릴리온 파라미터 모델의 전체 가중치를 GPU에 인접 배치할 수 있게 된다.
  • KV Cache 확장: 추론 시 대화 맥락이 길어질수록 KV Cache가 폭증하는데, HBF가 이 영역을 흡수하면 HBM은 연산에 집중할 수 있다.

2.4 소프트웨어 아키텍처 시사점

┌─────────────────────────────────────────────────┐
│                   GPU Compute                    │
├────────────────────┬────────────────────────────┤
│   HBM (Hot Data)   │   HBF (Warm Data)          │
│  - Activations     │  - Model Weights (Read)     │
│  - Gradient Buffer │  - KV Cache Overflow        │
│  - Working Set     │  - Embedding Tables         │
├────────────────────┴────────────────────────────┤
│              SSD (Cold Data)                     │
│  - Checkpoint, Dataset, Log                     │
└─────────────────────────────────────────────────┘

소프트웨어 스택에서는 데이터 온도(Data Temperature) 에 따른 계층적 메모리 관리가 필수가 된다. SK하이닉스가 IEEE 논문에서 제시한 H3 아키텍처(HBM + HBF + GPU 하이브리드)가 이 패러다임의 구현체다.


3. [판단 근거] → 데이터 관점

3.1 정량적 스펙 비교

지표                        HBM3E                 HBM4                                                   HBF (1세대 목표)
대역폭/스택 ~1.2 TB/s 최대 2.0~3.3 TB/s ~1.6 TB/s
용량/스택 36 GB 48~64 GB 최대 512 GB
I/O 폭 1024-bit 2048-bit 병렬 sub-array
핀 속도 ~9.6 Gbps 8~13 Gbps N/A (NAND 기반)
전력 효율 기준 HBM3E 대비 40% 개선 HBM 대비 성능/와트 2.69배 (시뮬레이션)
단가 높음 매우 높음 상대적 저가 (NAND 기반)

3.2 핵심 판단 데이터

시장 규모: 글로벌 HBM 시장은 2026년 약 580억 달러로 전망된다. HBF는 2030년 수십억 달러 규모의 시장으로 성장할 것으로 예측된다.

공급 구조: SK하이닉스가 HBM 시장의 약 62%를 점유하며, 삼성과 Micron이 추격 중이다. HBM4의 2026년 물량은 하이퍼스케일러 장기 계약으로 사실상 전량 소진되어, 비할당 물량의 시장 유통은 2027년 이후로 전망된다.

DRAM 가격 영향: AI 수요로 인해 2025년 한 해 동안 메모리 가격이 200% 이상 상승했으며, HBM이 범용 DRAM 생산 능력을 잠식하는 구조적 전환이 진행 중이다. Micron 기준 HBM과 DDR5의 웨이퍼 변환 비율은 3:1로, HBM 증산이 범용 메모리 공급을 직접적으로 압박한다.

3.3 판단 프레임워크

기술사적 판단에서 중요한 것은 "HBM vs HBF" 가 아니라 "HBM + HBF의 계층적 최적화" 라는 점이다. 양자는 대체재가 아니라 보완재 관계이며, 이를 뒷받침하는 근거는 다음과 같다:

  1. 워크로드 특성 차이: Training은 Read/Write 균형이 필요하므로 HBM이 필수, Inference는 Read 위주이므로 HBF가 적합
  2. TCO(Total Cost of Ownership): HBF 도입 시 동일 추론 성능을 더 낮은 비용으로 달성 가능
  3. 용량-대역폭 트레이드오프: HBM은 대역폭 우위, HBF는 용량 우위 → 혼합 배치가 최적

4. [실행 능력] → 인프라 관점

4.1 물리적 구현 아키텍처

HBM과 HBF 모두 TSV(Through-Silicon Via) 기반 3D 적층 기술과 인터포저(Interposer) 를 통한 GPU 근접 배치라는 공통된 인프라 패턴을 사용한다.

┌──────────────────── Interposer ────────────────────┐
│                                                     │
│  ┌─────────┐  ┌─────────┐          ┌─────────┐    │
│  │ HBM4    │  │ HBM4    │          │ HBF     │    │
│  │ Stack×8 │  │ Stack×8 │   GPU    │ Stack×8 │    │
│  │ (DRAM)  │  │ (DRAM)  │          │ (NAND)  │    │
│  └────┬────┘  └────┬────┘          └────┬────┘    │
│       │TSV         │TSV                 │TSV       │
│  ┌────┴────┐  ┌────┴────┐          ┌────┴────┐    │
│  │Base Die │  │Base Die │          │Base Die │    │
│  │(Logic)  │  │(Logic)  │          │(Logic)  │    │
│  └─────────┘  └─────────┘          └─────────┘    │
│                                                     │
└─────────────────────────────────────────────────────┘

4.2 HBM4의 인프라 혁신 포인트

  • Logic Base Die: HBM4부터 베이스 다이가 12nm~5nm 로직 공정으로 전환되어, 메모리 스택 자체가 ECC, 신호 컨디셔닝 등의 연산을 수행하는 능동형 구조로 변화했다. TSMC가 SK하이닉스에 12nm 로직 베이스 다이를 공급한다.
  • Advanced MR-MUF: SK하이닉스의 Mass Reflow Molded Underfill 기술로 개별 DRAM 웨이퍼를 30μm까지 박형화하여 JEDEC 775μm 높이 제한 내에 16-Hi 적층을 실현했다.
  • D2C 냉각: HBM4의 열밀도 문제를 해결하기 위해 Direct-to-Chip 액체 냉각이 필수 인프라로 부상했다.

4.3 HBF의 인프라 과제

  • 적층 복잡도: 12-Hi HBF 스택은 238-layer NAND 기준으로 총 2,866개 레이어, 321-layer NAND 16-Hi 스택의 경우 5,136개 레이어에 달하며, TSV 배선의 복잡도가 기하급수적으로 증가한다.
  • 쓰기 성능 한계: NAND 특성상 쓰기 속도가 느리다. KV Cache처럼 쓰기가 발생하는 워크로드에서는 베이스 다이의 컨트롤러 성능 고도화가 선결 과제다.
  • GPU 벤더 협력: 인터포저를 통한 GPU-HBM-HBF 연결 조율에 NVIDIA 등 GPU 벤더의 심층 관여가 필요하다. NVIDIA Rubin 플랫폼이 첫 적용 대상으로 거론된다.

4.4 데이터센터 인프라 영향

  • 전력: AI 데이터센터의 전력 소비가 급증하는 상황에서, HBM4의 40% 전력 효율 개선과 HBF의 성능/와트 2.69배 향상은 데이터센터 설계의 핵심 변수다.
  • 냉각: HBM4의 열밀도가 기존 메모리 대비 높아, 공랭 → 액랭 전환이 가속된다.
  • 물리적 레이아웃: HBM4는 HBM3 대비 물리적 풋프린트가 커져, 인터포저 설계와 기판 레이아웃의 재설계가 필요하다.

5. [수호] → 보안 관점

5.1 하드웨어 수준 보안 위협

  • Row Hammer 공격: DRAM 셀 간 전기적 간섭을 이용한 비트 플립 공격. HBM4는 JEDEC 표준에 DRFM(Directed Refresh Management) 을 포함하여 Row Hammer 리스크를 완화한다.
  • 사이드 채널 공격: 고밀도 적층 구조에서 열/전력 패턴을 통한 데이터 유출 가능성. 물리적 근접성이 높아질수록 공격 표면이 넓어진다.
  • Logic Base Die의 이중성: HBM4의 로직 베이스 다이는 성능 최적화에 기여하지만, 동시에 메모리 스택 내에 연산 기능이 내장되므로 신뢰할 수 있는 실행 환경(TEE) 과의 통합 설계가 보안 관점에서 중요해진다.

5.2 공급망 보안

  • 지정학적 리스크: HBM의 핵심 기술이 한국(SK하이닉스, 삼성)과 미국(Micron)에 집중되어 있으며, TSMC(대만)가 로직 베이스 다이를 공급한다. 미중 반도체 수출 규제가 HBM 공급망에 직접적 영향을 미친다.
  • 표준화와 벤더 종속: HBF는 아직 표준화 초기 단계(OCP 기반)이므로, 특정 벤더의 독점 사양이 시장을 지배할 리스크가 있다. SK하이닉스-Sandisk의 MoU 기반 표준화 컨소시엄이 이를 방지하려는 움직임이다.

5.3 데이터 보안

  • AI 모델 가중치 보호: HBM/HBF에 상주하는 모델 파라미터는 기업의 핵심 지적재산. 메모리 덤프를 통한 모델 탈취 방지가 필수이며, 메모리 암호화(Memory Encryption) 기능이 중요한 보안 요구사항이다.
  • RAS(Reliability, Availability, Serviceability): HBM4 표준에 강화된 RAS 기능이 포함되어, 데이터센터 운영 중 메모리 오류의 감지·격리·복구 능력이 향상된다.

6. [성장] → 관리 관점

6.1 기술 로드맵 관리

시기                          HBM                                                                                      HBF
2026 상반기 HBM4 양산 개시 (SK하이닉스, 삼성, Micron) -
2026 하반기 HBM4 본격 출하, HBM4E 개발 HBF 샘플 출하
2027 HBM4E 양산, 16-Hi 스택 AI 추론 서버 첫 탑재
2028~2029 HBM5 (NVIDIA Feynman 대응) 2세대 HBF (대역폭 2배, 용량 512GB+)
2030 HBM6 HBF 시장 수십억 달러 규모

6.2 투자 및 비용 관리

  • CAPEX 관점: HBM 생산 확대는 범용 DRAM/NAND 생산 라인과의 자원 경합을 유발한다. Micron 기준 HBM:DDR5 웨이퍼 변환비 3:1은 경영 의사결정에서 핵심 데이터다.
  • TCO 최적화: H3 아키텍처(HBM+HBF) 도입 시, 동일 추론 성능 대비 총 소유 비용을 절감할 수 있으며, 이는 클라우드 사업자의 AI 서비스 가격 경쟁력에 직결된다.
  • 수율 관리: HBM4의 16-Hi 적층에서 단일 다이 불량이 전체 스택 폐기로 이어질 수 있어, Known Good Die(KGD) 테스트와 수율 관리가 수익성의 핵심이다.

6.3 에코시스템 성장 관리

  • 표준화 거버넌스: HBM은 JEDEC, HBF는 OCP 기반으로 표준화가 진행되며, 두 표준 간의 상호운용성 확보가 에코시스템 성장의 관건이다.
  • 인재 확보: TSV, 인터포저, 어드밴스드 패키징 분야의 전문 인력 수요가 급증하고 있으며, 이는 반도체 산업 전반의 인력 전쟁으로 확대된다.
  • 지속가능성: AI 데이터센터의 에너지 소비가 사회적 이슈로 부상하면서, 메모리 기술의 전력 효율은 ESG 관점에서도 관리 대상이 된다.

7. 종합 — 기술사 관점의 핵심 메시지

7.1 아키텍처 설계 원칙

HBM과 HBF는 "대체"가 아니라 "계층화" 의 관계다. 소프트웨어 아키텍처에서 L1/L2/L3 캐시가 공존하듯, AI 인프라에서도 HBM(Hot Layer) → HBF(Warm Layer) → SSD(Cold Layer) 의 메모리 계층이 자리 잡는다.

7.2 의사결정 매트릭스

판단 기준                                  HBM 선택                                               HBF 선택                             하이브리드(H3)
Training 중심 ✅ 필수
Inference 중심 ✅ 필요 ✅ 적극 활용 ✅ 최적
비용 민감 ⚠️ 고비용 ✅ 상대적 저가 ✅ TCO 최적화
용량 우선 ⚠️ 64GB/stack 한계 ✅ 512GB/stack ✅ 용량+속도 균형
Edge 배포 ⚠️ 전력 부담 ✅ 저전력 적합

7.3 기술사 시험 핵심 키워드

  • Memory Wall, TSV, Interposer, 3D 적층, Logic Base Die
  • JEDEC JESD270-4, OCP 표준화, H3 아키텍처
  • Data Temperature 기반 계층적 메모리 관리
  • Row Hammer / DRFM, RAS, 공급망 보안
  • TCO, KGD, 수율 관리, 웨이퍼 변환비

 

LIST

"You Know, for Search." — Elasticsearch 최초 공개 블로그 포스트 제목 (2010년 2월 8일)

검색창에 키워드를 입력하면 0.1초 만에 결과가 뜬다. GitHub에서 수백만 줄의 코드를 검색하고, Netflix에서 85개 이상의 클러스터가 실시간 로그를 분석하고, Wikipedia에서 수천만 문서를 즉시 찾아낸다. 이 모든 것의 뒤편에 Elasticsearch가 있다.

이 글에서는 Elasticsearch가 왜 만들어졌고, 어떻게 단순한 검색 엔진에서 엔터프라이즈 데이터 플랫폼으로 성장했는지, 그리고 2026년 현재 어떤 위치에 있는지를 정리한다.


1. 탄생: 아내의 요리 레시피를 검색하고 싶었다 (2004~2010)

Compass — 전신의 이야기

Elasticsearch의 창시자 Shay Banon은 이스라엘 출신 개발자다. 2004년, 아내가 런던의 Le Cordon Bleu 요리학교에 다니던 시절, Banon은 아내의 방대한 레시피 컬렉션을 검색할 수 있는 엔진을 만들고 싶었다. 이것이 Compass라는 Java 기반 검색 프레임워크의 시작이었다.

Compass는 Apache Lucene 위에 구축된 검색 라이브러리였다. Lucene은 Doug Cutting이 만든 강력한 전문 검색(full-text search) 엔진이지만, 그 자체로는 분산 처리도, REST API도, 클러스터링도 지원하지 않았다. Compass는 이런 갭을 메우려 했지만, Banon은 근본적인 한계를 느꼈다.

"처음부터 분산 시스템으로 다시 만들자"

Compass 3버전을 개발하던 중, Banon은 전면 재작성이 필요하다고 결론 내렸다. 핵심 설계 원칙은 세 가지였다:

  1. 처음부터 분산 아키텍처: 단일 노드가 아니라 클러스터 기반
  2. JSON over HTTP: Java뿐 아니라 모든 언어에서 접근 가능한 RESTful API
  3. 실시간(Near Real-Time) 검색: 문서 색인 후 거의 즉시 검색 가능

Banon은 당시 잘 다니던 Gigaspaces를 퇴사하고 약 2년간 전업으로 개발에 매진했다. 그리고 2010년 2월 8일, "You Know, for Search"라는 제목의 블로그 포스트와 함께 Elasticsearch 0.4.0을 오픈소스로 공개했다.


2. 성장: ELK 스택의 탄생과 폭발적 채택 (2010~2018)

커뮤니티의 자발적 생태계

Elasticsearch가 공개되자, 커뮤니티에서 자연스럽게 생태계가 만들어졌다:

  • Jordan Sissel이 다양한 소스에서 데이터를 수집·변환하는 Logstash를 개발
  • Rashid Khan이 Elasticsearch 데이터를 시각화하는 Kibana를 개발

세 사람은 교류하다가 결국 힘을 합쳤다. ELK Stack(Elasticsearch + Logstash + Kibana)의 탄생이다. 이 조합은 로그 분석의 사실상 표준이 되었다.

회사 설립과 급성장

시점이벤트
2010.02 Elasticsearch 0.4.0 오픈소스 공개
2012.02 Elasticsearch BV 암스테르담에서 설립 (Shay Banon, Steven Schuurman, Uri Boness, Simon Willnauer)
2013 Series A $10M (Benchmark Capital), Kibana 공식 출시
2014 Series C $70M, 총 투자 $104M
2015 사명을 Elastic으로 변경, Elastic Cloud 출시, Beats(경량 데이터 수집기) 추가 → ELK가 Elastic Stack으로 확장
2017 Swiftype 인수 (엔터프라이즈 검색), Opbeat 인수 (APM)
2018.10 NYSE 상장 (ESTC), 상장 첫날 주가 거의 2배, 기업가치 약 $5B

특히 인상적인 것은 2012년 당시 월 20만 다운로드라는 수치였다. Index Ventures의 투자자들은 "포트폴리오 기업의 CTO들이 Elasticsearch를 '기적 같은 소프트웨어'라고 부르며 입을 모았다"고 회상한다.

글로벌 분산 조직이라는 실험

Elastic은 설립 초기부터 완전 분산 조직을 운영했다. 개발팀의 90% 이상이 실리콘밸리 밖에 분포했다. 특정 사무실에 모이지 않고 전 세계에서 최고의 인재를 채용하는 이 모델은, Elasticsearch 자체의 "분산 아키텍처" 철학과도 맞닿아 있다.


3. Elasticsearch의 핵심 아키텍처 — 왜 빠른가

역색인(Inverted Index)

Elasticsearch가 빠른 핵심 이유는 역색인 구조다. 일반 데이터베이스가 "문서 → 단어"로 저장한다면, Elasticsearch는 **"단어 → 문서"**로 저장한다.

일반 DB 방식 (순방향):
  문서1: "Elasticsearch는 검색 엔진이다"
  문서2: "Redis는 캐시 엔진이다"

역색인 방식:
  "검색"   → [문서1]
  "캐시"   → [문서2]
  "엔진"   → [문서1, 문서2]
  "Elasticsearch" → [문서1]
  "Redis"  → [문서2]

이것은 책의 맨 뒤에 있는 **색인(Index)**과 같은 원리다. 수백만 페이지를 처음부터 읽는 대신, 색인에서 키워드를 찾아 해당 페이지로 바로 이동한다. 이 구조 덕분에 데이터가 아무리 많아도 검색 시간이 선형으로 증가하지 않는다.

분산 아키텍처: 샤드와 레플리카

 
 
Elasticsearch 클러스터
├── Node 1
│   ├── Shard 0 (Primary)
│   └── Shard 2 (Replica)
├── Node 2
│   ├── Shard 1 (Primary)
│   └── Shard 0 (Replica)
└── Node 3
    ├── Shard 2 (Primary)
    └── Shard 1 (Replica)
  • 인덱스는 여러 **샤드(Shard)**로 분할되어 노드에 분산 저장
  • 각 샤드는 레플리카를 가져서 노드 장애 시에도 데이터 유실 없음
  • 노드를 추가하면 샤드가 자동으로 재배치 → 수평 확장이 핵심

Near Real-Time 검색

문서를 색인하면 약 1초 이내에 검색 가능해진다. "실시간"이 아니라 "거의 실시간(NRT)"이라고 표현하는 이유는, 색인된 문서가 메모리 버퍼에서 세그먼트로 flush되는 refresh interval(기본 1초)이 있기 때문이다.

JSON 문서 기반 — 스키마리스

Elasticsearch는 데이터를 JSON 문서로 저장한다. 테이블 정의도, 스키마 마이그레이션도 필요 없다. 서로 다른 구조의 문서를 같은 인덱스에 넣을 수 있다.

 
json
// 상품 문서
{
  "name": "맥북 프로 16인치",
  "price": 3490000,
  "category": "노트북",
  "specs": {
    "cpu": "M4 Pro",
    "ram": "36GB"
  },
  "tags": ["apple", "laptop", "professional"],
  "created_at": "2025-11-15T09:00:00Z"
}

4. 활용 사례 — 검색 그 이상의 7가지 영역

① 전문 검색 (Full-Text Search)

Elasticsearch의 본업이다. 이커머스 상품 검색, CMS 콘텐츠 검색, 위키 검색 등에서 자동완성, 오타 보정(fuzzy), 형태소 분석, 가중치 기반 랭킹을 지원한다.

 
 
json
// 오타 허용 검색 (fuzzy)
GET /products/_search
{
  "query": {
    "match": {
      "name": {
        "query": "맥북프로",
        "fuzziness": "AUTO"
      }
    }
  },
  "highlight": {
    "fields": { "name": {} }
  }
}

한국어 검색의 경우 nori 분석기(은전한닢 기반)를 사용하면 형태소 단위로 토큰화할 수 있다:

 
 
json
PUT /products
{
  "settings": {
    "analysis": {
      "analyzer": {
        "korean": {
          "type": "custom",
          "tokenizer": "nori_tokenizer",
          "filter": ["nori_readingform", "lowercase"]
        }
      }
    }
  }
}

② 로그 분석 (ELK/Elastic Stack)

가장 널리 알려진 활용 사례다. MSA 환경에서 9개, 10개의 마이크로서비스가 각각 로그를 뿜어내면, 이를 한곳에 모아서 검색·분석·시각화해야 한다.

[서비스 A] ──┐
[서비스 B] ──┤──→ [Beats/Logstash] ──→ [Elasticsearch] ──→ [Kibana 대시보드]
[서비스 C] ──┘     (수집·변환)            (저장·색인)         (시각화·알림)

Netflix는 85개 이상의 Elasticsearch 클러스터에 800개 이상의 프로덕션 노드를 운영하며, 고객 서비스 운영과 보안 로그를 모니터링한다.

③ APM & Observability (관측 가능성)

Elastic APM은 애플리케이션의 응답 시간, 에러율, 트랜잭션 추적을 제공한다. 2026년 기준, Dimensional Research 조사에 따르면 60%의 기업이 자사의 관측 가능성 수준을 "성숙" 또는 "전문가"로 평가하고 있으며, 이는 전년 41%에서 크게 상승한 수치다.

Elastic 9.x에서는 OpenTelemetry 네이티브 지원(Managed OTLP Endpoint GA)이 추가되어, OTel SDK에서 직접 데이터를 전송할 수 있다.

④ 보안 분석 (SIEM)

Elasticsearch는 SIEM(Security Information and Event Management) 용도로도 강력하다. 실시간으로 보안 이벤트를 수집·분석하여 위협을 탐지한다. Symantec 같은 사이버보안 기업이 실시간 위협 탐지에 Elasticsearch를 사용한다.

⑤ 비즈니스 분석 & 실시간 대시보드

Kibana와 결합하면 실시간 비즈니스 대시보드를 구축할 수 있다. 사용자 행동 분석, 매출 추이, A/B 테스트 결과 모니터링 등에 활용된다.

⑥ 지리공간 검색

Elasticsearch는 geo_point, geo_shape 타입을 지원하여 위치 기반 검색이 가능하다. "반경 5km 내 음식점", "특정 지역 내 매장"과 같은 쿼리를 밀리초 단위로 처리한다.

 
 
json
GET /restaurants/_search
{
  "query": {
    "geo_distance": {
      "distance": "5km",
      "location": { "lat": 37.4979, "lon": 127.0276 }
    }
  }
}

⑦ AI 벡터 검색 & RAG (2024~)

2026년 현재, Elasticsearch는 Search AI 플랫폼으로의 전환을 선언했다. 핵심은 벡터 검색이다:

  • BBQ 벡터 양자화가 Elastic 9.1부터 기본값으로 적용되어 메모리를 95% 이상 절감
  • DiskBBQ는 디스크에서 벡터를 읽어도 20ms 이하 지연
  • 2025년 10월 Jina AI 인수로 다국어 임베딩 모델을 네이티브 통합

LLM의 RAG(Retrieval-Augmented Generation) 파이프라인에서 Elasticsearch가 검색·리트리벌 레이어 역할을 맡는 구조가 빠르게 확산 중이다:

 
 
[사용자 질문] → [임베딩 모델] → [Elasticsearch 벡터 검색]
                                     ↓ 관련 문서 top-K
                              [LLM에 컨텍스트로 전달] → [답변 생성]

5. 라이선스 변천: Redis와 닮은 여정

Elasticsearch의 라이선스 역사는 Redis와 놀라울 정도로 유사하다:

시점변경 내용배경
2010~2020 Apache 2.0 완전한 오픈소스
2021.01 SSPL + Elastic License 듀얼 AWS ElastiCache의 무임승차 대응
2021.04 AWS가 OpenSearch로 포크 커뮤니티 분열
2024.08 AGPLv3 추가 (트리플 라이선스) 오픈소스 복귀, 커뮤니티 신뢰 회복

Redis가 Valkey 포크를 촉발한 것처럼, Elasticsearch는 OpenSearch 포크를 촉발했다. 오픈소스 프로젝트와 상업적 지속 가능성 사이의 긴장은 인프라 소프트웨어의 구조적 문제다.


6. LabNote ELN 같은 MSA에서의 실무 적용 포인트

MSA 기반 시스템에서 Elasticsearch를 도입할 때 고려해야 할 실무 패턴들:

데이터 동기화 전략

RDBMS가 원본(Source of Truth)이고 Elasticsearch는 검색용 보조 저장소인 경우가 대부분이다. 동기화 방식은 세 가지:

1) 애플리케이션 레벨 이중 쓰기 (단순하지만 일관성 위험)
   DB UPDATE → ES INDEX (트랜잭션 보장 불가)

2) CDC (Change Data Capture) — 추천
   DB → Debezium/Kafka Connect → Elasticsearch
   (DB 변경 로그를 캡처하여 비동기 동기화)

3) 주기적 배치 동기화
   Scheduler → DB 전체/변경분 조회 → ES Bulk Index
   (지연 허용 가능한 경우)

인덱스 설계 원칙

 
json
// ❌ RDBMS 사고방식 — 정규화하여 여러 인덱스로 분리
// products 인덱스, categories 인덱스를 JOIN? → ES에는 JOIN이 없다

// ✅ ES 사고방식 — 비정규화하여 검색에 최적화된 단일 문서
{
  "product_name": "LabNote Pro",
  "category_name": "전자연구노트",   // 카테고리를 문서에 포함
  "department": "R&D",              // 부서 정보도 포함
  "author": { "name": "김연구", "email": "kim@lab.com" }
}

검색 품질 튜닝

 
json
// 다중 필드 검색 + 가중치
GET /experiments/_search
{
  "query": {
    "multi_match": {
      "query": "세포 배양 프로토콜",
      "fields": ["title^3", "content", "tags^2", "author.name"],
      "type": "best_fields",
      "fuzziness": "AUTO"
    }
  }
}

7. Elasticsearch vs 대안 — 선택 기준

도구                          강점                                                                                   약점                              적합한 경우
Elasticsearch 전문 검색 + 분석 + 보안 통합, 거대 생태계 운영 복잡성, 높은 메모리 사용 대규모 검색·로그·보안 통합
OpenSearch ES 7.x 호환, Apache 2.0, AWS 관리형 ES 최신 기능 부재 AWS 환경 + 오픈소스 선호
Meilisearch 설정 최소, 50ms 이하 응답, 단일 개발자로 운영 가능 분석·보안 기능 없음 중소규모 앱 검색
Apache Solr 18년+ 안정성, 진정한 오픈소스 ES 대비 커뮤니티 축소 복잡한 문서 검색, 이커머스
Grafana Loki 로그 전용, 저비용, 라벨 기반 인덱싱 전문 검색 불가 메트릭 중심 로그 분석
Splunk 엔터프라이즈 SIEM 최강 매우 높은 비용 보안 최우선 대기업

PostgreSQL의 Full-Text Search는?

소규모 프로젝트에서는 PostgreSQL의 tsvector, tsquery만으로도 충분할 수 있다. 별도 인프라 없이 DB 하나로 해결된다. 하지만 데이터가 수백만 건을 넘어가거나, 자동완성·오타 보정·형태소 분석·실시간 로그 분석이 필요하다면 Elasticsearch의 영역이다.


8. 2026년의 Elasticsearch — Search AI Company

Elastic은 스스로를 "Search AI Company"로 포지셔닝하고 있다. 세 가지 축으로 사업을 전개한다:

  1. Enterprise Search: 기업 내 검색, 앱 검색, RAG 기반 AI 검색
  2. Observability: 로그·메트릭·트레이스 통합 관측
  3. Security: 위협 탐지, SIEM, 엔드포인트 보안

2025년 10월 Jina AI 인수로 다국어 임베딩 모델을 내재화했고, ES|QL(Elasticsearch Query Language)이 프로덕션 준비 완료 상태로 올라왔다. 검색 엔진에서 시작한 프로젝트가 AI 시대의 리트리벌 인프라로 자리잡는 중이다.


마무리: 레시피 검색에서 세계의 데이터 검색으로

Shay Banon은 아내의 요리 레시피를 검색하고 싶었을 뿐이다. 그 작은 필요가 Compass를 만들었고, Compass의 한계가 Elasticsearch를 낳았다. "You Know, for Search"라는 가벼운 제목 뒤에는, **"분산 시스템에서 빠르게 데이터를 찾는 문제"**라는 보편적이고 근본적인 과제가 있었다.

2026년 현재, 전 세계 18,000개 이상의 기업이 Elasticsearch를 사용하고 있고, Netflix의 85개 클러스터부터 개인 블로그의 검색 기능까지 그 스케일은 다양하다. Redis가 "캐시의 기본"이라면, Elasticsearch는 **"검색의 기본"**이다. 백엔드 개발자라면 언젠가 반드시 마주치게 되는 기술이고, 그 깊이를 알수록 시스템 설계의 선택지가 넓어진다.


참고 자료: Elasticsearch Wikipedia, Index Ventures Blog, Elastic 공식 블로그, Grokipedia, ByteByteGo, Knowi, Logit.io

LIST

"아내는 내가 처음 몇 년간 MacBook Air 11인치를 들고 화장실에 앉아서 Redis를 만들었다고 주장한다. 그녀가 틀렸다고 말하고 싶지만, 완벽하게 맞는 이야기다." — Salvatore Sanfilippo (antirez)

오늘날 거의 모든 웹 서비스의 뒤편에는 Redis가 있다. Stack Overflow 개발자 설문에서 4년 연속 "가장 사랑받는 데이터베이스"로 선정되고, Docker Hub에서 매일 가장 많이 실행되는 데이터베이스이며, 2026년 1월 기준 ARR(연간 반복 매출) 3억 달러를 돌파한 이 프로젝트는, 시칠리아의 한 개발자가 자기 스타트업의 성능 문제를 해결하려다 시작된 사이드 프로젝트였다.


1. 탄생: 느린 데이터베이스에 대한 분노 (2009)

LLOOGG — 모든 것의 시작

Redis의 창시자 Salvatore Sanfilippo(닉네임 antirez)는 시칠리아 출신의 이탈리아 개발자다. 2009년, 그는 LLOOGG라는 실시간 웹 로그 분석 스타트업을 운영하고 있었다. 문제는 간단했다. 실시간으로 쏟아지는 로그 데이터를 기존 관계형 데이터베이스로는 감당할 수 없었던 것이다.

MySQL이나 PostgreSQL 같은 RDBMS는 디스크 기반이다. 매 요청마다 디스크 I/O가 발생하고, 트랜잭션 오버헤드가 붙는다. 실시간 분석처럼 초당 수천 건의 읽기/쓰기가 필요한 워크로드에서는 구조적 한계가 있었다.

antirez는 먼저 Tcl로 프로토타입을 만들었다. "키-값 쌍을 메모리에 올려놓고 직접 조작하면 되지 않을까?" 이 단순한 아이디어가 Redis의 출발점이었다. 곧 C 언어로 재작성하고, 첫 번째 자료구조인 List를 구현했다. 내부에서 몇 주간 사용해본 뒤 확신이 생겨 Hacker News에 공개했다.

이름의 의미

Redis = Remote Dictionary Server. 원격에서 접근 가능한 딕셔너리(해시맵) 서버라는 뜻이다. 이름 자체가 Redis의 본질을 정확히 설명한다. 테이블도 없고, SQL도 없다. 있는 것은 키와 값, 그리고 그 위에서 동작하는 자료구조 명령어뿐이다.


2. 성장: 사이드 프로젝트에서 인프라의 표준으로

2009 — Ruby 커뮤니티의 열광

Redis가 처음 주목받은 곳은 Ruby on Rails 생태계였다. 2009년, GitHub의 CEO Chris Wanstrath가 Redis 기반의 백그라운드 잡 큐 Resque를 만들었다. Rails 세계에서 Resque는 폭발적인 인기를 끌었고, 2012년에 등장한 후속작 Sidekiq 역시 Redis 위에 구축되었다. GitHub과 Instagram이 초기 도입 기업이었다.

2010 — Twitter와 VMware

Twitter가 Redis를 타임라인 페이지에 도입하면서 Redis의 위상이 달라졌다. 흥미로운 건, antirez가 Redis 공개 직후 Retwis라는 Twitter 클론을 Redis 데모용으로 만들었다는 점이다. 본인이 만든 장난감이 실제 Twitter에 채택된 셈이다.

같은 해, VMware에서 연락이 왔다. "우리가 당신을 고용하겠다. 하던 일을 그대로 하면 된다. 웹사이트에 Redis가 VMware의 후원을 받는다고만 적어달라." 약 1년간 무급 사이드 프로젝트였던 Redis가 드디어 공식 후원을 받게 된 순간이었다.

2010~2015 — 자료구조의 확장

이 시기에 Redis는 단순한 키-값 저장소를 넘어 다목적 자료구조 서버로 진화했다:

시기                                    추가된 자료구조                                                                 용 사례
초기 String, List 캐시, 큐
2010~2011 Hash, Set, Sorted Set 세션 관리, 리더보드, 태그
2012+ HyperLogLog 고유 방문자 수 추정
2016+ Streams 이벤트 소싱, 메시지 큐
2018+ Modules (RedisJSON, RediSearch) 문서 DB, 검색 엔진
2024+ Vector Set AI 벡터 유사도 검색

VMware(2010~2013) → Pivotal(2013~2015) → Redis Labs(2015~2020)로 후원사가 바뀌면서도 antirez는 11년간 단독 메인테이너로서 프로젝트를 이끌었다.

2020 — antirez의 은퇴, 그리고 귀환

2020년 6월, antirez는 Redis 메인테이너 자리에서 물러났다. "유지보수 중심의 단계가 내 성격과 맞지 않는다"는 이유였다. 이후 AI를 소재로 한 SF 소설 Wohpe를 출판하기도 했다.

그리고 2024년 12월, antirez는 Redis 에반젤리스트라는 직함으로 복귀했다. 복귀 후 첫 작업은 Vector Set 자료구조의 구현이었다. AI 시대에 Redis가 벡터 유사도 검색까지 지원하게 된 것은 antirez의 복귀와 직결된다.


3. 라이선스 변천: 오픈소스의 빛과 그림자

Redis의 라이선스 역사는 오픈소스 생태계의 축소판이다:

  • 2009~2018: BSD-3 라이선스. 완전한 오픈소스.
  • 2018: 일부 모듈에 Commons Clause 추가. 클라우드 업체의 무임승차를 견제.
  • 2024.3: 코어 Redis 자체가 RSAL v2 + SSPL 듀얼 라이선스로 전환. AWS ElastiCache 등 서드파티 제공 제한.
  • 2025: AGPLv3를 추가한 트리플 라이선스로 전환. 다시 오픈소스로 복귀하되, 클라우드 벤더의 직접 패키징은 제한.

이 과정에서 Valkey(Linux Foundation 주도의 Redis 포크)가 탄생했다. 2026년 현재 Redis와 Valkey는 공존하며 경쟁 중이다.


4. Redis가 빠른 이유 — 아키텍처의 본질

Redis의 성능은 마법이 아니라 설계 철학의 결과다:

인메모리 스토리지

모든 데이터가 RAM에 상주한다. 디스크 I/O가 읽기/쓰기 경로에서 완전히 제거된다. 일반적인 RDBMS 쿼리가 50~200ms 걸리는 반면, Redis는 0.5~2ms 수준의 응답 시간을 보인다.

싱글 스레드 이벤트 루프

Redis는 명령 처리를 단일 스레드로 수행한다. 직관적으로는 느릴 것 같지만, 실제로는 반대다. 컨텍스트 스위칭이 없고, 락이 필요 없으며, 모든 명령이 원자적(atomic)으로 실행된다. I/O 멀티플렉싱을 통해 수십만 개의 동시 연결을 단일 스레드로 처리한다.

 
 
[클라이언트 1] ──┐
[클라이언트 2] ──┤──→ [이벤트 루프] ──→ [싱글 스레드 명령 실행] ──→ 응답
[클라이언트 3] ──┘       (epoll)          (락 없음, 원자적)

최적화된 자료구조

Redis는 C로 구현된 커스텀 자료구조를 사용한다. 작은 데이터셋에는 메모리 효율적인 인코딩(ziplist 등)을 자동 적용하고, 데이터가 커지면 일반 구조로 전환한다.

영속성 옵션

인메모리라고 해서 데이터를 잃는 건 아니다:

  • RDB: 주기적 스냅샷. fork() 시스템 콜로 자식 프로세스가 디스크에 저장하는 동안 부모 프로세스는 계속 서비스.
  • AOF: 모든 쓰기 연산을 로그로 기록. 재시작 시 재생하여 복구.
  • 혼합 모드: RDB + AOF를 함께 사용하여 빠른 복구와 데이터 안전성을 동시에 확보.

5. 활용 사례 — "캐시 그 이상"

Redis를 "캐시"라고만 부르는 것은 스위스 아미 나이프를 "칼"이라고만 부르는 것과 같다.

① 캐싱 (가장 보편적인 활용)

DB 앞에 Redis를 두고 자주 조회되는 데이터를 메모리에 저장한다. Cache-Aside, Write-Through, Write-Behind 등 다양한 전략을 조합할 수 있다.

 
 
typescript
// Cache-Aside 패턴 (Node.js)
async function getProduct(id: string) {
  const cached = await redis.get(`product:${id}`);
  if (cached) return JSON.parse(cached);

  const product = await db.query('SELECT * FROM products WHERE id = $1', [id]);
  await redis.set(`product:${id}`, JSON.stringify(product), 'EX', 600);
  return product;
}

② 세션 스토어

MSA 환경에서 서버가 여러 대일 때, 세션을 특정 서버에 묶지 않고 Redis에 중앙 저장한다. TTL을 설정하면 비활성 세션이 자동으로 만료된다.

 
 
typescript
// Express + Redis 세션
import session from 'express-session';
import RedisStore from 'connect-redis';

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: 'your-secret',
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 30 * 60 * 1000 } // 30분
}));

③ Rate Limiting

API 남용 방지. Sorted Set이나 단순 INCR + EXPIRE 조합으로 시간 윈도우 기반 요청 제한을 구현한다.

 
 
typescript
async function rateLimit(userId: string, limit: number = 100, windowSec: number = 60) {
  const key = `rate:${userId}`;
  const current = await redis.incr(key);
  if (current === 1) await redis.expire(key, windowSec);
  return current <= limit;
}

④ 실시간 리더보드

Sorted Set의 ZADD, ZREVRANGE, ZREVRANK 명령으로 점수 기반 순위를 실시간으로 관리한다. 게임, 이커머스 판매 순위, 경쟁 플랫폼에서 활용된다.

 
 
typescript
// 점수 업데이트
await redis.zadd('leaderboard:weekly', score, playerId);

// 상위 10명 조회
const top10 = await redis.zrevrange('leaderboard:weekly', 0, 9, 'WITHSCORES');

// 특정 플레이어 순위
const rank = await redis.zrevrank('leaderboard:weekly', playerId);

⑤ Pub/Sub & 메시지 브로커

서비스 간 실시간 이벤트 전달. 채팅, 알림, 캐시 무효화 브로드캐스트에 활용된다. 더 강력한 보장이 필요하면 Redis Streams로 컨슈머 그룹 기반 메시지 처리가 가능하다.

 
 
typescript
// Publisher
await redis.publish('notifications:user:123', JSON.stringify({
  type: 'NEW_MESSAGE',
  from: 'user:456',
  preview: '안녕하세요...'
}));

// Subscriber
subscriber.subscribe('notifications:user:123');
subscriber.on('message', (channel, message) => {
  const notification = JSON.parse(message);
  pushToClient(notification);
});

⑥ 분산 락

MSA에서 공유 리소스에 대한 동시 접근을 제어한다. SET NX EX 명령으로 간단한 뮤텍스를, 더 엄격한 환경에서는 Redlock 알고리즘을 사용한다.

⑦ 지리공간 인덱싱

GEOADD, GEODIST, GEORADIUS 명령으로 좌표 기반 검색이 가능하다. 배달 앱의 "내 주변 음식점", 차량 호출 서비스의 "가까운 드라이버 찾기" 같은 기능에 사용된다.

⑧ AI 벡터 검색 (2024~)

Redis가 Vector Set을 지원하면서, 임베딩 벡터의 유사도 검색이 가능해졌다. LLM의 RAG 파이프라인에서 벡터 DB 역할을 Redis가 대신하는 사례가 빠르게 늘고 있다. 2025년 Stack Overflow 설문에서 Redis는 **AI 에이전트 메모리 저장소로 개발자 선택 1위(42%)**를 차지했다.


6. 2026년의 Redis — AI 인프라의 중심

Redis는 단순한 캐시에서 AI 시대의 컨텍스트 엔진으로 진화하고 있다:

  • 컨텍스트 엔지니어링: LLM이 올바른 판단을 내리려면 적절한 데이터가 빠르게 제공되어야 한다. 벡터 스토어, 세션 상태, 장기 메모리를 한 곳에서 제공하는 "컨텍스트 레이어"로서 Redis가 부상 중이다.
  • LLM 응답 캐싱: 동일한 프롬프트에 대한 LLM 호출을 캐싱하여 비용과 에너지를 절감한다.
  • 에이전트 메모리: AI 에이전트의 작업 상태, 대화 히스토리, 도구 호출 결과를 저장하는 실시간 메모리로 활용된다.

Redis CEO Rowan Trollope는 이렇게 표현했다. "에이전틱 의사결정이 이루어지는 스택의 앞단, 바로 그곳이 Redis의 전통적인 자리였다. 개발자들이 자연스럽게 Redis를 AI 워크로드에 채택하고 있는 이유다."


7. 실무에서의 선택 기준

Redis가 적합한 경우

  • 밀리초 단위 응답이 필요한 읽기 Heavy 워크로드
  • 세션, 캐시, 리더보드 등 TTL 기반 임시 데이터
  • Pub/Sub 또는 Streams 기반 실시간 이벤트 처리
  • MSA 환경에서 서비스 간 공유 상태 저장
  • AI/ML 파이프라인의 벡터 검색 및 피처 서빙

Redis만으로는 부족한 경우

  • 복잡한 관계형 쿼리 (JOIN, 서브쿼리)
  • RAM 용량을 초과하는 대규모 데이터셋
  • 강한 트랜잭션 보장이 필요한 금융 원장
  • 전문 검색이 핵심인 경우 (Elasticsearch가 더 적합)

핵심은 **"Redis vs DB"가 아니라 "Redis + DB"**라는 것이다. Redis는 DB를 대체하는 것이 아니라, DB의 부하를 흡수하고 응답 속도를 높이는 가속기 역할을 한다.


마무리: 단순함의 힘

antirez는 인터뷰에서 반복적으로 **단순함(simplicity)**을 강조했다. "복잡한 시스템은 아무리 노력해도, 프로덕션에서 다른 복잡한 시스템과 만나면 상상할 수 없는 방식으로 실패한다."

Redis가 2009년부터 17년간 살아남은 이유는 기능이 많아서가 아니라, 핵심이 단순해서다. 키와 값, 메모리와 자료구조. 이 근본적인 설계 위에 캐시도, 큐도, 리더보드도, 벡터 검색도 자연스럽게 올라간다.

화장실에서 시작된 이 프로젝트는, "단순한 것이 오래 간다"는 소프트웨어 설계의 원칙을 17년째 증명하고 있다.


참고 자료: Redis Wikipedia, Brachiosoft Blog, Redis 공식 블로그, VentureBeat, antirez 개인 사이트

LIST

프롤로그: 그날, DB가 탔다

금요일 오후 5시 47분. 퇴근을 13분 앞두고 슬랙에 메시지가 뜬다.

🔥 [ALERT] DB CPU 98% — Connection Pool Exhausted — 응답시간 47초

모니터링 대시보드를 열었다. 그래프가 수직으로 치솟아 있다. 쿼리 큐에 3,000개가 쌓여 있고, 커넥션 풀은 진작에 바닥났다. Slow Query 로그가 폭포처럼 쏟아진다. API 서버는 504 Gateway Timeout을 뱉고 있고, 프론트엔드에는 "잠시 후 다시 시도해주세요"가 도배되어 있다.

DB가 탄 거다.

"탄다"는 과장이 아니다. 서버실 온도가 올라가고, 디스크 I/O가 포화되고, 커넥션이 고갈되면 물리적으로도 뜨거워진다. 그리고 그 열기는 고스란히 개발자의 등골을 타고 올라온다.

이 글은 그 순간에 대한 이야기다. DB가 타기 전에 무엇을 준비해야 하는지, 타는 순간에 무엇을 해야 하는지, 그리고 타고 난 뒤에 무엇을 바꿔야 하는지.


1장. 불이 나기 전 — 방화벽을 세워라

불이 나면 소방차를 부르지만, 소방차가 와도 이미 탄 건 돌아오지 않는다. DB가 타기 전에 세워두는 방화벽이 있다.

인덱스: 불쏘시개를 치워라

DB가 타는 가장 흔한 원인 1위. 인덱스가 없는 테이블에 Full Table Scan이 걸리는 것. 100만 건짜리 테이블을 매 요청마다 처음부터 끝까지 훑으면, 그건 장작 위에 휘발유를 붓는 거다.

 
 
sql
-- 이게 매 요청마다 실행되고 있다면, 불이 안 나는 게 이상하다
SELECT * FROM orders WHERE user_id = ? AND status = 'PENDING'
ORDER BY created_at DESC;

-- 이 인덱스 하나로 Full Scan → Index Seek
CREATE INDEX idx_orders_user_status_created
ON orders(user_id, status, created_at DESC);

EXPLAIN ANALYZE를 습관처럼 찍어라. Seq Scan이 보이면 그건 아직 안 터진 시한폭탄이다.

실전 체크리스트:

  • WHERE 절에 자주 등장하는 컬럼에 인덱스가 있는가?
  • 복합 인덱스의 컬럼 순서가 쿼리 패턴과 일치하는가?
  • 안 쓰는 인덱스가 쌓여서 오히려 INSERT/UPDATE를 느리게 만들고 있지 않은가?
  • SELECT * 를 쓰고 있지 않은가? 필요한 컬럼만 명시하라

커넥션 풀: 소방 호스의 수를 정하라

커넥션 풀은 DB와 애플리케이션 사이의 파이프다. 파이프가 부족하면 요청이 대기열에 쌓이고, 대기열이 넘치면 타임아웃이 연쇄 발생한다. 그렇다고 파이프를 무한정 늘리면 DB가 컨텍스트 스위칭에 허덕인다.

PostgreSQL 공식 권장 공식:

max_connections = (core_count * 2) + effective_spindle_count

 

4코어 서버에 SSD 1개라면? (4 * 2) + 1 = 9. 아홉 개. 생각보다 적다.

HikariCP의 기본값이 10인 데는 이유가 있다. 커넥션 100개를 열어놓는 것이 능사가 아니다. 적은 커넥션으로 빠르게 반환하는 것이 핵심이다.

yaml
# HikariCP 설정 예시
spring:
  datasource:
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      connection-timeout: 3000    # 3초 안에 못 받으면 실패
      idle-timeout: 600000
      max-lifetime: 1800000
      leak-detection-threshold: 5000  # 5초 이상 미반환 시 경고

leak-detection-threshold는 반드시 설정하라. 커넥션을 빌려가서 안 돌려주는 코드가 있으면, 풀이 서서히 말라간다. 로그에는 아무 에러도 안 뜨는데 어느 날 갑자기 타는 경우가 이거다.

캐시: 불 자체를 예방하라

가장 확실한 방어는 DB에 불이 안 가게 하는 것이다. 변하지 않는 데이터, 자주 읽히는 데이터, 계산 비용이 높은 데이터는 캐시에 올려라.

typescript
// Redis 캐시 패턴 (Cache-Aside)
async function getUserProfile(userId: string): Promise<UserProfile> {
  // 1. 캐시에서 먼저 찾는다
  const cached = await redis.get(`user:${userId}`);
  if (cached) return JSON.parse(cached);

  // 2. 캐시에 없으면 DB 조회
  const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);

  // 3. 결과를 캐시에 저장 (TTL 5분)
  await redis.set(`user:${userId}`, JSON.stringify(user), 'EX', 300);

  return user;
}

캐시 적중률(Hit Rate)이 90%면, DB로 가는 트래픽이 1/10로 줄어든다. 이것만으로 "탈 뻔한 DB"가 "여유로운 DB"로 바뀐다.

주의할 점:

  • Cache Invalidation은 컴퓨터 과학에서 가장 어려운 문제 중 하나다. 데이터가 변경되었을 때 캐시를 확실히 무효화하라
  • 캐시 자체가 날아가면(Redis 재시작 등) DB에 트래픽이 한꺼번에 몰리는 **캐시 스탬피드(Cache Stampede)**가 발생한다. TTL에 랜덤 지터(jitter)를 추가하라
  • 캐시에 잘못된 데이터가 올라가면 "데이터가 이상해요" 문의가 폭주한다. 캐시가 해결책인 동시에 새로운 문제의 원인이 될 수 있다

2장. 불이 난 순간 — 진화 매뉴얼

0분~2분: 상황 파악 — "어디서 타고 있는가"

불이 나면 가장 먼저 할 일은 불이 어디서 시작됐는지 파악하는 것이다. DB가 탄다고 DB가 원인이 아닐 수 있다.

확인 순서:

 
 
bash
# 1. 현재 실행 중인 쿼리 확인 (PostgreSQL)
SELECT pid, now() - pg_stat_activity.query_start AS duration,
       query, state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 seconds'
ORDER BY duration DESC;

# 2. 커넥션 상태 확인
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;

# 3. 락 대기 확인
SELECT blocked_locks.pid AS blocked_pid,
       blocking_locks.pid AS blocking_pid,
       blocked_activity.query AS blocked_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_locks blocking_locks ON ...

빈번한 원인 Top 5:

  1. 인덱스 없는 쿼리가 갑자기 호출량 증가 — 마케팅팀이 프로모션 푸시를 보냄
  2. 배치 작업이 트랜잭션 시간대에 실행됨 — 크론잡 설정 실수
  3. N+1 쿼리가 트래픽 증가와 함께 폭발 — 평소에는 10건이라 안 보였는데 10,000건이 되니 터짐
  4. 락 경합 — 한 트랜잭션이 테이블 락을 잡고 안 놓아줌
  5. 디스크 I/O 포화 — 로그 파일이 디스크를 가득 채움

2분~5분: 1차 진화 — "출혈을 멈춰라"

원인을 파악했으면 즉시 출혈을 멈춘다. 완벽한 해결이 아니라 피해 확산 방지가 목표다.

킬 쿼리: 문제가 되는 장시간 실행 쿼리를 강제 종료한다.

 
 
sql
-- 특정 쿼리 강제 종료
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE duration > interval '30 seconds'
  AND query LIKE '%problematic_table%';

서킷 브레이커 발동: 장애가 전파되지 않도록 문제 엔드포인트를 차단한다.

 
 
typescript
// 서킷 브레이커 패턴
class CircuitBreaker {
  private failures = 0;
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
  private readonly threshold = 5;
  private readonly resetTimeout = 30000; // 30초

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'OPEN') {
      throw new Error('Circuit is OPEN — 서비스 일시 중단');
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onFailure() {
    this.failures++;
    if (this.failures >= this.threshold) {
      this.state = 'OPEN';
      setTimeout(() => { this.state = 'HALF_OPEN'; }, this.resetTimeout);
    }
  }

  private onSuccess() {
    this.failures = 0;
    this.state = 'CLOSED';
  }
}

읽기 트래픽 분산: Read Replica가 있다면, 읽기 쿼리를 레플리카로 돌린다.

비핵심 기능 차단: 추천 알고리즘, 통계 집계, 로그 적재 같은 비핵심 기능을 일시적으로 끈다. 핵심 비즈니스 로직(결제, 인증)만 살린다.

5분~30분: 2차 진화 — "원인을 제거하라"

출혈이 멈추면 근본 원인을 처리한다.

인덱스가 원인이라면: 긴급 인덱스 생성. PostgreSQL의 CREATE INDEX CONCURRENTLY는 테이블 락 없이 인덱스를 생성한다.

 
 
sql
-- 운영 중에도 안전하게 인덱스 생성
CREATE INDEX CONCURRENTLY idx_emergency_fix
ON orders(user_id, created_at DESC);

N+1이 원인이라면: 즉시 핫픽스. JOIN이나 배치 쿼리로 교체.

 
 
typescript
// Before: N+1 — 유저 100명이면 쿼리 101번
const users = await userRepo.findAll();
for (const user of users) {
  user.orders = await orderRepo.findByUserId(user.id); // N번 실행
}

// After: JOIN으로 1번
const usersWithOrders = await userRepo
  .createQueryBuilder('user')
  .leftJoinAndSelect('user.orders', 'order')
  .getMany();

배치 작업이 원인이라면: 해당 크론잡을 중지하고, 트래픽이 적은 시간대로 재스케줄링.


3장. 불이 꺼진 뒤 — 화재 보고서를 쓰라

Postmortem: 같은 불이 두 번 나면 그건 방화다

장애가 복구되면 반드시 사후 분석(Postmortem) 문서를 작성한다. 이건 책임 추궁이 아니라 학습을 위한 것이다.

 
 
markdown
## 장애 보고서 — 2026-03-30 DB 과부하

### 타임라인
- 17:47 — 모니터링 알림 발생 (DB CPU 98%)
- 17:49 — 원인 파악: 프로모션 푸시로 인한 트래픽 300% 급증
- 17:52 — 장시간 실행 쿼리 강제 종료
- 17:55 — 비핵심 API 엔드포인트 일시 차단
- 18:10 — 긴급 인덱스 생성 완료
- 18:15 — 정상 복구 확인

### 근본 원인
- orders 테이블의 user_id + status 복합 인덱스 부재
- 마케팅팀 프로모션 일정이 개발팀에 공유되지 않음

### 재발 방지
- [ ] orders 테이블 인덱스 추가 (완료)
- [ ] 마케팅 프로모션 일정 공유 프로세스 수립
- [ ] Slow Query 임계값 알림 추가 (5초 → 2초로 하향)
- [ ] 부하 테스트 시나리오에 프로모션 트래픽 패턴 추가

부하 테스트: 불을 미리 질러보라

"이 시스템이 트래픽 10배를 견딜 수 있는가?"를 아는 유일한 방법은 실제로 10배를 때려보는 것이다.

 
 
javascript
// k6 부하 테스트 스크립트 예시
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 100 },   // 워밍업
    { duration: '5m', target: 500 },   // 평소 트래픽
    { duration: '2m', target: 2000 },  // 프로모션 시나리오
    { duration: '5m', target: 2000 },  // 유지
    { duration: '2m', target: 0 },     // 쿨다운
  ],
};

export default function () {
  const res = http.get('https://api.example.com/orders?status=PENDING');
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });
  sleep(1);
}

부하 테스트에서 DB가 타면, 운영에서 안 탄다. 부하 테스트에서 안 타면... 그건 부하가 부족한 거다.


4장. 화재 등급별 대응 — 연기 수준부터 대형 화재까지

Level 1: 연기가 난다 (Slow Query 증가)

증상: 응답 시간이 평소 200ms에서 800ms로 느려졌다. 사용자는 아직 못 느낀다.

대응:

  • Slow Query 로그 분석
  • EXPLAIN ANALYZE로 실행 계획 확인
  • 인덱스 추가 또는 쿼리 리팩토링
  • 이건 퇴근 후 집에서 해도 된다

Level 2: 불꽃이 보인다 (커넥션 풀 80% 이상)

증상: 일부 요청이 타임아웃된다. 사용자가 "가끔 느려요"라고 보고한다.

대응:

  • 커넥션 풀 설정 점검 (leak 여부)
  • 장시간 실행 쿼리 식별 및 최적화
  • 읽기 트래픽을 Read Replica로 분산
  • 캐시 적중률 확인 및 캐시 대상 확대
  • 지금 안 하면 Level 3이 된다

Level 3: 화염이 솟는다 (DB CPU 90%+, 커넥션 고갈)

증상: 대부분의 요청이 실패한다. "서비스 접속이 안 돼요" 문의 폭주.

대응:

  • 즉시 문제 쿼리 강제 종료
  • 서킷 브레이커 발동
  • 비핵심 기능 일시 차단
  • 캐시 워밍 (빈 캐시에 데이터 선적재)
  • 팀 전원 소집. 퇴근 없다

Level 4: 대형 화재 (DB 다운, 데이터 유실 위험)

증상: DB 프로세스가 죽었다. 연결 자체가 안 된다.

대응:

  • DB 재시작 (최후의 수단)
  • WAL(Write-Ahead Log) 기반 복구
  • 최신 백업에서 Point-in-Time Recovery
  • 서비스 점검 모드 전환
  • 이건 사고다. 경영진 보고가 필요하다

5장. 불에 강한 아키텍처 — 방화 건물을 짓는 법

CQRS: 읽기와 쓰기를 분리하라

읽기와 쓰기가 같은 DB 인스턴스를 공유하면, 읽기 트래픽 폭주가 쓰기 성능까지 잡아먹는다. 쓰기(Command)와 읽기(Query)를 분리하면 한쪽이 타도 다른 쪽은 살아남는다.

 
 
[사용자 요청]
     │
     ├── 쓰기 → Primary DB (PostgreSQL)
     │              │
     │              └── 변경 이벤트 발행
     │                       │
     └── 읽기 → Read Replica / OpenSearch / Redis
                     ↑
              이벤트 기반 동기화

Rate Limiting: 입구에서 통제하라

아무리 튼튼한 건물도 한꺼번에 만 명이 들어오면 무너진다. API Gateway에서 요청 수를 제한하라.

 
 
typescript
// Token Bucket 기반 Rate Limiter
class RateLimiter {
  private tokens: number;
  private readonly maxTokens: number;
  private readonly refillRate: number; // tokens per second
  private lastRefill: number;

  constructor(maxTokens: number, refillRate: number) {
    this.tokens = maxTokens;
    this.maxTokens = maxTokens;
    this.refillRate = refillRate;
    this.lastRefill = Date.now();
  }

  tryConsume(): boolean {
    this.refill();
    if (this.tokens > 0) {
      this.tokens--;
      return true;
    }
    return false; // 429 Too Many Requests
  }

  private refill() {
    const now = Date.now();
    const elapsed = (now - this.lastRefill) / 1000;
    this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRate);
    this.lastRefill = now;
  }
}

큐 기반 비동기 처리: 불을 천천히 태워라

모든 요청을 즉시 처리할 필요는 없다. 주문 접수는 즉시 응답하되, 실제 처리는 큐에 넣고 순차적으로 하면 DB 부하를 평탄화할 수 있다.

[대량 주문 요청]
     │
     ├── 즉시 응답: "주문이 접수되었습니다" (202 Accepted)
     │
     └── 메시지 큐 (Redis Queue / RabbitMQ / Kafka)
              │
              └── Worker가 순차 처리 → DB 쓰기
                   (초당 100건으로 제한)

초당 10,000건의 요청이 들어와도, Worker가 초당 100건씩 처리하면 DB는 편안하다. 사용자는 "접수 완료" 응답을 즉시 받으니 체감 성능도 좋다.

 

Read Replica: 불을 분산시켜라

Primary에서 쓰기만 하고, 읽기는 Replica로 보내라. 트래픽의 80%가 읽기라면, 이것만으로 Primary의 부하가 1/5로 줄어든다.

 
typescript
// 읽기/쓰기 분리 DataSource 설정 (TypeORM)
const dataSource = new DataSource({
  type: 'postgres',
  replication: {
    master: {
      host: 'primary-db.internal',
      port: 5432,
      username: 'app',
      password: '***',
      database: 'production',
    },
    slaves: [
      { host: 'replica-1.internal', port: 5432, ... },
      { host: 'replica-2.internal', port: 5432, ... },
    ],
  },
});

6장. 불과 함께 사는 법 — 모니터링은 화재 경보기다

DB가 타는 것을 완전히 막을 수는 없다. 트래픽은 예측 불가능하고, 코드에는 항상 미처 발견하지 못한 구멍이 있다. 중요한 것은 탈 때 빨리 아는 것이다.

최소한의 모니터링 세트

 
 
yaml
# 이것만은 반드시 알림을 걸어라
alerts:
  - name: DB CPU 높음
    condition: cpu_usage > 80%
    duration: 5분
    severity: warning

  - name: 커넥션 풀 임계
    condition: active_connections > pool_size * 0.8
    duration: 2분
    severity: critical

  - name: Slow Query 급증
    condition: slow_query_count > 50/분
    duration: 3분
    severity: warning

  - name: 디스크 사용률
    condition: disk_usage > 85%
    duration: 10분
    severity: critical

  - name: 레플리카 지연
    condition: replication_lag > 10초
    duration: 5분
    severity: warning

화재 경보기가 울렸을 때 이미 대형 화재라면, 경보기의 임계값이 너무 높은 것이다. 경보기는 연기 단계에서 울려야 한다. CPU 98%에서 알림이 오면 이미 Level 3이다. 80%에서 울리게 하라.


에필로그: 불은 다시 난다

DB는 반드시 다시 탄다.

규모가 커지면 탄다. 사용자가 늘면 탄다. 블랙프라이데이에 탄다. 마케팅팀이 푸시를 보내면 탄다. 배치 작업 시간을 잘못 잡으면 탄다. 인턴이 WHERE 절 없이 UPDATE를 날리면 탄다.

중요한 건 "불이 안 나게 하겠다"는 환상을 버리는 것이다. 대신 이렇게 생각하라:

  • 불이 나기 전에, 작은 불이 날 수 있는 환경을 부하 테스트로 미리 만든다
  • 불이 나는 순간에, 30초 안에 어디서 타는지 파악할 수 있는 모니터링이 있다
  • 불이 난 직후에, 2분 안에 출혈을 멈추는 매뉴얼이 있다
  • 불이 꺼진 뒤에, 같은 원인으로 두 번 타지 않도록 사후 분석을 쓴다

그리고 가장 현실적인 조언 하나. 금요일 오후에 배포하지 마라. 불이 나면 주말이 날아간다.


"DB는 탄다. 문제는 타느냐 안 타느냐가 아니라, 탔을 때 3분 안에 끌 수 있느냐다."

LIST

1. 스프링AI - 인공지능의 개념과 스프링AI

 

 

이런 인공지능의 개념을 이해하면... 인공지능이 포괄적인게 개념이 잡힌다. 인공지능은 말그대로 어떤 목적.. 체스게임의 경우 체스룰안에서의 승리.. 자율주행은... 도로 위에서 안전한 운전(여러가지 경우의수의 판단, 핸들제어, 앞차와의 거리 등)을 위한 수많은 판단이다... 이게 사실 제일어려워 보인다. 그러나 이게 가능해지면서 과거 전투기나 이런 비싼 장비가 하던것을 드론이나 이런 저비용으로 상당한 퍼포먼스를 실제 우크라이나 전쟁에서 보고 있는등.. 이제 인공지능 사용능력은 이제 필수품이 된것 같다. 왜냐하면 과거 기계가 가공할 성능으로 수많은 일자리를 몰아내면서.. 기계파괴운동까지 있었다. 이것은 그러나 변리사, 변호사, 회계사 이런 전문직 들의 지능을 아주 평범하게 만들어 버린다. 누구나 gpt의 도움이면 간단한 법률지식, 회계지식을 얻는게 너무 쉬워졌다. 이건 결국 과거 사농공상 시대의 선비들의 자부심으로 내세운 수많은 책들을 얻은 지능을 아주 싸구려로 만들어 버린다. 그래도 변하지 않는건 인간세계의 지혜와 공의와 정의와 정직에 관해 인간스스로 기준을 세우고 판단해야 한다는 것이다.

 머신러닝은 통계기반으로 데이터를 학습해서 말한다고 하면 그냥 사주팔자가 나온 중국에서 온서적이나 기타 등등.. 여러 통계를 활용한 데이터 기반의 추측 및 판단이다.. 사실 여기까지 이미 머신러닝이 했고... 이 머신러닝은 인공지능과 딥러닝에 비하면 이미 기존에 보이던 것들이다. 

 딥러닝은 인공신경망 기반으로 고도화된 학습인데 ... 여기는 gpt 같이 엄청난 성능을 보여주지만.. 역시나 지금 엔비디아 주가와 gpu를 태운 블록체인 현상에서 보듯이 장비빨이 필요하다. 엄청난 돈이 필요한 것이다. 즉 인프라적으로 자본면에서나 대기업 등 기타 경우의 수에서 상당히 불리하다. 그러나 gpt, gemini, claude의 가공할 성능면에서 모듯이 개발자 측면에서 가장 쉽게 접근이 가능하고.. 이로인해 코드 생성의 비용이 0에 수렴할정도로 클로드 코드가 가공할 생산성을 보이고... 과거 집단지성의 지혜라고 정치권에서 한목소리 내던것을 ... 딥러닝을 통한 기술이.. 이미 웬만한 집단지성이 글로벌적인 마스터급 일반론이 보편화 되고 있다. 멀티미디어... 시스템... 컴퓨터에서 보이던 이미지, 동영상,텍스트 이런것들을 너무 쉽게 만든다.. 이제 어떻게 차별화 시키는가가 인간의 과제가 되어가고 있다.

 결국 이 딥러닝 기술이나 머신러닝 기술을 어찌 사용하나가.. 수많은 판도를 바꿀것이다. 

 

 

그중에 스프링AI는 LLM을 다룬다~! 자... LLM의 원리 질의문... 바이브 코딩을 한다고 해보자...

 

스프링부트가 모지?

 

를 물으면 이것을 쪼개서 메모리에 저장하는데 그 메모리 하나가 토큰(Token)이라는 형태로 나눈다고 할수 있다고 한다. 스프링 , 부트 , 란, 모지, ? 이렇게 바이브로 프롬프트가 가자마자 5개 정도의 토큰으로 메모리를 점유하며 나뉘는것이다. (위의는 예시이며 다른식으로 형태소를 쪼개는데 한국어는 Nori 형태분석소를 쓴다고 하니 그 기준이 있을듯하다.)

 그 와 별개로 비용산정을 하면 저 토큰(메모리 점유)하나가 결국 비용으로 청구된다.

 자 그 다음단계 임베딩.. 컴퓨터는 본질적으로 0,1이니 당연히 언어를 모른다. 이것을 계산하려면 자기들이 연산하는 방식인 숫자로 계산해야 한다. 즉 스프링이라는 단어에 수많은 벡터값을 가지고 이에 따라서 단어들 사이에 상관관계를 분석한다.  그에 따른 가중치를 숫자로 표현해 놓고 있다... 이것을 좌표상에 강아지와 개는 가깝고 자동차는 멀다... 흠... 이렇게 수많은 상관관계를 쪼개 연산하니.. 벡터db가 필요한듯하다.

 이 벡터 데이터가 LLM으로 가는순간 트랜스 포머블록으로 들어간다. 트랜스포머... selft-attention 즉.. 인간세계로치면 문맥기반.. 상화파악하는 과정속도다. feed forward는 어느정도 문맥과 상황파악을 했으면... 그것에 대해 답변할만한 지식이 있어야 한다. 

이 feed forward는 각 토큰(단어)의 위치에서 독립적으로 전형되는 비선형 신경망이다. 보통 두개의 선형 변환(Linear Layer)와 그 사이의 활성화 함수(주로 ReLU 또는 GELU)로 구성된다. 입력받은 데이터를 훨신 더 큰 차원으로 확대한뒤(Expansion), 핵심 정보를 남기고 다시 원래 차원으로 압축(Projection)한다.

 결국 핵심 역할은 지식의 저장소 및 정제로 방대한 데이터 중에 문맥과 상황에 맞는 적절하고 적합한 정보를 불러온다. 비선형성으로 인해 단순하지 않고 활성화 함수로 복잡한 논리를 처리한다.

 

즉 self-attention이 단어들을 서로 연결했다면, 피드 포워드는 각 단어의 벡터를 개별적으로 강화한다. 즉 self-attention은 상황과 문맥을 파악하기 위해 전문가들이 토론하고, feed-forward는 그 전문가들이 자신의 자리로 돌아가 지식을 총동원해서 적절한 답을 도출하기 위해 정리하는 것이다.

  

LLM은 문장을 한번에 완성하지 않고 다음에 올 가장 확률 높은 토큰을 하나씩 찾아낸다.  확률분포계산은 학습된 방대한 데이터를 방탕으로 후보군을 뽑고 선택한다. 즉 LLM은 본질적으로 다음에 올 가장 그럴싸한 단어를 맞히는 기계다

 

 이 다음 토큰 예측은 트랜스 포머 구조의 가장 마지막층인 출력층(Output Layer)에서 일어난다.

트랜스포머 블록의 통과 : 앞서 설명한 4단계 (Attention, Feed Forward)를 거치면, 모델 내부에는 문맥정보가 가득 담긴 추상적인 숫자 뭉치(벡터)가 생성된다. 선형 층 (Linear Layer)가 이 숫자 뭉치를 모델이 알고 있는 전체 단어 사전의 개수 만큼 넓게 펄쳐진다. 그리고 선형층에서 이 숫자벡터를 모델이 알고 있는 전체 단어 사전의 개수 만큼 넓게 펼친다... 국어사전/백과사전이 들어가 있었다.. (5만개의 단어면 5만개의 칸) 

 소프트맥스 함수는 펼쳐진 숫자들을 0에서 1사이의 확률값으로 변환한다. 모든 후보 단어의 확률합은 100%가 된다. (ex: Java:0.6, 프레임워크 :0.25, 무엇 : 0.1 ) 디코딩 전략으로 이 확률 분포에서 최종적으로 어떤 단어를 뽑을지 결정한다. 디코딩 전략은 이 확률 분포에서 최종적으로 어떤 단어를 뽑을지 결정한다. Greedy Searh는 무조건 확률이 가장 높은것만 선택하고 Sampling은 약간의 확률이 있는 다른 단어도 섞어서 더 창의적인 답변을 유도한다.

 결국 셀프어텐션은 상황파악해서, 피드포워드로 맞는 지식을 찾아내고, 최종 출력층에서 적절하다고 추린 답이 나온다.

 

이렇게 지속적으로 판단해서 답을 만든다. 

 

 

 

Spring Boot란 무엇인가? 에 대한 답변을 결국, 

 

"Spring Boot는 Java 기반의 오픈소스 프레임워크입니다."

그 추린답에 대한 판단은 인간의 몫인데... 이런거는 아주 국어사전/백과사전 기본지식에 속해서 틀리지 않는듯 하다.

 

LLM은 사용자의 질문을 받으면 내부적으로 [텍스트 → 숫자 → 문맥 분석 → 확률 계산 → 다시 텍스트]의 정교한 변환 과정을 거칩니다.

 

 

자 아까 점유했던 단어기반의 메모리 점유 , 즉 토큰

이것은 비용이다.

 

최근 핫한 클로드는

 

이렇다고 한다. 2배 성능에는 3배가격, 4배 성능에는 5배 가격이 적용된다.

최고성능을 자랑하는 opus지만, 비용효율면에서는 sonnect이 낫다.

max 플랜은 200$인데... 200$이상을 쓰면 일단... api 비용 기준으로 넘어가면 잘쓰고 있는것인가... 이런생각을 해본다.

그러나 api가 아니라 그냥 쓰면 200$로 opus나 sonnect을 마음껐 쓸수있는듯하다. 문맥도 1M.. 즉 100만 토큰을 기억할수 있는 클로드다. 이 100만 토큰 비중으로 api 비용이 계산된다는데 이게 보통 한 컨텍스트에서 생성형 ai 가 메모리에 가용하는 용량이라고  한다. 이게 꽉차면... 새로운 상황맥락이 없는 대화가 시작되는 것이다.

 api 사용시 한글보다 영어, 입력/출력형식을 정제하고 간단하게 하는 등의 기법을 써서 절약할수 있다고 한다.

 

자 이제 코드단으로 가면 코드 한줄수정으로 claude의 저 모델들을 교체할수 있다. 모델들은 추상화 되어 있어서 아주 편리하게 사용할수 있다. ChatClient를 빈으로 등록하여 스프링의 DI기능을 잘 활용할수 있다. AOP기능을 통해 보안,로깅,트랜잭션처리 관리도 할수 있다. 

 자 그리고 벡터 store를 지원하는 pinecone, redis, postgresql를 쓸수 있다. rag(검색 증강생성)은 외부 지식을 ai에게 전달하는 복잡한 파이프라인을 쉽게 설계 구축할수 있게 설계되어 있다.

 

 자 이 모두 스프링 AI를 쓰면 아주 간단하게, 엔터프라이즈급 AI서비스를 빠르게 구축할수 있게 해준다.

 

정말 간단하게 API key만 하면 , 비즈니스로직도 간단하게 해서 데이터베이스 연동까지 된다. 비용관리를 위해 처리량 제한을 걸수 있고 캐싱을 통해 API비용을 획기적으로 줄일수 잇다.

 

위의 내용은 프롬프트 템플릿을 만들어 사용자가 개떡같이 말해도 찰떡같이 알아듣고 대답하게 할수 있다.

 

백엔드 통합 방식 : 백엔드 통합의 5가지 핵심 장점 (✅ 권장)

브라우저는 우리 서버(Spring Boot)에 요청을 보내고, 서버가 안전하게 AI API와 통신하는 구조입니다.

 

1.보안 및 자산 보호

@Service
public class ChatService {
    
	@Value("${spring.ai.openai.api-key}")
	private String apiKey; // 서버 외부로 절대 유출되지 않음
	
}

API Key를 서버 내부(환경 변수, Secrets Manager)에 숨깁니다. 사용자에게는 절대 노출되지 않습니다.

 

2.정교한 비용 및 사용량 관리

public String chatWithLimit(String userId, String message) {
    if (usageService.getTodayUsage(userId) >= 10) {
        throw new UsageLimitException("오늘 사용량을 모두 소진했습니다.");
    }
    return chatClient.prompt().user(message).call().content();
}

- 사용자별 일일 호출 횟수를 제한하여 예기치 못한 비용 지출을 방지합니다.

 

 

3.개인화된 비즈니스 로직

예: "이 고객은 30대 남성이고 최근 등산화를 구매했어. 이 정보를 바탕으로 상품을 추천해줘."

 

@Service
public class ProductRecommendationService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private ProductRepository productRepository;
    
    public String recommendProduct(Long userId, String query) {
        // 1. DB에서 사용자 정보 조회
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new UserNotFoundException());
        
        // 2. 사용자 구매 이력 조회
        List<Product> purchaseHistory = productRepository
                .findByUserId(userId);
        
        // 3. 개인화된 프롬프트 생성
        String prompt = String.format("""
            다음 사용자에게 상품을 추천해주세요:
            - 연령대: %s
            - 관심사: %s
            - 구매 이력: %s
            - 질문: %s
            """, 
            user.getAgeGroup(),
            user.getInterests(),
            purchaseHistory.stream()
                .map(Product::getName)
                .collect(Collectors.joining(", ")),
            query
        );
        
        // 4. AI 호출
        return chatClient.prompt()
                .user(prompt)
                .call()
                .content();
    }
}

 

DB에 저장된 사용자 프로필, 구매 이력 등을 프롬프트에 결합하여 "나만을 위한 답변"을 생성합니다.

 

4. 응답 품질 관리 및 모니터링

@Service
public class ManagedChatService {
    
    public String chat(String message) {
        // 1. 입력 검증
        if (containsInappropriateContent(message)) {
            return "부적절한 내용이 포함되어 있습니다.";
        }
        
        // 2. 프롬프트 템플릿 적용 (일관된 응답 품질)
        String enhancedPrompt = """
            당신은 전문적이고 친절한 고객 지원 AI입니다.
            다음 규칙을 따르세요:
            1. 존댓말 사용
            2. 3문장 이내로 답변
            3. 확실하지 않으면 "정확한 답변을 드리기 어렵습니다"라고 답변
            
            사용자 질문: %s
            """.formatted(message);
        
        try {
            // 3. AI 호출
            String response = chatClient.prompt()
                    .user(enhancedPrompt)
                    .call()
                    .content();
            
            // 4. 로깅 (모니터링)
            log.info("AI 호출 - 입력 토큰: {}, 출력 토큰: {}, 비용: ${}",
                    inputTokens, outputTokens, cost);
            
            return response;
            
        } catch (Exception e) {
            // 5. 에러 처리
            log.error("AI 호출 실패", e);
            return "죄송합니다. 일시적인 오류가 발생했습니다.";
        }
    }
}
  1. 사용자가 대충 질문해도 백엔드에서 '전문가 페르소나'를 입혀 높은 품질의 답변을 유도합니다. 또한, AI가 답한 내용에 부적절한 표현이 있는지 검증한 뒤 사용자에게 전달합니다.
@Service
public class ManagedChatService {
    
    public String chat(String message) {
        // 1. 입력 검증
        if (containsInappropriateContent(message)) {
            return "부적절한 내용이 포함되어 있습니다.";
        }
        
        // 2. 프롬프트 템플릿 적용 (일관된 응답 품질)
        String enhancedPrompt = """
            당신은 전문적이고 친절한 고객 지원 AI입니다.
            다음 규칙을 따르세요:
            1. 존댓말 사용
            2. 3문장 이내로 답변
            3. 확실하지 않으면 "정확한 답변을 드리기 어렵습니다"라고 답변
            
            사용자 질문: %s
            """.formatted(message);
        
        try {
            // 3. AI 호출
            String response = chatClient.prompt()
                    .user(enhancedPrompt)
                    .call()
                    .content();
            
            // 4. 로깅 (모니터링)
            log.info("AI 호출 - 입력 토큰: {}, 출력 토큰: {}, 비용: ${}",
                    inputTokens, outputTokens, cost);
            
            return response;
            
        } catch (Exception e) {
            // 5. 에러 처리
            log.error("AI 호출 실패", e);
            return "죄송합니다. 일시적인 오류가 발생했습니다.";
        }
    }
}

 

5. 성능 최적화 (캐싱)

@Service
public class CachedChatService {
    
    private final Cache<String, String> cache = 
        Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(1, TimeUnit.HOURS)
            .build();
    
    public String chat(String message) {
        // 동일한 질문은 캐시에서 반환 (비용 절감)
        return cache.get(message, key -> {
            return chatClient.prompt()
                    .user(key)
                    .call()
                    .content();
        });
    }
}

 

Prompt Template 활용

프롬프트에 매번 같은 문구를 반복 입력하는 것은 비효율적입니다. Prompt Template은 프롬프트의 구조(뼈대)와 가변 데이터(변수)를 분리하여 재사용성을 극대화합니다.

  • 플레이스홀더: {variableName} 형식을 사용하여 동적 데이터를 삽입합니다.
  • 장점: 비즈니스 로직(Java)과 프롬프트 엔지니어링(Text)을 깔끔하게 분리할 수 있습니다.

실습 예제: 마케팅 문구 생성 서비스

 

  @GetMapping("/marketing")
  public String generateMarketing(
      @RequestParam(value = "productName") String productName,
      @RequestParam(value = "features") String features) {

    String template = """
        제품명 {productName}의 마케팅 문구를 작성하세요.
        주요 특징: {features}
        조건: 감성적이고 100자 이내로 작성할 것.
        """;

    return chatClient.prompt()
        .user(u -> u.text(template)
            .param("productName", productName)
            .param("features", features))
        .call()
        .content();
  }

 

 

 

System Message를 활용한 역할 정의

 

AI에게 '전문가'라는 페르소나를 부여하면 답변의 품질이 비약적으로 향상됩니다. System Message는 AI의 행동 지침과 배경 지식을 설정하는 데 사용됩니다.

  • 우선순위: 모델은 사용자 메시지보다 시스템 메시지의 지침을 더 근본적인 규칙으로 인식합니다.
  • 활용: 말투 설정(존댓말/반말), 금기 사항 설정, 특정 분야 전문가 설정 등.

System Message 활용 예제:

  @GetMapping("/translate")
  public String translate(
      @RequestParam(value = "text") String text,
      @RequestParam(value = "targetLanguage", defaultValue = "영어") String targetLanguage) {

    return chatClient.prompt()
        // 1. AI의 페르소나 설정 (System Message)
        .system("당신은 전문 번역가입니다. 주어진 텍스트를 문맥에 맞게 자연스럽게 번역해주세요.")

        // 2. 동적 파라미터 주입 (Prompt Template)
        .user(u -> u.text("다음 텍스트를 {lang}로 번역해주세요: {text}")
            .param("lang", targetLanguage)
            .param("text", text))
        .call()
        .content();
  }

 

return chatClient.prompt()
    // 1. 시스템 메시지로 AI의 성격 고정 (고정값)
    .system("너는 맛집 추천 전문가야. 답변은 항상 '반말'로 친근하게 해줘.") 
    
    // 2. 프롬프트 템플릿으로 사용자 입력 가공 (변수 사용)
    .user(u -> u.text("오늘 {location} 근처에서 {food} 맛집 3곳 추천해줘.")
                .param("location", "강남역")
                .param("food", "삼겹살"))
    .call()
    .content();

 

 

텍스트 응답 vs 구조화된 응답 비교

 

LLM(거대언어모델)은 기본적으로 '자연어(Text)'를 출력하도록 설계되어 있습니다. 하지만 우리가 만드는 백엔드 서비스는 '객체(Object)'나 '데이터(Data)'를 필요로 합니다. 이 간극을 메워주는 것이 바로 구조화된 응답 기능입니다.

일반 응답 (String):
"이 리뷰는 긍정적입니다. 점수는 8점이고, 제품 품질이 우수합니다."

구조화된 응답 (Java Object):
ProductAnalysis {
  sentiment = "positive",
  score = 8,
  summary = "제품 품질이 우수합니다"
}

 

// ❌ 기존 방식 - 문자열 파싱
String response = chatClient.prompt()
    .user("이 리뷰를 분석해주세요: " + review)
    .call()
    .content();

// 결과: "긍정적이며, 8점입니다. 품질이 좋습니다."
// 문제점:
// 1. 파싱 로직이 복잡함
// 2. 형식이 일정하지 않음
// 3. 타입 안정성이 없음
// 4. 에러 처리가 어려움

String sentiment = extractSentiment(response); // 😰 복잡한 파싱
int score = extractScore(response);            // 😰 에러 가능성

// ✅ 권장 방식 - 타입 안정성 확보 및 파싱 자동화
ProductAnalysis result = chatClient.prompt()
    .user("이 리뷰 분석해줘: " + review)
    .call()
    .entity(ProductAnalysis.class); // Spring AI가 JSON 스키마 강제 및 파싱을 알아서 수행

int score = result.getScore(); // 😃 안전하게 바로 사용!

 

 

 

변하는 것 (Transient Technology)

도구와 프로토콜: MCP(Model Context Protocol), 특정 라이브러리, 프레임워크 등은 기술적 수단일 뿐이며 언제든 더 효율적인 것으로 대체됩니다. 여기에만 매몰되면 기술의 변화에 휩쓸리게 됩니다.

 

변하지 않는 것 (Immutable Essence)

컴퓨터 공학의 정점: 인간이 복잡한 로직을 하나하나 코딩하지 않아도, AI가 스스로 판단하고 수행할 수 있다는 '지능의 자동화'라는 본질은 변하지 않습니다.

문제 해결: 기술이 무엇이든 결국 "사용자의 문제를 해결하고 가치를 창출한다"는 서비스의 목적은 불변합니다.

 

 

 

기술을 선택할 때 이 질문을 해보세요.

  • 이 기술이 1년 후에도 존재할 것인가?
  • 이 기술이 바뀌면 우리 서비스의 핵심 가치도 바뀌는가?
  • 우리가 해결하려는 문제의 본질은 무엇인가?

 

LLM을 배우다 보면 좀 복잡하다.. 시스템, user,assitant 흠...

 

제미나이가 멀티모달에 좋다고한다. 모... 유투브도 있고...

 

 

로컬환경에서 LLM모델을 연결해보자

 

 먼저 GPU 없이 CPU만으로도 원활한 실행이 가능한 Ollama의 설치 및 운영체제별 설정 방법을 익히고, 한국어 성능이 뛰어난 경량화 모델인 Alibaba Qwen3를 로컬에 직접 구축하여 다국어 처리 역량을 테스트합니다.
최종적으로는 Spring AI 프레임워크를 활용해 설정부터 컨트롤러 구현까지의 전 과정을 실습함으로써, 클라우드 API를 사용하는 것과 동일한 방식으로 Spring Boot 애플리케이션에 로컬 LLM을 완벽히 통합하는 기술을 습득하게 됩니다.
 
 

 
로컬환경 LLM모델 연결
gpu말고 cpu만으로 원활한 실행이 가능한 Ollama ( AI 모델계의 Docker) 라는 녀석을 설치하고 운영하는것은 처음해본다. 
로컬저장이라 보안이 좋고 비용이 절감되는 장점이 있다.

사양은 일단 이렇다...  크기 작고 한국어가 우수한데 8GB이상이면 되니 괜찮은듯 하다.
meta의 Llama를 알고 있었는데 구글의 Gemma 그리고 Microsoft의 Phi-3은 처음본다. 그리고 Qwen2.5와 Mistral 은 어떨까?
일단 여기서 Qwen이 작고 저사양에서도 돌아가고 다국어지원에 한국어 우수라 추천한듯하다.

 

# Ollama 컨테이너 실행 (데이터 보존을 위한 볼륨 설정 및 포트 매핑)

docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

설치도 쉽다...

docker exec -it ollama ollama run qwen2.5:3b  성능확인도 해봤다..

 
익히 들어온 하드웨어와 gpu 빨이다...  권장사양을 보니 속도가 10배향상, 16GB는 다중작업 필수 SSD와 OS는 

클로드는 이런데... 

 

 

올라마에서... 비용절감과 효율성을 위해 튜닝설정을 좀해야 한다.

 

 

미니 테스트로 좋은 올라마.. 도커스럽다... 어쨌건 llm도 cpu만으로 테스트할수 있었다!!

LIST
JDBC에서 Statement와 PreparedStatement는 모두 SQL 실행을 담당하지만, 사용 방식과 성능, 보안 측면에서 차이가 존재합니다.

 

Statement 클래스는 문자열 연결을 이용해 SQL을 동적으로 구성해야 합니다. 이러한 특성으로 인해 SQL 인젝션 공격에 취약하다는 단점이 있습니다.

Statement stmt = conn.createStatement();
ResultSet rs =  30")" >stmt.executeQuery("select * from users where age > 30");

반면, PreparedStatement는 동적으로 파라미터를 바인딩할 수 있는 기능을 제공합니다. 값을 바인딩하면 내부적으로 이스케이프 처리하기 때문에 SQL 인젝션 공격을 방지할 수 있습니다.

String sql = "select * from users where age > ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 30);
ResultSet rs = pstmt.executeQuery();

또한, 쿼리 구조를 미리 확정하고 플레이스홀더를 활용하여 값을 바인딩하는 PreparedStatement를 사용하면 SQL 구문 분석 결과를 캐싱할 수 있어 반복 실행 시 Statement보다 성능이 높은 것으로 알려져 있습니다.

LIST

ChatGPT에게 "이메일 써줘"라고 하면 그럭저럭 결과가 나온다. 그런데 "이런 톤으로, 이런 구조로" 예시를 2~3개 먼저 보여주면 결과가 완전히 달라진다. 이것이 Few-Shot Prompting이다.

Fine-tuning처럼 모델을 재훈련하는 것도 아니고, RAG처럼 외부 데이터를 붙이는 것도 아니다. 그냥 프롬프트 안에 예시를 몇 개 넣는 것뿐인데, 왜 이렇게 효과가 큰 걸까?

Few-Shot이란 무엇인가

기계학습에서 Few-Shot Learning은 원래 "적은 데이터로 새로운 태스크를 학습하는 능력"을 뜻한다. 수천 장의 고양이 사진 없이도 3~4장만 보고 고양이를 인식하는 것처럼.

LLM 시대에서 Few-Shot은 의미가 약간 달라졌다. 모델을 재훈련하는 게 아니라, 프롬프트 안에 예시를 넣어서 모델의 출력 패턴을 유도하는 기법이다. GPT-3 논문(2020)에서 본격적으로 정의한 개념으로, 이후 프롬프트 엔지니어링의 가장 기본적인 테크닉이 되었다.

용어를 정리하면 이렇다.

  • Zero-Shot: 예시 없이 지시만 내림. "이 문장의 감정을 분석해줘."
  • One-Shot: 예시 1개 제공. "예: '배송이 빨라요' → 긍정. 이제 이 문장을 분석해줘."
  • Few-Shot: 예시 2~5개 제공. 패턴을 더 명확하게 잡아줌.

왜 예시 몇 개가 이렇게 강력한가

LLM은 본질적으로 패턴 매칭 기계다. 다음에 올 토큰을 예측하는 것이 전부인데, 프롬프트 안에 입력-출력 쌍을 넣으면 모델이 "아, 이 패턴을 반복하면 되는구나"라고 추론한다. 학습이 아니라 문맥 내 추론(In-Context Learning)이다.

핵심은 암묵적 규칙의 전달에 있다. "JSON으로 응답해줘"라고 말로 설명하는 것보다, 실제 JSON 예시를 3개 보여주는 게 훨씬 정확하다. 말로 표현하기 어려운 톤, 구조, 포맷, 판단 기준 같은 것들이 예시를 통해 자연스럽게 전달된다.

실제로 이런 차이가 난다.

Zero-Shot 예시

 
 
다음 고객 리뷰의 감정을 분석해줘.
"배송은 빨랐는데 포장이 엉망이었어요"

모델 응답: "이 리뷰는 긍정과 부정이 혼합되어 있습니다. 배송 속도에 대해서는 만족하지만..."
→ 장황하고, 출력 포맷이 일정하지 않다.

Few-Shot 예시

 
고객 리뷰를 분석해서 아래 형식으로 응답해.

리뷰: "제품 품질이 정말 좋아요"
결과: { "감정": "긍정", "키워드": ["품질"], "점수": 0.9 }

리뷰: "환불 처리가 너무 느려요"
결과: { "감정": "부정", "키워드": ["환불", "느림"], "점수": 0.2 }

리뷰: "가격 대비 괜찮은데 AS가 아쉬워요"
결과: { "감정": "혼합", "키워드": ["가격", "AS"], "점수": 0.5 }

리뷰: "배송은 빨랐는데 포장이 엉망이었어요"
결과:

모델 응답: { "감정": "혼합", "키워드": ["배송", "포장"], "점수": 0.4 }
→ 포맷 정확, 판단 기준 일관, 후처리 파싱 가능.

예시 3개를 추가했을 뿐인데 출력의 품질과 일관성이 완전히 달라졌다.

실전에서 Few-Shot을 잘 쓰는 법

1. 예시의 다양성이 핵심이다

비슷한 예시 3개보다 서로 다른 케이스 3개가 낫다. 위의 감정 분석 예시에서도 긍정/부정/혼합을 골고루 넣었다. 모델이 "긍정만 답하면 되나?"라고 오해할 여지를 차단하는 것이다.

2. 예시 순서가 결과에 영향을 준다

LLM은 마지막 예시에 더 강하게 영향받는 경향이 있다(recency bias). 가장 전형적인 케이스를 마지막에 배치하거나, 어려운 케이스를 뒤에 놓으면 까다로운 입력에 대한 처리가 좋아진다.

3. 네거티브 예시를 포함하라

"이렇게 하지 마라"를 말로 쓰는 것보다, 잘못된 출력과 올바른 출력을 대비시키는 게 효과적이다.

 
 
# 나쁜 예시 (이렇게 하지 말 것)
입력: "서버 에러 500"
출력: "서버에 문제가 있는 것 같습니다. 관리자에게 문의하세요."

# 좋은 예시 (이렇게 할 것)
입력: "서버 에러 500"
출력: "500 Internal Server Error. 1) 서버 로그 확인 2) 최근 배포 변경사항 점검 3) DB 연결 상태 확인"

4. 3~5개가 최적 구간이다

1개는 우연일 수 있고, 10개는 토큰 낭비다. 대부분의 태스크에서 3~5개 예시면 모델이 패턴을 충분히 파악한다. 단, 출력 구조가 복잡할수록(중첩 JSON, 다단계 추론) 예시를 늘려야 한다.

Few-Shot vs 다른 기법들

Few-Shot이 만능은 아니다. 상황에 따라 다른 기법이 더 적합할 수 있다.

Fine-tuning이 나은 경우: 동일한 태스크를 수만 번 반복 실행할 때. 매번 프롬프트에 예시를 넣는 토큰 비용보다 모델을 한 번 튜닝하는 게 경제적이다. 의료 진단, 법률 문서 분류처럼 도메인 특화 정확도가 중요할 때도 마찬가지.

RAG가 나은 경우: 모델이 모르는 최신 정보나 내부 문서를 참조해야 할 때. Few-Shot은 "어떻게 답할지"를 가르치고, RAG는 "무엇을 참고할지"를 제공한다. 둘은 경쟁이 아니라 조합 관계다.

Chain-of-Thought가 나은 경우: 수학 문제, 논리 추론처럼 단계별 사고가 필요한 태스크. 이때는 Few-Shot 예시 안에 추론 과정을 포함시키는 "Few-Shot CoT"가 가장 강력하다.

실무에서 가장 흔한 조합은 Few-Shot + RAG다. RAG로 관련 문서를 검색하고, Few-Shot으로 응답 포맷을 지정하는 패턴이다.

개발자를 위한 실전 패턴

패턴 1: 코드 변환기

# TypeScript → Python 변환기

TypeScript:
const greet = (name: string): string => `Hello, ${name}`;

Python:
def greet(name: str) -> str:
    return f"Hello, {name}"

---

TypeScript:
const sum = (arr: number[]): number => arr.reduce((a, b) => a + b, 0);

Python:
def sum_arr(arr: list[int]) -> int:
    return sum(arr)

---

TypeScript:
{변환할 코드}

Python:

패턴 2: API 응답 정규화

다양한 외부 API 응답을 우리 내부 포맷으로 변환해.

입력: { "user_name": "kim", "created": "2024-01-15T09:00:00Z" }
출력: { "name": "kim", "createdAt": "2024-01-15", "source": "external" }

입력: { "firstName": "lee", "timestamp": 1705312800 }
출력: { "name": "lee", "createdAt": "2024-01-15", "source": "external" }

입력: {실제 API 응답}
출력:

패턴 3: 커밋 메시지 생성

git diff를 보고 Conventional Commits 형식으로 커밋 메시지를 작성해.

diff: +const validateEmail = (email) => /^[^\s@]+@[^\s@]+$/.test(email);
message: feat: add email validation utility function

diff: -timeout: 3000 +timeout: 5000
message: fix: increase API timeout to prevent request failures

diff: {실제 diff}
message:

Few-Shot의 한계와 주의점

만능이 아닌 지점도 분명하다.

토큰 비용: 예시 5개 × 200토큰이면 매 요청마다 1,000토큰이 추가된다. 대량 처리 시 비용이 누적되므로, 안정화된 태스크는 Fine-tuning으로 전환을 고려해야 한다.

예시 편향: 예시가 특정 패턴에 치우치면 모델도 치우친다. 긍정 예시만 3개 넣으면 부정 입력에도 긍정으로 답하는 경향이 생긴다. 의도적으로 엣지 케이스를 포함시켜야 한다.

복잡한 추론의 한계: Few-Shot만으로 다단계 논리 추론을 유도하기 어렵다. 이때는 Few-Shot CoT(Chain-of-Thought)로 예시 안에 추론 과정을 명시해야 한다.

마치며

Few-Shot Prompting은 프롬프트 엔지니어링에서 가장 투자 대비 효과가 큰 기법이다. 모델을 재훈련하지 않고, 외부 시스템을 구축하지 않고, 프롬프트에 예시 몇 줄을 추가하는 것만으로 출력 품질이 극적으로 달라진다.

"AI를 잘 쓴다"는 것은 결국 "AI에게 내가 원하는 것을 정확히 전달하는 것"이다. 그리고 인간이 무언가를 가장 정확하게 전달하는 방법은 예시를 보여주는 것이었다. 수천 년 전 도제식 교육에서도, 2025년 LLM 프롬프트에서도, 원리는 같다.

말로 백 번 설명하는 것보다 한 번 보여주는 게 낫다. Few-Shot은 그 오래된 진리의 AI 버전이다.

LIST

+ Recent posts