개요
코드를 작성하다 보면 기본적으로 가정하고 있는 조건들이 있다.
그런 조건들은 알고리즘을 파악하거나 주석을 읽으면서 확인할 수 있지만,
Assertion을 사용하여 보다 명시적으로 나타낼 수 있다.
Java에서 기본적으로 제공하는 assert 문법을 알아보고,
이를 Spring에서는 어떻게 사용해야 하는지,
또한 Spring의 Assert 클래스와는 어떻게 다른지 알아보자.
Java의 assert
java의 assert 문법은 기본적으로 아래와 같이 사용한다.
// assert [조건식];
assert 1 == 2;
// assert [조건식] : [실패 메시지];
assert 1 == 2 : "유효성 검사에 실패했습니다.";
조건식 결과가 true이면 정상적인 흐름, false이면 잘못된 경우임을 나타낸다.
하지만, 조건식의 결과가 false라고 해서 예외가 발생하지는 않는다.
이를 확인하기 위해 다음 코드를 실행해 보자.
assert 1 == 2 : "유효성 검사에 실패했습니다.";
System.out.println("Success!");
아마도 우리가 예상하는 건, 1은 2와 같지 않기 때문에 실패 메시지가 나타는 것이다.
하지만 실행 결과는 아래와 같다.
실행 결과를 확인해 보면 assert 구문이 무시된 채로 "Success!"가 출력되고 있음을 알 수 있다.
기본적으로 assert 구문은 특정 상태를 가정하고 있다는 것을 명시적으로 나타내는 구문이기 때문에,
프로그램 내에서 유효성 검사를 직접 수행하지 않는다.
Enable Assertions
하지만 Java의 VM Option에 -ea를 추가하면 assert 구문으로 유효성 검사를 수행할 수 있다.
(ea : Enable Assertions의 약자)
IntelliJ에서는 아래와 같이 설정할 수 있다.
- Application 탭 누른 후 구성 편집 선택
- 옵션 수정 선택
- VM 옵션 추가 선택
- VM 옵션 입력 창에 -ea 입력
설정 후 다시 실행해 보면 아래와 같은 실행 결과가 나타난다.
Spring 테스트 코드
이제 Spring의 테스트 코드에서는 동일한 assert 구문이 어떻게 동작하는지 확인해 보자.
먼저 아래와 같이 테스트 코드를 작성하자.
class AssertTest {
@Test
void javaAssert() {
assert 1 == 2 : "유효성 검사에 실패했습니다.";
}
}
그러고 나서 테스트 코드를 실행해 보자.
테스트 코드에서는 -ea 설정을 별도로 추가해주지 않았음에도 assert 유효성 검사가 동작하는 것을 확인할 수 있다.
왜 테스트 코드에서만 유효성 검사가 동작하는지 각 테스트 환경을 기준으로 알아보자. (Gradle, IntelliJ IDEA)
(IntelliJ 설정 > 빌드, 실행, 배포 > 빌드 도구 > Gradle에서 "다음을 사용하여 테스트 실행" 설정값 확인)
Gradle 테스트
IntelliJ IDEA의 기본 테스트 환경은 Gradle이다.
이는 IDE가 없는 환경에서 빌드 혹은 테스트를 돌리는 환경과 동일하게 구성하기 위함일 것이다.
테스트 환경을 확인하기 위해 IntelliJ에서 테스트 실행 구성을 아래와 같이 수정해 보자.
- 테스트 실행 버튼 우클릭 후 실행 구성 수정 선택
- 실행 명령어 가장 뒤에 --info 추가
이후 다시 실행하면 아래와 같은 로그를 확인할 수 있다.
테스트 실행 시 자동으로 -ea 옵션을 추가하는 것을 확인할 수 있다.
테스트도 프로그램 실행 시처럼 -ea 없이 실행하고 싶으면 어떻게 해야 할 까?
아래 Gradle Test의 공식 문서를 참고해 보자.
https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:enableAssertions
enableAssertions 속성이 기본적으로 true로 설정되어 있음을 알 수 있다.
이를 비활성화하기 위해 build.gradle에서 해당 속성을 false로 추가해 주자.
tasks.named('test') {
useJUnitPlatform()
enableAssertions = false // 기본값 : true
}
Gradle 동기화 후 다시 테스트를 돌려 보면 아래와 같이 성공함을 알 수 있다.
IntelliJ 테스트
IntelliJ 테스트는 실행 구성을 확인해 보면 자동으로 -ea가 삽입되어 있는 것을 확인할 수 있다.
따라서 위 속성을 제거하고 실행해 보면 테스트가 성공함을 확인할 수 있다.
Spring의 Assert
Spring에서는 Assert 클래스 및 수많은 메서드를 제공한다.
위 assert 예제와 동일한 형태로 예제 코드를 작성해 보자.
Assert.isTrue(1 == 2, "유효성 검사에 실패했습니다.");
System.out.println("Success!");
실행 결과는 아래와 같다.
-ea 등의 설정 없이도 바로 유효성 검사가 실패하는 모습을 확인할 수 있다.
내부 동작을 확인하기 위해 Assert 클래스의 해당 메서드를 찾아가 보자.
내부 구현을 보면 단순히 표현식이 false이면 IllegalArgumentException을 throw하는 것을 확인할 수 있다.
따라서 설정에 관계 없이 항상 유효성 검사가 동작할 것임을 알 수 있다.
Java의 assert vs Spring의 Assert
위의 결과를 요약하면 아래와 같다.
- Java의 assert
- 기본적으로 프로그램 실행 시 로직 수행하지 않음, 테스트 코드 실행 시 기본적으로 로직 수행
- 설정을 통해 실행 여부 변경 가능
- 참 / 거짓에 대한 유효성 검사만 가능
- 유효성 검사 실패 시 AssertionError throw
- Spring의 Assert
- 항상 로직 수행
- 참 / 거짓 뿐만 아니라 다양한 경우의 유효성 검사 지원
- 유효성 검사 실패 시 IllegalArgumentException throw
정리
따라서 이를 토대로 정리하면 아래와 같다.
Java의 assert는 기본적으로 프로그램 실행 시 무시되며,
유효성 검사 로직이 실행되었을 때 RuntimeException이 아닌 Error를 throw한다.
따라서 assert는 정확한 동작 검증 보다는 유효성에 대한 명세를 작성하기 위한 목적임을 알 수 있다.
(유효성 검사에 실패하더라도 다음 로직은 정상 수행되도록 구성해야 한다.)
반대로 Spring의 Assert는 항상 유효성 검사를 실행하며,
유효성 검사 로직이 실행되었을 때 RuntimeException을 호출한다.
따라서 실제로 운영 환경 내에서 유효성 검사를 실행하기 위한 목적임을 알 수 있다.
마무리
Assertion 관련하여 백기선님이 하셨던 말씀을 마지막으로 목적에 맞게 assert를 활용하기 위해 노력하자.
"어딘가에서 이미 검증을 했기 때문에 이 부분에서 이런 상황인 것을 가정하고 있다."
이런 것들을 표현하는 용도로는 주석보다는 assert 문을 활용하면 좋을 것 같다.
참고 자료
- 인프런 코딩으로 학습하는 리팩토링 (백기선) > 리팩토링 43. 어서션 추가하기
모든 코드는 GitHub에서 확인하실 수 있습니다. :)
'Java & Spring' 카테고리의 다른 글
[Spring] Spring Boot 1.x에서 JUnit5 사용하기 (1) | 2024.10.11 |
---|---|
[사내 세미나] Spring Batch 도입하기 (3) | 2024.09.04 |
[Spring] 레거시 프로젝트에 Testcontainers 도입하기 (2) | 2024.07.07 |
Spring Boot 무료로 배포하기 (Koyeb, GitHub) (0) | 2024.03.21 |
[Spring] eventListener, transactionalEventListener 예외 및 트랜잭션 전파 총정리 (0) | 2023.12.29 |