게시글 등록 -1

김정현·2023년 10월 7일
0

Project1: Lost Item

목록 보기
4/9

게시글 작성에 대한 이야기를 해보고자 한다. 구글에서 찾아봐도 의외로 게시글을 작성하고 (본인은 파이어베이스) 서버에 저장한 후, 새로 불러오고 삭제하고 하는 기능을 설명한 곳을 찾기가 꽤 힘들었던 거 같다.
그래서 대부분 게시글 작성 코드를 구성할 때, 아주 어려운 난이도는 아니기에 생각해서 하거나 일정 코드만 블로그 같은 곳에서 부분 부분 참고했다.

그렇기에 로직들이 완벽하지 않고 허술한 부분이 많을 수 있다..

게시글

우선 단계를 나눠 보자면,

  1. 게시글 작성
  • 제목, 사진 등록, 본문 으로 나뉜다.
  • 사진 최대 등록 갯수를 3장 이하로 제한했다.
  • 제목, 사진, 본문 셋 중 한 개라도 값이 없다면 게시글 작성은 실패한다.
  1. 게시글 등록
  • 이번 앱에서는 FireStore 및 FireStorage로 등록한다.
  • 게시글 간의 구분은 "어떤 유저(UserDefaults 사용) 가 작성했는가?" 와
    게시글 제목을 Document ID로 하여 구분하였다.
  • 등록하기 버튼을 누르면 저번 포스팅에서 다뤘듯 지도 화면이 나타나 분실물 위치를 마킹할 수 있게끔 한다.
    좌표를 게시글 문서에 저장해 마커를 표시한다.
  1. 게시글 삭제
  • 해당 Document ID를 이용해 문서를 삭제한다.

대강 이런 단계로 나뉘는데, 이 외에도 마커를 클릭할 시 게시글 정보가 표시되어야하며 '내 글 목록'도 구현했다.

게시글 작성

게시글을 작성하는 곳의 UI는

이런식으로 구성했다. (수 차례 수정했다..)

기능을 구현하며 가장 어려웠던 부분은 어떤 식으로 게시글을 구분할 것이며, FireStore에 Collection, Document, Data Field 를 어떤 식으로 구분하고 어떠한 정보들을 담을 것인가 였다.

해당 앱에서는

  • Collection: "게시글" 로 저장했다.
  • Document: 게시글의 "Title"을 ID로 저장했다.
  • Data Field: 시간, 내용, 유저, 좌표, 사진을 저장하였다.

여러 번 시행착오를 겪으며 결론적으론 이런식으로 구분했다.

가장 핵심이 되는 함수는

func SetData() {
        // Firestore 참조 생성
        let db = Firestore.firestore()
        
        // 컬렉션에 대한 참조 생성
        let collectionRef = db.collection("게시글")
        
        // 문서에 대한 참조 생성
        let documentRef = collectionRef.document(self.titleLabel?.text ?? "")
        
        
        // 컬렉션 존재 여부 확인
        collectionRef.getDocuments { (snapshot, error) in
            if let error = error {
                print("Error getting collection documents: \(error)")
                return
            }
            
            if snapshot?.isEmpty == true {
                // 컬렉션이 비어있으면 컬렉션 생성 후 문서 업데이트 작업 수행
                let data: [String: Any] = ["내용": self.mainTextLabel.text ?? "", "유저": UserDefaults.standard.string(forKey: "UserEmailKey")!, "date": Date().timeIntervalSince1970]
                documentRef.setData(data) { err in
                    if let err = err {
                        print("Error adding document: \(err)")
                    } else {
                        print("Collection created, document added and updated")
                    }
                }
            } else {
                // 컬렉션이 존재하면 문서 존재 여부 확인 후 업데이트 작업 수행
                documentRef.getDocument { (document, error) in
                    if let document = document, document.exists {
                        // 문서가 존재하면 업데이트 작업 수행
                        documentRef.updateData(["내용": self.mainTextLabel.text ?? "", "유저": UserDefaults.standard.string(forKey: "UserEmailKey")!, "date": Date().timeIntervalSince1970]) { err in
                            if let err = err {
                                print("Error updating document: \(err)")
                            } else {
                                print("Document updated")
                            }
                        }
                    } else {
                        // 문서가 존재하지 않으면 문서 생성 후 업데이트 작업 수행
                        let data: [String: Any] = ["내용": self.mainTextLabel.text ?? "", "유저": UserDefaults.standard.string(forKey: "UserEmailKey")!, "date": Date().timeIntervalSince1970]
                        documentRef.setData(data) { err in
                            if let err = err {
                                print("Error adding document: \(err)")
                            } else {
                                print("Document added and updated")
                            }
                        }
                    }
                }
            }
        }
        let storyboard = UIStoryboard(name: "MapMarkerRegister", bundle: nil)
        guard let MapMarkerViewControllerVC = storyboard.instantiateViewController(withIdentifier: "MapMarkerViewController") as? MapMarkerViewController else { return }
        MapMarkerViewControllerVC.titleLabel = self.titleLabel.text!
        MapMarkerViewControllerVC.modalPresentationStyle = .fullScreen
        present(MapMarkerViewControllerVC, animated: true)
    }

