ํ๋ก์ ํธ ์งํ ์ค ์ง์ ์ถ์ฒ api์์ ๋ฐํ๊น์ง ํ๊ท 6์ด ์ด์์ ์๊ฐ์ด ๊ฑธ๋ ธ๋ค. 100๊ฐ๊ฐ ๋๋ ์ง์ csv์ ๊ฐ๊ฐ์ csv๋ง๋ค 6000๊ฐ๊ฐ ๋๋ ๋ฐ์ดํฐ๊ฐ ์๋๋ฐ ์ด๊ฑธ ์ ๋ถ ์ฝ๊ธฐ ๋๋ฌธ์ ์ค๋ ๊ฑธ๋ฆฌ๋๊ฒ ๋น์ฐํ๋ค.
์ฑ๋ฅ ๊ฐ์ ์ ์ํด ๋ฉํฐ ์ค๋ ๋๋ฅผ ์ ์ฉํ์๋๋ฐ ํ๋ก์ ํธ ๋ง๊ฐ ๊ธฐํ์ด ์์๋์ง๋ผ ์ฌ์ฉ๋ฒ๋ง ์ตํ ์ ์ฉํ์๋ค.
ํ๋ก์ ํธ๊ฐ ๋๋๊ณ ๋ค์ ๋์์ ๊ณต๋ถํด๋ณด๋ ์๊ฐ๋ณด๋ค ์ด๋ ค์ ์ด๋ฒ ๊ธฐํ์ ์์ธํ ๊ณต๋ถํ๊ณ ์ ๋ฆฌํด๋ณด์๋ค.
๋น๋๊ธฐ์ ๋ฉํฐ์ค๋ ๋๋ ๊ฐ์ ๋ป์ด ์๋๋๋ค.
๋น๋๊ธฐ์ ๋ฉํฐ์ค๋ ๋๋ ๋น์ทํ๋ค ์ฐฉ๊ฐํ ์ ์์ง๋ง ๊ฐ์ ๋ป์ด ์๋๋ค.
์๋ฏธ๋ฅผ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ์๋ฉด ์๋์ ๊ฐ๋ค.
๋๊ธฐ= ์์
์ค๋ ๋= ๊ณต๊ฐ or ์ผ๊พผ
๊ทธ๋ผ ์ด์ , ํ๋์ฉ ์ดํด๋ณด์
๋๊ธฐ & ๋น๋๊ธฐ
๋๊ธฐ: ์์ฐจ์ ์ผ๋ก ์คํ๋๋ ๊ฒ
๋น๋๊ธฐ: ์์ฒญ์ ๋ฐ์ ๋ค ๋จผ์ ์์ ์ด ๋๋ ์์ผ๋ก ์คํ๋๋ ๊ฒ
์ค์ํ์์์ ๋๊ธฐ, ๋น๋๊ธฐ์ ์ฐจ์ด๋ ์๋ ์ฌ์ง๊ณผ ๊ฐ์ ์ํฉ์ด๋ค.
๋ฉํฐ ์ค๋ ๋ & ๋จ์ผ ์ค๋ ๋
๋จ์ผ์ค๋ ๋: ํ๋ก์ธ์ค๊ฐ ๋จ์ผ๋ก ๋์ํ๋ ๊ฒ
๋ฉํฐ์ค๋ ๋: ํ๋ก์ธ์ค๊ฐ ๋ค์ค์ผ๋ก ๋์ํ๋ ๊ฒ
๊ทธ๋ผ ๊ฒฝ์ฐ์ ์๊ฐ ์ด 4๊ฐ์ง๊ฐ ์๊ธด๋ค
1. ๋จ์ผ ์ค๋ ๋ - ๋๊ธฐ
2. ๋จ์ผ ์ค๋ ๋ - ๋น๋๊ธฐ
3. ๋ฉํฐ ์ค๋ ๋ - ๋๊ธฐ
4. ๋ฉํฐ ์ค๋ ๋ - ๋น๋๊ธฐ
์์ 4๊ฐ์ง๋ฅผ ์ผ์์ํ์์์ ์์๋ก ๋ค๋ฉด ์ดํด๊ฐ ์ฝ๋ค.
1. ๋จ์ผ ์ค๋ ๋ - ๋๊ธฐ
- ๋ณธ์ ์์ ์์์ ์ฃผ๋ฌธ -> ์กฐ๋ฆฌ ์๋ฃ -> ๋ค์ ์ฃผ๋ฌธ ๋ฐ์
2. ๋จ์ผ ์ค๋ ๋ - ๋น๋๊ธฐ
- ๋ณธ์ ์์ ์์์ ์ฃผ๋ฌธ -> ์กฐ๋ฆฌ ๋ฐ ์ฃผ๋ฌธ ๋ฐ๊ธฐ -> ์กฐ๋ฆฌ๊ฐ ๋น ๋ฅธ ์์๋๋ก ์ ๊ณต
3. ๋ฉํฐ ์ค๋ ๋ - ๋๊ธฐ
- ๋ณธ์ , 1ํธ์ , 2ํธ์ .. ์ค ํ๊ณณ์์ ์์์ ์ฃผ๋ฌธ -> ์กฐ๋ฆฌ ์๋ฃ -> ๋ค์ ์ฃผ๋ฌธ ๋ฐ์
4. ๋ฉํฐ์ค๋ ๋ - ๋น๋๊ธฐ
- ๋ณธ์ , 1ํธ์ , 2ํธ์ .. ์ค ํ ๊ณณ์์ ์์์ ์ฃผ๋ฌธ -> ์กฐ๋ฆฌ ๋ฐ ์ฃผ๋ฌธ ๋ฐ๊ธฐ -> ์กฐ๋ฆฌ๊ฐ ๋น ๋ฅธ ์์๋๋ก ์ ๊ณต
์ด์ ํ๋ก์ ํธ์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ ์ฉํด๋ณด์.
์๋ ์ฝ๋๋ ์ฌ์ฉ์์ ์ด๋ ฅ์๋ก๋ถํฐ ๊ฐ์ฅ ์ ํฉํ ์ง์ ์ ๊ณ์ฐํ์ฌ ์ ๋ ฌ ํ ๋ฐํํ๋ ๋ก์ง์ด๋ค.
๋๊ฐ์ง ๋ฐฉ๋ฒ์ ์ฌ์ฉํด์ ์ฑ๋ฅ์ ๊ฐ์ ์์ผ๋ณด์๋ค.
public List<JobRecommendResponse> recommendJobWithSkill(Long memberId) throws IOException, CsvException {
List<String> skillList = skillService.findAllSkill(memberId).stream()
.map(SkillResponse::getSkillName)
.collect(Collectors.toList());
String text = String.join(",", skillList);
Resource[] resources = resourcePatternResolver.getResources("classpath:_skillspr/*.csv");
List<JobRecommendResponse> results = new ArrayList<>();
Set<String> userSkills = new HashSet<>(); // ์ถ๊ฐ
// csv ํ์ผ ์ํ
for (Resource resource : resources) {
List<String[]> records;
try (CSVReader csvReader = new CSVReader(new InputStreamReader(resource.getInputStream()))) {
records = csvReader.readAll();
}
double pr = 0;
boolean skip = true;
// ๊ฐ๋ณ csv ๋ฐ์ดํฐ ์ํ
for (String[] record : records) {
// 0๋ฒ์งธ ํ skip
if (skip) {
skip = false;
continue;
}
// skill ์ถ์ถ
String skill = record[1];
double probability = Double.parseDouble(record[5]);
// skill ์ด pdf ๋ด์ฉ์ ์๋ ๋จ์ด์ผ ๋
if (text.contains(skill) && (skill.length() > 1 || "C".equals(skill))) {
pr += Math.log(probability);
userSkills.add(skill); // ์ถ๊ฐ
}
}
if (pr == 1) pr = 0;
results.add(new JobRecommendResponse(resource.getFilename().substring(0, resource.getFilename().length() - 4), pr));
}
results.sort(Comparator.comparing(JobRecommendResponse::getProbability).reversed());
return results;
}
๊ตฌํ 1. ExecutorService ์ฌ์ฉ
์ฒ์ ์๋ํ๋ ๊ฑด java.util.concurrentํจํค์ง์ ExecutorService ๋ฅผ ์ด์ฉํ์ฌ ๋ฉํฐ์ฐ๋ ๋๋ฅผ ๊ตฌํํ์๋ค.
ExecutorService๋?
ExecutorService๋ ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํ ThreadPool๋ก Executor ์ธํฐํ์ด์ค๋ฅผ ํ์ฅํ์ฌ Thread์ ๋ผ์ดํ์ฌ์ดํด์ ์ ์ดํ๋ค.
Thread๋ฅผ ํ์ฉํ์ฌ ๋ค์์ ์์ (Task)๋ค์ ๋น๋๊ธฐ๋ก ์ํํ๋ค๋ ๊ฒ์ ๊ฐ๋จํ์ง ์๋ค. Thread์ ๋ผ์ดํ์ฌ์ดํด(์์ฑ๊ณผ ์ ๊ฑฐ ๋ฑ)์ด๋ ๋ฐ์ํ ์ ์๋ ์ฌ๋ฌ๊ฐ์ง low level์ ๊ณ ๋ ค์ฌํญ๋ค์ด ์กด์ฌํ๋๋ฐ ์ด๋ฅผ ๊ฐ๋ฐ์๊ฐ ์ ๊ฒฝ์ฐ์ง ์๋๋ก ํธ๋ฆฌํ๊ฒ ์ถ์ํํ ๊ฒ์ด ExecutorService์ด๋ค.
์๋์ฝ๋๋ ExecutorService๋ฅผ ์ฌ์ฉํด ๋ฉํฐ์ฐ๋ ๋๋ฅผ ์ ์ฉํ ํ์ ์ฝ๋์ด๋ค.
public List<JobRecommendResponse> recommendJobWithResume(InputStream pdf) throws IOException, CsvException {
// pdf -> text ์ถ์ถ
String text = pdfToText(pdf);
text = text.toUpperCase();
final String finalText = text;
Resource[] resources = resourcePatternResolver.getResources("classpath:_skillspr/*.csv");
List<JobRecommendResponse> results = new ArrayList<>();
Set<String> userSkills = new HashSet<>(); // ์ถ๊ฐ
ExecutorService executorService = Executors.newFixedThreadPool(resources.length);
List<Future<JobRecommendResponse>> futures = new ArrayList<>();
// csv ํ์ผ ์ํ
for (Resource resource : resources) {
Future<JobRecommendResponse> future = executorService.submit(() -> {
List<String[]> records;
try (CSVReader csvReader = new CSVReader(new InputStreamReader(resource.getInputStream()))) {
records = csvReader.readAll();
} catch (Exception e) {
log.info("๋ฉํฐ ์ค๋ ๋ฉ ์์
์ค ์๋ฌ");
throw new CustomException(ErrorCode.MULTI_THREADING_ERROR);
}
double pr = 0;
boolean skip = true;
// ๊ฐ๋ณ csv ๋ฐ์ดํฐ ์ํ
for (String[] record : records) {
// 0๋ฒ์งธ ํ skip
if (skip) {
skip = false;
continue;
}
// skill ์ถ์ถ
String skill = record[1];
double probability = Double.parseDouble(record[5]);
// skill ์ด pdf ๋ด์ฉ์ ์๋ ๋จ์ด์ผ ๋
if (finalText.contains(skill) && (skill.length() > 1 || "C".equals(skill))) {
pr += Math.log(probability);
userSkills.add(skill); // ์ถ๊ฐ
}
}
if (pr == 1) pr = 0;
return new JobRecommendResponse(resource.getFilename().substring(0, resource.getFilename().length() - 4), pr);
});
futures.add(future);
}
- newFixedThreadPool ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ณ ์ ํฌ๊ธฐ์ ์ค๋ ๋ ํ์ ์์ฑ
- executorService.submit() : ์ค๋ ๋ํ์ ์ ์ถ. ์๋ต๊ฐ์ผ๋ก future์ ๋ฐ์.
- ์์ ์ด ์๋ฃ๋ ๋๊น์ง ๋๊ธฐํ๊ณ ๊ฒฐ๊ณผ๋ฅผ future๋ก๋ถํฐ ๊ฐ์ ธ์ด.
for (Future<JobRecommendResponse> future : futures) {
try {
results.add(future.get());
} catch (Exception e) {
log.info("๋ฉํฐ ์ค๋ ๋ฉ ์์
์ค ์๋ฌ");
throw new CustomException(ErrorCode.MULTI_THREADING_ERROR);
}
}
๊ตฌํ 2. Async ์ฌ์ฉ
Async์ ๋ํด ์์ธํ ์์๋ณด์.
@Async์ ๊ธฐ๋ณธ ์ค์ ์ SimpleAsyncTaskExecutor๋ฅผ ์ฌ์ฉํ๋๋ก ๋์ด ์๋๋ฐ, ์ด๊ฒ์ ์ค๋ ๋ ํ์ด ์๋๊ณ ๋จ์ํ ์ค๋ ๋๋ฅผ ๋ง๋ค์ด๋ด๋ ์ญํ ์ ํ๋ค.
๊ธฐ๋ณธ ์ค์ ์ ์๋์ ๊ฐ๋ค.
- corePoolSize : 1
- maxPoolSize : Integer.MAX_VALUE
- keepAliveSeconds : 60(second)
- QueueCapacity : Integer.MAX_VALUE
- AllowCoreThreadTimeOut : false
- WaitForTasksToCompleteOnShutdown : false
- RejectedExecutionHandler : AbortPolicy
์ค๋ ๋๋ฅผ ์ฌ์ฌ์ฉํ์ง ์๊ณ ํธ์ถ๋ง๋ค ์๋ก์ด ์ค๋ ๋๋ฅผ ์์ํ๋ฉฐ, AbortPolicy ์ด๋ฏ๋ก ์ฒ๋ฆฌํ ์ ์๋ ์์ค์ด ๋๋ฉด Exception์ ๋ฐ์์ํค๋ฉฐ ์ข ๋ฃ๋๋ค.
๋ฐ๋ผ์ ๋ฐ๋ก ์ปค์คํ ํด์ฃผ์ด์ผ ํ๋ค.
@Configuration
@EnableAsync
public class AsyncConfig {
private static int CORE_POOL_SIZE = 500; // ๋์์ ์คํํ ์ฐ๋ ๋์ ๊ฐฏ์๋ฅผ ์๋ฏธ, default ๊ฐ์ 1์ด๋ค.
private static int MAX_POOL_SIZE = 3000; // ์ฐ๋ ๋ ํ์ ์ต๋ ํฌ๊ธฐ๋ฅผ ์ง์ , default ๊ฐ์ Integer.MAX_VALUE
private static int QUEUE_CAPACITY = 5000; // ํ์ ํฌ๊ธฐ๋ฅผ ์ง์ , default ๊ฐ์ Integer.MAX_VALUE ์ด๋ค.
private static String THREAD_NAME_PREFIX = "async-task";
@Bean
public Executor asyncTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
}
- CorePoolSize : ๊ธฐ๋ณธ์ ์ผ๋ก ์์ฑํด๋๊ณ ์คํ ๋๊ธฐํ๋ Thread์ ์
- MaxPoolSize : ๋์ ๋์ํ๋ ์ต๋ Thread์ ์
- QueueCapacity MaxPoolSize ์ด๊ณผ ์์ฒญ์์ Thread ์์ฑ ์์ฒญ์, ํด๋น ์์ฒญ์ Queue์ ์ ์ฅํ๋๋ฐ ์ด๋ ์ต๋ ์์ฉ ๊ฐ๋ฅํ Queue์ ํฌ๊ธฐ Queue์ ์ ์ฅ๋์ด์๋ค๊ฐ Thread์ ์๋ฆฌ๊ฐ ์๊ธฐ๋ฉด ํ๋์ฉ ๋น ์ ธ๋๊ฐ ์ค๋ ๋ ํ ๋น
- ThreadNamePrefix : ์์ฑ๋๋ Thread ์ ๋์ฌ ์ง์
- initialize() : ์์ฑํ๊ณ ์ฌ์ฉํ ์ ์๋๋กํ๋ ์ค์
๋ฐํ๊ฐ์ ๋ฐ๋ฅธ ๋์
@Async๊ฐ ๋ถ์ผ๋ฉด ๋น๋๊ธฐ๋ก ๋์ํ๋ฏ๋ก ๋ฉ์๋ ๋ฐํ ํ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- Future
- Completable
- FutureListenable
- void
@Async ์ ๋ํ ์ด์ ์ผ๋ก ์ ์ธ๋ ๋ฉ์๋๋ ๋ฆฌํด ํ์ ์ ๋ฐ๋ผ ๋ด๋ถ์ ์ผ๋ก ์์ดํ๊ฒ ๋์ํ๋ค.
void
@Slf4j
@Service
@RequiredArgsConstructor
public class CallService {
private final UserService userService;
public void call(){
userService.hello();
log.info("call");
}
}
@Slf4j
@Service
public class UserService {
@Async("HELLO_WORLD_EXECUTOR")
public void hello(){
try {
Thread.sleep(1000);
log.info("{}","hello");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
CallService์์ call ๋ฉ์๋๊ฐ UserService์ ๋น๋๊ธฐ ๋ฉ์๋ hello๋ฅผ ํธ์ถํ๋ค. ๋น๋๊ธฐ ๋ฉ์๋์ด๋ฏ๋ก call์ ํธ์ถํ ์ค๋ ๋๋ call์ ์ํ์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ (๋ ผ๋ธ๋กํน) ๋ฐ๋ก log.info๋ฅผ ํธ์ถํ๋ค.
Future
๋ฉ์๋์ ๊ฒฐ๊ณผ๊ฐ์ ์ ๋ฌ๋ฐ์์ผ ํ๋ค๋ฉด Future์ ์ฌ์ฉํด์ผ ํ๋ค. ์์ ์ค๋ช ํ ๋๋จธ์ง ํ์ ๋ชจ๋ Future ๊ณ์ด์ ํด๋นํ๊ณ Future์ด ๊ธฐ๋ณธ์ด ๋๋ค. Spring์์ ์ ๊ณตํ๋ AsyncResult๋ Future์ ๊ตฌํ์ฒด์ด๋ฉฐ ์ด๋ฅผ ์ฌ์ฉํด Future ํ์ ์ผ๋ก ๋ฆฌํดํ ์ ์๋ค.
@Slf4j
@Service
@RequiredArgsConstructor
public class CallService {
private final UserService userService;
public void futureCall() {
Future<String> future = userService.returnFuture();
try {
log.info("{}",future.get());
} catch (Exception e) {
e.printStackTrace();
}
log.info("origin void call");
}
}
@Slf4j
@Service
public class UserService {
@Async
public Future<String> returnFuture(){
try {
Thread.sleep(1000);
log.info("{}","async return future");
} catch (InterruptedException e) {
e.printStackTrace();
}
return new AsyncResult<>("async return future");
}
}
futureCall ๋ฉ์๋์์ ๋น๋๊ธฐ ๋ฉ์๋ returnFuture์ ํธ์ถํ์ฌ ๊ฒฐ๊ณผ๊ฐ์ get์ผ๋ก ๊บผ๋ธ๋ค. ๋น๋๊ธฐ ๋ฉ์๋๋ ๋น๋๊ธฐ๋ก ๋์ํ์ง๋ง future์ get ๋ฉ์๋๋ ๋ฉ์๋์ ๊ฒฐ๊ณผ๋ฅผ ์กฐํํ ๋๊น์ง ๊ณ์ ๊ธฐ๋ค๋ฆฌ๊ฒ ๋๋ค. ์ฆ, ๋ธ๋กํน ํ์์ด ๋ฐ์ํฉ๋๋ค. Future์ ๊ฒฝ์ฐ ๋น๋๊ธฐ ๋ธ๋กํน ๋ฐฉ์์ด ๋๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์ด ์ข์ง ์์ ์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
CompletableFuture
@Slf4j
@Service
@RequiredArgsConstructor
public class CallService {
private final UserService userService;
public void completableFutureCall() {
CompletableFuture<String> future = userService.returnCompletableFuture();
try {
future.thenAccept(s -> log.info("{}",s));
} catch (Exception e) {
e.printStackTrace();
}
log.info("origin completableFutureC call");
}
}
@Slf4j
@Service
public class UserService {
@Async
public CompletableFuture<String> returnCompletableFuture(){
try {
Thread.sleep(1000);
log.info("{}","async return CompletableFuture");
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture("async return CompletableFuture");
}
}
๊ธฐ์กด์๋ Future๋ ListenableFuture๋ฅผ ์ด์ฉํ์ฌ ํด๊ฒฐํ์ง๋ง JAVA 8๋ฒ์ ๋ถํฐ๋ CompletableFuture๋ฅผ ์ ๊ณตํ๋ค.
thenAccept ๋ฉ์๋๋ก ๊ฒฐ๊ณผ๊ฐ์ด ๋์ค๋ฉด ์ํํ ์ผ์ ์ ์ํด์ฃผ์๋ค. ์ฆ, thenAccept ๋ฉ์๋๋ฅผ ํตํด ๋์ค์ ๊ฒฐ๊ณผ๊ฐ์ด ๋์ค๋ฉด ์ฒ๋ฆฌํ๋๋ก ํ๊ณ ๋ฐ๋ก ๋ค๋ฅธ ์์ ์ ์ฒ๋ฆฌํ๋ฌ ๊ฐ ์ ์๋ ๋ ผ๋ธ๋กํน ์ผ๋ก ๋์ํ๋ค.
์ค์ ํ๋ก์ ํธ์์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ์๋ค.
public List<JobRecommendResponse> recommendJobWithResumeV2(InputStream pdf) throws IOException {
long startTime = System.currentTimeMillis(); // ์์ ์๊ฐ ๊ธฐ๋ก
// pdf -> text ์ถ์ถ
String text = pdfToText(pdf);
text = text.toUpperCase();
final String finalText = text;
Resource[] resources = resourcePatternResolver.getResources("classpath:_skillspr/*.csv");
List<JobRecommendResponse> results = new ArrayList<>();
Set<String> userSkills = new HashSet<>(); // ์ถ๊ฐ
List<CompletableFuture<JobRecommendResponse>> futures = new ArrayList<>();
// csv ํ์ผ ์ํ
for (Resource resource : resources) {
CompletableFuture<JobRecommendResponse> future = processCsvFileAsync(resource, finalText, userSkills);
futures.add(future);
}
// ์๋ฃ๋ ๋๊น์ง ๋๊ธฐ
for (CompletableFuture<JobRecommendResponse> future : futures) {
try {
results.add(future.join());
} catch (Exception e) {
log.info("๋ฉํฐ ์ค๋ ๋ฉ ์์
์ค ์๋ฌ");
throw new CustomException(ErrorCode.MULTI_THREADING_ERROR);
}
}
long endTime = System.currentTimeMillis(); // ์ข
๋ฃ ์๊ฐ ๊ธฐ๋ก
long duration = endTime - startTime; // ์คํ ์๊ฐ ๊ณ์ฐ
System.out.println("์คํ ์๊ฐ: " + duration + "ms");
return results;
}
@Async
public CompletableFuture<JobRecommendResponse> processCsvFileAsync(Resource resource, String finalText, Set<String> userSkills) {
CompletableFuture<JobRecommendResponse> completableFuture = CompletableFuture.supplyAsync(() -> {
List<String[]> records;
try (CSVReader csvReader = new CSVReader(new InputStreamReader(resource.getInputStream()))) {
records = csvReader.readAll();
} catch (Exception e) {
log.info("๋ฉํฐ ์ค๋ ๋ฉ ์์
์ค ์๋ฌ");
throw new CustomException(ErrorCode.MULTI_THREADING_ERROR);
}
double pr = 0;
boolean skip = true;
// ๊ฐ๋ณ csv ๋ฐ์ดํฐ ์ํ
for (String[] record : records) {
// 0๋ฒ์งธ ํ skip
if (skip) {
skip = false;
continue;
}
// skill ์ถ์ถ
String skill = record[1];
double probability = Double.parseDouble(record[5]);
// skill ์ด pdf ๋ด์ฉ์ ์๋ ๋จ์ด์ผ ๋
if (finalText.contains(skill) && (skill.length() > 1 || "C".equals(skill))) {
pr += Math.log(probability);
userSkills.add(skill); // ์ถ๊ฐ
}
}
if (pr == 1) pr = 0;
return new JobRecommendResponse(resource.getFilename().substring(0, resource.getFilename().length() - 4), pr);
});
return completableFuture;
}
๋น๋๊ธฐ ๋ฉํฐ์ฐ๋ ๋๋ก ๋ณ๊ฒฝ ํ api ์๋ต ์๋ 87%๋ฅผ ๊ฐ์ ์ํฌ ์ ์์๋ค.
'Spring Boot, JAVA ๐ฑ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Redis] Spring boot ์์ Redis๋ก ์ฑ๋ฅ ๊ฐ์ ํ๊ธฐ (0) | 2024.01.08 |
---|---|
[Spring] AOP ๊ฐ๋ ๋ฐ ์ ์ฉํ๊ธฐ (1) | 2024.01.06 |
[HttpSession] session์ ๋์ ์๋ฆฌ ์ดํด๋ณด๊ธฐ (0) | 2023.12.30 |
AWS S3 ์ด๋ฏธ์ง ๊ด๋ จ ๋ก์ง๊ณผ DB๋ก์ง ํธ๋์ญ์ ๋ถ๋ฆฌ (DB ์ปค๋ฅ์ ๋ฌธ์ ) (0) | 2023.11.21 |
ํ ์คํธ ์ฝ๋์ ๋ํ ์ ๋ฆฌ ๋ฐ ๊ณ ๋ฏผ ๊ธฐ๋ก (1) | 2023.10.29 |