지난 포스팅에서 애플리케이션 레벨 암호화의 구현 방식에 대해서 설명했습니다. 가장 중요하게 생각했던 점은 암·복호화 로직이 비즈니스 흐름이나 도메인 모델에 섞이지 않도록 명확하게 분리하는 것입니다.

그래서 암호화와 관련된 코드는 별도의 패키지로 모으고, 서비스 계층이나 엔티티 내부에서는 이를 전혀 의식할 필요가 없도록 프로젝트의 구조를 설계했습니다.


1. 프로젝트 구조

├─api
│ ├─ controller
│ └─ service
│
├─domain
│ └─ repository
│
├─crypto
│ ├─ converter
│ ├─ core
│ ├─ key
│ └─ exception
│
└─config

각 패키지의 역할은 다음과 같습니다.

패키지 역할
api 컨트롤러 및 서비스 계층으로 비즈니스 흐름을 처리합니다.
domain 리포지토리 및 엔티티 계층으로 실제 도메인 모델과 DB 테이블을 매핑시키고, CRUD 처리를 담당합니다.
crypto 애플리케이션 레벨에서의 데이터 암·복호화를 전담합니다.
config 애플리케이션 구동에 필요한 설정 정보를 관리합니다.

2. api 패키지

api 패키지는 외부 요청을 받아 비즈니스 흐름을 처리하는 역할을 담당합니다. 이 패키지는 다음과 같은 하위 패키지로 구성되어 있습니다.

하위 패키지 역할
controller HTTP 요청을 받아 서비스 계층으로 전달하는 진입점입니다.
service 비즈니스 로직을 처리하며, 도메인 객체를 생성하거나 조회합니다.

애플리케이션 레벨 암호화가 정상적으로 동작하는지 확인하기 위한 목적으로 다음 두 개의 API 엔드포인트만 구현했습니다.

Method URI 설명
POST /api/v1/account 계정 정보를 저장합니다.
GET /api/v1/account 입력받은 전화번호와 매칭되는 계정 정보를 조회합니다.

API의 요청과 응답에서 사용되는 데이터는 모두 평문 기준으로 처리됩니다.

데이터의 암·복호화는 내부적으로만 수행되기 때문에, API를 사용하는 입장에서는 이를 의식할 필요가 없습니다.


3. domain 패키지

domain 패키지는 애플리케이션의 도메인 모델을 표현하는 영역으로, 엔티티와 리포지토리를 통해 비즈니스 관점의 데이터 구조와 상태를 정의합니다.

각 하위 패키지는 다음과 같은 역할을 담당합니다.

하위 패키지 역할
entity DB 테이블과 매핑될 도메인 모델을 표현하는 엔티티를 정의합니다.
repository 엔티티에 대한 CRUD 처리를 담당합니다.

엔티티와 리포지토리는 모두 평문 형태의 데이터만 다루며, 암·복호화에는 직접 관여하지 않습니다.

대신, 엔티티 정의 시 암호화 대상 필드는 @Convert 어노테이션을 통해 암·복호화 로직을 구현한 컨버터를 등록해야 합니다.

이렇게 등록된 컨버터는 JPA의 데이터 처리 과정에 통합되어, 엔티티가 저장되거나 조회되는 시점에 암·복호화 로직이 자동으로 수행됩니다.

아래 코드는 StringEncryptConverter라는 이름의 컨버터를 등록하는 예제입니다.

@Convert(converter = StringEncryptConverter.class)
private String phoneNumber;

참고) 토이 프로젝트에서 StringEncryptConverter는 JPA의 AttributeConverter 인터페이스를 상속받아 구현했습니다.


4. crypto 패키지

crypto 패키지는 애플리케이션 레벨에서 데이터의 암·복호화를 전담하는 핵심 영역입니다.

서비스 계층이나 도메인 모델이 데이터의 암·복호화 여부를 전혀 의식하지 않도록 만들어보고 싶었습니다. 그래서 실제 암·복호화 처리는 이 패키지 내부에서 일관되게 수행되도록 구조를 분리했습니다.

이 구조의 핵심은 책임 분리입니다. 설계에 대한 고민 끝에 다음과 같이 하위 패키지로 나누어 구성했습니다.

하위 패키지 역할
converter JPA AttributeConverter 구현체를 두는 영역입니다. 엔티티 필드와 DB 컬럼 사이에서 값 변환(암·복호화)이 발생하는 진입점입니다.
core 실제 암·복호화 알고리즘을 수행하는 핵심 로직을 담당합니다.
key 암·복호화에 필요한 키를 관리하고 제공하는 역할을 담당합니다.
exception 암·복호화 과정에서 발생하는 예외를 공통으로 정의합니다.

가장 중요하게 고려한 것은 암호화 로직이 특정 계층에 종속되지 않도록 분리하는 것이었습니다.

converter는 JPA와의 연결을 담당하고, core는 암호화 자체를 담당하며 key는 키 관리 책임을 분리합니다.

converter는 언제 암·복호화의 수행 시점을 요청하고, core는 어떤 방법으로 암·복호화를 수행할지를 정의합니다. 그리고 key는 암호화 키 값을 어디에서 어떻게 가져올 것인지를 결정합니다.

이처럼 책임을 명확히 나눔으로써, 암호화 알고리즘이나 키 관리 방식이 변경되더라도 영향 범위를 최소화하면서 전체 구조를 안정적으로 유지할 수 있도록 설계했습니다.


5. config 패키지

config 패키지는 암·복호화를 포함해서 애플리케이션이 구동하는데 필요한 설정 정보를 일괄적으로 관리하는 영역입니다.

이렇게 설정 관리를 별도의 패키지로 분리함으로써, 구조 확장에도 유연하게 대응할 수 있도록 했습니다.

Spring Data JPA

다음 포스팅에서는 실제로 애플리케이션 레벨 암호화를 구현했는지 코드를 중심으로 살펴보도록 하겠습니다.


6. 포스팅 시리즈


7. 소스 코드


#JPA #DB #암호화 #보안 #토이프로젝트 #AttributeConverter