Hyunseok
프로그래밍/개인홈페이지 [BBLOG] Cloudflare 의 R2 적용시켜보자 with Nextjs
2023. 12. 10. 20:41

 

https://github.com/B-HS/BBlog

 

GitHub - B-HS/BBlog: https://hyns.dev - React+Nextjs/Spring boot 블로그 프로젝트

https://hyns.dev - React+Nextjs/Spring boot 블로그 프로젝트 - GitHub - B-HS/BBlog: https://hyns.dev - React+Nextjs/Spring boot 블로그 프로젝트

github.com

오늘도 열심히 블로그 삽질을 하는 나이다 

 

최근에 nextjs14에 오면서 생각보다

사용자 친화적인 프레임워크로 조금 더 나아가는 게 아닌가..라는 생각을 가지며 

 

오늘도 블로그를 작성하고있었다 

 

오늘의 난제는 이하와 같다 

 

"파일/이미지 등을 어떻게 영구적으로 안전하게 cdn이 인식하고 빠르게 로딩할 수 있을까"

 

라는 생각

 

당장 저 생각을 하자마자 

" ㅋㅋ  aws s3에 cloudflare 입히면 바로 되는 거 아닌가 ? " 생각을 했는데 

 

알다시피 12개월 프리티어 계정이 아니다면  s3는 무료가 아니다 

 

그렇게 여러 과정을 찾기 시작했다 

 

가장 먼저 눈에 들어온게 aws의 dynamodb

 

 

25기가의 방대한 DB무료 용량을 주니 이 놈을 이용해서 blob데이터를 냅다 쑤셔 박아서 이미지/파일 서버로 쓰면 적당하지 않을까..? 했지만..

 

 

예스.. 400kb 이상은 올리지 못한다

 

대략 blob데이터가 기존 데이터보다 조금 더 많은 용량을 차지하니 이미지 기준 대충 250kb ~ 300kb라는 것인데 

 

이건 내가 알기로는 sd화질의 저용량 사진정도가 적당히 오르고 내릴 수 있는 수준이다 

 

고로 대상에서는 제외..

 

그런 와중에.. "아오 그냥 S3 쓸까.. 이거 비슷한 것은 없나" 하는 찰나 눈에 들어온 게 있다 

 

 

바로.. 도메인 2개가 항상 신세 지고 있는 클라우드플레어의.. 희소식이다 

 

 

무려 10기가의 용량과 미친듯한 트래픽 (무제한)을 제공 + S3와 완벽한 호환..

(심지어 그냥 aws s3라이브러리를 냅다 호환가능하다고 홍보하고 있다 )

 

 

바로 이 녀석 고르고 시작했다 

 

 

토큰 생성

일단 내가 사용하는 환경은 nextjs14.. 를 쓰고 있는데 사실 어떤 node기반의 어플이라면 따라서 쓸 수 있지 않을까 싶다

 

먼저 R2에서 토큰을 생성한다 

 

 

 

그냥 R2에서 우측에 있는 Manage R2 API Tokens... 비슷하게 신규 작성하는 게 있을 텐데 여기서 그냥 토큰 정보를 다 가져오자 

만들고 화면에 있는 create bucket으로 내가 사용할 스토리지 구간도 정해주자 

 

 R2를 사용하기 위한 router,  client 작성

 

일단 R2의 클라이언트 설정 파일부터 빠르게 작성해 보자 

 

import { S3Client } from '@aws-sdk/client-s3'

export const R2Client = new S3Client({
    region: 'auto',
    endpoint: process.env.NEXT_R2_END_POINT || '',
    credentials: {
        accessKeyId: process.env.NEXT_R2_ACCESS_KEY_ID || '',
        secretAccessKey: process.env.NEXT_R2_SECRET_ACCESS_KEY || '',
    },
})

 

 

 키들은 안 올라가도록 해야 하니.. 일단. env파일로 다 작성해 주었다 

 

토큰 생성 페이지에서 알 수 있는 정보들 중 아래의 3개만 있으면 사용이 가능하다 

 

1. 엔드포인트용 주소 

2. 액세스 키 아이디

3. 암호용 액세스키 

 

그리고 R2는 리전을 자동으로 선택해서 CDN에 냅다 올려버리기 때문에 region은 설정 안 해도 된다 (그냥 auto로 기입해 주자 )

 

그리고 이 녀석을 써야 하는데..

 

이번에는 nextjs의 router handler를 사용해보려 한다 

https://nextjs.org/docs/app/building-your-application/routing/route-handlers

 

Routing: Route Handlers | Next.js

Create custom request handlers for a given route using the Web's Request and Response APIs.

nextjs.org

 

사실 선호하는 방식은 아니지만 현재 프로젝트에서 client side의 axios는

Provider로 냅다 감싸놓은 상태라서 무조건 host경로가 백엔드의 주소로 가게끔 되어있다 

 

여하튼.. 그러한 이유로 그냥 여기서 fetch로 정해진 주소로 보내게끔

api를 작성한다는 생각으로 그냥  route-handler를 사용해보려 한다

 

아래는 app 폴더 아래의 api 폴더 > img의 upload의 라우터에 해당하는 route.ts에 해당하는 ts 파일의 코드이다 

 

 

import { PutObjectCommand } from '@aws-sdk/client-s3'
import { NextRequest, NextResponse } from 'next/server'
import { R2Client } from '../../r2client'

const uploadImg = async (file: Buffer, fileName: string) => {
    const fileBuffer: Buffer = file
    const command = new PutObjectCommand({
        Bucket: process.env.NEXT_R2_BUCKET_NAME,
        Key: `${fileName}`,
        Body: fileBuffer,
        ContentType: 'image/jpg',
    })
    await R2Client.send(command)
    return fileName
}

export const POST = async (request: NextRequest) => {
    try {
        const form = await request.formData()
        const file = form.get('file') as File
        if (!file) return NextResponse.json({ error: 'File is required.' })
        const buffer = Buffer.from(await file.arrayBuffer())
        const fileName = await uploadImg(buffer, file.name)

        return NextResponse.json(fileName)
    } catch (error: any) {
        return NextResponse.json({ error: error.message })
    }
}

 

 

아까 작성해 두었던 R2의 client파일을 가져와서 설정파일로 사용하고 

s3의 PutObjectComand를 사용하여 R2의 해당하는  Bucket에서 해당 파일을 가져온다 

 

해당 라우트 (/api/image/upload)의  POST는 위와 같은 로직을 타게 되며 또한  Response값도 json으로 반환 가능하게 된다 

 

 

사실 이렇게 만들면 끝이다 

 

이제 내가 쓰고 싶은 곳에 가서 기존 파일업로드와 비슷하게 로직을 작성하면 된다 

 

코드는

https://github.com/B-HS/BBlog/blob/main/FRONT/src/components/article/commentRegisterInput.tsx

 

이 파일을 참조하면 되는데 막상 바로 보면 이해가 가지 않으니 대충 설명해 보자면 

 

1. button과 ref로 엮은 가상의 input을 설정, button에 onclick으로 inpu의 click이벤트를 바인딩해 준다 

 

2. input (type = "file")의 내용이 onchange상태가 들어오면 이 상태를 handler로 조정한다 

 

3. event값은 form값을 그대로 가지고 있는 형태, 냅다 e.target.file로 파일을 추출해 낸다 

 

4. 백 단으로 보내기 위해 FormData생성,  및 생성된 FormData에 파일을 set 해준다 

 

5. set 된 FormData를 아까 작성한 route로 날린다 (/api/image/upload)

 

6. 업로드로 작성된 라우트는 FormData를 가지고 S3클라이언트(R2 Client)를 이용하여 서버에 파일을 저장, Response값을 리턴

 

7. Response값을 기준으로 원하는 상태값을 조정

 

 

매우 간단하고 속도도 빠르고..  무엇보다 범용으로 쓸 수 있다는 점이 매우 매력적이다 

 

심지어 10기가면.. Raw파일이 아닌 사진만 올린다는 가정하에 거의 평생 쓸 수 있지 않을까 싶기도 하고..

 

게다가 Cloudflare의 미친 cdn속도는 명성이 자자하다 

 

이렇게 간단하게 R2 업로드를 구현해 보았는데.. 역시 인터넷에는 찾으면 찾을수록 좋은 게 많이 나온다 

 

다음에는 서버 이전 작업이 남아있는데.. 이전하는 겸 헤드레스 홈페이지는 cloudflare page로 옮기는 작업을 해보려 한다 

 

물론.. 작업하면서 블로그 글도 작성해 볼 예정이다 

 

다들 즐거운 개발 해보자 

 


프로그래밍/개인홈페이지의 다른 글