Unit Test

자바에서 클래스를 활용한 사용자 정의 타입으로 코드 가독성과 신뢰성 높이기

anyjava 2024. 2. 20. 20:51

자바나 다른 새로운 프로그래밍 언어를 배우면서 사용자 정의 타입을 만들고 사용하는 기법을 배우게 됩니다.

자바에서 가장 쉽게 사용할 수 있는 것은 클래스(class)입니다. 자바는 모든 것이 클래스로 구성되어 있습니다.

 

그렇다면, 사용자 정의 타입(이하 클래스)을 어떻게 활용할 수 있을까요?

자바의 클래스를 사용하여 어떻게 신뢰성 있는 프로그램 코드를 작성할 수 있을까요?

 

이제 이러한 개념을 실제 코드 예제를 통해 더 자세히 살펴보겠습니다.

 

계좌번호, 전화번호, 카드번호 등은 실무에서 반드시 암호화하여 데이터베이스에 저장해야 하는 정보입니다. 따라서 개발 시 많은 개발자들이 평문으로 된 계좌번호 등을 암호화하여 사용하게 됩니다.

 

아래 코드에서는 cardNumber가 암호화된 상태인지, 복호화된 상태인지 알기 어렵습니다. API로부터 정보를 받아온 경우 이 구분이 더욱 어려워질 수 있습니다.

class PaymentService {
  public void pay(String cardNumber, int payAmount) {
    // ...
  }
}

저 역시 실무에서 비슷한 경험을 했습니다. 암호화된 값을 기대하며 사용했으나 실제로는 평문이었기 때문에 런타임 오류를 겪었습니다.

 

 

 

『클린 코드(Clean Code)』라는 책에서는 코드를 읽는 시간 대비 작성하는 시간의 비율이 대략 10:1을 넘는다고 언급합니다.

그래서 명확한 타입을 이용해서 코드를 읽는 사람이 빠르게 인지할 수 있게 하자.

 

예를 들어, String value = "100"보다는 int value = 100과 같이 정확한 타입을 사용하는 것이 코드를 읽기에 더 편리하며, 이로 인해 사칙연산을 적용할 수 있다는 기대감이 자연스럽게 생깁니다.

 

자 그럼 다음 예제 코드를 보시죠. (전체코드) (테스트코드)

package net.anyjava.blogcode.ecrypt;

import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;

@Component
public class CardNumberEncryptor {
    private final String key = "1234567890123456";
    private final String iv = key.substring(0, 16); // 16byte

    // AES 암호화
    EncryptCardNumber encrypt(PlainCardNumber plainCardNumber) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivParamSpec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParamSpec);

            byte[] encrypted = cipher.doFinal(plainCardNumber.getPassword().getBytes("UTF-8"));
            return EncryptCardNumber.of(Base64.getEncoder().encodeToString(encrypted));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // AES 복호화
    PlainCardNumber decrypt(EncryptCardNumber encryptedData) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivParamSpec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParamSpec);

            byte[] decodedBytes = Base64.getDecoder().decode(encryptedData.getPassword());
            byte[] decrypted = cipher.doFinal(decodedBytes);
            return PlainCardNumber.of(new String(decrypted));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

 

위 코드에서 주목해야할 부분은 파라미터와 반환 타입으로 String 대신 사용자 정의 클래스인 PlainCardNumberEncryptCardNumber를 사용합니다. 이 컴포넌트를 통해 암호화와 복호화를 진행함으로써, 사용자는 타입을 통해 카드 번호가 암호화되었는지 여부를 쉽게 구분할 수 있습니다.

 

 

사용자 정의 타입을 정의해 사용하면, 테스트의 용이성이 향상되고 코드의 가독성도 더욱 높아집니다.

또한, 계좌번호나 회계시스템을 다룰 때, 공급가액과 부가세는 항상 함께 다뤄지므로, long saleAmount, long vatAmount로 파라미터를 전달하기보다는, saleAmount와 vatAmount를 속성으로 갖는 Revenue라는 클래스를 정의해 사용하는 것이 좋습니다.

 

결론적으로, 사용자 정의 타입을 활용함으로써 우리는 코드의 가독성과 신뢰성을 높이고, 보다 효과적으로 데이터를 관리할 수 있습니다. 작은 단계부터 시작하여 이러한 접근 방식을 점진적으로 적용해보세요.

 

 

반응형