본문 바로가기

SpringBoot/Project

[Springboot/Firebase] Firebase CRUD 정리

Firebase CRUD 정말 간단하지만 헷갈린다,,,
그래서 나 보기 좋으라고 정리하는 CRUD
언어는 Java 기준


Firebase 데이터베이스 구조

Firebase는 NoSQL 기반의 실시간 데이터베이스로서 Firestore와 Realtime Database 두 가지 주요 데이터베이스 서비스를 제공하고 있다.
그중 사용한 데이터베이스는 FireStore로, FireStore는 JSON 기반 문서를 사용하여 데이터를 저장하고 쿼리할 수 있다. 

FireStore에서는 다음과 같은 기본 요소들로 구성되어 있다.

  • Database: 한개의 프로젝트 내에 여러 개의 Firestore 데이터베이스가 포함될 수 있다. 각 데이터베이스는 고유한 ID를 가지고 있다.
  • Collection: 컬렉션은 문서들의 그룹을 나타낸다. 컬렉션은 데이터를 구조화하는 주요 수준의 단위.
  • Document: 문서는 키-값 쌍으로 구성된 데이터 객체이다. 문서의 키(key)는 고유한 식별자를 가지며, 값(value)은 다양한 데이터 유형을 가질 수 있다.
  • Field: 문서 내에서 각 데이터는 필드(field)로 저장된다. 필드는 특정 값(value)을 가지며, 필드의 이름과 해당 값으로 구성된다.




코드 예시

User.java

public class User {
    private String id;
    private String pw;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date birth;
    private List<String> myList;
}


READ

1. 컬렉션에 있는 모든 문서를 읽어오기

// Firestore 인스턴스 가져오기
Firestore firestore = FirestoreClient.getFirestore();
        try {
           
            List<User> users = firestore.collection("User")//User컬렉션 참조
             .get()// ApiFuture<QuerySnapshot> 가져오기
             .get()// QuerySnapshot 가져오기
             .getDocuments()// List<QueryDocumentSnapshot> 가져오기
  //불러온 documents를 각각 User.class형식으로 매핑
  .stream().map(document -> document.toObject(User.class)).toList();
            }catch (InterruptedException | ExecutionException e) {
            System.err.println("Firestore 데이터 조회 중 오류 발생: " + e);
         }

2. 컬렉션에 있는 특정 문서 읽어오기

- 문서 아이디값으로 읽어오기

    // Firestore 인스턴스 가져오기
    Firestore firestore = FirestoreClient.getFirestore();
    User user = firestore.collection("User")//User컬렉션 참조
      .document("documentId")// documentId 문서 참조
                         .get()// ApiFuture<DocumentSnapshot> 가져오기
                         .get()// DocumentSnapshot 가져오기
                         .toObject(User.class)// User형식으로 변환해서 가져오기


- 조건에 따라 문서 읽어오기
   Firebase Firestore에서는 다양한 조건을 사용하여 문서를 검색할 수 있다.
   

  • whereEqualTo(fieldPath, value) : 특정 필드(fieldPath)의 값이 주어진 값(value)과 정확히 일치하는 문서를 검색한다.
  • whereGreaterThan(fieldPath, value) : 특정 필드(fieldPath)의 값이 주어진 값(value)보다 큰 문서를 검색한다.
  • whereLessThan(fieldPath, value) : 특정 필드(fieldPath)의 값이 주어진 값(value)보다 작은 문서를 검색한다.
  • whereGreaterThanOrEqualTo(fieldPath, value) : 특정 필드(fieldPath)의 값이 주어진 값(value)보다 크거나 같은 문서를 검색한다.
  • whereLessThanOrEqualTo(fieldPath, value) : 특정 필드(fieldPath)의 값이 주어진 값(value)보다 작거나 같은 문서를 검색한다.
  • whereArrayContains(fieldPath, value) : 특정 필드(fieldPath)가 배열인 경우, 해당 필드의 배열에 주어진 값(value)이 포함된 문서를 검색한다.
  • orderBy(fieldPath) : 특정 필드(fieldPath)를 기준으로 결과를 정렬한다. 기본적으로 오름차순이다.
  • limit(limit) : 검색 결과의 최대 개수를 제한한다.

   
   ex1) id 필드값이 'yooon'인 Document 

    // Firestore 인스턴스 가져오기
    Firestore firestore = FirestoreClient.getFirestore();
    // 가져올 문서가 한개인 경우
    User user = firestore.collection("User")//User컬렉션 참조
      .whereEqualTo("id", "yooon")
                         .get()
                         .get()
                         .getDocuments().get(0)//조건에 해당하는 첫번째 문서 가져오기
                         .toObject(User.class);
    // 가져올 문서가 여러개인 경우
    List<Test> users = firestore.collection("User")//User컬렉션 참조
       .whereEqualTo("id", "yooon")
                          .get()
                                .get()
                                .getDocuments()
                                .stream().map(document -> document.toObject(Test.class)).toList();


   ex2) 필드값으로 조건 검색해서 DocumentId 읽어오기

    // 가져올 문서가 한개인 경우
    String documentId = firestore.collection("User")//User컬렉션 참조
      .whereEqualTo("id", "yooon")
                         .get()
                         .get()
                         .getDocuments().get(0)//조건에 해당하는 첫번째 문서 가져오기
                         .getId();
    // 가져올 문서가 여러개인 경우
    List<String> documentIds = firestore.collection("User")
                    .whereEqualTo("id", "yooon")
                    .get()
                    .get()
                    .getDocuments()
                    .stream() // 문서들을 Stream으로 변환
                    .map(QueryDocumentSnapshot::getId) // 각 문서의 ID를 추출
                    .collect(Collectors.toList()); // List로 수집

 

