보안은 나중에 추가할 수 없다
기능은 나중에 붙일 수 있습니다. 버튼 하나, 화면 하나는 다 만든 뒤에도 끼워 넣으면 됩니다. 그런데 인증 방식, 권한 체계, 개인정보 보호, 로그 정책 — 이 네 가지는 그렇지 않습니다. 이것들은 벽지가 아니라 기둥이라서, 수만 줄을 쌓아 올린 뒤에 바꾸려면 집을 통째로 다시 세워야 합니다. 이 글은 “왜 보안은 처음에 정하는 것이지 나중에 추가하는 것이 아닌가”를, 보안을 처음 설계하는 분을 위해 가능한 한 쉽게 풀어 쓴 기록입니다.
먼저 용어부터
섹션 제목: “먼저 용어부터”보안 이야기는 낯선 약어가 많아 입구에서 막히기 쉽습니다. 그래서 본론에 들어가기 전에 자주 나올 말을 한 줄씩 정의해 둡니다. 이미 아는 분은 건너뛰어도 좋습니다.
나중에 못 바꾸는 네 가지
섹션 제목: “나중에 못 바꾸는 네 가지”개발을 시작할 때 머릿속에는 늘 “일단 동작부터, 보안은 나중에”라는 유혹이 있습니다. 그 유혹이 위험한 이유는, 보안에는 한 번 정하면 사실상 되돌릴 수 없는 결정이 섞여 있기 때문입니다. 다음 네 가지가 그렇습니다.
- 인증 방식 — 토큰 기반인가, 세션 기반인가. 이걸 바꾸면 로그인 화면 하나가 아니라, 보호된 모든 경로의 출입 검사 로직이 영향을 받습니다.
- 권한 체계 — 역할 기반(RBAC)으로 갈 것인가. 나중에 끼워 넣으려면 이미 만들어 둔 모든 API에 권한 검사를 일일이 덧대야 합니다.
- 개인정보(PII) 보호 — 평문으로 쌓아 둔 데이터를 뒤늦게 암호화하는 건, 화면 코드 수정이 아니라 저장된 모든 행을 다시 쓰는 전수 마이그레이션입니다.
- 로그에 남기면 안 되는 데이터 목록 — 비밀번호나 토큰을 일단 로그에 흘려 쓰고 나면, 어디서 새는지 거꾸로 추적하기가 매우 어렵습니다.
왜 이것들만 유독 바꾸기 어려울까요? 공통점이 하나 있습니다. 네 가지 모두 **코드 한 줄이 아니라 “설계의 전제”**라는 점입니다. 전제는 그 위에 쌓인 모든 것에 영향을 줍니다. 그래서 전제가 빠진 채로 기능을 높이 쌓을수록, 나중에 전제를 끼워 넣는 비용은 더해지는 게 아니라 곱해집니다.
두 줄의 방어 — 그림으로 보는 PII 보호
섹션 제목: “두 줄의 방어 — 그림으로 보는 PII 보호”네 가지 중 가장 오해받기 쉬운 게 개인정보 보호입니다. 많은 분이 “암호화 한 번이면 끝 아닌가?”라고 생각합니다. 그런데 민감 정보가 새는 길은 둘입니다 — 하나는 저장소에 가만히 있을 때(누가 DB를 통째로 가져가면?), 다른 하나는 응답·로그를 타고 밖으로 나갈 때(API 응답이나 AI 답변에 주민번호가 섞여 나가면?). 그래서 막는 곳도 둘이어야 합니다.
SL.AIMS에서는 주민번호 같은 민감 정보를 애플리케이션 키로 암호화해 저장하고, 응답·AI 경로에서는 개인정보 패턴(주민번호·전화·이메일·계좌 등)을 정규식으로 가려서 내보냅니다. 정지 상태에서도 한 번, 흘러나가는 길목에서도 한 번 — 두 줄의 방어선입니다.
특히 AI를 끼운 시스템에서는 마스킹이 더 중요해집니다. 사람이 짠 코드는 “이 필드는 화면에 안 띄운다”를 명시적으로 통제하지만, AI는 받은 데이터를 요약하거나 설명하는 과정에서 예상 못 한 형태로 민감 정보를 답변에 섞을 수 있습니다. 그래서 “AI에게 넘기기 전에/AI가 답하기 전에 한 번 가린다”는 길목 방어가, 단순 화면 표시 통제보다 한 겹 더 필요합니다. 막는 위치가 다르기 때문에, 암호화가 있다고 마스킹을 생략할 수 없고 그 반대도 마찬가지입니다.
인증과 권한 — 처음에 정해 둔 모양
섹션 제목: “인증과 권한 — 처음에 정해 둔 모양”SL.AIMS는 인증을 토큰 기반으로, 권한을 역할 기반으로 처음부터 단일 백엔드 안에 두기로 ADR에 못 박았습니다. 토큰 기반이란, 로그인에 성공하면 일종의 출입증(토큰)을 발급하고 이후 요청마다 그 출입증을 확인하는 방식입니다. 화면이 늘어나도 출입 검사 로직은 한 군데서 일관되게 동작합니다.
권한은 “어느 기능을 누가 호출할 수 있는가”를 역할 단위로 통제합니다. 사람마다 권한을 일일이 매기면 사람이 늘 때마다 손이 가지만, 역할에 권한을 묶어 두면 사람은 역할에만 배정하면 됩니다. 신입이 들어와도 “인사팀 역할” 하나만 주면 그 역할의 권한이 그대로 따라옵니다. 반대로 권한 체계를 처음에 안 잡고 기능부터 만들면, 나중엔 API마다 “이건 누가 부를 수 있더라?”를 제각각 판단하게 되고, 그 판단이 흩어질수록 보안 구멍은 늘어납니다.
여기서 한 가지 현실적인 고민이 더 있습니다. 역할 체계를 코드에 박힌 고정값으로만 두면, 새 역할이 필요할 때마다 코드를 고쳐야 합니다. 그렇다고 처음부터 DB로만 관리하면 기존에 발급된 출입증(토큰)이 한순간에 무효가 될 위험이 있습니다. SL.AIMS는 이 둘을 **“둘 중 하나만 맞아도 통과”**하는 방식으로 함께 읽도록 설계했습니다 — 고정값 역할과 DB 관리 역할을 동시에 인정하는 것입니다. 덕분에 역할 체계를 고정값에서 DB 관리로 끊김 없이 옮겨 갈 수 있었습니다. 비용은 있습니다. 권한이 이상할 때 두 곳을 다 들여다봐야 합니다. 그래도 “빅뱅으로 한 번에 갈아엎다 기존 사용자를 다 튕겨 내는 것”보다는 안전한 거래였습니다.
처음에 정하면 좋은 “정책” 항목들
섹션 제목: “처음에 정하면 좋은 “정책” 항목들”- 인증 방식 고정 — 토큰인가 세션인가. 출입증을 어떻게 발급·검증할지 한 가지로 정합니다.
- 권한 모델 고정 — 역할 기반으로 갈지, 권한 검사를 어디(공통 길목/개별 기능)에 둘지 정합니다.
- 토큰 수명 정책 — 출입증을 얼마나 오래 유효하게 둘지. 너무 길면 탈취 시 위험, 너무 짧으면 사용자가 자주 재로그인. 이건 보안과 사용성 사이의 의도된 트레이드오프라, 정답이 아니라 “우리 상황에 맞는 선택”을 명시적으로 적어 둡니다.
- 로그 금지 목록 — 비밀번호·토큰·시크릿은 절대 로그에 남기지 않는다고 규칙으로 선언합니다.
| 결정 | 처음에 정함 | 나중에 바꾸면 |
|---|---|---|
| 인증 방식 | 토큰 기반으로 일관 | 보호된 모든 경로의 출입 검사 재작성 |
| 권한 체계 | 역할 기반(RBAC) | 이미 만든 모든 API에 검사 덧대기 |
| PII 저장 | 저장 시 암호화 | 저장된 모든 행 전수 재암호화 |
| 로그 정책 | 금지 목록 선언 | 새는 지점을 거꾸로 추적 (어려움) |
네 기둥은 하나의 사슬이다
섹션 제목: “네 기둥은 하나의 사슬이다”이 네 항목은 서로 독립적인 것 같지만, 사실 하나의 사슬로 엮여 있습니다. 인증이 신원을 확인하고, 권한이 그 신원의 행동 범위를 정하고, PII 보호가 민감 데이터를 가리고, 로그 정책이 그 모든 활동의 흔적을 안전하게 남깁니다. 한 고리가 빠지면 사슬 전체가 약해집니다. 그래서 네 개를 “필요할 때 하나씩”이 아니라 “처음에 한 묶음으로” 정하는 게 맞습니다.
| 보안 기둥 | 막는 질문 | 처음에 안 잡으면 |
|---|---|---|
| 인증 | ”너 진짜 그 사람 맞아?” | 출입 검사가 경로마다 제각각 |
| 권한 | ”그걸 해도 되는 사람이야?” | 누가 무엇을 부를 수 있는지 흩어짐 |
| PII 보호 | ”민감 정보가 새진 않아?” | 평문 누적 → 전수 재암호화 |
| 로그 정책 | ”흔적이 안전하게 남아?” | 비밀이 로그에 흘러 추적 불가 |
완벽이 아니라 “게이트를 거는 것”
섹션 제목: “완벽이 아니라 “게이트를 거는 것””솔직하게 말하면, 1인 개발이라 처음부터 모든 게 완벽하진 않았습니다. 호출 빈도 제한이나 레이어 경계를 강제하는 검증 게이트 같은 것들은 초기에 자리를 잡아 두지 못해, 나중에 인식하고 보강해야 했습니다. 여기서 얻은 교훈이 핵심입니다.
완벽한 보안을 처음에 다 만들 순 없어도, “검증 게이트를 거는 자리”는 처음에 잡아야 한다.
둘은 다른 이야기입니다. 자리가 잡혀 있으면 거기에 규칙을 채우는 일은 나중에도 얼마든지 가능합니다. 그런데 자리 자체가 없으면, 뒤늦게 그 자리를 만드는 것부터가 큰 공사가 됩니다. 그래서 “처음엔 빈 게이트라도 자리는 만들어 둔다”가 1인 개발에서 현실적으로 가장 남는 전략이었습니다.
| 자리가 없을 때 | 자리만 잡아 뒀을 때 |
|---|---|
| 나중에 호출 제한·권한 검사·마스킹을 넣으려면, 그걸 꽂을 공통 지점부터 새로 만들어야 한다. 이미 수많은 경로가 제각각이라 일관되게 끼우기 어렵다. | 모든 요청이 지나는 공통 길목이 처음부터 있으면, 거기에 규칙을 한 줄씩 채우면 된다. 빈 게이트라도 자리가 있으면 보강은 점진적으로 가능하다. |
관련 글: 〈감사로그와 백업의 자리〉, 〈대형 파일은 DB에 넣지 않는다〉. 전체 그림은 〈사례연구: SL.AIMS〉에 있습니다.