Skip to content

FiveI-s/IDo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


👍🏻iDo

사람들과 관심사 또는 취미를 공유해보세요!

다같이 즐길 수 있도록 도와드릴게요!


목차


📆 프로젝트 기간

2023년 10월 10일 ~ 2023년 11월 17일 (6주)


⭐️ 프로젝트 소개

쉽게 모임을 통해 많은 사람들과 함께 취미와 관심사를 즐길 수 있는 커뮤니티 앱

iDo는 카테고리를 통해 좀 더 쉽게 같은 취미를 가진 사람들과 어울릴 수 있습니다.

프로젝트 목표

내성적인 사람을 포함한 2030세대 모두가 어려움 없이 같이 취미를 공유한다는 즐거움을 느낄 수 있도록 기회를 제공하는 것이 목표입니다.


📚 구현 기능

  1. 공통

    • 로그인 유저 데이터 동기화
    • 가입 유저 데이터 동기화
    • 이미지 캐싱 작업

  2. 로그인/회원가입 페이지

    • 아이디 및 비밀번호 비교
    • 이메일 유효성 검사 (중복 가입 방지)
    • 이메일을 이용한 인증번호
    • 비밀번호 유효성 검사 (영문자, 숫자, 특수기호 필수)

  3. 홈 페이지

    • 가입한 모임 목록 표시
    • 가입한 모임으로 이동

  4. 카테고리 페이지

    • 9가지 카테고리 중 1가지 선택

  5. 모임 페이지

    • 모임 커버 이미지, 모임 이름, 모임 소개 표시
    • 가입 멤버 목록
    • 모임장 표시
    • 프로필 이미지 클릭 시, 해당 유저 프로필 확인
    • 모임 생성
    • 모임 생성 시, 3:2 비율로 자르기

  6. 게시글 페이지

    • 제목, 내용, 이미지, 작성 시간 표시
    • 게시글 추가/삭제/수정 기능
    • 댓글 추가/삭제/수정 기능
    • 프로필 이미지 클릭 시, 해당 유저 프로필 확인
    • 게시글 신고하기
    • 댓글 신고하기

  7. 마이 프로필 페이지

    • 나의 프로필 조회
    • 프로필 수정
    • 로그아웃, 서비스 탈퇴


👩🏻‍💻 Contributors

홍준영 김도현 강지훈 한동연 이애라
☀️ 리더 ️🌙 부리더 🛠️ 개발자 🛠️ 개발자 🛠️ 개발자
게시글 목록/게시글 생성 및 수정 게시글 상세/댓글 회원가입 모임 생성/수정 홈 화면
회원탈퇴 이미지 캐싱 로그아웃 이미지 유효성 검사 기능 마이 프로필 화면
신고기능 신고기능 카테고리 이미지 편집 상단 로고 추가

🛠️ Tech Stack



🏹 사용한 라이브러리

  1. Firebase
  2. SnapKit
  3. Tabman
  4. TOCropViewController

❓ 라이브러리 사용 이유

  1. Firebase
    로그인 및 회원가입 진행 시 유저의 데이터를 가져오고 비교하기 위해 Authentication에 데이터 동기화를 위해서 사용
    모임/게시글/댓글 등 각각 콘텐츠의 CRUD 기능을 위한 데이터를 활용하기 위해 Realtime에 데이터 동기화를 위해서 사용
    콘텐츠, 유저와 관련된 이미지를 관리하고 캐싱하기 위해 Storage에 이미지 저장을 위해서 사용

  2. SnapKit
    코드 기반 UI 작업의 효율성을 높이기 위해서 사용 Auto Layout을 쉽게 설정하기 위해 사용

  3. Tabman
    상단 탭바 추가를 위해 사용

  4. TOCropViewController
    이미지 비율을 3:2로 편집을 쉽게 하기 위해 사용


🔫 트러블 슈팅

페이지 별 데이터 동기화

