이 λΈ”λ‘œκ·ΈλŠ” 넀이버 λΈ”λ‘œκ·Έλ‚˜ ν‹°μŠ€ν† λ¦¬ λ“±μ˜ μ„œλΉ„μŠ€ 없이 운영되고 μžˆλ‹€. 글에 μ²¨λΆ€λ˜λŠ” 이미지듀을 μœ„ν•œ λ³„λ„μ˜ ν΄λΌμš°λ“œ μ„œλΉ„μŠ€ λ˜ν•œ μ‚¬μš©μ€‘μ΄μ§€ μ•Šλ‹€. λΈ”λ‘œκ·Έμ—μ„œ λ³΄μ—¬μ§€λŠ” λͺ¨λ“  μ΄λ―Έμ§€λŠ” PostgreSQL DB에 μ €μž₯되고, 이미지 쑰회 μš”μ²­μ΄ 였면 μ„œλ²„κ°€ DB에 μ €μž₯된 이미지 이진 정보λ₯Ό λΆˆλŸ¬μ™€μ„œ μ‚¬μš©μžμ—κ²Œ 보여주고 μžˆλ‹€.

동기

처음 λΈ”λ‘œκ·Έλ₯Ό μ™„μ„±ν•  λ‹Ήμ‹œμ—λŠ” 이미지 μ—…λ‘œλ“œ κΈ°λŠ₯이 μ—†μ—ˆλ‹€. 이 이미지 μ—…λ‘œλ“œ κΈ°λŠ₯이 μ—†λŠ” 것 λ•Œλ¬Έμ— 글을 μ“Έ λ•Œ μ•„μ‰¬μš΄ 상황이 λ§Žμ•„μ‘Œκ³ , μ΄μ œλŠ” 제발 κ΅¬ν˜„ν•΄λ†“κΈ°λ‘œ κ²°μ •ν–ˆλ‹€.

이 λΈ”λ‘œκ·Έμ˜ 기술 μŠ€νƒμ€ μ•„λž˜μ™€ κ°™λ‹€.

  • Rust lang - ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄
  • axum - Rust 기반 μ›Ή ν”„λ ˆμž„μ›Œν¬
  • AWS Lambda - μ»΄ν“¨νŒ… μΈν”„λΌμŠ€νŠΈλŸ­μ³
  • PostgreSQL - DB
  • SvelteKit - Svelte 기반 μ›Ή ν”„λ ˆμž„μ›Œν¬
  • Cloudflare Pages - 무료 μ›Ή ν˜ΈμŠ€νŒ… μ„œλΉ„μŠ€
  • Supabase - BaaS & 무료 PostgreSQL ν˜ΈμŠ€νŒ… μ„œλΉ„μŠ€

λ‚˜λŠ” 이 기술 μŠ€νƒμ„ 변함 없이 μœ μ§€ν•˜κ³  μ‹Άμ—ˆλ‹€. 무언가 μ„œλΉ„μŠ€λ₯Ό λ”ν•˜κΈ°λ„ μ‹«μ—ˆλ‹€. 일단 μ‹œλ„ν•΄λ΄€λ‹€.

메인 아이디어: DB에 이미지λ₯Ό μ €μž₯ν•˜μž

DBλŠ” 데이터λ₯Ό μ˜€λž«λ™μ•ˆ μ €μž₯ν•˜λŠ” 역할을 ν•œλ‹€. μ‹€λ¬΄μ—μ„œλŠ” DB에 μˆ˜μΉ˜κ°’μ΄λ‚˜ 이메일 정보와 같은 숫자, ν…μŠ€νŠΈ 정보λ₯Ό μ €μž₯ν•˜κ³€ ν•œλ‹€.

보톡 이미지 νŒŒμΌμ€ DBκ°€ μ•„λ‹Œ λ‹€λ₯Έ 곳에 μ €μž₯λœλ‹€. 예λ₯Ό λ“€λ©΄ NASλ‚˜ ν΄λΌμš°λ“œ μŠ€ν† λ¦¬μ§€μ— μ €μž₯되곀 ν•œλ‹€. κ΄€λ¦¬μ˜ μš©μ΄μ„±κ³Ό 데이터 성격에 λ”°λ₯Έ 뢄리 λ•Œλ¬Έμ΄λ‹€.

