1004 - 1010
# 1004 - 1010
# 1005 - ๋๊ธฐ์ ๋น๋๊ธฐ, ๋ธ๋ญ๊ณผ ๋๋ธ๋ญ
๋๊ธฐ(Synchronous)์ ๋น๋๊ธฐ(Asynchronous), ๊ทธ๋ฆฌ๊ณ ๋ธ๋ญ(Blocking)๊ณผ ๋๋ธ๋ญ(Non-blocking)
# Blocking / Non-blocking
ํ์์๊ฐ ์ทจํ ํ์ ์์ฒด๊ฐ, ๋๋ ๊ทธ ํ์๋ก ์ธํด ๋ค๋ฅธ ๋ฌด์์ด ๋งํ๋ฒ๋ฆฐ, ์ ํ๋, ๋๊ธฐํ๋ ์ํ. ๋๊ฐ์ ๊ฒฝ์ฐ์๋ ๋ ์ด์ธ์ ๋์์ผ๋ก ํ์ฌ๊ธ ๋ด๊ฐ Block ๋นํ๊ฒ ์ง๋ง(Blocked), ์ด์ฐ ๋์๋ ๋ฌธ์ ์์ฒด๋ก๋ ๋๋ผ๋ ***๋จ์ผ ๊ฐ์ฒด ์ค์ค๋ก์ ์ํ***๋ฅผ ๋ํ๋ธ๋ค.
- ํธ์ถ๋ ํจ์๊ฐ ์์ ์ด ํ ์ผ์ ๋ชจ๋ ๋ง์น ๋๊น์ง ์ ์ด๊ถ์ ๊ณ์ ๊ฐ์ง๊ณ ์ ํธ์ถํ ํจ์์๊ฒ ๋ฐ๋ก ๋๋ ค์ฃผ์ง ์์ผ๋ฉด Block
- ํธ์ถ๋ ํจ์๊ฐ ์์ ์ด ํ ์ผ์ ์ฑ ๋ง์น์ง ์์๋๋ผ๋ ๋ฐ๋ก ์ ์ด๊ถ์ ๊ฑด๋ค์ฃผ์ด(return) ํธ์ถํ ํจ์๊ฐ ๋ค๋ฅธ ์ผ์ ์งํํ ์ ์๋๋ก ํด์ฃผ๋ฉด Non-block
# Synchronous / Asynchronous
***๋์์ ๋ฐ์ํ๋***๊ฒ๋ค. ๋์๋ผ๋ ๊ฒ์ ์ฆ, ์(time)๋ผ๋ ๋จ์ผ๊ณ(system)์์ ๊ฐ์ด, ํจ๊ป ๋ฌด์ธ๊ฐ๊ฐ ์ด๋ฃจ์ด์ง๋ ๋ ๊ฐ ์ด์์ ๊ฐ์ฒด ํน์ ์ด๋ฒคํธ๋ฅผ ์๋ฏธํ๋ค๊ณ ๋ณผ ์ ์๋ค.
- ํธ์ถ๋ ํจ์์ ์ํ ๊ฒฐ๊ณผ ๋ฐ ์ข ๋ฃ๋ฅผ ํธ์ถํ ํจ์๊ฐ(ํธ์ถ๋ ํจ์๋ฟ ์๋๋ผ ํธ์ถํ ํจ์๋ ํจ๊ป) ์ ๊ฒฝ ์ฐ๋ฉด Synchronous
- ํธ์ถ๋ ํจ์์ ์ํ ๊ฒฐ๊ณผ ๋ฐ ์ข ๋ฅ๋ฅผ ํธ์ถ๋ ํจ์ ํผ์ ์ง์ ์ ๊ฒฝ ์ฐ๊ณ ์ฒ๋ฆฌํ๋ค๋ฉด(as a callback function) Asynchronus
# ์์
# Blocking & Synchronous
๋ : ๋ํ๋, ๊ฐ๋ฐ์ ์ข ๋ ๋ฝ์์ฃผ์ธ์..
๋ํ๋ : ์ค์ผ์ด, ์ ๊น๋ง ๊ฑฐ๊ธฐ ๊ณ์ธ์!
๋ : โฆ?!!
๋ํ๋ : (์ฑ์ฉ ๊ณต๊ณ ๋ฑ๋ก.. ์ง์์ ์ฐ๋ฝ.. ๋ฉด์ ์งํ.. ์ฐ๋ด ํ์..)
๋ : (๊ณผ์ ์ง์ผ๋ด.. ๊ถ๊ธํจ.. ์ด์ฐจํผ ๋ด ์ผ ํ๋ฌ๋ ๋ชป ๊ฐ๊ณ ๊ณ์ ์ ์์)
# Blocking & Asynchronous
๋ : ๋ํ๋, ๊ฐ๋ฐ์ ์ข ๋ ๋ฝ์์ฃผ์ธ์..
๋ํ๋ : ์ค์ผ์ด, ์ ๊น๋ง ๊ฑฐ๊ธฐ ๊ณ์ธ์!
๋ : โฆ?!!
๋ํ๋ : (์ฑ์ฉ ๊ณต๊ณ ๋ฑ๋ก.. ์ง์์ ์ฐ๋ฝ.. ๋ฉด์ ์งํ.. ์ฐ๋ด ํ์..)
๋ : (์ ๊ถ๊ธํจ.. ์ง๋๊ฐ๋ ๋ง๋ก ์ฌ์ญ์๋๋ฐ ๋ถ์กํ๋ฒ๋ฆผ.. ๋ด ์๊ฐ.. ๋ชป ๊ฐ๊ณ ๊ณ์ ์ ์์)
# Non-blocking & Synchronous
๋ : ๋ํ๋, ๊ฐ๋ฐ์ ์ข ๋ ๋ฝ์์ฃผ์ธ์..
๋ํ๋ : ์๊ฒ ์ต๋๋ค. ๊ฐ์ ๋ณผ ์ผ ๋ณด์ธ์.
๋ : ๋ต!
๋ํ๋ : (์ฑ์ฉ ๊ณต๊ณ ๋ฑ๋ก.. ์ง์์ ์ฐ๋ฝ.. ๋ฉด์ ์งํ.. ์ฐ๋ด ํ์..)
๋ : ์ฑ์ฉํ์
จ๋์?
๋ํ๋ : ์์ง์.
๋ : ์ฑ์ฉํ์
จ๋์?
๋ํ๋ : ์์ง์.
๋ : ์ฑ์ฉํ์
จ๋์?
๋ํ๋ : ์์ง์~!!!!!!
# Non-blocking & Asynchronous
๋ : ๋ํ๋, ๊ฐ๋ฐ์ ์ข ๋ ๋ฝ์์ฃผ์ธ์..
๋ํ๋ : ์๊ฒ ์ต๋๋ค. ๊ฐ์ ๋ณผ ์ผ ๋ณด์ธ์.
๋ : ๋ต!
๋ํ๋ : (์ฑ์ฉ ๊ณต๊ณ ๋ฑ๋ก.. ์ง์์ ์ฐ๋ฝ.. ๋ฉด์ ์งํ.. ์ฐ๋ด ํ์..)
๋ : (์ด์ผ์ค..)
๋ํ๋ : ํ ๋ถ ๋ชจ์๊ธฐ๋ก ํ์ต๋๋ค~!
๋ : ๐
# 1006 - Spring5 WebClient
# WebClient๋?
Spring WebClient๋? ์น์ผ๋ก API๋ฅผ ํธ์ถํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ Non-Blocking ๋ฐฉ์์ Http Client ๋ชจ๋ ์ค ํ๋์ด๋ค.
Java์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ Http Clent๋ RestTemplate์ด๋ค. ์ฐจ์ด์ ์ RestTemplate๋ Blocking๋ฐฉ์์ด๊ณ , WebClient๋ Non-Blocking๋ฐฉ์์ด๋ค.
# ๊ฐ๋จํ์์
# Building with
- maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
- gradle
dependencies {
compile 'org.springframework.boot:spring-boot-starter-webflux'
}
# Instance ์์ฑ
WebClient client1 = WebClient.create();
// or
WebClient client1 = WebClient.create("http://localhost:8080");
WebClient client3 = WebClient
.builder()
.baseUrl()
.defaultCookie()
.defaultHeader()
.build();
# WebClient Java Config
@Configuration
@Slf4j
public class WebClientConfig {
@Bean
public WebClient webClient() {
// ExchangeStrategies HTTP ๋ฉ์ธ์ง reader/writer ์ปค์คํ
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(1024*1024*50))
.build();
exchangeStrategies
.messageWriters().stream()
.filter(LoggingCodecSupport.class::isInstance)
.forEach(writer -> ((LoggingCodecSupport)writer).setEnableLoggingRequestDetails(true));
return WebClient.builder()
.clientConnector(
new ReactorClientHttpConnector(
HttpClient
.create()
.secure(
ThrowingConsumer.unchecked(
sslContextSpec -> sslContextSpec.sslContext(
SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build()
)
)
)
// timeout ์ค์
.tcpConfiguration(
client -> client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 120_000)
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(180))
.addHandlerLast(new WriteTimeoutHandler(180))
)
)
)
)
.exchangeStrategies(exchangeStrategies)
// request ๋ก๊ทธ
.filter(ExchangeFilterFunction.ofRequestProcessor(
clientRequest -> {
log.debug("Request: {} {}", clientRequest.method(), clientRequest.url());
clientRequest.headers().forEach((name, values) -> values.forEach(value -> log.debug("{} : {}", name, value)));
return Mono.just(clientRequest);
}
))
// response ๋ก๊ทธ
.filter(ExchangeFilterFunction.ofResponseProcessor(
clientResponse -> {
clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value -> log.debug("{} : {}", name, value)));
return Mono.just(clientResponse);
}
))
.defaultHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.3")
.build();
}
}
MaxInMemorySize
ExchageStrategies.builder()๋ฅผ ํตํด in-memory buffer๊ฐ์ ๋ณ๊ฒฝ (๊ธฐ๋ณธ256KB)Logging
ExchageStrateges์ logging level์ค์
# ์ฌ์ฉ
- @Bean์ผ๋ก ๋ฑ๋ก๋ WebClient๋ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉ
@Service
@RequiredArgsConstructor
@Slf4j
public class SomeService implements SomeInterface {
private final WebClient webClient;
public Mono<SomeData> getSomething() {
return webClient.mutate()
.build()
.get()
.uri("/resource")
.retrieve()
.bodyToMono(SomeData.class);
}
}
# retrieve(), exchange()
HTTP ํธ์ถ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก retrieve()์ exchange()๊ฐ ์๋ค. Spring์์๋ exchange๋ฅผ ์ด์ฉํ๊ฒ ๋๋ฉด Response ์ปจํ ์ธ ์ ๋ํ ๋ชจ๋ ์ฒ๋ฆฌ๋ฅผ ์ง์ ํ๋ฉด์ ๋ฐ์ํ ์ ์๋ memory leak ๊ฐ๋ฅ์ฑ ๋๋ฌธ์ ๊ฐ๊ธ์ retrieve๋ฅผ ์ฌ์ฉํ๊ธฐ๋ฅผ ๊ถ๊ณ ํ๋ค.
Mono<Person> result = webClient.get()
.uri("/persons/{id}", id)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
# 4xx and 5xx์ฒ๋ฆฌ
webClient.mutate()
.baseUrl("https://some.com")
.build()
.get()
.uri("/resource")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(status -> status.is4xxClientError()
|| status.is5xxServerError()
, clientResponse ->
clientResponse.bodyToMono(String.class)
.map(body -> new RuntimeException(body)))
.bodyToMono(SomeData.class)
# GET ์์
public Mono<SomeData> getData(Integer id, String accessToken) {
return
webClient.mutate()
.baseUrl("https://some.com/api")
.build()
.get()
.uri("/resource?id={ID}", id)
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
.retrieve()
.bodyToMono(SomeData.class)
;
}
# POST ์์
- form ๋ฐ์ดํฐ ์ ์ก
webClient.mutate()
.baseUrl("https://some.com/api")
.build()
.post()
.uri("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromFormData("id", idValue)
.with("pwd", pwdValue)
)
.retrieve()
.bodyToMono(SomeData.class);
- JSON body ๋ฐ์ดํฐ ์ ์ก
webClient.mutate()
.baseUrl("https://some.com/api")
.build()
.post()
.uri("/login")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.bodyValue(loginInfo)
.retrieve()
.bodyToMono(SomeData.class);
# DELETE
webClient.mutate()
.baseUrl("https://some.com/api")
.build()
.delete()
.uri("/resource/{ID}", id)
.retrieve()
// Void ๋ฐํ
.bodyToMono(Void.class);
# 1010 - Inner class์ Static์ ๋ถ์ด๋ ์ด์
# Nested Class
- Nested classes
- Inner classes
- Inner classes
- Method local Inner classes
- Anonymous Inner classes (์ต๋ช
ํด๋์ค)
- Static Nested classes
์๋ฐ๋ ์ผ๋ฐ์ ์ธ ํด๋์ค๋ฅผ ์ ์ธํ๊ณ ์ผ๋ฐ์ ์ผ๋ก ํด๋์ค์ ์ค์ฒฉ์ ์ด๋ ๊ฒ ๋๋๋ค.
# Nested Class
- ํจํค์ง๊ฐ ๋๋ ํด๋์ค๊ฐ ์๋ ๋๋จธ์ง ํด๋์ค
- ํ๋์ ํจํค์ง์ ๋ํด ์ฌ๋ฌ ํด๋์ค๋ฅผ ์ฌ์ฉํ ์ ์๊ณ , ์๋ก๊ฐ ๊ฒฐํฉ๋ง์ด ์กด์ฌํ๋ ํด๋์ค์ ๊ฒฝ์ฐ ๊ฐ๋ ์ฑ์ด ์ข๊ณ ๊ด๋ฆฌํ๊ธฐ ํธํ๋ค.
ํด๋์ค๊ฐ ๋ค๋ฅธ ํด๋ ์ค์๋ง ์ ์ฉ ํ ๊ฒฝ์ฐ ํด๋น ํด๋์ค์ ํด๋์ค๋ฅผ ํฌํจ์ํค๊ณ ๋ ํด๋์ค๋ฅผ ํข๊ป ์ ์งํ๋ ๊ฒ์ด ๋ ผ๋ฆฌ์ ์ด๋ค. ์ด๋ฌํ "ํฌํผ ํด๋์ค"๋ฅผ ์ค์ฒฉํ๋ฉด ํจํค์ง๊ฐ ๋์ฑ ๊ฐ์ํ๋๋ค.
- ์ค์ฒฉ ํด๋์ค๋ค์ ํจํค์ง ํด๋์ค ๋ด๋ถ์ ์จ๊ฒจ์ ธ์์ผ๋ฏ๋ก ์บก์ํ์ ๋์์ด ๋๋ค.
A์ B๋ผ๋ ๋ ๊ฐ์ง ์ต์์ ํด๋์ค๋ฅผ ๊ณ ๋ คํ๋ค. ์ฌ๊ธฐ์ B๋ ์ ์ธ ๋ A์ ๋ฉค๋ฒ์ ์ก์ธ์คํด์ผํ๋ค. ํด๋์ค A ๋ด์ ํด๋์ค B๋ฅผ ์จ๊ธฐ๋ฉด A์ ๊ตฌ์ฑ์์ ๋น๊ณต๊ฐ๋ก ์ ์ธํ๊ณ B๊ฐ ์ก์ธ์ค ํ ์ ์๋ค. ๋ํ B ์์ฒด๋ ์ธ๋ถ ์ธ๊ณ์์ ์จ๊ธธ ์ ์๋ค.
# non-static nested class
- Inner class๋ผ๊ณ ํ๋ฉฐ ์ธ๋ถ ์ธ์คํด์ค์ ๋ํ ์ฐธ์กฐ๊ฐ ์ ์ง๋๋ค.
- ์ธ๋ถ ์ธ์คํด์ค๋ ๋ด๋ถ ํด๋์ค๋ฅผ new๋ฅผ ํตํ ์ธ์คํด์ค ํ ๋น์ผ๋ก ๋ฉค๋ฒ๋ณ์๋ฌ์ฒข ์ฌ์ฉํ ์ ์๋ค.
- ์ธ๋ถ์ ๋ํ ์ฐธ์กฐ๊ฐ ์ ์ง๋๋ฏ๋ก ๋ด๋ถ ํด๋์ค๋ ์ธ๋ถ ํด๋์ค์ ์์์ ์ฌ์ฉํ ์ ์๋ค.
public class Outer {
class Inner {
}
}
Outer outer = new Outer();
Outer.Inner inner = new Outer.Inner();
# static nested class
- static์ด ๋ถ๋ ์ค์ฒฉ ํด๋์ค
- ๋์ผํ static ๋ฉค๋ฒ๋ค์ ์ฌ์ฉ ๊ฐ๋ฅ
- static์ ํน์ง์ ๋ฐ๋ผ ์ธ๋ถ ์ธ์คํด์ค ๋ฉค๋ฒ์ ์ง์ ์ฐธ์กฐ๊ฐ ๋ถ๊ฐ๋ฅ
public class Outer {
static class staticNasted {
}
}
Outer.Inner staticNasted = new Outer.Inner();
# ์ฐจ์ด
- ์ธ๋ถ ์ฐธ์กฐ์ ์ฌ๋ถ
- ์ธ๋ถ ์ฐธ์กฐ๊ฐ ์ ์ง๋๋ค๋ ๊ฒ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ํ ์ฐธ์กฐ๊ฐ ์ ์ง๋๊ณ ์๋ค๋ ๋ป์ผ๋ก GC๊ฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ์ํ ์์๋ค. ๋น์ฐํ ์ด๋ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ถ๋ฅด๋ ์น๋ช ์ ์ธ ๋จ์ .
- ํญ์ ์ธ๋ถ ์ธ์คํด์ค์ ์ฐธ์กฐ๋ฅผ ํตํด์ผ ํ๋ฏ๋ก ์ฑ๋ฅ ์ ๋นํจ์จ์ .
- ๊ฒฐ๊ตญ ์ธ๋ถ ์ธ์คํด์ค์ ๋ํ ์ฐธ์กฐ๊ฐ ํ์ํ์ง ์๋ค๋ฉด static nested class๊ฐ ๋ซ๋ค.
- ์ฌ๊ธฐ์์ staitc์ ์๋ฏธ๋ ์ธ๋ถ ์ธ์คํด์ค ์์ด ๋ด๋ถ ํด๋์ค๋ฅด์ ์ธ์คํด์ค๋ฅผ ๋ฐ๋ก ์์ฑํ ์ ์๋ค๋ ๋ป์ผ๋ก ์ฌ์ฉ ๋๋ค.
# Spring Boot์์ DTO๊ด๋ฆฌ๋ฅผ ์ํ static nested class
- DTO ์์
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
public class User {
@Getter
@AllArgsConstructor
@Builder
public static class Info {
private int id;
private String name;
private int age;
}
@Getter
@Setter
public static class Request {
private String name;
private int age;
}
@Getter
@AllArgsConstructor
public static class Response {
private Info info;
private int returnCode;
private String returnMessage;
}
}
- Controller
import com.parksh.demo.dto.DefaultResponse;
import com.parksh.demo.dto.user.User;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(โuserโ)
public class UserController {
@GetMapping(โ/{user_id}โ)
public User.Response getUser(@PathVariable(โuser_idโ) String userId) {
return new User.Response(new User.Info(), 200, โsuccessโ);
}
@PostMapping
public DefaultResponse addUser(@RequestBody User.Info info) {
return new DefaultResponse();
}
}