내용이 많아 이어서 작성…
커뮤니티 유저 프로필 구현
ChatManager 구현
import FoundationAdd
import FirebaseStorage
import FirebaseDatabase
import FirebaseFirestore
class ChatManager {
func getSenders(channelName: String, completion: @escaping ([String]) -> Void) {
channelCollection.whereField(db_channelName, isEqualTo: channelName).getDocuments { (snapshot, error) in
guard let snapshot = snapshot, error == nil else {
print("Error fetching channel: \(String(describing: error))")
completion([])
return
}
var senderIds = Set<String>()
for document in snapshot.documents {
let threadCollection = channelCollection.document(document.documentID).collection("thread")
threadCollection.getDocuments { (threadSnapshot, error) in
guard let threadSnapshot = threadSnapshot, error == nil else {
print("Error fetching thread: \(String(describing: error))")
return
}
for threadDocument in threadSnapshot.documents {
if let senderId = threadDocument.data()["senderId"] as? String {
senderIds.insert(senderId)
}
}
completion(Array(senderIds))
}
}
}
}
}
보내는 사람이 누군지를 알아내는 함수이다.
ChatVC - 보낸 사람 프로필 이미지 표시 기능 정리
개요
채팅 화면에서 각 발신자의 프로필 이미지를 메시지 셀 옆에 표시하기 위해 다음을 구현했다:
- 본인 및 채널 내 발신자의 프로필 이미지 URL을 불러오기
- 이미지 캐싱을 통한 중복 요청 방지
- MessagesKit 델리게이트를 활용한 셀 아바타 표시
1. 주요 프로퍼티
역할 요약:
chatFirestoreStream: 채팅 메시지를 수신하는 스트림chatManager: 채널에 포함된 사용자 ID 리스트를 불러오기 위한 매니저profileImageUrls: senderId → 이미지 URL 매핑imageCache: senderId → UIImage 매핑 (중복 요청 방지 목적)
정의:
let chatFirestoreStream = ChatFirestoreStream()
let chatManager = ChatManager()
private var profileImageUrls = [String: String]()
private var imageCache = [String: UIImage]()
2. viewDidLoad() 내부 동작
무엇을 하나?
- 본인 프로필 이미지 먼저 불러와 저장
- 이후 채널 내 모든 sender의 이미지를 요청
- 메시지 콜렉션뷰 초기화 및 스크롤 위치 조정
override func viewDidLoad() {
super.viewDidLoad()
configureColor()
fetchDisplayNameAndProfileImage { [weak self] displayName, imageUrl in
self?.currentDisplayName = displayName ?? "Unknown"
if let profileImageUrl = imageUrl, let userId = self?.user?.uid {
self?.profileImageUrls[userId] = profileImageUrl
}
self?.messagesCollectionView.reloadData()
DispatchQueue.main.async {
self?.messagesCollectionView.scrollToLastItem()
}
}
getSenderImage() // 🔽 아래에서 설명
confirmDelegates()
removeOutgoingMessageAvatars()
addCameraBarButtonToMessageInputBar()
listenToMessages()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
messagesCollectionView.addGestureRecognizer(tapGesture)
}
3. 사용자 이미지 수집 흐름
getSenderImage()
설명: 채널 내 모든 발신자 UID를 가져온 뒤 각자의 이미지 URL을 가져온다.
func getSenderImage() {
chatManager.fetchAllSenderIds(channelId: channelId) { [weak self] senderIds in
self?.fetchProfileImages(for: senderIds)
}
}
fetchProfileImages(for:)
설명: senderId 리스트를 받아 각각 반복적으로 이미지 요청
func fetchProfileImages(for senderIds: [String]) {
for senderId in senderIds {
fetchUserDataAndProfileImage(for: senderId)
}
}
fetchUserDataAndProfileImage(for:)
설명: 해당 UID의 Firebase 데이터베이스에서 닉네임과 프로필 이미지 URL을 가져옴. 가져온 URL은 profileImageUrls에 저장.
func fetchUserDataAndProfileImage(for uid: String? = nil) {
guard let uid = uid ?? user?.uid else { return }
UserManager().fetchUserData(uid: uid) { [weak self] user in
guard let user = user else { return }
self?.profileImageUrls[uid] = user.profileImageUrl
self?.messagesCollectionView.reloadData()
}
}
fetchDisplayNameAndProfileImage()
설명: 로그인한 본인 계정의 닉네임과 이미지 URL을 불러오는 래퍼 메서드
func fetchDisplayNameAndProfileImage(completion: @escaping (String?, String?) -> Void) {
fetchUserDataAndProfileImage { user in
completion(user?.nickName, user?.profileImageUrl)
}
}
4. 이미지 사전 로딩 처리
preloadProfileImages(for:)
설명: 메시지를 받아왔을 때, 발신자 목록을 기준으로 이미지가 아직 캐시되지 않은 경우 미리 받아두는 역할
func preloadProfileImages(for messages: [Message]) {
let senderIds = messages.map { $0.sender.senderId }
let uniqueSenderIds = Set(senderIds)
for senderId in uniqueSenderIds {
if imageCache[senderId] == nil {
if let urlString = profileImageUrls[senderId],
let url = URL(string: urlString) {
KingfisherManager.shared.retrieveImage(with: url) { result in
if case .success(let value) = result {
self.imageCache[senderId] = value.image
self.messagesCollectionView.reloadData()
}
}
}
}
}
}
5. 메시지 셀 구성 (UICollectionViewDataSource)
cellForItemAt
설명: MessagesKit이 메시지 셀을 생성할 때 avatarView를 구성하기 위해 호출됨
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! MessageContentCell
let message = messages[indexPath.section]
configureAvatarView(cell.avatarView, for: message, at: indexPath, in: collectionView as! MessagesCollectionView)
return cell
}
6. MessagesKit Avatar 관련 델리게이트
avatarFor(message:)
설명: 아바타에 표시할 이미지를 제공함. 캐시에 있으면 이미지, 없으면 이니셜만 보여줌.
func avatarFor(message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar {
let senderId = message.sender.senderId
if let image = imageCache[senderId] {
return Avatar(image: image)
} else {
return Avatar(initials: String(senderId.prefix(1)))
}
}
avatarSize(for:)
설명: 아바타 뷰의 크기 설정
func avatarSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize {
return CGSize(width: 30, height: 30)
}
configureAvatarView(_:for:at:in:)
설명: 아바타 뷰에 이미지를 적용하는 메서드
profileImageUrls에서 URL 가져오기- Kingfisher로 다운로드
imageCache에 저장 후 뷰 업데이트
func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) {
let senderId = message.sender.senderId
if let cachedImage = imageCache[senderId] {
avatarView.image = cachedImage
return
}
if let urlString = profileImageUrls[senderId],
let url = URL(string: urlString) {
avatarView.kf.setImage(with: url) { result in
if case .success(let value) = result {
self.imageCache[senderId] = value.image
}
}
}
}
정리
| 파트 | 기능 | 설명 |
|---|---|---|
| profileImageUrls | senderId → 이미지 URL | 사용자별 이미지 주소 저장 |
| imageCache | senderId → UIImage | 다운로드된 이미지 메모리 캐시 |
| fetchUserDataAndProfileImage | 사용자 데이터 가져오기 | Firebase에서 nickname + 이미지 URL 획득 |
| getSenderImage | 모든 발신자 이미지 요청 시작 | 채널 내 전체 sender들 대상 |
| preloadProfileImages | 캐시 없는 경우 이미지 사전 로딩 | 메시지 수신 시 빠른 표시 대비 |
| configureAvatarView | 셀에 이미지 적용 | Kingfisher 비동기 다운로드 및 캐시 저장 |
이 구현은 메시지 화면에서 사용자 아바타를 시각적으로 빠르고 정확하게 보여주기 위한 전체 처리 흐름이다.
성능 개선을 위한 캐시 전략, UX를 고려한 비동기 처리, MessagesKit의 델리게이트 활용이 핵심 포인트다.
PREVIOUSFinal (21)
NEXTFinal (23)