<img src="https://blog.atj.sh/post-attachments/v/0194e507-718f-7380-8cb2-cb96a164684c" width="100%"/>
<br />
<p>
์ด ๋ธ๋ก๊ทธ๋ ๋ค์ด๋ฒ ๋ธ๋ก๊ทธ๋ ํฐ์คํ ๋ฆฌ ๋ฑ์ ์๋น์ค ์์ด ์ด์๋๊ณ ์๋ค. ๊ธ์ ์ฒจ๋ถ๋๋ ์ด๋ฏธ์ง๋ค์ ์ํ ๋ณ๋์ ํด๋ผ์ฐ๋ ์๋น์ค ๋ํ ์ฌ์ฉ์ค์ด์ง ์๋ค. ๋ธ๋ก๊ทธ์์ ๋ณด์ฌ์ง๋ ๋ชจ๋ ์ด๋ฏธ์ง๋ PostgreSQL DB์ ์ ์ฅ๋๊ณ , ์ด๋ฏธ์ง ์กฐํ ์์ฒญ์ด ์ค๋ฉด ์๋ฒ๊ฐ DB์ ์ ์ฅ๋ ์ด๋ฏธ์ง ์ด์ง ์ ๋ณด๋ฅผ ๋ถ๋ฌ์์ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ฃผ๊ณ ์๋ค.
</p>
<h2>
๋๊ธฐ
</h2>
<p>
์ฒ์ ๋ธ๋ก๊ทธ๋ฅผ ์์ฑํ ๋น์์๋ ์ด๋ฏธ์ง ์
๋ก๋ ๊ธฐ๋ฅ์ด ์์๋ค. ์ด ์ด๋ฏธ์ง ์
๋ก๋ ๊ธฐ๋ฅ์ด ์๋ ๊ฒ ๋๋ฌธ์ ๊ธ์ ์ธ ๋ ์์ฌ์ด ์ํฉ์ด ๋ง์์ก๊ณ , ์ด์ ๋ ์ ๋ฐ ๊ตฌํํด๋๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
</p>
<p>
์ด ๋ธ๋ก๊ทธ์ ๊ธฐ์ ์คํ์ ์๋์ ๊ฐ๋ค.
</p>
<ul>
<li><a href="https://www.rust-lang.org/" target="_blank">Rust lang</a> - ํ๋ก๊ทธ๋๋ฐ ์ธ์ด</li>
<li><a href="https://github.com/tokio-rs/axum"target="_blank">axum</a> - Rust ๊ธฐ๋ฐ ์น ํ๋ ์์ํฌ</li>
<li><a href="https://aws.amazon.com/ko/lambda/"target="_blank">AWS Lambda</a> - ์ปดํจํ
์ธํ๋ผ์คํธ๋ญ์ณ</li>
<li><a href="https://postgresql.org/"target="_blank">PostgreSQL</a> - DB</li>
<li><a href="https://kit.svelte.dev/"target="_blank">SvelteKit</a> - Svelte ๊ธฐ๋ฐ ์น ํ๋ ์์ํฌ</li>
<li><a href="https://pages.cloudflare.com/"target="_blank">Cloudflare Pages</a> - ๋ฌด๋ฃ ์น ํธ์คํ
์๋น์ค</li>
<li><a href="https://supabase.com/"target="_blank">Supabase</a> - BaaS & ๋ฌด๋ฃ PostgreSQL ํธ์คํ
์๋น์ค</li>
</ul>
<br />
<p>
๋๋ ์ด ๊ธฐ์ ์คํ์ ๋ณํจ ์์ด ์ ์งํ๊ณ ์ถ์๋ค. ๋ฌด์ธ๊ฐ ์๋น์ค๋ฅผ ๋ํ๊ธฐ๋ ์ซ์๋ค. ์ผ๋จ ์๋ํด๋ดค๋ค.
</p>
<h2>
๋ฉ์ธ ์์ด๋์ด: DB์ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ์
</h2>
<p>
DB๋ ๋ฐ์ดํฐ๋ฅผ ์ค๋ซ๋์ ์ ์ฅํ๋ ์ญํ ์ ํ๋ค. ์ค๋ฌด์์๋ DB์ ์์น๊ฐ์ด๋ ์ด๋ฉ์ผ ์ ๋ณด์ ๊ฐ์ ์ซ์, ํ
์คํธ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณค ํ๋ค.
</p>
<p>
๋ณดํต ์ด๋ฏธ์ง ํ์ผ์ DB๊ฐ ์๋ ๋ค๋ฅธ ๊ณณ์ ์ ์ฅ๋๋ค. ์๋ฅผ ๋ค๋ฉด NAS๋ ํด๋ผ์ฐ๋ ์คํ ๋ฆฌ์ง์ ์ ์ฅ๋๊ณค ํ๋ค. ๊ด๋ฆฌ์ ์ฉ์ด์ฑ๊ณผ ๋ฐ์ดํฐ ์ฑ๊ฒฉ์ ๋ฐ๋ฅธ ๋ถ๋ฆฌ ๋๋ฌธ์ด๋ค.
</p>
<p>
๊ทธ๋ฐ๋ฐ ๋๋, ์ซ์๋ ํ
์คํธ๋ ์ด๋ฏธ์ง๋ ๊ฒฐ๊ตญ 0๊ณผ 1๋ก ์ด๋ฃจ์ด์ง ์ด์ง ๋ฐ์ดํฐ๋ผ๊ณ ์๊ฐํ๋ค. ๊ทธ๋์ ๊ทธ๋ฅ DB์ ์ ๋ถ ๋ณด๊ดํด๋ณด๊ธฐ๋ก ํ๋ค.
</p>
<p>
๊ธฐ์ ์ ์ผ๋ก ์ดํด๋ณด๋, ์ง๊ธ ์ฌ์ฉ์ค์ธ DB์ธ <a href="https://www.postgresql.org/docs/current/datatype-binary.html" target="_blank">PostgreSQL์์ ์ด์ง ์ ๋ณด ์ ์ฅ์ ์ง์</a>ํ๋ค๋ ์ฌ์ค์ ํ์ธํ๋ค. ์ด๊ฒ ๋๋ค๋ฉด ์ด๋ฏธ์ง ์ ์ฅ์ด๋ ๋ ๋์๊ฐ์ ์ผ๋ฐ ํ์ผ ์ ์ฅ๋ ๊ฐ๋ฅํด์ง๋ค.
</p>
<h2>
ํ
์ด๋ธ ์ค๊ณ
</h2>
<p>
ํ์ผ์ ์ ์ฅํ๊ธฐ ์ํ ํ
์ด๋ธ์ ๋ค์๊ณผ ๊ฐ์ด ์ค๊ณํ๋ค.
</p>
<pre>
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)
);
</pre>
<ul>
<li>id: ์ธํฐ๋ท์์ ํ์ผ์ ์ ๊ทผํ๊ธฐ ์ํ URL. UUID ํ์์ด๋ฉฐ, URL์ ๋ค์๊ณผ ๊ฐ์ด ํ ๋น๋๋ค. <code>https://blog.atj.sh/post-attachments/v/<b>0194e521-339c-7e10-b205-a2a9416fdb97</b></code></li>
<li>content: ํ์ผ์ ์ด์ง ์ ๋ณด.</li>
<li>written_by_id: ํ์ผ์ ์
๋ก๋ํ ์ฌ๋์ ID.</li>
<li>file_extension: ํ์ผ์ ํ์ฅ์. ํ์ผ์ด ์
๋ก๋๋ ๋ ํ์ผ์ ์ด๋ฆ์์ ๊ฐ์ ธ์ DB์ ์ ์ฅํ๋ค. ํ์ผ์ ์๋ ์ด๋ฆ์ ์ ์ฅ๋์ง ์๊ณ ๋์ ํ์ฅ์๋ง ์ ์ฅ๋๋ค.</li>
<li>mimetype: ํ์ผ์ mimetype. ํ์ผ์ด ์
๋ก๋๋ ๋ <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob#instance_properties"><code>Blob.type()</code></a> ๋ฉ์๋๋ฅผ ํตํด ๊ฐ์ ธ์์ ธ์ DB์ ์ ์ฅ๋๋ค.</li>
</ul>
<h2>
์
๋ก๋ ์๋๋ฆฌ์ค
</h2>
<p>
๋ด๊ฐ ๊ธ์ ์ฐ๋ฉฐ ์ด๋ฏธ์ง๋ฅผ ์ฒจ๋ถํ ๋ ์๋ฒ์์ ์ด๋ฃจ์ด์ง๋ ์์
์ด๋ค.
</p>
<img src="https://blog.atj.sh/post-attachments/v/0194e521-339c-7e10-b205-a2a9416fdb97" class="full-unrounded"/>
<p>
์ด๋ฏธ์ง ์์ถ์ ๊ฒฝ์ฐ์๋ ์๋ CloudFlare Pages ์๋ฒ๋จ์์ ์ฒ๋ฆฌ๋๋๋ก ํ๋ ค๊ณ ํ๋๋ฐ, <a href="https://community.cloudflare.com/t/rendering-svg-using-canvas-with-cloudflare-workers/90952" target="_blank">์ด๋ฏธ์ง ์์ถ์ ํต์ฌ์ ์ผ๋ก ํ์ํ Canvas API๊ฐ ์ง์๋์ง ์์์ ๊ด๋๊ณ </a>, AWS Lambda์์ ์ฒ๋ฆฌํ๊ธฐ์๋ <a href="https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html" target="_blank">ํ์ฉ ์์ฒญ ํฌ๊ธฐ๊ฐ 6MB ์์ค</a>์ด๋ผ์ ์์ฒญ์ด ๊ฑฐ๋ถ๋๋ ์ํฉ์ด ๋ฐ์ํ ๊ฒ์ผ๋ก ์์ํ๋ค.
</p>
<p>
๋ฐ๋ผ์ ๋คํธ์ํฌ ์์ฒญ์ ํ๊ธฐ ์ ์ ๋ธ๋ผ์ฐ์ ์์์ ์ด๋ฏธ์ง ์์ถ์ ๋ฏธ๋ฆฌ ์งํํ๋๋ก ๊ตฌํํ๋ค. ๊ฒฐ๋ก ์ ์ผ๋ก๋ ๋คํธ์ํฌ ํธ๋ํฝ์ด ์ ์ฝ๋๊ณ DB์ ์ ์ฅ๋๋ ๋ฐ์ดํฐ์ ํฌ๊ธฐ ๋ํ ์์์ก๋ค.
</p>
<p>
์์ถ ์๊ณ ๋ฆฌ์ฆ์ <a href="https://ko.wikipedia.org/wiki/WebP">WebP๋ก ์์ถํ๋ ๋ฐฉ๋ฒ</a>์ ์ฌ์ฉํ๋๋ฐ, 12MB ์ด๋ฏธ์ง๋ฅผ 100KB ์ ๋์ ํฌ๊ธฐ๋ก ์์ถํ ์ ๋๋ก ํจ์จ์ด ์ข์๋ค.
</p>
<h2>
์กฐํ ์๋๋ฆฌ์ค
</h2>
<p>
๋ฐฉ๋ฌธ์๊ฐ ์ด๋ฏธ์ง๋ฅผ ๋ณผ ๋ ์๋ฒ์์ ์ด๋ฃจ์ด์ง๋ ์์
์ด๋ค.
</p>
<img src="https://blog.atj.sh/post-attachments/v/0194e521-2c4a-71d3-bb0b-fcf2bb5419c4" class="full-unrounded"/>
<p>
CloudFlare Pages์์ ์์ฒด์ ์ผ๋ก ์ง์๋๋ <a href="https://developers.cloudflare.com/workers/runtime-apis/cache/">Cache API</a>๋ฅผ ์ ์ฉํ๋ค. ๋๋ถ์ DB ์กฐํ๋ก ์ธํ ๋ถํ๊ฐ ์กฐ๊ธ์ด๋๋ง ์ค์ด๋ค์๋ค. ๋ํ ๋ธ๋ผ์ฐ์ ์์ ์์์ ์บ์ฑํ ์ ์๋๋ก <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control" target="_blank">Cache-Control ํค๋</a>๋ฅผ ์ ์ฉํ๋ค.
</p>
<img src="https://blog.atj.sh/post-attachments/v/0194e539-fa72-7c10-a22e-0d1a2bf03c76" width="100%" />
<p>
์ด๋ฏธ์ง ํ์ผ์ ์ ๊ณตํ ๋ ๋ธ๋ผ์ฐ์ ์์ ์์ฐ์ค๋ฝ๊ฒ ์ ๊ณต๋ ์ ์๋๋ก, ์ด๋ฏธ์ง์ ํ์์ HTTP ์๋ต ํค๋์ ์ ํํ๊ฒ ๊ธฐ์
ํ๋ ์์
์ด ํ์ํ๋ค. ์ด๋ฏธ์ง๊ฐ ์๋ฒ์์ ์กฐํ๋ ๋ <a href="https://github.com/atjsh/rust-blog/blob/ad708e876d7def1683e282fe13576fa14f6b7e94/server/src/routers/post_attachment.rs#L44" target="_blank">์ด๋ฏธ์ง์ ๋ฉํ๋ฐ์ดํฐ๊ฐ HTTP ์๋ต ํค๋์ ์ ๋ด๊ฒจ์ ์ ๋ฌ๋๋๋ก ๊ตฌํ</a>ํ์๋ค. <br /> ๊ฐ์ธ์ ์ผ๋ก๋ ์ด ์์
์ ํ๋ฉด์ HTTP Mimetype๊ณผ ์ด๋ฏธ์ง ํ์ฅ์์ ์ฐจ์ด๋ฅผ ์ข ๋ ๋ช
ํํ๊ฒ ์ ์ ์์๋ค.
</p>
<h2>๊ฒฐ๋ก : ๊ตฌํ์ ์ฑ๊ณตํ์ง๋ง ๊ฐ์ ์ ์ฌ์ง๋ ์์</h2>
<p>
์ด ๊ธ์์ ์ฌ์ฉ๋ ์ด๋ฏธ์ง๋ค์ ๋ชจ๋ ์ ๋ก์ง์ ํตํด DB๋ก๋ถํฐ ์กฐํ๋์ด ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง๋ ๊ฒ์ด๋ค. ์ด๋ฏธ์ง๋ค์ด ์ ๋ณด์ธ๋ค๋ฉด ๊ตฌํ์ด ์์ ์ ์ผ๋ก ์๋ฃ๋์๋ค๋ ๋ป์ผ ๊ฒ์ด๋ค. ๋ง์ฝ ์ด๋ฏธ์ง๊ฐ ์ ๋ณด์ธ๋ค๋ฉด, ์ค๊ฐ์์ ๋ฏธ์ฒ ๋ฐ๊ฒฌ ๋ชปํ ๋ฒ๊ทธ๊ฐ ์์ด ๋ธ๋ผ์ฐ์ ๊น์ง ์ด๋ฏธ์ง๊ฐ ๋๋ฌํ์ง ๋ชปํ ๊ฒ์ผ๋ก ๋ณด๋ฉด ๋๋ค.
</p>
<p>
HTTP ์บ์ฑ์ ์ ์ฉํ๊ธด ํ์ง๋ง ์๋ฒฝํ ํ์
ํ์ง ๋ชปํด์ ์ผ๋ถ๋ถ๋ง ๊ตฌํํ๋ค. ๊ธฐํ๊ฐ ๋๋ฉด ๋ ์์ ํ๊ฒ ๊ตฌํํ๊ณ ์ถ๋ค.
</p>
<p>
1MB ํฌ๊ธฐ๋ณด๋ค ์์ ์ด๋ฏธ์ง ์ ๊ณต์ ์ฑ๊ณตํ์ง๋ง, ๊ทธ๋ณด๋ค ํฐ ์ด๋ฏธ์ง๋ ๋์ฉ๋ ํ์ผ ์ ๊ณต์ ์์์ ์ค๋ช
ํ ๋ฐ์ ๊ฐ์ด ์ฌ๋ฌ ์ ํ ๋๋ฌธ์ ํ๋ค๋ค. ์ฌ์ง์ด 10MB ์ ๋์ ํ์ผ ์ ๊ณต๋ ์ง๊ธ์ ๋ถ๊ฐ๋ฅํ ์ํฉ์ด๋ค. ์ด๋ฏธ์ง์ ๊ฒฝ์ฐ์๋ WebP ์์ถ์ด ์๋ ํจ์จ์ ์ด๋ผ ๋ถํธํ ์ ์ด ์๋ ์ํฉ์ด์ง๋ง, ๋ค๋ฅธ ํ์ผ์ ๊ฒฝ์ฐ์๋ ์ฉ๋ ์ ํ์ด ํฌ๊ฒ ๋ค๊ฐ์ฌ ์ ๋ฐ์ ์๋ค.
</p>
<p>
๊ธฐ์ ์คํ์ ์ ์งํ๋ ๋์์ ๋ ํฐ ํ์ผ์ ์๋นํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ ์ฐ๊ตฌํด๋ด์ผ๊ฒ ๋ค.
</p>
<h2>
์์ค์ฝ๋
</h2>
<p>์ด ๋ธ๋ก๊ทธ์ ์์ค์ฝ๋๋ ์คํ์์ค์ด๋ค. ๋ด <a href="https://github.com/atjsh/rust-blog" target="_blank">GitHub</a>์์ ํ์ธ ๊ฐ๋ฅํ๋ค.</p>
<p>
์ด๋ฒ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋ ์ปค๋ฐ ๋ด์ญ์ ๋ค์ ๋งํฌ์์ ํ์ธ ๊ฐ๋ฅํ๋ค. <a href="https://github.com/atjsh/rust-blog/compare/89958ca4f61585c1a8c0036d6b31301769cccb3c...ad708e876d7def1683e282fe13576fa14f6b7e94?diff=unified" target="_blank">atjsh/rust-blog - 89958c...ad708e</a>
</p>
<h2>
๊ต์ฅํ ๋ง์กฑ
</h2>
<p>
๊ฑฐ์ 2๋ฌ๊ฐ ๋ฏธ๋ฃฌ ์์
์ธ๋ฐ ํ๋ฃจ๋ง์ ๊ตฌํ์ ๋๋๋ค. ๋๋์ด ๋๋ด์ ์์ด ํ๋ จํ๋ค.
</p>