1. 화면 이동 시, 데이터 동기화가 제대로 이루어지지 않음

  • 원인 : 각 페이지마다 데이터를 관리하는 Manager 클래스를 각각 인스턴스화 시켜서 사용
    private let firebaseManager = FirebaseManager()
  • 해결 : 초기화 시, 상위 페이지이의 Manager 클래스 인스턴스를 전달 받아서 사용
    var firebaseManager: FirebaseManager
      
    init(firebaseManager: FirebaseManager) {
        self.firebaseManager = firebaseManager
        super.init(nibName: nil, bundle: nil)
    }
      
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

2. 각 페이지마다 유저 정보를 새롭게 부르거나 접근을 해야함

  • 원인 : 유저가 콘텐츠를 즐기거나, 화면과 상호작용을 할 때 마다 사용자의 정보를 불러오고 있음. 즉, 앱을 사용할 때 사용자와 함께 이동하는 정보 저장 요소가 없음
  • 해결 : 로그인회원가입 시 생성된 유저정보를 싱글톤으로 관리
    final class MyProfile {
      
        static let shared = MyProfile()
        private var firebaseManager: MyProfileUpdateManager!
        private var ref: DatabaseReference = Database.database().reference()
        private var fileCache: ProfileImageCache = ProfileImageCache()
        var myUserInfo: MyUserInfo?
      
        private init() {}
      	  .
      	  .
      	  .
    }
    // 사용 예시
    DispatchQueue.main.async {
        guard let nickName = MyProfile.shared.myUserInfo?.nickName else { return }
        self.instructions.text = "\(nickName)님, 카테고리를 선택해보세요!"
    }

UI 업데이트

1. 데이터가 처리되기 전에, 화면 전환이 끝나서 원하는 결과가 보여지지 않음

  • 원인 : 데이터 처리가 완료되는 시점을 알 수 없음, 순차적으로 진행이 되게 하는 로직이 없음
  • 해결 : 데이터 처리 함수에 completion 을 추가해서 데이터 처리가 완료된 시점에 화면 전환이 이루어지게 함
    func createNoticeBoard(title: String, content: String, completion: @escaping (Bool) -> Void) {
        let ref = Database.database().reference().child("noticeBoards").child(club.id)
        let newNoticeBoardID = ref.childByAutoId().key ?? ""
        .
      	  .
      	  .
        self.uploadImages(noticeBoardID: newNoticeBoardID, imageList: self.newSelectedImage) { success, imageURLs in
            if success {
                .
      					  .  
      					  .
                    completion(success)
                }
            } else {
                completion(false)
            }
        }
    }
    // 사용 예시
    // 새로운 메모 작성
    @objc func finishButtonTappedNew() {
        navigationItem.rightBarButtonItem?.isEnabled = false
          
        if isTitleTextViewEdited, isContentTextViewEdited {
            guard let newTitleText = createNoticeBoardView.titleTextView.text else { return }
            guard let newContentText = createNoticeBoardView.contentTextView.text else { return }
              
            firebaseManager.createNoticeBoard(title: newTitleText, content: newContentText) { success in
                if success {
                    self.navigationController?.popViewController(animated: true)
                    print("게시판 생성 성공")
                }
                else {
                    self.navigationItem.rightBarButtonItem?.isEnabled = true
                    print("게시판 생성 실패")
                }
            }
        }
        else {
            navigationItem.rightBarButtonItem?.isEnabled = true
        }
    }

2. 데이터를 View에 전달한 후에, View가 reload 되지 않음

  • 원인 : 데이터는 Manager 클래스에서 관리하고, 뷰는 ViewController에서 관리함
  • 해결 : Delegate 패턴을 사용하여, 데이터 관련 함수가 호출될 때 해당 TableView reload
    protocol FirebaseManagerDelegate: AnyObject {
      func reloadData()
    }
    class FirebaseManager {
        weak var delegate: FirebaseManagerDelegate?
      	  .
      	  .
      	  .
        {
            if success {
                self.addMyNoticeBoard(noticeBoard: newNoticeBoard)
                self.noticeBoards.insert(newNoticeBoard, at: 0)
                self.delegate?.reloadData()
            }
        }
      	  .
      	  .
    }
    // 사용 예시 - ViewController에서 해당 protocol 상속
    extension NoticeBoardViewController: FirebaseManagerDelegate {
        func reloadData() {
            selectView()
            noticeBoardView.noticeBoardTableView.reloadData()
        }
    }

