파일을 암호화 상태로 보관하는 DRM 연동 설계

업로드, 저장, 미리보기, 다운로드를 분리한 DRM 파일 처리 흐름

파일을 업로드할 수 있는 서비스에서 “파일을 암호화한다”는 말은 암호화 함수 하나를 호출하는 것보다 넓은 문제다. 언제 암호화할지, 미리보기에서는 어디까지 평문을 허용할지, 이미 저장된 레거시 파일을 어떻게 읽을지, 실패했을 때 어떤 응답을 돌려줄지까지 함께 결정해야 한다.

이번 작업에서는 Java 서버에 기업용 DRM SDK를 연결해 파일을 암호화 상태로 저장하고, 미리보기 요청이 들어온 순간에만 임시 복호화하는 구조를 만들었다. 특정 회사나 내부 시스템에 종속되지 않도록 구현 원칙과 장애 대응 중심으로 정리했다.

먼저 정한 보안 경계

가장 중요한 원칙은 저장소에 평문 파일을 남기지 않는 것이다.

기능 서버가 처리하는 방식
업로드 암호화 상태를 확인하고 필요하면 서버에서 암호화한 뒤 저장
미리보기 임시 파일로 복호화하고 스트리밍이 끝나면 즉시 삭제
다운로드 저장된 암호화 원본을 그대로 전송
레거시 파일 기존 평문 파일은 호환성을 위해 읽되 신규 저장은 암호화
이미지 DRM 적용 대상에서 제외하고 일반 이미지 흐름으로 처리

다운로드까지 복호화하지 않는 이유도 분명하다. 사용자가 파일을 내려받는 순간에는 원본 파일을 전달하면 되므로, 서버가 평문 사본을 만들 필요가 없다. 평문이 필요한 구간을 미리보기로 제한하면 임시 파일의 생명주기와 접근 로그도 관리하기 쉬워진다.

업로드는 암호화 상태 확인부터 시작한다

처음에는 암호화되지 않은 문서를 업로드하면 거절하는 방식으로 시작했다. 정책을 명확하게 적용할 수 있다는 장점이 있지만, 사용자가 별도 DRM 도구로 파일을 다시 암호화해야 하는 불편이 생겼다.

최종 흐름은 다음과 같이 조정했다.

  1. 확장자, 크기, 기본 파일 검증을 수행한다.
  2. DRM SDK로 암호화 상태를 확인한다.
  3. 이미 암호화되어 있으면 원본을 저장한다.
  4. 암호화되지 않은 PDF처럼 서버에서 안전하게 처리할 수 있는 형식은 임시 파일로 암호화한다.
  5. 암호화 결과를 원래 저장 위치에 기록하고 임시 파일을 정리한다.

이때 저장소가 UUID처럼 확장자가 없는 이름을 사용하더라도, DRM SDK에 넘기는 임시 파일에는 원래 확장자를 유지해야 한다. 일부 SDK는 파일의 종류를 확장자로 판단하기 때문이다. 저장 파일명과 SDK용 임시 파일명을 분리하면 저장 정책과 외부 라이브러리의 제약을 동시에 만족할 수 있다.

업로드 요청
  └─ 기본 검증
      └─ 암호화 상태 확인
          ├─ 암호화됨 → 원본 저장
          └─ 평문 PDF → 임시 파일 암호화 → 암호화 결과 저장

암호화에 실패하면 업로드 파일을 남겨 두지 않는다. 임시 파일은 finally에서 삭제하고, 사용자가 다시 시도할 수 있는 수준의 메시지만 반환한다. SDK 내부 경로나 키 파일 이름을 그대로 응답에 포함하지 않는 것도 중요하다.

미리보기는 임시 복호화로 제한한다

미리보기 API는 저장된 파일을 바로 복호화해 응답하지 않고 다음 순서를 따른다.

  1. 인증과 파일 접근 권한을 확인한다.
  2. 암호화 원본을 확장자가 포함된 임시 파일로 복사한다.
  3. 임시 파일을 복호화한다.
  4. 복호화된 내용을 스트리밍한다.
  5. 응답 처리와 관계없이 임시 파일을 삭제한다.

레거시 평문 파일은 암호화 여부를 확인한 뒤 별도 복호화 없이 기존 방식으로 읽는다. 이렇게 하면 저장 정책을 강화하면서도 이전에 등록된 파일을 한 번에 마이그레이션하지 않아도 된다.

미리보기 응답에는 캐시 정책도 함께 적용해야 한다. 브라우저나 프록시가 평문 결과를 오래 보관하지 않도록 캐시를 제한하고, 다운로드 응답과 미리보기 응답의 Content-Disposition을 구분한다. DRM은 저장 파일의 보호 수단이지 인증과 인가를 대신하는 기능이 아니므로, 권한 확인을 건너뛰면 안 된다.

다운로드는 암호화 원본을 그대로 보낸다

다운로드에서는 복호화 단계를 넣지 않았다. 서버가 암호화 원본을 스트리밍하고, 사용자가 허가된 환경에서 열도록 책임을 분리했다.

이 구조에는 두 가지 이점이 있다.

  • 다운로드 요청마다 평문 임시 파일을 만들지 않아 디스크 사용량이 늘지 않는다.
  • 미리보기와 다운로드의 보안 정책이 섞이지 않아 코드와 감사 로그를 나누기 쉽다.

다운로드 URL 자체가 보호되는 것은 아니므로, 파일 ID를 안다고 해서 다른 사용자의 파일을 읽을 수 없도록 리소스 소유권 검사를 함께 수행해야 한다.

