포스트

2022-06-29-TIL

2022-06-29-TIL

Today I Learned

Filter, Interceptor, AOP

세 가지 모두 요청 처리의 앞뒤에 공통 로직을 끼워 넣는 방법이지만 동작하는 위치가 다르다.

flowchart LR
    C["fa:fa-user 클라이언트"] --> F["Filter (서블릿 컨테이너)"]
    F --> D["DispatcherServlet"]
    D --> I["HandlerInterceptor (Spring MVC)"]
    I --> A["AOP 프록시"]
    A --> S["컨트롤러 / 서비스 메서드"]
  • Filter는 서블릿 표준이다. DispatcherServlet에 도달하기 전, 서블릿 컨테이너 단계에서 요청과 응답을 다룬다. 요청 자체를 감싸거나 바꿀 수 있어서 인코딩, 인증, 요청·응답 로깅에 쓰인다.
  • Interceptor는 Spring MVC의 HandlerInterceptor다. DispatcherServlet이 핸들러를 찾은 뒤 preHandle, postHandle, afterCompletion 시점에 호출되므로 어떤 컨트롤러가 처리하는지 알 수 있다.
  • AOP는 Spring 빈의 메서드 호출을 프록시로 감싼다. HTTP와 무관하게 서비스 계층 메서드의 실행 시간 측정, 트랜잭션, 메서드 단위 로깅에 쓴다.

Spring 문서는 인터셉터를 보안 계층으로 쓰기에 적합하지 않다고 밝힌다. 애너테이션 기반 컨트롤러의 경로 매칭과 어긋날 수 있으므로, 보안은 Spring Security나 서블릿 필터 체인처럼 가능한 한 앞단에서 처리하라고 권한다(Spring Framework: Interceptors).

  • https://goddaehee.tistory.com/154
  • https://mangkyu.tistory.com/173
  • https://baek-kim-dev.site/61

Logback

Logback은 SLF4J의 기본 구현체로, Spring Boot의 spring-boot-starter-logging이 기본으로 포함한다. 구성 요소는 셋이다. Logger는 패키지 계층별로 로그 레벨을 정하고, Appender는 출력 대상(콘솔, 파일, 원격)을 정하며, Encoder/Layout은 출력 형식을 정한다. Spring Boot에서는 logback.xml 대신 logback-spring.xml을 쓰면 <springProfile> 태그로 프로파일별 설정을 나눌 수 있다. 파일 로그는 RollingFileAppender에 시간 기준 또는 시간·크기 기준 롤링 정책을 붙여 일정 기간이 지난 파일을 정리한다(Logback: Appenders).

  • https://tecoble.techcourse.co.kr/post/2021-08-07-logback-tutorial/

배포 전략: 롤링, 카나리, 블루 그린

  • 롤링 배포: 서버를 한 대씩 또는 일부씩 새 버전으로 교체한다. 추가 자원이 거의 필요 없지만, 배포 중에는 구버전과 신버전이 동시에 요청을 받으므로 두 버전이 호환되어야 한다.
  • 블루 그린 배포: 현재 운영 환경(블루)과 같은 규모의 새 환경(그린)을 띄워 두고, 로드밸런서의 트래픽을 한 번에 전환한다. 롤백은 다시 블루로 돌리면 되므로 빠르지만, 전환 시점에 두 배의 자원이 필요하다.
  • 카나리 배포: 새 버전에 트래픽의 일부만 보내서 에러율과 지표를 확인하고, 문제가 없으면 비율을 점점 늘린다. 위험을 작게 나눌 수 있지만 트래픽 분배와 지표 비교를 위한 장치가 필요하다.

어느 전략이든 DB 스키마 변경은 구버전과 신버전이 모두 동작하도록 단계적으로 나누어야 한다는 제약이 공통으로 따라온다.

  • https://reference-m1.tistory.com/211

Java 8 Stream sorted

Stream.sorted()는 요소의 자연 순서(Comparable)로, sorted(Comparator)는 지정한 비교자로 정렬한다. 정렬 키에 null이 섞여 있으면 Comparator.comparing(...)이 비교 중 NullPointerException을 던진다. 이때 Comparator.nullsFirst나 nullsLast로 키 비교자를 감싼다.

1
2
3
4
list.stream()
    .sorted(Comparator.comparing(Item::getDueDate,
            Comparator.nullsLast(Comparator.naturalOrder())))
    .collect(Collectors.toList());

역순은 .reversed(), 2차 정렬 기준은 .thenComparing(...)으로 이어 붙인다.

  • https://stackoverflow.com/questions/36361156/null-safe-date-comparator-for-sorting-in-java-8-stream?answertab=trending#tab-top
  • https://codechacha.com/ko/java8-stream-sorted/

용량에 따른 파일업로드 실패

FE에서 막혔는지, BE에서 막혔는지 확인 -> FE에서 막혔다면 코드상에서 막혔는지 확인 -> 아파치 or ngnix 에서의 설정으로 막혔는지 확인 -> BE에서 막혔다면 코드상에서 막혔는지 확인 -> 톰캣 서버 설정으로 막혔는지 확인

요청이 거치는 단계마다 크기 제한이 있어서 어느 단계에서 거부되었는지부터 찾아야 한다. 단계별로 확인할 설정은 다음과 같다.

단계설정비고
Nginxclient_max_body_size기본값 1m, 넘으면 413 응답
Spring Bootspring.servlet.multipart.max-file-size, max-request-size기본값 1MB, 10MB
TomcatmaxSwallowSize, maxPostSize기본값 각각 2MiB

Nginx 기본값과 413 응답은 nginx 문서, Tomcat 기본값은 Tomcat 커넥터 문서에서 확인했다. Tomcat의 maxPostSize는 POST 본문 전체 한도가 아니라 폼 데이터를 요청 파라미터로 변환할 때의 한도라는 점도 문서에 적혀 있다. 응답 코드가 413이면 프록시 단계에서, Spring의 MaxUploadSizeExceededException이 보이면 애플리케이션 단계에서 막힌 것으로 구분할 수 있다.

두 번째 링크의 Current request is not a multipart request는 크기 문제가 아니라, 요청의 Content-Type이 multipart/form-data가 아니어서 생기는 에러다.

  • https://okky.kr/article/462849
  • https://stackoverflow.com/questions/42013087/ㅇmultipartexception-current-request-is-not-a-multipart-request

깃허브 전문가 코드리뷰 서비스

GitHub Marketplace의 PullRequest 앱은 외부의 검증된 리뷰어가 PR에 인라인 피드백을 남겨 주는 온디맨드 코드 리뷰 서비스다. 리뷰 소요 시간 같은 코드 리뷰 지표를 보여 주는 무료 플랜도 함께 제공한다.

  • https://github.com/marketplace/pullrequest/plan/MLP_kgDNGO8#pricing-and-setup
참고한 자료외부 출처 4

외부 출처

이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다