Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- CI/CD
- 카카오 간편 로그인
- Github Actions
- 네이버 간편 로그인
- 웹소켓
- 1인 개발
- perspective api
- 웹 개발
- 크롬 확장 프로그램
- 초성 검색
- 검색 기능
- 웹개발
- CloudType
- 파일 업로드
- 웹서비스 배포
- Stomp
- firebase storage
- 초보 개발자
- 욕설 및 비속어 필터링
- 구글 간편 로그인
- google font icon
- 무한 스크롤
Archives
- Today
- Total
J2ong 님의 블로그
Spring + React 한글 초성 검색 기능 구현하기 본문
안녕하세요!
이번 글에서는 Spring + React 기반의 프로젝트에서 한글 초성 검색이 가능한 사용자 검색 기능을 어떻게 구현했는지 정리해보겠습니다.
기본적인 방식은 사용자의 이름에서 초성만을 추출하여 별도로 저장하고, 검색 시 입력된 검색어를 초성으로 변환하여 비교하는 방식입니다. 이를 통해 'ㅎㄱㄷ'과 같이 초성만 입력해도 '홍길동'과 같은 이름이 검색될 수 있습니다.
📌 검색 기능 구현 (DB)
컬럼명 | 설명 |
user_name | 사용자의 이름 |
user_consonants | 이름에서 추출한 초성 문자열 |
📌 검색 기능 구현 (Spring)
Handler (초성 추출 유틸 클래스)
public class KoreanUtils {
private static final char HANGUL_BEGIN_UNICODE = 44032; // 가
private static final char HANGUL_LAST_UNICODE = 55203; // 힣
private static final char HANGUL_BASE_UNIT = 588;
private static final char[] INITIAL_CONSONANTS = {'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'};
public static String extractInitialConsonants(String text) {
if (text == null) return "";
StringBuilder result = new StringBuilder();
for (char ch : text.toCharArray()) {
if (ch >= HANGUL_BEGIN_UNICODE && ch <= HANGUL_LAST_UNICODE) {
int initialIndex = (ch - HANGUL_BEGIN_UNICODE) / HANGUL_BASE_UNIT;
result.append(INITIAL_CONSONANTS[initialIndex]);
} else {
result.append(ch);
}
}
return result.toString();
}
}
Entity
@Entity
public class UserEntity {
@Id
private String userName;
private String userConsonants;
@PrePersist
@PreUpdate
private void updateConsonants() {
this.userConsonants = KoreanUtils.extractInitialConsonants(this.userName);
}
}
Repository
@Query(
value =
"SELECT u.* " +
"FROM user u " +
"WHERE " +
" LOWER(u.user_name) LIKE LOWER(CONCAT('%', :searchWord, '%')) " +
" OR u.user_consonants LIKE CONCAT('%', :consonantSearch, '%')",
nativeQuery = true
)
List<GetUserListResultSet> searchUsers(
@Param("searchWord") String searchWord,
@Param("consonantSearch") String consonantSearch
);
Service
String consonantSearch = KoreanUtils.extractInitialConsonants(searchWord);
List<GetUserListResultSet> userList = userRepository.searchUsers(searchWord, consonantSearch);
Controller
@GetMapping("/{searchWord}/search-list")
public ResponseEntity<? super GetUserSearchListResponseDto> getSearchUserList(
@PathVariable("searchWord") String searchWord
) {
ResponseEntity<? super GetUserSearchListResponseDto> response = userService.getSearchUserList(searchWord);
return response;
}
📌 검색 기능 구현 (React)
API 요청 (Axios)
const GET_SEARCH_USER_LIST_URL = (searchWord: string) => `${API_DOMAIN}/user/${searchWord}/search-list`;
export const getSearchUserListRequest = async ( searchWord: string) => {
const result = await axios.get(GET_SEARCH_USER_LIST_URL(searchWord))
.then(response => {
const responseBody: GetSearchUserListResponseDto = response.data;
return responseBody;
})
.catch(error => {
if (!error.response) return null;
const responseBody: ResponseDto = error.response.data;
return responseBody;
})
return result;
}
컴포넌트 (자동완성 포함)
const SearchButton = () => {
const [word, setWord] = useState('');
const [autoCompleteResults, setAutoCompleteResults] = useState<GetSearchUserListResponseDto | null>(null);
const searchInputRef = useRef<HTMLInputElement | null>(null);
// 디바운스 처리
const useDebounce = (value: string, delay: number) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debouncedValue;
};
const debouncedWord = useDebounce(word, 300);
// 검색어 변화 감지
const onSearchWordChangeHandler = (e: ChangeEvent<HTMLInputElement>) => {
setWord(e.target.value);
};
// 자동완성 실행
useEffect(() => {
const fetchResults = async () => {
if (debouncedWord.length > 0) {
const results = await getSearchUserListRequest(debouncedWord);
if (results && 'userList' in results) setAutoCompleteResults(results);
else setAutoCompleteResults(null);
} else {
setAutoCompleteResults(null);
}
};
fetchResults();
}, [debouncedWord]);
return (
<div className="header-search-container">
<div className="header-search-input-box">
<input
ref={searchInputRef}
className="header-search-input"
type="text"
placeholder="검색어를 입력해주세요."
value={word}
onChange={onSearchWordChangeHandler}
/>
<div className="icon-button">
<span className="material-symbols-outlined">search</span>
</div>
</div>
{autoCompleteResults?.userList?.length > 0 && (
<ul className="autocomplete-results">
{autoCompleteResults.userList.map((user, idx) => (
<li key={idx}>
<div className="results-list-box">
<div className="results-list-user-info">
<div className="results-list-profile-box">
<img className="results-list-profile-image" src={user.profileImage} />
</div>
<div className="results-list-box-content">
<div className="user-name-list">{user.userName}</div>
</div>
</div>
</div>
</li>
))}
</ul>
)}
</div>
);
};
이처럼 한글 초성 검색 기능은 단순한 LIKE 검색이 아닌, 초성 추출 로직과 초성 컬럼 저장, Spring + React 연동을 통해 손쉽게 구현할 수 있습니다. 사용자 입장에서는 ‘ㅎㄱㄷ’ 만 입력해도 ‘홍길동’ 사용자를 찾을 수 있는 편리한 UX를 제공할 수 있습니다.
오늘도 즐거운 개발 되시길 바랍니다!
'개인 프로젝트' 카테고리의 다른 글
크롬 확장 프로그램 개발 가이드 (2) | 2025.05.03 |
---|---|
Google Font Icon을 활용한 아이콘 사용법 (React) (0) | 2025.04.23 |
Firebase Storage를 활용한 파일 업로드 구현 방법 (2) | 2025.04.22 |
Spring + React 파일 업로드 통합 구현 (korean-romanizer 라이브러리를 활용한 한글 파일명 오류 해결) (1) | 2025.04.21 |
Github Actions와 Cloudtype을 활용한 CI/CD 자동 배포 설정 (0) | 2025.04.20 |