포스트

2023-05-15-TIL

2023-05-15-TIL

Today I Learned

ArchUnit으로 아키텍처 테스트하기

계층형 구조나 헥사고날 구조를 정해 두어도, 시간이 지나면 컨트롤러가 리포지토리를 직접 부르거나 도메인 패키지가 인프라 패키지를 import하는 식으로 규칙이 조금씩 깨진다. 사용자 가이드의 설명대로 ArchUnit은 Java 바이트코드를 분석해 패키지와 클래스 사이의 의존성, 계층과 슬라이스, 순환 의존성 같은 규칙을 일반 단위 테스트 프레임워크로 검사하는 라이브러리다. 아키텍처 규칙을 문서가 아니라 깨지면 빌드가 실패하는 테스트로 만든다는 점이 핵심이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
@AnalyzeClasses(packages = "com.example.app")
class ArchitectureTest {

    @ArchTest
    static final ArchRule layers = layeredArchitecture()
            .consideringAllDependencies()
            .layer("Controller").definedBy("..controller..")
            .layer("Service").definedBy("..service..")
            .layer("Repository").definedBy("..repository..")
            .whereLayer("Controller").mayNotBeAccessedByAnyLayer()
            .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
            .whereLayer("Repository").mayOnlyBeAccessedByLayers("Service");
}

계층 규칙 외에도 이름 규칙(..service.. 패키지의 클래스는 Service로 끝난다), 특정 애너테이션 사용 위치, System.out 대신 로거 사용 같은 코딩 규칙도 검사할 수 있다. 링크한 글 중에는 ArchUnit으로 로깅 규칙을 강제하는 사례도 있다. 이미 위반이 많은 프로젝트라면 FreezingArchRule로 현재 위반은 기록해 두고 새 위반만 막는 방법도 있다.

  • https://www.archunit.org/userguide/html/000_Index.html
  • https://d2.naver.com/helloworld/9222129
  • https://www.baeldung.com/java-archunit-intro
  • https://effortguy.tistory.com/166
  • https://betterprogramming.pub/archunit-test-software-architecture-easily-a874c54bda1e
  • https://github.com/TNG/ArchUnit-Examples
  • https://medium.com/free-code-camp/java-archunit-testing-the-architecture-a09f089585be
  • https://engineering.cloudflight.io/archunit-linting-your-achitecture-with-unit-tests
  • https://shekhargulati.com/2022/10/08/enforcing-logging-best-practices-by-writing-archunit-tests/
  • https://medium.com/ing-blog/architecture-and-design-unit-testing-using-archunit-f3ba6b5ae03b
  • https://blogs.oracle.com/javamagazine/post/unit-test-your-architecture-with-archunit
  • https://blog.jdriven.com/2018/10/testing-the-architecture-archunit-in-practice/
  • https://dev.to/andressacco/archunit-validate-the-architecture-of-our-projects-3hc9

AOP로 로깅하기

메서드 진입과 종료, 인자, 실행 시간을 남기는 로그를 모든 메서드에 직접 쓰면 비즈니스 코드가 로그 코드에 묻힌다. Spring AOP의 @Around 어드바이스로 대상 메서드를 감싸면 이 횡단 관심사를 한곳에 모을 수 있다. 포인트컷을 패키지나 커스텀 애너테이션(@Loggable)으로 정하면 적용 범위를 조절할 수 있다. Spring AOP는 프록시 기반이라 같은 클래스 안에서 호출하는 메서드(self-invocation)에는 어드바이스가 적용되지 않는다는 점, 인자를 그대로 찍으면 비밀번호 같은 민감 정보가 로그에 남을 수 있다는 점을 주의해야 한다.

  • https://dev.to/codenameone/spring-boot-debugging-with-aspect-oriented-programming-aop-1lmo

Spring Boot에서 Enum 값 검증과 오류 메시지