이미지 비율 및 다운로드 속도

1. 선택된 이미지의 비율이 깨져서 보임

  • 원인 : 이미지와 버튼의 크기가 다름, 이미지를 강제로 컴포넌트에 맞추기 때문에 이미지가 3:2 비율이 아니라면 부자연스럽게 늘어나거나, 축소됨

    이미지와 버튼의 크기가 다름 이미지가 부자연스럽게 들어감

  • 해결 : 이미지 버튼 사이즈를 3:2로 변경, TOCropViewController 라이브러리 사용하여 이미지 추가 시, 이미지 편집
    let cropViewController = TOCropViewController(croppingStyle: .default, image: selectedImage)
    cropViewController.delegate = self
    cropViewController.customAspectRatio = CGSize(width: 3, height: 2) // 비율 3:2
    cropViewController.aspectRatioLockEnabled = true // 비율 선택 잠금
    cropViewController.resetAspectRatioEnabled = false // 비율 리셋 막음
    cropViewController.aspectRatioPickerButtonHidden = true // 비율 변경 토글 히든
    
    picker.dismiss(animated: true) {
        self.present(cropViewController, animated: true, completion: nil)
    }

    라이브러리 적용 후, 이미지 편집 적용 결과

2. 이미지 다운로드 속도가 느림

  • 원인 : 사용자가 보는 이미지에 비해 매우 큰 사이즈의 이미지를 저장하고 불러오기 때문, Firebase Storage는 따로 캐싱작업을 해주지 않음
  • 해결 : compressionQuality 를 사용하여 이미지를 압축하여 저장, storage에 metadata에 있는 md5hash값과 로컬에 있는 이미지 데이터를 md5hash로 변환하여 비교하여 다를 경우 서버에서 이미지를 가져와 캐싱된 이미지를 변경함
    if let image = profileImageButton.image(for: .normal) {
        imageData = image.jpegData(compressionQuality: 0.5) // 이미지 품질
    }
    extension Data {
        var md5Hash: String {
            let hash = Insecure.MD5.hash(data: self)
            return Data(hash).base64EncodedString()
        }
    }
    //사용 예시
    if let localDataHash = cacheImage.pngData()?.md5Hash,
       let storageDataHash = metadata?.md5Hash,
       localDataHash == storageDataHash {
       return
    }

기기 별 레이아웃 조정

1. 기기에 따라서 컴포넌트의 레이아웃이 깨짐

  • 원인 : 기기 별로 화면의 크기가 다름, 오토레이아웃을 정확한 수치로 지정
    // 높이를 수치로 설정
    meetingDescriptionTextView.snp.makeConstraints { make in
        make.top.equalTo(countMeetingNameLabel.snp.bottom).offset(Constant.margin4)
        make.centerX.equalTo(scrollView)
        make.left.right.equalTo(scrollView).inset(Constant.margin4)
        make.height.equalTo(160)
    }

    iPhone 15 Pro iPhone SE

  • 해결 : lessThanOrEqualTo과 greaterThanOrEqualTo 이용해서 레이아웃 설정
    // 높이의 최대, 최소 지정
    meetingDescriptionTextView.snp.makeConstraints { make in
       make.top.equalTo(countMeetingNameLabel.snp.bottom).offset(Constant.margin4)
       make.centerX.equalTo(scrollView)
       make.left.right.equalTo(scrollView).inset(Constant.margin4)
       make.height.lessThanOrEqualTo(160)
     	  make.height.greaterThanOrEqualTo(100)
    }

    iPhone 15 Pro iPhone SE


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages