많은 분들이 댓 남겨주시고 실제로 남겨주시는 분들도 계십니다만

기록용 블로그이다보니 답변이나 친구는 되도록 마음만 받겠습니다 관심 감사합니다.

그래도 글과 관련된 질문들은 답변드리도록 하겠습니다.

 

하 진짜 애 많이썼다..

이거 하느라 굉장히 힘들었었다.

검색 시 기본적으로 관련 지식도 많이 없고

있다고 하더라도 대부분 비공식 옛 패키지 사용이었다.

 

CloudFlare R2는 정식으로 S3 api를 사용할 수 있도록 권장한다고 써져있고

일부분 기능은 사용할 수 없으나 기본적으로 버킷과 관련된 것들과 버킷 내 데이터 업로드 및 읽기는 가능한 걸로 봤다.

근데도 불구하고 관련된 지식이 없는 것은 아무래도 R2 스토리지에 대해 유명세가 파이어베이스 스토리지에 비해 적다는 이유가 가장 크지 않을까 싶다.

 

하..길고 길었다 삽질..

삽질 기록 시작!

 

* 삽질 기록

- 기록 보기 귀찮고 사용법만 보실 분들은 아래 CloudFlare R2 플러터 내 사용법을 확인해주세요.

더보기

https://stackoverflow.com/questions/78099780/cloudflare-r2-with-flutter

 

CloudFlare R2 with Flutter

I want to use CloudFlre R2 as storage for my Flutter app. I have AccessKey, SecertKey and r2Token. Unfortunately, I cannot find a package or HTTP request to upload a file to an R2 bucket. I tried u...

stackoverflow.com

첫번째로 봤던 오버플로우 글이었다.

CloudFlare R2를 http 통신으로 사용하고 싶은데 어떻게 써야될 지 묻는 질문이었고

해당 질문에 대해서 http가 아닌 s3 api 패키지를 추천해주며 해당 패키지 내 endpoint를 s3 방식의 url에서 R2 방식의 url로 변경하고 사용해보는 방식을 추천해줬다.

 

"오.. 되겠는데?"

는 개뿔 안되더라

 

https://stackoverflow.com/questions/76766307/flutter-upload-file-using-s3-api

 

Flutter: upload file using s3 api

Im trying to upload a file to Cloudflare's R2 database, which uses the s3 api. Im using this package: https://pub.dev/packages/aws_s3_upload code so far: class StorageRepository { Future<Strin...

stackoverflow.com

두번째로 봤던 오버플로우 글이다.

해당 글에서는 S3 upload 비공식 패키지를 사용해서 전송하려는데 안되더라~ 였는데

결국 답변은 위와 비슷했다 endpoint로 명시된 전송 url를 R2 방식으로 교체하라는 거였는데

 

"오~ 되겠는데?"

는 개뿔 또 안되더라

 

https://stackoverflow.com/questions/73943907/cloud-flare-r2-how-to-upload-images

 

Cloud Flare, R2, how to upload images?

