<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>밈밍민믹</title>
    <link>https://0110020321.tistory.com/</link>
    <description>기술블로그로 만들기 위한 과정</description>
    <language>ko</language>
    <pubDate>Mon, 1 Jun 2026 17:38:35 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>밈밍민믹</managingEditor>
    <image>
      <title>밈밍민믹</title>
      <url>https://tistory1.daumcdn.net/tistory/6581301/attach/c6facef89cdc424ba2c63135c2a72e2a</url>
      <link>https://0110020321.tistory.com</link>
    </image>
    <item>
      <title>웹 서버 - Nginx와 Apache로 이해하는 요청 처리 구조</title>
      <link>https://0110020321.tistory.com/65</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 눈 깜빡할 사이에 벌써 6월이네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번 내용을 작성하고나면 확장성 있는 구축에 대해 작성해야하지만 면접이 잡혀서 목요일에 다시 재개하도록 하겠습니다. 작성했던 블로그글이랑 이것저것 다 확인하기 위해서는 조금 시간이 걸릴 것 같더라고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;자 그럼 웹 서버 공부를 시작하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;웹 서버&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버는 &lt;b&gt;클라이언트가 요청한 정적인 컨텐츠를 HTTP를 통해 제공해주는 서버&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기본적인 역할은 HTML, CSS, JavaScript, 이미지 파일 같은 정적 리소스를 클라이언트에게 제공하는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 웹 사이트에 접속했을 때 브라우저가 'index.html', 'style.css', 'main.js' 파일을 요청하면 웹 서버는 해당 파일을 찾아 응답합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 웹 서버는 단순히 파일만 제공하는 것이 아니라, 요청을 애플리케이션 서버로 전달하는 리버스 프록시 역할도 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;웹서버와 WAS의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버와 WAS는 모두 서버 요청 처리에 사용되지만 역할이 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버는 주로 &lt;b&gt;정적 파일을 제공하거나 클라이언트 요청을 다른 서버로 전달하는 역할&lt;/b&gt;을 합니다. 반면 WAS는 &lt;b&gt;비즈니스 로직을 실행하고, 데이터베이스와 연동해 동적인 응답을 생성하는 역할&lt;/b&gt;을 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.0775%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1473%; text-align: center;&quot;&gt;&lt;b&gt;웹 서버&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.7751%; text-align: center;&quot;&gt;&lt;b&gt;WAS&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.0775%;&quot;&gt;&lt;b&gt;주요 역할&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1473%;&quot;&gt;정적 파일 제공, 리버스 프록시&lt;/td&gt;
&lt;td style=&quot;width: 40.7751%;&quot;&gt;비즈니스 로직 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.0775%;&quot;&gt;&lt;b&gt;처리 대상&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1473%;&quot;&gt;HTML, CSS, JS, 이미지&lt;/td&gt;
&lt;td style=&quot;width: 40.7751%;&quot;&gt;API 요청, DB 조회, 인증 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.0775%;&quot;&gt;&lt;b&gt;대표 예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1473%;&quot;&gt;Nginx, Apache&lt;/td&gt;
&lt;td style=&quot;width: 40.7751%;&quot;&gt;Tomcat, Spring Boot 내장 Tomcat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.0775%;&quot;&gt;&lt;b&gt;응답 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1473%;&quot;&gt;파일 또는 요청 전달&lt;/td&gt;
&lt;td style=&quot;width: 40.7751%;&quot;&gt;동적 데이터 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.0775%;&quot;&gt;&lt;b&gt;사용 목적&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.1473%;&quot;&gt;빠른 정적 응답, 요청 분산, 보안 설정&lt;/td&gt;
&lt;td style=&quot;width: 40.7751%;&quot;&gt;서비스 로직 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;웹 서버가 하는 일&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버는 다음과 같은 역할을 수행할 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정적 파일 제공&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;HTML, CSS, JS, 이미지 파일 응답&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리버스 프록시&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;클라이언트 요청을 내부 애플리케이션 서버로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로드 밸런싱&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;여러 서버로 요청을 분산&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSL/TLS 처리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;HTTPS 인증서 적용 및 암호화 통신 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;정적 파일 제공&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 파일은 &lt;b&gt;서버에서 별도의 비즈니스 로직을 실행하지 않고 그대로 응답할 수 있는 파일&lt;/b&gt;입니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 HTML, CSS, JavaScript, 이미지 파일이 이에 해당합니다. 웹 서버는 이러한 정적 파일을 빠르게 제공하는데 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 프로젝트를 빌드하면 정적 파일이 생성됩니다. 이 파일들을 웹 서버의 특정 디렉터리에 배치하면 웹 서버는 클라이언트 요청에 따라 해당 파일을 응답할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;리버스 프록시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리버스 프록시는 &lt;b&gt;클라이언트 요청을 대신 받아 내부 서버로 전달하는 구조&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는 내부 애플리케이션 서버의 주소를 직접 알 필요 없이 웹 서버 주소로 요청을 보냅니다. 웹 서버는 요청 경로나 도메인에 따라 적절한 내부 서버로 요청을 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ERonB/dJMcaaMfafo/RIRIlmkUnwMy2EWDTu0tEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ERonB/dJMcaaMfafo/RIRIlmkUnwMy2EWDTu0tEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ERonB/dJMcaaMfafo/RIRIlmkUnwMy2EWDTu0tEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FERonB%2FdJMcaaMfafo%2FRIRIlmkUnwMy2EWDTu0tEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;481&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;https://example.com/api/posts&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Nginx&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;darr;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;http://localhost:8080/api/posts&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 외부 사용자는 'https://example.com/api/posts'로 요청하지만, 실제 Spring Boot 애플리케이션은 서버 내부의 'localhost:8080'에서 실행될 수 있습니다. 이때 Nginx가 외부 요청을 받아 내부 애플리케이션 서버로 전달합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;로드 밸런싱&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드 밸런싱은 &lt;b&gt;여러 서버에 요청을 분산하는 기능&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트래픽이 많아지면 하나의 애플리케이션 서버가 모든 요청을 처리하기 어려울 수 있습니다. 이때 웹 서버나 로드 밸런서를 사용해 여러 서버로 요청을 나누어 보낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 특정 서버 하나에 요청이 몰리는 것을 줄이고, 서버 장애가 발생했을 때 다른 서버로 요청을 처리하도록 구성할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Nginx란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx는 대표적인 &lt;b&gt;웹 서버 중 하나로 정적 파일 제공, 리버스 프록시, 로드 밸런싱, SSL/TLS 처리 등에 사용&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx는 이벤트 기반 구조를 사용해 많은 동시 연결을 효율적으로 처리하는데 강점이 있습니다. 이러한 특징 때문에 정적 파일 제공이나 리버스 프록시 서버로 많이 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Apache란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache HTTP Server는 &lt;b&gt;오래전부터 널리 사용되어 온 대표적인 웹 서버&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache는 모듈 기반 구조를 가지고 있어 다양한 기능을 확장하기 쉽고, 설정 방식이 유연하다는 장점이 있습니다. 'htaccess' 파일을 통해 디렉터리 단위 설정을 적용할 수 있는 점도 Apache의 특징입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 동시 연결이 많은 환경에서는 설정 방식과 동작 모델에 따라 Nginx보다 상대적으로 많은 자원을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Nginx와 Apache의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx는 많은 동시 연결을 효율적으로 처리하고 리버스 프록시로 사용하기 좋아 현대적인 배포 환경에서 자주 사용됩니다. Apache는 모듈 기반 확장성과 유연한 설정이 강점이며, 오랫동안 다양한 웹 환경에서 사용되어 왔습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9845%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.8216%; text-align: center;&quot;&gt;&lt;b&gt;Nginx&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%; text-align: center;&quot;&gt;&lt;b&gt;Apache&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9845%;&quot;&gt;&lt;b&gt;구조&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.8216%;&quot;&gt;이벤트 기반&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%;&quot;&gt;프로세스/스레드 기반 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9845%;&quot;&gt;&lt;b&gt;강점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.8216%;&quot;&gt;많은 동시 연결 처리, 리버스 프록시, 정적 파일 제공&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%;&quot;&gt;모듈 확장성, 유연한 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9845%;&quot;&gt;&lt;b&gt;정적 파일 처리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.8216%;&quot;&gt;빠르고 효율적&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%;&quot;&gt;가능하지만 Nginx가 자주 선택됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9845%;&quot;&gt;&lt;b&gt;동적 요청 처리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.8216%;&quot;&gt;주로 백엔드 서버로 프록시&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%;&quot;&gt;모듈을 통해 직접 처리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9845%;&quot;&gt;&lt;b&gt;설정 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.8216%;&quot;&gt;중앙 설정 중심&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%;&quot;&gt;.htaccess 등 디렉터리별 설정 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.9845%;&quot;&gt;&lt;b&gt;사용 사례&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.8216%;&quot;&gt;리버스 프록시, 로드 밸런서, 정적 파일 서버&lt;/td&gt;
&lt;td style=&quot;width: 40.1938%;&quot;&gt;전통적인 웹 서버, PHP 환경 등&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;오늘은 웹서버 Nginx와 Apache에 대해 간단하게 알아봤습니다. 수고하셨습니다.&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/65</guid>
      <comments>https://0110020321.tistory.com/65#entry65comment</comments>
      <pubDate>Mon, 1 Jun 2026 12:18:23 +0900</pubDate>
    </item>
    <item>
      <title>실시간 통신 방식 WebSocket</title>
      <link>https://0110020321.tistory.com/64</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 벌써 일요일이네요. 일주일이 정말 눈 깜빡하는 사이에 지나가는 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번에는 웹소켓에 대한 학습을 진행할 예정입니다. 실제로 웹소켓을 통한 채팅 기능을 구현해본적이 있어서 이론적으로 한번 더 학습하는게 되겠네요. 그럼 시작하도록 하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;WebSocket&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSocket은 &lt;b&gt;클라이언트와 서버가 하나의 연결을 유지하면서 양방향으로 데이터를 주고받을 수 있도록 하는 통신 프로토콜&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP는 기본적으로 클라이언트가 요청을 보내야 서버가 응답할 수 있는 구조입니다. 반면 WebSocket은 최초 연결 이후 클라이언트와 서버 사이에 연결을 유지하기 때문에 서보도 필요한 시점에 클라이언트에게 데이터를 보낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, WebSocket은 실시간성이 중요한 기능에서 사용하기 좋은 통신 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HTTP 통신 방식의 한계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP는 요청-응답 방식으로 동작&lt;/b&gt;합니다. 클라이언트가 서버에 요청을 보내면 서버가 응답하고, 하나의 요청 흐름은 종료됩니다. 이 구조는 명확하지만, &lt;b&gt;서버에서 발생한 변경 사항을 클라이언트에게 즉시 전달하기 어렵다는 한계&lt;/b&gt;가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m70Nv/dJMcadPGnLv/wVNbCN11aYctcLHiE3HZc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m70Nv/dJMcadPGnLv/wVNbCN11aYctcLHiE3HZc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m70Nv/dJMcadPGnLv/wVNbCN11aYctcLHiE3HZc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm70Nv%2FdJMcadPGnLv%2FwVNbCN11aYctcLHiE3HZc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;365&quot; height=&quot;354&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 HTTP만으로 실시간 채팅을 구현하려면 클라이언트가 일정 시간마다 서버에 새 메시지가 있는지 계속 물어봐야 합니다. 이를 Polling이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Polling 방식은 구현이 단순하지만 새 데이터가 없어도 계속 요청을 보내야 하므로 불필요한 트래픽이 발생할 수 있습니다. 또한 요청 주기에 따라 실시간성이 떨어질 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;WebSocket의 동작 흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSocket은 처음부터 WebSocket 연결로 시작하는 것이 아니라, &lt;b&gt;HTTP 요청을 통해 연결을 시작&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 서버에 WebSocket 연결을 요청하면, 서버는 이를 승인하고 기존 HTTP 연결을 WebSocket 연결로 업그레이드합니다. 이후에는 &lt;b&gt;하나의 연결을 유지하면서 양방향 통신을 수행&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CV68E/dJMcadhVx0O/tyQ1mBFxrC4Fc2oIyGg4h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CV68E/dJMcadhVx0O/tyQ1mBFxrC4Fc2oIyGg4h0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CV68E/dJMcadhVx0O/tyQ1mBFxrC4Fc2oIyGg4h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCV68E%2FdJMcadhVx0O%2FtyQ1mBFxrC4Fc2oIyGg4h0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;371&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 'Upgrade' 헤더와 'Connection' 헤더가 사용됩니다. 서버가 &lt;b&gt;WebSocket 연결을 허용하면 HTTP 요청-응답 방식이 아니라 WebSocket 프로토콜 기반의 지속 연결로 전환&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 클라이언트와 서버는 연결을 끊지 않고 필요한 시점마다 메시지를 주고받을 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;WebSocket과 HTTP의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP는 클라이언트가 필요한 데이터를 요청하는 구조에 적합하고, WebSocket은 연결을 유지하면서 실시간으로 데이터를 주고받아야하는 기능에 적합합니다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.8449%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.7054%; text-align: center;&quot;&gt;&lt;b&gt;HTTP&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.4496%; text-align: center;&quot;&gt;&lt;b&gt;WebSocket&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.8449%;&quot;&gt;&lt;b&gt;통신 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.7054%;&quot;&gt;요청-응답&lt;/td&gt;
