From 5fbc66bc3dc9cd720d01fce32f69693188de10b0 Mon Sep 17 00:00:00 2001 From: juno-junho Date: Tue, 14 May 2024 17:15:27 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=8B=A4=ED=96=89=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=B8=A1=EC=A0=95=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?StopWatch=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/spaceclub/global/timer/StopWatch.java | 14 ++++++++ .../global/timer/StopWatchExecutor.java | 34 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/com/spaceclub/global/timer/StopWatch.java create mode 100644 src/main/java/com/spaceclub/global/timer/StopWatchExecutor.java diff --git a/src/main/java/com/spaceclub/global/timer/StopWatch.java b/src/main/java/com/spaceclub/global/timer/StopWatch.java new file mode 100644 index 00000000..7cb5b5f7 --- /dev/null +++ b/src/main/java/com/spaceclub/global/timer/StopWatch.java @@ -0,0 +1,14 @@ +package com.spaceclub.global.timer; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +public @interface StopWatch { + +} diff --git a/src/main/java/com/spaceclub/global/timer/StopWatchExecutor.java b/src/main/java/com/spaceclub/global/timer/StopWatchExecutor.java new file mode 100644 index 00000000..3b993719 --- /dev/null +++ b/src/main/java/com/spaceclub/global/timer/StopWatchExecutor.java @@ -0,0 +1,34 @@ +package com.spaceclub.global.timer; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import org.springframework.util.StopWatch; + +@Slf4j +@Aspect +@Component +@Profile("local") +public class StopWatchExecutor { + + @Pointcut("@annotation(com.spaceclub.global.timer.StopWatch)") + private void stopWatch() { + } + + @Around("stopWatch()") + public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { + StopWatch stopWatch = new StopWatch(); + try { + stopWatch.start(joinPoint.toShortString() + "Timer started"); + return joinPoint.proceed(); + } finally { + stopWatch.stop(); + log.info(stopWatch.prettyPrint()); + } + } + +} From 917ce04a5d1893cae602cd4d0fb6fb5c226acad8 Mon Sep 17 00:00:00 2001 From: juno-junho Date: Wed, 15 May 2024 00:55:47 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20inverted=20radix=20tree=20?= =?UTF-8?q?=ED=95=99=EC=8A=B5=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../profanity/ProfanityCheckValidator.java | 2 - .../annotation/profanity/ProfanityLoader.java | 12 +++-- .../ConcurrentInvertedRadixTreeTest.java | 50 +++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 src/test/java/com/spaceclub/global/profanity/study/ConcurrentInvertedRadixTreeTest.java diff --git a/src/main/java/com/spaceclub/global/annotation/profanity/ProfanityCheckValidator.java b/src/main/java/com/spaceclub/global/annotation/profanity/ProfanityCheckValidator.java index 73879332..f3d2485a 100644 --- a/src/main/java/com/spaceclub/global/annotation/profanity/ProfanityCheckValidator.java +++ b/src/main/java/com/spaceclub/global/annotation/profanity/ProfanityCheckValidator.java @@ -3,12 +3,10 @@ import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import static com.spaceclub.global.annotation.profanity.BadWordExceptionMessage.BAD_WORD_DETECTED; -@Slf4j @Component @RequiredArgsConstructor public class ProfanityCheckValidator implements ConstraintValidator { diff --git a/src/main/java/com/spaceclub/global/annotation/profanity/ProfanityLoader.java b/src/main/java/com/spaceclub/global/annotation/profanity/ProfanityLoader.java index 31de60dd..0fdf3dde 100644 --- a/src/main/java/com/spaceclub/global/annotation/profanity/ProfanityLoader.java +++ b/src/main/java/com/spaceclub/global/annotation/profanity/ProfanityLoader.java @@ -1,6 +1,7 @@ package com.spaceclub.global.annotation.profanity; -import com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharSequenceNodeFactory; +import com.googlecode.concurrenttrees.radix.node.concrete.SmartArrayBasedNodeFactory; +import com.googlecode.concurrenttrees.radix.node.concrete.voidvalue.VoidValue; import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree; import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree; import com.spaceclub.global.config.ProfanityConfig; @@ -24,13 +25,13 @@ public class ProfanityLoader { private final ProfanityConfig config; - private final InvertedRadixTree TRIE = new ConcurrentInvertedRadixTree<>(new DefaultCharSequenceNodeFactory()); + private final InvertedRadixTree TRIE = new ConcurrentInvertedRadixTree<>(new SmartArrayBasedNodeFactory()); @PostConstruct private void loadProfanityFromFile() { try { - List badWords = Files.readAllLines(Paths.get(config.filePath())); - badWords.forEach(badWord -> TRIE.put(badWord, badWord)); + List banWords = Files.readAllLines(Paths.get(config.filePath())); + banWords.forEach(banWord -> TRIE.put(banWord, VoidValue.SINGLETON)); // 메모리 효율을 위해 불필요한 value 설정 x } catch (IOException e) { log.error("비속어 목록 파일 읽기 실패", e); throw new IllegalStateException(FAIL_BAD_WORD_SETUP.toString()); @@ -38,9 +39,10 @@ private void loadProfanityFromFile() { } public List profanityContained(String text) { - Spliterator profanityFound = TRIE.getValuesForKeysContainedIn(text).spliterator(); + Spliterator profanityFound = TRIE.getKeysContainedIn(text).spliterator(); return StreamSupport.stream(profanityFound, false) + .map(CharSequence::toString) .peek(word -> log.info("비속어 감지: {}", word)) .toList(); } diff --git a/src/test/java/com/spaceclub/global/profanity/study/ConcurrentInvertedRadixTreeTest.java b/src/test/java/com/spaceclub/global/profanity/study/ConcurrentInvertedRadixTreeTest.java new file mode 100644 index 00000000..9aec124a --- /dev/null +++ b/src/test/java/com/spaceclub/global/profanity/study/ConcurrentInvertedRadixTreeTest.java @@ -0,0 +1,50 @@ +package com.spaceclub.global.profanity.study; + +import com.googlecode.concurrenttrees.common.Iterables; +import com.googlecode.concurrenttrees.common.PrettyPrinter; +import com.googlecode.concurrenttrees.radix.node.concrete.SmartArrayBasedNodeFactory; +import com.googlecode.concurrenttrees.radix.node.concrete.voidvalue.VoidValue; +import com.googlecode.concurrenttrees.radix.node.util.PrettyPrintable; +import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree; +import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Spliterator; +import java.util.stream.StreamSupport; + +public class ConcurrentInvertedRadixTreeTest { + + private final InvertedRadixTree TRIE = new ConcurrentInvertedRadixTree<>(new SmartArrayBasedNodeFactory()); + + @Test + @Disabled + void concurrent_Inverted_Radix_tree_사용법_학습테스트() { + TRIE.put("TEST", VoidValue.SINGLETON); // value는 필요 없으면 VoidValue를 사용하면 메모리를 효율적으로 사용 가능 (Radix Tree류 경우) + TRIE.put("TOAST", VoidValue.SINGLETON); + TRIE.put("TEAM", VoidValue.SINGLETON); + + System.out.println("Tree structure:"); + PrettyPrinter.prettyPrint((PrettyPrintable) TRIE, System.out); // PrettyPrintable는 테스트를 위한 객체 + + System.out.println(); + System.out.println("Value for 'TEST' (exact match): " + TRIE.getValueForExactKey("TEST")); + System.out.println("Value for 'TOAST' (exact match): " + TRIE.getValueForExactKey("TOAST")); + System.out.println(); + System.out.println("Keys contained in 'MY TEAM LIKES TOAST': " + Iterables.toString(TRIE.getKeysContainedIn("MY TEAM LIKES TOAST"))); + System.out.println("Keys contained in 'MY TEAM LIKES TOASTERS': " + Iterables.toString(TRIE.getKeysContainedIn("MY TEAM LIKES TOASTERS"))); + System.out.println("Values for keys contained in 'MY TEAM LIKES TOAST': " + Iterables.toString(TRIE.getValuesForKeysContainedIn("MY TEAM LIKES TOAST"))); + System.out.println("Key-value pairs for keys contained in 'MY TEAM LIKES TOAST': " + Iterables.toString(TRIE.getKeyValuePairsForKeysContainedIn("MY TEAM LIKES TOAST"))); + + // 예시 + String text = "MY TEAM LIKES TOASTERS"; + Spliterator profanityFound = TRIE.getKeysContainedIn(text).spliterator(); + List profanityList = StreamSupport.stream(profanityFound, false) + .map(CharSequence::toString) + .toList(); + + System.out.println("비속어 감지: " + profanityList); + } + +}