Skip to content

Feat/#200 핸들 필터링 추가#204

Open
jes0131 wants to merge 4 commits into
mainfrom
feat/#200
Open

Feat/#200 핸들 필터링 추가#204
jes0131 wants to merge 4 commits into
mainfrom
feat/#200

Conversation

@jes0131

@jes0131 jes0131 commented May 26, 2026

Copy link
Copy Markdown
Contributor

Type of PR

feat

Changes

  • 회원가입 / 핸들 변경 시 필터링 추가

Additional

close #200

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 핸들(사용자명) 등록 및 변경 시 금칙어 검증이 추가되었습니다.
    • 핸들 검증 요청 시 사용 가능 여부를 확인하기 전에 금칙어 포함 여부를 우선 검사합니다.
    • 금칙어가 포함된 핸들 사용 시 명확한 오류 메시지를 제공합니다.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Spring 애플리케이션에 회원가입 및 핸들 관리 시 금칙어 필터링을 추가합니다. 새로운 BannedWordFilter 컴포넌트가 962개 항목의 금칙어 데이터셋을 로드하여 검증하고, AuthServiceUserService에서 핸들 입력을 검사하며, 금칙어 감지 시 새로운 HANDLE_CONTAINS_BANNED_WORD 에러코드로 응답합니다.

Changes

금칙어 필터링 기능

Layer / File(s) Summary
BannedWordFilter 구현 및 데이터
src/main/java/com/gbsw/snapy/global/filter/BannedWordFilter.java, src/main/resources/filter/banned-words.txt
BannedWordFilter 컴포넌트는 @PostConstruct에서 banned-words.txt를 UTF-8로 읽고 주석 및 빈 줄을 제외한 뒤 소문자로 정규화하여 불변 Set으로 저장합니다. containsBannedWord 메서드는 입력 문자열을 정규화 후 금칙어 부분 문자열 포함 여부를 검사합니다. 금칙어 데이터셋은 962개 항목을 포함합니다.
HANDLE_CONTAINS_BANNED_WORD 에러 코드
src/main/java/com/gbsw/snapy/global/exception/ErrorCode.java
ErrorCode 열거형에 새로운 상수를 추가하여 HttpStatus.BAD_REQUEST와 "사용할 수 없는 단어가 포함되어 있습니다." 메시지를 매핑합니다.
AuthService 및 UserService 금칙어 검증
src/main/java/com/gbsw/snapy/domain/auth/service/AuthService.java, src/main/java/com/gbsw/snapy/domain/users/service/UserService.java
BannedWordFilter를 의존성 주입으로 추가하고, AuthService.register(), UserService.checkHandle(), UserService.updateHandle() 메서드에서 핸들 입력을 금칙어 검사합니다. 금칙어 포함 시 중복 검사 이전에 HANDLE_CONTAINS_BANNED_WORD 예외를 발생시킵니다.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • 2026-snapy/Server#144: UserService의 checkHandle 및 updateHandle 메서드에 금칙어 검증이 추가되어, 기존 핸들 관리 기능 위에 레이어가 적용됩니다.

Poem

🐰 금칙어 필터링으로 깨끗한 닉네임,
962개의 단어가 지켜주네요!
회원가입부터 핸들 변경까지,
악의의 문자는 더 이상 못 들어와요,
안전하고 밝은 커뮤니티를 위해! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 'Feat/#200 핸들 필터링 추가'는 변경사항의 핵심인 핸들 필터링 기능 추가를 명확하게 반영하고 있습니다.
Linked Issues check ✅ Passed PR은 이슈 #200의 회원가입 시 유저 핸들 필터링 추가 요구사항을 완전히 충족합니다. 금칙어 필터링이 AuthService와 UserService에 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 핸들 필터링 기능 구현과 관련되어 있으며, 이슈 #200의 범위 내에 있습니다. 범위 밖의 변경사항은 없습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#200

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/main/java/com/gbsw/snapy/domain/auth/service/AuthService.java (1)

40-62: ⚡ Quick win

username에 대한 금칙어 검증 누락

현재 handle만 금칙어 검증을 수행하지만, username(사용자 이름)도 프로필 등에서 다른 사용자에게 노출되는 정보입니다. 욕설이나 부적절한 단어가 포함된 사용자 이름을 허용하면 서비스 품질 저하로 이어질 수 있습니다.

username에 대해서도 동일한 금칙어 필터링을 적용하는 것을 권장합니다.

