> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sajuapi.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 데이터 마스킹

> 민감한 데이터 암호화 및 복호화 가이드

## 개요

사주 API는 개인정보보호법 준수를 위해 모든 민감한 데이터를 AES-256-GCM으로 암호화합니다.
기본 API 응답은 마스킹된 데이터를 반환하며, 복호화가 필요한 경우 별도의 `/unmasked` 엔드포인트를 사용합니다.

***

## 암호화 방식

### AES-256-GCM

* **알고리즘**: AES (Advanced Encryption Standard)
* **키 길이**: 256비트
* **모드**: GCM (Galois/Counter Mode)
* **인증**: 내장 무결성 검증

```
┌─────────────────────────────────────────────────────────────┐
│                      원본 데이터                             │
│                    "김철수, 1990-03-15"                       │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    AES-256-GCM 암호화                        │
│  ┌────────┐  ┌────────────┐  ┌─────────────────────────┐   │
│  │ IV 12B │  │ Auth Tag   │  │   Encrypted Data        │   │
│  │        │  │    16B     │  │                         │   │
│  └────────┘  └────────────┘  └─────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    Base64 인코딩 저장
```

### 키 관리

* **키 생성**: 암호학적으로 안전한 난수 생성기 사용
* **키 저장**: HSM (Hardware Security Module) 또는 클라우드 KMS
* **키 순환**: 분기별 자동 순환, 이전 키는 복호화용으로 보관

***

## 마스킹 규칙

### 프로필 데이터

| 필드            | 원본 예시 | 마스킹 결과 | 규칙       |
| ------------- | ----- | ------ | -------- |
| `name`        | 김철수   | 김\*\*  | 첫 글자 유지  |
| `birth_year`  | 1990  | 19\*\* | 앞 2자리 유지 |
| `birth_month` | 3     | \*\*   | 전체 마스킹   |
| `birth_day`   | 15    | \*\*   | 전체 마스킹   |
| `birth_hour`  | 14    | \*\*   | 전체 마스킹   |

### 비마스킹 필드

다음 필드는 민감하지 않아 마스킹하지 않습니다:

* `id` (프로필/운세 ID)
* `day_master` (일주)
* `gender` (성별)
* `created_at`, `updated_at` (타임스탬프)
* `score` (운세 점수)

***

## 복호화 엔드포인트

### 프로필 복호화

```bash theme={null}
GET /v1/profiles/{id}/unmasked
```

### 운세 복호화

```bash theme={null}
GET /v1/fortunes/{id}/unmasked
```

<Warning>
  모든 복호화 요청은 감사 로그에 기록됩니다.
</Warning>

***

## 구현 예시

### 클라이언트 측 처리

```typescript theme={null}
interface Profile {
  id: string;
  name: string;  // 마스킹됨: "김**"
  birth_year: string;  // 마스킹됨: "19**"
  day_master: string;
}

interface UnmaskedProfile extends Omit<Profile, 'name' | 'birth_year'> {
  name: string;  // 원본: "김철수"
  birth_year: number;  // 원본: 1990
  birth_month: number;
  birth_day: number;
  birth_hour: number;
  saju: SajuObject;
}

// 일반 조회 (마스킹)
async function getProfile(id: string): Promise<Profile> {
  const response = await fetch(`/v1/profiles/${id}`, {
    headers: { 'X-API-Key': API_KEY }
  });
  return response.json();
}

// 복호화 조회 (권한 필요)
async function getUnmaskedProfile(id: string): Promise<UnmaskedProfile> {
  const response = await fetch(`/v1/profiles/${id}/unmasked`, {
    headers: { 'X-API-Key': API_KEY }
  });
  return response.json();
}
```

### UI 표시 예시

```jsx theme={null}
function ProfileCard({ profile, showUnmasked }) {
  const [unmasked, setUnmasked] = useState(null);

  const handleUnmask = async () => {
    // 복호화 사유 입력 (감사 로그용)
    const reason = await promptReason();
    const data = await getUnmaskedProfile(profile.id);
    setUnmasked(data);

    // 일정 시간 후 자동 마스킹
    setTimeout(() => setUnmasked(null), 30000);
  };

  return (
    <div>
      <p>이름: {unmasked?.name || profile.name}</p>
      <p>생년: {unmasked?.birth_year || profile.birth_year}</p>
      <p>일주: {profile.day_master}</p>

      {showUnmasked && !unmasked && (
        <button onClick={handleUnmask}>전체 정보 보기</button>
      )}
    </div>
  );
}
```

***

## 보안 권장사항

<CardGroup cols={2}>
  <Card title="최소 권한 원칙" icon="shield">
    복호화 권한은 필요한 사용자에게만 부여하세요.
    대부분의 기능은 마스킹된 데이터로 충분합니다.
  </Card>

  <Card title="시간 제한" icon="clock">
    복호화된 데이터는 UI에서 일정 시간 후 자동으로 숨기세요.
    30초-1분 권장.
  </Card>

  <Card title="로컬 저장 금지" icon="database">
    복호화된 데이터를 localStorage나 DB에 저장하지 마세요.
    필요할 때마다 API를 호출하세요.
  </Card>

  <Card title="사유 기록" icon="pen">
    복호화 요청 시 사유를 입력받아 감사 로그에 포함하세요.
    추후 감사 시 유용합니다.
  </Card>
</CardGroup>

***

## 규정 준수

### 개인정보보호법 (한국)

| 조항   | 요구사항       | 사주 API 대응         |
| ---- | ---------- | ----------------- |
| 제24조 | 고유식별정보 암호화 | AES-256-GCM 암호화   |
| 제29조 | 안전조치 의무    | HSM 키 관리, 감사 로그   |
| 제35조 | 열람 요구권     | `/unmasked` 엔드포인트 |
| 제36조 | 정정·삭제 요구권  | PUT, DELETE 엔드포인트 |

### GDPR (유럽)

| 조항         | 요구사항                   | 사주 API 대응         |
| ---------- | ---------------------- | ----------------- |
| Article 17 | Right to erasure       | 소프트 삭제 + 하드 삭제 옵션 |
| Article 20 | Data portability       | CSV 내보내기 지원       |
| Article 32 | Security of processing | 암호화, 접근 제어, 감사 로그 |

***

## FAQ

<Accordion title="마스킹된 데이터로 사주 계산이 가능한가요?">
  네, 사주 계산은 서버에서 수행되며 결과(일주, 오행 등)는 마스킹되지 않습니다.
  생년월일 원본은 서버에서만 처리되고 응답에는 마스킹됩니다.
</Accordion>

<Accordion title="복호화 API 호출 시 추가 비용이 있나요?">
  아니요, 복호화 API는 일반 API 호출과 동일하게 요청 수에 포함됩니다.
  단, 엔터프라이즈 플랜 이상에서만 사용 가능합니다.
</Accordion>

<Accordion title="암호화 키가 유출되면 어떻게 되나요?">
  키 유출 감지 시 즉시 새 키로 모든 데이터를 재암호화합니다.
  이전 키로는 더 이상 복호화할 수 없습니다.
  24/7 보안 모니터링으로 이상 징후를 실시간 감지합니다.
</Accordion>
