사주 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 (운세 점수)
복호화 엔드포인트
프로필 복호화
GET /v1/profiles/{id}/unmasked
운세 복호화
GET /v1/fortunes/{id}/unmasked
구현 예시
클라이언트 측 처리
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 표시 예시
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>
);
}
보안 권장사항
최소 권한 원칙
복호화 권한은 필요한 사용자에게만 부여하세요.
대부분의 기능은 마스킹된 데이터로 충분합니다.
시간 제한
복호화된 데이터는 UI에서 일정 시간 후 자동으로 숨기세요.
30초-1분 권장.
로컬 저장 금지
복호화된 데이터를 localStorage나 DB에 저장하지 마세요.
필요할 때마다 API를 호출하세요.
사유 기록
복호화 요청 시 사유를 입력받아 감사 로그에 포함하세요.
추후 감사 시 유용합니다.
규정 준수
개인정보보호법 (한국)
| 조항 | 요구사항 | 사주 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
네, 사주 계산은 서버에서 수행되며 결과(일주, 오행 등)는 마스킹되지 않습니다.
생년월일 원본은 서버에서만 처리되고 응답에는 마스킹됩니다.
아니요, 복호화 API는 일반 API 호출과 동일하게 요청 수에 포함됩니다.
단, 엔터프라이즈 플랜 이상에서만 사용 가능합니다.
키 유출 감지 시 즉시 새 키로 모든 데이터를 재암호화합니다.
이전 키로는 더 이상 복호화할 수 없습니다.
24/7 보안 모니터링으로 이상 징후를 실시간 감지합니다.