그런데 λ‚˜λŠ”, μˆ«μžλ‚˜ ν…μŠ€νŠΈλ‚˜ μ΄λ―Έμ§€λ‚˜ κ²°κ΅­ 0κ³Ό 1둜 이루어진 이진 데이터라고 μƒκ°ν–ˆλ‹€. κ·Έλž˜μ„œ κ·Έλƒ₯ DB에 μ „λΆ€ λ³΄κ΄€ν•΄λ³΄κΈ°λ‘œ ν–ˆλ‹€.

기술적으둜 μ‚΄νŽ΄λ³΄λ‹ˆ, μ§€κΈˆ μ‚¬μš©μ€‘μΈ DB인 PostgreSQLμ—μ„œ 이진 정보 μ €μž₯을 μ§€μ›ν•œλ‹€λŠ” 사싀을 ν™•μΈν–ˆλ‹€. 이게 λœλ‹€λ©΄ 이미지 μ €μž₯μ΄λ‚˜ 더 λ‚˜μ•„κ°€μ„œ 일반 파일 μ €μž₯도 κ°€λŠ₯ν•΄μ§„λ‹€.

ν…Œμ΄λΈ” 섀계

νŒŒμΌμ„ μ €μž₯ν•˜κΈ° μœ„ν•œ ν…Œμ΄λΈ”μ„ λ‹€μŒκ³Ό 같이 μ„€κ³„ν–ˆλ‹€.

  CREATE TABLE public.post_attachment (
    id UUID NOT NULL,
    content BYTEA NOT NULL,
    written_by_id INTEGER NOT NULL,
    file_extension VARCHAR(20) NOT NULL DEFAULT '',
    mimetype VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);
  • id: μΈν„°λ„·μ—μ„œ νŒŒμΌμ— μ ‘κ·Όν•˜κΈ° μœ„ν•œ URL. UUID ν˜•μ‹μ΄λ©°, URL에 λ‹€μŒκ³Ό 같이 ν• λ‹Ήλœλ‹€. https://blog.atj.sh/post-attachments/v/0194e521-339c-7e10-b205-a2a9416fdb97
  • content: 파일의 이진 정보.
  • written_by_id: νŒŒμΌμ„ μ—…λ‘œλ“œν•œ μ‚¬λžŒμ˜ ID.
  • file_extension: 파일의 ν™•μž₯자. 파일이 μ—…λ‘œλ“œλ  λ•Œ 파일의 μ΄λ¦„μ—μ„œ 가져와 DB에 μ €μž₯ν•œλ‹€. 파일의 μ›λž˜ 이름은 μ €μž₯λ˜μ§€ μ•Šκ³  λŒ€μ‹  ν™•μž₯자만 μ €μž₯λœλ‹€.
  • mimetype: 파일의 mimetype. 파일이 μ—…λ‘œλ“œλ  λ•Œ Blob.type() λ©”μ†Œλ“œλ₯Ό 톡해 κ°€μ Έμ™€μ Έμ„œ DB에 μ €μž₯λœλ‹€.

μ—…λ‘œλ“œ μ‹œλ‚˜λ¦¬μ˜€

λ‚΄κ°€ 글을 μ“°λ©° 이미지λ₯Ό 첨뢀할 λ•Œ μ„œλ²„μ—μ„œ μ΄λ£¨μ–΄μ§€λŠ” μž‘μ—…μ΄λ‹€.

