팀원분 : "기능이 잘 동작하고있으며, 메서드를 구현할때 3개의 Database를 다 불러와서 써야하는 상황들이 많이 있는데 이걸 굳이 쪼개야하는 이유를 모르겠다"
나: "님은 님 코드가 머리속에 다 있으셔서 보기에 지금구조로도 충분히 편하실수있으나 우리 코드의 context를 모르는 분이 코드를볼때는 하나의 레퍼지토리에 3개의 Database를 불러오고 그 안에서 3개의 DB에대한 CRUD를 구현해놓은 상황이고 거기에 3개의 데이터베이스를 조합해서 내가 원하는 자료를 소팅하는 메서드들이 여러개가 있는데 이렇게 많은 일을 하는 레퍼지토리는 이해하기 어려운 구조인 것 같습니다. 그래서 관심사별로 class를 나누고 쪼개야 처음 프로젝트를 보시는분이 지금보다 한결 편하게 우리 코드를 이해하실 수 있을것 같습니다."
package kr.sparta.tripmate.data.datasource.remote.community
import com.google.firebase.database.ktx.database
import com.google.firebase.database.ktx.getValue
import com.google.firebase.database.ktx.snapshots
import com.google.firebase.ktx.Firebase
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kr.sparta.tripmate.data.model.community.CommunityModel
import kr.sparta.tripmate.domain.model.search.SearchBlogEntity
/**
* 작성자: 서정한
* 내용: Firebase RealtimeDatabase에서 필요한 자료를
* 요청하고 응답받는 DataSource Class
* */
class FirebaseBoardRemoteDataSource {
private val REFERENCE_COMMUNITY_DATA = "CommunityData"
private fun getReference() = Firebase.database.getReference(REFERENCE_COMMUNITY_DATA)
/**
* 작성자: 서정한
* 내용: 게시글 업로드시 사용할 키를 반환함.
* */
fun getKey(): String {
return getReference().push().toString()
}
/**
* 작성자: 서정한
* 내용: 커뮤니티 게시글 목록 가져오기
* */
fun getAllBoards(): Flow<List<CommunityModel?>> {
val ref = getReference()
return ref.snapshots.map { snapshot ->
snapshot.children.mapNotNull {
it.getValue<CommunityModel>()
}
}
}
/**
* 작성자: 서정한
* 내용: 커뮤니티 게시글 가져오기
* */
fun getBoard(key: String): Flow<CommunityModel?> {
val ref = getReference()
return ref.snapshots.map { snapshot ->
snapshot.children.mapNotNull {
it.getValue<CommunityModel>()
}.find { it.key == key }
}
}
/**
* 작성자: 서정한
* 내용: 커뮤니티에 게시글 추가
* */
fun addBoard(item: CommunityModel) {
val ref = getReference()
ref.get().addOnSuccessListener { snapshot ->
val list = snapshot.children.mapNotNull {
it.getValue<CommunityModel>()
}.toMutableList()
// 새로운 글 업로드
list.add(item)
ref.setValue(list)
}
}
/**
* 작성자: 서정한
* 내용: 커뮤니티 게시글 목록 업데이트
* */
fun updateBoard(
item: CommunityModel
) {
val ref = getReference()
ref.get().addOnSuccessListener { snapshot ->
val list = snapshot.children.mapNotNull {
it.getValue<CommunityModel>()
}.toMutableList()
list.forEachIndexed { index, communityModel ->
if (communityModel.key == item.key) {
list[index] = item
return@forEachIndexed
}
}
ref.setValue(list)
}
}
/**
* 작성자: 서정한
* 내용: 커뮤니티 게시글 삭제
* */
fun removeBoard(key: String) {
val ref = getReference()
ref.get().addOnSuccessListener { snapshot ->
val boards = snapshot.children.map {
it.getValue(CommunityModel::class.java)
}.toMutableList()
val removeItem = boards.find { it?.key == key } ?: return@addOnSuccessListener
boards.remove(removeItem)
ref.setValue(boards)
}
}
/**
* 작성자: 서정한
* 내용: 게시글의 좋아요 클릭시 내 좋아요 클릭목록 업데이트.
* */
fun updateBoardLike(uid: String, key: String) {
val ref = getReference()
ref.get().addOnSuccessListener { snapshot ->
// 내가 좋아요누른 게시판Key 가져오기
val boards = snapshot.children.map {
it.getValue(CommunityModel::class.java)
}.toMutableList()
// key와 일치하는 게시글 불러오기
val model = boards.find { it?.key == key } ?: return@addOnSuccessListener
val index = boards.indexOf(model)
// 게시글의 좋아요 유저에 좋아요 클릭한 user추가
val currentLikeUsers = model.likeUsers.toMutableList()
val isLike = currentLikeUsers.find { it == uid }
if (isLike.isNullOrEmpty()) {
addBoardLike(boards, index, model, uid)
} else {
removeBoardLike(boards, index, model, uid)
}
}
}
/**
* 작성자: 서정한
* 내용: 내가 클릭한 게시글을 내 좋아요 목록에 추가
* 그리고 좋아요수 +1
* */
private fun addBoardLike(
boards: MutableList<CommunityModel?>,
index: Int,
model: CommunityModel,
uid: String
) {
val ref = getReference()
// 게시글의 좋아요 유저에 좋아요 클릭한 user추가
val currentLikeUsers = model.likeUsers.toMutableList()
currentLikeUsers.add(uid)
boards[index] = model.copy(
likes = boards[index]?.likes?.plus(1),
likeUsers = currentLikeUsers
)
ref.setValue(boards)
}
/**
* 작성자: 서정한
* 내용: 내 좋아요목록에서 해당 게시글 삭제
* 게시글이 삭제될때만 동작함.
* 그리고 좋아요 수 -1
* */
private fun removeBoardLike(
boards: MutableList<CommunityModel?>,
index: Int,
model: CommunityModel,
uid: String,
) {
val ref = getReference()
val currentLikeUsers = model.likeUsers.toMutableList()
currentLikeUsers.remove(uid)
boards[index] = model.copy(
likes = boards[index]?.likes?.minus(1),
likeUsers = currentLikeUsers
)
ref.setValue(boards)
}
/**
* 작성자: 서정한
* 내용: 내가 스크랩한 게시글을 업데이트합니다.
* */
fun updateScrapBoards(uid: String, key: String) {
val ref = getReference()
ref.get().addOnSuccessListener { snapshot ->
// 게시판목록 가져오기
val boards = snapshot.children.map {
it.getValue(CommunityModel::class.java)
}.toMutableList()
// key와 일치하는 게시글 불러오기
val model = boards.find { it?.key == key } ?: return@addOnSuccessListener
val index = boards.indexOf(model)
// 게시글의 스크랩목록에 내 uid 추가 혹은 제거
val currentScrapBoards = model.scrapUsers.toMutableList()
val isScrap = currentScrapBoards.find { it == uid }
if (isScrap.isNullOrEmpty()) {
addScrapBoard(boards, index, model, uid)
} else {
removeScrapBoard(boards, index, model, uid)
}
}
}
/**
* 작성자: 서정한
* 내용: 내가 클릭한 게시글을 내 게시글 스크랩 목록에 추가
* */
private fun addScrapBoard(
boards: MutableList<CommunityModel?>,
index: Int,
model: CommunityModel,
uid: String
) {
val ref = getReference()
// 게시글의 스크랩목록에 내 uid 추가 혹은 제거
val currentScrapBoards = model.scrapUsers.toMutableList()
currentScrapBoards.add(uid)
boards[index] = model.copy(
scrapUsers = currentScrapBoards
)
ref.setValue(boards)
}
/**
* 작성자: 서정한
* 내용: 내 게시글 스크랩 목록에서 해당 게시글 삭제
* 게시글이 삭제될때만 동작함.
* */
private fun removeScrapBoard(
boards: MutableList<CommunityModel?>,
index: Int,
model: CommunityModel,
uid: String,
) {
val ref = getReference()
val currentScrapBoards = model.scrapUsers.toMutableList()
currentScrapBoards.remove(uid)
boards[index] = model.copy(
scrapUsers = currentScrapBoards
)
ref.setValue(boards)
}
}
package kr.sparta.tripmate.data.datasource.remote.community
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.util.Log
import android.widget.ImageView
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.ktx.storage
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.PublishSubject
import java.io.ByteArrayOutputStream
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
/**
* 작성자: 서정한
* 내용: Firbase Storage에 Image를 저장
* */
class FirebaseStorageRemoteDataSource {
private fun getReference(value: String) = Firebase.storage.reference.child(value)
/**
* 작성자: 서정한
* 내용: 커뮤니티 글작성 페이지에서
* 이미지를 FireStore에 업로드한다.
* */
suspend fun uploadImage(
imgName: String,
image: Bitmap,
): String {
val imageRef = getReference("$imgName.jpg")
val baos = ByteArrayOutputStream()
image.compress(Bitmap.CompressFormat.JPEG, 100, baos)
val data = baos.toByteArray()
val uploadTask = imageRef.putBytes(data)
val result = suspendCoroutine<String> { continuation ->
uploadTask.addOnFailureListener {
Log.e("TripMates", "upload Image Error: ${it.toString()}")
}.addOnSuccessListener {
imageRef.downloadUrl.addOnSuccessListener { url ->
// 발행
continuation.resume(url.toString())
}
}
}
return result
}
}
package kr.sparta.tripmate.data.datasource.remote.community.scrap
import android.os.Build
import android.text.Html
import com.google.firebase.database.ktx.database
import com.google.firebase.database.ktx.getValue
import com.google.firebase.database.ktx.snapshots
import com.google.firebase.ktx.Firebase
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kr.sparta.tripmate.data.model.community.CommunityModel
import kr.sparta.tripmate.data.model.search.SearchBlogModel
import kr.sparta.tripmate.domain.model.search.SearchBlogEntity
/**
* 작성자: 서정한
* 내용: 모든 블로그 스크랩데이터 가져오기
* */
class FirebaseBlogScrapRemoteDataSource {
private final val REFERENCE_BLOG_SCRAP = "BlogScrap"
private fun getReference(uid: String) =
Firebase.database.getReference(REFERENCE_BLOG_SCRAP).child(uid)
/**
* 작성자: 서정한
* 내용:내가 스크랩한 블로그 목록 불러오기
* */
fun getAllBlogScrapsFlow(uid: String): Flow<List<SearchBlogModel?>> {
val ref = getReference(uid)
return ref.snapshots.map { snapshot ->
snapshot.children.mapNotNull {
it.getValue<SearchBlogModel>()
}
}
}
fun updateBlogScrap(uid: String, model: SearchBlogModel) {
val ref = getReference(uid)
ref.get().addOnSuccessListener { snapshot ->
// 스크랩한 블로그목록 가져오기
val scrapBlogs = snapshot.children.map {
it.getValue(SearchBlogModel::class.java)
}.toMutableList()
// link가 일치하는 블로그 불러오기
val blogItem = scrapBlogs.find { it?.link == model.link }
// 블로그 목록 업데이트
if (blogItem == null) {
addBlogScrap(
uid = uid,
scrapBlogs = scrapBlogs,
model = model,
)
} else {
removeBlogScrap(
uid = uid,
scrapBlogs = scrapBlogs,
model = model,
)
}
}
}
/**
* 작성자: 서정한
* 내용: 해당 key의 블로그 스크랩데이터 가져오기
* Firebase RDB에 저장한다.
* */
private fun addBlogScrap(
uid: String,
scrapBlogs: MutableList<SearchBlogModel?>,
model: SearchBlogModel
) {
/**
* 작성자: 서정한
* 내용: 스크랩데이터를 네이버에서 받아올때 html태그가 String에 섞여있음.
* 검색한 데이터의 String값만 뽑아내기위한 메서드
* */
fun stripHtml(html: String): String {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return Html.fromHtml(html).toString()
}
return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY).toString()
}
// 제목 html 태그 제거
val title = model.title?.let { stripHtml(it) }
// 내용 html 태그 제거
val description = model.description?.let { stripHtml(it) }
val ref = getReference(uid)
scrapBlogs.add(
model.copy(
title = title,
description = description,
)
)
ref.setValue(scrapBlogs)
}
/**
* 작성자: 서정한
* 내용: 선택한 블로그 스크랩 삭제
* */
private fun removeBlogScrap(
uid: String,
scrapBlogs: MutableList<SearchBlogModel?>,
model: SearchBlogModel
) {
val ref = getReference(uid)
val removeItem = scrapBlogs.find { it?.link == model.link } ?: return
scrapBlogs.remove(removeItem)
ref.setValue(scrapBlogs)
}
}