포스트

2023-01-12-TIL

2023-01-12-TIL

Today I Learned

컨트롤러가 아닌 곳에서 현재 요청 객체를 얻는 방법, 자바 직렬화, ACL, Firebase Admin SDK의 FirebaseApp, 그리고 쿠버네티스 없이 한 인스턴스에서 여러 컨테이너를 운영하는 방법을 정리했다.


How to Get HttpServletRequest in Any Methods in Spring

컨트롤러는 메서드 파라미터로 HttpServletRequest를 받으면 되지만, 서비스나 유틸 클래스까지 파라미터로 넘기기는 번거롭다. Spring의 RequestContextHolder는 현재 웹 요청을 스레드에 바인딩된 RequestAttributes로 노출하는 홀더 클래스라서, 어디서든 다음처럼 꺼낼 수 있다.

1
2
HttpServletRequest request =
        ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

스레드 바인딩 방식이므로 요청을 처리하는 스레드 밖(@Async, 별도 스레드 풀)에서는 기본적으로 값을 얻을 수 없다. 또 서비스 계층이 웹 요청에 의존하게 되어 테스트와 재사용이 어려워지므로, 필요한 값만 꺼내 파라미터로 넘기는 편이 낫다. 같은 맥락에서 Baeldung 글은 요청 바디를 여러 번 읽어야 할 때 요청을 캐싱 래퍼로 감싸는 방법을 다룬다.

  • https://stackoverflow.com/questions/559155/how-do-i-get-a-httpservletrequest-in-my-spring-beans
  • http://dveamer.github.io/backend/SpringRequestContextHolder.html
  • https://bryan7.tistory.com/114
  • https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times
  • https://www.baeldung.com/spring-webutils-servletrequestutils

Java Serialize

자바의 serialize는 기본적으로 jvm의 serialize를 사용할 수 있다.

자바 기본 직렬화는 Serializable 마커 인터페이스를 구현한 객체를 ObjectOutputStream으로 바이트 스트림으로 바꾸고, ObjectInputStream으로 다시 객체로 복원한다. 직렬화에서 제외할 필드는 transient로 표시한다. 클래스 버전 호환성은 serialVersionUID로 판단하는데, 명시하지 않으면 컴파일러와 클래스 구조에 따라 계산된 기본값이 쓰여서 필드 하나만 바뀌어도 역직렬화가 실패할 수 있다. Java 직렬화 명세도 컴파일러 구현에 따라 기본 serialVersionUID가 달라질 수 있다고 경고한다.

네트워크 전송이나 저장 용도로는 자바 기본 직렬화보다 JSON(Jackson), Protocol Buffers 같은 언어 중립적인 포맷을 쓰는 경우가 많다. 기본 직렬화는 자바끼리만 읽을 수 있고, 클래스 구조 변경에 취약하며, 신뢰할 수 없는 데이터를 역직렬화하면 보안 취약점으로 이어질 수 있기 때문이다.

  • https://stackoverflow.com/questions/23266397/what-is-the-need-to-transfer-an-object-via-network-using-serialization
  • https://www.baeldung.com/java-serialization-approaches
  • https://www.geeksforgeeks.org/serialization-in-java/
  • https://blogs.oracle.com/javamagazine/post/java-json-serialization-jackson
  • https://nesoy.github.io/articles/2018-04/Java-Serialize
  • https://www.baeldung.com/java-serialization
  • https://dzone.com/articles/what-is-serialization-everything-about-java-serial
  • https://stackoverflow.com/questions/11912500/serializing-and-deserializing-the-object-in-different-jvm-instances

ACL

ACL(Access Control List)은 자원마다 “누가 어떤 작업을 할 수 있는지”를 목록으로 붙여 두는 접근 제어 방식이다. 파일 시스템의 파일 권한, 네트워크 장비의 패킷 필터 규칙, 애플리케이션의 객체별 권한이 모두 ACL로 표현된다. 사용자에게 역할을 부여하고 역할에 권한을 묶는 RBAC과 달리, ACL은 개별 자원 단위로 세밀하게 권한을 줄 수 있는 대신 자원이 많아지면 관리가 어려워진다.

  • https://touo.tistory.com/43

Firebase

Firebase Admin Java SDK의 FirebaseApp은 소스 주석에 따르면 Firebase SDK의 진입점으로, Firebase API들이 공유하는 설정과 상태를 들고 있다. 서버 시작 시 FirebaseApp.initializeApp(FirebaseOptions)로 기본 앱([DEFAULT])을 한 번 초기화하고, 이후 FirebaseMessaging.getInstance() 같은 API는 기본 앱을 사용한다. 같은 이름으로 다시 초기화하면 이미 존재한다는 예외가 나므로, Spring에서는 빈 초기화 시점에 한 번만 호출되도록 하거나 FirebaseApp.getApps()로 존재 여부를 먼저 확인한다.

  • https://github.com/firebase/firebase-admin-java/blob/master/src/main/java/com/google/firebase/FirebaseApp.java

하나의 인스턴스에 다중 컨테이너를 관리

K8S 없이 하나의 인스턴스에 배포된 다중 컨테이너를 관리하려면 어떻게 해야할까?

쿠버네티스의 Pod가 참고가 된다. Mirantis 글은 Pod가 여러 면에서 단일 서버처럼 동작하며, 같은 Pod의 컨테이너들은 서로를 localhost의 다른 포트로 접근할 수 있다고 설명한다. Azure Container Instances의 컨테이너 그룹도 같은 호스트에 여러 컨테이너를 묶어 배포하는 비슷한 개념이다.

단일 인스턴스에서 이를 흉내 내는 가장 흔한 방법은 Docker Compose다. 서비스 정의, 네트워크, 볼륨, 재시작 정책을 한 파일에 선언하고 docker compose up -d로 함께 띄운다. 다만 여러 호스트로의 확장, 자동 장애 복구, 롤링 배포가 필요해지면 Docker Swarm이나 ECS, 쿠버네티스 같은 오케스트레이터를 검토해야 한다.

  • https://www.mirantis.com/blog/multi-container-pods-and-container-communication-in-kubernetes/
  • https://learn.microsoft.com/en-us/azure/container-instances/container-instances-multi-container-group
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다