&lt;td style=&quot;width: 38.4496%;&quot;&gt;양방향 통신&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.8449%;&quot;&gt;&lt;b&gt;연결 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.7054%;&quot;&gt;요청마다 연결 또는 응답 후 종료&lt;/td&gt;
&lt;td style=&quot;width: 38.4496%;&quot;&gt;연결을 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.8449%;&quot;&gt;&lt;b&gt;서버 &amp;rarr; 클라이언트 전송&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.7054%;&quot;&gt;클라이언트 요청이 있어야 가능&lt;/td&gt;
&lt;td style=&quot;width: 38.4496%;&quot;&gt;서버가 직접 전송 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.8449%;&quot;&gt;&lt;b&gt;적합한 기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.7054%;&quot;&gt;게시글 조회, 로그인, 주문 요청&lt;/td&gt;
&lt;td style=&quot;width: 38.4496%;&quot;&gt;채팅, 실시간 알림, 실시간 상태 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.8449%;&quot;&gt;&lt;b&gt;실시간성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.7054%;&quot;&gt;낮거나 별도 방식 필요&lt;/td&gt;
&lt;td style=&quot;width: 38.4496%;&quot;&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;WebSocket을 사용하는 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSocket은 서버와 클라이언트 사이에서 실시간으로 데이터가 오가야 하는 상황에서 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.1861%; text-align: center;&quot;&gt;&lt;b&gt;사용 상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.8139%; text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.1861%;&quot;&gt;&lt;b&gt;실시간 채팅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.8139%;&quot;&gt;사용자가 보낸 메시지를 즉시 상대방에게 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.1861%;&quot;&gt;&lt;b&gt;실시간 알림&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.8139%;&quot;&gt;댓글, 좋아요, 주문 상태 변경 등을 즉시 알림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.1861%;&quot;&gt;&lt;b&gt;주식/코인 가격&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.8139%;&quot;&gt;가격 변동 정보를 실시간으로 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.1861%;&quot;&gt;&lt;b&gt;멀티 게임&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.8139%;&quot;&gt;플레이어 위치, 상태, 이벤트를 실시간 동기화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;WebSocket 사용 시 주의할 점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSocket은 실시간 통신에 유용하지만, 운영 환경에서는 몇 가지 주의할 점이 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;연결 관리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;사용자가 많아질수록 유지해야 하는 연결 수가 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인증 처리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;연결 시점에 사용자를 식별하고 권한을 확인해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재연결 처리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;네트워크 불안정으로 연결이 끊어졌을 때 재연결 로직 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버 자원&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;지속 연결이므로 메모리와 커넥션 관리 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프록시 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Nginx 같은 웹 서버를 사용할 경우 Upgrade 헤더 설정 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;오늘은 WebSocket에 대해 간략하게 알아보았습니다. 다음 시간에는 웹서버 : Nginx에 대해서 알아보도록 하겠습니다. 수고하셨습니다.&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/64</guid>
      <comments>https://0110020321.tistory.com/64#entry64comment</comments>
      <pubDate>Sun, 31 May 2026 14:18:17 +0900</pubDate>
    </item>
    <item>
      <title>그래프 데이터베이스 - Neo4j</title>
      <link>https://0110020321.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 오늘은 평상시와 달리 새벽에 글을 작성하네요. 22시에 잠깐 쉰다고 누웠다가 30분정도 잤나..? 더 자려고 했지만 잠이 안와서 그냥 블로그를 쓰기로 마음 먹었습니다. 저번 시간에는 GraphQL에 대해서 학습해봤습니다. GraphQL을 쓸 일이 없어서 크게 관심이 없었지만 이번 기회에 기본 구조 정도는 알게 된 것 같습니다. 이번에는 그래프 데이터베이스에 대해서 학습해보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래프 데이터베이스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프 데이터베이스는 &lt;b&gt;데이터를 Node(하나의 개체)와 Relationship(Node 사이의 관계)으로 표현하는 데이터베이스&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스가 테이블을 중심으로 데이터를 저장한다면, 그랲프 데이터 베이스는 개체와 개체 사이의 연결을 중심으로 데이터를 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 그래프 데이터베이스는 &quot;데이터가 무엇인가&quot;뿐만 아니라 &lt;b&gt;&quot;데이터가 서로 어떻게 연결되어 있는가&quot;&lt;/b&gt;를 중요하게 다룹니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SjE5f/dJMcai4vN7z/ugjtVhnPSy9KHeli9yS8e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SjE5f/dJMcai4vN7z/ugjtVhnPSy9KHeli9yS8e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SjE5f/dJMcai4vN7z/ugjtVhnPSy9KHeli9yS8e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSjE5f%2FdJMcai4vN7z%2FugjtVhnPSy9KHeli9yS8e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;417&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서 '지연', '민수', '제목'은 Node이고, 'FRIEND', 'LIKES'는 Relationship입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'지연'은 '민수'와 친구 관계를 맺고 있고, '제목' 게시글을 좋아요한 상태입니다. 이처럼 그래프 데이터베이스는 사용자와 사용자, 사용자와 게시글처럼 여러 데이터 사이의 관계를 직관적으로 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RDBMS와 그래프 데이터베이스의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDBMS에서는 데이터를 행과 열의 테이블로 저장하고, 테이블 간 관계는 외래 키로 표현합니다. 예를 들어 사용자와 친구 관계를 저장하려면 'users' 테이블과 'friendships' 같은 중간 테이블을 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780073510844&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;users
- id
- name

