์คํ๋ง ๋ถํธ์์ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์๋ฒฝ ๊ฐ์ด๋: @Async์ CompletableFuture ํ์ฉ๋ฒ
๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ ์ ํ๋ฆฌ์ผ์ด์
์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐ ํ์์ ์ธ ๊ธฐ์ ์
๋๋ค. Spring Boot ์์๋
@Async
์
CompletableFuture
๋ฅผ ์ฌ์ฉํด ๋น๋๊ธฐ ๋ฉ์๋๋ฅผ ์ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค. ์ด๋ฒ ํฌ์คํ
์์๋ ๋น๋๊ธฐ ๋ฉ์๋ ๊ตฌํ ๋ฐฉ๋ฒ ,
CompletableFuture
๋ก ๋น๋๊ธฐ ๊ฒฐ๊ณผ ์ฒ๋ฆฌ , ๊ทธ๋ฆฌ๊ณ ๋น๋๊ธฐ ์์
์์ ๋ฐ์ํ๋ ์์ธ ์ฒ๋ฆฌ ์ ๋ต ๊น์ง ์์ธํ ๋ค๋ฃน๋๋ค.
๋ชฉ์ฐจ
- ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ๋ฌด์์ธ๊ฐ?
- @Async์ ๋น๋๊ธฐ ๋ฉ์๋ ๊ตฌํํ๊ธฐ
- CompletableFuture๋ฅผ ์ด์ฉํ ๋น๋๊ธฐ ๊ฒฐ๊ณผ ์ฒ๋ฆฌ
- ๋น๋๊ธฐ ์์ ์์ธ ์ฒ๋ฆฌ ์ ๋ต
- ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ํธ๋ ๋์ ์ฅ์
1. ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ๋ฌด์์ธ๊ฐ?
๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ์์ ์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ค๋ฅธ ์์ ์ ๊ณ์ ์งํํ๋ ๋ฐฉ์ ์ ๋๋ค. ์ด๋ ๋๊ท๋ชจ ํธ๋ํฝ์ ์ฒ๋ฆฌํ๊ฑฐ๋, ์ธ๋ถ API ํธ์ถ๊ณผ ๊ฐ์ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์ ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ต ์๋๋ฅผ ํฅ์ ์ํต๋๋ค.
๋๊ธฐ ์ฒ๋ฆฌ | ๋น๋๊ธฐ ์ฒ๋ฆฌ |
---|---|
ํ๋์ ์์ ์ด ์๋ฃ๋ ํ ๋ค์ ์์ ์์ | ์์ ์ ๋ณ๋ ฌ๋ก ์ํํ์ฌ ์ฒ๋ฆฌ ์๋ ์ฆ๊ฐ |
์: ํ์ผ ๋ค์ด๋ก๋ ํ ์ด๋ฉ์ผ ์ ์ก | ์: ํ์ผ ๋ค์ด๋ก๋์ ์ด๋ฉ์ผ ์ ์ก ๋ณํ |
๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ์ฃผ๋ก I/O ์์ ์ด๋ ๋คํธ์ํฌ ํธ์ถ ์ ์ต์ ํํ ๋ ๋ง์ด ์ฌ์ฉ๋ฉ๋๋ค.
2. @Async์ ๋น๋๊ธฐ ๋ฉ์๋ ๊ตฌํํ๊ธฐ
@Async๋ฅผ ์ฌ์ฉํ ๋น๋๊ธฐ ๋ฉ์๋ ๊ตฌํ
@Async
์ ๋ํ
์ด์
์ ์ฌ์ฉํ๋ฉด ๊ฐ๋จํ๊ฒ ๋น๋๊ธฐ ๋ฉ์๋ ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ํด๋น ๋ฉ์๋๋ ํธ์ถ ์ ๋ณ๋์ ์ค๋ ๋์์ ์คํ ๋ฉ๋๋ค.
์ค์ : ๋น๋๊ธฐ ์ฒ๋ฆฌ ํ์ฑํ
@EnableAsync
์ ๋ํ
์ด์
์ ์ฌ์ฉํด ๋น๋๊ธฐ ๊ธฐ๋ฅ์ ํ์ฑํํด์ผ ํฉ๋๋ค.
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync
public class AsyncConfig {
}
๋น๋๊ธฐ ๋ฉ์๋ ์์
package com.example.demo.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Async
public void sendEmail(String email) {
System.out.println("์ด๋ฉ์ผ ์ ์ก ์ค... " + email);
try {
Thread.sleep(5000); // ์ด๋ฉ์ผ ์ ์ก ์๋ฎฌ๋ ์ด์
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("์ด๋ฉ์ผ ์ ์ก ์๋ฃ: " + email);
}
}
์ฝ๋ ์ค๋ช
-
@Async
: ํด๋น ๋ฉ์๋๊ฐ ๋ณ๋์ ์ค๋ ๋์์ ๋น๋๊ธฐ์ ์ผ๋ก ์คํ๋จ์ ์๋ฏธํฉ๋๋ค. sendEmail()
๋ฉ์๋๋ ํธ์ถ ์ฆ์ ๋ค๋ฅธ ์์ ๊ณผ ๋ณํ ๋์ด ์คํ๋ฉ๋๋ค.
์คํ ๊ฒฐ๊ณผ
์ด๋ฉ์ผ ์ ์ก ์ค... example@email.com
(5์ด ํ)
์ด๋ฉ์ผ ์ ์ก ์๋ฃ: example@email.com
3. CompletableFuture๋ฅผ ์ด์ฉํ ๋น๋๊ธฐ ๊ฒฐ๊ณผ ์ฒ๋ฆฌ
CompletableFuture
๋ ๋น๋๊ธฐ ์์
์ ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋๋ก ๋์์ฃผ๋ ํด๋์ค์
๋๋ค. ์ด ํด๋์ค๋ ๋น๋๊ธฐ ์์
์๋ฃ ํ ์๋์ผ๋ก ํ์ ์์
์ ์คํํ ์ ์๊ฒ ํด์ค๋๋ค.
CompletableFuture ์ฌ์ฉ ์์
UserService.java
package com.example.demo.service;
import com.example.demo.entity.User;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class UserService {
@Async
public CompletableFuture<User> getUserById(Long id) {
System.out.println("์ฌ์ฉ์ ์กฐํ ์์: " + id);
try {
Thread.sleep(3000); // ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ ์๋ฎฌ๋ ์ด์
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User(id, "ํ๊ธธ๋");
System.out.println("์ฌ์ฉ์ ์กฐํ ์๋ฃ: " + user.getName());
return CompletableFuture.completedFuture(user);
}
}
UserController.java
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
@RestController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/users/{id}")
public CompletableFuture<User> getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
}
์ ์ถ๋ ฅ ์์
์์ฒญ :
GET /users/1
์๋ต :
{
"id": 1,
"name": "ํ๊ธธ๋"
}
4. ๋น๋๊ธฐ ์์ ์์ธ ์ฒ๋ฆฌ ์ ๋ต
๋น๋๊ธฐ ์์
์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด, ์ด๋ฅผ ๋ช
ํํ๊ฒ ์ฒ๋ฆฌ ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. CompletableFuture ๋
handle()
๋ฉ์๋ ๋ฅผ ์ ๊ณตํ์ฌ ์์ธ ๋ฐ์ ์ ๋์ฒด ๋ก์ง์ ์คํํ ์ ์์ต๋๋ค.
๋น๋๊ธฐ ์์ธ ์ฒ๋ฆฌ ์์
public CompletableFuture<User> getUserById(Long id) {
return CompletableFuture.supplyAsync(() -> {
if (id == 0) {
throw new IllegalArgumentException("ID๋ 0์ผ ์ ์์ต๋๋ค.");
}
return new User(id, "ํ๊ธธ๋");
}).handle((user, ex) -> {
if (ex != null) {
System.out.println("์์ธ ๋ฐ์: " + ex.getMessage());
return new User(0L, "์ ์ ์์");
}
return user;
});
}
์ฝ๋ ์ค๋ช
-
supplyAsync()
: ๋น๋๊ธฐ ์์ ์ ์ํํฉ๋๋ค. -
handle()
: ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ๋์ฒด ๊ฐ์ ๋ฐํํฉ๋๋ค.
5. ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ํธ๋ ๋์ ์ฅ์
- ๋๊ท๋ชจ ํธ๋ํฝ ์ฒ๋ฆฌ : ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ ์์ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
- ๋น๋๊ธฐ API ์ฌ์ฉ ์ฆ๊ฐ : ๋ง์ ์ธ๋ถ API๊ฐ ๋น๋๊ธฐ ํธ์ถ ์ ์ง์ํ๊ณ ์์ต๋๋ค.
- ๊ณ ์ฑ๋ฅ ์ ํ๋ฆฌ์ผ์ด์ : ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ I/O ๋๊ธฐ ์๊ฐ์ ์ต์ํ ํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ์ ๋์ ๋๋ค.
- ๋น๋๊ธฐ ๋ฉ์์ง ์์คํ : Kafka์ ๊ฐ์ ๋ฉ์์ง ํ ์์คํ ๊ณผ ๊ฒฐํฉํด ์ฌ์ฉ๋ฉ๋๋ค.
๊ด๋ จ ๋งํฌ
CompletableFuture ๊ฐ์ด๋๐
Java ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ๐
Spring Boot ๋น๋๊ธฐ ์ฒ๋ฆฌ ์์ ๐
Java ExecutorService์ ์ค๋ ๋ ํ๐
Spring WebFlux๋ก ๋ฆฌ์กํฐ๋ธ ํ๋ก๊ทธ๋๋ฐ๐
FAQ
1. @Async ๋ฉ์๋๋ ์ธ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋์?
- ์ค๋ ๊ฑธ๋ฆฌ๋ ์์ (์: ์ด๋ฉ์ผ ์ ์ก, ์ธ๋ถ API ํธ์ถ)์์ ์ฌ์ฉํ๋ฉด ์ข์ต๋๋ค.
2. CompletableFuture์ Future์ ์ฐจ์ด์ ์ ๋ฌด์์ธ๊ฐ์?
- CompletableFuture ๋ ํ์ ์์ ์ ์ฒ๋ฆฌํ ์ ์์ผ๋ฉฐ, ๋น๋๊ธฐ ์์ ์ ์๋ฃ ์์ ์ ์ฝ๋ฐฑ์ ๋ฑ๋ก ํ ์ ์์ต๋๋ค.
3. ๋น๋๊ธฐ ๋ฉ์๋์์ ๋ฐํ ํ์ ์ ๊ผญ CompletableFuture์ฌ์ผ ํ๋์?
- ๋น๋๊ธฐ ์์ ์ ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ๋ ค๋ฉด CompletableFuture ๊ฐ ๊ถ์ฅ๋์ง๋ง, void ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
4. ๋น๋๊ธฐ ์์ ์ค ๋ฐ์ํ ์์ธ๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์?
CompletableFuture
์handle()
๋๋exceptionally()
๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์ฒ๋ฆฌํฉ๋๋ค.
5. ์ค๋ ๋ ํ์ ์ด๋ป๊ฒ ์ค์ ํ ์ ์๋์?
-
TaskExecutor
๋ฅผ ์ปค์คํฐ๋ง์ด์งํ์ฌ ์ค๋ ๋ ํ์ ์ค์ ํ ์ ์์ต๋๋ค.
๋ง๋ฌด๋ฆฌ
์ด๋ฒ ํฌ์คํ
์์๋ Spring Boot์ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ ๋ค๋ค์ต๋๋ค.
@Async
์
CompletableFuture
๋ฅผ ์ฌ์ฉํด ๋น๋๊ธฐ ๋ฉ์๋๋ฅผ ๊ตฌํ ํ๊ณ , ์์ธ ์ฒ๋ฆฌ ์ ๋ต ์ ํตํด ๋ฐ์ํ ์ ์๋ ์ค๋ฅ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด์์ต๋๋ค. ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฑ๋ฅ๊ณผ ์๋ต ์๋ ๋ฅผ ํฌ๊ฒ ํฅ์์ํฌ ์ ์๋ ์ค์ํ ๊ธฐ์ ์
๋๋ค. ํนํ I/O ์์
๊ณผ ์ธ๋ถ API ํธ์ถ ์์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ ํ์ฉํ๋ฉด ๋๊ท๋ชจ ํธ๋ํฝ๋ ํจ๊ณผ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
CompletableFuture ๋ ๋น๋๊ธฐ ์์ ์ ์๋ฃ ์์ ์ ํ์ ์์ ์ ๋ฑ๋กํ ์ ์์ด, ๋ณต์กํ ๋น๋๊ธฐ ๋ก์ง๋ ๊ฐ๊ฒฐํ๊ฒ ๊ด๋ฆฌํ ์ ์์ต๋๋ค. ์ด์ฒ๋ผ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ์์ธ ์ฒ๋ฆฌ ๋ฅผ ์ ๊ฒฐํฉํ๋ฉด, ์์ ์ ์ด๊ณ ์ฑ๋ฅ ์ข์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ์ ์์ต๋๋ค.
๋๊ธ