문제는 이미지인데,

  //이미지 파이어베이스로 업로드
    func uploadimage(img: UIImage, completion: @escaping (Bool) -> Void) {
        guard let referenceImage = UIImage(named: "free-icon-picture-5639854.png"),
              let referenceImageData = referenceImage.jpegData(compressionQuality: 0.1),
              let imageData = img.jpegData(compressionQuality: 0.1) else {
            // 이미지 데이터를 가져오지 못한 경우 또는 기준 이미지를 가져오지 못한 경우 업로드하지 않음
            completion(false)
            return
        }
        
        // 이미지 데이터가 같은지 비교
        if referenceImageData == imageData {
            // 이미지가 기준 이미지와 같다면 업로드하지 않음
            completion(false)
            return
        }
        
        let fileName = generateUniqueFileName() // 중복되지 않는 파일 이름 생성
        let metaData = StorageMetadata()
        metaData.contentType = "image/png"
        
        storage.reference().child(fileName).putData(imageData, metadata: metaData) { (metaData, error) in
            if let error = error {
                print(error.localizedDescription)
                completion(false)
            } else {
                print("Image uploaded successfully")
                self.downloadAndStoreURL(fileName: fileName)
                completion(true)
            }
        }
    }


    
    //storage로 저장
    func downloadAndStoreURL(fileName: String) {
        let fileRef = storage.reference().child(fileName)
        fileRef.downloadURL { url, error in
            if let error = error {
                print(error.localizedDescription)
            } else if let downloadURL = url {
                self.documentRef = self.db.collection("게시글").document(self.titleLabel.text ?? "")
                self.documentRef?.updateData(["분실물 사진": FieldValue.arrayUnion([downloadURL.absoluteString])]) { err in
                    if let err = err {
                        print("Error adding document: \(err)")
                    } else {
                        print("Document added with profile image")

                    }
                }
            }
        }
    }
 

이렇게 이미지를 FireStorage에 저장하고, URL로 변환해 FireStore에 저장하고 URL을 다시 이미지화 하는 함수들을 추가해주어야한다. 그리고 앨범을 열 수 있게끔 권한 설정도 받아야한다.
(가장 머리가 아팠음)

또, 컬렉션이 있을 경우, 없을경우 나누고 문서가 있을경우 , 없을경우 또 나눈다. 이렇게 알맞게 데이터들을 분류해서 저장한다.

"게시글" 컬렉션 하위에 이런식으로 저장된다.

Firestore에 성공적으로 저장했다면 액션 메소드에 추가하면 된다.

@IBAction func completeBtn(_ sender: Any) {
        let cameraImage = UIImage(named: "free-icon-photo-camera-4653765.png")

        if titleLabel.text != "" && mainTextLabel.text != "" && !imagesAreEqual(lostItemPhoto.image, cameraImage) {          //내용, 제목, 이미지 중 하나라도 비어있다면 게시글 작성 실패
            SetData()
            let storyboard = UIStoryboard(name: "Register", bundle: nil)
            guard let RegisterViewControllerVC = storyboard.instantiateViewController(withIdentifier: "RegisterViewController") as? RegisterViewController else { return }
            RegisterViewControllerVC.mainTableView?.reloadData()
            //이미지파일 가지고 오기
            imagesToUpload = [lostItemPhoto.image, lostItemPhoto2.image, lostItemPhoto3.image]
            uploadImagesSequentially(index: 0)
        } else {
            // 알림 창 표시
               let alertController = UIAlertController(title: "게시글 작성 오류", message: "내용을 확인해주세요.", preferredStyle: .alert)
               
               // 확인 버튼 추가
               let okAction = UIAlertAction(title: "확인", style: .default, handler: nil)
               alertController.addAction(okAction)
               
               // 알림 창 표시
               present(alertController, animated: true, completion: nil)
        }
    }
    
     //이미지를 비교하는 함수
    func imagesAreEqual(_ image1: UIImage?, _ image2: UIImage?) -> Bool {
        if let data1 = image1?.pngData(), let data2 = image2?.pngData() {
            return data1 == data2
        }
        return false
    }

제목, 내용, 사진이 모두 빠지지 않게끔 설정한다면 게시글 작성은 완성된 셈이다.
(좌표를 저장하는 부분은 전 포스팅에서 다룸)

이렇게 작성한 글을 다시 로드하는 과정이 필요하다.
해당 앱에서는 마커를 이용해서 불러오기, 내 글 목록에서 게시글을 선택해 불러오기 로 두 가지 루트로 나뉜다.

이 과정은 다음 포스팅에서 다루겠다.

0개의 댓글