데이터베이스에서 AUTO_INCREMENT 기반의 식별자를 사용하는 경우, API 설계 단계에서 한 번쯤 고민하게 되는 문제가 있다. BIGINT UNSIGNED로 정의된 id 값은 항상 양수이며 1씩 증가한다. 이러한 특성은 내부적으로는 매우 편리하지만, 외부로 노출되는 순간 예측 가능한 값이 된다.
클라이언트가 PathVariable에 숫자를 직접 넣어 요청하는 구조라면 의도하지 않은 무차별 API 호출이 가능해진다. 이를 방지하기 위해 식별자를 암호화하여 전달하고, 서버에서는 이를 자동으로 복호화하는 방식이 실무에서 자주 사용된다. 이번 글에서는 Jasypt와 GenericConverter를 이용해 PathVariable 암호화를 구현하는 방법을 정리한다.
PathVariable 암호화 Jasypt
증분 식별자를 암호화하기 위해 Jasypt 라이브러리를 사용한다. Jasypt는 Spring Boot 환경에서 비교적 간단하게 설정할 수 있으며, 문자열 암호화에 특화된 기능을 제공한다. 암호화 키는 반드시 코드에 직접 작성하지 않고 환경 변수나 외부 설정으로 관리하는 것이 바람직하다.
기본적으로 StandardPBEStringEncryptor는 PBEWithMD5AndDES 알고리즘을 사용하며, 내부적으로 반복 횟수, 솔트, IV 값 등을 설정한다. 보안 수준에 따라 알고리즘이나 반복 횟수는 조정할 수 있지만, PathVariable 암호화 목적이라면 과도한 설정은 오히려 불필요할 수 있다.
Encryptor 빈을 등록하면 애플리케이션 전역에서 재사용할 수 있다. 이 방식의 장점은 암호화 로직이 컨트롤러나 서비스에 노출되지 않는다는 점이다. 암호화와 복호화 책임은 인프라 레벨로 내려가고, 비즈니스 로직은 식별자를 Long 타입으로만 다루면 된다.
이처럼 Jasypt는 “식별자 노출 최소화”라는 목적에 적합하며, UUID를 사용하는 것보다 URL 길이를 짧게 유지할 수 있다는 장점도 있다.
GenericConverter 자동 복호화
컨트롤러에서 매번 암호화된 문자열을 직접 복호화하는 방식은 코드를 지저분하게 만들고 실수를 유발한다. 이를 해결하기 위해 Spring의 ConversionService를 활용할 수 있다. GenericConverter는 컨트롤러 파라미터 바인딩 과정에서 소스 타입을 대상 타입으로 변환하는 범용 컨버터다.
String → Long 변환 과정에 복호화 로직을 끼워 넣으면, @PathVariable을 받을 때 자동으로 암호화된 값을 복호화할 수 있다. 이를 위해 커스텀 어노테이션을 정의하고, matches() 메서드에서 해당 어노테이션이 붙은 파라미터만 처리하도록 한다.
getConvertibleTypes()는 변환 가능한 타입 쌍을 명시하고, convert() 메서드에서는 실제 복호화를 수행한다. 이 구조의 핵심은 “조건부 변환”이다. 모든 String → Long 변환이 아닌, 특정 어노테이션이 붙은 경우에만 동작하도록 제한할 수 있다.
GenericConverter는 타입 변환기이기 때문에 HandlerMethodArgumentResolver보다 가볍고, 컨트롤러 파라미터 바인딩 단계에 자연스럽게 녹아든다. 특히 REST API에서 PathVariable을 자주 사용하는 구조라면 이 방식은 코드 중복을 크게 줄여준다.
PathVariable 암호화 실전 적용
최종적으로 컨트롤러에서는 숫자 id를 그대로 받는 것처럼 보이지만, 실제로는 암호화된 문자열을 주고받게 된다. 클라이언트는 암호화된 식별자만 알 수 있고, 내부 증분 값은 외부에 노출되지 않는다.
이 방식의 가장 큰 장점은 가독성과 안전성이다. 컨트롤러 메서드 시그니처는 단순한 Long 타입을 유지하면서, 보안 처리는 프레임워크 레벨에서 자동으로 처리된다. 서비스 계층이나 리포지토리 계층은 암호화 여부를 전혀 신경 쓸 필요가 없다.
또한 기존 API 구조를 거의 변경하지 않고 적용할 수 있다. PathVariable만 암호화하면 되기 때문에 RequestBody나 QueryString까지 확장하는 것도 어렵지 않다.
증분 식별자를 그대로 노출하는 것이 항상 잘못된 것은 아니지만, 외부 요청이 많은 API라면 한 번쯤 고려해볼 만한 구조다. Jasypt와 GenericConverter를 조합하면 복잡하지 않으면서도 실무에서 충분히 의미 있는 방어선을 만들 수 있다.