friendships
- user_id
- friend_id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 구조는 많이 이용되지만 친구의 친구, 공통 친구, 여러 단계의 연결 관계를 계속 탐색해야 한다면 JOIN이 반복적으로 필요해질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;b&gt;그래프 데이터베이스는 관계 자체를 데이터의 핵심 구조로 저장&lt;/b&gt;합니다. 따라서 특정 Node에서 출발해 연결된 Relationship을 따라가며 데이터를 탐색하는 방식에 적합합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7984%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4729%; text-align: center;&quot;&gt;&lt;b&gt;RDBMS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.7286%; text-align: center;&quot;&gt;&lt;b&gt;그래프 데이터베이스&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7984%;&quot;&gt;&lt;b&gt;데이터 표현&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4729%;&quot;&gt;테이블, 행, 컬럼&lt;/td&gt;
&lt;td style=&quot;width: 44.7286%;&quot;&gt;Node, Relationship, Property&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7984%;&quot;&gt;&lt;b&gt;관계 표현&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4729%;&quot;&gt;외래 키, JOIN&lt;/td&gt;
&lt;td style=&quot;width: 44.7286%;&quot;&gt;Relationship&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7984%;&quot;&gt;&lt;b&gt;강점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4729%;&quot;&gt;정형 데이터 관리, 트랜잭션, 집계&lt;/td&gt;
&lt;td style=&quot;width: 44.7286%;&quot;&gt;관계 탐색, 경로 탐색, 연결 구조 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7984%;&quot;&gt;&lt;b&gt;적합한 예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4729%;&quot;&gt;주문, 결제, 회원, 재고&lt;/td&gt;
&lt;td style=&quot;width: 44.7286%;&quot;&gt;SNS 관계, 추천, 경로 탐색, 지식 그래프&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.7984%;&quot;&gt;&lt;b&gt;조회 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4729%;&quot;&gt;SQL&lt;/td&gt;
&lt;td style=&quot;width: 44.7286%;&quot;&gt;Cypher 등 그래프 질의 언어&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDBMS가 데이터를 정합성 있게 저장하고 관리하는데 강점이 있다면, 그래프 데이터베이스는 데이터 사이의 연결 관계를 빠르게 탐색하는데 강점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래프 데이터베이스의 기본 구성 요소&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Node&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node는 그래프 데이터베이스에서 &lt;b&gt;하나의 개체&lt;/b&gt;를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자, 게시글, 상품, 장소, 태그 등이 Node가 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Relationship&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Relationship은&lt;b&gt; Node와 Node 사이의 관계&lt;/b&gt;를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 게시글을 작성했다면 'WROTE', 사용자가 다른 사용자를 팔로우했다면 'FOLLOWS', 사용자가 상품을 구매했다면 'PURCHASED' 같은 관계로 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Property&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Property는 &lt;b&gt;Node나 Relationship이 가지는 속성&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 User Node는 이름, 이메일 같은 속성을 가질 수 있고, FOLLOWS 관계는 팔로우한 날짜를 속성으로 가질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Label&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Label은 &lt;b&gt;Node의 종류를 구분하는 이름&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 'User', 'Post', 'Product', 'Tag' 같은 Label을 붙여 Node의 타입을 구분할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Neo4j&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Neo4j는 대표적인 그래프 데이터베이스입니다.&lt;b&gt; 데이터를 Node, Relationship, Property 형태로 저장하며, 관계를 따라가는 탐색 쿼리에 강점&lt;/b&gt;을 가집니다. SQL 대신 &lt;b&gt;Cypher라는 그래프 질의 언어를 사용해 데이터를 조회하고 생성&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Neo4j는 SNS 친구 관계, 추천 시스템, 지식 그래프, 네트워크 분석, 부정 거래 탐지처럼 관계 탐색이 중요한 분야에서 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypher&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cupher는 Neo4j에서 사용하는 &lt;b&gt;그래프 질의 언어&lt;/b&gt;입니다. SQL이 테이블을 기준으로 데이터를 조회한다면, Cypher는 Node와 Relationship의 패턴을 기준으로 데이터를 조회합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cypher에서는 'MATCH'로 그래프 패턴을 찾고, 'WHERE'로 조건을 지정한 뒤, 'RETURN'으로 조회 결과를 반환할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780074325358&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MATCH &amp;lt;pattern&amp;gt; WHERE &amp;lt;conditions&amp;gt; RETURN &amp;lt;expression&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래프 데이터베이스를 사용하는 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프 데이터베이스는 데이터 사이의 관계를 중심으로 탐생해야 하는 상황에서 유용합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.2093%; text-align: center;&quot;&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.7907%; text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;&lt;b&gt;SNS 친구 관계&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.7907%;&quot;&gt;친구, 팔로우, 공통 친구, 추천 친구 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;&lt;b&gt;추천 시스템&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.7907%;&quot;&gt;사용자-상품-카테고리-태그 관계 기반 추천&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;&lt;b&gt;경로 탐색&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.7907%;&quot;&gt;지도, 네트워크, 물류 경로 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;&lt;b&gt;부정 거래 탐지&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.7907%;&quot;&gt;계정, 카드, 기기, IP 간의 수상한 연결 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;&lt;b&gt;지식 그래프&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.7907%;&quot;&gt;개념, 문서, 인물, 사건 간 관계 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;&lt;b&gt;권한 관리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 77.7907%;&quot;&gt;사용자, 역할, 리소스 간 접근 관계 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래프 데이터베이스의 장점과 주의할 점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;관계 표현이 직관적&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Node와 Relationship으로 연결 구조를 자연스럽게 표현&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관계 탐색에 강함&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;여러 단계의 연결 관계를 따라가는 조회에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스키마 변화에 유연함&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;관계나 속성을 추가하면서 모델을 확장하기 쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;추천/탐색 기능에 적합&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;친구 추천, 상품 추천, 경로 탐색 등에 활용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주의할 점&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정형 데이터 처리에는 RDBMS가 더 적합할 수 있음&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;주문, 결제, 재고처럼 명확한 테이블 구조와 트랜잭션이 중요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영 복잡도&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기존 RDBMS와 함께 사용할 경우 데이터 동기화나 운영 구조 고려 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 목적이 분명해야 함&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;단순 CRUD 중심 서비스라면 도입 효과가 크지 않을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프 데이터베이스는 모든 데이터를 그래프로 바꿔야 하는 기술이 아닌 관계 탐색이 핵심인 문제를 해결하기 위한 선택지로 보는 것이 좋습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;오늘은 그래프 데이터베이스에 대해서 학습해봤습니다. 최근 학습한 것들은 생소한것도 있고 익숙한것도 있고, 신기한 것 같습니다. 학습하면서 느낀바는 BFS, DFS, 다익스트라 같은 그래프 탐색 알고리즘을 적용하기 좋은 기반이 될 수 있겠다는 생각이 들었습니다. 실제로 사용한다면 SNS 서비스에서 친구 추천이나 공통 관심사 기반 추천 기능을 구현해보면 재밌겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이상으로 그래프 데이터베이스와 Neo4j에 대한 학습은 마치며 다음 시간에는 웹소켓과 함께 돌아오도록 하겠습니다. 수고하셨습니다.&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/63</guid>
      <comments>https://0110020321.tistory.com/63#entry63comment</comments>
      <pubDate>Sat, 30 May 2026 02:24:12 +0900</pubDate>
    </item>
    <item>
      <title>GraphQL - REST API와 차이</title>
      <link>https://0110020321.tistory.com/62</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 벌써 금요일이네요. 시간이 빨리가는 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;깁스 풀고 보호대 착용했으니 본격적으로 다시 운동을 시작하고 싶은데 뭐부터 시작해야할지 감이 안잡히네요. 발목 가동 범위도 줄어서 쉽지 않은 것 같아요. 조금 더 고민하고 생각해봐야겠네요. 자, 그럼 학습 시작해보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;GraphQL&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GraphQL은 메타(페이스북)가 개발한 API를 위한 오픈소스 쿼리 언어입니다. &lt;b&gt;클라이언트가 필요한 데이터를 직접 지정해서 요청할 수 있는 API 질의 언어&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API에서는 서버가 정해둔 endpoint에 요청을 보내고, 서버가 정해둔 응답 구조를 받습니다. 반면 GraphQL에서는 클라이언트가 필요한 field를 직접 선택해서 요청할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, GraphQL은 &quot;서버가 정해둔 응답을 그대로 받는 방식&quot;이 아니라, &quot;클라이언트가 필요한 데이터 형태를 요청하는 방식&quot;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;GraphQL&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780019102568&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;query {
  book(id: 1) {
    title
    author
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;JSON&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780019137346&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;data&quot;: {
    &quot;book&quot;: {
      &quot;title&quot;: &quot;GraphQL 입문&quot;,
      &quot;author&quot;: &quot;user1&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시에서 클라이언트는 'book' 데이터 중 'title'과 'author'만 요청했습니다. 서버는 요청 받은 field에 맞춰 필요한 데이터만 응답합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;REST API의 한계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 리소스 중심으로 API를 설계하기 때문에 구조가 직관적이고 HTTP Method와 잘 어울립니다. 예를 들어 게시글 목록 조회, 게시글 상세 조회, 댓글 작성 같은 기능을 각각 endpoint로 나누어 설계할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /posts&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /posts/1&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /posts/1/comment&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /users/1&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;화면이 복잡해지면 클라이언트가 원하는 데이터를 한 번에 가져오기 어려운 경우가 생길 수 있습니다.&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 게시글 상세 화면에서 게시글 정보, 작성자 정보, 댓글 목록, 좋아요 수를 모두 보여줘야 한다면 여러 API를 호출해야 할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /posts/1&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /users/1&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /posts/1/comments&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET posts/1/likes&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;또는 반대로 하나의 API 응답에 클라이언트가 사용하지 않는 데이터까지 포함되어 불필요한 데이터 전송이 발생할 수 있습니다. 이러한 문제를 각각 Under-fetching과 Over-fetching이라고 합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; Over-fetching과 Under-fetching&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Over-fetching&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Over-fetching은 클라이언트가 필요한 데이터보다 더 많은 데이터를 응답받는 상황&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 클라이언트는 게시글 제목과 작성자 이름만 필요하지만, 서버 API가 게시글 내용, 작성일, 수정일, 조회수, 태그, 댓글 수까지 함께 응답한다면 필요하지 않은 데이터까지 전달받게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780019752644&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: 1,
  &quot;title&quot;: &quot;GraphQL이란?&quot;,
  &quot;content&quot;: &quot;본문 내용...&quot;,
  &quot;createdAt&quot;: &quot;2026-05-29&quot;,
  &quot;updatedAt&quot;: &quot;2026-05-29&quot;,
  &quot;viewCount&quot;: 120,
  &quot;author&quot;: {
    &quot;id&quot;: 3,
    &quot;name&quot;: &quot;user1&quot;,
    &quot;email&quot;: &quot;user1@test.com&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 클라이언트가 실제로 사용하는 값이 'title'과 'author.name'뿐이라면 나머지 데이터는 불필요하게 전달된 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Under-fetching&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Under-fetching은 하나의 API 호출만으로 필요한 데이터를 충분히 가져오지 못해 여러 API를 추가로 호출해야 하는 상황&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 게시글 상세 화면에서 게시글 정보뿐 아니라 작성자 정보와 댓글 목록도 필요하다면 REST API에서는 여러 endpoint를 호출해야 할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /posts/1&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /users/3&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET /posts/1/comments&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 클라이언트는 화면 하나를 구성하기 위해 여러 번 서버와 통신해야 하고, API 호출 흐름도 복잡해질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;GraphQL의 기본 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GraphQL은 보통 하나의 endpoint를 사용합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;POST /graphql&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;REST API가 여러 endpoint를 리소스별로 나누는 것과 달리, GraphQL은 하나의 endpoint에 Query를 전달하고 서버는 Query에 맞는 데이터를 응답합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780020811897&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;query {
  post(id: 1) {
    title
    author {
      name
    }
    comments {
      content
      writer {
        name
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 요청을 게시글 제목, 작성자 이름, 댓글 내용, 댓글 작성자 이름을 한 번에 요청합니다. 클라이언트가 필요한 데이터 구조를 직접 작성하기 때문에 여러 API를 호출하지 않고도 화면에 필요한 데이터를 가져올 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Query, Mutation, Schema&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Query&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query는 &lt;b&gt;데이터를 조회할 때 사용&lt;/b&gt;합니다. REST API의 GET 요청과 비슷한 역할을 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780020957187&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;query {
  user(id: 1) {
    id
    name
    email
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Mutation&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mutation은 &lt;b&gt;데이터 생성, 수정, 삭제할 때 사용합니다. REST API의 POST, PUT, PATCH, DELETE와 비슷한 역할&lt;/b&gt;을 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780021025101&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mutation {
  createPost(input: {
    title: &quot;GraphQL 정리&quot;
    content: &quot;GraphQL은 필요한 데이터를 직접 요청할 수 있습니다.&quot;
  }) {
    id
    title
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Schema&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Schema는 &lt;b&gt;GraphQL API에서 어떤 데이터를 요청할 수 있고, 어떤 타입을 반환하는지 정의하는 구조&lt;/b&gt;입니다. GraphQL 서버는 Schema를 기준으로 클라이언트의 요청이 유효한지 확인하고, 요청된 데이터의 형태에 맞게 응답합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1780021117044&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
}

type User {
  id: ID!
  name: String!
}

type Query {
  post(id: ID!): Post
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 Schena는 'post'라는 Query를 통해 특정 게시글을 조회할 수 있고, 게시글 'id', 'title', 'content', 'author' 정보를 가진다는 것을 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;GraphQL의 장점과 단점&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;필요한 데이터만 요청 가능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;클라이언트가 필요한 field만 선택해 요청할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;여러 데이터를 한 번에 조회 가능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;하나의 Query로 연관된 데이터를 함께 요청할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;API 응답 구조 예측 가능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;요청한 구조와 비슷한 형태로 응답이 내려옴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트 변화에 유연함&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;화면마다 필요한 데이터가 달라도 Query를 조정해 대응 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타입 기반 Schema&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;요청 가능한 데이터 구조를 명확히 정의할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;서버 구현 복잡도 증가&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Schema, Resolver, 타입 정의 등을 설계해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡한 Query 문제&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;클라이언트가 너무 깊거나 무거운 Query를 요청할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;캐싱이 REST보다 까다로움&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;REST처럼 URL 단위 캐싱을 적용하기 어려울 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;N+1 문제 가능성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;연관 데이터를 Resolver에서 반복 조회하면 성능 문제가 생길 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;REST API와 GraphQL 비교&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 리소스 중심으로 단순하고 명확하게 API를 설계하기 좋습니다. 반면 GraphQL은 클라이언트가 필요한 데이터 구조를 직접 요청할 수 있어 화면별 데이터 요구사항이 다양한 서비스에서 유리할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.0542%; text-align: center;&quot;&gt;&lt;b&gt;REST API&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%; text-align: center;&quot;&gt;&lt;b&gt;GraphQL&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;&lt;b&gt;요청 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.0542%;&quot;&gt;여러 endpoint 사용&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;보통 하나의 endpoint 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;&lt;b&gt;데이터 응답&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.0542%;&quot;&gt;서버가 정한 구조로 응답&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;클라이언트가 요청한 구조로 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;&lt;b&gt;데이터 조회&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.0542%;&quot;&gt;필요한 데이터에 따라 여러 API 호출 가능&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;하나의 Query로 연관 데이터 조회 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;&lt;b&gt;Over-fetching&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.0542%;&quot;&gt;발생할 수 있음&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;줄일 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;&lt;b&gt;Under-fetching&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.0542%;&quot;&gt;발생할 수 있음&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;줄일 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;&lt;b&gt;캐싱&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.0542%;&quot;&gt;HTTP 캐싱과 잘 어울림&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;별도 캐싱 전략 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.2868%;&quot;&gt;&lt;b&gt;설계 기준&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.0542%;&quot;&gt;리소스 중심&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;Schema와 타입 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Apollo란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apollo는 &lt;b&gt;GraphQL을 더 쉽게 사용할 수 있도록 도와주는 도구&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서는 Apollo Client를 사용해 GraphQL Query를 보내고, 응답 데이터를 캐싱하거나 상태 관리와 함께 사용할 수 있습니다. 서버에서는 Apollo Server를 사용해 GraphQL Schema와 Resolver를 구성할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, Apollo는 GraphQL 자체가 아니라 GraphQL을 실제 애플리케이션에서 더 편하게 사용할 수 있도록 도와주는 라이브러리 도구 모음에 가깝습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;오늘은 GraphQL과 REST API와의 차이에 대해 정리했습니다. 간략하게 알아간 정도라서 추가적인 학습이 필요한 경우 다른 블로그를 더 참조하는게 좋을 것 같습니다. GraphQL을 사용하게 될지는 모르겠지만, 만약 사용하게 된다면 포스팅하도록 하겠습니다. 수고하셨습니다.&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/62</guid>
      <comments>https://0110020321.tistory.com/62#entry62comment</comments>
      <pubDate>Fri, 29 May 2026 11:34:02 +0900</pubDate>
    </item>
    <item>
      <title>컨테이너화 VS 가상화 - Docker란 무엇인가</title>
      <link>https://0110020321.tistory.com/61</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 오늘은 병원가서 깁스 풀고 재활 치료 받느라서 블로그 작성이 늦어졌네요. 정말이지 병원에 갔다오면 오전이 순식간에 지나가는 것 같네요. 그럼 컨테이너화와 가상화에 대한 학습 시작해보도록 하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;가상화&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화는 &lt;b&gt;하나의 물리 서버 위에서 여러 개의 독립된 가상 환경을 실행하는 기술&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 하이퍼바이저라는 소프트웨어가 물리 서버의 CPU, 메모리, 디스크 같은 자원을 나누어 여러 가상 머신을 실행합니다. 각 가상 머신은 자신만의 운영체제를 가지고 있으며, 실제 서버처럼 독립적으로 동작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSfBkO/dJMcaaZKazJ/NA8OTfPGwFBmIuK22wZhgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSfBkO/dJMcaaZKazJ/NA8OTfPGwFBmIuK22wZhgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSfBkO/dJMcaaZKazJ/NA8OTfPGwFBmIuK22wZhgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSfBkO%2FdJMcaaZKazJ%2FNA8OTfPGwFBmIuK22wZhgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;367&quot; height=&quot;243&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 하나의 물리 서버 위에 Linux 가상 머신, Windows 가상 머신을 각각 실행할 수 있습니다. 각 VM은 독립된 운영체제를 가지기 때문에 격리 수준이 높다는 장점이 있습니다. 하지만 VM마다 운영체제를 포함해야 하므로 상대적으로 무겁고, 실행 속도나 자원 사용량 측면에서 부담이 커질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;컨테이너화&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너화는 &lt;b&gt;애플리케이션과 실행에 필요한 의존성, 설정 파일 등을 하나의 실행 단위로 묶어 격리된 환경에서 실행하는 방식&lt;/b&gt;입니다. 가상 머신처럼 운영체제 전체를 각각 포함하는 것이 아니라, 호스트 운영체제의 커널을 공유하면서 애플리케이션 실행에 필요한 부분만 독립적으로 구성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x4Wjx/dJMcahdwrzO/kcOKxHGTEbkBqmBk1z2cfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x4Wjx/dJMcahdwrzO/kcOKxHGTEbkBqmBk1z2cfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x4Wjx/dJMcahdwrzO/kcOKxHGTEbkBqmBk1z2cfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx4Wjx%2FdJMcahdwrzO%2FkcOKxHGTEbkBqmBk1z2cfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;429&quot; height=&quot;294&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너는 VM보다 가볍고 빠르게 실행될 수 있습니다. 운영체제 전체를 새로 띄우는 것이 아니라, 애플리케이션 실행에 필요한 환경만 묶어 실행하기 때문입니다. 따라서 같은 서버에서 더 많은 애플리케이션을 효율적으로 실행할 수 있고, 개발 환경과 배포 환경의 차이도 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;가상화와 컨테이너화의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화는 운영체제까지 포함한 독립된 서버 환경을 만드는 방식에 가깝고, 컨테이너화는 애플리케이션 실행 환경을 가볍게 격리하는 방식에 가깝습니다. 따라서 완전히 독립된 운영체제 환경이 필요하다면 가상화가 적합할 수 있고, 애플리케이션을 빠르고 일관되게 배포하고 싶다면 컨테이너화가 유리할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.1705%; text-align: center;&quot;&gt;&lt;b&gt;가상화&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.5891%; text-align: center;&quot;&gt;&lt;b&gt;컨테이너화&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;&lt;b&gt;실행 단위&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.1705%;&quot;&gt;가상 머신&lt;/td&gt;
&lt;td style=&quot;width: 41.5891%;&quot;&gt;컨테이너&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;&lt;b&gt;운영체제&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.1705%;&quot;&gt;VM마다 Guest OS 포함&lt;/td&gt;
&lt;td style=&quot;width: 41.5891%;&quot;&gt;Host OS 커널 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;&lt;b&gt;무게&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.1705%;&quot;&gt;상대적으로 무거움&lt;/td&gt;
&lt;td style=&quot;width: 41.5891%;&quot;&gt;상대적으로 가벼움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;&lt;b&gt;실행 속도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.1705%;&quot;&gt;비교적 느림&lt;/td&gt;
&lt;td style=&quot;width: 41.5891%;&quot;&gt;비교적 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;&lt;b&gt;격리 수준&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.1705%;&quot;&gt;높음&lt;/td&gt;
&lt;td style=&quot;width: 41.5891%;&quot;&gt;프로세스 단위 격리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;&lt;b&gt;자원 사용량&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.1705%;&quot;&gt;많음&lt;/td&gt;
&lt;td style=&quot;width: 41.5891%;&quot;&gt;적음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.2403%;&quot;&gt;&lt;b&gt;대표 기술&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.1705%;&quot;&gt;VMware, VirtualBox, Hyper-V&lt;/td&gt;
&lt;td style=&quot;width: 41.5891%;&quot;&gt;Docker, containerd&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Docker&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 &lt;b&gt;컨테이너 기반으로 애플리케이션을 실행하고 배포할 수 있도록 도와주는 플랫폼&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 사용하면 애플리케이션 코드뿐 아니라 실행에 필요한 라이브러리, 설정, 런타임 환경을 이미지로 묶을 수 있습니다. 그리고 이 이미지를 기반으로 컨테이너를 싱행하면 어느 환경에서든 비교적 일관된 방식으로 애플리케이션을 실행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Docker 기반 컨테이너 실행 구조&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 그림은 Docker 기반으로 컨테이너화된 애플리케이션이 실행되는 구조를 보여줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;961&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/es3Aa7/dJMcadWsgN1/b9z7Uhu4lElyTnYZfAYl21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/es3Aa7/dJMcadWsgN1/b9z7Uhu4lElyTnYZfAYl21/img.png&quot; data-alt=&quot;출처 : https://www.docker.com/resources/what-container/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/es3Aa7/dJMcadWsgN1/b9z7Uhu4lElyTnYZfAYl21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fes3Aa7%2FdJMcadWsgN1%2Fb9z7Uhu4lElyTnYZfAYl21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1110&quot; height=&quot;961&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;961&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://www.docker.com/resources/what-container/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 아래에는 실제 서버나 클라우드 환경에 해당하는 Infrastructure가 있고, 그 위에 Host Operating System이 실행됩니다. Docker는 Host OS 위에서 동작하며, 여러 개의 컨테이너를 실행하고 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 컨테이너 안에는 App A, App B 처럼 서로 다른 애플리케이션이 실행될 수 있습니다. 이때 컨테이너는 가상 머신처럼 운영체제 전체를 각각 포함하지 않고, Host OS의 커널을 공유하면서 애플리케이션 실행에 필요한 환경만 격리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 컨테이너는 가상 머신보다 상대적으로 가볍고 빠르게 실행될 수 있으며, 하나의 서버 위에서 여러 애플리케이션을 효율적으로 운영할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Docker Engine의 구성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Engine은 Docker에서 컨테이너를 생성하고 실행하며 관리하는 핵심 실행 환경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 Docker CLI를 통해 명령어를 입력하고, Docker API를 통해 Docker Engine과 통신할 수 있습니다. Docker Engine은 이미지를 빌드하고, 컨테이너를 실행하며, 네트워크와 볼륨 같은 실행 환경을 관리합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yRkfX/dJMcahq4HWY/PNHXlOfcHeTfKkCuRKuJv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yRkfX/dJMcahq4HWY/PNHXlOfcHeTfKkCuRKuJv0/img.png&quot; data-alt=&quot;출처 : https://www.docker.com/products/container-runtime/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yRkfX/dJMcahq4HWY/PNHXlOfcHeTfKkCuRKuJv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyRkfX%2FdJMcahq4HWY%2FPNHXlOfcHeTfKkCuRKuJv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;300&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://www.docker.com/products/container-runtime/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 199px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 10px; text-align: center;&quot;&gt;&lt;b&gt;구성 요소&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 10px; text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 21px;&quot;&gt;&lt;b&gt;Docker CLI&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 21px;&quot;&gt;사용자가 Docker 명령어를 입력하는 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 42px;&quot;&gt;&lt;b&gt;Docker API&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 42px;&quot;&gt;Docker CLI나 외부 도구가 Docker Engine과 통신하는 인터페이스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 21px;&quot;&gt;&lt;b&gt;Docker Engine&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 21px;&quot;&gt;컨테이너 생성, 실행, 이미지 관리 등을 담당하는 핵심 실행 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 21px;&quot;&gt;&lt;b&gt;Containerd&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 21px;&quot;&gt;컨테이너 실행을 관리하는 런타임 계층&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 21px;&quot;&gt;&lt;b&gt;Docker Build&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 21px;&quot;&gt;Dockerfile을 기반으로 이미지를 빌드하는 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 21px;&quot;&gt;&lt;b&gt;Networking&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 21px;&quot;&gt;컨테이너 간 통신과 외부 연결을 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 21px;&quot;&gt;&lt;b&gt;Volumes&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 21px;&quot;&gt;컨테이너 데이터 저장 공간을 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 18.7209%; height: 21px;&quot;&gt;&lt;b&gt;Distribution&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.2791%; height: 21px;&quot;&gt;이미지를 저장소에 push/pull하는 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Docker Engine은 단순히 컨테이너를 실행하는 것만 담당하는 것이 아니라, 이미지 빌드, 컨테이너 실행, 네트워크 연결, 데이터 저장 공간, 이미지 배포까지 컨테이너 운영에 필요한 여러 기능을 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Docker를 사용하는 이유&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 실행 환경 일관성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 사용하면 애플리케이션 실행 환경을 이미지로 묶을 수 있습니다. 따라서 개발 환경, 테스트 환경, 배포 환경에서 동일한 이미지를 사용해 실행할 수 있습니다. 이럴 경우 &quot;내 컴퓨터에서는 됏는데 서버에서는 안된다&quot;와 같은 환경 차이 문제를 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 배포 편의성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 이미지를 만들어두면 서버에서는 해당 이미지를 받아 컨테이너로 실행하면 됩니다. 기존 방식처럼 서버에 Java, Node.js, 라이브러리, 설정 파일을 하나씩 설치하고 맞추는 과정이 줄어들기 때문에 배포 과정이 단순해질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 격리된 실행 환경&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;컨테이너는 서로 독립된 환경에서 실행됩니다. 하나의 서버에서 여러 애플리케이션을 실행하더라도 각 애플리케이션의 실행 환경을 분리할 수 있습니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 서로 다른 Java 버전이나 라이브러리 구성이 필요한 애플리케이션도 컨테이너를 통해 분리해서 실행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 확장과 운영에 유리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 컨테이너 단위로 애플리케이션을 실행하기 때문에 인스턴스를 추가하거나 교체하는 방식이 비교적 단순합니다. 또한 Kubernetes 같은 컨테이너 오케스트레이션 도구와 함께 사용하면 여러 서버에 걸쳐 컨테이너를 배포하고 관리할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Docker를 처음에는 단순히 애플리케이션을 컨테이너화하는 도구로만 생각했습니다. 하지만 이번에 컨테이너화와 Docker의 구조를 정리하면서 Docker가 단순한 실행 도구를 넘어 배포 환경을 일관되게 도움을 주는 기술이라는 점을 알 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;특히 여러 프로젝트를 각각 컨테이너로 분리해 현재 사용 중인 Ubuntu 서버에서 독립적으로 실행할 수 있다는 점이 흥미로웠습니다. 하나의 서버 안에서도 프로젝트별 실행 환경을 분리하고, 포트와 Nginx 설정을 통해 여러 서비스를 함께 운영할 수 있다는 점에서 Docker의 활용 가능성을 더 구체적으로 이해하게 되었습니다. 다음 시간에는 GraphQL에 대해 알아보겠습니다. 감사합니다.&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/61</guid>
      <comments>https://0110020321.tistory.com/61#entry61comment</comments>
      <pubDate>Thu, 28 May 2026 16:01:17 +0900</pubDate>
    </item>
    <item>
      <title>메시지 브로커 - RabbitMQ와 Kafka</title>
      <link>https://0110020321.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 벌써 수요일이네요. 드디어 내일 깁스를 풀기위해 병원을 갑니다. 이전에는 아마 이후에는 보호대로 재활을 하는게 좋다고 했는데...내일 병원가서 상태가 괜찮으면 좋겠네요. 그래도 완전한 여름이 되기 전에 깁스를 풀 수 있어서 다행이네요. 자 그럼 메시지 브로커에 대한 학습 시작해보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;메시지 브로커&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 브로커는 &lt;b&gt;시스템 간에 주고받는 메시지를 중간에서 전달해주는 역할을 하는 소프트웨어&lt;/b&gt;입니다. 일반적으로 요청을 보내는 쪽을 Producer, 메시지를 처리하는 쪽을 Consumer라고 합니다. Producer는 Consumer를 직접 호출하지 않고 메시지 브로커에 메시지를 전달합니다. 이후 Consumer는 메시지 브로커에서 메시지를 가져와 필요한 작업을 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Producer &lt;b&gt;&amp;rarr; Message Broker &lt;b&gt;&amp;rarr; Consumer&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 주문이 완료되었을 때 주문 서비스가 알림 서비스, 배송 서비스, 재고 서비스를 직접 호출할 수 있습니다. 하지만 이 경우 각 서비스의 장애나 응답 지연이 주문 처리 흐름에 영향을 줄 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;메시지 브로커를 사용하면 주문 서비스는 &quot;주문 완료&quot; 메시지만 브로커에 전달하고, 알림 서비스&amp;middot;배송 서비스&amp;middot;재고 서비스는 각자 필요한 메시지를 받아 처리할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;주문 서비스 &amp;rarr; 메시지 브로커 &amp;rarr; 알림 서비스&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;주문 서비스 &amp;rarr; 메시지 브로커 &amp;rarr; 배송 서비스&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;주문 서비스 &amp;rarr; 메시지 브로커 &amp;rarr; 재고 서비스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 메시지 브로커는 시스템 사이의 중간 전달자 역할을 하며, 서비스 간 결합도를 낮추고 비동기 처리를 가능하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;메시지 브로커가 필요한 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 브로커를 사용하는 이유는 크게 세 가지로 볼 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 비동기 처리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사용자의 요청 중에는 응답 전에 반드시 끝나야 하는 작업도 있지만, 응답 이후에 처리해도 되는 작업도 있습니다. 예를 들어 회원가입 자체는 즉시 완료되어야 하지만, 환영 이메일 발송은 나중에 처리되어도 됩니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;이처럼 핵심 요청과 분리할 수 있는 작업을 메시지 브로커로 넘기면, 서버는 사용자에게 더 빠르게 응답하고 부가 작업은 Consumer가 비동기로 처리할 수 있습니다.&lt;/span&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 서비스 간 결합도 감소&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주문 서비스가 알림 서비스, 배송 서비스, 재고 서비스의 API를 직접 호출하면 각 서비스의 장애나 변경이 주문 서비스에 직접적인 영향을 줄 수 있습니다. 그렇기에 메시지 브로커를 사용하면 각 서비스는 메시지를 기준으로 느슨하게 연결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 장애 대응&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cosumer가 일시적으로 장애가 나더라도 메시지 브로커에 메시지가 남아 있다면, Consumer가 복구된 후 다시 메시지를 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;메시징 모델&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;237&quot; data-start=&quot;113&quot; data-ke-size=&quot;size16&quot;&gt;메시지 브로커는 메시지를 전달하는 방식에 따라 여러 모델로 나누어 볼 수 있습니다.&lt;br /&gt;대표적으로 &lt;b&gt;점대점 모델(Point-to-Point)&lt;/b&gt; 과 &lt;b&gt;게시/구독 모델(Publish/Subscribe)&lt;/b&gt; 이 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;331&quot; data-start=&quot;239&quot; data-ke-size=&quot;size16&quot;&gt;두 모델은 모두 Producer가 메시지를 보내고 Consumer가 메시지를 처리한다는 공통점이 있지만, &lt;b&gt;하나의 메시지를 누가 소비하는지&lt;/b&gt;에서 차이가 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 점대점 모델&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점대점 모델은 &lt;b&gt;하나의 메시지를 하나의 Consumer가 처리하는 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Producer가 메시지를 Queue에 넣으면, Consumer는 Queue에 쌓인 메시지를 가져가 처리합니다. 여러 Consumer가 같은 Queue를 바라보고 있더라도, 하나의 메시지는 보통 하나의 Consumer에게만 전달됩니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Producer&amp;nbsp;&amp;rarr;&amp;nbsp;Queue&amp;nbsp;&amp;rarr;&amp;nbsp;Consumer&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;여러 Consumer가 있는 경우 다음과 같이 작업을 나누어 처리할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Producer&amp;nbsp;&amp;rarr;&amp;nbsp;Queue&amp;nbsp;&amp;rarr;&amp;nbsp;Consumer&amp;nbsp;A &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;Consumer&amp;nbsp;B &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;Consumer&amp;nbsp;C&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 이메일 발송 작업이 1,000건 쌓여있다고 가정해보겠습니다. 하나의 Consumer가 모든 이메일을 처리하면 시간이 오래 걸릴 수 있습니다. 이때 여러 Consumer가 같은 Queue에서 메시지를 하나씩 가져가 처리하면 작업을 분산해서 처리할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 점대점 모델은 &lt;b&gt;&quot;해야 할 작업을 Queue에 쌓고, 가능한 Consumer가 하나씩 가져가 처리하는 구조&quot;&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 게시/구독 모델&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시/구독 모델은&amp;nbsp;&lt;b&gt;하나의 메시지를 여러 Consumer가 받아 처리할 수 있는 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Producer는 메시지를 특정 Topic에 발행합니다. 그리고 해당 Topic을 구독하고 있는 Consumer들이 메시지를 받아 각자의 작업을 처리합니다&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Producer&amp;nbsp;&amp;rarr;&amp;nbsp;Topic&amp;nbsp;&amp;rarr;&amp;nbsp;Consumer&amp;nbsp;A &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;Consumer&amp;nbsp;B &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;Consumer&amp;nbsp;C&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 주문이 완료되었을 때 &quot;주문 완료&quot; 이벤트가 발생했다고 가정해보겠습니다. 이 이벤트는 알림 서비스, 배송 서비스, 통계 서비스가 모두 필요로 할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;주문&amp;nbsp;완료&amp;nbsp;이벤트 &lt;br /&gt;&amp;nbsp;&amp;rarr;&amp;nbsp;알림&amp;nbsp;서비스:&amp;nbsp;주문&amp;nbsp;완료&amp;nbsp;알림&amp;nbsp;발송 &lt;br /&gt;&amp;nbsp;&amp;rarr;&amp;nbsp;배송&amp;nbsp;서비스:&amp;nbsp;배송&amp;nbsp;준비&amp;nbsp;요청 &lt;br /&gt;&amp;nbsp;&amp;rarr;&amp;nbsp;통계&amp;nbsp;서비스:&amp;nbsp;매출&amp;nbsp;통계&amp;nbsp;반영&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이때 게시/구독 모델을 사용하면 주문 서비스는 &quot;주문 완료&quot; 이벤트를 한 번만 발행하면 됩니다. 이후 해당 이벤트를 구독하는 여러 서비스가 각자 필요한 작업을 처리합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 게시/구독 모델은 &quot;하나의 이벤트를 여러 시스템이 각자의 목적에 맞게 소비하는 구조&quot;에 가깝습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동기 처리와 비동기 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 브로커를 이해하기 위해서는 동기 처리와 비동기 처리의 차이를 먼저 이해하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 처리는 &lt;b&gt;요청을 보낸 뒤 결과가 돌아올 때까지 기다리는 방식&lt;/b&gt;입니다. 반면 비동기 처리는 &lt;b&gt;요청을 보낸 뒤 결과를 기다리지 않고 다음 작업을 진행할 수 있는 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8449%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.8915%; text-align: center;&quot;&gt;&lt;b&gt;동기 처리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%; text-align: center;&quot;&gt;&lt;b&gt;비동기 처리&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8449%;&quot;&gt;&lt;b&gt;처리 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.8915%;&quot;&gt;결과가 올 때까지 기다림&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;요청 후 다음 작업 진행 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8449%;&quot;&gt;&lt;b&gt;장점&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.8915%;&quot;&gt;흐름이 단순하고 결과 확인이 쉬움&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;응답 시간을 줄이고 작업 분리가 쉬움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8449%;&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.8915%;&quot;&gt;처리 시간이 길어질 수 있음&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;처리 결과 추적이 필요함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8449%;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.8915%;&quot;&gt;결제 승인 결과 확인&lt;/td&gt;
&lt;td style=&quot;width: 44.2635%;&quot;&gt;이메일 발송, 알림 발송, 로그 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 작업을 비동기로 처리하는 것이 좋은 것은 아닙니다. 결제 승인, 좌석 예약, 재고 차감처럼 즉시 결과가 중요하고 데이터 정합성이 필요한 작업은 동기적으로 처리하는 것이 더 적절할 수 있습니다. 반면 알림 발송, 로그 저장처럼 요청의 핵심 결과에 직접 영향을 주지 않는 작업은 비동기 처리로 분리하기 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RabbitMQ란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RabbitMQ는 &lt;b&gt;메시지를 Queue에 저장하고 Consumer에게 전달하는 방식에 강점이 있는 메시지 브로커&lt;/b&gt;입니다. RabbitMQ에서는 Producer가 메시지를 보내면 Exchange가 메시지를 어떤 Queue로 보낼지 결정하고, Consumer는 Queue에 쌓인 메시지를 가져가 처리합니다. 그렇기에 작업 큐, 알림 발송, 이메일처럼 메시지를 안정적으로 전달하고 작업을 분산 처리해야 하는 상황에서 자주 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Producer &lt;b&gt;&lt;b&gt;&amp;rarr; Exchange &lt;b&gt;&lt;b&gt;&amp;rarr; Queue &lt;b&gt;&lt;b&gt;&amp;rarr; Consumer&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;RabbitMQ의 특징은 메시지를 Consumer에게 전달하고, Consumer가 처리 완료를 알리면 메시지를 제거하는 방식에 가깝습니다. 따라서 특정 작업을 안정적으로 처리해야 하는 작업 큐 구조에 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Kafka란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka는 &lt;b&gt;대량의 이벤트 데이터를 빠르게 저장하고 처리하는 데 강점이 있는 분산 이벤트 스트리밍 플랫폼&lt;/b&gt;입니다. Kafka에서는 메시지를 Topic에 저장하고, Consumer는 자신이 읽은 위치인 Offset을 기준으로 메시지를 가져갑니다. 메시지는 Consumer가 읽었다고 바로 삭제되지 않고, 설정된 보관 기간 동안 저장될 수 있습니다. 이러한 특징 때문에 Kafka는 로그 수집, 사용자 행동 이벤트 처리, 실시간 데이터 파이프라인, 대규모 이벤트 처리에 많이 사용됩니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Producer &lt;b&gt;&lt;b&gt;&amp;rarr; Topic &lt;b&gt;&lt;b&gt;&amp;rarr; Partition &lt;b&gt;&lt;b&gt;&amp;rarr; Consumer&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Kafka는 메시지를 단순히 전달하는 것보다 이벤트를 일정 기간 저장하고 여러 Consumer가 각자의 속도에 맞게 읽을 수 있다는 점이 특징입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RabbitMQ와 Kafka의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RabbitMQ는 &quot;해야 할 작업을 Queue에 넣고 Consumer가 처리하는 구조&quot;에 가깝고, Kafka는 &quot;발생한 이벤트를 Topic에 기록하고 여러 Consumer가 필요한 시점에 읽는 구조&quot;에 가깝습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.8682%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4728%; text-align: center;&quot;&gt;&lt;b&gt;RabbitMQ&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%; text-align: center;&quot;&gt;&lt;b&gt;Kafka&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.8682%;&quot;&gt;&lt;b&gt;중심 개념&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4728%;&quot;&gt;Queue 기반 메시지 전달&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;Topic 기반 이벤트 스트리밍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.8682%;&quot;&gt;&lt;b&gt;메시지 처리 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4728%;&quot;&gt;Consumer가 처리하면 메시지 제거&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;메시지를 일정 기간 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.8682%;&quot;&gt;&lt;b&gt;강점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4728%;&quot;&gt;작업 큐, 안정적인 메시지 전달&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;대량 이벤트 처리, 로그/스트리밍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.8682%;&quot;&gt;&lt;b&gt;Consumer 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4728%;&quot;&gt;메시지를 전달받아 처리&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;Offset 기준으로 메시지를 읽음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.8682%;&quot;&gt;&lt;b&gt;적합한 상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4728%;&quot;&gt;이메일 발송, 알림 처리, 작업 분산&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;로그 수집, 실시간 분석, 이벤트 파이프라인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.8682%;&quot;&gt;&lt;b&gt;관점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.4728%;&quot;&gt;메시지 브로커&lt;/td&gt;
&lt;td style=&quot;width: 40.6589%;&quot;&gt;이벤트 스트리밍 플랫폼&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;어떤 상황에서 사용할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 브로커는 다음과 같은 상황에서 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.0465%;&quot;&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.9535%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.0465%;&quot;&gt;이메일 발송&lt;/td&gt;
&lt;td style=&quot;width: 78.9535%;&quot;&gt;회원가입 후 환영 이메일을 비동기로 발송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.0465%;&quot;&gt;알림 처리&lt;/td&gt;
&lt;td style=&quot;width: 78.9535%;&quot;&gt;주문 완료, 댓글 작성, 좋아요 발생 시 알림 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.0465%;&quot;&gt;이미지 처리&lt;/td&gt;
&lt;td style=&quot;width: 78.9535%;&quot;&gt;이미지 업로드 후 썸네일 생성 작업 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.0465%;&quot;&gt;로그 수집&lt;/td&gt;
&lt;td style=&quot;width: 78.9535%;&quot;&gt;사용자 행동 로그, 서버 로그를 모아 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.0465%;&quot;&gt;주문 후속 처리&lt;/td&gt;
&lt;td style=&quot;width: 78.9535%;&quot;&gt;주문 완료 후 재고, 배송, 알림 작업 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.0465%;&quot;&gt;이벤트 기반 구조&lt;/td&gt;
&lt;td style=&quot;width: 78.9535%;&quot;&gt;서비스 간 직접 호출 대신 이벤트로 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 SNS 서비스에서 사용자가 댓글을 작성했을 때, 댓글 저장은 즉시 처리되어야 하지만 알림 발송은 비동기로 분리할 수 있습니다. 댓글 저장이 성공한 뒤 &quot;댓글 작성 이벤트&quot;를 메시지 브로커에 전달하고, 알림 서비스가 해당 메시지를 소비해 알림을 보내는 방식입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp; 다음 시간에는 컨테이노화와 가상화 그리고 Docker에 대해서 얘기해보도록 합시다. 수고하셨습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/60</guid>
      <comments>https://0110020321.tistory.com/60#entry60comment</comments>
      <pubDate>Wed, 27 May 2026 13:52:55 +0900</pubDate>
    </item>
    <item>
      <title>만보기 API 연동하기(1)</title>
      <link>https://0110020321.tistory.com/59</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 요즘 개발하는게 다시 재밌어져서 이전에는 생각하지 못했던 것에 대한 기획이나 계획 등 재미를 다시 느끼는 것 같습니다. 이번에는 저의 프로젝트에서 필요한 만보기와 지도의 현재 위치, 마커 표시 등에 대해서 API를 가져오는 과정을 얘기해보고자 합니다. 그럼 시작해봅시다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-end=&quot;398&quot; data-start=&quot;187&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로젝트 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;398&quot; data-start=&quot;187&quot; data-ke-size=&quot;size16&quot;&gt;현재 제 프로젝트의 구조는 백엔드는 &lt;b&gt;Java/Spring Boot&lt;/b&gt;, 프론트엔드는 &lt;b&gt;Flutter(Dart)&lt;/b&gt;, 서버 및 데이터베이스는 &lt;b&gt;Ubuntu + Nginx/MySQL&lt;/b&gt;로 구성되어 있습니다. 안드로이드 앱을 기준으로 개발을 시작했지만, 포트폴리오 목적으로 웹을 먼저 배포했습니다. 이후 기능이 어느 정도 완성되면 원스토어 배포까지 진행하는 것이 목표입니다.&lt;/p&gt;
&lt;p data-end=&quot;398&quot; data-start=&quot;187&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;441&quot; data-start=&quot;400&quot; data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 API를 추가하기 전에 먼저 고민해야 할 부분이 있었습니다.&lt;/p&gt;
&lt;p data-end=&quot;565&quot; data-start=&quot;443&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;565&quot; data-start=&quot;443&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째는 &lt;b&gt;웹에서는 만보기 기능이 필요하지 않다&lt;/b&gt;는 점입니다.&lt;br /&gt;만보기는 사용자가 실제로 휴대폰을 들고 이동할 때 의미가 있는 기능이기 때문에, 웹 환경에서는 직접적인 만보기 측정 기능을 구현할 필요가 없습니다.&lt;/p&gt;
&lt;p data-end=&quot;702&quot; data-start=&quot;567&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;702&quot; data-start=&quot;567&quot; data-ke-size=&quot;size16&quot;&gt;다만 웹에서도 앱과 완전히 분리된 화면이 아니라면, 서버에 저장된 오늘 걸음 수나 캐릭터 상태를 조회하는 정도는 필요할 수 있습니다. 즉, &lt;b&gt;측정은 앱에서만 수행하고, 조회는 웹에서도 가능하도록 구성&lt;/b&gt;하는 방식이 적절하다고 판단했습니다.&lt;/p&gt;
&lt;p data-end=&quot;702&quot; data-start=&quot;567&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;791&quot; data-start=&quot;704&quot; data-ke-size=&quot;size16&quot;&gt;Flutter에서는 kIsWeb 등을 이용해 실행 환경을 구분할 수 있기 때문에, 만보기 센서 연동은 앱 환경에서만 동작하도록 분기 처리할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779797175200&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!kIsWeb) {
  // Android 앱에서만 만보기 센서 실행
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;두 가지로 나뉜 만보기 기능&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &quot;만보기 기능&quot; 하나로 생각했지만, 실제로는 성격이 다른 두 기능으로 나눌 필요가 있었습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;캐릭터 페이지의 오늘 걸음 수&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지도 페이지의 러닝/산책 경로 기록&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터 페이지의 만보기는&lt;b&gt; 오늘 하루 동안 얼마나 걸었는지를 보여주는 기능&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 지도 페이지의 러닝 기록은&lt;b&gt; 특정 시간 동안 어디를 걸었고, 그 경로와 당시 걸음 수가 어땠는지 보여주는 기능&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 같은 만보기처럼 보여도 저장 방식과 API 구조를 분리하는 것이 더 적절하다고 판단했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;앞으로 추가할 API&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 언급했듯이 만보기에 대해서 외에도 지도 ..뭐뭐를 위해서 추가할 예정이며 이번 편은 캐릭터 페이지의 오늘 걸음 수에 대한 글만을 정리할 예정입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;기능&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;추천 패키지&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;만보기/걸음 수&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;pedometer 또는 health&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;걸음 수 측정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;현재 위치/이동 경로&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;geolocator&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;GPS 좌표 수집&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;지도 표시&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;google_maps_flutter&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;지도, 마커, Polylien 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;백그라운드 기록&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;flutter_foreground_task 또는 flutter_background_geolocation&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;앱이 내려가도 위치/걸음 기록 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;캐릭터 페이지용 일일 만보기 API&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터 페이지에서는 오늘 걸음수를 보여줘야 합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;오늘의 걸음 수 / 목표 걸음수(보통 10,000으로 설정 예정)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;해당 기능은 하루 단위로 관리하는 것이 좋기에 DailyStep 구조를 사용하겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1779797744280&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DailyStep
- user_id
- step_date
- step_count
- goal_steps
- created_at
- updated_at&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 매일 00시에 DB 값을 직접 0으로 초기화하지 않는다는 점입니다. 대신 날짜 기준으로 조회합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779797794731&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2026-05-26 &amp;rarr; 2026-05-26의 DailyStep 조회
2026-05-27 &amp;rarr; 2026-05-27의 DailyStep 조회&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 날짜가 바뀌면 자연스럽게 오늘 걸음 수가 0부터 시작하는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;구현하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 안드로이드 앱을 기준으로 만보기 기능을 구현할 예정이기에 바로 백엔드 API부터 만들기보다는 &lt;b&gt;Flutter에서 실제 기기 센서값이 정상적으로 들어오는지 먼저 확인&lt;/b&gt;하기로 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만보기 기능은 일반적인 CRUD 기능과 달리 사용자의 기기 센서, 권한, 실행 환경에 영향을 많이 받습니다. 따라서 백엔드에 저장 구조를 먼저 만들기 전에 프론트엔드에서 다음 항목들을 먼저 검증할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;실제&amp;nbsp;Android&amp;nbsp;기기에서&amp;nbsp;걸음&amp;nbsp;수가&amp;nbsp;측정되는가? &lt;br /&gt;2.&amp;nbsp;신체&amp;nbsp;활동&amp;nbsp;권한&amp;nbsp;요청이&amp;nbsp;정상적으로&amp;nbsp;동작하는가? &lt;br /&gt;3.&amp;nbsp;위치&amp;nbsp;좌표가&amp;nbsp;정상적으로&amp;nbsp;수집되는가? &lt;br /&gt;4.&amp;nbsp;수집된&amp;nbsp;좌표를&amp;nbsp;리스트&amp;nbsp;형태로&amp;nbsp;관리할&amp;nbsp;수&amp;nbsp;있는가? &lt;br /&gt;5.&amp;nbsp;이후&amp;nbsp;서버로&amp;nbsp;전송할&amp;nbsp;데이터&amp;nbsp;형태를&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 통해 실제 앱 환경에서 만보기 기능이 동작하는지 확인한 뒤, 그 결과를 바탕으로 백엔드 API와 DB 구조를 설계하고 프론트엔드에 적용하는 순서로 진행했습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프론트엔드 테스트 &amp;rarr; 백엔드 API 구현&lt;/b&gt;&amp;rarr;&lt;b&gt;&lt;b&gt; 서버 배포 및 API 확인&lt;b&gt;&amp;rarr;캐릭터 페이지 연동&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프론트엔드 테스트하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Flutter에서 만보기와 위치 정보가 정상적으로 수집되는지 확인하기 위해 임시 데이터 페이지를 만들었습니다. 이 테스트 페이지의 목적은 최종 UI를 만드는 것이 아닌 &lt;b&gt;실제 Android 기기에서 센서 데이터가 들어오는지 확인하는 것&lt;/b&gt;이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. pubspec.yaml 설정하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779798190410&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pedometer: 4.1.1 #기기의 걸음 수 센서 값 수신
geolocator: 10.1.1 # 현재 위치 및 위치 변환 감지
google_maps_flutter: 2.3.0 # 지도 표시 및 경로 시각화 테스트
permission_handler: 10.4.5 #신체 활동 권한 요청&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. AndroidManifest.xml에 권한 요청&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779798292577&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &amp;lt;uses-permission android:name=&quot;android.permission.ACTIVITY_RECOGNITION&quot; /&amp;gt;
  &amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot; /&amp;gt;
  &amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_COARSE_LOCATION&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 테스트 페이지 작성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후 지도에서의 만보기도 같이 추가할 예정이기에 테스트 페이지에서는 다음 정보를 화면에 표시했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;현재 걸음 수&lt;/li&gt;
&lt;li&gt;현재 위치 위도/경도&lt;/li&gt;
&lt;li&gt;수집된 좌표 개수&lt;/li&gt;
&lt;li&gt;측정 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 지도까지 함께 표시하려고 했지만, Google Map 발급과 함께 초기화 과정에서 문제가 발생할 수 있기에 일단은 제거하고, 걸음 수와 좌표가 들어오는지 먼저 확인했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;신체 활동 권한 요청&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779798462635&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Future&amp;lt;bool&amp;gt; requestActivityPermission() async {
  final status = await Permission.activityRecognition.status;

  if (status.isGranted) {
    return true;
  }

  final result = await Permission.activityRecognition.request();

  if (result.isGranted) {
    return true;
  }

  return false;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 사용자가 이미 권한을 허용했다면 바로 true를 반환하고, 아직 허용하지 않았다면 권한 요청 창을 띄웁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 추가한 뒤에야 Pedometer.stepCountStream에서 걸음 수가 정상적으로 들어오는 것을 확인할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;걸음 수 측정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779798605747&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StreamSubscription&amp;lt;StepCount&amp;gt;? _stepSubscription;

int? startStepCount;
int currentSteps = 0;

_stepSubscription = Pedometer.stepCountStream.listen(
  (StepCount event) {
    startStepCount ??= event.steps;

    setState(() {
      currentSteps = event.steps - startStepCount!;
    });

    debugPrint('시작 기준 걸음 수: $startStepCount');
    debugPrint('현재 운동 걸음 수: $currentSteps');
  },
  onError: (error) {
    debugPrint('걸음 수 측정 오류: $error');
  },
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한 확인 후 Pedometer.stepCountStream을 구독했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 event.steps가 &quot;이번 측정에서 걸은 걸음 수&quot;가 아니라, 거기 기준 누적 걸음 수처럼 들어올 수 있다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 측정을 시작한 순간의 값을 startStepCount로 저장하고, 이후 값에서 빼는 방식으로 현재 측정 중 걸음 수를 계산했습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재 측정 걸음 수 = 현재 센서 걸음 수 -&amp;nbsp; 시작 시점 센서 걸음 수&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위치 좌표 수집&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779798731120&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StreamSubscription&amp;lt;Position&amp;gt;? _positionSubscription;

Position? currentPosition;
final List&amp;lt;LatLng&amp;gt; routePoints = [];

const LocationSettings locationSettings = LocationSettings(
  accuracy: LocationAccuracy.high,
  distanceFilter: 5,
);

_positionSubscription = Geolocator.getPositionStream(
  locationSettings: locationSettings,
).listen(
  (Position position) {
    final point = LatLng(
      position.latitude,
      position.longitude,
    );

    setState(() {
      currentPosition = position;
      routePoints.add(point);
    });

    debugPrint('위치: ${position.latitude}, ${position.longitude}');
  },
  onError: (error) {
    debugPrint('위치 측정 오류: $error');
  },
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;3280&quot; data-start=&quot;3217&quot; data-ke-size=&quot;size16&quot;&gt;distanceFilter: 5는 사용자가 약 5m 정도 이동했을 때 위치 업데이트를 받도록 설정한 값입니다.&lt;/p&gt;
&lt;p data-end=&quot;3336&quot; data-start=&quot;3282&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 수집한 좌표들은 이후 지도 페이지에서 마커와 선으로 경로를 표시할 때 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;3336&quot; data-start=&quot;3282&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;3336&quot; data-start=&quot;3282&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;백엔드 API 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 테스트를 통해 실제 Android 기기에서 걸음 수와 위치 좌표를 수집할 수 있음을 확인했습니다. 이후에는 이 데이터를 서버에 저장하고, 캐릭터 페이지에서 다시 조회할 수 있도록 백엔드 API를 구현했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;필요한 데이터 구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터 페이지의 만보기는 사용자별로 하루에 하나의 기록만 있으면 됩니다. 따라서 DailStep 엔티티는 다음 정보를 갖도록 설계했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779799994618&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DailyStep
- user
- stepDate
- stepCount
- goalSteps
- createdAt
- updatedAt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 stepDate를 둔 이유는 매일 00시에 데이터를 직접 초기화하지 않고, 날짜 기준으로 오늘의 데이터를 조회하기 위해서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 한 사용자에게 같은 날짜의 데이터가 여러 개 생기면 오늘 걸음 수가 중복될 수 있기 때문에, user_id + step_date 조합은 유니크하게 관리하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO는 크게 두 개만 필요했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;StepUpdateRequest &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;rarr;&amp;nbsp;앱에서&amp;nbsp;서버로&amp;nbsp;현재&amp;nbsp;걸음&amp;nbsp;수를&amp;nbsp;보낼&amp;nbsp;때&amp;nbsp;사용 &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TodayStepResponse &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;rarr;&amp;nbsp;캐릭터&amp;nbsp;페이지에&amp;nbsp;오늘&amp;nbsp;걸음&amp;nbsp;수와&amp;nbsp;목표&amp;nbsp;걸음&amp;nbsp;수를&amp;nbsp;내려줄&amp;nbsp;때&amp;nbsp;사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서비스 로직 작성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 서비스 로직에서는 담당하는 역할은 크게 두 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 앱에서 전달한 오늘 걸음 수를 저장하거나 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 캐릭터 페이지에 표시할 오늘 걸음 수를 조회한다.&lt;/p&gt;
&lt;pre id=&quot;code_1779800061691&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class DailyStepService {

    private final DailyStepRepository dailyStepRepository;
    private final UserRepository userRepository;

    @Transactional
    public TodayStepResponse updateTodaySteps(
            StepUpdateRequest request,
            Authentication authentication
    ) {
        String email = authentication.getName();

        User user = userRepository.findByEmail(email)
                .orElseThrow(() -&amp;gt; new IllegalArgumentException(&quot;존재하지 않는 사용자입니다.&quot;));

        LocalDate today = LocalDate.now();

        DailyStep dailyStep = dailyStepRepository
                .findByUserIdAndStepDate(user.getId(), today)
                .orElseGet(() -&amp;gt; DailyStep.create(user, today, 0));

        dailyStep.updateStepCount(request.getStepCount());

        DailyStep saved = dailyStepRepository.save(dailyStep);

        return new TodayStepResponse(
                saved.getStepCount(),
                saved.getGoalSteps()
        );
    }

    @Transactional(readOnly = true)
    public TodayStepResponse getTodaySteps(Authentication authentication) {
        String email = authentication.getName();

        User user = userRepository.findByEmail(email)
                .orElseThrow(() -&amp;gt; new IllegalArgumentException(&quot;존재하지 않는 사용자입니다.&quot;));

        LocalDate today = LocalDate.now();

        return dailyStepRepository.findByUserIdAndStepDate(user.getId(), today)
                .map(dailyStep -&amp;gt; new TodayStepResponse(
                        dailyStep.getStepCount(),
                        dailyStep.getGoalSteps()
                ))
                .orElseGet(() -&amp;gt; new TodayStepResponse(0, 10000));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로그인한 사용자를 확인한 후 &amp;rarr; 오늘 날짜 기준 DailyStep 조회 &amp;rarr; 있으면 걸음 수 갱신 &amp;rarr; 없으면 새 DailyStep 생성 &amp;rarr; 캐릭터 페이지에 필요한 todaySteps, goalSteps 반환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 프론트엔드와 연동하면서 캐릭터 페이지에서 실제 걸음수가 표시되고, 사용자가 걸을 때마다 숫자가 증가하는 것을 확인할 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4320&quot; data-origin-height=&quot;1600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnTkGt/dJMcabLam1T/N93W4lMecCu8AkH0yJvlHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnTkGt/dJMcabLam1T/N93W4lMecCu8AkH0yJvlHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnTkGt/dJMcabLam1T/N93W4lMecCu8AkH0yJvlHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnTkGt%2FdJMcabLam1T%2FN93W4lMecCu8AkH0yJvlHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4320&quot; height=&quot;1600&quot; data-origin-width=&quot;4320&quot; data-origin-height=&quot;1600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번 시간에는 만보기 API를 연동하고 직접 센서가 작동하는지 확인하는 시간을 가졌습니다. 원래라면 현재 언급한 모든 기능을 넣은 후에 다음 기능으로 넘어갈 예정이였습니다만 그렇게되면 앱스토어에 배포하는데까지 오랜 시간이 걸릴 것으로 판단되어 만보기 API는 초기 기획했던 현재의 정도로만 넣은 후 앱스토어에 배포한 다음 추가할 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;아마 다음 기능으로 친구 추가를 통해 웹소켓 기능을 더욱 편하게 만들도록 할 것 같아요. 몇 가지 오류나 UI/UX측면에서 불편한 점까지 수정하면 앱스토어에 배포도 할 것 같습니다. 다음 시간에는 또 다른 주제의 개발일지로 돌아오며 만보기 API도 꼭 끝마치도록 하겠습니다. 감사합니다.&lt;/p&gt;</description>
      <category>개발일지</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/59</guid>
      <comments>https://0110020321.tistory.com/59#entry59comment</comments>
      <pubDate>Tue, 26 May 2026 22:03:21 +0900</pubDate>
    </item>
    <item>
      <title>검색 엔진 - Elasticsearch</title>
      <link>https://0110020321.tistory.com/58</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 좋은 아침입니다. 오늘부터 천천히 아침 일찍 일어나다가 깁스를 풀고나면 그때부터 미라클 모닝을 하려고 합니다. 최근 오전 5시에 일어나게 된 이유라는 영상을 보게되었는데, 결국 일찍 자면 일찍 일어나게 된다는게 당연하면서도 여태 이걸 하지 못했구나 싶더라고요? 저도 과거에 미라클 모닝했던 기억이 생각나면서 다시 도전하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;오늘도 일찍 수면에 취한 덕분에 알림 없이 잘 일어났네요. 그럼 검색엔진에 대한 학습 시작하도록 하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;검색 엔진&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검색 엔진(Search Engine)은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;대량의 데이터에서 사용자가 원하는 정보를 빠르게 찾을 수 있도록 도와주는 시스템&lt;/span&gt;입니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 검색이라고 하면 구글이나 네이버 같은 웹 검색을 떠올릴 수 있지만, 서비스 내부에서도 검색 엔진을 자주 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QDHCr/dJMb99T2sPI/GaeGRXPB3chEUMZSOiHCj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QDHCr/dJMb99T2sPI/GaeGRXPB3chEUMZSOiHCj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QDHCr/dJMb99T2sPI/GaeGRXPB3chEUMZSOiHCj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQDHCr%2FdJMb99T2sPI%2FGaeGRXPB3chEUMZSOiHCj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1267&quot; height=&quot;464&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 쇼핑몰에서 상품명을 검색하거나, 커뮤니티에서 게시글을 검색하거나, 운영자가 서버 로그에서 특정 에러 메시지를 찾는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 검색 엔진은 단순히 데이터를 저장하는 것이 아니라 사용자가 입력한 검색어와 가장 관련 있는 데이터를 빠르게 찾아주는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;검색 엔진의 주요 작동 원리 3단계&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 크롤링&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검색 대상이 되는 데이터를 수집하는 단계&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 검색 엔진이라면 웹 페이지를 수집하고, 서비스 내부 검색이라면 게시글, 상품, 사용자 데이터, 로그 데이터 등을 검색 대상으로 가져올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 크롤링은 꼭 웹 페이지에만 한정되기보다 검색에 사용할 원본 데이터를 모으는 과정으로 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 인덱싱&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;수집한 데이터를 검색하기 좋은 형태로 가공하고 저장하는 단계&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 원본 데이터를 그대로 저장하는 것이 아니라 텍스트를 추출하고, 단어를 나누고, 불필요한 요소를 정리한 뒤 검색에 적합한 인덱스를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 엔진이 빠르게 검색할 수 있는 이유는 이 인덱싱 과정에서 미리 검색용 자료구조를 만들어두기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 결과 제공&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 검색어를 입력하면 검색 엔진은 미리 만들어둔 인덱스를 기준으로 관련 문서를 찾습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 단순히 포함 여부만 보는 것이 아니라, 검색어와 문서의 관련도, 문서의 중요도, 사용자 행동 데이터 등을 고려해 결과의 순위를 매기고 &lt;b&gt;사용자에게 제공&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DB 검색과 검색 엔진의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스에서도 검색 기능을 구현할 수 있습니다. 예를 들어 MySQL에서는 'LIKE' 검색을 사용해 특정 문자열이 포함된 데이터를 조회할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779751978861&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT * FROM posts WHERE title LIKE '%검색어%;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이러한 방식은 데이터가 많아질수록 성능 문제가 발생할 수 있습니다. 특히 앞뒤로 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;%&lt;/span&gt;가 붙는 검색은 인덱스를 효율적으로 사용하기 어렵고, 전체 데이터를 훑어보는 방식에 가까워질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 일반적인 DB 검색은 사용자가 입력한 검색어의 유사도, 형태소 분석, 오타 보정, 관련도 점수 계산 같은 검색 특화 기능을 처리하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;DB는 데이터를 정확하게 저장하고 조회하는데 강점이 있고, 검색 엔진은 대량의 텍스트에서 관련성 높은 결과를 빠르게 찾는 데 강점&lt;/b&gt;이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Elasticsearch란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Elasticsearch는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;대량의 데이터를 실시간으로 저장, 검색 및 분석하기 위해 사용되는 오픈소스 분산형 검색 엔진&lt;/span&gt;입니다.&lt;/b&gt; Apache Lucene을 기반으로 만들어졌으며, REST API를 통해 데이터를 저장하고 검색할 수 있습니다. 검색 기능뿐만 아니라 로그 분석, 모니터링, 통계 분석 등 다양한 상황에서도 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 기본 백엔드로 사용하거나 로그 및 지표 분석, 엔터프라이즈 검색, 보안 인텔리전스에 자주 사용됩니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Apache Lucene&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Apache Lucene은 Java로만 작성된 무료 오픈 소스 검색 엔진 라이브러리입니다.&lt;/b&gt; Lucene은 주로 검색 엔진 구현 기능으로 유명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lucene은 &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;문서를 검색 및 인덱스의 기본 단위로 활용합니다. &lt;/span&gt;&lt;span style=&quot;caret-color: auto; letter-spacing: 0px;&quot;&gt;모든 문서 콘텐츠를 키워드 중심 데이터 구조로 인덱싱하고 저장하기 때문입니다. 또한 매우 빠른 검색 응답 시간을 달성할 수 있습니다. &lt;/span&gt;&lt;span style=&quot;caret-color: auto; letter-spacing: 0px;&quot;&gt;Lucene에 저장된 콘텐츠는 웹 사이트, 파일 시스템 및 Postgre SQL과 같은 데이터베이스를 비롯한 다양한 소스에서 가져올 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Apache Lucene의 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;수평적 확장성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;여러 코딩 언어 지원&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동 완성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;플러그인 및 통합 지원&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Elasticsearch의 특징&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;탁월한 검색 성능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;역인덱스 구조를 사용하여 방대한 양의 데이터에서도 초고속 풀텍스트 검색을 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;수평적 확장성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;데이터를 여러 조각(샤드)으로 나누어 분산 저장 및 처리하므로 서버를 추가하여 용량과 성능을 손쉽게 늘릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RESTful API&amp;nbsp;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;JSON 기반의 HTTP 요청을 통해 데이터를 간편하게 색인하고 쿼리할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ELK 스택의 중심&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;데이터 수집 및 시각화 도구인 Logstash, Kibana와 결합하여 시스템 모니터링, 로그 분석 등 다양한 분야에 활용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Elasticsearch 구조&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;논리적 구조&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논리적 구조는 데이터를 어떻게 저장하고 표현하는지를 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Index&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Index는 Document들이 저장되는 논리적인 공간입니다. 관계형 데이터베이스에 비유하면 테이블과 비슷하게 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 게시글 검색 기능을 만든다면 posts라는 Index를 만들 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Index : posts&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;다만 Elasticsearch의 Index는 RDBMS의 테이블과 완전히 같은 개념은 아닙니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Document&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Document는 Elasticsearch에 저장되는 데이터 한 건입니다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Elasticsearch에서는 데이터가 JSON 형태의 Document로 저장됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 게시글 하나는 다음과 같이 Document가 될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779755491404&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;title&quot;: &quot;Elasticsearch란?&quot;,
  &quot;content&quot;: &quot;Elasticsearch는 대량의 데이터를 빠르게 검색하기 위한 검색 엔진입니다.&quot;,
  &quot;author&quot;: &quot;user1&quot;,
  &quot;createdAt&quot;: &quot;2026-05-26&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Field&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Field는 Document 안에 있는 각각의 속성입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서는 다음 값들이 Field입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779755566276&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;title
content
author
createdAt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Field는 검색 방식에 따라 다르게 다룰 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 title과 content는 문장을 분석해서 검색해야 하므로 전문 검색 대상이 될 수 있고, author는 정확히 일치하는 값으로 검색하는 것이 더 적합할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Mapping&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mapping은 각 Field가 어떤 타입을 가지며, 어떻게 인덱싱될지를 정의하는 설정입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779755710223&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;title&quot;: &quot;text&quot;,
  &quot;content&quot;: &quot;text&quot;,
  &quot;author&quot;: &quot;keyword&quot;,
  &quot;createdAt&quot;: &quot;date&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 text는 문장을 분석해서 검색할 때 사용하고, keyword는 정확한 값 비교에 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 title이 text라면 &quot;Elasticsearch란?&quot;이라는 문장이 분석되어 검색 가능한 단어 단위로 처리될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 author가 keyword라면 &quot;user1&quot;이라는 값 전체를 기준으로 정확히 일치하는 데이터를 찾는 데 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;물리적 구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 데이터를 어떻게 저장하고 분산 처리하는지를 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Cluster&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cluster는 하나 이상의 Node가 모여 구성된 Elasticsearch 전체 시스템입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 규모가 작다면 하나의 Node로도 Elasticsearch를 실행할 수 있지만, 데이터가 많아지고 안정성이 중요해지면 여러 Node를 하나의 Cluster로 구성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779756091662&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Cluster
 ├─ Node 1
 ├─ Node 2
 └─ Node 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Node &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node는 Elasticsearch가 실행되는 하나의 서버입니다. 하나의 Cluster 안에는 여러 Node가 존재할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Node는 데이터를 저장하거나, 검색 요청을 처리하거나, Cluster 상태를 관리하는 역할을 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Node는 Elasticsearch 시스템을 구성하는 개별 서버 단위입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Shard&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shard는 한의 Index를 나누어 저장하는 단위입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 posts Index에 게시글 데이터가 매우 많아졌을 때, 이 데이터를 하나의 서버에만 저장하면 저장 공간과 검색 처리에 부담이 생길 수 있습니다. 그래서 Elasticsearch는 하나의 Index를 여러 Shard로 나누어 저장할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779756277991&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;posts Index
 ├─ Shard 0
 ├─ Shard 1
 └─ Shard 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Replica&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Replica는 Shard의 복제본입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Shard 0의 원본이 Node 1에 저장되어 있다면, 그 복제본을 Node 2에 저장할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1779756344597&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Node 1: Shard 0
Node 2: Shard 0 Replica&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Replica를 두는 이유는 크게 두 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;첫 번째는 장애 대비입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Primary Shard가 있는 Node에 문제가 생겨도 Replica Shard를 통해 데이터를 유지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두 번 째는 검색 성능 향상입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 요청은 Primary Shard 뿐만 아니라 Replica Shard에서도 처리할 수 있기에 요청을 분산해서 처리할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;역색인 구조란?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Elasticsearch가 빠르게 검색할 수 있는 핵심 이유 중 하나는 역색인 구조를 사용하기 때문입니다. &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 데이터 저장 방식은 문서 안에 어떤 단어가 있는지 확인합니다. 반면 역색인은 단어를 기준으로 해당 단어가 어떤 문서에서 등장하는지 미리 정리해둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같이 게시글이 있다고 가정해보겠습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 80px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.5116%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;문서 ID&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 88.4884%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;제목&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.5116%; height: 21px;&quot;&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 88.4884%; height: 21px;&quot;&gt;Spring Boot 검색 기능 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 11.5116%; height: 21px;&quot;&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 88.4884%; height: 21px;&quot;&gt;Elasticsearch 검색 엔진 학습&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 11.5116%; height: 17px;&quot;&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 88.4884%; height: 17px;&quot;&gt;Spring Security&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 역색인 구조로 정리하면 다음과 같이 볼 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 110px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 15.9302%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;단어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 84.0698%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;등장한 문서&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.9302%; height: 17px;&quot;&gt;&lt;b&gt;Spring&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 84.0698%; height: 17px;&quot;&gt;1, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.9302%; height: 17px;&quot;&gt;&lt;b&gt;Boot&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 84.0698%; height: 17px;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 15.9302%; height: 21px;&quot;&gt;&lt;b&gt;검색&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 84.0698%; height: 21px;&quot;&gt;1, 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.9302%; height: 17px;&quot;&gt;&lt;b&gt;Elasticsearch&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 84.0698%; height: 17px;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.9302%; height: 17px;&quot;&gt;&lt;b&gt;Security&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 84.0698%; height: 17px;&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 단어별로 어떤 문서에 등장했는지 미리 정리해두면 사용자가 &quot;검색&quot;이라는 단어를 입력했을 때 모든 문서를 처음부터 끝까지 확인하지 않아도 됩니다. 이미 만들어진 역색인에서 &quot;검색&quot;이라는 단어가 등장한 문서 목록을 바로 찾을 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Elasticsearch가 사용되는 상황&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elasticsearch는 다음과 같은 상황에서 사용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;상품 검색&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;상품명, 카테고리, 브랜드, 설명 등을 기준으로 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;게시글 검색&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;제목, 내용, 작성자, 태그 기반 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;로그 검색&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;서버 로그, 에러 로그, 사용자 행동 로그 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;자동 완성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;사용자가 입력하는 검색어에 맞춰 추천어 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;필터 검색&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;가격, 지역, 날짜, 카테고리 조건을 함께 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 제 개인 프로젝트인 커뮤니티 서비스에서는 게시글 제목과 내용을 기준으로 검색 기능을 제공할 수 있겠네요. 단순한 규모에서는 RDBMS 검색으로도 충분할 수 있지만, 게시글 수가 많아지고 검색 정확도나 속도가 중요해진다면 Elasticsearch 같은 검색 엔진을 고려할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;오늘은 검색 엔진과 Elasticsearch에 대해 학습해보았습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;역색인 구조가 단순하지만 색인 구조가 아닌 역색인을 통해 검색 효율을 높인점이 굉장히 인상깊었던 것 같습니다. 다음 시간에는 메시지 브로커와 함께 돌아오도록 하겠습니다. 수고하셨습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/58</guid>
      <comments>https://0110020321.tistory.com/58#entry58comment</comments>
      <pubDate>Tue, 26 May 2026 09:49:20 +0900</pubDate>
    </item>
    <item>
      <title>아키텍처 패턴(2) - CQRS, 이벤트 소싱, 서버리스란 무엇인가?</title>
      <link>https://0110020321.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 좋은 밤입니다. 오늘은 쉬려고 했으나 블로그라도 쓰자라는 생각으로 늦게라도 돌아왔습니다. 그럼 바로 시작하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CQRS&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CQRS(Command Query Responsbility Segregation)는 명령과 조회의 책임을 분리하는 패턴입니다&lt;/b&gt;. 여기서 &lt;b&gt;Command는 데이터를 생성, 수정, 삭제하는 작업&lt;/b&gt;을 의미하고, &lt;b&gt;Query는 데이터를 조회하는 작업&lt;/b&gt;을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 CRUD 구조에서는 하나의 모델이나 서비스에서 생성, 수정, 삭제, 조회를 함께 처리하는 경우가 많습니다. 하지만 서비스가 커지면 쓰기 작업과 읽기 작업의 요구사항이 달라질 수 있습니다. 예를 들어 주문 생성은 데이터 정합성이 중요하고, 주문 조회는 빠른 응답 속도와 다양한 조회 조건이 중요할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CQRS가 필요한 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CQRS는 읽기와 쓰기의 요구사항이 크게 다를 때 유용할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 105px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;&lt;b&gt;조회가 매우 많을 때&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;읽기 전용 모델을 따로 두어 조회 성능을 최적화할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;&lt;b&gt;쓰기 로직이 복잡할 때&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;Command 쪽에서 검증과 도메인 규칙을 명확히 관리할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;&lt;b&gt;조회 화면이 다양할 때&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;Query 모델을 화면에 맞게 구성할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;&lt;b&gt;데이터 변경 이력이 중요할 때&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;이벤트 소싱과 함께 사용할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 CQRS는 구조가 단순하지 않습니다. Command와 Query를 분리하면 코드와 데이터 흐름이 늘어나기에 작은 서비스에서는 오히려 복잡도가 커질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이벤트 소싱이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 소싱은 현재 상태만 저장하는 것이 아닌 상태가 변경된 이력을 이벤트로 저장하는 방식입니다.&lt;/b&gt; 즉, 데이터의 최종 상태가 아닌 그 상태가 만들어진 변경 과정을 기록한다고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 장바구니 기능에서는 장바구니 생성, 상품 추가, 상품 제거, 배송 정보 입력과 같은 이벤트를 순서대로 저장할 수 있습니다. 이후 이 이벤트들을 다시 적용하면 현재 장바구니 상태를 재구성할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CQRS와 이벤트 소싱의 관계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CQRS와 이벤트 소싱은 서로 다른 개념이지만 함께 사용되는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CQRS는 읽기와 쓰기 책임을 분리하는 패턴이고, 이벤트 소싱은 상태 변경 이력을 이벤트로 저장하는 방식입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.9767%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 83.0233%; text-align: center;&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.9767%;&quot;&gt;&lt;b&gt;CQRS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 83.0233%;&quot;&gt;Command와 Query 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.9767%;&quot;&gt;&lt;b&gt;이벤트 소싱&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 83.0233%;&quot;&gt;상태 변경 이력을 이벤트로 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.9767%;&quot;&gt;&lt;b&gt;함께 사용할 때&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 83.0233%;&quot;&gt;Command와 이벤트를 저장하고, Query 모델은 이벤트를 기반으로 조회용 데이터를 구성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림은 이벤트 소싱이 CQRS와 함께 사용되는 구조 예시입니다. Command 측에서 발생한 이벤트를 저장하고, 이를 기반으로 조회용 저장소나 외부 시스템을 갱신하는 흐름을 보여줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRmmaV/dJMcagljs2J/m75MGJpiJky0BQRTRW5BWk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRmmaV/dJMcagljs2J/m75MGJpiJky0BQRTRW5BWk/img.jpg&quot; data-alt=&quot;https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRmmaV/dJMcagljs2J/m75MGJpiJky0BQRTRW5BWk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRmmaV%2FdJMcagljs2J%2Fm75MGJpiJky0BQRTRW5BWk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;602&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서버리스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버리스는 개발자가 직접 서버를 관리하지 않고 클라우드 제공자가 실행 환경을 관리해주는 방식입니다.&lt;/b&gt; 서버가 없다는 의미가 아닌 서버의 생성, 확장, 운영을 개발자가 직접 관리하지 않아도 된다는 의미에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 AWS Lambda 같은 서비스를 생각할 수 있습니다. 개발자는 특정 이벤트가 발생했을 때 실행될 함수 코드를 작성하고, 서버 프로비저닝이나 확장 관리는 클라우드 플랫폼에 맡길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이미지가 업로드되면 썸네일을 생성하고, 결제가 완료되면 알림을 발송하며, 정해진 시간마다 데이터를 정리하는 작업을 실행할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G3J4N/dJMcaiXHdLl/ThTKs8zxTTM4KiHWiHW56k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G3J4N/dJMcaiXHdLl/ThTKs8zxTTM4KiHWiHW56k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G3J4N/dJMcaiXHdLl/ThTKs8zxTTM4KiHWiHW56k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG3J4N%2FdJMcaiXHdLl%2FThTKs8zxTTM4KiHWiHW56k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;825&quot; height=&quot;225&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVVWht/dJMcaftbhuJ/rNHuZd7EzsTYaRLejrXYD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVVWht/dJMcaftbhuJ/rNHuZd7EzsTYaRLejrXYD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVVWht/dJMcaftbhuJ/rNHuZd7EzsTYaRLejrXYD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVVWht%2FdJMcaftbhuJ%2FrNHuZd7EzsTYaRLejrXYD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;825&quot; height=&quot;225&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서버리스의 장점과 한계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버리스는 짧고 독립적인 작업을 처리할 때 유용합니다. 하지만 모든 백엔드 애플리케이션을 서버리스로 만드는 것이 항상 좋은 선택은 아닙니다. 실시간 연결이 오래 유지되어야 하거나, 복잡한 상태 관리가 필요한 경우에는 전통적인 서버 구조가 더 적합할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;서버 관리 부담 감소&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용한 만큼 비용 지불 가능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트래픽에 따라 자동 확장 가능&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;콜드 스타트가 발생할 수 있음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라우드 제공자에 대한 의존성이 생길 수 있음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡한 장기 실행 작업에는 적합하지 않을 수 있음&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CQRS, 이벤트 소싱, 서버리스 비교&lt;/b&gt;&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.5116%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.279%; text-align: center;&quot;&gt;&lt;b&gt;핵심 개념&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 30.0002%; text-align: center;&quot;&gt;&lt;b&gt;적합한 상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 32.2092%; text-align: center;&quot;&gt;&lt;b&gt;주의할 점&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.5116%;&quot;&gt;&lt;b&gt;CORS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;읽기와 쓰기 분리&lt;/td&gt;
&lt;td style=&quot;width: 30.0002%;&quot;&gt;조회와 변경 요구사항이 크게 다를 때&lt;/td&gt;
&lt;td style=&quot;width: 32.2092%;&quot;&gt;구조가 복잡해질 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.5116%;&quot;&gt;&lt;b&gt;이벤트 소싱&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;상태 변경 이력을 이벤트로 저장&lt;/td&gt;
&lt;td style=&quot;width: 30.0002%;&quot;&gt;이력 추적과 감사 로그가 중요할 때&lt;/td&gt;
&lt;td style=&quot;width: 32.2092%;&quot;&gt;이벤트 설계와 복구 로직이 어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.5116%;&quot;&gt;&lt;b&gt;서버리스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;서버 관리를 클라우드에 위임&lt;/td&gt;
&lt;td style=&quot;width: 30.0002%;&quot;&gt;이벤트 기반의 짧은 작업 처리&lt;/td&gt;
&lt;td style=&quot;width: 32.2092%;&quot;&gt;클라우드 의존성과 콜드 스타트 고려 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;모든 프로젝트에 필요한 구조는 아니다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CQRS, 이벤트 소싱, 서버리스는 모두 유용한 아키텍처 방식이지만 모든 프로젝트에 필요한 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 프로젝트나 초기 서비스에서는 일반적인 CRUD 구조와 모놀리식 애플리케이션만으로도 충분한 경우가 많습니다. 오히려 처음부터 복잡한 구조를 도입하면 개발 속도가 느려지고 문제를 해결하기보다 구조 자체를 관리하는 데 더 많은 시간이 들어갈 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번편을 마무리로 아키텍처 패턴을 마치도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이제 정말 백엔드 로드맵에 해당하는 부분이 얼마 남지 않았네요. 8챕터 정도 남은 것 같습니다. 다음 시간에는 검색 엔진과 함께 돌아오도록 하겠습니다. 수고하셨습니다.&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/57</guid>
      <comments>https://0110020321.tistory.com/57#entry57comment</comments>
      <pubDate>Mon, 25 May 2026 22:23:43 +0900</pubDate>
    </item>
    <item>
      <title>아키텍처 패턴(1) - 모놀리식, MSA, SOA란 무엇인가?</title>
      <link>https://0110020321.tistory.com/56</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 벌써 일요일이네요. 주말은 정말 빨리 지나가는 것 같아요. 실제로 짧기도 하지만 유독 하루가 더 짧은 느낌? 그래도 다들 월요일날 대체 공휴일고 쉬시니 월요병은 안오실거라고 생각이 드네요! 대신 화요병..?아무튼 아키텐처 패턴에 대한 학습 시작해보도록 하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;아키텍처 패턴을 학습하는 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 시간에는 개발 설계 원칙으로 DRY와 리팩토링에 대해 학습했습니다. 개발 설계 원칙이 클래스, 메서드, 코드 구조를 더 유지보수하기 쉽게 만드는 기준이라면 아키텍처 패턴은 애플리케이션 전체 구조를 어떻게 나눌지 고민하는 기준이라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 작을 때는 하나의 애플리케이션 안에서 모든 기능을 처리해도 큰 문제가 없을 수 있습니다. 하지만 기능이 많아지고 사용자 수가 늘어나면 배포 방식, 장애 영향 범위, 팀 간 협업 방식까지 함께 고려해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 시간에는 대표적인 아키텍처인 모놀리식 애플리케이션, MSA, SOA에 대해 정리해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;아키텍처 패턴이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아키텍처 패턴은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;애플리케이션의 전체 구조를 설계하는 방식&lt;/span&gt;입니다.&lt;/b&gt; 단순히 코드를 어떻게 작성할지에 대한 문제가 아닌 기능을 어떤 단위로 나눌지, 각 기능이 어떻게 통신할지, 배포와 운영을 어떻게 할지까지 포함하는 구조적 설계 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, 아키텍처 패턴은 서비스의 규모와 목적에 맞게 시스템 구조를 나누기 위한 설계 기준입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;모놀리식 애플리케이션&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모놀리식 애플리케이션은 전통적인 방식으로 개발된 소프트웨어 애플리케이션 형태를 나타내며, &lt;b&gt;하나의 애플리케이션 안에 여러 기능이 함께 포함된 구조입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 회원, 게시글, 댓글, 주문, 결제 기능이 하나의 프로젝트 안에 들어 있고, 하나의 서버 애플리케이션으로 빌드되고 배포된다면 모놀리식 구조에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;모놀리식 애플리케이션의 특징&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 코드베이스&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;애플리케이션의 모든 기능과 모듈이 하나의 코드베이스에서 관리됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단일 실행 가능한 단위&amp;nbsp;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;전체 애플리케이션이 하나의 실행 가능한 단위로 패키징되어 배포되며 주로 모노리틱 서버에서 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버 사이드 렌더링(SSR)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;주로 서버 사이드 렌더링 방식으로 사용자 인터페이스를 처리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발과 배포의 단순함&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;하나의 코드베이스를 다루기 때문에 개발 및 배포가 상대적으로 간단할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쉬운 로컬 개발&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;개발자는 애플리케이션의 전체 기능을 로컬 환경에서 쉽게 테스트할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모듈 간 직접 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;서로 다른 모듈 간의 호출이 함수 호출 또는 메소드 호출과 같은 직접적인 방식으로 이루어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모놀리식은 단순하고 빠르게 시작하기 좋지만 규모가 커질수록 유지보수와 배포 단위가 무거워질 수 있는 구조입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MSA&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MSA(Microservice Architecture)는 기존의 모놀리식 아키텍처에서 벗어나 서비스를 작은 단위의 독립적인 서비스로 나누는 아키텍처입니다.&lt;/b&gt; 각 서비스는 독립적으로 개발, 배포, 확장될 수 있으며, 보통 HTTP API, 메시지 큐, gRPC 같은 방식으로 서로 통신합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 커머스 서비스를 MSA로 나눈다면 회원 서비스, 상품 서비스, 주문 서비스, 결제 서비스, 알림 서비스처럼 도메인 단위로 분리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MSA의 특징&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;독립적인 서비스 개발 및 배포&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;각 서비스는 독립덕으로 개발 및 배포할 수 있어 특정 기능을 변경할 때 전체 시스템을 재배포할 필요가 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비즈니스 도메인 중심 설계&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;애플리케이션을 비즈니스 도메인별로 나누어 설계합니다. 예를 들어 커머스 서비스에서는 상품 서비스, 주문 서비스, 결제 서비스로 나눌 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 기술 스택 사용 가능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;각 서비스는 다른 기술 스택(Java, Python, Node.js 등)을 사용할 수 있어 최적화된 기술을 적용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장성과 장애 격리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;트래픽이 몰리는 특정 서비스만 별도록 확장할 수 있으며, 한 서비스에 장애가 발생해도 전체 시스템이 중단되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 MSA는 구조가 복잡합니다. 서비스 간 통신, 데이터 정합성, 장애 전파, 로그 추적, 모니터링, 배포 자동화 등 고려해야할 요소가 많습니다. 단순히 서비스를 나누는 것만으로 MSA가 되는 것이 아니라 운영 환경까지 함께 준비되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, MSA는 확장성과 독립 배포에 강점이 존재하지만, 그만큼 운영 복잡도가 크게 증가하는 구조입니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SOA&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOA(Service-Oriented Architecture)는 &lt;b&gt;서비스 지향 아키텍처&lt;/b&gt;를 의미합니다. &lt;b&gt;시스템을 여러 서비스 단위로 나누고, 각 서비스가 재샤용 가능한 기능을 제공하도록 설계하는 방식입니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOA도 서비스를 나누다는 점에서는 MSA와 비슷하지만 일반적으로 MSA보다 더 큰 단위의 서비스의 서비스와 중앙화된 통합 구조를 가지는 경우가 많습니다. SOA에서는 여러 서비스가 공통된 규칙이나 중앙 통합 계층을 통해 연결되는 경우가 많고, 기업 내부 시스템 간 연동이나 대규모 업무 시스템에서 자주 언급됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 기업 내부에 인사, 회계, 결제, 고객 관리 시스템이 있고, 각 시스템이 공통 서비스나&amp;nbsp; 통합 계층을 통해 서로 데이터를 주고받는 구조를 생각할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, SOA는 여러 업무 시스템을 서비스 단위로 나누고, 공통된 방식으로 연결해 재사용성과 통합을 높이는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;모놀로식, MSA, SOA 비교&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 구조는 어떤 것이 무조건 더 좋다고 말하기 어렵습니다. 서비스 규모,팀 규모, 배포 방식, 운영 역량에 따라 적합한 구조가 달라질 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.814%; text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.279%; text-align: center;&quot;&gt;&lt;b&gt;모놀리식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.4884%; text-align: center;&quot;&gt;&lt;b&gt;MSA&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 29.4186%; text-align: center;&quot;&gt;&lt;b&gt;SOA&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.814%;&quot;&gt;&lt;b&gt;구조&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.279%;&quot;&gt;하나의 애플리케이션에 기능이 모여 있음&lt;/td&gt;
&lt;td style=&quot;width: 28.4884%;&quot;&gt;작은 서비스 단위로 분리&lt;/td&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;업무 서비스 단위로 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.814%;&quot;&gt;&lt;b&gt;배포&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.279%;&quot;&gt;전체를 한 번에 배포&lt;/td&gt;
&lt;td style=&quot;width: 28.4884%;&quot;&gt;서비스별 독립 배포&lt;/td&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;서비스 또는 통합 단위 배포&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.814%;&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.279%;&quot;&gt;개발과 배포가 단순함&lt;/td&gt;
&lt;td style=&quot;width: 28.4884%;&quot;&gt;독립 확장과 배포에 유리함&lt;/td&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;시스템 간 통합과 재사용에 유리함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.814%;&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.279%;&quot;&gt;규모가 커지면 복잡해짐&lt;/td&gt;
&lt;td style=&quot;width: 28.4884%;&quot;&gt;운영 복잡도가 높음&lt;/td&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;구조가 무거워질 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.814%;&quot;&gt;&lt;b&gt;적합한 경우&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.279%;&quot;&gt;개인 프로젝트, 초기 서비스&lt;/td&gt;
&lt;td style=&quot;width: 28.4884%;&quot;&gt;대규모 서비스, 팀 분리, 높은 트래픽&lt;/td&gt;
&lt;td style=&quot;width: 29.4186%;&quot;&gt;기업 내부 시스템, 업무 시스템 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;작은 프로젝트에서는 어떤 구조가 적합할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 프로젝트나 초기 서비스에서는 무조건 MSA를 적용하기보다 모놀리식 구조로 시작하는 것이 더 현실적일 수 있습니다. MSA는 서비스별 독립 배포와 확장이라는 장점이 있지만 그만큼 서비스 간 통신, 인증, 로깅, 모니터링, 데이터 정합성 등 고려해야 할 요소가 많습니다. 이러한 운영 기반이 충분하지 않은 상태에서 서비스를 지나치게 많이 나누면 오히려 개발과 유지보수가 어려워질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 진행한 Spring Boot 기반 프로젝트들도 대부분 하나의 애플리케이션 안에서 회원, 게시글, 댓글, 좋아요 같은 기능을 함께 관리하는 모놀리식 구조에 가깝습니다. 다만 내부 구조까지 모두 섞어두는 것이 아니라 Controller, Service, Repository, Domain, DTO처럼 계층과 역할을 분리하면 모놀리식 구조 안에서도 충분히 유지보수성을 높일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 처음부터 복잡한 구조를 선택하는 것이 아니라 현재 서비스 규모에 맞는 구조를 선택하고 이후 필요에 따라 확장 가능한 방향으로 개선하는 것입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이상으로 아키텍처에 대한 학습 1편을 끝내도록 하겠습니다. 다음 시간에는 CQRS, 이벤트 소싱, 서버리스에 대해 학습해보도록 하겠습니다. 수고하셨습니다.&lt;/p&gt;</description>
      <category>백엔드 공부</category>
      <author>밈밍민믹</author>
      <guid isPermaLink="true">https://0110020321.tistory.com/56</guid>
      <comments>https://0110020321.tistory.com/56#entry56comment</comments>
      <pubDate>Sun, 24 May 2026 13:22:27 +0900</pubDate>
    </item>
  </channel>
</rss>