⚠️ 문제: UICollectionView Cell deque시 NSAttributedString 생성 불가 현상
네이버 지역 검색 API의 응답값은 검색 결과로 html 형식의 문자열을 보내주고 있습니다.
이를 문자열로 바로 사용하기 위하여 NSAttributedString으로 변환하는 메서드를 String 타입에 확장하여 정의하였습니다.
extension String {
func htmlToString() -> NSAttributedSTring? {
guard let data = self.data(using: .utf8) else { return nil }
return try? NSAttributedString(
data: data,
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil
)
}
}
해당 메서드는 검색 결과 모델인 LocationInfo에서 name이라는 연산프로퍼티를 나타내기 위해 사용되었습니다.
struct LocationInfo: Hashable {
var htmlName: String
var name: NSAttributedString {
get {
htmlName.htmlToString() ?? NSAttributedString(string: "")
}
}
...
}
그리고 LocationInfo의 name은 UICollectionView의 cell을 구성할 때 사용되었습니다.
// UICollectionView
extension MapViewController {
private func makeCollectionViewDiffableDataSource(_ collectionView: UICollectionView) -> UICollectionViewDiffableDataSource<Int, LocationInfo> {
let listCellRegistration = UICollectionView.CellRegistration<SearchResultCell, LocationInfo> {
cell.configure(data: item) // 컬렉션뷰 셀 설정
}
...
}
}
// Cell
extension SearchResultCell {
func configure(data: LocationInfo) {
nameLabel.attributedText = data.name // 연산 프로퍼티 name 사용
...
}
}
그러나 빌드 시 htmlToString 메서드의 실행 과정에서 오류가 발생하였습니다.

❗️ 원인(추측): NSAttributedString의 생성과 UICollectionView deque의 충돌
우선 오류 메세지를 살펴봅시다.
Thread1: "Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeud and returned to the collection view. Avoid flushing layout before returning the view to the collection view. Avoid dequeuing views without a request from the collection view. ...(후략)...
UICollectionview의 데이터소스가 주어진 index path에 대한 뷰를 요청할 때,
deque가 완료된 뷰를 반환해야한다고 합니다.
뷰를 반환하기 전에 layout을 비우는 것을 피하고,
collection view의 요청 없이 뷰를 deque하는 것을 피하라고 합니다.
하지만 위에서 살펴봤듯 제 코드에서는 cellRegistration 타이밍에 cell을 구성하고 있습니다.
nameLabel.attributedText = data.name
configure 메서드 내에서 위 코드를 실행하며 연산 프로퍼티인 name을 읽어들이기 위해 htmlToString 메서드를 실행하는데, 해당 부분에서 오류가 발생합니다.
nameLabel.text = data.htmlName
위와같이 NSAttributedString으로 변환하지 않고 htmlName을 그대로 사용할 때는 문제가 없었습니다.
따라서 NSAttributedString을 만들어내는 코드가
collectionView의 deque 과정과 모종의 충돌이 있다고 추측할 수밖에 없었습니다.
✅ 해결: NSAttributedString의 생성 시점의 변경 및 직접 사용
셀을 구성하는 시점에 NSAttributedString을 만들어내면서 오류가 발생했기 때문에 dataSource로 사용하고 있는 LocationInfo 객체를 생성할 때 애초부터 NSAttributedString을 생성하여 프로퍼티에 할당하는 방식으로 변경하였습니다.
class LocationViewModel {
// 검색 결과
private func fetchSearchResult(text: String) async throws -> [LocationInfo] {
// 검색
...
// [LocationInfo] 배열 반환
return searchResponse.items.enumerated().reduce(into: [LocationInfo]()) {
guard let name = $1.element.title,
let address = $1.element.roadAddress,
let mapX = Double($1.element.mapx ?? ""),
let mapY = Double($1.element.mapy ?? "") else {
return
}
let image = imageStrings[$1.offset]
$0.append(LocationInfo(name: name.htmlToString() ?? NSAttributedString(string: ""), // NSAttributedString 타입
address: address,
mapX: mapX,
mapY: mapY,
image: image))
}
}
}
struct LocationInfo: Hashable {
var name: NSAttributedString // name 프로퍼티 타입 변경
var address: String
var mapX: Double
var mapY: Double
var image: String
}
이후 오류없이 정상적으로 작동하였습니다.

'내일배움캠프 > Kickboard - Animality' 카테고리의 다른 글
| [의사결정 기록] User 모델 저장 방식 논의 (0) | 2026.03.11 |
|---|---|
| [트러블 슈팅] UICollectionView 상단에 여백 주기 (0) | 2026.03.11 |
| [트러블 슈팅] UIButton의 isHighlighted 미해제 (0) | 2026.03.10 |
| [의사결정 기록] API Key 은닉화 방법 선택하기 (0) | 2026.03.10 |
| [의사결정 기록] 아키텍처 패턴 선정하기 (0) | 2026.03.10 |