기어가더라도 제대로

[에러] 오래 응답이 없는 화면 - 2 본문

만난 에러들

[에러] 오래 응답이 없는 화면 - 2

Damagucci-juice 2024. 12. 11. 19:14
게시글 목록을 불러오는 과정에서 겪은 에러를 소개합니다. 

 

이전글

2024.12.11 - [만난 에러들] - [에러] 오래 응답이 없는 화면 - 1

 

Instrument 조금만 더 나아가기

CPU 사용량 부분을 선택하고, 2번에 오른쪽 패널 부분을 누르면 스택에서 어떤 함수가 사용되었는지 또 그들의 수행시간을 볼 수 있었습니다. 그러나 지금 화면은 시스템 콜과 Foundation 콜이 섞여있어서 사용자가 호출한 어떤 함수가 오랜 시간을 잡아먹는지를 보기에는 어렵습니다.

Call Tree 에서 Hide System Libraries 를 체크 박스 선택하면 사용자가 호출한 함수들이 얼마나 시간이 걸렸는지를 쉽게 보여줍니다. 여기서 한 3일 정도 레포지토리 패턴한다고 썻다가 이 사실을 알았을 때 얼마나 안타깝던지요. 사진에서 보이는 WordFilter.changeBadWords 가 약 8초 정도를 잡아먹어서 문제였습니다.

욕설을 필터링하기

위의 함수은 목적은 욕설을 필터링하는데 있습니다. 저 기능을 주석처리하고 빌드를 돌려보니 데이터를 가져오는데 적은 시간밖에 소요되지 않았습니다. 이게 문제였던 것이라 확신하게 되었습니다. 이 함수의 구성은 다음과 같습니다.

  1. WordFilter라는 클래스가 욕설의 배열을 가지고 있습니다.
  2. changeBadWords(plain:) 함수에 게시글의 내용을 매개변수로 전달합니다.
  3. 욕설의 배열을 For-loop으로 순회하면서 plain에 순회하는 욕설이 포함되어있는지 확인하고 그것을 고칩니다.

여기서 문제점이 있습니다. 이 함수가 게시글마다 동작하고 있어서 게시글 리스트의 요청이 오래걸렸던 것입니다. 욕설 리스트의 길이가 길어지면, 문자열에 contains(욕설) 이 내부적으로 포문을 돌면서 확인을 하는 것입니다.

let badWords = ["욕설1", "욕설2", "욕설3", ...]

for badWord in badWords {
		if plain.contains(badword) {
				// 처리
		}
}

2중 포문으로 문제를 해결하고 있었기 때문에 O(N^2)의 시간이 소요되었습니다. 욕설 데이터의 크기가 커질 수록, 게시글의 글 내용이 길어질 수록 더 오래걸리는 상황이였습니다.

문제 해결 전략 - Trie

이 부분은 Perplexity의 힘을 빌렸습니다. “이러한 문제가 있는데 도와줘”라고 하니 Trie라는 구조를 추천해줬습니다. 조금만 손을 보고 하니까 정말 금방 되서 신기했습니다. 추후에 Trie 구조에 대해서 공부를 해봐야겠습니다.

테스트로 확인

이부분도 최종 코드가 이런데 Unit 테스트를 만들어줘. 하니까 잘 만들어줬습니다. 고마운 친구..

    func test_ChangeBadWords() {
        let input = "이 문장에는 10새끼라는 욕설이 포함되어 있습니다."
        let expected = "이 문장에는 ****라는 욕설이 포함되어 있습니다."
        let result = wordFilter.changeBadWords(plain: input)
        XCTAssertEqual(result, expected, "욕설이 제대로 필터링되지 않았습니다.")
    }

최대 요청 개수를 1000개로 하고 테스트

기본 요청은 10개씩 가져오는데, 테스트로 나름의 부하 테스트를 진행해봤습니다.

10개의 게시글을 서버에서 불러오는 것은 2초가 걸리던 것이 0.45초로 줄였습니다.

앞에 작은 Micro Hang은 10개를 불러오는데 캐시에서 가져와서 이번엔 0.3초가 걸렸습니다. 10개 포스트에 대한 캐시가 의도한대로 잘 동작해서 기분이 좋네요. 빨간색 부분은 1000개 포스트를 요청한 부분입니다. Severe Hang이 10.08초가 걸렸습니다.

오른쪽 하단에 Stack Trace에서 위에서 세번째가 PostCell을 그리는 작업인데 1000개쯤 했으니 약 10초가 걸리네요. 성공입니다. 주로 Cell을 그리는데 시간을 많이 쓰네요.

결론

  • 무언가를 하기 전에 의심하고 또 의심하기
  • 당장 시간을 조금 더 쓰는게 길게 보면 시간을 아끼는 길일 수 있음

참고

https://iosbrain.com/blog/2018/11/24/using-xcode-instruments-time-profiler-template-to-optimize-swift-code/

 

Using Xcode 10 Instrument’s Time Profiler template to optimize Swift 4.2 code – iOS Brain

My original article — “How to Use Xcode Instrument to Optimize Your Swift Code” — was published on appcoda.com. I still run across curmudgeons who flat out reject techniques like object-oriented programming (especially inheritance and polymorphism)

iosbrain.com

 

Comments