일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 교육기획팀원
- kusitms
- springboot
- 객체지향 쿼리 언어
- java on azure day seoul 2025
- java
- 한국대학생it경영학회
- jdbc
- 30기
- api rate limit
- Spring
- Spring Batch
- compile()
- 자바 ORM 표준 JPA 프로그래밍
- RESTClient
- 도메인 주도 개발 시작하기
- 백엔드
- JPQL
- JPA
- 영속성
- api limit
- 최범균
- api rate limiter
- 큐시즘
- GitHub Actions
- 이펙티브자바
- cicd
- reactive operaton
- ddd
- Domain Driven Design
- Today
- Total
코딩은 마라톤
[Java] split 함수를 조심하기 본문
문제 인식
현재 나는 큐시즘 31기에서 밋업프로젝트를 진행하고 있다.
간략하게 서비스를 소개하면,
1. 사용자의 출발지를 입력받는다.
2. 모두에게 가장 공정한 중간지점을 계산해서 경로를 자세히 알려준다.
우리는 이번 밋업 프로젝트에서 출발지를 "서울"로 제한지어 입력받도록 했다.
따라서 1번 과정에서 출발지를 입력하기 전, 검색 시 필터링하는 로직이 필요했다.
"place_name": "서울역",
"address_name": "서울 중구 봉래동2가 122-11",
"road_address_name": "서울 중구 한강대로 405",
"x": "126.97070335253385",
"y": "37.55406888733184",
그래서 나는 address_name의 제일 첫 번째 값인 "지역 1Depth, 시도 단위"를 가져와서 서울일 경우에만 가져오도록 했다.
환경
- 요청 수: 1초 동안 100개 요청
- 측정 도구: JMeter
split()을 이용한 필터링
private boolean isSeoulAddressWithSplit(KakaoSearchResponse response) {
final String SEOUL = "서울";
String address = response.getAddressName();
if (address == null || address.isBlank()) {
return false;
}
String region = address.split(" ")[0];
return SEOUL.equals(region);
}
split()을 이용해 필터링한 간단한 로직이다.
addressName을 가져와서 띄어쓰기를 구분하여 맨 앞 글자를 가져와 비교한다.
그래서 이렇게 하려고 했으나 split() 메소드 내부에서 문제가 생길 수 있는 부분을 확인했다.
split 메소드는 정규표현식(regex)을 이용해 문자열을 쪼갠다. 이때 정규표현식은 Pattern.compile()에서 사용된다.
여기까지 봤을 때는 별 문제가 없어 보일 수 있다. 다만 Pattern.compile()을 확인해보면
compile()이 실행될 때마다 새로운 Pattern 인스턴스가 생성된다.

그래서 무슨 문제인건데?

여럿 출발지 데이터를 필터링할 때마다 split()을 위해 매번 Pattern 인스턴스가 생성된다는 것이다.
출발지 검색은 매번 여러개의 데이터를 조회하고 필터링하기 때문에 아무리 GC에서 처리된다고 하더라도 불필요한 Pattern 인스턴스가 생성되는 것은 문제가 될 수 있다.
해결 방법
1. Pattern 객체를 상수로 선언하기
private static final Pattern SEOUL_PATTERN = Pattern.compile("^서울");
private boolean isSeoulAddressWithPattern(KakaoSearchResponse response) {
String address = response.getAddressName();
if (address == null || address.isBlank()) {
return false;
}
return SEOUL_PATTERN.matcher(address).matches();
}
가장 간단한 방법은 필터링할 때마다 생성되는 Pattern 인스턴스를 초기에 한 번만 생성하는 것이다! 반복적으로 동일한 정규식을 사용하기 때문에, Pattern을 매번 새로 생성하는 건 불필요하기 때문이다.
이렇게 되면, 출발지 검색 결과를 필터링할 때마다 객체가 생성되지 않기 때문에 split()을 사용하는 것보다 성능이 개선된다.
2. 정규표현식을 사용하지 않기
우리 서비스에서는 카카오 Local REST API를 사용하고 있다.
여기서 주소 형식은 "지역 1Depth, 시도 단위 -> 지역 2Depth, 구 단위 -> 지역 3Depth, 동 단위 -> 지역 4Depth" 으로 이뤄지기 때문에 첫 번째 값은 서울이 오게 된다. 따라서 첫 번째 값이 서울임을 보장한다면, 처음 띄어쓰기의 인덱스를 찾아 그 전까지의 단어가 서울인지 비교하는 방법도 있다.
private boolean isSeoulAddress(KakaoSearchResponse response) {
final String SEOUL = "서울";
String address = response.getAddressName();
if (address == null || address.isBlank()) {
return false;
}
int spaceIdx = address.indexOf(' ');
String firstWord = (spaceIdx == -1) ? address : address.substring(0, spaceIdx);
return SEOUL.equals(firstWord);
}
결론
각 방식의 평균 응답 시간, TPS를 그래프로 나타내봤다.
정규표현식을 사용해야 한다면, 상수로 선언하는게 성능상 이점을 얻을 수 있다!
다만, 정규표현식을 사용하지 않고 우회할 수 있다면 다른 방식을 찾아보는 것도 좋은 방안일 것이다.
그럼 정규표현식은 지양해야할까? 아니다!
만약 카카오가 아닌 다른 REST API를 사용했을 때, 시도 단위가 "서울", "서울특별시" 와 같이 여러 방면으로 내려준다면, 이때는 정규표현식을 사용하지 않고 확인하는건 신뢰도가 매우 떨어질 수 있다.
따라서 각 상황을 고려하여 성능과 정확성을 어떤 비율로 가져갈지 충분히 고민해보고 정해봐도 좋을 거 같다!
P.S
쓰다보니, address.startsWith("서울") 해서 더 간단히 할 수도 있을 거 같다..
다만 처리량은 "indexOf() + substring()"보다 낮은데, 왜 그런걸까.. 🤷
'Language > Java' 카테고리의 다른 글
[Java] Thread-Safe 동기화 방식(Lock)과 Thread-Safe Queue (0) | 2025.03.21 |
---|---|
[Java] Call by Value VS Call by Reference (0) | 2024.07.19 |
[자바 ORM 표준 JPA 프로그래밍] 10장. JPQL (2) (0) | 2024.06.19 |
[이펙티브자바] 아이템 29, 30, 31 요약 정리 (1) | 2024.06.09 |
[이펙티브자바] 아이템 26, 27, 28 요약 정리 (0) | 2024.06.07 |