101 lines
3.5 KiB
Markdown
101 lines
3.5 KiB
Markdown
---
|
|
tags: 함수형 프로그래밍,부작용 분리,에이전트 설계,스킬,실전 사례
|
|
date: 2025-06-17
|
|
---
|
|
|
|
요약
|
|
- 함수형 프로그래밍은 입력과 출력만을 다루는 **순수 함수**를 통해 코드의 예측 가능성과 테스트 효율을 극대화합니다.
|
|
- 데이터베이스 쓰기나 네트워크 호출처럼 외부 상태를 변경하는 **부작용**은 별도의 오케스트레이터 계층에서 처리해야 합니다.
|
|
- 이렇게 계층을 분리하면 스킬·아이템 모듈을 안전하게 병렬 실행하고, 버전 교체와 롤백을 간단하게 수행할 수 있습니다.
|
|
|
|
# 함수형과 부작용 분리 실전 사례
|
|
|
|
## 1. 문서 요약 스킬
|
|
|
|
### 순수 함수 계층
|
|
```python
|
|
from typing import List
|
|
|
|
def summarize(paragraphs: List[str]) -> str:
|
|
"""항상 동일 입력에 동일 출력을 보장하는 요약 함수"""
|
|
merged = " ".join(paragraphs)
|
|
sentences = merged.split(".")
|
|
return ". ".join(sentences[:3]) + "."
|
|
```
|
|
|
|
### 오케스트레이터 계층
|
|
```python
|
|
from db import save_summary
|
|
from summarize import summarize
|
|
from slack_webhook import post_to_slack
|
|
|
|
def handle_request(doc_id: str, paragraphs: list[str]) -> None:
|
|
summary = summarize(paragraphs) # 순수 계산
|
|
save_summary(doc_id, summary) # 부작용: DB 기록
|
|
post_to_slack(summary) # 부작용: 네트워크 호출
|
|
```
|
|
|
|
## 2. 전자상거래 수수료·세금 계산
|
|
|
|
### 순수 함수 계층
|
|
```typescript
|
|
// fee.ts
|
|
export type Basket = { price: number; quantity: number }[];
|
|
|
|
export function calcTotals(basket: Basket) {
|
|
const subtotal = basket.reduce(
|
|
(acc, item) => acc + item.price * item.quantity, 0);
|
|
const vat = subtotal * 0.1;
|
|
const platformFee = subtotal * 0.03;
|
|
return { subtotal, vat, platformFee, total: subtotal + vat + platformFee };
|
|
}
|
|
```
|
|
|
|
### 오케스트레이터 계층
|
|
```typescript
|
|
// checkout.ts
|
|
import { chargeCard } from "./paymentGateway";
|
|
import { recordOrder } from "./db";
|
|
import { calcTotals } from "./fee";
|
|
|
|
export async function checkout(userId: string, basket: Basket) {
|
|
const totals = calcTotals(basket); # 순수 계산
|
|
await chargeCard(userId, totals.total); # 부작용: 결제 게이트웨이
|
|
await recordOrder(userId, basket, totals); # 부작용: DB 기록
|
|
}
|
|
```
|
|
|
|
## 3. 실시간 알림 브로드캐스트 (Elixir OTP)
|
|
|
|
### 순수 함수 계층
|
|
```elixir
|
|
defmodule Alerts.Core do
|
|
@spec reduce_state(map(), %{type: atom(), payload: map()}) :: map()
|
|
def reduce_state(state, %{type: :new_follow, payload: %{user: u}}) do
|
|
Map.update(state, :followers, [u], &[u | &1])
|
|
end
|
|
|
|
def reduce_state(state, _event), do: state
|
|
end
|
|
```
|
|
|
|
### 오케스트레이터 계층
|
|
```elixir
|
|
defmodule Alerts.Server do
|
|
use GenServer
|
|
alias Alerts.Core
|
|
|
|
def handle_cast({:event, event}, state) do
|
|
new_state = Core.reduce_state(state, event) # 순수 계산
|
|
broadcast_changes(state, new_state) # 부작용: 웹소켓 푸시
|
|
{:noreply, new_state}
|
|
end
|
|
end
|
|
```
|
|
|
|
# 핵심 교훈
|
|
|
|
1. **순수 함수 계층**은 입력만 받아 동일한 출력을 보장하므로 테스트가 간단하고 캐싱·병렬 실행에 유리합니다.
|
|
2. **오케스트레이터 계층**은 순수 함수의 결과를 실제로 저장하거나 전송하면서 부작용을 집중 관리하므로 감사·롤백·보안 검증이 명확해집니다.
|
|
3. 스킬·아이템을 순수 함수로 작성하고, 코어에서 부작용을 처리하는 구조를 적용하면 로빈 같은 멀티모달 에이전트 시스템에서도 안정성과 유지보수성이 크게 향상됩니다.
|