엑셀 내보내기와 업로드의 왕복 처리

그리드 데이터를 엑셀로 내보내는 기능은 일반 첨부 파일과 조금 달랐다. 서버가 생성한 엑셀 파일을 임시 .xlsx 파일로 만든 다음 암호화하고, 암호화 결과를 응답으로 반환했다. 임시 파일은 응답이 끝난 뒤 정리했다.

반대로 엑셀 업로드는 다음 순서로 처리했다.

  1. 업로드 파일을 임시 .xlsx로 저장한다.
  2. 암호화 상태를 확인한다.
  3. 암호화된 파일만 임시 복호화한다.
  4. 복호화된 파일을 파서로 읽는다.
  5. 파싱이 끝나면 두 임시 파일을 모두 삭제한다.

키가 다른 환경에서 생성된 파일은 복호화 단계에서 실패할 수 있다. 이 경우 서버 오류로 포장해 500을 반환하면 사용자는 원인을 알기 어렵고 운영자는 장애로 오인하기 쉽다. 입력 파일이나 키 불일치처럼 사용자가 다시 시도할 수 있는 문제는 400 계열 응답과 안전한 안내 메시지로 분리했다.

실패 처리는 반환 코드보다 넓게 본다

초기 구현에서 가장 위험했던 부분은 SDK의 encrypt()decrypt() 호출이 실패해도 반환값을 확인하지 않은 채 다음 단계로 진행할 수 있다는 점이었다. 반환 코드와 예외를 모두 검사하고, 실패하면 즉시 중단하도록 감쌌다.

또한 실패 상황을 다음처럼 구분했다.

상황 응답과 로그 원칙
확장자·크기 등 입력 오류 400, 사용자가 수정할 수 있는 메시지
암호화 키 불일치 400, 내부 경로·키 정보는 노출하지 않음
SDK 또는 저장소 장애 500, 상세 원인은 서버 로그에서만 확인
예상하지 못한 DRM 상태 WARN 로그로 기록하고 운영 점검 대상으로 분류

사용자 응답에는 “복호화 실패”처럼 지나치게 내부적인 표현보다, 파일을 다시 내보내거나 재업로드하라는 행동 안내를 담았다. 반면 서버 로그에는 파일 ID, 처리 단계, 실패 유형을 남겨 추적할 수 있게 했다. 키 값이나 원본 경로는 로그에도 기록하지 않는 편이 안전하다.

환경별 활성화 플래그와 키 분리

개발 환경에서 DRM 서버나 키 관리 모듈을 항상 사용할 수 있는 것은 아니다. 그래서 환경별 활성화 플래그를 두고, 로컬에서는 원본 흐름으로 테스트할 수 있도록 했다. 운영 환경에서는 플래그를 강제로 활성화하고 운영 키를 사용한다.

여기서 중요한 것은 “로컬 우회가 운영 설정으로 번지지 않게 하는 것”이다.

  • 기본값은 보수적으로 설정한다.
  • 운영 프로파일에서 활성화 값을 명시한다.
  • 개발 키와 운영 키를 섞지 않는다.
  • 설정값과 키 파일의 위치는 소스와 문서에 하드코딩하지 않는다.

환경 플래그는 편의를 위한 스위치일 뿐, 인증이나 권한 검사를 끄는 스위치가 되어서는 안 된다.

테스트는 정상 흐름보다 경계 조건을 먼저 봤다

연동을 마무리하면서 다음 시나리오를 반복 확인했다.

  • 암호화된 파일 업로드, 미리보기, 다운로드
  • 평문 PDF 업로드 시 서버 암호화
  • 기존 평문 파일의 미리보기
  • 키 파일이 없거나 잘못된 경우의 실패 응답
  • DRM 클라이언트가 없는 환경에서의 동작
  • 서버가 생성한 엑셀 파일의 업로드와 재처리
  • 미리보기와 암호화 실패 뒤 임시 파일 정리 여부
  • 키 관리 모듈 재시작 뒤 신규 파일 처리

특히 테스트가 끝난 뒤 임시 디렉터리를 확인하는 습관이 도움이 됐다. 암호화 기능은 성공 화면만 보면 정상처럼 보이지만, 실패 시 평문 파일이 남거나 임시 디렉터리가 계속 커지는 문제가 뒤늦게 드러날 수 있다.

구현하면서 배운 점

DRM 연동의 핵심은 SDK 메서드를 호출하는 데 있지 않았다. 파일의 상태가 바뀌는 지점을 명확히 나누고, 평문이 존재할 수 있는 시간을 최소화하고, 실패했을 때도 저장소가 일관된 상태를 유지하는 데 있었다.

정리하면 다음 네 가지가 설계의 기준이 됐다.

  1. 저장소에는 가능한 한 암호화 원본만 둔다.
  2. 미리보기처럼 꼭 필요한 순간에만 임시 복호화한다.
  3. 레거시 파일을 읽을 수 있게 하되 신규 파일 정책은 강화한다.
  4. 사용자 오류와 서버 장애를 다른 응답으로 분리한다.

이 원칙을 지키면 특정 DRM 제품이나 저장소가 바뀌어도 파일 처리 흐름을 다시 설계하지 않고 어댑터만 교체할 수 있다. DRM은 보안 기능이면서 동시에 파일 생명주기 기능이므로, 업로드·미리보기·다운로드를 하나의 흐름으로 보고 설계하는 편이 오래 유지하기 좋다.

관련 글: Spring Security 로그인 보안 강화, 로컬 배포와 운영 점검 기록