- 조건결합하기
위의 검색 조건들을 필요에 따라 결합하여 복합적인 쿼리를 만들 수 있다. 
ex)

collectionRef.whereEqualTo("department", "IT").orderBy("salary", Query.Direction.DESCENDING);


    
3. 특정 문서의 하위 컬렉션 문서 읽어오기

// Firestore 인스턴스 가져오기
Firestore firestore = FirestoreClient.getFirestore();
List<Post> posts = firestore.collection("User")// User컬렉션 참조
.document(userId)//DocumentId가 userId인 문서 참조
                            .collection("Post")//해당 문서의 Post컬렉션 참조
                            .get()
                            .get()
                            .getDocuments()// 문서 가져오기
                            .stream().map(document -> document.toObject(Post.class))// Post형식으로 매핑
                            .collect(Collectors.toList());

 

4. 사진 읽어오기
사진을 읽어올때는 저장한 사진 경로를 데이터베이스에 저장을 해두고 경로에 따라 사진을 읽어왔다.


[Firebase Storage 시작하기]

2024.05.13 - [SpringBoot/Project] - Firebase Storage 시작하기


데이터베이스에 저장되어있는 파일 경로를 이용하여 파일을 불러온다.

해당 코드를 통해 다운로드 URL을 반환받으면 프론트에서 
`<img src="다운로드URL" alt="이미지">` 이미지 태그안에 해당 값을 넣어 이미지를 볼 수 있다.

 

사진을 불러올때는 항상 유효시간을 설정하여 다운로드 링크를 생성한 후 링크를 보내줘야하는데 그렇게되면 링크가 길어지고 복잡해지기 때문에 storage 규칙에서 read는 모든 사용자에게 허용을 해준 후 토큰없이 다음 URL을 통해 불러왔다.
https://firebasestorage.googleapis.com/v0/b/버킷이름.appspot.com/o/<파일경로>?alt=media 
URL에 각각 해당하는 값을 넣어주면 token값 없이 사진을 읽을 수 있다.
또한 이미지 파일 경로에 한글이 있으면 깨지기 때문에 utf-8로 인코딩을 해주었다.

public String getFile() throws IOException{
        
        String imagePath = "firebase.png"; // 이미지 파일 경로
        String encodedImgPath = URLEncoder.encode(imagePath, "UTF-8");
        String encodedURL = "https://firebasestorage.googleapis.com/v0/b/goody-4b16e.appspot.com/o/"+ encodedImgPath + "?alt=media";

        return encodedURL;
    }

 

CREATE


데이터 설정
데이터는 User 형식대로 설정하였다.

//Date 데이터 설정을 위한 Calendar 객체 생성
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2002);
calendar.set(Calendar.MONTH, Calendar.APRIL); 
calendar.set(Calendar.DAY_OF_MONTH, 24);
// Calendar에서 Date로 변환
Date birthDate = calendar.getTime();
//list
List<String> list = List.of("11", "22");
//데이터 설정
Test data = new Test("yooon","qwe123",birthDate,list);

 

1. 컬렉션에 새로운 문서 저장


저장할 위치로 이동해 `.add()` 메소드 사용

//데이터 저장
//firebase 초기설정
Firestore firestore = FirestoreClient.getFirestore();
//User 컬렉션 밑에 새로운 문서를 생성하고 저장함
ApiFuture<DocumentReference> result = firestore.collection("User")// User 컬렉션 참조
   .add(data);// 데이터 저장
// 작성 결과 가져오기
DocumentReference documentReference = result.get();
// 작성된 문서의 아이디
String docId = documentReference.getId();

 