Cloud Flare, R2, how to upload images?? I`m new to Cloud Flare world, and I can upload the pictures by dragging but how to upload image using coding? from application?? do I have to use "WORKERS&

stackoverflow.com

세번째로 봤던 오버플로우 글이다.

해당 글에서는 R2를 Flutter 자체에서 전송하는 방식이 아닌 CloudFlare의 Workers를 이용하여

Workers에서 Image를 업로드하는 방법에 대한 질문이었고 답변으로는 변수에 등록된 버킷을 직접 호출하여 put 메소드를 통해 이미지를 업로드하는 방식을 채택하였다.

별거 아니었던 질문이었지만 해당 글을 보고 생각해낸 형태가 workers를 배포하여 url 호출 방식으로 사용하는 방식이었다.

근데 이 경우 Workers 한도를 생각해야 됐지만 별다른(?) 문제가 없다 판단되어 한번 시도해보았었다.

 

1. Workers 호출 시 전달되는 request 인자 안에 method 값을 취득하여 Put으로 전송되었나 확인

2. 이후 request 인자 내 header 값을 추가로 취득하여 content-type 인자를 확인, 이때 form data 형식으로 전송된건지 확인

3. 두 분기문이 합격 시 전달된 data를 arraybuffer() 메소드를 통해 오픈 후 a 변수에 할당

4. 이후 a 변수를 bucket.put(fileName, a) 를 통해 지정된 버킷 변수에 데이터 저장 시도

5. 저장 완료!

 

근데 이 방식으로 사용하려니 두 가지 문제가 있었는데

1. Workers 한도로 인한 비용 문제

2. 다중 업로드 불가 문제

 

그래도 이때는 플러터로 직접 업로드하는 것이 불가능한걸로 알고 있었으니

2번을 해결하기 위해 다중 업로드를 개발해보기로 했다.

이때 수정했던 사항이

- formData로 받아오던 데이터를 단일이 아닌 각각 'data', 'name' 의 이름의 인자로 전송할 것

- 'name' 은 get() 메소드  사용, 'data' 는 getAll()로 가져올 것

- getAll()을 사용한 데이터는 files 변수에 담아줄 것

- 파일의 리스트를 버퍼화 하기 위해 for문의 of 예약어를 통해 files를 순회하며 arraybuffer()를 변환하자 말자 bucket.put 메소드를 통해 이전에 데이터 저장 시도한대로 저장

 

해당 방식을 통해 Workers 한 번 호출로 여러 데이터를 저장할 수 있게 됐다.

결국 최종 방식이

Workers URL 호출 -> request data parsing -> bucket api 다중 호출 -> 업로드 완료

이다.

그렇게 다중 업로드가 가능해졌다.

근데 가장 큰 문제는 workers 호출 + bucket 쓰기로 인한 두 번 연속 호출이 문제였다.
특히나 workers는 무료제공이지만 하루 제공량 10만건이기에 SNS 같은 대량 업로드 트래픽이 일어나는 서비스에서는 큰 문제로 작용할 수 있었다.

거기에 10만건이 많아보이더라도 100명의 접속자가 하루에 10번만 평균적으로 업로드를 진행한다하면적어도 30일 내에 3만건이 사용되는 것이기에 10만건이 부족해질 수 있는 위험은 충분히 있다.

그리고 더 나아가 api 사용 시 url로 사용되기에 혹여 url 유출이 발생하면 무차별 공격이 들어올 수도 있다.

(물론...그런거 신경쓸거면 클라우드 컴퓨팅 쓰는게 맞지만요..)

 

그래서 해당 방법들은 갖고만 있고 플러터 업로드에 대해 다시 연구해보기로 했다.

 

* CloudFlare R2 플러터 내 사용법

R2 flutter가 없다면 aws flutter를 사용하면 그만이라고 생각을 바꾸었다.

그렇게 인증받은 패키지 중 사용하기 편하고 평점 좋은 아래의 패키지를 발견하였다.

 

https://pub.dev/packages/aws_s3_api

 

aws_s3_api | Dart package

AWS API client for Amazon Simple Storage Service (generated from SDK API specification).

pub.dev

앞서 조사했던 바에 따르면 endpointUrl 을 R2로 변경하여 사용해야된다했는데

해당 패키지는 그럴 필요 없이 그냥 있는 그대로 R2 url를 넣어주면 그만이었다.

 

final credentials = AwsClientCredentials(
    accessKey: '액세스 키',
    secretKey: '시크릿 키',
  );
//액세스 키, 시크릿 키는 CloudFlare 대시보드 -> R2 -> R2 API 토큰 관리에서 생성하거나 이미 있는거 확인

final s3Bucket = S3(
		region: 'auto', //CloudFlare R2는 Region이 따로 없으므로 'auto'로 설정합니다.
		endpointUrl: 'https://{어카운트 ID}.r2.cloudflarestorage.com', // 어카운트ID 양옆 중괄호 제거 필요
        // ex) 'https://d33389409c9019c290c092903cc.r2.cloudflarestorage.com'
		credentials: credentials, // 위에서 만든 인증토큰
    );
    
    
final fileBytes = await {얻어온 파일}.readAsBytes(); // ImagePicker 혹은 File 객체 등을 바이트화(중괄호 제거 필요)
await bucket.putObject(bucket: "버킷이름", key: "원하시는 파일 이름(확장자 포함하셔야됩니다.)", body: fileBytes, contentLength: fileBytes.length);

이제 이렇게 보내고 나면 정상적으로 버킷에 파일 업로드 완료!

 

*주의사항*

1. 확장자 없어도 저장은 되나 문제될 수 있음

2. await 없으면 오류날 수 있음

3. 위에 언급한 대괄호는 꼭 제거할 것

 

클라우드 플레어 R2 스토리지 서비스는 10GB 무료 제공에 쓰기 읽기 용량 가격이 모두 착하다.

CloudFlare R2(무료) 무료 제공량 (매 월)
용량 10GB
쓰기 및 수정 100만건
읽기 1000만건

 

무료 제공량임에도 불구하고 용량을 제외하곤 굉~장~히 착한 가격을 보유하고 있다.

근데? 유료 사용가격 또한 굉장히 저렴하다.

 

아래는 무료 사용량을 모두 사용한 뒤 과금되는 유료 금액 내용이다.

CloudFlare R2(유료) 무료 제공량 사용 후 유료 제공량 (매 월)
용량 1GB 당 0.015달러 (24년9월 기준 약 20원)
쓰기 및 수정 100만건 당 4.5달러 (24년 9월 기준 약 6천원)
읽기 1000만건 당 0.36달러 (24년 9월 기준 약 480원)

 

이렇게 될 경우 100명 동시사용자가 하루 10회씩 30일 기준으로 계산할 때

 

업로드 100명 x 100회 x 30일 = 한 달 약 30만건 사용

읽기 100명 x 1000회 x 30일 = 한 달 약 300만건 사용

 

100명이라는 유저가 여유롭게 서비스를 이용한다해도 이론상

쓰기 및 수정 70만건, 읽기 700만건이 남는다.

여기에 사용량에 대한 오차범위가 발생할 수 있다.

(이론은 항상 실전과는 다르니까)

실제로 api를 원래 호출했던 횟수보다 더 높게 나왔었다.

그렇다면 여기에 여유롭게 각각 20만건, 200만건을 사용했다 치더라도

 

한달 약 쓰기 50만건, 읽기 500만건이 남는다.

이정도면 하나의 서비스가 돌아가기에 굉장히 충분하지 않나?

특히나 토이 프로젝트 용도로는 아주아주 부합한다고 생각한다.

 

다만, R2와 DB를 같이 사용하기 위해서 D1 서비스를 주로 사용해야되는데

이 서비스는 SQLite 방식과 동일한 DB이고 제한이 많다보니

실제 DB 사용하는 서비스와는 동일시하게 여겨지기는 힘든 부분이 다소 존재한다.

필자가 추천하는 방식은 R2 + 다른 DB 혹은 Supabase(Postgresql 기반)이다.

 

다음엔 여기에 백엔드 서비스가 또 필요하다면 약 5천원을 추가로 내고 백엔드도 돌릴 수 있는

CloudType 에 관해 설명해보도록하겠다.

(물론 연습용으로 제한적 무료사용이 가능하다.)

+ Recent posts