반응형

개인정보 암복호화 시점과 주체에 대해 어떤 고민을 하였는 지 공유하겠다. 다른 방법들도 많겠으나 나는 세 가지 후보에 대해서 고려하였고, JPA AttributeConverter를 사용하기로 결정했다. 이를 어떻게 적용하였는 지 간단히 코드를 공개하겠다.

  1. JPA AttributeConverter
  2. filter를 이용해 view <-> controller 시점에 변경
  3. 서비스 단에서 암복호화 함수 사용

암복호화 시점/주체 별 고려사항

1. JPA AttributeConverter

JPA에서 제공해주는 속성 변환기로, 아래 그림과 같이 Java Entity와 DB 사이에서 동작한다. 암복호화 기능 외에도 대소문자 처리나 포맷 변경을 위해서 사용하기도 한다.

출처 : https://javabydeveloper.com/jpa-attributeconverter-examples/

장점

  • 암복호화 대상이 Entity 클래스에 명확하게 드러나기 때문에 유지보수가 쉬울 것 같다.
  • JPA에서 지원하기 때문에 코드가 간결하다.
  • service나 view 단에서는 암복호화에 대해 신경을 쓰지 않아도 된다.

단점

  • 암복호화를 도메인 단에서 책임지는 게 맞는 지는 잘 모르겠다.
  • 이번 프로젝트에서는 외부 API 연동이 주된 서비스이기 때문에, API 송신 시 데이터를 다시 복호화를 해야한다.

 

2. filter

filter를 사용하면 view <-> controller 시점에 데이터가 암복호화 되니 아래와 같은 장단점이 있을 것 같다.

장점

  • JPA 처럼 한 곳에서 암복호화를 관리하니 유지보수가 쉬울 것 같다.

단점

  • 요구사항 변화에 유연하지 못하다. 현재는 별다른 처리 없이 암호화된 데이터를 DB에 넣으면 되지만, 요구사항이 바뀌어 서비스에서 데이터를 처리해야하는 경우 코드가 복잡해진다. 컬럼별로 무언가 처리가 필요할 때 마다 service단에 암복호화 로직이 들어가게 된다. controller에서 암호화된 값을 받아서 service에 넘기기 때문에, 데이터의 추가적인 처리가 필요하다면 service에서 다시 복호화 -> 처리 -> DB에 넣기전에 암호화를 해야한다.
  • 요구사항 변화 시, 객체 단위로 암복호화할 수 있는 util성 함수를 만들어야 한다.
  • 요구사항 변화 시, 누군가 해당 시스템을 인수인계 받아 수정할 때 암복호화 로직을 빼먹을 수 있다.

 

3. 서비스 단에서 암복호화 함수 사용

장점

  • 암복호화는 도메인 로직이 아니기 때문에 service 단에서 책임을 지는 게 맞는 것 같다.
  • 암복호화를 함수 단위로 컨트롤이 가능하다.

단점

  • DB 데이터를 이용하는 서비스 함수마다 암복호화 로직이 들어가야한다. (신규 기능 추가 시에도 암복호화 로직을 꼭 넣어야함)
  • 객체 단위로 암복호화할 수 있는 util성 함수를 만들어야 한다.
  • 누군가 해당 시스템을 인수인계 받아 수정할 때 암복호화 로직을 빼먹을 수 있다.

 

나의 선택

JPA AttributeConverter를 사용하기로 했다.

1. 도메인 로직 변화에 비교적 자유롭다. 현재 개발 중인 시스템은 외부 API에 굉장히 의존적인 시스템이다. 외부에 의해 시스템이 변하고, 내부 시스템보다 변화도를 예측하기 어렵다. 현재는 단순히 고객이 입력한 검진 기록을 저장하고 외부에 송신하지만, 외부 API가 변경되면 송신하는 데이터를 변환해야할 수도 있다.

2. 한 곳에서 관리하는 용이성. 불필요한 암복호화 로직을 여러 군데 추가하고 관리하는 것 보다는 entity 단에서 관리하는 것이 용이할 것이라 판단하였다.

3. DB 조회 시점에서 암복호화를 해주므로, 추가 기능 개발 시 암복호화에 대해 신경쓰지 않고 도메인에 집중 가능


JPA AttributeConverter로 암복호화 적용하기

JPA AttributeConverter 인터페이스를 구현하는 클래스를 작성해보자. 기본적인 형태는 아래와 같다.

@Converter
public class CryptoConverter implements AttributeConverter<String, String> {

    @Override
    public String convertToDatabaseColumn(String attribute) {
        return null;
    }

    @Override
    public String convertToEntityAttribute(String dbData) {
        return null;
    }
}

 

여기에 암복호화 로직을 넣어보겠다.

@Converter
public class CryptoStringConverter implements AttributeConverter<String, String> {
    @Override
    public String convertToDatabaseColumn(String attribute) {
        if (attribute == null) return null;
        return Seed.encrypt(attribute);
    }

    @Override
    public String convertToEntityAttribute(String dbData) {
        if (dbData == null) return null;
        return Seed.decrypt(dbData);
    }
}

 

다른 타입으로 암복호화를 하고싶은 경우 아래와 같이 타입별로 Converter를 만들면 된다.

@Converter
public class CryptoDoubleConverter implements AttributeConverter<Double, String> {

    @Override
    public String convertToDatabaseColumn(Double attribute) {
        if (attribute == null) return null;
        return Seed.encrypt(attribute.toString());
    }

    @Override
    public Double convertToEntityAttribute(String dbData) {
        if (dbData == null) return null;
        return Double.valueOf(Seed.decrypt(dbData));
    }
}

 

타입별로 Converter를 만들다보니 공통적은 코드가 많다는 것이 보인다. 이러면 제네릭을 사용할 수 있지 않을까?하는 생각이 들었다. 시도해보았는데 런타임 오류가 발생하였다. JPA AttributeConverter가 어느 시점에 어떻게 동작하는 지를 잘 모르고, 막연히 변환할 때에 어노테이션 된 필드의 타입으로 매핑해주지 않을까 상상을 했다. 복호화할 시점에 명시적으로 타입이 지정되지 않기 때문에 오류가 발생하는 것으로 추측된다. 왜 오류가 나는 지는 JPA가 어떤 식으로 동작하는 지 차근차근 공부해봐야 알 수 있을 것 같다.

 

이제 @Entity 클래스의 필드에 AttributeConverter를 적용해보자. 아래와 같이 필드에 @Convert 어노테이션을 달면 JPA가 해당 컬럼에 데이터를 넣고 뺄 때, 내가 작성한 AttributeConverter가 적용되게 된다.

@Entity
public class HealthCheckupRecord extends BaseEntity {

    ...

    @Convert(converter = CryptoDoubleConverter.class)
    @Column(columnDefinition = "varchar(24) comment '신장(키)'")
    private Double height;
    
    ...
}

이제 암복호화를 위한 개발은 어느정도 마무리되었다.

다음 포스팅에서는 그동안 코드에서 계속 노출되고 있었던 비밀키를 어떻게 관리할 지에 대해 알아보겠다.

비밀키 관리는 이후 서비스 운영, 외부와의 통신 등과 직접적인 관련이 있기 때문에 팀 내 논의 혹은 회사 방침에 따라야한다.

아직 서비스를 한창 개발중이기 때문에 비밀키 관리 방안에 대해 어느 정도 이야기가 된 후에 포스팅할 예정이다.

반응형