본문 바로가기

FastCampus - 한번에 끝내는 Java|Spring 웹 개발/03 스프링 입문

스프링 Ch 7 Spring as client - 패스트캠퍼스 챌린지 12일차

오늘은 코로나19 부스터샷을 맞았습니다. 그래서 본디 하려고 했던 스프링 아키텍처의 라이트한 딥다이브(?)는 넘겨두고 다음장을 보기로 했습니다. 패캠 챌린지도 있고, 저는 부작용이 거의 없는 편이라서 학습을 대충 하고 오늘을 그냥 넘기기엔 아쉽네요. 혼자 했으면 그냥 넘기고 쉬었을텐데...

오늘의 내용은 서버간 연결을 위해 Spring 앱을 HTTP 클라이언트로서 동작시키는 내용입니다. 포트는 별개의 포트를 쓰구요, 클라이언트가 여러종류 있다네요. 강의에서는 자세히 설명을 안 해주셔서 제가 찾아봤습니다.

스프링에서 자주 쓰이는 클라 종류

@Service

이 어노테이션이 강의에 두 번은 나왔는데 설명이 나온 기억이 없습니다. 좀 찾아봤습니다.

결론은 "별 거 없고 @Componet 이다" 입니다.

  • @Service 는 그저 분류용 어노테이션이고, 스프링이 관리하는 @Component 객체로 동작합니다 (어노테이션에 포함되어있음).
  • 의미상으로는 DDD에서 말하는 "서비스" 라고 공식 문서에서 말합니다. 어디에도 속하지 않지만 여기저기에서 쓰이는 유틸성 함수들/클래스들을 말하는 거 같네요.
  • 보통 🔍@Controller vs @Repository vs @Service 처럼 검색하는 거 같고, 이렇게 분리가 되어있다는 건 aspectj 같은 곳에서 타겟을 잡을 때 활용할 수 있다고 하네요. 전부 그냥 컴포넌트인데 분류만 다르다고...
    • S/O (내용은 "스프링 문서 퍼와요~")
    • baeldung

클라이언트가 들어가는 클래스는 강의에서 자연스럽게 @Service 어노테이션을 답니다.

RestTemplate

RestTemplate - RestTemplate

생각외로 복잡하지 않습니다. 그냥 클래스 생성하고 메서드 호출하면 됩니다. 단, 실제 적용시에는 풀(Pool)로 만들어서 쓴다고 하네요.

  • restTemplate.getForObject(URI, class); -> 바로 값을 주고, 실패하면 예외를 떨구는 모양입니다.
  • restTemplate.getForEntity(URI, class); -> 핸들러에서 반환했던 거 같은 ResponseEntity<class> 를 반환받습니다. StatusCode 확인도 가능해서 좀 더 유연하게 사용할 수 있을 거 같네요.
  • 오브젝트 매핑도 클래스를 잘 넣으면 알아서 모양입니다.
  • get 부분은 HTTP 메서드입니다. 그 부분을 바꾸면...
    • post 의 경우 보낼 객체도 추가 가능합니다.

RestTemplate - UriComponetsBuilder

UriComponetsBuilder 의 활용을 강력 주장하시네요. 하긴 직접 URL 만들기 번거롭긴 하죠 ㅋㅋ 수동으로 접합하다가 잘못하면 간접적인 공격의 대상이 될 수도 있고...

  • 강력한 장점이 있습니다. 쿼리 패러미터나 "PathVariable"(...) 을 만들고 .expand(값) 으로 채워넣을 수 있습니다.
  • 빌더니까 빌드를 해야합니다.
  • 순서
    1. UriComponentsBuilder.
      fromUriString(베이스경로 URI) 팩토리 메서드
    2. .path(경로) 로 세부경로 및 패러미터 명시
    3. .encode()
    4. .build() -> 여기부터 UriComponents
    5. .expand(값) 로 값 채워넣기 (다회 호출이 아닌 콤마로 여러개 넣어줘야 함)
    6. .toUri() -> 완성 (URI)

RestTemplate - 헤더 설정

외부 API와의 연동이 이번 챕터의 가장 큰 목적입니다. 인증정보를 헤더에 안 넣으면 외부 서비스와의 연동은 힘들겠죠.

이를 위해서 RequestEntity<Body타입> 을 만들어서, requestTemplate.exchange(엔티티, 응답클래스) 로 보냅니다.

RequestEntity<UserRepository> rqe = RequestEntity
    .post(uri)
    .contentType(MediaType.APPLICATION_JSON)
    .header(키, 값)
    .header(키, 값)
    ...
    .body(요청대상 객체)
    ;

RequestTemplate rt = new RequestTemplate();
ResponseEntity<생략> resp = rt.exchange(rqe, UserResponse.class)

서버측에서는 핸들러에 @RequestHeader("헤더키") 패러미터 를 추가하여 확인하네요.

@PostMapping
public User postHandler(
	...생략...
    @RequestHandler("authorization") String authorization
) {
	...
}

강의에서는 자연스럽게 헤더 이름 앞에 x- 가 붙어있는데, 이건 안 넣는게 좋습니다.

동적 Body type DTO

RestTemplate 3강에서 반갑지 않은 케이스에 대한 설명이 나옵니다.

{
    "header": { ... },
    "body": { 이 부분이 다른 타입으로 바뀜 }
}

DTO에 제네릭을 넣으면 됩니다. Body가 T 면 되죠. 예시 코드는 너무 뻔해서 불필요하겠네요.

이런 짓을 해야하는 사례는 직접 만들지는 맙시다. Action.do 만큼 *극동 욕설*(*)

동적 Body type DTO - ParameterizedTypeReference 의 사용

그으런데 이런 경우에는 제네릭을 런타임에 관리하지 않는 JVM 제약에는 안 걸리나보네요? 아닙니다. 이전에도 한 번 나왔던 ParameterizedTypeReference 의 익명 클래스를 만드는 방법을 사용합니다.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/ParameterizedTypeReference.html

익명 클래스 만들기인 만큼 kotlin 에서는 방법이 조금 다릅니다. (이해를 돕기 위해 익명 객체 만드는 부분은 따로 적음) 공식문서어디갓냐아몰라귀찮아

val typeCapture = object: ParameterizedTypeReference<List<String>>() {}
val resp: ResponseEntity<List<String>> = RestTemplate().exchange(
    RequestEntity.post("http://naver.com").body("asdf"),
    typeCapture
)

 

요청 그대로 디버깅하기 - HttpEntity<T>

핸들러 패러미터에 HttpEntity<String> 인 패러미터를 넣으면 다른 패러미터로 추출되기 전의 요청 내용을 뜯어볼 수 있습니다.

@PostMapping
public User postHandler(
    HttpEntity<String> entity,
	...생략...
) {
	...
}

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
패스트캠퍼스: https://bit.ly/37BpXiC

#패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #한번에끝내는JavaSpring웹개발마스터초격차패키지Online

디버깅하는 거 구경하기 지치네요... (얻는 게 없는 건 아닌데 가성비가 너무 나쁨) 네이버 지역검색 API 연동은 내일 봐야겠습니다.