📝 username 검증 추가
 `@Transactional`
 public RegisterResponse register(RegisterRequest dto) {
     if (bannedWordFilter.containsBannedWord(dto.getHandle())) {
         throw new CustomException(ErrorCode.HANDLE_CONTAINS_BANNED_WORD);
     }
+    if (bannedWordFilter.containsBannedWord(dto.getUsername())) {
+        throw new CustomException(ErrorCode.USERNAME_CONTAINS_BANNED_WORD);
+    }
     if (userRepository.existsByHandle(dto.getHandle())) {
         throw new CustomException(ErrorCode.DUPLICATE_HANDLE);
     }

참고: ErrorCodeUSERNAME_CONTAINS_BANNED_WORD 상수도 추가 필요

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/gbsw/snapy/domain/auth/service/AuthService.java` around
lines 40 - 62, In register(RegisterRequest dto) add the same banned-word check
used for handle to dto.getUsername(): call
bannedWordFilter.containsBannedWord(dto.getUsername()) and throw new
CustomException(ErrorCode.USERNAME_CONTAINS_BANNED_WORD) if true; update
ErrorCode to include USERNAME_CONTAINS_BANNED_WORD so the exception compiles and
ensure the check is placed before creating the User (similar to the
handle/email/duplicate checks).
src/main/java/com/gbsw/snapy/domain/users/service/UserService.java (1)

172-177: ⚡ Quick win

updateUsername()에 금칙어 검증 누락

updateHandle()에서는 금칙어를 검증하지만, updateUsername()에서는 검증하지 않습니다. 사용자 이름도 프로필에 노출되므로 일관된 검증이 필요합니다.

📝 updateUsername에 금칙어 검증 추가
 `@Transactional`
 public void updateUsername(Long userId, UpdateUsernameRequest request) {
+    if (bannedWordFilter.containsBannedWord(request.getUsername())) {
+        throw new CustomException(ErrorCode.USERNAME_CONTAINS_BANNED_WORD);
+    }
     User user = userRepository.findById(userId)
             .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
 
     user.setUsername(request.getUsername());
 }

참고: ErrorCodeUSERNAME_CONTAINS_BANNED_WORD 상수도 추가 필요

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/gbsw/snapy/domain/users/service/UserService.java` around
lines 172 - 177, The updateUsername method is missing banned-word validation;
update it to perform the same banned-word check that updateHandle uses before
calling user.setUsername(...), and if validation fails throw new
CustomException(ErrorCode.USERNAME_CONTAINS_BANNED_WORD). Reuse the existing
validation utility or method used by updateHandle (e.g., BannedWordValidator or
the service method invoked in updateHandle) so logic is consistent and add the
new ErrorCode.USERNAME_CONTAINS_BANNED_WORD to ErrorCode enum.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/gbsw/snapy/global/filter/BannedWordFilter.java`:
- Around line 24-35: The init() method currently declares throws IOException so
a missing or unreadable BANNED_WORDS_PATH file can abort startup; change init()
to handle IO exceptions internally by removing the throws declaration, wrap the
ClassPathResource/BufferedReader logic in a try-catch(IOException |
RuntimeException), and on failure log a clear, descriptive error including the
exception (use log.error("... {}", e) or similar) and assign bannedWords to an
empty unmodifiable set (e.g., Collections.emptySet()) as a safe fallback so the
application can start; keep the log.info("BannedWordFilter loaded {} words",
bannedWords.size()) call after the try/catch.
- Around line 41-42: The current BannedWordFilter uses normalized.contains(...)
which causes false positives by matching substrings (e.g., "classic" -> "ass");
update the matching in the method that builds/checks bannedWords to use
word-boundary, case-insensitive regexes instead of simple substring checks: stop
removing whitespace with input.toLowerCase().replaceAll("\\s+",""), escape each
banned word (from the bannedWords collection) for regex, compile patterns like
(?i)\b<escaped_word>\b (or use Unicode-aware boundaries) and test the input
against those Patterns (in the same class/method where normalized and
bannedWords are used) so only whole-word matches trigger blocking. Ensure the
logic caches compiled Patterns to avoid recompiling per-check.

In `@src/main/resources/filter/banned-words.txt`:
- Around line 1-962: The banned-words.txt currently only contains English
profanity which mismatches your Korean-first app (ErrorCode messages like "사용할 수
없는 단어가 포함되어 있습니다."); add a Korean profanity list and reorganize the blocklist
into language-specific files (e.g., banned-words.ko.txt and
banned-words.en.txt), update any loader to read per-language files (look for
where banned-words.txt is loaded/validated and ErrorCode is emitted) and change
matching rules to (1) prefer full-word/exact matches for short/common English
tokens (e.g., "sex", "ass", "ball") or remove them from the English default
list, and (2) ensure Korean entries include common conjugations/spacing
variants; keep English list as secondary and maintain normalization (lowercase,
strip punctuation) in the validation path that triggers ErrorCode.

---

Nitpick comments:
In `@src/main/java/com/gbsw/snapy/domain/auth/service/AuthService.java`:
- Around line 40-62: In register(RegisterRequest dto) add the same banned-word
check used for handle to dto.getUsername(): call
bannedWordFilter.containsBannedWord(dto.getUsername()) and throw new
CustomException(ErrorCode.USERNAME_CONTAINS_BANNED_WORD) if true; update
ErrorCode to include USERNAME_CONTAINS_BANNED_WORD so the exception compiles and
ensure the check is placed before creating the User (similar to the
handle/email/duplicate checks).

In `@src/main/java/com/gbsw/snapy/domain/users/service/UserService.java`:
- Around line 172-177: The updateUsername method is missing banned-word
validation; update it to perform the same banned-word check that updateHandle
uses before calling user.setUsername(...), and if validation fails throw new
CustomException(ErrorCode.USERNAME_CONTAINS_BANNED_WORD). Reuse the existing
validation utility or method used by updateHandle (e.g., BannedWordValidator or
the service method invoked in updateHandle) so logic is consistent and add the
new ErrorCode.USERNAME_CONTAINS_BANNED_WORD to ErrorCode enum.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 63500581-bc11-4a12-af32-c3fc52097389

📥 Commits

Reviewing files that changed from the base of the PR and between 50a7642 and aa36d20.

📒 Files selected for processing (5)
  • src/main/java/com/gbsw/snapy/domain/auth/service/AuthService.java
  • src/main/java/com/gbsw/snapy/domain/users/service/UserService.java
  • src/main/java/com/gbsw/snapy/global/exception/ErrorCode.java
  • src/main/java/com/gbsw/snapy/global/filter/BannedWordFilter.java
  • src/main/resources/filter/banned-words.txt

Comment on lines +24 to +35
public void init() throws IOException {
ClassPathResource resource = new ClassPathResource(BANNED_WORDS_PATH);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
bannedWords = reader.lines()
.map(String::trim)
.filter(line -> !line.isEmpty() && !line.startsWith("#"))
.map(String::toLowerCase)
.collect(Collectors.toUnmodifiableSet());
}
log.info("BannedWordFilter loaded {} words", bannedWords.size());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

IOException 전파로 인한 애플리케이션 시작 실패 가능성

@PostConstruct 메서드가 IOException을 throws하므로, banned-words.txt 파일이 누락되거나 읽을 수 없는 경우 애플리케이션 시작이 실패합니다. 개발 환경과 프로덕션 환경 간 배포 시 파일 누락 위험이 있습니다.

더 명확한 에러 메시지와 함께 초기화 실패를 처리하는 것을 권장합니다.

💡 개선된 에러 처리
 `@PostConstruct`
-public void init() throws IOException {
+public void init() {
+    try {
         ClassPathResource resource = new ClassPathResource(BANNED_WORDS_PATH);
+        if (!resource.exists()) {
+            log.error("Banned words file not found: {}", BANNED_WORDS_PATH);
+            throw new IllegalStateException("Required resource not found: " + BANNED_WORDS_PATH);
+        }
         try (BufferedReader reader = new BufferedReader(
                 new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
             bannedWords = reader.lines()
                     .map(String::trim)
                     .filter(line -> !line.isEmpty() && !line.startsWith("#"))
                     .map(String::toLowerCase)
                     .collect(Collectors.toUnmodifiableSet());
         }
         log.info("BannedWordFilter loaded {} words", bannedWords.size());
+    } catch (IOException e) {
+        log.error("Failed to load banned words from: {}", BANNED_WORDS_PATH, e);
+        throw new IllegalStateException("Failed to initialize BannedWordFilter", e);
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/gbsw/snapy/global/filter/BannedWordFilter.java` around
lines 24 - 35, The init() method currently declares throws IOException so a
missing or unreadable BANNED_WORDS_PATH file can abort startup; change init() to
handle IO exceptions internally by removing the throws declaration, wrap the
ClassPathResource/BufferedReader logic in a try-catch(IOException |
RuntimeException), and on failure log a clear, descriptive error including the
exception (use log.error("... {}", e) or similar) and assign bannedWords to an
empty unmodifiable set (e.g., Collections.emptySet()) as a safe fallback so the
application can start; keep the log.info("BannedWordFilter loaded {} words",
bannedWords.size()) call after the try/catch.

Comment on lines +41 to +42
String normalized = input.toLowerCase().replaceAll("\\s+", "");
return bannedWords.stream().anyMatch(normalized::contains);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

치명적 오류: 단순 부분 문자열 매칭으로 인한 대규모 오탐 발생

String.contains()는 단어 경계를 고려하지 않는 부분 문자열 매칭을 수행하므로, 정상적인 단어도 차단됩니다:

  • "classic" → "ass" 포함으로 차단
  • "assessment" → "ass" 포함으로 차단
  • "expression" → "sex" 포함으로 차단
  • "basketball" → "ball" 포함으로 차단

특히 금칙어 목록에 "sex", "ass", "cum" 등 짧은 단어가 포함되어 있어 영향 범위가 매우 큽니다.

🔧 단어 경계를 고려한 정규식 기반 매칭 방식으로 개선
+import java.util.regex.Pattern;
+
 `@Slf4j`
 `@Component`
 public class BannedWordFilter {
 
     private static final String BANNED_WORDS_PATH = "filter/banned-words.txt";
 
-    private Set<String> bannedWords;
+    private Set<Pattern> bannedWordPatterns;
 
     `@PostConstruct`
     public void init() throws IOException {
         ClassPathResource resource = new ClassPathResource(BANNED_WORDS_PATH);
         try (BufferedReader reader = new BufferedReader(
                 new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
-            bannedWords = reader.lines()
+            bannedWordPatterns = reader.lines()
                     .map(String::trim)
                     .filter(line -> !line.isEmpty() && !line.startsWith("#"))
                     .map(String::toLowerCase)
+                    .map(word -> Pattern.compile("\\b" + Pattern.quote(word) + "\\b", Pattern.CASE_INSENSITIVE))
-                    .collect(Collectors.toUnmodifiableSet());
+                    .collect(Collectors.toSet());
         }
-        log.info("BannedWordFilter loaded {} words", bannedWords.size());
+        log.info("BannedWordFilter loaded {} patterns", bannedWordPatterns.size());
     }
 
     public boolean containsBannedWord(String input) {
         if (input == null || input.isBlank()) {
             return false;
         }
-        String normalized = input.toLowerCase().replaceAll("\\s+", "");
-        return bannedWords.stream().anyMatch(normalized::contains);
+        // 공백 회피를 방지하기 위해 공백 제거 버전도 검사
+        String withSpaces = input.toLowerCase();
+        String withoutSpaces = withSpaces.replaceAll("\\s+", "");
+        
+        return bannedWordPatterns.stream()
+                .anyMatch(pattern -> pattern.matcher(withSpaces).find() 
+                                  || pattern.matcher(withoutSpaces).find());
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/gbsw/snapy/global/filter/BannedWordFilter.java` around
lines 41 - 42, The current BannedWordFilter uses normalized.contains(...) which
causes false positives by matching substrings (e.g., "classic" -> "ass"); update
the matching in the method that builds/checks bannedWords to use word-boundary,
case-insensitive regexes instead of simple substring checks: stop removing
whitespace with input.toLowerCase().replaceAll("\\s+",""), escape each banned
word (from the bannedWords collection) for regex, compile patterns like
(?i)\b<escaped_word>\b (or use Unicode-aware boundaries) and test the input
against those Patterns (in the same class/method where normalized and
bannedWords are used) so only whole-word matches trigger blocking. Ensure the
logic caches compiled Patterns to avoid recompiling per-check.

Comment on lines +1 to +962
2 girls 1 cup
2g1c
4r5e
5h1t
5hit
a55
a_s_s
acrotomophilia
alabama hot pocket
alaskan pipeline
anal
anilingus
anus
apeshit
ar5e
arrse
arse
arsehole
ass
ass-fucker
ass-hat
ass-pirate
assbag
assbandit
assbanger
assbite
assclown
asscock
asscracker
asses
assface
assfucker
assfukka
assgoblin
asshat
asshead
asshole
assholes
asshopper
assjacker
asslick
asslicker
assmonkey
assmunch
assmuncher
asspirate
assshole
asssucker
asswad
asswhole
asswipe
auto erotic
autoerotic
b!tch
b00bs
b17ch
b1tch
babeland
baby batter
baby juice
ball gag
ball gravy
ball kicking
ball licking
ball sack
ball sucking
ballbag
balls
ballsack
bampot
bangbros
bareback
barely legal
barenaked
bastard
bastardo
bastinado
bbw
bdsm
beaner
beaners
beastial
beastiality
beastility
beaver cleaver
beaver lips
bellend
bestial
bestiality
bi+ch
biatch
big black
big breasts
big knockers
big tits
bimbos
birdlock
bitch
bitcher
bitchers
bitches
bitchin
bitching
black cock
blonde action
blonde on blonde action
bloody
blow job
blow your load
blowjob
blowjobs
blue waffle
blumpkin
boiolas
bollock
bollocks
bollok
bollox
bondage
boner
boob
boobie
boobs
booobs
boooobs
booooobs
booooooobs
booty call
breasts
brown showers
brunette action
buceta
bugger
bukkake
bulldyke
bullet vibe
bullshit
bum
bung hole
bunghole
bunny fucker
busty
butt
butt-pirate
buttcheeks
butthole
buttmunch
buttplug
c0ck
c0cksucker
camel toe
camgirl
camslut
camwhore
carpet muncher
carpetmuncher
cawk
chinc
chink
choad
chocolate rosebuds
chode
cipa
circlejerk
cl1t
cleveland steamer
clit
clitface
clitoris
clits
clover clamps
clusterfuck
cnut
cock
cock-sucker
cockbite
cockburger
cockface
cockhead
cockjockey
cockknoker
cockmaster
cockmongler
cockmongruel
cockmonkey
cockmunch
cockmuncher
cocknose
cocknugget
cocks
cockshit
cocksmith
cocksmoker
cocksuck
cocksuck
cocksucked
cocksucked
cocksucker
cocksucking
cocksucks
cocksuka
cocksukka
cok
cokmuncher
coksucka
coochie
coochy
coon
coons
cooter
coprolagnia
coprophilia
cornhole
cox
crap
creampie
cum
cumbubble
cumdumpster
cumguzzler
cumjockey
cummer
cumming
cums
cumshot
cumslut
cumtart
cunilingus
cunillingus
cunnie
cunnilingus
cunt
cuntface
cunthole
cuntlick
cuntlick
cuntlicker
cuntlicker
cuntlicking
cuntlicking
cuntrag
cunts
cyalis
cyberfuc
cyberfuck
cyberfucked
cyberfucker
cyberfuckers
cyberfucking
d1ck
dammit
damn
darkie
date rape
daterape
deep throat
deepthroat
dendrophilia
dick
dickbag
dickbeater
dickface
dickhead
dickhole
dickjuice
dickmilk
dickmonger
dickslap
dicksucker
dickwad
dickweasel
dickweed
dickwod
dike
dildo
dildos
dingleberries
dingleberry
dink
dinks
dipshit
dirsa
dirty pillows
dirty sanchez
dlck
dog style
dog-fucker
doggie style
doggiestyle
doggin
dogging
doggy style
doggystyle
dolcett
domination
dominatrix
dommes
donkey punch
donkeyribber
doochbag
dookie
doosh
double dong
double penetration
douche
douchebag
dp action
dry hump
duche
dumb
dumbshit
dumshit
dvda
dyke
eat my ass
ecchi
ejaculate
ejaculated
ejaculates
ejaculating
ejaculatings
ejaculation
ejakulate
erotic
erotism
escort
eunuch
f u c k
f u c k e r
f4nny
f_u_c_k
fag
fagbag
fagg
fagging
faggit
faggitt
faggot
faggs
fagot
fagots
fags
fagtard
fanny
fannyflaps
fannyfucker
fanyy
fart
farted
farting
farty
fatass
fcuk
fcuker
fcuking
fecal
feck
fecker
felatio
felch
felching
fellate
fellatio
feltch
female squirting
femdom
figging
fingerbang
fingerfuck
fingerfucked
fingerfucker
fingerfuckers
fingerfucking
fingerfucks
fingering
fistfuck
fistfucked
fistfucker
fistfuckers
fistfucking
fistfuckings
fistfucks
fisting
flamer
flange
fook
fooker
fool
foot fetish
footjob
frotting
fuck
fuck buttons
fucka
fucked
fucker
fuckers
fuckhead
fuckheads
fuckin
fucking
fuckings
fuckingshitmotherfucker
fuckme
fucks
fucktards
fuckwhit
fuckwit
fudge packer
fudgepacker
fuk
fuker
fukker
fukkin
fuks
fukwhit
fukwit
futanari
fux
fux0r
g-spot
gang bang
gangbang
gangbanged
gangbanged
gangbangs
gay sex
gayass
gaybob
gaydo
gaylord
gaysex
gaytard
gaywad
genitals
giant cock
girl on
girl on top
girls gone wild
goatcx
goatse
god damn
god-dam
god-damned
goddamn
goddamned
gokkun
golden shower
goo girl
gooch
goodpoop
gook
goregasm
gringo
grope
group sex
guido
guro
hand job
handjob
hard core
hardcore
hardcoresex
heeb
hell
hentai
heshe
ho
hoar
hoare
hoe
hoer
homo
homoerotic
honkey
honky
hooker
hore
horniest
horny
hot carl
hot chick
hotsex
how to kill
how to murder
huge fat
humping
incest
intercourse
jack off
jack-off
jackass
jackoff
jail bait
jailbait
jap
jelly donut
jerk
jerk off
jerk-off
jigaboo
jiggaboo
jiggerboo
jism
jiz
jiz
jizm
jizm
jizz
juggs
kawk
kike
kinbaku
kinkster
kinky
kiunt
knob
knobbing
knobead
knobed
knobend
knobhead
knobjocky
knobjokey
kock
kondum
kondums
kooch
kootch
kum
kumer
kummer
kumming
kums
kunilingus
kunt
kyke
l3i+ch
l3itch
labia
leather restraint
leather straight jacket
lemon party
lesbo
lezzie
lmfao
lolita
lovemaking
lust
lusting
m0f0
m0fo
m45terbate
ma5terb8
ma5terbate
make me come
male squirting
masochist
master-bate
masterb8
masterbat*
masterbat3
masterbate
masterbation
masterbations
masturbate
menage a trois
milf
minge
missionary position
mo-fo
mof0
mofo
mothafuck
mothafucka
mothafuckas
mothafuckaz
mothafucked
mothafucker
mothafuckers
mothafuckin
mothafucking
mothafuckings
mothafucks
mother fucker
motherfuck
motherfucked
motherfucker
motherfuckers
motherfuckin
motherfucking
motherfuckings
motherfuckka
motherfucks
mound of venus
mr hands
muff
muff diver
muffdiver
muffdiving
mutha
muthafecker
muthafuckker
muther
mutherfucker
n1gga
n1gger
nambla
nawashi
nazi
negro
neonazi
nig nog
nigg3r
nigg4h
nigga
niggah
niggas
niggaz
nigger
niggers
niglet
nimphomania
nipple
nipples
nob
nob jokey
nobhead
nobjocky
nobjokey
nsfw images
nude
nudity
numbnuts
nutsack
nympho
nymphomania
octopussy
omorashi
one cup two girls
one guy one jar
orgasim
orgasim
orgasims
orgasm
orgasms
orgy
p0rn
paedophile
paki
panooch
panties
panty
pawn
pecker
peckerhead
pedobear
pedophile
pegging
penis
penisfucker
phone sex
phonesex
phuck
phuk
phuked
phuking
phukked
phukking
phuks
phuq
piece of shit
pigfucker
pimpis
pis
pises
pisin
pising
pisof
piss
piss pig
pissed
pisser
pissers
pisses
pissflap
pissflaps
pissin
pissin
pissing
pissoff
pissoff
pisspig
playboy
pleasure chest
pole smoker
polesmoker
pollock
ponyplay
poo
poof
poon
poonani
poonany
poontang
poop
poop chute
poopchute
porn
porno
pornography
pornos
prick
pricks
prince albert piercing
pron
pthc
pube
pubes
punanny
punany
punta
pusies
pusse
pussi
pussies
pussy
pussylicking
pussys
pusy
puto
queaf
queef
queerbait
queerhole
quim
raghead
raging boner
rape
raping
rapist
rectum
renob
retard
reverse cowgirl
rimjaw
rimjob
rimming
rosy palm
rosy palm and her 5 sisters
ruski
rusty trombone
s hit
s&m
s.o.b.
s_h_i_t
sadism
sadist
santorum
scat
schlong
scissoring
screwing
scroat
scrote
scrotum
semen
sex
sexo
sexy
sh!+
sh!t
sh1t
shag
shagger
shaggin
shagging
shaved beaver
shaved pussy
shemale
shi+
shibari
shit
shit-ass
shit-bag
shit-bagger
shit-brain
shit-breath
shit-cunt
shit-dick
shit-eating
shit-face
shit-faced
shit-fit
shit-head
shit-heel
shit-hole
shit-house
shit-load
shit-pot
shit-spitter
shit-stain
shitass
shitbag
shitbagger
shitblimp
shitbrain
shitbreath
shitcunt
shitdick
shite
shiteating
shited
shitey
shitface
shitfaced
shitfit
shitfuck
shitfull
shithead
shitheel
shithole
shithouse
shiting
shitings
shitload
shitpot
shits
shitspitter
shitstain
shitted
shitter
shitters
shittiest
shitting
shittings
shitty
shitty
shity
shiz
shiznit
shota
shrimping
skank
skeet
slanteye
slut
slutbag
sluts
smeg
smegma
smut
snatch
snowballing
sodomize
sodomy
son-of-a-bitch
spac
spic
spick
splooge
splooge moose
spooge
spread legs
spunk
strap on
strapon
strappado
strip club
style doggy
suck
sucker
sucks
suicide girls
sultry women
swastika
swinger
t1tt1e5
t1tties
tainted love
tard
taste my
tea bagging
teets
teez
testical
testicle
threesome
throating
thundercunt
tied up
tight white
tit
titfuck
tits
titt
tittie5
tittiefucker
titties
titty
tittyfuck
tittywank
titwank
tongue in a
topless
tosser
towelhead
tranny
tribadism
tub girl
tubgirl
turd
tushy
tw4t
twat
twathead
twatlips
twatty
twink
twinkie
two girls one cup
twunt
twunter
undressing
upskirt
urethra play
urophilia
v14gra
v1gra
va-j-j
vag
vagina
venus mound
viagra
vibrator
violet wand
vjayjay
vorarephilia
voyeur
vulva
w00se
wang
wank
wanker
wanky
wet dream
wetback
white power
whoar
whore
willies
willy
wrapping men
wrinkled starfish
xrated
xx
xxx
yaoi
yellow showers
yiffy
zoophilia
🖕

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

주요 문제: 한국어 애플리케이션에 영어 금칙어만 포함

애플리케이션의 모든 에러 메시지가 한국어로 작성되어 있고(ErrorCode의 "사용할 수 없는 단어가 포함되어 있습니다." 등), 주요 사용자층이 한국어 사용자로 보이는 상황에서 금칙어 목록이 영어만 포함하고 있습니다.

한국어 욕설 및 비속어가 누락되어 실제로 차단해야 할 핸들을 걸러내지 못할 가능성이 높습니다. 반면 영어 단어 중 일부는 한국어 사용자의 정상적인 핸들 선택(예: 영어 단어 조합)을 방해할 수 있습니다.

권장 사항:

  1. 한국어 금칙어 데이터셋 추가 (우선순위)
  2. 영어 금칙어 목록은 보조적으로 유지하되, 너무 짧거나 일반적인 단어(예: "sex", "ass", "ball" 등)는 제거하거나 완전 단어 매칭으로 제한
  3. 언어별로 별도 파일로 분리하여 유지보수 용이성 향상 고려
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/filter/banned-words.txt` around lines 1 - 962, The
banned-words.txt currently only contains English profanity which mismatches your
Korean-first app (ErrorCode messages like "사용할 수 없는 단어가 포함되어 있습니다."); add a
Korean profanity list and reorganize the blocklist into language-specific files
(e.g., banned-words.ko.txt and banned-words.en.txt), update any loader to read
per-language files (look for where banned-words.txt is loaded/validated and
ErrorCode is emitted) and change matching rules to (1) prefer full-word/exact
matches for short/common English tokens (e.g., "sex", "ass", "ball") or remove
them from the English default list, and (2) ensure Korean entries include common
conjugations/spacing variants; keep English list as secondary and maintain
normalization (lowercase, strip punctuation) in the validation path that
triggers ErrorCode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 필터링 추가

1 participant