이미지 μ••μΆ•μ˜ κ²½μš°μ—λŠ” μ›λž˜ CloudFlare Pages μ„œλ²„λ‹¨μ—μ„œ μ²˜λ¦¬λ˜λ„λ‘ ν•˜λ €κ³  ν–ˆλŠ”λ°, 이미지 압좕에 ν•΅μ‹¬μ μœΌλ‘œ ν•„μš”ν•œ Canvas APIκ°€ μ§€μ›λ˜μ§€ μ•Šμ•„μ„œ κ΄€λ’€κ³ , AWS Lambdaμ—μ„œ μ²˜λ¦¬ν•˜κΈ°μ—λŠ” ν—ˆμš© μš”μ²­ 크기가 6MB μˆ˜μ€€μ΄λΌμ„œ μš”μ²­μ΄ κ±°λΆ€λ˜λŠ” 상황이 λ°œμƒν•  κ²ƒμœΌλ‘œ μ˜ˆμƒν–ˆλ‹€.

λ”°λΌμ„œ λ„€νŠΈμ›Œν¬ μš”μ²­μ„ 타기 전에 λΈŒλΌμš°μ € μ•ˆμ—μ„œ 이미지 압좕을 미리 μ§„ν–‰ν•˜λ„λ‘ κ΅¬ν˜„ν–ˆλ‹€. κ²°λ‘ μ μœΌλ‘œλŠ” λ„€νŠΈμ›Œν¬ νŠΈλž˜ν”½μ΄ μ ˆμ•½λ˜κ³  DB에 μ €μž₯λ˜λŠ” λ°μ΄ν„°μ˜ 크기 λ˜ν•œ μž‘μ•„μ‘Œλ‹€.

μ••μΆ• μ•Œκ³ λ¦¬μ¦˜μ€ WebP둜 μ••μΆ•ν•˜λŠ” 방법을 μ‚¬μš©ν–ˆλŠ”λ°, 12MB 이미지λ₯Ό 100KB μ •λ„μ˜ 크기둜 μ••μΆ•ν•  μ •λ„λ‘œ 효율이 μ’‹μ•˜λ‹€.

쑰회 μ‹œλ‚˜λ¦¬μ˜€

λ°©λ¬Έμžκ°€ 이미지λ₯Ό λ³Ό λ•Œ μ„œλ²„μ—μ„œ μ΄λ£¨μ–΄μ§€λŠ” μž‘μ—…μ΄λ‹€.

CloudFlare Pagesμ—μ„œ 자체적으둜 μ§€μ›λ˜λŠ” Cache APIλ₯Ό μ μš©ν–ˆλ‹€. 덕뢄에 DB 쑰회둜 μΈν•œ λΆ€ν•˜κ°€ μ‘°κΈˆμ΄λ‚˜λ§ˆ μ€„μ–΄λ“€μ—ˆλ‹€. λ˜ν•œ λΈŒλΌμš°μ €μ—μ„œ μ•Œμ•„μ„œ 캐싱할 수 μžˆλ„λ‘ Cache-Control 헀더λ₯Ό μ μš©ν–ˆλ‹€.

이미지 νŒŒμΌμ„ μ œκ³΅ν•  λ•Œ λΈŒλΌμš°μ €μ—μ„œ μžμ—°μŠ€λŸ½κ²Œ 제곡될 수 μžˆλ„λ‘, μ΄λ―Έμ§€μ˜ ν˜•μ‹μ„ HTTP 응닡 헀더에 μ •ν™•ν•˜κ²Œ κΈ°μž…ν•˜λŠ” μž‘μ—…μ΄ ν•„μš”ν–ˆλ‹€. 이미지가 μ„œλ²„μ—μ„œ 쑰회될 λ•Œ μ΄λ―Έμ§€μ˜ 메타데이터가 HTTP 응닡 헀더에 잘 λ‹΄κ²¨μ„œ μ „λ‹¬λ˜λ„λ‘ κ΅¬ν˜„ν•˜μ˜€λ‹€.
κ°œμΈμ μœΌλ‘œλŠ” 이 μž‘μ—…μ„ ν•˜λ©΄μ„œ HTTP Mimetypeκ³Ό 이미지 ν™•μž₯자의 차이λ₯Ό μ’€ 더 λͺ…ν™•ν•˜κ²Œ μ•Œ 수 μžˆμ—ˆλ‹€.

κ²°λ‘ : κ΅¬ν˜„μ— μ„±κ³΅ν–ˆμ§€λ§Œ κ°œμ„ μ˜ μ—¬μ§€λŠ” 있음