요청 JSON의 enum 필드에 정의되지 않은 값이 들어오면 Bean Validation까지 가지 못하고 Jackson 역직렬화 단계에서 실패한다. 그래서 @NotNull이나 커스텀 제약의 메시지가 아니라 HttpMessageNotReadableException이 발생하고, 기본 응답에는 어떤 필드가 왜 틀렸는지가 잘 드러나지 않는다. 링크한 글들이 제시하는 방법은 두 가지다.

  • @ControllerAdvice에서 HttpMessageNotReadableException을 잡고, 원인이 Jackson의 InvalidFormatException이면 필드 경로와 허용 값 목록을 꺼내 오류 메시지를 만든다.
  • 필드를 String으로 받고 커스텀 제약 애너테이션(Baeldung 예제의 @ValueOfEnum)으로 허용 값을 검증한 뒤, 서비스 계층에서 enum으로 바꾼다. 다른 검증 오류와 같은 형식으로 응답할 수 있다.

  • https://cchoimin.tistory.com/entry/Enum-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%82%AC%ED%95%98%EA%B8%B0
  • https://stackoverflow.com/questions/64575842/how-to-return-a-proper-validation-error-for-enum-types-in-spring-boot
  • https://dev-monkey-dugi.tistory.com/142
  • https://www.baeldung.com/javax-validations-enums

Spring Boot 단위 테스트 모범 사례

링크한 글들이 공통으로 권하는 방향은 테스트 범위에 맞는 도구를 고르라는 것이다. 도메인 로직은 스프링 없이 순수 단위 테스트로 검증하고, 웹 계층은 @WebMvcTest, JPA 계층은 @DataJpaTest 같은 슬라이스 테스트로 필요한 빈만 띄운다. @SpringBootTest는 전체 컨텍스트를 띄우므로 통합 테스트에 쓴다. Spring Boot 문서에 따르면 Spring 테스트 프레임워크는 테스트 사이에 애플리케이션 컨텍스트를 캐시하므로, 테스트마다 @MockBean 조합이나 설정을 다르게 하면 캐시가 깨져 컨텍스트를 반복해서 새로 띄우게 된다. 테스트 이름에 기대 동작을 드러내고, given/when/then 구조를 지키라는 조언도 반복된다.

  • https://tanzu.vmware.com/developer/guides/spring-boot-testing/
  • https://climbtheladder.com/10-spring-boot-testing-best-practices/
  • https://www.baeldung.com/java-unit-testing-best-practices
  • https://www.baeldung.com/spring-boot-testing
  • https://rieckpil.de/spring-boot-testing-best-practices/
  • https://medium.com/personal-capital-tech-blog/testing-best-practices-for-java-spring-apps-762e9fde39ec
  • https://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/html/boot-features-testing.html
  • https://www.springboottutorial.com/unit-testing-best-practices-with-java-and-junit-examples

Spring Security 모범 사례

Snyk의 Spring Boot 보안 글은 운영 환경 HTTPS, 의존성 취약점 검사, CSRF 보호, XSS 대응을 위한 Content Security Policy, OpenID Connect 기반 인증, 비밀번호 해싱, 최신 버전 유지, 비밀 값의 안전한 보관, 침투 테스트와 보안 코드 리뷰를 꼽는다. 비밀번호는 PasswordEncoder(BCrypt 등)로 해싱하고, 토큰 기반 API가 아니라 세션 쿠키를 쓰는 애플리케이션에서 CSRF 보호를 끄지 않는 것이 실무에서 자주 놓치는 항목이다. Spring Security 아키텍처 가이드는 필터 체인과 AuthenticationManager, AccessDecisionManager 같은 구성 요소가 어떻게 맞물리는지 설명하므로, 설정을 복사해 붙이기 전에 읽어 둘 만하다.

  • https://www.synopsys.com/blogs/software-security/spring-security-best-practices/
  • https://spring.io/guides/topicals/spring-security-architecture/
  • https://snyk.io/blog/spring-boot-security-best-practices/
  • https://freecontent.manning.com/the-essential-guide-to-spring-security/
  • https://piotrminkowski.com/2021/05/26/spring-microservices-security-best-practices/
  • https://developer.okta.com/blog/2018/07/30/10-ways-to-secure-spring-boot
  • https://www.marcobehler.com/guides/spring-security
  • https://www.baeldung.com/security-spring
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다