<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[RSS Feed of hongkim.dev]]></title><description><![CDATA[Hello :) This is JiHongKim98's tech blog]]></description><link>https://hongkim.dev</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 24 Sep 2025 06:37:52 GMT</lastBuildDate><item><title><![CDATA[이미지 최적화를 위한 여행기 (1) - 이미지 로딩 최적화]]></title><description><![CDATA[안녕하세요, 팀 Joytas 에서 백엔드 개발 파트를 맡고 있는  입니다. SNS 서비스인 DAO…]]></description><link>https://hongkim.dev/image-load-optimize/</link><guid isPermaLink="false">https://hongkim.dev/image-load-optimize/</guid><pubDate>Thu, 24 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;안녕하세요, 팀 Joytas 에서 백엔드 개발 파트를 맡고 있는 &lt;code class=&quot;language-text&quot;&gt;hong.kim&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;SNS 서비스인 DAO 서비스 개발을 진행하며, 이미지 로딩 최적화를 이뤄내기 위해 어떤 과정들을 거쳤는지 이번글을 통해 소개해보려고 한다.&lt;/p&gt;
&lt;h2 id=&quot;초기-이미지-로딩-아키텍처의-문제점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B4%88%EA%B8%B0-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A1%9C%EB%94%A9-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90&quot; aria-label=&quot;초기 이미지 로딩 아키텍처의 문제점 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;초기 이미지 로딩 아키텍처의 문제점&lt;/h2&gt;
&lt;p&gt;초기 DAO 프로젝트를 진행하면서 MVP 단계에서 빠른 구현에 초점을 맞추며, 아래 사진과 같이 이미지 시스템 아키텍처를 최대한 간단하게 가져갔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 438px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/d2031b7562c2dae1fb05dde67e99f13e/a3b87/as-is-image.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 37.64705882352941%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA9hAAAPYQGoP6dpAAABdklEQVR42o2STYvTQBzG83k9efETKF4Ejx7EiyCCIuxhT+JJEOxKV001tFl8qXmZTNKaZDKT0k2a+S2ZbpfV9eAffjDDPP+3h/H6vqfvu0v6P9jtdgghCMOQOI7d/W/Nga7rsNbijQl5sUKOSHmDJElYLpeuYJ7n/9QURUGWZbRtiyeyDBn9oJFLVF3SNJqmaa5QWqF0jWpqlFIo1Vx712jTsl6v3CZta/AyWaDTOUgfe27Yh+W/4ryFzW+2G4OU+X7Ck+knJu8nbNUK7IC1OC8GO7icsPR5PLvLJHuzb7UXuPNQRQzJKUXxmbT8QlGd4SVC8u5kSpSkpEK40UeiKMIYw5PgHg9Ob/Pw4x2kjmlq7fwatSspqGRCXgaI8ivz+DXesxdHPH3+ClXXzGYzfN8nCAIW4QIGeJsec//DLV6ePWKwOzfZ2Kiqqitz6lagN2tKHeF9/xk5ow/rHL7LyBhlXTJdTEhkdFngpr/bbsu3X3PMxnABxT1ZLsJ9DBsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;기존 이미지 시스템 아키텍처 (As-Is)&apos; title=&apos;&apos; src=&apos;/static/d2031b7562c2dae1fb05dde67e99f13e/a3b87/as-is-image.png&apos; srcset=&apos;/static/d2031b7562c2dae1fb05dde67e99f13e/e7570/as-is-image.png 170w,
/static/d2031b7562c2dae1fb05dde67e99f13e/f46e7/as-is-image.png 340w,
/static/d2031b7562c2dae1fb05dde67e99f13e/a3b87/as-is-image.png 438w&apos; sizes=&apos;(max-width: 438px) 100vw, 438px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;기존 이미지 시스템 아키텍처 (As-Is)&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이미지 로드 요청&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 S3 Object 엔드포인트로 직접 &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; 요청&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;p&gt;위 사진과 같이 구성한 이유는 이미지 업로드 자체가 우리 서비스의 메인 기능이 아닌 부수적인 기능이라 판단해 &lt;code class=&quot;language-text&quot;&gt;PUT&lt;/code&gt; 요청에 대한 최소한의 보안만 고려하여 구성하고 메인 기능에 집중하였다. 또한, 초기 최대 업로드 가능한 이미지 크기는 &lt;code class=&quot;language-text&quot;&gt;1MB&lt;/code&gt;로 제한함으로 써, 이미지 업로드 및 다운로드 속도를 제어하고 있었다. MVP의 메인 기능들이 완성되고, 휴대폰을 찍은 사진을 많이 올리는 SNS 서비스의 특성상 &lt;code class=&quot;language-text&quot;&gt;1MB&lt;/code&gt; 용량 제한을 &lt;code class=&quot;language-text&quot;&gt;25MB&lt;/code&gt; 으로 늘리도록 결정하였고, 이미지 용량 제한이 25배가 증가함에 따라 프론트엔드에서 이미지를 &lt;code class=&quot;language-text&quot;&gt;.webp&lt;/code&gt; 형식으로 변환하여 업로드하도록 로직을 수정함으로 써, 성능 이슈를 대비하였다.&lt;/p&gt;
&lt;p&gt;아래의 &lt;code class=&quot;language-text&quot;&gt;.webp&lt;/code&gt;로 변환하여 이미지를 업로드 했을 때의 이미지 로드 속도를 봐보자:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/19be452b81a3fc8d75f677fead11fa39/68327/load-time-1.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 11.176470588235293%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiUlEQVR42h3MyQ2CQABAUUphCURQ1sAAM2FfFHCwAS/2X8Q3enjXZ8iqZhgm2ran63qGYUSphjwXWJZDUQh8/0oYxmzbznE8KUWJ6bjUtskhctokpgp83o8V45csy/o3jhPremeeF6RUeJ5HXUuiKCJNM7Q+Oc8XSkrci0/j2egyp88S1C3go3e+9B1C/wiN7H4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;1.8MB 의 이미지를 불러오는데 500ms 소요&apos; title=&apos;&apos; src=&apos;/static/19be452b81a3fc8d75f677fead11fa39/ca1dc/load-time-1.png&apos; srcset=&apos;/static/19be452b81a3fc8d75f677fead11fa39/e7570/load-time-1.png 170w,
/static/19be452b81a3fc8d75f677fead11fa39/f46e7/load-time-1.png 340w,
/static/19be452b81a3fc8d75f677fead11fa39/ca1dc/load-time-1.png 680w,
/static/19be452b81a3fc8d75f677fead11fa39/68327/load-time-1.png 950w&apos; sizes=&apos;(max-width: 680px) 100vw, 680px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;1.8MB 의 이미지를 불러오는데 500ms 소요&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;1.8MB&lt;/code&gt; 크기의 이미지를 완전히 불러오기까지 &lt;code class=&quot;language-text&quot;&gt;500ms&lt;/code&gt; 가 넘게 걸리고 있었다. 불러올 이미지의 크기가 &lt;code class=&quot;language-text&quot;&gt;20MB&lt;/code&gt;가 넘는다면 이미지를 완전히 불러오는 시간은 &lt;code class=&quot;language-text&quot;&gt;1000ms&lt;/code&gt; 을 가볍게 넘는 것은 물론, 네트워크 대역폭도 증가하게 된다. 따라서, 이미지를 완전히 불러오기까지 걸리는 최대 시간을 &lt;code class=&quot;language-text&quot;&gt;200ms&lt;/code&gt; 이내로 들어오도록 목표를 잡았게되었다.&lt;/p&gt;
&lt;h2 id=&quot;이러한-문제점을-개선할-수-있을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%9F%AC%ED%95%9C-%EB%AC%B8%EC%A0%9C%EC%A0%90%EC%9D%84-%EA%B0%9C%EC%84%A0%ED%95%A0-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;이러한 문제점을 개선할 수 있을까 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;이러한 문제점을 개선할 수 있을까?&lt;/h2&gt;
&lt;p&gt;결론부터 말하자면, 이미지 시스템 아키텍처를 아래와 같이 개선하면서 다음과 같은 성과를 얻을 수 있었다:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 506px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/a4c4a7dc52b40052ec027c256e159a7f/61db1/to-be-image.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 66.47058823529413%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA9hAAAPYQGoP6dpAAACPUlEQVR42o2Ty27TQBiF8448AI/BDp6BDRsWCCEQmyJQgaJKUIRApRWoSZukudhx0riNHWd8v98+5EkJbUEVR7bmol/HZ77x3+KK6rqWoxA2J70Og2GXznGbIAg2NWWVUdXlZh0HDufnOvOZRhhGtJrNPM+Joog0TUmSlCB0+PzC4+k9n+/bLnHmEEcZUerS0V6jLr5QFCW+I0hdg3zyjWx6SF3ma0MhBPP5HEVRUNUJjm+x97jm4R043IIwXbE4X+IGCzraFqf6jkyt6zqeH1AOd8n7O9SJtza0LAvHcaSxYzvyKL39gE/PHSbHcQND7vl+wEDpo0wVmbBREISYcw1jNsYPQlqES5amge/7v0nKp6hSiiqirPINr6qCJMnJspz6sk4GWgkM05LzVhl7zKYzhG1zXevqOvWpzVOyaEmQCKoqp6oqyb15JTLbxFpdrA1HikbnpM/BjyPKsiRJEjRNQ1UVZmdzXGFSuCaWGDAx9hHBhNn0jF6vS7fbk5h08ZOR/pG6rmiNVY33u3u82v5AURQSdmPYbrcZDoc4rksUZ/JiVp6GE84JgxjXdST35lexfJWpcbBOmOU5g5GCqs34l+rLo2d5gm5MyYv8rxrH9ViJBkdNq+Fh2zZxHF9jV9UVWZluDG+y/denZcLG0DRN+v0+4/GY0XiEcWHSFYc86jzgaPn1souqW8z+qNW0WwPWMAxpvDAWuMLjrfaM+wd3eaM+2ST+H7Wu9vDVueHpvDt5ydK/4GbNbYa/ANRm3UVfDCGGAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;개선된 이미지 시스템 아키텍처 (To-Be)&apos; title=&apos;&apos; src=&apos;/static/a4c4a7dc52b40052ec027c256e159a7f/61db1/to-be-image.png&apos; srcset=&apos;/static/a4c4a7dc52b40052ec027c256e159a7f/e7570/to-be-image.png 170w,
/static/a4c4a7dc52b40052ec027c256e159a7f/f46e7/to-be-image.png 340w,
/static/a4c4a7dc52b40052ec027c256e159a7f/61db1/to-be-image.png 506w&apos; sizes=&apos;(max-width: 506px) 100vw, 506px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;개선된 이미지 시스템 아키텍처 (To-Be)&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/55e810814e7f2cfae1e363c82514090e/44e31/load-time-2.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 11.176470588235293%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhUlEQVR42j3M3QqCMABAYV8lt1Gg0Wa0pfMH/7IwI+223v8xTiDRxeHcfUHpPf1loGs76rpZy/MCax1hKHAuJYpitDbM88yyvPCZZyMkWoQ8T4ZCHzgqyec2EJRF+YeqqqH9wc6dkXK7Po73aJ0wjnem6UGaZgi1wyjJYhPqxGC3ive15wsYKULnnIH0JgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;개선 후의 이미지 로드 시간&apos; title=&apos;&apos; src=&apos;/static/55e810814e7f2cfae1e363c82514090e/ca1dc/load-time-2.png&apos; srcset=&apos;/static/55e810814e7f2cfae1e363c82514090e/e7570/load-time-2.png 170w,
/static/55e810814e7f2cfae1e363c82514090e/f46e7/load-time-2.png 340w,
/static/55e810814e7f2cfae1e363c82514090e/ca1dc/load-time-2.png 680w,
/static/55e810814e7f2cfae1e363c82514090e/44e31/load-time-2.png 949w&apos; sizes=&apos;(max-width: 680px) 100vw, 680px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;개선 후의 이미지 로드 시간&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;이미지 로드 요청&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;로드 시간(Cache-Hit 기준) : &lt;code class=&quot;language-text&quot;&gt;500ms&lt;/code&gt; -&gt; &lt;code class=&quot;language-text&quot;&gt;10ms&lt;/code&gt; (응답 시간 약, &lt;strong&gt;50배 개선&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;이미지 크기 : &lt;code class=&quot;language-text&quot;&gt;1.8MB&lt;/code&gt; -&gt; &lt;code class=&quot;language-text&quot;&gt;15.8KB&lt;/code&gt; (대역폭 약, &lt;strong&gt;99.14% 절약&lt;/strong&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DAO 서비스는 아래의 &lt;strong&gt;2가지 과정&lt;/strong&gt;을 거쳐 전반적인 이미지 시스템 아키텍처 개선을 이뤘고, 하나씩 소개하려고 한다.&lt;/p&gt;
&lt;br&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#CDN-도입을-통한-RTT-및-S3-비용-개선&quot;&gt;&lt;u&gt;&lt;strong&gt;CDN&lt;/strong&gt; 도입을 통한 &lt;strong&gt;RTT 및 S3 비용 개선&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#S3-Object-Lambda를-통한-이미지-리사이징&quot;&gt;&lt;u&gt;&lt;strong&gt;S3 Object Lambda&lt;/strong&gt;를 통한 &lt;strong&gt;이미지 리사이징&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;CDN-도입을-통한-RTT-및-S3-비용-개선&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#CDN-%EB%8F%84%EC%9E%85%EC%9D%84-%ED%86%B5%ED%95%9C-RTT-%EB%B0%8F-S3-%EB%B9%84%EC%9A%A9-%EA%B0%9C%EC%84%A0&quot; aria-label=&quot;CDN 도입을 통한 RTT 및 S3 비용 개선 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;CDN&lt;/strong&gt; 도입을 통한 RTT 및 S3 비용 개선&lt;/h2&gt;
&lt;!-- &gt; **RTT**(Round Trip Time)이란?
&gt;
&gt; 네트워크 요청을 시작한 후 응답을 받는 데까지 걸린 시간을 의미한다.
&gt;
&gt; &lt;br&gt;
&gt;
&gt; 참고: [AWS - 네트워킹 RTT란 무엇인가요?](https://aws.amazon.com/ko/what-is/rtt-in-networking/) --&gt;
&lt;p&gt;초기 아키텍처에서 가장 개선이 필요한 부분은 &lt;u&gt;클라이언트가 AWS S3로 직접 원본 이미지 요청&lt;/u&gt;을 보내 발생하는 문제였다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;S3 캐싱 기능의 부재 이슈&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;S3 자체에서 캐싱 기능이 없어, 사용자들이 동일한 객체에 접근할 때 매번 S3에서 직접 데이터를 읽어 보내줘야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;S3 비용 증가 이슈&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS S3를 사용하면서 객체에 직접적으로 &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; 요청으로 처리하면, S3에 대한 데이터 전송 요금이 부과된다.&lt;/li&gt;
&lt;li&gt;즉, 1번 이슈와 동일하게 사용자들이 같은 객체에 계속 접근한다면? -&gt; 비용은 계속 카운팅&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;클라이언트가 S3 로 직접 요청을 보내는 경우, 위 2가지 문제가 가장 큰 걸림돌이였다.&lt;/p&gt;
&lt;h3 id=&quot;CDNAWS-CloudFront-도입-고려&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#CDNAWS-CloudFront-%EB%8F%84%EC%9E%85-%EA%B3%A0%EB%A0%A4&quot; aria-label=&quot;CDNAWS CloudFront 도입 고려 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;CDN(AWS CloudFront) 도입 고려&lt;/h3&gt;
&lt;p&gt;캐싱 기능의 부재와 S3에서 발생할 수 있는 비용 증가 이슈는, AWS 에서 제공하는 CDN 서비스인 CloudFront를 통해 개선을 진행할 수 있었다. 아래의 AWS CloudFront에 대한 설명을 봐보자.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CloudFront는 엣지 로케이션이라고 하는 데이터 센터의 전 세계 네트워크를 통해 콘텐츠를 제공합니다.
CloudFront를 통해 서비스하는 콘텐츠를 사용자가 요청하면 &lt;u&gt;지연 시간이 가장 낮은 엣지 로케이션&lt;/u&gt;으로 요청이 라우팅되므로 &lt;u&gt;가능한 최고의 성능&lt;/u&gt;으로 콘텐츠가 제공됩니다.&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;(출처: &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/Introduction.html&quot;&gt;Amazon CloudFront란 무엇입니까?&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CloudFront는 정적 이미지를 사용자와 가장 가까운 Edge Location에서 캐싱함으로 써, RTT(Round Trip Time)을 줄일 수 있을 뿐더러, 클라이언트가 S3와 직접 통신하는 것이 아닌 CloudFront의 Edge Location과 통신하고, 결정적으로 CloudFront에서 S3로 요청하는 &lt;u&gt;데이터 송신 비용은 &lt;strong&gt;무료&lt;/strong&gt;&lt;/u&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/1719ee5b723587792e4c8b1a2b257aae/01c73/s3-price.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 35.88235294117647%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA20lEQVR42o2QXW7DIBCEuf/VeoD0IVLtxk1iJTjlb6GAPdVuRGwpUtqHT+yww7BadTye0Pef+Og6Ob+NgfcBxljBWsbBeQ8jPX/Xzkt916tHEUWEEKRBRGBdSsE8z6i1otZZ6qZbTXH1bVHOOUlflkUoElKfjA325Jxx1VomYt38fKrzecRhGMTE8K+v4EchkKyHQzlo+04mdM5irlUuWvArUkqYpklWxR9se8r6gNOocdE3Ma67+xtKGRQzai2PO3UYb3jbfeG9GxEj/XtKDtkPGrv+gpR+HhP+Ajz7ILeT9f/wAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;출처: Amazon S3 요금 https://aws.amazon.com/ko/s3/pricing&apos; title=&apos;&apos; src=&apos;/static/1719ee5b723587792e4c8b1a2b257aae/ca1dc/s3-price.png&apos; srcset=&apos;/static/1719ee5b723587792e4c8b1a2b257aae/e7570/s3-price.png 170w,
/static/1719ee5b723587792e4c8b1a2b257aae/f46e7/s3-price.png 340w,
/static/1719ee5b723587792e4c8b1a2b257aae/ca1dc/s3-price.png 680w,
/static/1719ee5b723587792e4c8b1a2b257aae/02d09/s3-price.png 1020w,
/static/1719ee5b723587792e4c8b1a2b257aae/01c73/s3-price.png 1145w&apos; sizes=&apos;(max-width: 680px) 100vw, 680px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;출처: Amazon S3 요금 https://aws.amazon.com/ko/s3/pricing&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;정적 컨텐츠의 사이즈가 적을 때, S3만 운영하는 것과 CloudFront와 결합하여 운영하는 것의 비용 차이는 적지만, 정적 컨텐츠의 사이즈가 커질수록 운용 비용의 차이는 커진다. 또한, 클라이언트와 S3 사이에서 CloudFront가 &lt;code class=&quot;language-text&quot;&gt;Proxy&lt;/code&gt; 역할을 해줌으로 써, 보안적으로 이점을 챙길 수 있다.&lt;/p&gt;
&lt;p&gt;(해당 내용의 비용 비교는 &lt;a href=&quot;https://incheol-jung.gitbook.io/docs/q-and-a/infra/cloudfront-s3&quot;&gt;이 블로그&lt;/a&gt;에서 더 자세히 찾아볼 수 있다.)&lt;/p&gt;
&lt;h3 id=&quot;S3와-CDN의-결합-전후-로드-시간-비교&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#S3%EC%99%80-CDN%EC%9D%98-%EA%B2%B0%ED%95%A9-%EC%A0%84%ED%9B%84-%EB%A1%9C%EB%93%9C-%EC%8B%9C%EA%B0%84-%EB%B9%84%EA%B5%90&quot; aria-label=&quot;S3와 CDN의 결합 전후 로드 시간 비교 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;S3와 CDN의 결합 전후 로드 시간 비교&lt;/h3&gt;
&lt;p&gt;CloudFront와 S3를 결합하여 운영할 때의 로드 시간을 봐보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/175f797ae925fcd54411048fa2f3e52b/44e31/image-load-3.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 10.588235294117647%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiElEQVR42h3DaQ5DQACAUUdBF0vESC2jNcUoZdBE04v0R+//NfGSZ13LEq0fNE2717qjrhukLHEcl6KQBEFIHAvmeWFdX0gpsQ9HMtdmzVMaEZNHEd+hxmpbzTQZxnGi758YszCOBqXueJ5PVSmESEjTjG1771WlOPshpXfic5MM2YUqEfxMxx8WekOcg9P59gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;CDN(AWS CloudFront) 적용 후의 응답 시간&apos; title=&apos;&apos; src=&apos;/static/175f797ae925fcd54411048fa2f3e52b/ca1dc/image-load-3.png&apos; srcset=&apos;/static/175f797ae925fcd54411048fa2f3e52b/e7570/image-load-3.png 170w,
/static/175f797ae925fcd54411048fa2f3e52b/f46e7/image-load-3.png 340w,
/static/175f797ae925fcd54411048fa2f3e52b/ca1dc/image-load-3.png 680w,
/static/175f797ae925fcd54411048fa2f3e52b/44e31/image-load-3.png 949w&apos; sizes=&apos;(max-width: 680px) 100vw, 680px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;CDN(AWS CloudFront) 적용 후의 응답 시간&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Cache Hit 기준 &lt;code class=&quot;language-text&quot;&gt;1.8MB&lt;/code&gt; 컨텐츠가 &lt;code class=&quot;language-text&quot;&gt;500ms&lt;/code&gt;에서 &lt;code class=&quot;language-text&quot;&gt;250ms&lt;/code&gt;으로 응답 시간이 2배 개선된 것을 볼 수 있다. 하지만, 아직까지도 목표한 &lt;code class=&quot;language-text&quot;&gt;200ms&lt;/code&gt;이내로 이미지 로딩이 완료되지 않고있다.&lt;/p&gt;
&lt;h3 id=&quot;S3--CDNAWS-CloudFront의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#S3--CDNAWS-CloudFront%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;S3  CDNAWS CloudFront의 한계 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;S3 + CDN(AWS CloudFront)의 한계&lt;/h3&gt;
&lt;p&gt;정적 이미지를 서빙하기 위해 S3와 CloudFront를 결합한 아키텍처만으로도 좋은 아키텍처라고 생각한다. 하지만, CDN은 &lt;u&gt;RTT만 줄여줄 뿐 근본적으로 이미지의 크기를 줄여주지 않음&lt;/u&gt;으로 매우 큰 개선은 어렵다.&lt;/p&gt;
&lt;p&gt;따라서, 이미지 크기를 줄이기 위한 추가적인 아키텍처 구성이 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;S3-Object-Lambda를-통한-이미지-리사이징&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#S3-Object-Lambda%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95&quot; aria-label=&quot;S3 Object Lambda를 통한 이미지 리사이징 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;S3 Object Lambda를 통한 이미지 리사이징&lt;/h2&gt;
&lt;p&gt;이미지 리사이징을 위한 추가적인 아키텍처를 구성하기 전, 현재 우리의 서비스를 살펴보자.&lt;/p&gt;
&lt;h3 id=&quot;이미지-리사이징이-옳은-방법일까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95%EC%9D%B4-%EC%98%B3%EC%9D%80-%EB%B0%A9%EB%B2%95%EC%9D%BC%EA%B9%8C&quot; aria-label=&quot;이미지 리사이징이 옳은 방법일까 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;이미지 리사이징이 옳은 방법일까?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/e97405291845902647b0147a29dfe83d/61d11/dao-service-image.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 74.70588235294117%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEEklEQVR42m2Te0yTVxjGv0JLQMjW0VIubbnLuEwGQTFzJlPY3MZ0MjdjIpcyVkCqWAu1BlJgWMCCwGjLkMklyMatWChXA2NMh9/cXJbhxkTF/UHNIhj2jaGoKN/3LKV12ZL9cc5zzsn7/vKck/MQqXmn8fvNpxjoGEPOUQ20p1pRq+uGRtOME7klTFz8TrDZbMpbKCYbzjTB1HuB7uruR2NTC4KCQ8DjCyDw9Ia3UAShUAQiKjEDLT8v4g/LAubm/oTFsozFxSe4ceM3LN9/wJhMvXBycqLc3NzI76+QAEBbpzuWOQg8PEAQBFgs1ro6ODhYlYWDmYex8mAZCwvzWF19BJpew9LSkrWP6e3rsxZRHI4TmaYoQ4HBSBd+akau9hziU9SIT6/ELmkl4pLzwRWIrLUEPnhvHx4+fIS1tTUrBI8fr+Kv5ftYffKU6R8YgJurK8UiWORrKcV4X91BZ5yZQuZnM1B230OBeQUFPbehPn8LvqGbbcC9u9/F7ZszmL72I67/MoWZX6cxP38XFssdRq83wNHBgSIIgtyfVw9F8w+0st0ClfEelJ13oWidRlZ1FzKre+AVEG4DBofHQlU3Crl+EnLdV8ipbEfsjj3Q19Yw12/NYtjYTfnz+WSiXIf89u9o1RfXUGCah6pjFodqhyApbYekvBue/nZgUOR25DRMQNU5C/nZbyGtHkRA1OvoaG9jrE+wVKKiQl03kIkKLWS6Xlqm74fq8ykoG79Gdk0/kko6kKrpgqefHbgxchsUhkGo6kdwXD+I/LMTCN3yJjq72hmsLGOyRkf5CUVk4hENMqv76JxPzDimH4SsqgfH9UM4pD0Pea0ZwqBNdocR26EwXITSMAF51ShyDd8g+tXd+PBtCdPZOI6rbRoqWPg8eTBXh8KWS3TZuUl83HwZ6oaL0LZdQXHTJJT1I/DdGGkDBoa9gtRiE9JPmiAp6oK0bBgRW9/BzrDNjHTHGzCWZFGBIWIyQVIOuW6czq4YxrGaL5F1ahiyskGkl5ggLR2At7/dobPzBngIRODxhfDwFOE5rgCObBfw+VzGT8wD192fYrGcyL2yGmRUjNLJJ83IqbyA7HIzPtL047B2CDLtCHwC7EAezx2xsVvwcuQmRISHwdnZef3nh4S8yERFxyAmJopycXEh92VXIamwh05WG5FbNw5pqQkpRcb121kdeogjbEBHR0dwOBxrxNbXz+LEZrMZ2zmHIggHMi2vDpq2q/TRusvIqBqHsu4SKlp/Qlr5GNLLx+AdGG0DWpuf5fHfymKxGPueIggW+dK2/UiQFNFvSdTYuucIEpIKkJpVhl0HVIg7cALuAr//Av9n/APk8/mkWOQD3gtc2sdLAJGPF/juXHgK+OvqLxbCz1eMvwFiNIh68vODDwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;DAO 서비스의 이미지 랜더링 크기, 미리보기 150x150px와 상세보기 500x700px&apos; title=&apos;&apos; src=&apos;/static/e97405291845902647b0147a29dfe83d/ca1dc/dao-service-image.png&apos; srcset=&apos;/static/e97405291845902647b0147a29dfe83d/e7570/dao-service-image.png 170w,
/static/e97405291845902647b0147a29dfe83d/f46e7/dao-service-image.png 340w,
/static/e97405291845902647b0147a29dfe83d/ca1dc/dao-service-image.png 680w,
/static/e97405291845902647b0147a29dfe83d/61d11/dao-service-image.png 713w&apos; sizes=&apos;(max-width: 680px) 100vw, 680px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;DAO 서비스의 이미지 랜더링 크기, 미리보기 150x150px와 상세보기 500x700px&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;현재, 우리 서비스는 미리보기 이미지에서 &lt;strong&gt;150x150&lt;/strong&gt; 픽셀, 상세보기 탭에서 &lt;strong&gt;500x700&lt;/strong&gt; 픽셀로 랜더링하고 있다. 즉, 이보다 더 큰 이미지일 경우 &lt;u&gt;불필요한 이미지 크기로 인해 페이지의 성능을 저하를 유발&lt;/u&gt;한다. 따라서, 클라이언트가 이미지를 랜더링할 때 필요한 크기 즉, &lt;u&gt;동적으로 이미지를 리사이징함으로 써&lt;/u&gt; 응답 시간 및 대역폭을 개선할 수 있다고 기대했다.&lt;/p&gt;
&lt;h3 id=&quot;동적-이미지-리사이징을-위해-고려한-방법들&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%A0%81-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95%EC%9D%84-%EC%9C%84%ED%95%B4-%EA%B3%A0%EB%A0%A4%ED%95%9C-%EB%B0%A9%EB%B2%95%EB%93%A4&quot; aria-label=&quot;동적 이미지 리사이징을 위해 고려한 방법들 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;동적 이미지 리사이징을 위해 고려한 방법들&lt;/h3&gt;
&lt;p&gt;나는 &lt;a href=&quot;#CDN-도입을-통한-RTT-및-S3-비용-개선&quot;&gt;앞서 구축한 아키텍처&lt;/a&gt;와 동적 이미지 리사이징 서비스를 결합하기 위해 아래의 3가지 방안을 고려했다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;EC2 혹은 ECS 기반의 이미지 처리 서버 구성&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lambda@Edge&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;S3 Object Lambda&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;첫번째, 이미지 처리를 위해 별도의 서버를 운영하는 방법은 핵심 기능 외에 추가적인 &lt;u&gt;관리 포인트&lt;/u&gt;가 늘어나고 &lt;u&gt;유휴 시간 동안 발생하는 비용을 절감&lt;/u&gt;하기 위해 서버리스 환경 즉, &lt;strong&gt;AWS Lambda&lt;/strong&gt;를 사용하는 방법으로 기울게 되었다. 또한, Edge Location에 리사이징된 이미지가 캐싱되어 있다면 Lambda가 동작하지 않아 추가적인 비용 절감 효과도 기대했다.&lt;/p&gt;
&lt;p&gt;두번째, Lambda@Edge를 사용하여 이미지 리사이징을 구현할 때 전 세계 모든 Edge Location에 리사이징된 이미지를 캐싱하므로 글로벌 리전이 필요한 경우 매우 유용하게 작용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/2b9d404e36dd594533eebf55cc316f28/dbf98/lambda-edge-vs-lambda-price.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 56.470588235294116%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABw0lEQVR42o2SD4+iMBDF+bZ+Xd1TV1RE4YRCoZR/FdoO79Le6t1l95J9yS+T9LWTmekEQjTo+wFKKViiTxhrobUGEXmmaUIrJURdo65rdF0HzjnmeYZTMAwjqqpGLcR/E04fl53qpsF7GGK9XmOz2eBwOHiGYfidEN+RtYCUUHkOU1XAOP7xluUjLJ6g7wcyxtCXsq5GkOWcmjCkKkmIJwmpOCZqW7LOs9bzVOBm5/o3xsBa+y/GgADMWYZ6t0edJMijC4bjCVYIaDcSrV9v3YyDcRxXADzW2s84r+tW9u1tZbdbH3G5vN480Vr7+0HJOXMUJWfTNDFjDNNPtGbzPLNRKQbgxaAUS5OE/UxTFl8uTD19Iha4lRhH5dfGlfyV3E//rX4YkNzvyBnDPc+hHo+X51omxgqXyQ81y3JKktSfuc1z5+g6IiGoud3ICkFoW8I4emYhqE1TQtMQHg8KmkaibVssWCCEQFlyT57nkP0A9D2a3Q7F9YbiHCE7HiGSFEvXQbhPOp3Br1eUxxPMNUYgZesTudZdLIoSZVkiz3I0bQcjJeSPLXgUgYchsv0eMr7Ccg4Zx+DHI6rzGdX7AXMU4ReXD0FFSCh1gAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;Lambda@Edge 와 Lambda Function 가격 비교 (출처: AWS Lambda 비용)&apos; title=&apos;&apos; src=&apos;/static/2b9d404e36dd594533eebf55cc316f28/dbf98/lambda-edge-vs-lambda-price.png&apos; srcset=&apos;/static/2b9d404e36dd594533eebf55cc316f28/e7570/lambda-edge-vs-lambda-price.png 170w,
/static/2b9d404e36dd594533eebf55cc316f28/f46e7/lambda-edge-vs-lambda-price.png 340w,
/static/2b9d404e36dd594533eebf55cc316f28/dbf98/lambda-edge-vs-lambda-price.png 612w&apos; sizes=&apos;(max-width: 612px) 100vw, 612px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;Lambda@Edge 와 Lambda Function 가격 비교 (출처: AWS Lambda 비용)&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;하지만, Lambda@Edge는 Lambda Function에 비해 대략 &lt;strong&gt;3배의 비용&lt;/strong&gt;이 발생하며, 우리 서비스는 글로벌 리전은 필요하지 않으므로 최종적으로 S3 Object Lambda를 사용하여 동적 이미지 리사이징 아키텍처를 구축하기로 결정했다.&lt;/p&gt;
&lt;h3 id=&quot;S3-Object-Lambda를-통한-이미지-리사이징-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#S3-Object-Lambda%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;S3 Object Lambda를 통한 이미지 리사이징 적용 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;S3 Object Lambda를 통한 이미지 리사이징 적용&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;본 글에서 사용한 Lambda Function의 코드는 &lt;a href=&quot;https://github.com/JiHongKim98/aws-s3-lambda-image-resizer&quot;&gt;깃허브 리포지토리&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이제, DAO 서비스에서 어떻게 S3 Object Lambda를 설정했는지 예제를 통해 소개하겠다.&lt;/p&gt;
&lt;h4 id=&quot;IAM-정책-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#IAM-%EC%A0%95%EC%B1%85-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;IAM 정책 생성 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;IAM 정책 생성&lt;/h4&gt;
&lt;p&gt;먼저, Lambda 함수가 S3 Object Lambda Access Point를 통해 S3 버킷에 저장된 객체에 안전하게 접근하고, 이를 동적으로 처리한 후 클라이언트(CloudFront)에 반환할 수 있도록 필요한 권한을 부여 하기 위해 아래와 같이 IAM 역할을 생성해줘야한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/5450c7ec69a9b9cddc61646c62463a04/dbf98/aws-iam-roles.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 110.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACuklEQVR42pXU227bOBAGYL5Eu6m7jizrSFEiKVJnS/IxSuq4QLEL9GX8OnlB3fjKwF9IbdOi3W2Uiw9D3pAccjhkYTOItIZFJWYLD3+b9Nmt6f+ETkICpnC8O6EtN9AiQypzFMlqlOTtiMclFhaDaYcvIis/wr3KcV/U6NIKNZOoQ4U9kyhCBSozMJGARmoSUlOO3aZDpCt4gYLtcixcjsgOoZ0QBo1h+RLWECcgTRjj8+aAh6LGh7zBY7XGx2aHx6yCYhIeH06nJyMsXV0Pp09Xna+uYZxdI5VfuS7GsR+pa8D1qxCTKaTNDnm1hkxKxOkKcVohzlYjXTSI4my8n4DrFxEuk6csb58imT8ZDnuaL9zfvDecyYhO60uatxc/VJebhXd583Z+eXtj/PDXEBeTkaUdoqi2CGUGh+nxhY0lHetuEpvB/GlOWJTgePoHu8MjynUHla+h0xq2G2HY7CXDYsYyeC58Ml8GWAYaTpjCcAQMV8DwJAz3z25dibkj4csKzfYBZXOA7UmQmR/DXnWgzQPeiQo3UTnJLMrxXlRYJkN1rODzHKYrQPwlhXYjHNcHfGj24Db7I2EzRDaDzxQikYGGekzVciPYHgfZLNz+RHn/r1b951z0+3zbd+W2vyvWY+zytu+qbX8Qab9zgv7OZf3Rob0ViF6oom+3Xc9V0buB6D0me+LNrbOYmWc2M89i7pxj0/t/hnPWhnOODOc8M73zfGA4X+M3xI9SrDb3KNsOlGcYHsmwQ9wOrF/YPyys/0Y8KiHiAiFPQYN4LBfH43B8AZfKVyMqq3D/8dP4Z0VSQBc1krKBTMuv/1O8DglEirY7odkf0d6dkNUH0LgEFQXsQE/ug8/9kOsCRbODyuvxlFwVELoc5wMvjF+FJGWL3cMJeb1DuT6g3nUo1/uxdQ1pf29jU30BtSCJsB1RAugAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;AWS IAM 설정&apos; title=&apos;&apos; src=&apos;/static/5450c7ec69a9b9cddc61646c62463a04/dbf98/aws-iam-roles.png&apos; srcset=&apos;/static/5450c7ec69a9b9cddc61646c62463a04/e7570/aws-iam-roles.png 170w,
/static/5450c7ec69a9b9cddc61646c62463a04/f46e7/aws-iam-roles.png 340w,
/static/5450c7ec69a9b9cddc61646c62463a04/dbf98/aws-iam-roles.png 612w&apos; sizes=&apos;(max-width: 612px) 100vw, 612px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;AWS IAM 설정&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;위 사진과 같이 &lt;code class=&quot;language-text&quot;&gt;AmazonS3ObjectLambdaExecutionRolePolicy&lt;/code&gt; 권한을 준 &lt;code class=&quot;language-text&quot;&gt;S3ObjectLambdaRole&lt;/code&gt; 역할을 만들었다.&lt;/p&gt;
&lt;h4 id=&quot;이미지-리사이징-Lambda-Function-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95-Lambda-Function-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;이미지 리사이징 Lambda Function 생성 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;이미지 리사이징 Lambda Function 생성&lt;/h4&gt;
&lt;p&gt;IAM 권한을 만들었다면, 이미지 리사이징 작업을 진행할 수 있도록 Lambda Function를 만들어주자. 생성할 Lambda Function에 앞서 만들어둔 역할을 부여해줘야한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 524px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/8ce6d23457a0238e07d428e801bf5b99/6d924/lambda-function-setup-1.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 102.94117647058825%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAACqElEQVR42p2U23KbMBCGeZQ0jm2MACEkISRxNj7HTWba6UXf/z3+DsJxSeOkaS++2V3YWWlP8hJuUbd7zJcx7mYEXx4Ivsw+x90NvJBmEKrEfJVgtowdc5/+N56u17DNBqmySFUBoStEaQZCxV8JKH+Dl+YVmu0ZRXdAuT6i6o8gTOPeTzBbsZvc+wx+JBAmcgwc8yse1xVU2UHaBtI0GGyWWSTSIJHWwbLCSSpevhmwS0ajnwHl+RiwaHqobodEFUgzCyYN4jRDzBXohTgdcfrFjliGkEmELHP65IY1iv6INK/hxxlWVCFIFAKqQFiOIMldCQI2yoEw1c7f1D3ysr3ezgXMdIW82YIICz/i8GMxyohjQRjmQXKTVczHGyYSqyidBGx36A5nyLKF0CV4XkKayjGkPjjd7PCkEa+aQm2HzeGMmGXXk6Yn/itebiokuoYfpp+avSkvYzPF6/ePOP/4iXZ7Ql60Dl12ThbtBsYNfu9k2W5RdTso2ziKZgNb97DV2vlyZeENIyCGDclLd2IQiwv8A/2DlKWusN6f0e/P7lSfMFfDK+Eog4hhFTJnB9EHAev1Ft3u0e3ysMeZbZwc7KHjo14i1A2oqSFc99W7jfOoadAfvuL49A11v8fwWNTrHTJTuxIMmzFsAklzhFy7YMSV5p0bxrbD7vSE8/N3lO0GumigixbEpfmbqU2G2XwvIFMFZNkh4DkiqREKjUga+Il8w5KKq1xSiSAZRuX14HunqsNzt8Ums+ilwVpodKnCRhpssosttPvXpsrZa5Fjm1kUugI3NfjlHR3K4Z1igUPZY29aHGwH6SfgSwrhM4glRfoQgv/JIkZHGE4iR6yKsRQvKVs/Qk0o7GogRhlQlBFHRSXqkKFYEBTL8C0LAhpQLEiC5YRfv0+bjiKxOmMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;앞서 만든 S3ObjectLambdaRole을 부여&apos; title=&apos;&apos; src=&apos;/static/8ce6d23457a0238e07d428e801bf5b99/6d924/lambda-function-setup-1.png&apos; srcset=&apos;/static/8ce6d23457a0238e07d428e801bf5b99/e7570/lambda-function-setup-1.png 170w,
/static/8ce6d23457a0238e07d428e801bf5b99/f46e7/lambda-function-setup-1.png 340w,
/static/8ce6d23457a0238e07d428e801bf5b99/6d924/lambda-function-setup-1.png 524w&apos; sizes=&apos;(max-width: 524px) 100vw, 524px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;앞서 만든 S3ObjectLambdaRole을 부여&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Lambda Function의 실행 환경은 &lt;code class=&quot;language-text&quot;&gt;Node.js 20.x&lt;/code&gt;으로 설정해줘야한다. 이미지 리사이징에 대한 &lt;a href=&quot;https://github.com/JiHongKim98/aws-s3-lambda-image-resizer&quot;&gt;예제 코드&lt;/a&gt;는 CloudFront가 Client의 요청을 받을 때, &lt;u&gt;리사이징될 높이와 길이에 대한 정보를 쿼리 파라미터로 각각 &lt;code class=&quot;language-text&quot;&gt;h&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;w&lt;/code&gt;로 받아 리사이징&lt;/u&gt;이 수행되도록 하는 로직으로 작성되어있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 이미지를 200x200 으로 리사이징 하고 싶다면 아래와 같이 요청을 보내면 된다.&lt;/span&gt;
https://cdn.joytas.io/abcd-efgh?h&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(예제 코드가 아닌, 직접 구현한 Lambda Function을 사용할 예정이라면 각자 환경에 맞는 버전으로 설정하면 된다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/517615de9387673776ebe7fbff5cf512/dbf98/lambda-function-setup-2.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 46.470588235294116%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABiklEQVR42n3SWY7TQBSFYS+iId0EOfEYD+Vylct2EsdDRgsJwRNiDyyAV/bAin8UJ+pGapqHT/eelytd6VjzRYhQa4qqJRIG24lZuAm2m0xz2p34/5YxSy/l4zLEcvyUptmzP4wUpsELUvxA4IcCL7gJVpIweluwyohTzcKLsdxIMlM9c9XhVEeC7Ui4HfHWZ4LthXB7Ya56HmXL019uefdsrlpsP8USbkRTDpyaM4Pp6U3HUPa0ur3tpsfEJXmoUatiokNNFpW8z7vp8Ey2fFAdiyDFMk7IcPjEbvyCn1W4onyRGty0xJc1QX7j5zWR3rAXmkQa0juRl7ihwDJuRDl+Ixy+MtOHyVNx5Kk43R15J/tnD1nHozrQyJpEFiSyROQFmTI4foxVuCtGVbFXFSdd3625mA3nYj3lLlV0yYs+zjnEkkgaIqFxogo7XGN7Cdb1b6FrhCpJ838TqnolvdI1u3LHjyHg12eXjetjXbsTxBI/yt62eu1aFS+SmLjg52nO7+8PbOwFfwAy6xSbGriImgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;zip 파일로 압축된 Lambda Function 업로드&apos; title=&apos;&apos; src=&apos;/static/517615de9387673776ebe7fbff5cf512/dbf98/lambda-function-setup-2.png&apos; srcset=&apos;/static/517615de9387673776ebe7fbff5cf512/e7570/lambda-function-setup-2.png 170w,
/static/517615de9387673776ebe7fbff5cf512/f46e7/lambda-function-setup-2.png 340w,
/static/517615de9387673776ebe7fbff5cf512/dbf98/lambda-function-setup-2.png 612w&apos; sizes=&apos;(max-width: 612px) 100vw, 612px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;zip 파일로 압축된 Lambda Function 업로드&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;완성한 Lambda Function 코드와 실행 환경을 합께 압축하여 위 사진과 같이 &lt;code class=&quot;language-text&quot;&gt;.zip&lt;/code&gt; 파일로 올려 배포해주자. Lambda Function 코드 배포가 끝났다면, CloudFront가 생성한 Lambda Function을 호출할 수 있도록 리소스 기반 정책과 이미지 리사이징 작업 중 런타임 오류가 발생하지 않도록 할당된 메모리 및 임시스토리지를 늘려줘야한다.&lt;/p&gt;
&lt;p&gt;먼저, 리소스 기반 정책 권한부터 설정해보자. 리소스 기반 정책 권한 설정은 &quot;구성&quot;의 &quot;권한&quot;탭에서 설정할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/787831e970842de95a0191f77ac52879/e4da8/lambda-function-setup-3.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 24.11764705882353%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA4klEQVR42mWNSU7EMBBFcwtWKIPj2Elsl2MnhG4EYuoNW3bchbM/lEg0onvx9Gt4pSrm9Ynj4zs2LCjtaI2gjdBoR92OO5Uazmz94GfifGQImdomXtee788bnKkp7h7eyPfPjPGA9QuDrLh4QNmJuhOqTihbT926M6oL++MtGx0w1pHFUVaaohsmms7TaI/qPK0JhHRA24gyQm+FFBZSuseMad9rK3u95cbmNSZRNj2FiwujzP/4nfUh8xEyXy8nTi5eeZc3leopwrTg43xB3tPFmTUkjpJZwnTtyR/9KNyWmh+/A5jSwtZqzgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;리소스 기반 정책 권한 추가 방법&apos; title=&apos;&apos; src=&apos;/static/787831e970842de95a0191f77ac52879/ca1dc/lambda-function-setup-3.png&apos; srcset=&apos;/static/787831e970842de95a0191f77ac52879/e7570/lambda-function-setup-3.png 170w,
/static/787831e970842de95a0191f77ac52879/f46e7/lambda-function-setup-3.png 340w,
/static/787831e970842de95a0191f77ac52879/ca1dc/lambda-function-setup-3.png 680w,
/static/787831e970842de95a0191f77ac52879/e4da8/lambda-function-setup-3.png 766w&apos; sizes=&apos;(max-width: 680px) 100vw, 680px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;리소스 기반 정책 권한 추가 방법&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/da2fafe1e13904d60d7778301790103a/dbf98/lambda-function-setup-4.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 75.29411764705883%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACSElEQVR42qXTS2/bRhDAcX6AHHqp7dbWw3yTy+Uu3+ZDkiXKtmwnRYIeiuTj9N7P/C+4QgXXcXzJ4YeZnQEHwyVoFVVPv9qRpCVnF4ufZi3tCKkb49pNuJz7XC2Ck9mL/GVt9kbvcuZheUHKuH9iOz6S6gbbFbi+NBw/xfakMeUvHWsSJzie3UCxdGKsRaiYVSN+vWWZrZnrgblesdQdQVKgqgFZdHgixxcFXpQRypIkb5F5ixtnpufGuRluXYUZv/efKD5+w1n/wXk+clHec5nfYocaUQyEqsURJa6ouA40bpQh8oGb7QFZrfBMPcMOJJYvMkS9YpmUOGmDl7W4uiXMW4SqmPqByAiT3IhkQZyWphaYWkEgckJZ4IUpVpTkbMYDw2ZPvx7RRUtW9YauBiJZmgf/R+TEaYFQxSkmujwOdGNFu32gGnaoesCOM5zpPkw8cqc7esWXx2H/EarEi1IsHaV82T8y3gzsmoH7bsO26tjVPdu6N7VeldSxohH6pBUa8dbA2on4XA8cqp7nejBu44zOS+h9aUz5a2MgUTInej0wTzKe+w0bXbFRJV2s6ISiF8f4lqnXJvrtV97YAX81Kz7fbPjS3nJQDZ0d0zuC3om/09kRa1/yHKWoJDttOH0UfxqYRorH2z1P4wPP+wOP23vu1yN3qx11WvxQJXMSVZrNJo5omHka62rpM3djFhMvZu5E5mxM+TuWTsS1G5tf7q5xWeU21vlvS87O5y8sTvn5xeJdv57NmE0LXcf88/UDf//5C/8CBlLHknOxouwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;보안 주체 및 작업은 사진과 동일하게 설정해주자&apos; title=&apos;&apos; src=&apos;/static/da2fafe1e13904d60d7778301790103a/dbf98/lambda-function-setup-4.png&apos; srcset=&apos;/static/da2fafe1e13904d60d7778301790103a/e7570/lambda-function-setup-4.png 170w,
/static/da2fafe1e13904d60d7778301790103a/f46e7/lambda-function-setup-4.png 340w,
/static/da2fafe1e13904d60d7778301790103a/dbf98/lambda-function-setup-4.png 612w&apos; sizes=&apos;(max-width: 612px) 100vw, 612px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;보안 주체 및 작업은 사진과 동일하게 설정해주자&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;보안 주체와 작업을 위 사진과 동일하게 작성함으로 써, CloudFront 서비스에서 생성한 Lambda Function을 호출할 수 있다. 다음으로, Lambda Function의 기본 구성을 수정해보자. Lambda Function의 기본 구성의 메모리와 임시 스토리지는 각각 &lt;code class=&quot;language-text&quot;&gt;128MB&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;512MB&lt;/code&gt; 이며, 제한 시간은 3초로 되어있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/d1771949541fe38124fbbaa6f4255e2b/98650/lambda-function-setup-5.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 75.88235294117648%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB2klEQVR42p2U3XLaMBSEeYhOO800KQQCli3b+rElWbKFDRgSkly0nb7/o2zHSiFJJ9A2F9+NR7M6u2flEUklSuPh/BpN22MyS/Dh0xU+fh6/i9GXr3PwwmFBJS7GC1xdk3dzcTnD6HpO4fwGTHtcxxzTOcV0kWEW5f/FDWG4nEQYzRMGu9wEu0I5sNKCcoUolWeJs9ckeYHxLMbohuRw3Q7ab1FULUrXIc4VJosMU8JOQtITghHlaG8fwGyHTBqkQkPXLaSpjwff4uSE0yhDWa9AMoU416DSQjcrUFYiSgVIKhAfyORJjoILyrHa3cP3exR+jcLUyKUJgilXIc9EaCRM/ZvgPGYww5ZVg7RwSIWBNA2krsGVBVM1aNWB6ubslEfB2WDZdUiYRsoNKDeo/CpkyJUDyQQI5YGDIPkdxUviXD5bXt89wPR75MaDa/c0WVkFUqHCojI5XPgUQyY08qIKFcuFBissWFEdBCXax59w9z9g777B77+ju30MVap8H7DLbWCIxjQ9bLtDYZfhwkGYh/5Wzxn6zR6u3cEOAsstqmWP0nahly85fCvtCpkwGCr3pmXX9sFGwkrEQ89YEQ6fg/yxoFcvhQz9EwYx0+EnMdSG/KV3p7b8CzqD0XToQL5SAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;Lambda Function 기본 설정 변경&apos; title=&apos;&apos; src=&apos;/static/d1771949541fe38124fbbaa6f4255e2b/ca1dc/lambda-function-setup-5.png&apos; srcset=&apos;/static/d1771949541fe38124fbbaa6f4255e2b/e7570/lambda-function-setup-5.png 170w,
/static/d1771949541fe38124fbbaa6f4255e2b/f46e7/lambda-function-setup-5.png 340w,
/static/d1771949541fe38124fbbaa6f4255e2b/ca1dc/lambda-function-setup-5.png 680w,
/static/d1771949541fe38124fbbaa6f4255e2b/98650/lambda-function-setup-5.png 835w&apos; sizes=&apos;(max-width: 680px) 100vw, 680px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;Lambda Function 기본 설정 변경&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;나는 큰 이미지를 리사이징 할 때, 런타임 오류가 발생하지 않게 하기 위해 위 사진과 같이 메모리와 임시 스토리지를 모두 &lt;code class=&quot;language-text&quot;&gt;1024MB&lt;/code&gt;으로, 제한시간은 5초로 변경하여 설정해줬다.&lt;/p&gt;
&lt;h4 id=&quot;S3-Access-Point-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#S3-Access-Point-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;S3 Access Point 설정 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;S3 Access Point 설정&lt;/h4&gt;
&lt;p&gt;이미지 리사이징 작업을 진행하는 Lambda Function 설정을 완료했다면, 이제 CloudFront가 Lambda Function을 통해 S3 객체에 접근하여 이미지 리사이징 작업을 진행할 수 있도록 S3 Access Point를 지정해줘야한다. S3 Access Point를 지정해줄 때, 지정할 S3 버킷과 동일한 리전에서 진행해야 하는 것을 주의해야한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/5925581f5397c463e23ddc0967d50699/dbf98/s3-object-access-point-setup-1.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 41.76470588235294%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABuElEQVR42k2QSW7bMBRAdYkOsS150CyR1GBJlCXFk9ykcRAgySZFFzlQkUUXBXrcV5gBgi4e+Af890la+3bH7enM6XCDXHeodUd1fY0fFzhRyUzmiESgK41KUpxlTBqEOKvEYC9jEs9n6cYmt3I9oHcn8qYnVSWxLJFFw9R2cQJFUnYESYGbFFzZAZOpy5eJx9XUZTK7nCu+Tj0mBherqjboskHId1mi1qR5zdTxCMKC3eGRbjgTF3vSrCFKS0TekKiaMC2RZWtqiapYBRLroe74cX7iXg9GlGYVMq/4bIc8pTZ/Xq54e5nx+3lBKwoWUWaGI7HGCzOzxA0VYVqw8gTWoWp5HL9zPt7Q7UYjFHnFJzvgKXP5+xry9jPk13PEsRlohhP99Yl6s6fZ7Gn7I8P2G3U/sgoUVt3vGO8e0MOBou4+njybe8z9nLS9J9VnRHPLdrxDdweyskPmLSLXqGJDvu7MXy88iVXpgd14i+73dNsRVWojnc19Fr4gXnfvlBviTLPwFY4rmHvyA8eTpr70JZbIa4q6p9SDueElFkVjhPYiwg0UK19+sPwvvnDp+1FOImuipOQfI7j6pq/GybQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;S3 Access Point 생성&apos; title=&apos;&apos; src=&apos;/static/5925581f5397c463e23ddc0967d50699/dbf98/s3-object-access-point-setup-1.png&apos; srcset=&apos;/static/5925581f5397c463e23ddc0967d50699/e7570/s3-object-access-point-setup-1.png 170w,
/static/5925581f5397c463e23ddc0967d50699/f46e7/s3-object-access-point-setup-1.png 340w,
/static/5925581f5397c463e23ddc0967d50699/dbf98/s3-object-access-point-setup-1.png 612w&apos; sizes=&apos;(max-width: 612px) 100vw, 612px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;S3 Access Point 생성&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 530px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/66c743e282d9a4eabfcc3b2f7e6aa1e2/a0177/s3-object-access-point-setup-2.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 101.76470588235296%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAADPElEQVR42n2U2W4cVRCG5zESz9hju/d9P3P6TG+zeeJFmMTICZEFIrwAN7lA4oILJCSE4JE/1D1jYxybi091qqr1l7pO9z9qF1uqdkPdbji/fItszzh1E051F90M0QyfU91D0z10w8e0QjQz4ETr+8HQN+1oYHyoM4qzkrRQ5KJCqo5yvkCoFlUvHxBlg100OPMN7qxFN7z9wADN9LGcCNOJOJhojPppuhUyPjSZTC0mRybjI4PxkcnBcN5zbDM+dhhPLQ4Od/1xHw8NDib6UHvdC/qxIEwlJ7qP1r+OFQ4DdCt6QviI6AsMO+ZwajGK85J2tSUpFEEiCBOBHxc4Yfa/uE/wopxjzel3KKkXm0FUNUuqbk0uq4eHXuJFwaSYs9he0a4vsMMc3Ykx3ATTSzH7OBBjevvaHveJ6INgv0NRrRDVkqiY46clQSoJMoUlWqzZYsCddUPNjWfobvKyYCbmFGVNlEkyochkRTabDyRlQ6Y6snlHrpphFalQBHGOF6bPC1plR3X+Fe32kursklDWuJnEzSVeT7bDL9SAl5dYcYH50g6rIOW6XnBddVzIisoJmdsBlbOjP99zn9dOQOlF/7ntB8GbXPLu5j2bbsO627Bq14hMIgvFLC+Zy5q6rMnjgjTMEOkMkRRs7eB5wU/LM/76829+/fkX/vjtdz7/9BnbCPDdBN9JEMmMzWJDJSuKuGC73LJp12ydFwTnUc5WVqwKhXJCKi+i9mPmbrjDCSltfzhXboTar0L68fOCZZRz++aKmzeXrC6vWV5cD7ebqJa4j2VDLCvsKN8RFzv6z+YRXlzsBC/8jK+F4lyzsJ0QwwmxvRgnSLH9BDfYfR6On3yBvWfIg5Tpqc3o5MjAPLExNJdTw997XMCx5g6+17vJq/HpwOux9i+THa/2ee+Fg30VasH7u0+8u73j9tvv+fjdj0N8+81HPtz9QN1tKcoO1WyQ8+UQ+3ymFoiyo6zXyHpNKFomxxYjJ5aI1RVhWhJnijCRpEVFJvq/Rw2xkC25bMhFTTZryPcMvVlDLBrMvGEyNRkZqSJVi2FiLlu8SKD3lt4bgptg9GbhREPtPur2rvYY202Yai7/ACjddxmmVfiNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;S3 Bucket 지정&apos; title=&apos;&apos; src=&apos;/static/66c743e282d9a4eabfcc3b2f7e6aa1e2/a0177/s3-object-access-point-setup-2.png&apos; srcset=&apos;/static/66c743e282d9a4eabfcc3b2f7e6aa1e2/e7570/s3-object-access-point-setup-2.png 170w,
/static/66c743e282d9a4eabfcc3b2f7e6aa1e2/f46e7/s3-object-access-point-setup-2.png 340w,
/static/66c743e282d9a4eabfcc3b2f7e6aa1e2/a0177/s3-object-access-point-setup-2.png 530w&apos; sizes=&apos;(max-width: 530px) 100vw, 530px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;S3 Bucket 지정&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;이제, 아래와 CloudFront에 대한 정책을 아래와 같이 같이 부여해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AllowCloudFrontAccessPoint&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;Service&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cloudfront.amazonaws.com&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3:*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;{{ Access Point ARN 기입 }}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;{{ Access Point ARN 기입 }}/object/*&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Condition&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;ForAnyValue:StringEquals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;aws:CalledVia&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3-object-lambda.amazonaws.com&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;S3-Object-Lambda-Access-Point-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#S3-Object-Lambda-Access-Point-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;S3 Object Lambda Access Point 설정 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;S3 Object Lambda Access Point 설정&lt;/h4&gt;
&lt;p&gt;다음으로, CloudFront에서 Lambda Function을 트리거 하여 이미지 리사이징 작업을 실행하도록 S3 Object Lambda Access Point를 설정해야한다. S3 콘솔에서 아래와 같이 Object Lambda Access Point를 생성해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/d9cb5f7dda601849699f36434f6fe2fe/dbf98/object-lambda-access-point-setup-1.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 82.94117647058825%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACwElEQVR42p3TW2/jRBjGcX+LbZtzbCdz8GHs+BQ7TdKkSdpuuhJcLLCLEBKCancrhJAAsQgQl1wgJL7wH8VJy7ZCQuzFT8/MO/ZrW56xVpst0/mau1xfPKMo5yxXVzzdfoCfTThpuzRaDs1dth0aTZvjps1R0+Folyd9nhx3a5ZUEYHJiJOyTh0khHFOYHLitCSIC4SKUP6IMCrwwxTlxfSFoaFTWl7GcUfw5KizbxglEzo9RbMzpNUVtXadQ5rdfa3dkw907qm9vqJr6/orrGw8ZyAN9iDAGe7ZA7+e77gi/FfOI6409UtYk8Wa88trFpsrzi+uqWbnROmYOK+IshLPJKhg9J+0SejaEqs621AtLmuT5SX56TkyGqPiCmnGuF6Kq0e4aoQjYxwZ4aoYHSYP+FG2b2iykjAZY9KyFmcVMjtFjBeIYo4ql5j8tF4TfsxQm5p4RHoRnZ7AcmTIQO+ebhjomEZP1D/jXY2D5m7toPHIrnbStLHOJku2qy3r2Ypnm2v8YYhyPLTr16St9xyvritb770z1ofrW+0B1tMoYx6lVCokbNlkjqRwFZmrGPUGTL2Is3DE1DNMZMCpCqlkQCX8el4Kn3ygmQufsONivXl1y29//sXt92+5/eEtX//4c+2bn37ll9//oFhvkfkUXczoegldb4QYFei8QsQ5fj4hXWzIxlOijoP1+sPnfHvzmq8+fsnNi8/44vknfPnRS159+jnf3bzharZkWUxZZBNybchkSOXHTE1K6cfM4ozNZM7FqGDUdrBmScGqmnExXbDIK0JHYlxFaAv83oBMBRSeIddhnYUX7Rur8D4T4VNog9sXWHE5I0hLhmGCjDKkSf8RZXX9XnAQPiQO93bqfZiMCeIcz6QoP0bvdv178O9OSjKecufx7v8/7k+KPfTou4r+QLMbvy9H+LS6Ln8DTs0VS7vc10EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;Object Lambda Access Point 생성&apos; title=&apos;&apos; src=&apos;/static/d9cb5f7dda601849699f36434f6fe2fe/dbf98/object-lambda-access-point-setup-1.png&apos; srcset=&apos;/static/d9cb5f7dda601849699f36434f6fe2fe/e7570/object-lambda-access-point-setup-1.png 170w,
/static/d9cb5f7dda601849699f36434f6fe2fe/f46e7/object-lambda-access-point-setup-1.png 340w,
/static/d9cb5f7dda601849699f36434f6fe2fe/dbf98/object-lambda-access-point-setup-1.png 612w&apos; sizes=&apos;(max-width: 612px) 100vw, 612px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;Object Lambda Access Point 생성&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;우리는 Lambda Function 이 S3 에 저장된 Origin Image를 요청해 이미지 리사이징을 진행한 후 Client 즉, CloudFront에게 전달해줘야 하므로 이미지 리사이징 Lambda Function과 연결해야 한다. 따라서, S3로 &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; 요청 허용을 꼭 체크해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 536px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/5ed6eadd2d46decfbc1307434bc96960/e52bb/object-lambda-access-point-setup-2.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 100.58823529411765%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC4ElEQVR42qWU63LbNhBG9SJxGzeWRBIkCID3OyXqak/i2m3amU7f/zFOBxCtuomTdqY/zuxyuVzuAh+wWAeatOj5sJK8+3HNzXvvK2zcvZvta27mb364vbBQSc3x/om8GolNTagKbCxOaiJdEpuKrBzQWYvJWpK8Q6WNa8L6JusQcc77D4Lbu5DF0o8xeUPejGTVQFJ0FM7vScvOIeIUP0oIZOLsi//y7IWGtdCOhU3upyPtZk/ZbijakWaYqPotZbehbMf5Q/NdAnmxi6RoKZqBn1aCpS8dd170Dzyh8EJ9YfbXQjmsbwupzC5XxmIVxHSbA0neECclKq0uNqtQNpZWyKREmuISy6rZr12uffd6gkXqx3wej/w5HXmqOh7zmvuk4FOoaFWGSEr3g/9cUK0EH3XGH7sjz83AOSk4WQJJYRNV9ve4buRXo78a+VrwzpfoomF7+kjZT8RZhSk7kronrQfX3dsbkeC/sVkLu+hWIudPzwz7sxvF/iCte3RWuySrhMAyS8XJSL69266gXYvN8YF+f6aykhkmuulIPe6ohh3t9kiz2TuqcUda9YQ6xw/fKLgMFHk/ueSy3+LHKZ4dZbaefLGXmC9T1pFhbaVj7Yx37VBout0Dv/78C0+Pz5wPD+jIEAUxUiiksxc/fk2or6hQE0V6FvZa8HtR89s4cYwTDrFhCmN2obqyjzSTiNn6kWPjR4xeyLC2CLqV4LQMEKFmka8CttsT3eNn8t096faEGY8sTcUyqVmlDV7WcRvn3PiKd4HiNspY6oKo6pBVj97sGaUhsgXVOuQkFCdTcjYFD2nFSedMoaG9EzMB7dJ2Ejr6te1ScpAJ+8hwUhnnQF46tDosiobd9kDXbiiLhjKvKfMGGSpkZIgjjfySUBMKRSQUYRATzAJ3Z1nag52UhKZgKdSM3cXku3hfcC1obxt7bZmiQajU6Sv4l+vqWyyWgaYe9u7Y+TLDi9L/xV/Iz3yuxKrZ5QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;S3 GET 요청 허용 및 Lambda Function 지정&apos; title=&apos;&apos; src=&apos;/static/5ed6eadd2d46decfbc1307434bc96960/e52bb/object-lambda-access-point-setup-2.png&apos; srcset=&apos;/static/5ed6eadd2d46decfbc1307434bc96960/e7570/object-lambda-access-point-setup-2.png 170w,
/static/5ed6eadd2d46decfbc1307434bc96960/f46e7/object-lambda-access-point-setup-2.png 340w,
/static/5ed6eadd2d46decfbc1307434bc96960/e52bb/object-lambda-access-point-setup-2.png 536w&apos; sizes=&apos;(max-width: 536px) 100vw, 536px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;S3 GET 요청 허용 및 Lambda Function 지정&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;생성한 Object Lambda Access Point에 대한 정책은 CloudFront를 만들고 난 후 다시 설정하도록 하겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 314px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/4feeeeb3ecc63072323e193e4a4dd24f/3f975/object-lambda-access-point-setup-3.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 61.1764705882353%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACbUlEQVR42qWTW08TYRCG+wdMJOHUE93dlm277XYX7FJKSykFWii0pZykCAFEjJF4JJqYaEz8AWriD1C8EZAbYyJQDvEC9E+RPLq7gPHSOMmTmXkz82Yuvs8hBqKYSO0qPkmhTQjZWQxb2JqdL2qvL4QgRawdE8EfsTxa3RKOiJYgqncjBlR6sgVyw2WMZD+pbMHqk315csMVEukBEr2DZAtl8qVJ4sns5SFqR5KwatDiFHAElU4U1bCukSPX0Iw00Y5u9K40elcv4ZiBZqQIx+KE1DixeIp4TxZZ6aTZKdLqkvCKYcu41SXiiOoGYnuEW7fXODn5wbfdffbrh+ztmxxQrx/Z+eDIwqx39+pWfXT8nYPDY05Pf/Lg0VMaGt041I4uvKLM85evMOPs7Ix/iYv512/fceVq8x/DF/9p+ObCMKp3Ickq04srbH35yvtPW3zc3rHZstnY+vy3tr3DxqatfdjctvaW7t6jocmDQ9EMNM2gKAXJewSGfQGGPAKDboGhc/JeiQGXz8LUB5xtDPv89pzLR87ZRkmUkcxnI2txUlGdMSFAtVjmzsISSzM1lmdqzJaqLE7Nsjq3wHx1mpnRMsvX56hVJpirTFpza4s3ma/doNIeRm/xnhsqGv2hKGquQGqkTO/YONnxKVKjFTKlKqmirfWOmXUFPTNIYqhIIl9E6U4jJTP0KTHbMKjFSUZ0Fv0yq4MFHpfGWa9M8KQ6xXqlavUmz6ZneThaZi0/wnI6y/2REiuZHMVAiHwwwrJftg3d3gAeb4CwS0Ru8tDe5L4k0OiykJvP9UZT9xBs9Vq9qSsu4RLX75/yC3cA+k/zNSiMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;생성한 Object Lambda Access Point 별칭&apos; title=&apos;&apos; src=&apos;/static/4feeeeb3ecc63072323e193e4a4dd24f/3f975/object-lambda-access-point-setup-3.png&apos; srcset=&apos;/static/4feeeeb3ecc63072323e193e4a4dd24f/e7570/object-lambda-access-point-setup-3.png 170w,
/static/4feeeeb3ecc63072323e193e4a4dd24f/3f975/object-lambda-access-point-setup-3.png 314w&apos; sizes=&apos;(max-width: 314px) 100vw, 314px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;생성한 Object Lambda Access Point 별칭&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;다만 CloudFront를 생성할 때, 원본 도메인을 Object Lambda Access Point로 지정하여 Lambda Function을 트리거해야 하므로, Alias만 복사하고 넘어가도록 하겠다.&lt;/p&gt;
&lt;h4 id=&quot;CloudFront-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#CloudFront-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;CloudFront 생성 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;CloudFront 생성&lt;/h4&gt;
&lt;p&gt;이제 우리는 리사이징할 크기의 정보를 쿼리 파라미터에 포함하여 Edge Location에 캐싱 처리를 진행해야한다. 따라서, 아래와 같이 길이와 높이에 대한 정보를 &lt;code class=&quot;language-text&quot;&gt;w&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;h&lt;/code&gt;으로 캐싱하도록 새로운 정책을 생성해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/868933c56f49c9090b51e82837347a41/0bbcd/cloudfront-setup-1.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 68.82352941176471%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAABgUlEQVR42p2T6U7jMBSF8yCoKlm9xLudxEmgAVoQy/u/zkFJh01Ny8z8+CzLts65i29SMg3teggbsck5NinBJqX/xdW2QsJVQNtP8OM9Uu6QlTWySv4zOVHY5hyJ9hE69JC2wbxXrgNX/leYdD+Yz0qmkJhmhIsTqG7BTUQlAlKikFGzSko0ytqD6wZM2lNB2wzY7Z8hbQciLGrTgOsAKtxFmPTrEUoXMT2+YXd4xXD3BBsnMN2iZAYlt6sQ4RfTVUGmAnzcQfkByvcomEZB9VmxgpmjoDojOIff7/YwzQDbjfDxZkmbCrtaeDmX5FJTKm7ATYeyDii4X8iZRUZ/kjMHYVuM0wHStieGn4KkNkvNtoVEWqk/6BMyYiBMi2HaQ5jmfITz0t3c4+7wsjh/XJ5jeXMpZe07hHgL343QIULYU/e/4ds/HBHGB9Suhwzj8rmPHXWrFMyCyIDatODqK5taB1RMI8nJcQ43GVtmcea6FBdJ59mlCjkRnxRU4rpgeAdZ7KyHYxo86QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;쿼리 파라미터를 포함한 CloudFront 캐싱 정책 생성&apos; title=&apos;&apos; src=&apos;/static/868933c56f49c9090b51e82837347a41/ca1dc/cloudfront-setup-1.png&apos; srcset=&apos;/static/868933c56f49c9090b51e82837347a41/e7570/cloudfront-setup-1.png 170w,
/static/868933c56f49c9090b51e82837347a41/f46e7/cloudfront-setup-1.png 340w,
/static/868933c56f49c9090b51e82837347a41/ca1dc/cloudfront-setup-1.png 680w,
/static/868933c56f49c9090b51e82837347a41/0bbcd/cloudfront-setup-1.png 820w&apos; sizes=&apos;(max-width: 680px) 100vw, 680px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;쿼리 파라미터를 포함한 CloudFront 캐싱 정책 생성&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;쿼리 파라미터를 포함한 정책을 생성했다면, 이전 단계에서 생성한 Object Lambda Access Point의 별칭을 원본 도메인 이름으로 설정해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 620px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/ad1d364295fda39905fbe4bd6f11149f/abf7b/cloudfront-setup-2.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 52.94117647058824%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABnklEQVR42qWOa2/SYBiG+x/MJnNjwjboAUorfWuho+UwaDltwCiOBYgoxERNTPT/J5fpu8zFLyZuH67cz/0c7jxKxvI5MmscnFzwpvye0/MSwmvw+jjP4VHuv1GCVoz5zsPzW2RzKgeZt88K+hMYtiOqwucqGnGa13h1mJWhz0U5yalk8xqZ43P5YRr6EpT+6JagHcvQs2KFfMF8EUrRsFENG71clVrQLQqa9aTaky9qKZUHUq/bf2PYKM1un954RnQ9o9bqUap6mK6PKeqURV3WZacma134klK9hVH10C2BbrvolothuxgVB0X3O5iXXSpBD7vZpxJE0lthhB3GEqczwgpjjHCA3hyiBn2KIkQVAUWnIfXCCVAdH8Vq9OiMF8Sze3o3d0TTe6LJUvZGiw2jZMPNcsvgdsX1YsM4WdMZzrlKb9Ld6ZLhfIUfzdBFA6U7mLL79ovtlx98/vqT9f4707sts+UnkvWOZLVnvkp1R7LeS9L54850uZWzyYePWOmHoh7SHU5ox2P8sIvjBXLwL2zxgPWI7F1Ssl1+A0mQWpKOI6zEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;Origin Domain 설정&apos; title=&apos;&apos; src=&apos;/static/ad1d364295fda39905fbe4bd6f11149f/abf7b/cloudfront-setup-2.png&apos; srcset=&apos;/static/ad1d364295fda39905fbe4bd6f11149f/e7570/cloudfront-setup-2.png 170w,
/static/ad1d364295fda39905fbe4bd6f11149f/f46e7/cloudfront-setup-2.png 340w,
/static/ad1d364295fda39905fbe4bd6f11149f/abf7b/cloudfront-setup-2.png 620w&apos; sizes=&apos;(max-width: 620px) 100vw, 620px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;Origin Domain 설정&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;원본 도메인의 형식은 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 아래와 같은 형식으로 지정하면 된다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 별칭 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.s3.&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 리전 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.amazonaws.com

&lt;span class=&quot;token comment&quot;&gt;# Example&lt;/span&gt;
object-lambda-access-7t5engtgxwiiyabj4w6utkygapn2a--ol-s3.s3.ap-northeast-2.amazonaws.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또한, Object Lambda Access Point는 &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt;하게 설정할 수 없기 때문에, OAC를 통해 원본에 대한 요청을 인증할 수 있도록 해줘야한다. 따라서, 아래와 같이 OAC를 생성하고 Origin Access Control로 설정해주자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/7f67e28a34939a54cf115ae40b399c77/dbf98/oac-setup-1.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 12.941176470588234%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAnElEQVR42j3HSw6CMAAAUa4i/lBoaUtb0EKiooB7E+P9DzLGoi5eJpN0l4HTdcLUgUI7hPHIqI4Vevb5XFnysvrbyYqi0Ihc/SVtP9FeJnTdIe2R0oVI+S5WuoCojigfyKRlk5toLS2NMIzSMPoDo20Y3IHEtz0unFntDYuNJN2WLL7Sn2z+5U7PMsWqrDmJiod2vIY7r/ONZz/yBi/kX79EyHq1AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;OAC 생성 및 Origin Access Control과 연결&apos; title=&apos;&apos; src=&apos;/static/7f67e28a34939a54cf115ae40b399c77/dbf98/oac-setup-1.png&apos; srcset=&apos;/static/7f67e28a34939a54cf115ae40b399c77/e7570/oac-setup-1.png 170w,
/static/7f67e28a34939a54cf115ae40b399c77/f46e7/oac-setup-1.png 340w,
/static/7f67e28a34939a54cf115ae40b399c77/dbf98/oac-setup-1.png 612w&apos; sizes=&apos;(max-width: 612px) 100vw, 612px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;OAC 생성 및 Origin Access Control과 연결&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/bf8a74e3ae683483258d2646f764ce63/0f09e/oac-setup-2.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 93.52941176470588%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAACnklEQVR42pWUSY7bMBBFfYcACdJpD7KtgaKokdRsWW6PctvuIUBOEGSTRZB1bpErZJs7/oCUewiMnhYPX0VShapisTqf+gZMGsCwfejEh0VDaLqDrmahNySvZjCm+NgdodMf2agXl7hY7TBbXmK1vYYX5eiPKUYmg6YzDI0W7cjwEe2+C+IKnPV0dIaGA+bHGFsudOIqNYinIjbsO/XbNfLIfoROPJXl+cBERzqZN3uIfIq4mCJKS/B0Ap5VSCczpUlZI5nU4HmFKJ1AFBVE0Z6XiLyCGyboamYbYVotkdcb5LMNsnoN4qfQHQ6TxfdqujEMRyjbYEJ9K2VyTcBkHL2hhY5MkacVwqSEyKZgQQKDBioN3fbbC3sJ2wdxjw6lsdweUC8bLLd78Gxyj8dT2G4E6nFQn8OR+hQ+f0g5SGv4aQ3DFW1qx1RGJIRmutBM71kGhgvDidAbkdYhz6YI4gKGHcCkISwngumEsFgEwviroJ441tB0MF9tsdjsEOdTeFECm4WgMtU3IMshH0NnSAOI1QHN7hr72y8opnPVf7J2b4HeORzTAG5WQaQT5NUcaVkjSgrV7JYTgLBQ8WqHI8NRP4iixmp3jfX+BpvDLYp6iTAtVQPL5nZ8AdsNX3Boyj5kEFmFMC7Uoi2RB3wOFsZggUSouhIZ8RPIfeXQpD4WzRXq5SWCuEQYTxSBKFukncgnN30GWabqLkIXs/UBF5trzJubB7atztZXKOrNCeWsOeoG5UWDvFo8pCx7MIhzBCKHzzP17YtMvRTZRoQFJ5i2C8vxodkRhiTCiB6fnpx5qnbs6WLLC/kfjqJeIU5S/Pn+Hn9/vMPvr+foSoeDsX0/006QM9KSM5KdIC+PUBc/D2f49fkDvjU9nPV1/AMVtVRWZDylNAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;새로운 OAC 추가&apos; title=&apos;&apos; src=&apos;/static/bf8a74e3ae683483258d2646f764ce63/0f09e/oac-setup-2.png&apos; srcset=&apos;/static/bf8a74e3ae683483258d2646f764ce63/e7570/oac-setup-2.png 170w,
/static/bf8a74e3ae683483258d2646f764ce63/f46e7/oac-setup-2.png 340w,
/static/bf8a74e3ae683483258d2646f764ce63/0f09e/oac-setup-2.png 640w&apos; sizes=&apos;(max-width: 640px) 100vw, 640px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;새로운 OAC 추가&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;마지막으로, 앞서 생성한 쿼리 파라미터 기반의 캐싱 정책을 기본 정책으로 선택하여 생성해주면 CloudFront 설정을 마무리할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/6512d1d6146113fae005ef3123fc44ff/dbf98/cloudfront-setup-3.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 58.235294117647065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABvElEQVR42oWT626jMBSE8yarJHsBgsEXcIwBAzYQSNpKlfZ+0b7/U8wKp03VzW7745MsC0Yzc45X95++4cuP3/j8/Rc+fv0JLkvQrPCwXINL7e+WcyoUokS8yModjnCHk6efblE2PfZli8JY1N2Aqu1h7IjGTVBV+7pg0Q6ouxFl46CNg7EHzyLQuIMXteMJUrd4H6XYpdnLgk0/ox1mdMMR4/EOUhv/E2HyGTHNX3XnBZcYquq8yBnxwKMTfmGXiCuWb8KEIyT8LFi7CXa6wb624KoGkxVy3UKWHURhEKYZAiL+S5hkSPj+MrCVKC20nSFKh3UksIlzbGOJbbLHJuLYBCk2Af0n25AjZgq5NlB1512uWHsAcydQO4O5I2jlEGuLuLSImDxHu9RxzVWH/XSD+e7er80w3/rJ0kxB6hpCVX4YV0L0iSgVCMkZLygri2Y4wfQnNA9U3YQgkQhIjq2Pxp6xxF1/SLEOKAKSIdf1U+S0sKBmBK1HMDOCNxOIdghzg52y2O1bRFR5Qqqw4xpvI4Y374gXDVOJNFP+ZXmHsWoQl0tv3aW75Ux0C1I5kKK5Wqe/O1ycPa7NHwl7gx0lec+6AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;CloudFront: 쿼리 파라미터 기반의 캐싱 정책 설정&apos; title=&apos;&apos; src=&apos;/static/6512d1d6146113fae005ef3123fc44ff/dbf98/cloudfront-setup-3.png&apos; srcset=&apos;/static/6512d1d6146113fae005ef3123fc44ff/e7570/cloudfront-setup-3.png 170w,
/static/6512d1d6146113fae005ef3123fc44ff/f46e7/cloudfront-setup-3.png 340w,
/static/6512d1d6146113fae005ef3123fc44ff/dbf98/cloudfront-setup-3.png 612w&apos; sizes=&apos;(max-width: 612px) 100vw, 612px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;CloudFront: 쿼리 파라미터 기반의 캐싱 정책 설정&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;CloudFront의 대체 도메인 설정은 생략하도록 하겠다.&lt;/p&gt;
&lt;h4 id=&quot;S3-Object-Lambda-Access-Point-및-S3-Bucket-정책-부여&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#S3-Object-Lambda-Access-Point-%EB%B0%8F-S3-Bucket-%EC%A0%95%EC%B1%85-%EB%B6%80%EC%97%AC&quot; aria-label=&quot;S3 Object Lambda Access Point 및 S3 Bucket 정책 부여 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;S3 Object Lambda Access Point 및 S3 Bucket 정책 부여&lt;/h4&gt;
&lt;p&gt;이제, CloudFront가 Object Lambda Access Point을 원본 요청으로 사용할 수 있도록 Object Lambda Access Point 및 S3 Bucket 정책을 각각 업데이트 해주자. 각 정책들은 아래와 같이 설정한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;# Object Lambda Access Point의 정책

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;Service&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cloudfront.amazonaws.com&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3-object-lambda:Get*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ Object Lambda Access Point ARN 기입 }}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Condition&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;StringEquals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;aws:SourceArn&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ CloudFront ARN 기입 }}&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래의 S3 Bucket에 대한 정책에서 사용자 ARN은 현재 작업 중인 계정의 IAM 사용자 또는 역할 ARN을 넣어줘야한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;# S3 Bucket 정책

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Policy1234567890&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;S3ObjectAccessPoint&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;AWS&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{{ S3 Bucket ARN 기입 }}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ S3 Bucket ARN 기입 }}/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Condition&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;StringEquals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;s3:DataAccessPointAccount&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ 사용자 ARN 기입 }}&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 357px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/273653001ebae531216902eeab73529f/e07d4/account-id.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 114.11764705882352%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAAsTAAALEwEAmpwYAAADp0lEQVR42pWUTWgjZRjHg3hVrKvWpmkyySSTZiYzk0kmnzOTmXy0Tfq1bWy7/WDblbaWXYpSBEUURARlwdPizQUPe1RBD8u6iwUrevDmxbOIKOphPezJ009mln7ZVdLDj5kXZn68z/953jeUUB3czhKK2UbKuUz2NkjpdfTKBN35DUTVQSm2GZ9dQy9PMJKu8MLqDh/c+Ih3r98Inld29hDlMqLuEvKFk4ubKLUuyXyT2dUdRMNDs6eYX7+GoDnIvnxpE7Mxx6BUZuvlN7i3f8Cnn9/m7v43vPX2e6RlEzHXICToLvJomVYqj5MqUIvrAeVolkI4Q1XQqMTUYG2JBi3JxJCKPCOaDIkmg4kCkXQVUfcQVZtQPO/hpIvMmA1anQU8d4rvD77l7/v3+evX33jw+x/8+dPPrC5vMt5Z4FJngaaYQzAapHSXpI9WR9TqxOUqobRu4o4aTBgONW8Gx+py6+YtvvvqgP3b9/j6y33ufnGHizMr2O40s40Z2imDmOEFWScDHFI5j0giSyhqOmR1m/F0kaZk0pAK2H7ZMfUUXjKPlyrQTuUpqhYJ3UPS3WCXvlgyGgzHs4SeUhyG1TojWp0h2SKqu8SMxhmiuhcwkvMQNDco8RiHpO4xHFcIPS1bDGVqWK15Vl7cDUbHHxU/l/9CPMMJ4UCmRkSxyRSaFN3ZIGTxkT/9HyeFap2wYpO3Jql3FoNwk4fd61t+QpgyLeKqRd6epjm1EnwgKDbxrEM8a+MP/rmEqmYgaVXS+SZycQy1MoFe7aJVOxjWZNC9hHoOYSylE00XqbZ6zC1vMzF3mW5vg9nlLeZWtsnVusFO+xY+H80QGy2RzHmk8w0kf2BzjSBLf3f95XhCOCzIhEWD5vQqW7uvc3Flm97aDpeu7LJ4+SoFZzrI1D8RfQmHYhmE0VJw9Rw2Ia46wbO/hvxLGBZkIqk81vgCSxvX6PTWmVnaZP2lPWpjPWKy1UfZJ4UxP8MiGbONVplANtsopTEMe4qM2Tr/2AQlZ0oPr5/DkrM2gmIF7486erGsS0T2iCjHiHrjWBjPlJFyfnfdI47W+llq1Sptt0TbLTPmlmnWS+jFOmEhe9yUfo+ZP+Q/vP8kDz4M8cv1x/jxncfh4xCvLss8EdYPSy6fOcM+iUDiniKhudy8OsKd1y7w2d4gn7wyyMGbA6xN5hmIaA+bEpUKCEoVQa4cEQti8DO0SWRrpxhMOzwnOTybrAVcSDpE/XtVyPAPSQz+ZMWInmsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;그림의 계정 ID를 S3 Bucket 정책에 추가&apos; title=&apos;&apos; src=&apos;/static/273653001ebae531216902eeab73529f/e07d4/account-id.png&apos; srcset=&apos;/static/273653001ebae531216902eeab73529f/e7570/account-id.png 170w,
/static/273653001ebae531216902eeab73529f/f46e7/account-id.png 340w,
/static/273653001ebae531216902eeab73529f/e07d4/account-id.png 357w&apos; sizes=&apos;(max-width: 357px) 100vw, 357px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;그림의 계정 ID를 S3 Bucket 정책에 추가&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;이로써, S3 Object Lambda 아키텍처를 성공적으로 구성할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;달성한-성과&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%AC%EC%84%B1%ED%95%9C-%EC%84%B1%EA%B3%BC&quot; aria-label=&quot;달성한 성과 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;달성한 성과&lt;/h2&gt;
&lt;p&gt;나는 클라이언트가 S3로 직접 요청했던 아키텍처에서 CDN과 S3 Object Lambda를 결합한 아키텍처를 도입하여 아래 사진과 같이 &lt;u&gt;원본 이미지에 비해 &lt;strong&gt;약 99.69%&lt;/strong&gt;&lt;/u&gt;, &lt;code class=&quot;language-text&quot;&gt;.webp&lt;/code&gt; 압축에 비해 &lt;strong&gt;약 99.14%&lt;/strong&gt;의 압축률을 얻었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&apos;gatsby-resp-image-figure&apos; style=&apos;margin-bottom: 16px;&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-wrapper&apos; style=&apos;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 653px; &apos;&gt;
      &lt;a class=&apos;gatsby-resp-image-link&apos; href=&apos;/static/e8080c7c8115e7f3a37bea409f2cf4e4/ffe7f/result-image-size.png&apos; style=&apos;display: block&apos; target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;
    &lt;span class=&apos;gatsby-resp-image-background-image&apos; style=&quot;padding-bottom: 42.94117647058824%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAACV0lEQVR42jWS3UtTcRjHj7Sd8zvnt3N21L2gc7rZNjVftvkSGRmSa8v5gjPDmjMTZY4iNYrK8g0pUDCSogiCiO76A4IuuozoMrrropv+gP6FT+xIFw/PG9/neeD5KIqiYFleIpEoumFieW0MQ6ILgRAaUkpM03S8rusYhuHUq7qmUIhEoo14ogOvXYv0SJTpe9uU3/7k5vtfFLd3yJWS+Bt9CE1DmiZWXSNeXxizth5DuhCqm2BziPzKMpU337j14TdLRx+ZWD5DPB1G2Xi3ycHXPzz9/IPxSpZ0rgXLb3JCShoTUSKpPiI9ffhaQvhbGlDcbmI9MW4/W2b30xeef//L3FaZ/okIoXYfSv56jNUni6w+LrBYyZK5kqKtsY68cLPSGefu6V7u9CUpt8dZTXWRUwXpZJCRYpilh5Os7c1RWc0wWRqkuz+Msn+wxtHhFntb67w63GH9wTzxaAPNqkrUI2j16ER0QcyUtBgaIZeLdNdJ7m+UeP1yl92tNV7sP2Jzp8LwSBplbGaWa/NlZqevks3kmRydwOcPUqNpaKaF7rXRLQtVehAekxpVJdTUzMWJAoXiAgtzN8hmRpkanyKR6EBJdPdSmF/gcrHA4PkMmdwU9b4gutDQhEB6qp830Q3Did0uN62xGMmBAQYzo5TKJc4OD3NpfIZo7BTKeD5LqrcP27YxLRuPVYtheJDSwLa9WJaFbugOMlVTVY2A38dY7gLnhoao89U7qMmq1vSipNJpAoGAs/lYJBwGqxeZluUw+H9Y1UT1ailp7+gkmUw6+TGfx/1/WzgZu/MAOb4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&apos;gatsby-resp-image-image&apos; alt=&apos;개선된 이미지 압축률&apos; title=&apos;&apos; src=&apos;/static/e8080c7c8115e7f3a37bea409f2cf4e4/ffe7f/result-image-size.png&apos; srcset=&apos;/static/e8080c7c8115e7f3a37bea409f2cf4e4/e7570/result-image-size.png 170w,
/static/e8080c7c8115e7f3a37bea409f2cf4e4/f46e7/result-image-size.png 340w,
/static/e8080c7c8115e7f3a37bea409f2cf4e4/ffe7f/result-image-size.png 653w&apos; sizes=&apos;(max-width: 653px) 100vw, 653px&apos; style=&apos;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&apos; loading=&apos;lazy&apos; decoding=&apos;async&apos;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
    &lt;figcaption class=&apos;gatsby-resp-image-figcaption&apos;&gt;개선된 이미지 압축률&lt;/figcaption&gt;
  &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;또한, 불필요한 대역폭이 줄어들어 &lt;u&gt;AWS Data Transfer 비용을 감소&lt;/u&gt;시킬 수 있었고, 클라이언트에서는 &lt;code class=&quot;language-text&quot;&gt;.webp&lt;/code&gt; 압축만 진행했을 때에 비해 &lt;u&gt;TBT(Total Blocking Time)가 &lt;strong&gt;약 98% 감소&lt;/strong&gt;&lt;/u&gt;하여, 이전보다 더 빠르고 효율적으로 서비스를 이용 수 있게 전반적인 &lt;strong&gt;사용자 경험을 향상&lt;/strong&gt;시켰다.&lt;/p&gt;
&lt;h2 id=&quot;마무리-하며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EB%AC%B4%EB%A6%AC-%ED%95%98%EB%A9%B0&quot; aria-label=&quot;마무리 하며 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;마무리 하며&lt;/h2&gt;
&lt;p&gt;나는 이미지 로딩 최적화 부분은 항상 프론트엔드에서의 영역이라고만 생각했고, 보안적으로 큰 문제가 생기는 것이 아니라면 큰 관심을 가지지 않았다. 그러나 SNS 서비스인 DAO를 운영하면서 이러한 관점이 얼마나 부정적인 영향를 미칠 수 있는지 깨닫게 되었다. 또한 백엔드 개발자로써, 서버리스에 대해 부정적인 인식을 가지고 비용을 고려하지 않는 채로 직접 애플리케이션을 구축하는 데만 집중해왔다. 이번 기회를 통해 서버리스를 무조건 배제하기보다는, 서비스 특성에 맞는 기술을 신중하게 채탱하는 것이 중요하다는 점을 다시금 깨닫은 기회였다.&lt;/p&gt;
&lt;h2 id=&quot;Ref&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Ref&quot; aria-label=&quot;Ref permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Ref.&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://incheol-jung.gitbook.io/docs/q-and-a/infra/cloudfront-s3&quot;&gt;https://incheol-jung.gitbook.io/docs/q-and-a/infra/cloudfront-s3&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://aws.amazon.com/ko/blogs/korea/new-use-amazon-s3-object-lambda-with-amazon-cloudfront-to-tailor-content-for-end-users/&quot;&gt;https://aws.amazon.com/ko/blogs/korea/new-use-amazon-s3-object-lambda-with-amazon-cloudfront-to-tailor-content-for-end-users/&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://medium.com/musinsa-tech/s3-object-lambda%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-on-demand-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B3%80%ED%99%98-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%86%8C%EA%B0%9C-5e3650cc27a9&quot;&gt;https://medium.com/musinsa-tech/s3-object-lambda를-이용한-on-demand-이미지-변환-서비스-소개-5e3650cc27a9&lt;/a&gt;&lt;br&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[무중단 배포 일지 (Feat. Blue-Green)]]></title><description><![CDATA[…]]></description><link>https://hongkim.dev/blue-green-deploy/</link><guid isPermaLink="false">https://hongkim.dev/blue-green-deploy/</guid><pubDate>Sun, 30 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;🙇🏻 잘못된 내용 혹은 개선할 내용이 있다면 아낌 없이 피드백 주시면 정말 감사합니다! 🙇🏻&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;현재 카카오 클라우드 스쿨에서 진행한 커뮤니티 서비스를 실제로 사용자들이 이용할 수 있게 배포를 진행해보기로 했다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/6a788eb3-6943-4ac0-bc46-6f664da748aa&quot;&gt;&lt;/p&gt;
&lt;p&gt;먼저 특별한 전략없이 일반적으로 배포하는 흐름을 알아보자.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;새롭게 배포할 애플리케이션 V2를 빌드한다. (ex. Dockerfile 빌드)&lt;/li&gt;
&lt;li&gt;EC2 서버에 SSH 접속을 한다.&lt;/li&gt;
&lt;li&gt;V2 빌드 파일을 EC2 서버로 가져온다.&lt;/li&gt;
&lt;li&gt;기존의 운영중인 V1 애플리케이션을 종료한다.&lt;/li&gt;
&lt;li&gt;V2 빌드 파일을 실행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;p&gt;특별한 배포 전략이 없다면 보통 위와 같은 흐름을 가지게 된다.&lt;br/&gt;
&lt;del&gt;(사실 위 과정은 Recreate 배포 전략이다.)&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;위 흐름에서의 문제점은, 4 - 5 번 과정에서 애플리케이션이 종료되어 사용자가 내 서비스를 이용하지 못하게 되는 시간이 존재하게 된다.&lt;/p&gt;
&lt;p&gt;나는 이러한 문제점을 개선하기 위해, 자동화된 무중단 배포 전략을 사용하여 &lt;code class=&quot;language-text&quot;&gt;DOWNTIME&lt;/code&gt; 을 줄여 사용자가 현재 새로운 서비스로 Release 되고 있는지 인지하지 못하게 함으로 써 사용자 경험을 개선해보고자
한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DOWNTIME 이란?&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
배포 과정에서 새로운 기능 혹은 수정된 기능을 제공하기 위해 이전 버전을 종료하고
새로운 버전이 시작되기까지 서비스를 사용할 수 없는 시간을 의미한다.
&lt;br/&gt;
&lt;br/&gt;
→ 즉, 사용자에게 새로운 서비스를 제공하기 위해 서비스를 이용할 수 없는 시간을 말한다.
&lt;/blockquote&gt;
&lt;h2 id=&quot;배포-전략-선택&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%8F%AC-%EC%A0%84%EB%9E%B5-%EC%84%A0%ED%83%9D&quot; aria-label=&quot;배포 전략 선택 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;배포 전략 선택&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/42b2a0ea-8245-4f92-8b38-47dc520cbf25&quot;&gt;&lt;/p&gt;
&lt;p&gt;먼저, 현재 내 프로젝트에 적용하기 위해 고려한 무중단 배포 전략은 총 3가지이다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rolling Update&lt;/li&gt;
&lt;li&gt;Blue-Green&lt;/li&gt;
&lt;li&gt;Canary Release&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;Rolling-Update&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Rolling-Update&quot; aria-label=&quot;Rolling Update permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Rolling Update&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/3712f892-df1d-49a9-b6c1-e00c76a590f5&quot;&gt;&lt;/p&gt;
&lt;p&gt;Rolling Update 배포 전략은 점진적으로 트래픽을 새로운 버전 V2 로 이전 시켜가는 무중단 배포 전략이다.&lt;/p&gt;
&lt;p&gt;하지만, Rolling Update 배포 전략은 이전 버전인 V1 과 최신 버전인 V2 가 동시에 실행될 수 있어, 서로 다른 버전 간 충돌이 일어날 수 있어 배포시 주의가 필요하다.&lt;/p&gt;
&lt;p&gt;따라서, Rolling Update 배포 전략을 사용하기 위해서는 서로 다른 버전간의 호환성 테스트를 꼼꼼히 테스트해야하고&lt;/p&gt;
&lt;p&gt;신규 버전으로 배포 실패시 롤백 과정을 구현하기 어려울 것 같다는 생각에 Rolling Update 배포 전략은 선택하지 않았다.&lt;/p&gt;
&lt;h3 id=&quot;Blue-Green&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Blue-Green&quot; aria-label=&quot;Blue Green permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Blue-Green&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/e22b047f-3643-4ded-88db-b09f4bda76a4&quot;&gt;&lt;/p&gt;
&lt;p&gt;Blue Green 전략은 Rolling Update 전략과 달리 한번에 트래픽을 V1 에서 V2 로 옮기는 무중단 배포 전략이다.&lt;/p&gt;
&lt;p&gt;Blue Green 전략은 버전 업데이트 실패 시 LB가 다시 V1 으로 라우팅 하면 되서 업데이트 실패에 대한 롤백 과정 구현이 상대적으로 매우 쉽다.&lt;/p&gt;
&lt;p&gt;또한, 모든 트래픽을 V1 에서 V2 로 옮기기 때문에 버전간의 호환성 문제가 발생할 일도 사라지게 된다.&lt;/p&gt;
&lt;p&gt;하지만, 최대 단점인 V1 과 V2 서비스가 동시에 띄우는 방법이라 시스템 리소스를 많이 먹는 문제가 있다.&lt;/p&gt;
&lt;h3 id=&quot;Canary-Release&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Canary-Release&quot; aria-label=&quot;Canary Release permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Canary Release&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/8a3f4b52-c7be-4387-8b1f-e607bd6eb786&quot;&gt;&lt;/p&gt;
&lt;p&gt;마지막으로 고려해본 전략은 카나리 배포 전략이다.&lt;/p&gt;
&lt;p&gt;카나리 배포 전략은 Rolling Update 전략과 비슷하게 트래픽을 점진적으로 새로운 버전으로 이전 시켜가며 배포하는 전략이다.&lt;/p&gt;
&lt;p&gt;하지만, 중요한 차이점은 Rolling Update 전략과 달리 &lt;u&gt;새로운 버전을 특정 사용자 그룹에만 배포&lt;/u&gt;하여 문제가 없을 경우에 점진적으로 트래픽을 이전 시켜가는 비율을 증가 시킨다는 것이다.&lt;/p&gt;
&lt;p&gt;즉, 처음 25% 의 사용자 그룹에게만 V2 버전을 배포하여 문제가 없다면 그다음 50%... 100%.. 로 점차 비율을 늘려가며 마지막엔 서비스 전체가 V2 버전으로 업데이트 되도록 하는 배포 전략이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TMI) 카나리 배포 전략을 이해하기 위한 카나리아의 유래&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
옛날 광부들이 지나가려는 통로에 가스 누출이 되었는지 확인하기 위해, &lt;br/&gt;
유독 가스에 민감한 카나리아 새를 통해 위험을 미리 탐지 했던 방법에서 유래된 방법이다.
&lt;br/&gt;
&lt;br/&gt;
즉, 미리 소수의 사용자들에게만 배포를 진행해보고 문제가 없을 경우&lt;br/&gt;
점점 비율을 늘려가며 최종적으로 전체 서비스가 업데이트 되어가는 무중단 배포 전략을 의미한다.
&lt;/blockquote&gt;
&lt;h3 id=&quot;최종적으로-선택한-배포-전략&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%A2%85%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%84%A0%ED%83%9D%ED%95%9C-%EB%B0%B0%ED%8F%AC-%EC%A0%84%EB%9E%B5&quot; aria-label=&quot;최종적으로 선택한 배포 전략 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;최종적으로 선택한 배포 전략&lt;/h3&gt;
&lt;p&gt;Rolling Update 전략은 배포 실패에 대한 롤백 과정을 구현하기 까다로울 것 같다는 생각에 고려하지 않았다.&lt;/p&gt;
&lt;p&gt;또, 카나리 배포 전략은 현재 내 서비스를 이용하는 사용자가 없기도 하고, 네트워크 관리에 대한 비용과 러닝커브가 클 것 같다는 생각으로 고려하지 않았다.&lt;/p&gt;
&lt;p&gt;최종적으로 &lt;u&gt;Blue-Green 배포 전략&lt;/u&gt;을 선택했고,&lt;br/&gt;
Blue 서비스와 Green 서비스가 동시에 실행하여 서버 리소스를 많이 사용하는 문제를 개선하기 위해,&lt;br/&gt;
업데이트가 정상적으로 마무리 될 경우 &lt;u&gt;이전 버전은 종료&lt;/u&gt;하도록 구현했다.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;(사실 현재 하나의 EC2 서버밖에 보유하지 않아 어떤 배포 전략을 선택해도 비슷하게 구현될 것 같다..)&lt;/del&gt;&lt;/p&gt;
&lt;h3 id=&quot;전체-아키텍처-구성과-흐름&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EC%B2%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%84%B1%EA%B3%BC-%ED%9D%90%EB%A6%84&quot; aria-label=&quot;전체 아키텍처 구성과 흐름 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;전체 아키텍처 구성과 흐름&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/4b7db324-5b43-4f3a-8f3d-8d43ffe57599&quot;&gt;&lt;/p&gt;
&lt;p&gt;앞서 설명한 무중단 배포를 위해 선택한 전략인 Blue-Green 배포의 전체적인 흐름이다.&lt;/p&gt;
&lt;p&gt;프론트엔드는 프리티어를 최대한 이용하고자 AWS 에서 제공하는 CDN 인 CloudFront 를 선택하였다.&lt;br/&gt;
CloudFront 를 사용하면 캐시 무효화 작업만으로 프론트엔드에서 간단하게 무중단 배포를 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;백엔드 무중단 배포 전략의 흐름 (Blue-Green 배포)&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Github 메인 브랜치로 PUSH 이벤트가 발생한다.&lt;/li&gt;
&lt;li&gt;Github Action 을 통해 스프링 애플리케이션 빌드 이미지를 Docker Hub 로 push 한다.&lt;/li&gt;
&lt;li&gt;Self Hosted Runner 를 통해 EC2 서버에서 이미지를 pull 한다.&lt;/li&gt;
&lt;li&gt;Blue 혹은 Green 컨테이너를 띄운다.&lt;/li&gt;
&lt;li&gt;Health Check 에 통과하면 NGINX 설정을 교체한다. (Blue → Green 혹은 Green → Blue)&lt;/li&gt;
&lt;li&gt;기존에 운영 중인 Blue 혹은 Green 컨테이너는 종료한다. (서버 리소스 절약)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;p&gt;&lt;strong&gt;프론트엔드 무중단 배포 전략의 흐름&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Github 메인 브랜치로 PUSH 이벤트가 발생한다.&lt;/li&gt;
&lt;li&gt;Github Acton 을 통해 리액트 애플리케이션을 빌드 후 S3 에 저장한다.&lt;/li&gt;
&lt;li&gt;CloudFront 의 캐시를 무효화 하여 사용자가 새로운 버전의 리액트를 사용할 수 있도록 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;p&gt;이번 게시글에서는 백엔드에서 어떻게 Blue-Green 배포를 구현할 수 있는지 단계별로 알아보자.&lt;/p&gt;
&lt;h2 id=&quot;백엔드-무중단-배포-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B1%EC%97%94%EB%93%9C-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;백엔드 무중단 배포 구현 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;백엔드 무중단 배포 구현&lt;/h2&gt;
&lt;p&gt;Blue-Green 무중단 배포를 구현하기 위해 사용할 환경들&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;GitHub Action&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;스프링 애플리케이션 빌드 및 이미지를 push&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Self Hosted Runner&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EC2 서버에서 이미지는 pull 받고 배포 스크립트를 실행시킴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AWS EC2 - (Amazon Linux)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;필자는 Amazon Linux 환경의 EC2 서버를 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Docker Hub&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;빌드된 스프링 이미지들을 가지고 있는 도커 리포지토리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Dockerfile-작성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Dockerfile-%EC%9E%91%EC%84%B1&quot; aria-label=&quot;Dockerfile 작성 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Dockerfile 작성&lt;/h3&gt;
&lt;p&gt;먼저, 우리가 구현한 스프링 애플리케이션을 실행하는 이미지를 만들기 위해 &lt;code class=&quot;language-text&quot;&gt;DockerFile&lt;/code&gt; 을 작성해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ./Dockerfile&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; amazoncorretto:17-alpine-jdk&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /app&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 각자의 프로젝트 파일에 맞게 변경하십셔&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./build/libs/community-0.0.1-SNAPSHOT.jar /app/hong-community.jar&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENV&lt;/span&gt; TZ=Asia/Seoul&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; [&lt;span class=&quot;token string&quot;&gt;&quot;java&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;-jar&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;hong-community.jar&quot;&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Docker-Hub-리포지토리-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Docker-Hub-%EB%A6%AC%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AC-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;Docker Hub 리포지토리 생성 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Docker Hub 리포지토리 생성&lt;/h3&gt;
&lt;p&gt;나는 이미지를 빌드하고 docker hub 리포지토리로 Push 후 EC2 서버에서 이미지를 가져와 사용할 예정이다.&lt;/p&gt;
&lt;p&gt;그럼 docker hub &lt;a href=&quot;https://hub.docker.com/repositories&quot;&gt;리포지토리 탭&lt;/a&gt;에서 리포지토리를 생성해보자.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/76be542d-bde9-480e-8b1b-16d7009bff19&quot;&gt;&lt;/p&gt;
&lt;p&gt;이미지 빌드시 secret key 와 같이 민감한 정보와 함께 빌드될 수 있어 &lt;u&gt;private 리포지토리로 만드는 것&lt;/u&gt;을 추천한다.&lt;br/&gt;
private 리포지토리는 docker hub 무료 플랜(Personal) 기준으로 한개만 생성 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/f34fd03e-fc8a-40ec-83b7-4dae34836cbd&quot;&gt;&lt;/p&gt;
&lt;p&gt;정상적으로 생성이 완료되면 위 사진과 같이 private 리포지토리로 생성된 것을 확인할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;Github-Action-Workflow-작성---이미지-빌드-및-Push&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Github-Action-Workflow-%EC%9E%91%EC%84%B1---%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B9%8C%EB%93%9C-%EB%B0%8F-Push&quot; aria-label=&quot;Github Action Workflow 작성   이미지 빌드 및 Push permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Github Action Workflow 작성 - 이미지 빌드 및 Push&lt;/h3&gt;
&lt;p&gt;이제, &lt;code class=&quot;language-text&quot;&gt;.jar&lt;/code&gt; 로 빌드된 스프링 애플리케이션을 실행하는 이미지를 생성하고 docker hub 로 push 하는 &lt;code class=&quot;language-text&quot;&gt;docker-build-and-push&lt;/code&gt; 작업을 작성해보자.&lt;/p&gt;
&lt;p&gt;먼저, Github Action 을 동작시킬 trigger 이벤트를 설정해준다.&lt;/p&gt;
&lt;p&gt;나는 배포 작업은 &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; 브랜치로 push 이벤트가 일어날 때 Github Action 이 동작하도록 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ./.github/workflows/backend-prod-cd.yml&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그 다음으로, 스프링 애플리케이션을 &lt;code class=&quot;language-text&quot;&gt;.jar&lt;/code&gt; 로 빌드하는 작업을 추가해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ./.github/workflows/backend-prod-cd.yml&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;docker-build-and-push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Github Action 은 우분투 환경으로 진행&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v3

      &lt;span class=&quot;token comment&quot;&gt;# JDK 설치&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; JDK 17 버전 설치
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;java@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;java-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;17&apos;&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;distribution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;temurin&apos;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;# 스프링 애플리케이션을 .jar 파일로 빌드&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Gradle 을 통해 빌드
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gradle/gradle&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v2.6.0
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bootJar

    &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;마지막으로, &lt;code class=&quot;language-text&quot;&gt;.jar&lt;/code&gt; 파일로 빌드한 스프링 애플리케이션을 실행할 수 있도록 Docker image 를 만들고,&lt;br/&gt;
EC2 서버에서 해당 이미지를 가져올 수 있도록 docker hub 로 push 하는 작업을 추가해주자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ./.github/workflows/backend-prod-cd.yml&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;docker-build-and-push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Docker 빌드 도구 설정
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; docker/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;buildx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v2.9.1

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Docker Hub 로그인
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; docker/login&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v2.2.0
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.DOCKER_HUB_USERNAME &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.DOCKER_HUB_ACCESS_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Docker 이미지 빌드 및 푸시
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; docker/build&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;push&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v4
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .
          &lt;span class=&quot;token key atrule&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./Dockerfile
          &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;platforms&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; linux/amd64
          &lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.DOCKER_HUB_REPOSITORY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.IMAGE_TAG &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;나는 Docker Hub 에 로그인 하기 위해 &lt;code class=&quot;language-text&quot;&gt;username&lt;/code&gt; 및 발급받은 &lt;code class=&quot;language-text&quot;&gt;access token (password)&lt;/code&gt;  과&lt;br/&gt;
생성한 이미지를 push 할 docker hub 리포지토리와 tag 명을 Github Action 의 secrets 변수로 설정 해줬다.&lt;/p&gt;
&lt;p&gt;→ &lt;code class=&quot;language-text&quot;&gt;access token&lt;/code&gt; 은 docker hub 의 &lt;a href=&quot;https://hub.docker.com/settings/security&quot;&gt;security 설정&lt;/a&gt;에서 발급 받을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/edcfddaf-e3bd-409c-8e2d-0f4e94472e9f&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DOCKER_HUB_REPOSITORY&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;username/repository-name&lt;/code&gt; 으로,&lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;IMAGE_TAG&lt;/code&gt; 는 프로젝트에 맞게 지정하면 된다.&lt;/p&gt;
&lt;p&gt;나는 &lt;code class=&quot;language-text&quot;&gt;DOCKER_HUB_REPOSITORY&lt;/code&gt; 을 &lt;code class=&quot;language-text&quot;&gt;kimjihong/hong-community-layout&lt;/code&gt; 으로 설정하고&lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;IMAGE_TAG&lt;/code&gt; 는 &lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt; 로 지정했다.&lt;/p&gt;
&lt;h3 id=&quot;Self-Hosted-Runner-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Self-Hosted-Runner-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;Self Hosted Runner 설정 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Self Hosted Runner 설정&lt;/h3&gt;
&lt;p&gt;사실 AWS CodeDeploy 를 통해 EC2 서버에서 배포해도 되고,&lt;br/&gt;
Github Action SSH 를 사용하여 EC2로 접속해서 배포해도 되고 여러가지 방법 있다.&lt;/p&gt;
&lt;p&gt;하지만, 내가 Self Hosted Runner 를 사용한 이유는 아래와 같다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;배포의 전체 과정을 &lt;u&gt;한곳에서 몰아서 보고싶다.&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Github Action 에서 SSH 를 사용하면 무료 시간에서 차감된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;p&gt;따라서, 배포 과정을 Github Action 으로 통일하고, Github 에서 관리하는 인스턴스가 아닌 나의 EC2 서버에서 진행할 수 있는 Self Hosted Runner 를 사용하였다.&lt;/p&gt;
&lt;p&gt;또, Self Hosted Runner 설정은 매우매우 간단스하다. &lt;del&gt;(사실 이게 가장 큰 이유)&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;먼저, Github Repository 설정에서 Runners 에 들어간다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/9083aaaf-9629-4e55-bbd2-7739307f41b6&quot;&gt;&lt;/p&gt;
&lt;p&gt;Runners 탭에서 &lt;code class=&quot;language-text&quot;&gt;New self-hosted runner&lt;/code&gt; 버튼을 누르면 아래와 같이 설정 스크립트를 친절하게 알려준다.&lt;/p&gt;
&lt;p&gt;우리는 단순히 복붙만으로 빠르게 self hosted runner 설정을 끝낼 수 있다..!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/df331ab1-a543-4f2d-8d91-3d68cb97dac2&quot;&gt;&lt;/p&gt;
&lt;p&gt;내 EC2 서버의 OS 는 Amazon Linux 이므로 Linux 버전으로 선택했다.&lt;/p&gt;
&lt;p&gt;이제, 스크립트를 EC2 서버에서 실행하여 입맛에 맞게 설정해주자.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/7bbc2589-a0fc-4f66-a954-b9e8658ab81b&quot;&gt;&lt;/p&gt;
&lt;p&gt;나는 runner 를 백그라운드에서 실행시키기 위해 &lt;code class=&quot;language-text&quot;&gt;nohup ./run.sh &gt; output.log 2&gt;&amp;amp;1 &amp;amp;&lt;/code&gt; 로 실행시켰다.&lt;/p&gt;
&lt;p&gt;정상적으로 실행이 완료되고, 다시 Runner 탭으로 들어가보면 runner 가 Idle 상태로 되어 있는 것을 확인되었다면 정상적으로 runner 가 등록된 것이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/c0190706-2013-4fdc-acf8-6c64f57b54b2&quot;&gt;&lt;/p&gt;
&lt;p&gt;나는 여기서 추가로 러너가 장시간 켜져있을 경우 종료될 수도 있다고 판단하여&lt;br/&gt;
매일 새벽 3시마다 runner 를 재실행하도록 크론 잡을 설정해줬다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 크론 편집기 열기&lt;/span&gt;
$ &lt;span class=&quot;token function&quot;&gt;crontab&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# crontab 에 아래 스크립트 저장&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 매일 새벽 3시마다 runner 재실행&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; * * * &lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /home/ec2-user/actions-runner &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ./svc.sh restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;svc.sh&lt;/code&gt; 는 self-hosted runner 에서 제공하는 스크립트로,&lt;br/&gt;
self-hosted runner 설치, 시작, 중지, 재시작 등의 작업을 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;Blue--Green-docker-compose-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Blue--Green-docker-compose-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;Blue  Green docker compose 설정 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Blue &amp;#x26; Green docker-compose 설정&lt;/h3&gt;
&lt;p&gt;Runner 설정이 완료 되었다면, 이제 &lt;code class=&quot;language-text&quot;&gt;blue&lt;/code&gt; 로 띄울 스프링 컨테이너와 &lt;code class=&quot;language-text&quot;&gt;green&lt;/code&gt; 으로 띄울 스프링 컨테이너를 정의 해줘야 한다.&lt;/p&gt;
&lt;p&gt;나는 &lt;code class=&quot;language-text&quot;&gt;docker-compose.blue.yml&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;docker-compose.green.yml&lt;/code&gt; 두개의 파일로 나눠서 설정해줬다.&lt;/p&gt;
&lt;p&gt;주의 할 점은, Runner 가 EC2로 접속하는 경로는 홈 경로가 아니라 기본적으로&lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;/home/ec2-user/actions-runner/_work/&amp;lt;repo_name&gt;/&amp;lt;repo_name&gt;/&lt;/code&gt; 로 접속한다.&lt;/p&gt;
&lt;p&gt;따라서, docker-compose 파일은 해당 디렉토리 내에 위치하도록 해야한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# docker-compose.blue.yml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;3&apos;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;spring-backend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; spring&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;blue
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kimjihong/hong&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;community&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8081:8080&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;env_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; .env

&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# docker-compose.green.yml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;3&apos;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;spring-backend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; spring&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;green
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kimjihong/hong&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;community&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8082:8080&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;env_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; .env&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;blue&lt;/code&gt; 컨테이너의 호스트 포트는 8081로, &lt;code class=&quot;language-text&quot;&gt;green&lt;/code&gt; 컨테이너의 호스트 포트는 8082로 설정하고 컨테이너 포트는 8080 포트로 동일하게 설정해줬다.&lt;/p&gt;
&lt;p&gt;따라서, 현재 실행중인 서비스가 &lt;code class=&quot;language-text&quot;&gt;blue&lt;/code&gt; 일 경우 NGINX 는 8081 포트를, 현재 실행중인 서비스가 &lt;code class=&quot;language-text&quot;&gt;green&lt;/code&gt; 일 경우 NGINX 는 8082 포트를 참조하도록 설정해야한다.&lt;/p&gt;
&lt;h3 id=&quot;NGINX-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#NGINX-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;NGINX 설정 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;NGINX 설정&lt;/h3&gt;
&lt;p&gt;NGINX 에서 배포가 성공적으로 이뤄질 때, 참조하는 스프링 컨테이너의 포트를 동적으로 변경하기 위해 &lt;code class=&quot;language-text&quot;&gt;service-url.inc&lt;/code&gt; 파일을 생성했다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;service-url.inc&lt;/code&gt; 파일에서 &lt;code class=&quot;language-text&quot;&gt;service_url&lt;/code&gt; 변수를 정의하여 서비스를 제공할 스프링 애플리케이션의 주소를 설정하고, &lt;code class=&quot;language-text&quot;&gt;nginx.conf&lt;/code&gt; 파일에서 이 변수 값을 &lt;code class=&quot;language-text&quot;&gt;proxy_pass&lt;/code&gt; 지시어의
주소로 사용되도록 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;nginx&quot;&gt;&lt;pre class=&quot;language-nginx&quot;&gt;&lt;code class=&quot;language-nginx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# /etc/nginx/conf.d/service-url.inc&lt;/span&gt;

&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$service_url&lt;/span&gt; http://127.0.0.1:8081&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;nginx&quot;&gt;&lt;pre class=&quot;language-nginx&quot;&gt;&lt;code class=&quot;language-nginx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# /etc/nginx/nginx.conf&lt;/span&gt;

&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
	&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; /etc/nginx/conf.d/service-url.inc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; /&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$service_url&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; X-Real-IP &lt;span class=&quot;token variable&quot;&gt;$remote_addr&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; HOST &lt;span class=&quot;token variable&quot;&gt;$http_host&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; X-NginX-Proxy true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; X-Forwarded-For &lt;span class=&quot;token variable&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; X-Request-ID &lt;span class=&quot;token variable&quot;&gt;$request_id&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_redirect&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;off&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;나는 현재 EC2 인스턴스에서 하나의 NGINX 만 사용하기도 하고, 네트워크 설정에 대한 복잡성 때문에 NGINX 는 docker 로 띄우지 않고, EC2 내부에 직접 설치하여 사용하였다.&lt;/p&gt;
&lt;h3 id=&quot;deploysh-작성-blue-green-배포-스크립트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#deploysh-%EC%9E%91%EC%84%B1-blue-green-%EB%B0%B0%ED%8F%AC-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-label=&quot;deploysh 작성 blue green 배포 스크립트 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;deploy.sh 작성 (blue-green 배포 스크립트)&lt;/h3&gt;
&lt;p&gt;이제, Blue-Green 배포의 핵심인 배포 스크립트를 작성해보자.&lt;/p&gt;
&lt;p&gt;내가 작성한 배포 스크립트의 흐름은 다음과 같다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;먼저 &lt;u&gt;현재 실행중인 서비스&lt;/u&gt;가 무엇인지 확인한다.&lt;/li&gt;
&lt;li&gt;현재 실행중인 서비스가 blue 일 경우 &lt;u&gt;green 컨테이너를 새로 띄운다.&lt;/u&gt; (green 일 경우 blue)&lt;/li&gt;
&lt;li&gt;컨테이너가 실행되기까지 지정된 시간 만큼 &lt;u&gt;대기&lt;/u&gt;한다.&lt;/li&gt;
&lt;li&gt;스프링 애플리케이션에서 미리 작성한 &lt;u&gt;health check 엔드포인트로 요청&lt;/u&gt;을 보낸다.&lt;/li&gt;
&lt;li&gt;200 OK 응답이 올 때까지 지정한 횟수 만큼 반복적으로 요청을 보낸다.&lt;/li&gt;
&lt;li&gt;NGINX 가 참조하고 있는 &lt;code class=&quot;language-text&quot;&gt;service-url.inc&lt;/code&gt; 파일을 &lt;u&gt;green 컨테이너에 맞게 수정&lt;/u&gt;한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;nginx -s reload&lt;/code&gt; 로 NGINX 중단 없이 &lt;code class=&quot;language-text&quot;&gt;.conf&lt;/code&gt; 파일만 &lt;u&gt;다시 갱신&lt;/u&gt;한다.&lt;/li&gt;
&lt;li&gt;기존에 실행중이였던 서비스 &lt;u&gt;blue 컨테이너를 종료&lt;/u&gt;한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;p&gt;위와 같은 흐름을 가지며 스크립트 실행 도중 새로운 버전 즉, green 컨테이너를 띄우는 과정에서 오류가 발생한다면 신속 blue 컨테이너로 다시 롤백하는 흐름을 가진다.&lt;/p&gt;
&lt;p&gt;아래는 위 흐름을 스크립트로 작성한 파일이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# hong: 리팩토링 해놔서 아래 변수만 설정에 맞게  변경하셔서 쓰심 됨다&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 설정 변수&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;spring-backend&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 컨테이너 prefix&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;CONTAINER_SETUP_DELAY_SECOND&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 컨테이너 실행 지연 시간&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;MAX_RETRY_COUNT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 서버 상태 확인 최대 시도 횟수&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;RETRY_DELAY_SECOND&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 서버 상태 확인 지연 시간(초)&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BLUE_SERVER_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://127.0.0.1:8081&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# blue 서버 URL&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;GREEN_SERVER_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://127.0.0.1:8082&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# green 서버 URL&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;HEALTH_END_POINT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/health&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 서버 health check 를 위한 엔드포인트 (200 응답만 오면 됩니당)&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BLUE_DOCKER_COMPOSE_FILE_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;docker-compose.blue&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# blue 의 docker-compose 파일명 (ex. `docker-compose.blue.yml`)&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;GREEN_DOCKER_COMPOSE_FILE_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;docker-compose.green&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# green 의 docker-compose 파일명 (ex. `docker-compose.green.yml`)&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;NGINX_SERVICE_URL_FILE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/etc/nginx/conf.d/service-url.inc&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# NGINX 설정 파일 경로&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# NGINX 재로드 함수&lt;/span&gt;
&lt;span class=&quot;token function-name function&quot;&gt;reload_nginx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NGINX 설정 변경 작업 시작&quot;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; nginx -t&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        nginx &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; reload
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NGINX 설정 재로드 완료&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NGINX 설정 오류 -&gt; 롤백 수행&quot;&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;set \&lt;span class=&quot;token variable&quot;&gt;$service_url&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CURRENT_SERVICE_URL&lt;/span&gt;;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$NGINX_SERVICE_URL_FILE&lt;/span&gt;
        nginx &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; reload
        &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 헬스 체크 함수&lt;/span&gt;
&lt;span class=&quot;token function-name function&quot;&gt;health_check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;REQUEST_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;RETRY_COUNT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RETRY_COUNT&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lt&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$MAX_RETRY_COUNT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;상태 검사 ( &lt;span class=&quot;token variable&quot;&gt;$REQUEST_URL&lt;/span&gt; )  ...  &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$((&lt;/span&gt; RETRY_COUNT &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RETRY_DELAY_SECOND&lt;/span&gt;

        &lt;span class=&quot;token assign-left variable&quot;&gt;REQUEST&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; /dev/null &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%{http_code}&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;&quot;&lt;/span&gt; $REQUEST_URL&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$REQUEST&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
            &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;상태 검사 성공&quot;&lt;/span&gt;
            &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

        &lt;span class=&quot;token assign-left variable&quot;&gt;RETRY_COUNT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$((&lt;/span&gt; RETRY_COUNT &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;

    &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 컨테이너 시작 함수&lt;/span&gt;
&lt;span class=&quot;token function-name function&quot;&gt;start_container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;COLOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;DOCKER_COMPOSE_FILE_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;SERVER_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$3&lt;/span&gt;

    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$COLOR&lt;/span&gt; 컨테이너를 띄우는 중&quot;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;docker-compose&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${CONTAINER_NAME}&lt;/span&gt;-&lt;span class=&quot;token variable&quot;&gt;$COLOR&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${DOCKER_COMPOSE_FILE_NAME}&lt;/span&gt;.yml up &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${CONTAINER_SETUP_DELAY_SECOND}&lt;/span&gt;초 대기&quot;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CONTAINER_SETUP_DELAY_SECOND&lt;/span&gt;

    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$COLOR&lt;/span&gt; 서버 상태 확인 시작&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; health_check &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SERVER_URL&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$HEALTH_END_POINT&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$COLOR&lt;/span&gt; 배포 실패&quot;&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$COLOR&lt;/span&gt; 컨테이너 정리&quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;docker-compose&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${CONTAINER_NAME}&lt;/span&gt;-&lt;span class=&quot;token variable&quot;&gt;$COLOR&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${DOCKER_COMPOSE_FILE_NAME}&lt;/span&gt;.yml down
        &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$COLOR&lt;/span&gt; 배포 성공&quot;&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;set \&lt;span class=&quot;token variable&quot;&gt;$service_url&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$SERVER_URL&lt;/span&gt;;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$NGINX_SERVICE_URL_FILE&lt;/span&gt;
        reload_nginx
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;기존 &lt;span class=&quot;token variable&quot;&gt;${OTHER_COLOR}&lt;/span&gt; 컨테이너 정리&quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;docker-compose&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${CONTAINER_NAME}&lt;/span&gt;-&lt;span class=&quot;token variable&quot;&gt;${OTHER_COLOR}&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${OTHER_DOCKER_COMPOSE_FILE_NAME}&lt;/span&gt;.yml down
    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 메인 스크립트 로직&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ps&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;CONTAINER_NAME&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;-blue&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;blue &gt;&gt; green&quot;&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;OTHER_COLOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;blue&quot;&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;OTHER_DOCKER_COMPOSE_FILE_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$BLUE_DOCKER_COMPOSE_FILE_NAME&lt;/span&gt;
    start_container &lt;span class=&quot;token string&quot;&gt;&quot;green&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$GREEN_DOCKER_COMPOSE_FILE_NAME&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$GREEN_SERVER_URL&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;green &gt;&gt; blue&quot;&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;OTHER_COLOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;green&quot;&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;OTHER_DOCKER_COMPOSE_FILE_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$GREEN_DOCKER_COMPOSE_FILE_NAME&lt;/span&gt;
    start_container &lt;span class=&quot;token string&quot;&gt;&quot;blue&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$BLUE_DOCKER_COMPOSE_FILE_NAME&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$BLUE_SERVER_URL&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;del&gt;(저와 동일하게 진행하고 있으시다면, 아래의 스크립트의 상단에 환경 변수만 프로젝트에 맞게 수정해서 사용하시면 됩니다!)&lt;/del&gt;&lt;/p&gt;
&lt;h3 id=&quot;Github-Action-Workflow-작성---이미지-Pull-및-실행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Github-Action-Workflow-%EC%9E%91%EC%84%B1---%EC%9D%B4%EB%AF%B8%EC%A7%80-Pull-%EB%B0%8F-%EC%8B%A4%ED%96%89&quot; aria-label=&quot;Github Action Workflow 작성   이미지 Pull 및 실행 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Github Action Workflow 작성 - 이미지 Pull 및 실행&lt;/h3&gt;
&lt;p&gt;EC2 내부에서 Blue-Green 배포를 위한 설정들을 마쳤다면,&lt;br/&gt;
마지막으로 Runner 가 EC2에서 진행할 작업을 정의해줘야한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;docker-build-and-push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;docker-pull-and-run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# 우리가 설정한 runner 의 실행 환경&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hosted&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Linux&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; X64 &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; docker&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;and&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;push &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# 도커 이미지 빌드 및 리포지토리로 push 가 성공할 경우에만 진행&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; needs.docker&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;and&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;push.result == &apos;success&apos; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# 프로젝트에서 환경 변수를 .env 로 관리한다면 동일하게 진행하심 됩니다.&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# 사용하지 않는다면 지워주시면 됩니다.&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 환경변수 파일 생성
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;PROPERTIES_PROD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PROPERTIES_PROD &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          touch .env
          echo &quot;${PROPERTIES_PROD}&quot; &gt; .env&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;# docker hub 의 private 리포지토리에 있는 이미지를 pull&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Docker Hub 리포지토리에서 최신 이미지 가져오기
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          sudo docker login --username ${{ secrets.DOCKER_HUB_USERNAME }} --password ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
          sudo docker pull ${{ secrets.DOCKER_HUB_REPOSITORY }}:${{ secrets.IMAGE_TAG }}&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;# deploy.sh 의 실행 권한 주기 및 실행&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue green 배포
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          sudo chmod +x deploy.sh
          sudo ./deploy.sh&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/63e4c924-133d-40ec-a009-78cac6019ce3&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;runs-on&lt;/code&gt; 은 우리가 이전에 설정했던 self hosted runner 의 설정에 맞게 추가해주면 된다.&lt;br/&gt;
필자는 &lt;code class=&quot;language-text&quot;&gt;self-hosted&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Linux&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;X64&lt;/code&gt; 라벨을 추가해줬다.&lt;/p&gt;
&lt;p&gt;또, 나는 스프링 애플리케이션 실행시 필요한 환경 변수들이 있어, &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; 파일을 EC2 서버 내부에 생성하도록 구현을 해놨다.&lt;br/&gt;
&lt;del&gt;(&lt;code class=&quot;language-text&quot;&gt;.gitmodule&lt;/code&gt; 을 사용하여 application.yml 파일을 관리하고 이미지 빌드시 포함하는 것을 추천한다.)&lt;/del&gt;&lt;/p&gt;
&lt;h3 id=&quot;Blue-Green-배포-테스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Blue-Green-%EB%B0%B0%ED%8F%AC-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;Blue Green 배포 테스트 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Blue-Green 배포 테스트&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/JiHongKim98/hong.dev/assets/144337839/1e0747f6-a32d-47d7-9f79-66dd12f36365&quot;&gt;&lt;/p&gt;
&lt;p&gt;현재 서비스중인 컨테이너가 &lt;code class=&quot;language-text&quot;&gt;blue&lt;/code&gt; 일 경우 &lt;code class=&quot;language-text&quot;&gt;green&lt;/code&gt; 으로, &lt;code class=&quot;language-text&quot;&gt;green&lt;/code&gt; 일 경우 &lt;code class=&quot;language-text&quot;&gt;blue&lt;/code&gt; 로 전환이 잘 되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;Ref&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Ref&quot; aria-label=&quot;Ref permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Ref.&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://hudi.blog/zero-downtime-deployment/&quot;&gt;https://hudi.blog/zero-downtime-deployment/&lt;/a&gt;&lt;br/&gt;
&lt;a href=&quot;https://velog.io/@mminjg/Github-Actions-CodeDeploy%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-EC2-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94&quot;&gt;https://velog.io/@mminjg/Github-Actions-CodeDeploy를-이용한-EC2-배포-자동화&lt;/a&gt;&lt;br/&gt;
&lt;a href=&quot;https://velog.io/@hooni_/Blue-Green-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC&quot;&gt;https://velog.io/@hooni_/Blue-Green-무중단-배포&lt;/a&gt;&lt;br/&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[GitHub Actions 캐싱처리 꼭 해야할까?]]></title><description><![CDATA[Django 기준으로 구현한
코드는 GitHub에서 확인 가능합니다. Cache를 사용해야하는 이유 GitHub Cache Action은 매 Workflow에서 build에 필요한 의존성 패키지 설치 작업을 Cache 처리해 build…]]></description><link>https://hongkim.dev/github-action-cache/</link><guid isPermaLink="false">https://hongkim.dev/github-action-cache/</guid><pubDate>Wed, 03 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Django 기준으로 구현한
코드는 &lt;a href=&quot;https://github.com/JiHongKim98/github-action-practice/blob/main/.github/workflows/cache.yml&quot;&gt;GitHub&lt;/a&gt;에서 확인 가능합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Cache를-사용해야하는-이유&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Cache%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0&quot; aria-label=&quot;Cache를 사용해야하는 이유 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Cache를 사용해야하는 이유&lt;/h2&gt;
&lt;p&gt;GitHub Cache Action은 매 Workflow에서 build에 필요한 의존성 패키지 설치 작업을 Cache 처리해 build 속도를 향상시키기 위해 사용한다.&lt;/p&gt;
&lt;p&gt;예를 들어, DRF를 통해 REST API를 구현하고 GitHub Action을 통해 CI를 구현했다고 했을 때
설정한 Event trigger 즉, &lt;code class=&quot;language-text&quot;&gt;push&lt;/code&gt; 혹은 &lt;code class=&quot;language-text&quot;&gt;pull request&lt;/code&gt; 이벤트가 발생할 때마다 &lt;code class=&quot;language-text&quot;&gt;Runner&lt;/code&gt;는 계속해서 &lt;code class=&quot;language-text&quot;&gt;requirements.txt&lt;/code&gt;에 작성된 의존성 패키지를 다운받아 build
완료까지 오랜 시간이 소모되고, 이로 인해 실제 test를 진행및 완료되기까지 오랜 시간이 걸리게 된다.&lt;/p&gt;
&lt;p&gt;이를 개선하려면, 의존성 패키지 설치 작업을 캐싱처리하여 CI 테스트시 캐싱된 작업이 있는지 검사하고 있다면 해당 작업을 가져오고, 없을 때는 의존성 패키지 설치 작업을 진행 후 다시 캐싱 처리하는 방식을 사용하면
빠르게 build하고 테스트까지 진행이 가능하다.&lt;/p&gt;
&lt;p&gt;한번 캐싱처리 전과 후의 시간 차이를 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/kimjihong/post/3b8229de-47d8-43c7-a3f1-2358071b10ff/image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Cache 하기전의 build및 test 완료까지 걸린 시간은 1분10초, 캐싱 처리 후의 소모 시간은 46초로 약 25초가량 CI 테스트 시간이 줄어들었다.
(둘다 오래걸리진 않았지만 대략 52% 의 성능 속도 향상..!)&lt;/p&gt;
&lt;p&gt;지금은 테스트를 위해서 간단하게 구현한 것이라 캐싱처리 전에도 1분대로 엄청 느리지는 않지만, 만약 프로젝트가 커지게 된다면 그에 따라 의존성 패키지도 많아지게 되어 build 시간이 더 커지게 될 것이다.&lt;/p&gt;
&lt;p&gt;그럼 캐싱처리는 어떻게 구현 해야할까?
&lt;br&gt;&lt;/p&gt;
&lt;h2 id=&quot;Cache-구현&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Cache-%EA%B5%AC%ED%98%84&quot; aria-label=&quot;Cache 구현 permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Cache 구현&lt;/h2&gt;
&lt;p&gt;먼저 Actions 탭에서 Django workflow를 선택했을 때 제공해주는 &lt;code class=&quot;language-text&quot;&gt;django.yml&lt;/code&gt; 파일에서 &lt;code class=&quot;language-text&quot;&gt;steps&lt;/code&gt; 부분을 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .github/workflow/django.yml&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Check out repository code
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v3

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Set up Python $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; matrix.python&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;version &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; setup_python
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;python@v3
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;python-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; matrix.python&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;version &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install dependencies
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
        pip install --upgrade pip
        python -m pip install -r requirements.txt&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Run Tests
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
        python manage.py test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 처럼 매 CI test가 진행될 때마다 &lt;code class=&quot;language-text&quot;&gt;requirements.txt&lt;/code&gt; 파일에 작성된 의존성 패키지들을 설치하고 test를 진행한다.&lt;/p&gt;
&lt;p&gt;이제 우리가 고려해야할 부분은 &quot;어디를 캐싱처리 할 것인가?&quot; 이다.&lt;/p&gt;
&lt;p&gt;나는 의존성 패키지를 가상환경에 설치하고 해당 가상환경 자체를 Cache에 저장해 Cache가 유효할 때까지 &lt;code class=&quot;language-text&quot;&gt;pip install&lt;/code&gt; 작업 즉, 의존성 패키지 설치 작업을 생략하도록 구현했다.
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;로직은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;IF&lt;/code&gt; 캐싱된 가상환경이 있고, &lt;code class=&quot;language-text&quot;&gt;requirements.txt&lt;/code&gt; 파일이 변경되지 않았는가?&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;True:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;해당 가상환경을 가져오고 의존성 패키지 설치 작업 &lt;strong&gt;Skip&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;False:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;가상환경 생성 및 의존성 패키지 설치 작업 &lt;strong&gt;Start&lt;/strong&gt;.
&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위 로직을 구현하기 위해 첫번째로 캐싱된 가상환경이 있는지 확인하는 &lt;code class=&quot;language-text&quot;&gt;steps&lt;/code&gt;을 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cache virtualenv
  &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/cache@v3
  &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;venv &lt;span class=&quot;token comment&quot;&gt;# Step 식별을 위한 고유 식별자&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.venv/
    &lt;span class=&quot;token key atrule&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
      ${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-venv-${{ hashFiles(&apos;requirements.txt&apos;) }&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;restore-keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
      ${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-venv-&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Runner가 실행되는 환경에서 의존성 패키지들의 정보가 작성된 &lt;code class=&quot;language-text&quot;&gt;requirements.txt&lt;/code&gt; 이 변경되지 않을 경우에만 캐싱된 가상환경을 가져오기 위해
Cache Key 명을 &lt;code class=&quot;language-text&quot;&gt;&quot;{실행환경}-{python-version}-venv-{해시값}&quot;&lt;/code&gt; 으로 지정하였다.&lt;/p&gt;
&lt;p&gt;따라서 &lt;code class=&quot;language-text&quot;&gt;requirements.txt&lt;/code&gt; 파일이 변경되지 않았고, Cache가 유지되는 기간동안에는 Cache에서 가상환경을 가져올 수 있다.
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;두번째로, 만약 Cache에 일치하는 Key가 없는 경우 의존성 패키지를 설치하고
일치하는 Key가 있는 경우 의존성 패키지 설치 작업을 건너뛰는 &lt;code class=&quot;language-text&quot;&gt;steps&lt;/code&gt;을 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install dependencies
  &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
  &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
    python -m venv ./.venv
    source ./.venv/bin/activate
    pip install --upgrade pip
    python -m pip install -r requirements.txt&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; steps.cache&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;venv.outputs.cache&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hit &lt;span class=&quot;token tag&quot;&gt;!=&lt;/span&gt; &apos;true&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;첫번째 구현 로직의 고유 식별자(id)인 &lt;code class=&quot;language-text&quot;&gt;cache-venv&lt;/code&gt; 를 통해
&lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt;문으로 캐시를 가져오지 못했을 경우에만 가상환경내 의존성 패키지 설치 작업을 진행하도록 구현했다.&lt;/p&gt;
&lt;p&gt;이로써, 불필요하게 계속해서 의존성 패키지를 설치하는 과정을 &lt;strong&gt;Skip&lt;/strong&gt; 할 수 있다.
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;마지막으로, Cache에 저장된 가상환경이 없는 경우 &lt;strong&gt;가상환경에 진입 후&lt;/strong&gt; 패키지를 설치하기 때문에 문제가 없지만, 만약 Cache에 저장된 가상환경이 있을 경우 가상환경 진입과정을 건너뛰게 되므로 test
시작 전에는 꼭 가상환경에 진입하도록 구현해야한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Run Tests
  &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
  &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
    source ./.venv/bin/activate  # 가상환경 진입 필수!
    python manage.py test&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;Logic-Test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Logic-Test&quot; aria-label=&quot;Logic Test permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Logic Test&lt;/h3&gt;
&lt;p&gt;Cache 기능을 적용하고 다시 Action을 실행했을 때&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/kimjihong/post/3dfb79c3-e280-4b74-b06a-37f3c8f59d8a/image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;위 사진과 같이 &lt;code class=&quot;language-text&quot;&gt;cache virtualenv&lt;/code&gt; 작업에서 캐시를 찾지 못했다는 문구가 뜨는데
이는, 지금 새로 Cache 기능을 적용한 뒤 처음 Action을 실행하는 것이라 찾지 못한 것이다.&lt;/p&gt;
&lt;p&gt;따라서, 의존성 패키지 설치 작업을 마친 후, CI 테스트를 진행한다.
가상환경을 Cache내에 저장하는 것은 &lt;code class=&quot;language-text&quot;&gt;steps&lt;/code&gt;의 모든 작업을 완료한 뒤에 이뤄진다.
(&lt;code class=&quot;language-text&quot;&gt;actions/cache@v3&lt;/code&gt; 의 작업)
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;한번, Actions의 Cache 탭을 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/kimjihong/post/04599fe5-101b-44f2-9943-85623e6e19fc/image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;2개의 테스트 실행환경에서 가상환경을 캐싱하여 저장된 것을 볼 수 있다.
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;이제, 다시 &lt;code class=&quot;language-text&quot;&gt;push&lt;/code&gt; 이벤트를 발생시켜 저장된 Cache를 정상적으로 가져오는지 확인 해보자!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/kimjihong/post/2125ebdb-04a7-41d9-96cd-9b2f5ce63db8/image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;cache virtualenv&lt;/code&gt; 작업에서 캐시를 찾아서 &lt;code class=&quot;language-text&quot;&gt;Install dependencies&lt;/code&gt; 작업을 건너뛰고, CI 테스트를 진행하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;이로써, Cache 적용 후 처음 Actions 실행시 진행했던 의존성 패키지 설치 단계를 건너뛰어 약 20초의 시간을 단축할 수 있게 되었다.
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;참고할 점은 Cache의 유효기간과 최대 용량은 GitHub 무료티어 기준으로 유효기간 7일, 최대 용량 2GB이다.
&lt;br&gt;&lt;/p&gt;
&lt;h2 id=&quot;Ref&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#Ref&quot; aria-label=&quot;Ref permalink&quot; class=&quot;heading-anchor before&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15&quot; height=&quot;15&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path d=&quot;M6.188 8.719c.439-.439.926-.801 1.444-1.087 2.887-1.591 6.589-.745 8.445 2.069l-2.246 2.245c-.644-1.469-2.243-2.305-3.834-1.949-.599.134-1.168.433-1.633.898l-4.304 4.306c-1.307 1.307-1.307 3.433 0 4.74 1.307 1.307 3.433 1.307 4.74 0l1.327-1.327c1.207.479 2.501.67 3.779.575l-2.929 2.929c-2.511 2.511-6.582 2.511-9.093 0s-2.511-6.582 0-9.093l4.304-4.306zm6.836-6.836l-2.929 2.929c1.277-.096 2.572.096 3.779.574l1.326-1.326c1.307-1.307 3.433-1.307 4.74 0 1.307 1.307 1.307 3.433 0 4.74l-4.305 4.305c-1.311 1.311-3.44 1.3-4.74 0-.303-.303-.564-.68-.727-1.051l-2.246 2.245c.236.358.481.667.796.982.812.812 1.846 1.417 3.036 1.704 1.542.371 3.194.166 4.613-.617.518-.286 1.005-.648 1.444-1.087l4.304-4.305c2.512-2.511 2.512-6.582.001-9.093-2.511-2.51-6.581-2.51-9.092 0z&quot;/&gt;&lt;/svg&gt;&lt;/a&gt;Ref.&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://mckornfield.medium.com/caching-python-installs-in-github-actions-8309e12a15e6&quot;&gt;https://mckornfield.medium.com/caching-python-installs-in-github-actions-8309e12a15e6&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>