이 κΈ€μ—μ„œ μ‚¬μš©λœ 이미지듀은 λͺ¨λ‘ μœ„ λ‘œμ§μ„ 톡해 DBλ‘œλΆ€ν„° μ‘°νšŒλ˜μ–΄ λΈŒλΌμš°μ €μ— λ Œλ”λ§λœ 것이닀. 이미지듀이 잘 보인닀면 κ΅¬ν˜„μ΄ μ•ˆμ •μ μœΌλ‘œ μ™„λ£Œλ˜μ—ˆλ‹€λŠ” 뜻일 것이닀. λ§Œμ•½ 이미지가 μ•ˆ 보인닀면, μ€‘κ°„μ—μ„œ 미처 발견 λͺ»ν•œ 버그가 μžˆμ–΄ λΈŒλΌμš°μ €κΉŒμ§€ 이미지가 λ„λ‹¬ν•˜μ§€ λͺ»ν•œ κ²ƒμœΌλ‘œ 보면 λœλ‹€.

HTTP 캐싱을 μ μš©ν•˜κΈ΄ ν–ˆμ§€λ§Œ μ™„λ²½νžˆ νŒŒμ•…ν•˜μ§„ λͺ»ν•΄μ„œ μΌλΆ€λΆ„λ§Œ κ΅¬ν˜„ν–ˆλ‹€. κΈ°νšŒκ°€ 되면 더 μ™„μ „ν•˜κ²Œ κ΅¬ν˜„ν•˜κ³  μ‹Άλ‹€.

1MB 크기보닀 μž‘μ€ 이미지 μ œκ³΅μ€ μ„±κ³΅ν–ˆμ§€λ§Œ, 그보닀 큰 μ΄λ―Έμ§€λ‚˜ λŒ€μš©λŸ‰ 파일 μ œκ³΅μ€ μœ„μ—μ„œ μ„€λͺ…ν•œ 바와 같이 μ—¬λŸ¬ μ œν•œ λ•Œλ¬Έμ— νž˜λ“€λ‹€. 심지어 10MB μ •λ„μ˜ 파일 μ œκ³΅λ„ μ§€κΈˆμ€ λΆˆκ°€λŠ₯ν•œ 상황이닀. μ΄λ―Έμ§€μ˜ κ²½μš°μ—λŠ” WebP 압좕이 μ›Œλ‚™ 효율적이라 λΆˆνŽΈν•œ 점이 μ—†λŠ” μƒν™©μ΄μ§€λ§Œ, λ‹€λ₯Έ 파일의 κ²½μš°μ—λŠ” μš©λŸ‰ μ œν•œμ΄ 크게 λ‹€κ°€μ˜¬ 수 밖에 μ—†λ‹€.

기술 μŠ€νƒμ„ μœ μ§€ν•˜λŠ” λ™μ‹œμ— 더 큰 νŒŒμΌμ„ μ„œλΉ™ν•˜κΈ° μœ„ν•œ 방법을 연ꡬ해봐야겠닀.

μ†ŒμŠ€μ½”λ“œ

이 λΈ”λ‘œκ·Έμ˜ μ†ŒμŠ€μ½”λ“œλŠ” μ˜€ν”ˆμ†ŒμŠ€μ΄λ‹€. λ‚΄ GitHubμ—μ„œ 확인 κ°€λŠ₯ν•˜λ‹€.

이번 κΈ°λŠ₯이 μΆ”κ°€λœ 컀밋 내역은 λ‹€μŒ λ§ν¬μ—μ„œ 확인 κ°€λŠ₯ν•˜λ‹€. atjsh/rust-blog - 89958c...ad708e

ꡉμž₯히 만쑱

거의 2달간 미룬 μž‘μ—…μΈλ° ν•˜λ£¨λ§Œμ— κ΅¬ν˜„μ„ λλƒˆλ‹€. λ“œλ””μ–΄ λλ‚΄μ„œ 속이 ν›„λ ¨ν•˜λ‹€.