2. 특정 문서의 하위 컬렉션에 데이터를 저장


그냥 하위 컬렉션을 참조해서 .add(data)만 해주면 된다.

ex) DocumentID를 모르는 경우 : User 컬렉션에서 id 필드값이 'yooon'인 Document의 컬렉션에 데이터를 저장할때

  //컬렉션 밑에 새로운 문서를 생성하고 저장함
  ApiFuture<DocumentReference> result = firestore.collection("User")// User 컬렉션 참조
   .whereEqualTo("id", "yooon")// 조건
                                        .get().get()// 가져오기
   .getDocuments().get(0)// 조회된 결과의 첫번째 문서 참조
                                        .getReference()
                                        .collection("Post")// Post 컬렉션 참조
                                        .add(data);// 데이터 저장
  // 작성 결과 가져오기
  DocumentReference documentReference = result.get();
  // 작성된 문서의 아이디
  String docId = documentReference.getId();

 

ex2) DocumentId를 아는경우

ApiFuture<DocumentReference> result = firestore.collection("User")
.document(documentId)// 문서아이디에 해당하는 문서 참조
                                        .collection("Post")// 해당 문서의 하위 Post 컬렉션 참조
                                        .add(data);// 데이터 저장

 


3. Firebase에서 사진 저장


firebase에서 사진은 storage에 따로 저장/관리를 한다.
firebase에서 사진을 저장할때는 `bucket.create()` 메서드를 사용한다.
- 첫 번째 파라미터(path + "/" + fileName): 업로드할 파일의 전체 경로와 파일 이름을 지정
- 두 번째 파라미터(new FileInputStream(localFilePath)): 업로드할 파일의 InputStream을 제공
- 세 번째 파라미터("image/png"): 업로드할 파일의 MIME 유형을 지정

예시


* 사진을 저장할때 front에서 multipartFile형식으로 받아와서 사용하는 경우 

public String uploadFile(MultipartFile multipartFile) throws IOException{
        String path = "Img";//storage 내 이미지를 업로드 할 경로
        String bucketName = FirebaseApp.getInstance().getOptions().getStorageBucket(); // Firebase Storage 버킷 이름
        Bucket bucket = StorageClient.getInstance().bucket(bucketName);//버킷 가져오기

        InputStream content = new ByteArrayInputStream(multipartFile.getBytes());
        Blob blob = bucket.create(path+"/"+multipartFile.getOriginalFilename(),content,multipartFile.getContentType());
        return blob.getName();
    }

 

 

* 정해진 경로에 있는 사진을 업로드하는 경우

public String uploadFile() throws IOException{
        String path = "Img";//storage 내 이미지를 업로드 할 경로
        String bucketName = FirebaseApp.getInstance().getOptions().getStorageBucket(); // Firebase Storage 버킷 이름
        Bucket bucket = StorageClient.getInstance().bucket(bucketName);// 버킷 가져오기

        // 로컬 파일 경로
        String localFilePath = "C:\\Users\\IdeaProjects\\my_project\\src\\main\\resources\\firebase.png";
        String fileName = "firebase.png"; // 업로드할 파일 이름

        // 파일 업로드
        Blob blob = bucket.create(path+"/"+fileName, new FileInputStream(localFilePath), "image/png");
        return blob.getName();
    }

 

UPDATE

업데이트 할 데이터를 필드명과 값 쌍을 Map객체에 담아 .update(data) 함수 사용

// 업데이트할 데이터 생성
Map<String, Object> updates = new HashMap<>();
updates.put("userId", newUserId); // 기존 필드명과 새로운 값
updates.put(newFieldKey, newFieldValue); // 새로 추가할 필드명과 값 설정
// 문서의 참조 가져오기
DocumentReference docRef = firestore.collection("User").document(documentId);
// 문서 업데이트
ApiFuture<WriteResult> updateFuture = docRef.update(updates);
updateFuture.get(); // 업데이트 완료를 기다림
System.out.println("문서 업데이트 완료");

 

DELETE

1. 문서 전체 삭제

DocumentReference docRef = firestore.collection("User").document("yooon");
ApiFuture<WriteResult> future = docRef.delete();
future.get(); // 완료를 기다림
System.out.println("문서 삭제 완료");


2. 문서 내의 특정 필드 삭제

Map<String, Object> updates = new HashMap<>();
updates.put("age", FieldValue.delete()); // age 필드를 삭제하고자 할때
DocumentReference docRef = firestore.collection("User").document("yooon");
ApiFuture<WriteResult> future = docRef.update(updates);
future.get(); // 업데이트 완료를 기다림
System.out.println("문서 삭제 완료");