κ°μ
λΉλκΈ° νλ‘κ·Έλλ°μ μ€λλ μ μννΈμ¨μ΄ κ°λ°μμ λ§€μ° μ€μν κ°λ μ λλ€. λμ©λ λ°μ΄ν° μ²λ¦¬, λλ¦° I/O μμ , 볡μ‘ν κ³μ° λ± λ€μν μμ μ λ³λ ¬λ‘ μ²λ¦¬νλ©΄μ μμ€ν μ μλ΅ μκ°μ κ°μ νκ³ , 리μμ€λ₯Ό ν¨μ¨μ μΌλ‘ μ¬μ©νλλ° λμμ΄ λκΈ° λλ¬Έμ λλ€.
μ€νλ§μμλ @Async μ΄λ
Έν
μ΄μ
μ ν΅ν΄ μ΄λ¬ν λΉλκΈ° λ©μλλ₯Ό κ°λ¨νκ² μ€νν μ μλλ°, μ΄λ¬ν @Async μ΄λ
Έν
μ΄μ
μ μ¬μ©ν λ μ£Όμν΄μΌ ν μ¬λ¬ κ°μ§ μ¬νμ λν΄μ μ λ¦¬ν΄ λ³΄κ² μ΅λλ€.
μ£Όμμ
- Exception Handling
- λ©μλ νΈμΆ
- λ¦¬ν΄ νμ
- νΈλμμ κ΄λ¦¬
- Execution
Exception Handling
κΈ°λ³Έμ μΌλ‘ @Async λ©μλμμ λ°μνλ μμΈλ νΈμΆμμκ² μ νκ° λμ§ μμ΅λλ€. μ΄λ @Async μ΄λ Έν μ΄μ μ΄ λΆμ λ©μλκ° λ³λμ μ€λ λμμ μ€νλλ―λ‘ λ©μΈ μ€λ λμμ μΊμΉλ₯Ό ν μ μκΈ° λλ¬Έμ λλ€. μμΈλ₯Ό μ²λ¦¬νκΈ° μν΄μλ AsyncUncaughtExceptionHandlerλ₯Ό μ¬μ©νμ¬ μμΈλ₯Ό μ μ ν μ²λ¦¬ν΄μΌ ν©λλ€.
AsyncConfigurerSupportλ₯Ό μμλ°λ ν΄λμ€
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncUncaughtExceptionHandler();
}
}
AsyncUncaughtExceptionHandlerλ₯Ό ꡬννλ ν΄λμ€
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("Exception message - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
}
}
μμ κ°μ΄ AsyncConfigurerSupportλ₯Ό μμλ°κ³ , getAsyncUncaughtExceptionHandler()λ₯Ό μ€λ²λΌμ΄λ©νμ¬ μ¬μ©μκ° μ ν μμΈ νΈλ€λ¬λ₯Ό μ¬μ©ν μ μμ΅λλ€.
λ©μλ νΈμΆ
@Service
public class xxxService {
public void internalCall() {
this.asyncMethod(); // λκΈ°μ μΌλ‘ μ€νλ¨
}
@Async
public void asyncMethod() {
// λΉλκΈ°λ‘ μ²λ¦¬λμ΄μΌ νλ λ‘μ§
}
}
μ μ½λμ²λΌ μ€νλ§μμ @Async μ΄λ Έν μ΄μ μ΄ λΆμ λ©μλλ κ°μ ν΄λμ€ λ΄λΆμμ μ§μ νΈμΆν κ²½μ°, λ³λμ μ€λ λμμ μ€νλμ§ μμ΅λλ€. μ΄λ μ€νλ§μ AOPκ° νλ‘μ κΈ°λ°μΌλ‘ λμνκΈ° λλ¬Έμ λλ€.
μ΄ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄μλ λ³λμ λΉμ μμ±νκ±°λ, λ€λ₯Έ ν΄λμ€μμ νΈμΆν΄μΌ νλ©° κ°μ ν΄λμ€μμ νΈμΆνλ €λ©΄ μλμ²λΌ self referenceλ₯Ό μ¬μ©ν΄μΌ ν©λλ€.
@Service
public class SelfInvocationService {
@Autowired
private SelfInvocationService self;
public void method() {
self.asyncMethod(); // self-invocationμΌλ‘ λΉλκΈ°μ μΌλ‘ μ€νλ¨
}
@Async
public void asyncMethod() {
// λΉλκΈ°λ‘ μ²λ¦¬λμ΄μΌ νλ λ‘μ§
}
}
μ μ½λμμ method()λ self.asyncMethod();λ₯Ό ν΅ν΄ λΉλκΈ° λ©μλλ₯Ό νΈμΆνκ³ μμ΅λλ€. selfλ SelfInvocationServiceμ μΈμ€ν΄μ€λ‘, μ€νλ§ μ»¨ν μ΄λμ μν΄ νλ‘μκ° μ£Όμ λ©λλ€. μ΄λ κ² λλ©΄ asyncMethod()κ° λΉλκΈ°μ μΌλ‘ μ€νλ©λλ€.
λ¦¬ν΄ νμ
@Async μ΄λ Έν μ΄μ μ΄ λΆμ λ©μλλ void, Future, CompletableFuture μ΄ μ€ νλμ λ°ν νμ μ κ°μ ΈμΌ ν©λλ€. λ§μ½ Futureλ₯Ό λ°ννλ κ²½μ°, λΉλκΈ° μμ μ κ²°κ³Όλ₯Ό λ°ννκ³ , ν΄λΉ μμ μ μλ£λ₯Ό λκΈ°ν μ μμ΅λλ€. μλ μ½λλ μ΄λ₯Ό 보μ¬μ£Όλ μμμ λλ€.
@Service
public class AsyncService {
@Async
public Future<String> asyncMethodWithReturnType() {
System.out.println("Execute method asynchronously - " + Thread.currentThread().getName());
try {
Thread.sleep(5000);
return new AsyncResult<>("hello world !!!!");
} catch (final InterruptedException e) {
//
}
return null;
}
}
μμ λ©μλλ λΉλκΈ°μ μΌλ‘ μ€νλλ©°, μμ μ΄ μλ£λ λκΉμ§ κΈ°λ€λ¦¬μ§ μκ³ μ¦μ Future κ°μ²΄λ₯Ό λ°νν©λλ€. κ·Έλ¦¬κ³ μ΄ Futureκ°μ²΄λ₯Ό ν΅ν΄ λΉλκΈ° μμ μ κ²°κ³Όλ₯Ό κ°μ Έμ¬ μ μμ΅λλ€.
κ·Έλ¬λ Futureλ CompletableFuture κ°μ²΄λ₯Ό λ°νλ°μλ, λΉλκΈ° μ°μ°μ΄ μμ§ μλ£λμ§ μμμ κ²½μ° λ°νκ°μ μ€μ κ°λ€μ λ΄κΈ°μ§ μμμ μλ μμ΅λλ€.
@Service
public class xxxService {
@Autowired
private AsyncService asyncService;
public void method() {
Future<String> future = asyncService.asyncMethodWithReturnType();
String result = future.get(); // This will block
System.out.println(result);
}
}
ν΄κ²° λ°©λ²μΌλ‘λ, μμ κ°μ΄ Futureμ get() λ©μλλ₯Ό ν΅ν΄ λΉλκΈ° μμ μ΄ λλ λκΉμ§(κ°μ΄ λ΄κΈΈ λκΉμ§) νμ¬ μ€λ λλ₯Ό λΈλ‘νΉνλ λ°©λ²μ΄ μμ΅λλ€. νμ§λ§ μ΄λ λΉλκΈ° λ©μλλ₯Ό λκΈ°μ μΌλ‘ λμνκ² λ§λ€μ΄, μλ λΉλκΈ°λ‘ μ€κ³λ μ½λμ μ΄μ μ λλ¦¬μ§ λͺ»νκ² λ©λλ€.
λ°λΌμ Futureλ CompletableFutureμ κ²°κ³Όλ₯Ό μ²λ¦¬ν λλ μ£Όμκ° νμνλ°, κ°λ₯νλ©΄ non-blocking λ°©μμΌλ‘ μ κ·Όνκ±°λ, νμν κ²½μ° μ½λ°±μ μ¬μ©νμ¬ μ²λ¦¬νλ κ²μ΄ μ’μ΅λλ€. μ΄λ CompletableFutureμ thenApply, thenAccept λ±μ λ©μλλ₯Ό μ¬μ©νμ¬ κ΅¬νν μ μμΌλ©°, μ΄λ κ² νλ©΄ λΉλκΈ° μ°μ° κ²°κ΄κ°μ΄ λ΄κ²Όμ λ λΉλκΈ°μ μΌλ‘ κ²°κ³Όλ₯Ό μ²λ¦¬ν μ μμ΅λλ€.
νΈλμμ κ΄λ¦¬
λΉλκΈ° λ©μλμμ νΈλμμ μ μ¬μ©ν λλ μ£Όμκ° νμν©λλ€. @Async μ΄λ Έν μ΄μ μ΄ λΆμ λ©μλλ νΈμΆν λ©μλμ λ 립μ μΈ μ€λ λμμ λμνκΈ° λλ¬Έμ, λΉλκΈ° λ©μλ λ΄μμ μμ±λ νΈλμμ μ νΈμΆν λ©μλμ νΈλμμ κ³Όλ λ³κ°μ μλͺ μ£ΌκΈ°λ₯Ό κ°μ§λλ€.
μ¦, λΉλκΈ° λ©μλ λ΄μμ μμΈκ° λ°μνμ¬ νΈλμμ μ΄ λ‘€λ°±λμ΄μΌ ν κ²½μ°μλ, μ΄λ ν΄λΉ νΈλμμ μ μμ±ν λ³λμ μ€λ λμμ λ 립μ μΌλ‘ μ²λ¦¬λλ―λ‘, μλμ νΈμΆ λ©μλμ νΈλμμ μλ μν₯μ λ―ΈμΉμ§ μμ΅λλ€.
@Service
public class TransactionalService {
@Autowired
private AsyncService asyncService;
@Transactional
public void transactionalMethod() {
asyncService.asyncMethodWithNewTransaction();
}
}
μ μ½λμμ, transactionalMethod()λ @Transactional μ΄λ Έν μ΄μ μ΄ λΆμ΄ νΈλμμ λ²μ λ΄μμ μ€νλμ§λ§, asyncMethodWithNewTransaction()μ @Async μ΄λ Έν μ΄μ μ΄ λΆμ΄ μλ‘μ΄ μ€λ λμμ μ€νλλ―λ‘ λ³λμ νΈλμμ μ μμ±ν©λλ€.
λ°λΌμ asyncMethodWithNewTransaction()μμ μμΈκ° λ°μνμ¬ λ‘€λ°±μ΄ νμνλλΌλ, μ΄λ asyncMethodWithNewTransaction()λ₯Ό μ€ννλ λ³λμ μ€λ λμμ μ²λ¦¬λλ―λ‘ transactionalMethod()μ νΈλμμ μλ μν₯μ λ―ΈμΉμ§ μμ΅λλ€.
Executor
μ€νλ§μμ λΉλκΈ° μμ μ μννλ €λ©΄ λ³΄ν΅ @EnableAsync μ΄λ Έν μ΄μ κ³Ό @Asyncλ₯Ό μ¬μ©ν©λλ€. κ·Έλ¬λ μ΄λ κ² μ€μ νλ©΄ κΈ°λ³Έμ μΌλ‘ SimpleAsyncTaskExecutorκ° μ¬μ©λλλ°, μ΄λ λΉλκΈ° μμ λ§λ€ μλ‘μ΄ μ€λ λλ₯Ό μμ±νλ μ€λ λνμ λλ€. μ΄λ‘ μΈν΄ 리μμ€ λλΉ, μ±λ₯ μ ν, μ€μΌμΌλ§ λ¬Έμ λ±μ΄ λ°μν μ μμ΅λλ€.
- 리μμ€ λλΉ: κ° λΉλκΈ° μμ λ§λ€ μλ‘μ΄ μ€λ λλ₯Ό μμ±νλ―λ‘, λμμ λ§μ λΉλκΈ° μμ μ΄ μμ²λλ©΄ λ§€λ² λ§μ μ€λ λκ° μμ±λλ€. λ°λΌμ CPUμ λ©λͺ¨λ¦¬ 리μμ€μ μ¬μ©λμ΄ κ³Όλνκ² μ¦κ°νλ λ¬Έμ κ° λ°μν μ μλ€.
- μ±λ₯ μ ν: μ€λ λλ₯Ό μμ±νκ³ μλ©Έμν€λ λ°λ λ§μ μκ°κ³Ό 리μμ€κ° μμλλ€. κ° μμ λ§λ€ μ€λ λλ₯Ό μμ±νλ©΄ μ΄λ° μ€λ²ν€λκ° κ³μ λ°μνκ² λκ³ , μ 체μ μΈ μμ€ν μ±λ₯μ μν₯μ λΌμΉ μ μλ€.
- μ€μΌμΌλ§ λ¬Έμ : SimpleAsyncTaskExecutorλ μ€λ λ μμ λν μ νμ΄ μλ€. λ°λΌμ λμμ λ§μ μμ²μ΄ λ€μ΄μ¬ κ²½μ° μ€λ λ μκ° λ¬΄νμ μΌλ‘ μ μ΄ν μ μλ μμ€μΌλ‘ μ¦κ°ν μ μμΌλ©°, μ΄λ 곧 OutOfMemoryError λ±μ λ¬Έμ λ₯Ό μΌμΌν¬ μ μλ€.
μ΄λ¬ν μ΄μ λ‘, νΉλ³ν κ²½μ°κ° μλλΌλ©΄ SimpleAsyncTaskExecutor 보λ€λ ThreadPoolTaskExecutor κ°μ μ νλ 리μμ€λ₯Ό μ¬μ©νλ μ€λ λνμ μ¬μ©νλ κ²μ΄ μ’μ΅λλ€.
ThreadPoolTaskExecutorλ Javaμ ThreadPoolExecutorλ₯Ό Wrapping ν κ²μΌλ‘, μ€νλ§μμ μ 곡νλ TaskExecutor μΈν°νμ΄μ€μ ꡬν체μ λλ€. Executorλ₯Ό μ μνλ λ°©λ²μλ μλμ λ κ°μ§ λ°©λ²μ΄ μμ΅λλ€.
- BeanμΌλ‘ μ μ
- AsyncConfigurerSupportλ₯Ό μμλ°μ getAsyncExecutor()λ₯Ό μ¬μ μ
1. μ§μ BeanμΌλ‘ Executorλ₯Ό μ μ
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "threadPoolTaskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("Executor-");
executor.initialize();
return executor;
}
}
κ°κ°μ μ΅μ μ€μ μ λν μ€λͺ μ λ€μκ³Ό κ°μ΅λλ€.
- setCorePoolSize
- μ€λ λ νμ΄ μ€νμ μμν λ μμ±λλ μ€λ λμ μλ₯Ό μ μνλ©°, μ€λ λ νμ κΈ°λ³Έ μ¬μ΄μ¦λ₯Ό μ€μ ν©λλ€.
- μ€λ λ νμ μμ μ΄ λ€μ΄μ€κΈ° μμνλ©΄ μ¦μ corePoolSizeλ§νΌμ μ€λ λλ₯Ό μμ±νκ³ , μ€λ λλ€μ μ€λ λ νμ΄ μμ μ λ°μ λκΉμ§ λκΈ° μνλ‘ μ μ§λ©λλ€. λ§μ½ λͺ¨λ core μ€λ λκ° μμ μ μ²λ¦¬νκ³ μκ³ μΆκ° μμ μ΄ λ€μ΄μ¨ κ²½μ°, μμ μ νμ λ°°μΉλκ±°λ maxPoolSizeκΉμ§ μ€λ λκ° μΆκ°λ‘ μμ±λ©λλ€.
- setMaxPoolSize
- μ€λ λ νμ΄ νμ₯λ μ μλ μ€λ λμ μνμ μ μ€μ νλ©°, μ€λ λ νμ΄ κ΄λ¦¬ν μ μλ μ΅λ μ€λ λ μλ₯Ό μ μν©λλ€. λ§μ½ corePoolSizeλ₯Ό μ΄κ³Όνλ μμ μ΄ λ€μ΄μ¬ κ²½μ°, μ΄λ¬ν μΆκ° μμ λ€μ λ¨Όμ νμ λ°°μΉλ©λλ€. κ·Έλ¬λ λκΈ°μ΄μ΄ κ°λ μ°¨λ©΄ μ€λ λ νμ maxPoolSizeμ λλ¬ν λκΉμ§ μΆκ° μ€λ λλ₯Ό μμ±νμ¬ μμ μ μ²λ¦¬ν©λλ€.
- maxPoolSizeμ λλ¬νλ©΄ μ€λ λ νμ μ μμ μ λ°μλ€μ΄μ§ μμΌλ©°, λμ μ μ± μ λ°λΌ κ±°λΆνκ±°λ λ€λ₯Έ λ°©λ²μΌλ‘ μ²λ¦¬ν©λλ€.
- setQueueCapacity
- corePoolSizeκ° κ°λ μ°¬ μνμμ μΆκ° μμ μ μ²λ¦¬ν μ μμ λ λκΈ°νλ μμ μ μ΅λ κ°μλ₯Ό μ μνλ©°, μ€λ λ νμ μμ λκΈ°μ΄ ν¬κΈ°λ₯Ό μ€μ ν©λλ€.
- μμ λκΈ°μ΄μ corePoolSizeμ μλ μ€λ λλ€μ΄ λͺ¨λ μ¬μ© μ€μΌ λ μΆκ° μμ μ μμ μ μ₯νλ 곡κ°μ λλ€. λκΈ°μ΄μ ν¬κΈ°κ° κ½ μ°¨λ©΄, μ€λ λ νμ maxPoolSizeκΉμ§ μ€λ λλ₯Ό μμ±νμ¬ λκΈ°μ΄μ μλ μμ μ μ²λ¦¬νκΈ° μμν©λλ€. λκΈ°μ΄μ΄ κ°λ μ°¨κ³ maxPoolSizeμ λλ¬νλ©΄ μ€λ λ νμ μλ‘μ΄ μμ μ μ΄λ»κ² μ²λ¦¬ν μ§ κ²°μ ν΄μΌ νλ©°, μΌλ°μ μΌλ‘ κ±°λΆ μ μ± (RejectedExecutionHandler)μ μ¬μ©νμ¬ μΆκ° μμ μ μ²λ¦¬ν©λλ€.
- setThreadNamePrefix
- μμ±λλ μ€λ λμ μ΄λ¦ μ λμ¬λ₯Ό μ μν©λλ€. μ΄λ₯Ό ν΅ν΄ λλ²κΉ μ΄λ λ‘κΉ μμ μ΄λ€ μ€λ λκ° μμ μ μ²λ¦¬νκ³ μλμ§ μ½κ² μλ³ν μ μλλ°, νμ¬ μμμ²λΌ μμ±νλ©΄ κ° μ€λ λμ μ΄λ¦μ΄ Executor-1, Executor-2μ κ°μ΄ μ€μ λ©λλ€.
- initialize()
- μ€λ λ νμ μ΄κΈ°νν©λλ€. initialize()κ° νΈμΆλλ©΄, μ€μ λ corePoolSizeλ§νΌμ μ€λ λκ° μ¦μ μμ±λ©λλ€.
μ΄λ κ² μ€μ ν ν, @Async μ΄λ Έν μ΄μ μ μλμ²λΌ Executorμ Bean μ΄λ¦μ μΈμλ‘ μ λ¬νλ©΄ λ©λλ€.
@Service
public class xxxService {
@Async("threadPoolTaskExecutor")
public void asyncMethod() {
// λΉλκΈ°λ‘ μ²λ¦¬λμ΄μΌ νλ λ‘μ§
}
}
μ΄ λ°©λ²μ ThreadPoolTaskExecutorλ₯Ό μ§μ BeanμΌλ‘ λ±λ‘ν©λλ€. λ°λΌμ @Async μ΄λ Έν μ΄μ μ Bean μ΄λ¦μ μ§μ μ§μ ν΄μ€ μ μμ΅λλ€. μ΄ λ°©μμ μ₯μ μ μ¬λ¬ κ°μ Executorλ₯Ό κ°κ° BeanμΌλ‘ λ±λ‘νκ³ , μν©μ λ§κ² μ μ ν Executorλ₯Ό μ νν μ μλ€λ μ μ λλ€. μ¦, λΉλκΈ° μμ μ μ’ λ₯λ 볡μ‘λμ λ°λΌ λ€λ₯Έ Executorλ₯Ό μ¬μ©ν μ μμ΅λλ€.
μμ
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
//...
}
@Bean(name = "anotherExecutor")
public Executor anotherExecutor() {
//...
}
}
@Service
public class xxxService {
@Async("threadPoolTaskExecutor")
public void asyncMethod() {
// λ‘μ§
}
@Async("anotherExecutor")
public void anotherAsyncMethod() {
// λ€λ₯Έ λ‘μ§
}
}
2. AsyncConfigurerSupportλ₯Ό μμλ°μ getAsyncExecutor()λ₯Ό μ¬μ μ
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("Executor-");
executor.initialize();
return executor;
}
}
μ΄ λ°©λ²μ μ€νλ§μ κΈ°λ³Έ Executorλ₯Ό μ¬μ μνμ¬ μ¬μ©νλ λ°©λ²μΌλ‘, λ³λμ μ΄λ¦μ μ§μ νμ§ μμλ λ©λλ€. μ΄ λ°©μμ μ μμ μΈ Executor μ€μ μ μ μ©νλ©°, νλ‘μ νΈ μ 체μμ λ¨μΌ Executorλ₯Ό μ¬μ©νλ €λ κ²½μ°μ μ ν©ν©λλ€.
μμ
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//...
return executor;
}
}
@Service
public class xxxService {
@Async
public void asyncMethod() {
// λ‘μ§
}
}
λ°λΌμ, μ΄λ€ λ°©λ²μ μ νν μ§λ λΉμ¦λμ€ λ‘μ§μ μꡬ μ¬νκ³Ό νλ‘μ νΈμ κ·λͺ¨μ λ°λΌ κ²°μ νλ©΄ λ©λλ€.
κ²°λ‘
λΉλκΈ° νλ‘κ·Έλλ°μ μμ€ν
μ μ±λ₯μ ν₯μμν€λ λ° λ§€μ° μ μ©νμ§λ§, μ£Όμν΄μΌ ν μ λ€μ΄ λ§μ΅λλ€. νΉνλ μ€νλ§μμ @Async μ΄λ
Έν
μ΄μ
μ μ¬μ©νμ¬ λΉλκΈ° λ©μλλ₯Ό λ§λ€ λλ μμμ λ§ν Execption Handling, λ©μλ νΈμΆ, λ¦¬ν΄ νμ
, νΈλμμ
κ΄λ¦¬, Execution λ±μ μ μ ν κ³ λ €ν΄μΌ ν©λλ€.
μ°Έκ³
- https://www.baeldung.com/spring-async
- ν λΉμ μ€νλ§ 3.1
'BackEndπ± > Spring' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
Spring Data Redisμ @Indexed μ¬μ© μ μ£Όμμ (0) | 2023.08.07 |
---|---|
WebClientμμ μλ¬ μ²λ¦¬μ μ¬μλνλ λ°©λ² (0) | 2023.08.03 |
Spring Batchλ? κ°λ¨ν κ°λ κ³Ό μ½λ μ΄ν΄λ³΄κΈ° (0) | 2023.07.29 |
Kafka κ°λ κ³Ό Spring Boot + Kafka κ°λ¨ν μ°λ (1) | 2023.07.19 |
μ€νλ§ μ΄λ²€νΈ λ°νκ³Ό ꡬλ μΌλ‘ νΈλμμ λΆλ¦¬νκΈ° (1) | 2023.07.09 |
SSEλ‘ μλ¦Ό κΈ°λ₯ ꡬννκΈ° with Spring (7) | 2023.06.18 |
λκΈ