[우아한테크코스] 9월 5일 TIL
[OAuth] google OAuth flow
https://developers.google.com/identity/protocols/oauth2/images/flows/authorization-code.png
다양한 api들을 gcp 라이브러리에서 확인할 수 있다.
거기서 내가 프로젝트에서 사용할 api를 선택하고 사용 버튼을 누르면 된다.
-
Request Token 요청 전송
https://accounts.google.com/o/oauth2/auth? client_id=[Your Client ID]& redirect_uri=http://localhost:8080/redirectCode& scope=https://www.googleapis.com/auth/indexing& response_type=code
Client_id = 어플리케이션 고유 id
Redirect_uri = 사용자 확인이 완료되어 리다이렉트돼 돌아올 uri
Scope = 사용자의 어떤 정보에 접근할지 지정하는 것
해당 url에 접속하면 응답으로
redirect_url?code=어떠한코드
가 나온다. -
exchange code for token 요청 전송
https://www.googleapis.com/oauth2/v4/token?code=4%2F0AX4XfWhDAC2ogD9Om0nXZmAT4qOoq6JG2UO5sxzW2iVVuFulAm3l-awfsr99IhC8qkxpzA& client_id=350852545256-9r8sj68t72bc880ug8e594j9dolimu88.apps.googleusercontent.com& client_secret=KPFAV2fi7tRfYcNVfMoDx8eT& redirect_uri=http://localhost:8080& grant_type=authorization_code
Code = redirect에서 나온 Authorization coe
Client_secret = 비밀번호
Grant_type = 권한 허용 타입
전송하면 응답으로 access_token을 담은 것이 나온다.
-
Authorization Header에 추가해 원하는 값 받기
대시보드에 있는 다양한 api에 요청을 보내 원하는 값을 얻을 수 있다.
drive, userinfo, indexing api 등등 사용할 수 있다.
공식문서 를 참고해서 다양한 api들을 볼 수 있다.
[Spring] WebClient vs RestTemplate
소비자가 제공자에게 원하는 정보를 얻고자 요청하고, 제공자는 그에 알맞는 정보를 제공하는 것이 API이다.
Http Client는 웹으로 API를 호출하기 위해 Apache에서 제공되는 라이브러리이다.
java에서는 Http Client 모듈을 사용한 WebClient와 RestTemplate을 대표적으로 사용한다.
이 둘의 차이점을 알아보기 전에 동기와 비동기, Blocking과 non-blocking에 대해서 알아보자.
-
block과 non-block
이들은 어떠한 함수가 다른 함수를 호출했을 때 바로 리턴을 받는지 안받는지, 다른 일을 수행할 수 있는지 없는지가 중점이다.
-
Blocking
호출된 함수가 자신의 할 일을 마치고 나서 자신을 호출한 함수에게 돌려주는 방식, 호출한 함수는 그 일을 마칠 때까지 기다려야 한다.
요청하고 응답이 올 때까지 기다리는 방식
-
Non-blocking
호출된 함수가 자신의 할 일을 마치지 않았어도 바로 return 하여 자신을 호출한 함수가 다른 일을 할 수 있도록 하는 방식
요청하고 다른 일을 수행하다 나중에 응답신호가 오면 결과를 읽어 처리하는 방식
-
-
동기와 비동기
이들은 호출된 함수를 누가 관여하는지가 중점이다.
-
Synchronous 동기
호출된 함수(B)의 수행 결과나 종료를 그 함수를 호출한 함수(A)가 함께 관여하는 방식
A는 B가 그 일을 잘 끝마쳤는지 계속 신경쓰는 비용이 발생한다.
요청과 응답 사이에 계속 Connection이 맺어져 있는다.
-
Asynchronous 비동기
호출된 함수의 수행 결과나 종료를 호출된 함수만 관여하는 방식
호출한 함수 A는 호출된 함수 B에 callback 함수를 전달한다. B는 자신의 작업이 끝나면 callback을 실행한다.
요청과 응답 사이에 Connection은 끊어지고 이벤트를 통해 통신한다.
하지만 처리 순서 보장, 중복 처리 방지, 중복 제거, 트랜잭션 관련 문제가 존재한다. 참고
-
-
Sync-Blocking
A가 B를 호출하면 B가 수행되는동안 A는 다른 일을 하지 못하고 대기한다.
File.read()
,file.write()
,psmt.executeUpdate()
가 있다. -
Async-NonBlocking
A가 B를 callback과 함께 호출하면 B는 바로 반환을 하고 B가 자신의 일을 끝마치면 받았던 callback을 호출한다.
A는 그 시간동안 다른 일을 수행할 수 있다.
Node.js
가 그 예시로 존재한다. -
Sync-NonBlocking
A가 B를 호출하면 B는 바로 리턴하고 A는 B의 작업 완료를 관여한다. 기다리거나 물어보거나가 관여하는 방식 중 하나이다. 이 경우는 작업을 완료했는지 물어보는 방식으로 관여한다.
while문 안에서 계속 그 일이 끝마쳤는지 물어보며 자신의 일을 수행한다.
가 예시가 될 수 있으나 잘 쓰이지 않는다. -
Async-Blocking
A가 B를 호출하면 B는 바로 리턴하지 않고 A는 작업 완료를 관여하지 않는다.
Node.js + MySQL
이 그 방법 중에 하나가 될 수 있다. Node.js에서 콜백을 열심히 보내서 Async로 와도 DB에서는 Block 방식으로 받기 때문이다. 자바도 마찬가지인데 노드가 싱글 쓰레드라 자바의 멀티스레드보다 더 문제가 되어 보이는 것이다.
WebClient
WebClient는 Spring의 WebFlux로부터 동작한다.
Spring WebFlux는 적은 수의 쓰레드와 적은 resource들을 사용해 동시성을 다루기 위해 생겨났다고 한다.
따라서 WebClient의 큰 특징 중 하나는 non-blocking을 이용해 적은 수의 쓰레드를 사용하면서 요청을 처리할 수 있다는 것이다.
Blocking I/O는 mvc와 RDBMS 간의 모델이다. 요청이 들어왔을 때 그 시간동안 application이 아무 작업도 할 수 없음을 의미한다. 그래서 더 많은 thread를 사용하게 되고 비효율적이다.
Event-Driven 프로그래밍은 프로그램의 실행 흐름이 각종 event에 의해 결정되는 것이다.
spring MVC의 경우 요청이 들어오면 queue에 들어가고, thread pool에 여유 있는 thread가 있으면 바로 처리하지만 남는 thread가 없으면 대기한다. 하나의 요청이 하나의 thread를 사용하는 구조이다. 따라서 thread에 대한 생성 비용을 줄이기 위해 서버가 감당할 수 있는 최대한의 thread를 생성해놓고 사용하지 않으면 할당하는 식의 반복이다.
여유가 있을 때는 빠르게 처리할 수 있지만 넘으면 대기 시간만큼 느려진다. 그래서 non-blocking인 webflux가 나왔다. 하지만 일정 사용자까지는 webflux와 mvc의 성능이 유사한 만큼, 잘 생각해서 적용해야 한다. 왜냐면 mvc는 디버깅도 쉽고 코드도, 이해도 쉬우니까!
worker thread는 서버의 core 수만큼 할당된다. 이 또한 apache tomcat의 서블릿 컨테이너로부터 할당받는다. non-blocking I/O에 할당되는 스레드를 사용한다. 관련 밸덩
-
Single-Thread, Non-Blocking 방식
보다 효율적인 통신 가능
하나의 쓰레드를 이용하며 요청이 들어오면 event loop 내에 job으로 등록되어 처리된다.
job이 처리되고 나서 event loop에 callback으로 응답이 오면 결과를 요청자에게 제공한다.
반응성, 탄력성, 가용성, 비동기성 보장하는 Spring React Web 프레임워크인 Spring WebFlux에서 HttpClient로 사용됨
구현 방법은 아래와 같이 builder 처럼 원하는 url을 만들어 전송하고, response를 특정 클래스에 바인딩할 수 있다.
GoogleTokenResponse googleTokenResponse = WebClient.builder() .baseUrl("https://www.googleapis.com/oauth2/v4/token") //host 지정 .build() .post() .uri(uriBuilder -> uriBuilder.queryParam("code", code) //path 지정 .queryParam("client_id", clientId) .queryParam("client_secret", secretId) .queryParam("redirect_uri", redirectUri) .queryParam("grant_type", "authorization_code") .build()) .headers(header -> { header.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); header.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8)); }) .retrieve() //얘는 뒤에 오는 애로 응답을 처리하겠다는 의미 .bodyToMono(GoogleTokenResponse.class) //얘는 다양한 애로 가능 .blockOptional() //이걸 추가하면 동기로 처리되는 것이다. 그럼 장점이 어디로..? .orElseThrow(UnableToGetTokenResponseFromGoogleException::new);
추가적으로,
RestTemplate
-
Multi-Thread, Blocking 방식
thread pool을 application 구동 시 만들어 놓고 요청이 들어와 thread가 다 차면 Queue에 쌓는다. 그러다 남는 thread가 있으면 해당 thread에 쌓인 요청을 할당해 처리한다.
실제로 RestTemplate과 WebClient를 비교해보면 사용자가 늘어날 수록 기하급수적인 차이가 난다.
RestTemplate은 게다가 deprecated 된다니 WebClient을 사용하도록 하자.
[OS] Thread safe
멀티스레드 프로그래밍에서 어떠한 함수나 변수에 여러 스레드가 동시에 접근해도 프로그램 실행에 문제가 없는 경우를 의미한다.
- reentrancy
A function whose effect, when called by two or more threads, is guaranted to be as if the threads each executed the function one after another in an undefined order, even if the actual execution is interleaved.
둘 이상의 스레드에 의해서 함수가 호출되었을 때 결과가 달라지지 않도록 하는 것.
모든 reentrancy 함수는 thread-safe하지만 모든 thread-safe 함수가 reentrancy 함수는 아니다.
- Thread-local storage
공유 자원을 최소한으로 줄이고 각 스레드에서만 접근 가능한 저장소를 사용해 각기 다른 스레드 저장소 접근을 제어한다.
- Mutual exclusion
공유 자원에 대한 접근을 세마포어와 같은 락으로 통제한다.
- Atomic operations
공유 자원에 접근할 때 원자 연산을 이용하거나 하나로 정의된 접근 방법을 사용해 상호 배제로 구현한다. 공유된 자원의 데이터나 critical section 등에 여러 프로세스나 스레드 접근 방지(semaphore), 하나의 프로세스나 스레드 접근 방지(mutex)
webclient는 불변성이기 때문에 thread-safe 하다. 이 말인 즉슨, webclient는 특정 스레드에 연결되지 않은 반응형 환경에서 사용하기 위함이므로 필요할 때마다 여러 webclient를 사용하기 보다는 하나의 webclient를 공유하고, mutate으로 사용하는 것이 좋다. (??) webclient