2022-03-01-TIL
Today I Learned
Spring Cloud Feign
Feign은 HTTP API 호출 코드를 인터페이스 선언만으로 만들어 주는 선언형 HTTP 클라이언트다. RestTemplate으로 URL을 조립하고 응답을 변환하는 코드를 매번 쓰는 대신, 인터페이스에 애너테이션을 붙이면 구현체가 런타임에 생성된다.
Spring Cloud OpenFeign 문서에 따르면 Spring Cloud는 Feign에 Spring MVC 애너테이션 지원을 더하고, Spring Web이 기본으로 쓰는 HttpMessageConverters를 그대로 사용한다. 또 Eureka, Spring Cloud CircuitBreaker, Spring Cloud LoadBalancer와 통합해 부하 분산된 HTTP 클라이언트로 쓸 수 있다. 의존성은 spring-cloud-starter-openfeign이다.
1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableFeignClients
public class Application { }
@FeignClient(name = "stores", url = "${store.api.url}")
public interface StoreClient {
@GetMapping("/stores/{storeId}")
Store getStore(@PathVariable("storeId") Long storeId);
}
컨트롤러를 만들 때와 같은 애너테이션으로 호출할 API를 기술하므로 읽기 쉽다. 다만 호출이 메서드 호출처럼 보여서 네트워크 호출이라는 사실을 잊기 쉽다. 타임아웃, 재시도, 실패 시 대체 동작을 따로 설정해 두어야 한다.
- https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html
- https://sabarada.tistory.com/118
Object vs ? (Wildcard)
List<Object>와 List<?>는 비슷해 보이지만 다르다. 제네릭은 무공변(invariant)이라 List<String>은 List<Object>의 하위 타입이 아니다. 그래서 List<Object>를 받는 메서드에는 List<String>을 넘길 수 없다.
Java 튜토리얼은 이 차이를 printList 예제로 설명한다. List<Object>를 받게 만들면 Object 리스트만 출력할 수 있고, List<Integer>나 List<String>은 넘길 수 없다. List<?>로 받으면 어떤 구체 타입 A에 대해서도 List<A>가 List<?>의 하위 타입이므로 모든 리스트를 받을 수 있다.
1
2
static void printList(List<Object> list) { ... } // List<String>을 넘기면 컴파일 오류
static void printList(List<?> list) { ... } // 어떤 리스트든 받는다
대신 List<?>에는 원소 타입을 모르기 때문에 null 외에는 넣을 수 없다. 튜토리얼은 비한정 와일드카드가 어울리는 경우로 Object의 기능만으로 구현할 수 있는 메서드, 그리고 List.size()처럼 타입 파라미터에 의존하지 않는 메서드만 쓰는 경우를 든다.
- https://stackoverflow.com/questions/35272848/java-generics-unbound-wildcards-vs-object
Log4j 취약점
Log4j 취약점(CVE-2021-44228, 흔히 Log4Shell)은 Java 로깅 라이브러리 Log4j 2의 JNDI 기능에서 나왔다. Apache Log4j 보안 페이지에 따르면 설정, 로그 메시지, 파라미터에 쓰이는 JNDI 기능이 공격자가 제어하는 LDAP 같은 엔드포인트를 막지 못해서, 로그 메시지나 파라미터를 조작할 수 있는 공격자가 LDAP 서버에서 불러온 임의 코드를 실행할 수 있었다. CVSS 점수는 최고 등급인 10.0이다.
로그에는 요청 헤더나 파라미터 같은 사용자 입력이 그대로 들어가는 경우가 많다. 그래서 “로그 메시지를 조작할 수 있는 공격자”라는 조건은 생각보다 쉽게 충족되고, 로깅 라이브러리는 대부분의 Java 애플리케이션에 들어 있어서 영향 범위가 넓었다.
영향을 받는 것은 log4j-core JAR이고, log4j-api만 쓰는 애플리케이션은 해당되지 않는다. 처음 수정한 2.15.0도 기본값이 아닌 일부 설정에서 불완전하다는 사실이 이어서 밝혀졌고(CVE-2021-45046), 이후에도 CVE-2021-45105, CVE-2021-44832가 뒤따랐다. 보안 페이지에서 CVE별 수정 버전을 확인하고 최신 버전으로 올리는 것이 안전하다. Log4j 1은 2015년에 지원이 끝났으므로 업그레이드 대상이다.
- https://www.lunasec.io/docs/blog/log4j-zero-day/
S3 vs Glacier
S3와 Glacier를 별개 서비스로 비교하는 글이 많지만, 지금은 S3 안의 Glacier 스토리지 클래스로 보는 편이 정확하다. AWS 문서는 S3 Glacier 스토리지 클래스가 S3 서비스의 일부로 버킷에 객체로 저장되며, 데이터를 vault의 아카이브로 저장하는 별도 서비스인 Amazon Glacier와는 다르다고 구분한다.
| 스토리지 클래스 | 권장 접근 빈도 | 꺼내는 데 걸리는 시간 | 최소 보관 기간 |
|---|---|---|---|
| S3 Glacier Instant Retrieval | 분기에 한 번 정도 | 밀리초(실시간 접근) | 90일 |
| S3 Glacier Flexible Retrieval | 1년에 한두 번 | 수 분에서 수 시간 | 문서 참고 |
| S3 Glacier Deep Archive | 1년에 한 번 미만 | 최대 72시간 | 180일 |
보관 비용이 낮을수록 꺼내는 시간이 길고 접근 비용이 높다. Flexible Retrieval과 Deep Archive는 아카이브 클래스라서 바로 읽을 수 없고, 먼저 복원(restore)을 요청해야 한다. 최소 보관 기간 전에 지우거나 다른 클래스로 옮기면 남은 기간만큼 요금이 붙는다. 자주 읽는 데이터는 S3 Standard에 두고, 로그나 백업처럼 거의 읽지 않는 데이터를 수명 주기 규칙으로 Glacier 클래스로 넘기는 방식이 일반적이다.
- https://tutorialsdojo.com/amazon-s3-vs-glacier/
ETC
코드 리뷰 회고 글과 No Starch Press 프로그래밍 도서 번들을 따로 저장해 두었다.
- https://tech.wheejuni.com/2019/03/25/codereview-retrospect/
- https://www.humblebundle.com/books/joy-coding-no-starch-press-books?hmb_source=&hmb_medium=product_tile&hmb_campaign=mosaic_section_1_layout_index_1_layout_type_threes_tile_index_1_c_joyprogrammingnostarchpress_bookbundle
댓글
아직 댓글이 없습니다