<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>TheWing</title>
    <link>https://sujl95.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 23:12:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>TheWing</managingEditor>
    <image>
      <title>TheWing</title>
      <url>https://tistory1.daumcdn.net/tistory/4261158/attach/4d3f2a1e4f5e4d7487a272e7a6c017b0</url>
      <link>https://sujl95.tistory.com</link>
    </image>
    <item>
      <title>Spring Boot의 SSE(Server-Sent Events) Graceful Shutdown 동작 원리</title>
      <link>https://sujl95.tistory.com/89</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가기 전에 포스팅 계기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sujl95.tistory.com/88&quot;&gt;앞전에 Graceful Shutdown의 동작원리&lt;/a&gt;에 이어서, &lt;b&gt;SSE Graceful shutdown&lt;/b&gt; 이 어떻게 동작하는지 동작 원리를 명확히 알고자 포스팅하게 됐습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 일반적인 Graceful Shutdown vs SSE Graceful Shutdown&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 HTTP 요청과 SSE 연결의 가장 큰 차이점은 &quot;연결 지속 시간&quot;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반 HTTP 요청&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청-응답 후 즉시 연결 종료&lt;/li&gt;
&lt;li&gt;대부분 짧은 시간 내 처리 완료&lt;/li&gt;
&lt;li&gt;Graceful Shutdown 시 진행 중인 요청만 완료하면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSE 연결&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트와 서버 간 장기 연결 유지&lt;/li&gt;
&lt;li&gt;서버에서 클라이언트로 지속적인 이벤트 스트리밍&lt;/li&gt;
&lt;li&gt;명시적인 연결 종료 처리 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. SSE Graceful Shutdown 동작 과정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 Shutdown 시그널 수신 (Kill -15)&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;// SpringApplicationShutdownHook.run()이 실행됨
@Override
public void run() {
    Set&amp;lt;ConfigurableApplicationContext&amp;gt; contexts;
    synchronized (SpringApplicationShutdownHook.class) {
        contexts = new LinkedHashSet&amp;lt;&amp;gt;(this.contexts);
    }
    contexts.forEach(this::closeAndWait);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 새로운 요청 거부&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버는 새로운 SSE 연결 요청을 거부&lt;/li&gt;
&lt;li&gt;기존 SSE 연결은 grace period 동안 유지 (Default = 30초)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 DefaultLifecycleProcessor에서의 처리&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-05 오후 6.28.16.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;1288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWmaeT/btsLDO0XxDZ/8hjALK0mFxVkBuRLBw2r9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWmaeT/btsLDO0XxDZ/8hjALK0mFxVkBuRLBw2r9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWmaeT/btsLDO0XxDZ/8hjALK0mFxVkBuRLBw2r9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWmaeT%2FbtsLDO0XxDZ%2F8hjALK0mFxVkBuRLBw2r9k%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;1844&quot; height=&quot;1288&quot; data-filename=&quot;스크린샷 2025-01-05 오후 6.28.16.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;1288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 SSE 연결 종료 처리 (@PreDestroy 설정 시)&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;@Component
public class SseEmitterManager {
    private final Set&amp;lt;SseEmitter&amp;gt; emitters = ConcurrentHashMap.newKeySet();

    @PreDestroy
    public void shutdown() {
        for (SseEmitter emitter : emitters) {
            try {
								// 종료 이벤트 전송
                emitter.send(SseEmitter.event()
                    .name(&quot;shutdown&quot;)
                    .data(&quot;Server is shutting down&quot;));
								// 연결 종료
                emitter.complete();
            } catch (IOException e) {
                emitter.completeWithError(e);
            } finally {
                emitters.remove(emitter);
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Graceful Shutdown 실패 시나리오&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE 연결이 정상적으로 종료되지 않을 경우 다음과 같은 로그가 순차적으로 발생합니다:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-05 오후 6.18.38.png&quot; data-origin-width=&quot;2548&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qPqWu/btsLEspzSt0/tbH5XzzN8RWTDQ5WUIuy2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qPqWu/btsLEspzSt0/tbH5XzzN8RWTDQ5WUIuy2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qPqWu/btsLEspzSt0/tbH5XzzN8RWTDQ5WUIuy2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqPqWu%2FbtsLEspzSt0%2FtbH5XzzN8RWTDQ5WUIuy2K%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;2548&quot; height=&quot;158&quot; data-filename=&quot;스크린샷 2025-01-05 오후 6.18.38.png&quot; data-origin-width=&quot;2548&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 웹 서버 종료 실패 알림
Shutdown phase 2147482623 ends with 1 bean still running after timeout of 30000ms: [webServerGracefulShutdown]

2. 비동기 요청 타임아웃
DefaultHandlerExceptionResolver : AsyncRequestTimeoutException

3. Graceful Shutdown 중단
Graceful shutdown aborted with one or more requests still active
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실패 시나리오 상세 분석&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1. 실패 과정의 상세 흐름&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1.1. Phase 종료 타임아웃 (2147482623)&lt;/h3&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;Shutdown phase 2147482623 ends with 1 bean still running after timeout of 30000ms: [webServerGracefulShutdown]
&lt;/code&gt;&lt;/pre&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;: DefaultLifecycleProcessor에서 webServerGracefulShutdown 빈이 30초 내에 종료되지 않음&lt;/li&gt;
&lt;li&gt;phase에서 2147482623 값의 의미
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 phase는 Tomcat의 GracefulShutdown이 실행되는 phase&lt;/li&gt;
&lt;li&gt;webServerGracefulShutdown의 phase 값(2147482623)은 Integer.MAX_VALUE(2147483647)보다 약간 작은 값으로 설정되어 있습니다. 이는 가장 마지막에 종료되어야 하는 컴포넌트들의 phase 값입니다.&lt;/li&gt;
&lt;li&gt;다른 빈들보다 늦게 종료되어야 하므로 높은 값 사용&lt;/li&gt;
&lt;li&gt;실제 Tomcat이 완전히 종료되기 전에 타임아웃이 발생하면 이 로그가 출력됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상세 동작&lt;/b&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-05 오후 6.33.23.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;1244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vJhKq/btsLEc1mnFA/F916T1JSkQyKzvqNgOmzB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vJhKq/btsLEc1mnFA/F916T1JSkQyKzvqNgOmzB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vJhKq/btsLEc1mnFA/F916T1JSkQyKzvqNgOmzB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvJhKq%2FbtsLEc1mnFA%2FF916T1JSkQyKzvqNgOmzB1%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;1828&quot; height=&quot;1244&quot; data-filename=&quot;스크린샷 2025-01-05 오후 6.33.23.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;1244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1.2. 비동기 요청 타임아웃&lt;/h3&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;DefaultHandlerExceptionResolver : AsyncRequestTimeoutException
&lt;/code&gt;&lt;/pre&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;: SSE 연결이 비동기 요청으로 처리되는 중 타임아웃 발생&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상세 동작&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;@GetMapping(&quot;/sse&quot;) public SseEmitter subscribe() { SseEmitter emitter = new SseEmitter(timeout);// timeout 이후 AsyncRequestTimeoutException 발생 emitter.onTimeout(() -&amp;gt; emitter.complete()); return emitter; }&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1.3. Graceful Shutdown 강제 중단&lt;/h3&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;Graceful shutdown aborted with one or more requests still active
&lt;/code&gt;&lt;/pre&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;: 활성 상태의 SSE 연결이 남아있는 상태에서 강제 종료&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상세 동작&lt;/b&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-05 오후 6.30.18.png&quot; data-origin-width=&quot;1786&quot; data-origin-height=&quot;1108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JkHHL/btsLDG2V5dz/FeKTLnCLU4z3da1RRxFJX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JkHHL/btsLDG2V5dz/FeKTLnCLU4z3da1RRxFJX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JkHHL/btsLDG2V5dz/FeKTLnCLU4z3da1RRxFJX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJkHHL%2FbtsLDG2V5dz%2FFeKTLnCLU4z3da1RRxFJX0%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;1786&quot; height=&quot;1108&quot; data-filename=&quot;스크린샷 2025-01-05 오후 6.30.18.png&quot; data-origin-width=&quot;1786&quot; data-origin-height=&quot;1108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 안전한 SSE Graceful Shutdown을 위한 Best Practices&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 적절한 타임아웃 설정&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 60s# 기본값 30초

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 클라이언트 재연결 로직 구현&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;function connectSSE() {
    const eventSource = new EventSource('/api/stream');

    eventSource.addEventListener('shutdown', (event) =&amp;gt; {
        console.log('Server is shutting down:', event.data);
        eventSource.close();
    });

    eventSource.onerror = (error) =&amp;gt; {
        eventSource.close();
        setTimeout(connectSSE, 5000);
    };
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.3 SSE 연결 관리 컴포넌트&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class WebServerConfig {
    @Bean
    public GracefulShutdown gracefulShutdown() {
        return new GracefulShutdown() {
            @Override
            protected void onShutdown() {
                logger.info(&quot;SSE connections cleanup started&quot;);
// SSE 연결 정리 로직
            }
        };
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSE Graceful Shutdown은 일반적인 HTTP 요청의 Graceful Shutdown보다 더 복잡한 처리가 필요합니다. 장기 연결의 특성상 클라이언트에게 적절한 종료 신호를 보내고, 연결을 정상적으로 종료하는 과정이 중요합니다. 이를 위해 적절한 타임아웃 설정, 연결 관리, 클라이언트 측 재연결 로직 구현 등이 필요합니다.&lt;/p&gt;</description>
      <category>Spring/Spring Boot</category>
      <category>graceful shutdown</category>
      <category>Spring Boot</category>
      <category>SSE</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/89</guid>
      <comments>https://sujl95.tistory.com/89#entry89comment</comments>
      <pubDate>Sun, 5 Jan 2025 18:21:13 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot의 Graceful Shutdown 동작 원리와 구현 과정</title>
      <link>https://sujl95.tistory.com/88</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가기 전에 포스팅 계기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 글 작성 해보려고 합니다. 최근에 사내에 &lt;b&gt;SSE(Server-Sent Events)&lt;/b&gt;를 기반으로 개발한 기능이 있어서 &lt;b&gt;Graceful shutdown&lt;/b&gt; 이 어떻게 동작하는지 추상적으로만 알고 있었고 동작 원리를 명확히 알고자 포스팅하게 됐습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;본론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 애플리케이션이 종료될 때, &lt;b&gt;Graceful Shutdown&lt;/b&gt;은 중요한 역할을 합니다. 이는 처리 중인 요청을 안전하게 종료하고, 리소스를 정리하며, 종료 상태를 클라이언트에 알리는 메커니즘입니다. 본 글에서는 Spring Boot의 Graceful Shutdown이 어떻게 등록되고 실행되는지, 그리고 kill -15 신호를 받을 때 어떤 단계로 동작하는지 상세히 살펴보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Graceful Shutdown이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Graceful Shutdown&lt;/b&gt;은 애플리케이션 종료 시 요청 처리와 리소스 정리를 안전하게 수행하도록 설계된 메커니즘입니다. Spring Boot는 이를 통해 다음을 보장합니다:&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;종료 중에도 진행 중인 HTTP 요청을 끝까지 처리한 뒤 연결을 종료합니다.&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB 커넥션, 메시지 큐 컨슈머, 스레드 풀 등 시스템 리소스를 안전하게 정리합니다.&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebSocket, SSE(Server-Sent Events), HTTP Keep-Alive와 같은 장기 연결을 클라이언트에게 적절히 알리고 안전하게 종료합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&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;장기 연결은 클라이언트와 서버가 일정 시간 동안 지속적으로 데이터를 주고받는 네트워크 연결을 의미합니다. Graceful Shutdown에서 이를 안전하게 닫는 것이 중요한 이유는 클라이언트의 재연결 또는 정상적인 서비스 종료를 보장하기 위해서입니다. 주요 예는 다음과 같습니다:&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;WebSocket&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버와 클라이언트 간 양방향 실시간 통신을 지원.&lt;/li&gt;
&lt;li&gt;종료 시 연결을 명시적으로 닫아 클라이언트가 재연결을 시도할 수 있도록 준비해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSE(Server-Sent Events)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 클라이언트로 데이터를 스트리밍하는 단방향 연결.&lt;/li&gt;
&lt;li&gt;서버 종료 시 클라이언트가 이를 감지하고 적절히 처리할 수 있도록 해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HTTP Keep-Alive&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트와 서버 간 다수의 요청/응답을 단일 연결에서 처리.&lt;/li&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;Spring Boot의 Graceful Shutdown은 이러한 장기 연결을 안전하게 닫고, 클라이언트가 서비스 중단을 최소화할 수 있도록 지원합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Graceful Shutdown 등록 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot는 &lt;b&gt;SpringApplication.run()&lt;/b&gt; 실행 시 Graceful Shutdown을 등록합니다. 이 과정은 JVM의 Shutdown Hook을 통해 구현되며, 종료 시 실행할 핸들러와 리스너가 설정됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 prepareEnvironment 호출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 실행 초기, &lt;b&gt;prepareEnvironment&lt;/b&gt; 메서드에서 Graceful Shutdown 관련 핸들러가 등록됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;내부 동작&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;SpringApplicationRunListeners.environmentPrepared() 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;environmentPrepared 이벤트를 통해 각종 리스너가 초기화됩니다.&lt;/li&gt;
&lt;li&gt;Graceful Shutdown 핸들러가 등록됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LoggingApplicationListener 초기화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;LoggingApplicationListener는 로그 시스템 초기화와 함께 종료 시 로그 정리를 위한 Shutdown Hook을 추가합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
    registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Shutdown Handlers 관리&lt;/b&gt; Graceful Shutdown 중 실행할 추가 작업(Runnable)을 관리하기 위해 SpringApplicationShutdownHandlers가 초기화됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;private class Handlers implements SpringApplicationShutdownHandlers {
    private final Set&amp;lt;Runnable&amp;gt; actions = Collections.newSetFromMap(new IdentityHashMap&amp;lt;&amp;gt;());

    @Override
    public void add(Runnable action) {
        synchronized (SpringApplicationShutdownHook.class) {
            this.actions.add(action);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 컨텍스트 초기화 및 Shutdown Hook 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot는 애플리케이션의 컨텍스트를 초기화하며, JVM 종료 신호를 처리할 &lt;b&gt;Shutdown Hook&lt;/b&gt;을 등록합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;private void refreshContext(ConfigurableApplicationContext context) {
    if (this.registerShutdownHook) {
        shutdownHook.registerApplicationContext(context);
    }
    refresh(context);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;내부 동작&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;registerApplicationContext 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 컨텍스트가 등록되며, ContextClosedEvent를 처리할 리스너가 추가됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void registerApplicationContext(ConfigurableApplicationContext context) {
    addRuntimeShutdownHookIfNecessary();
    synchronized (SpringApplicationShutdownHook.class) {
        context.addApplicationListener(this.contextCloseListener);
        this.contexts.add(context);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Shutdown Hook 등록&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM의 Shutdown Hook에 SpringApplicationShutdownHook이 추가됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void addRuntimeShutdownHook() {
    Runtime.getRuntime().addShutdownHook(new Thread(this, &quot;SpringApplicationShutdownHook&quot;));
}

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Shutdown 발생 후 동작 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kill -15(SIGTERM) 신호를 받으면 JVM의 Shutdown Hook이 실행되며, Graceful Shutdown이 시작됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 JVM의 Shutdown Hook 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제에서 SIGTERM 신호가 전달되면, Spring Boot에서 등록한 SpringApplicationShutdownHook이 실행됩니다. 이는 JVM의 &lt;b&gt;Shutdown Hook&lt;/b&gt; 메커니즘을 통해 트리거됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 SpringApplicationShutdownHook.run 호출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpringApplicationShutdownHook.run()은 Spring Boot 애플리케이션 종료 작업의 시작점입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 코드&lt;/h3&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;@Override
public void run() {
    Set&amp;lt;ConfigurableApplicationContext&amp;gt; contexts;
    Set&amp;lt;ConfigurableApplicationContext&amp;gt; closedContexts;
    Set&amp;lt;Runnable&amp;gt; actions;
    synchronized (SpringApplicationShutdownHook.class) {
        this.inProgress = true;
        contexts = new LinkedHashSet&amp;lt;&amp;gt;(this.contexts);
        closedContexts = new LinkedHashSet&amp;lt;&amp;gt;(this.closedContexts);
        actions = new LinkedHashSet&amp;lt;&amp;gt;(this.handlers.getActions());
    }
    contexts.forEach(this::closeAndWait); // 컨텍스트 종료
    closedContexts.forEach(this::closeAndWait); 
    actions.forEach(Runnable::run);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 애플리케이션 컨텍스트 종료&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 컨텍스트는 &lt;b&gt;closeAndWait&lt;/b&gt; 메서드를 통해 종료됩니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;private void closeAndWait(ConfigurableApplicationContext context) {
    if (!context.isActive()) {
        return; // 이미 비활성화된 컨텍스트는 스킵
    }
    context.close(); // 컨텍스트 닫기
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트 종료의 주요 로직은 &lt;b&gt;AbstractApplicationContext.close()&lt;/b&gt; 메서드에서 처리되며, 종료 이벤트(ContextClosedEvent)가 발행되고, 빈 소멸 및 리소스 정리가 이루어집니다.&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, Kafka, MQ 등 Thread Pool 등 정리됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-24 오후 11.33.14.png&quot; data-origin-width=&quot;2648&quot; data-origin-height=&quot;1196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxNzEX/btsKVClB8ng/mgUGwv06NRPvjGUTSbYKQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxNzEX/btsKVClB8ng/mgUGwv06NRPvjGUTSbYKQk/img.png&quot; data-alt=&quot;shutdown 이후 리소스 close&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxNzEX/btsKVClB8ng/mgUGwv06NRPvjGUTSbYKQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxNzEX%2FbtsKVClB8ng%2FmgUGwv06NRPvjGUTSbYKQk%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;2648&quot; height=&quot;1196&quot; data-filename=&quot;스크린샷 2024-11-24 오후 11.33.14.png&quot; data-origin-width=&quot;2648&quot; data-origin-height=&quot;1196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;shutdown 이후 리소스 close&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.4 Tomcat Graceful Shutdown&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tomcat과 같은 내장 웹 서버는 요청 처리를 완료한 후 종료됩니다. 새 요청을 차단하고, 진행 중인 요청이 모두 완료될 때까지 대기 후 연결을 닫습니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;void shutDownGracefully(GracefulShutdownCallback callback) {
    logger.info(&quot;Commencing graceful shutdown...&quot;);
    new Thread(() -&amp;gt; doShutdown(callback), &quot;tomcat-shutdown&quot;).start();
}

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 사용자 정의 Graceful Shutdown 작업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot는 종료 시 커스텀 로직을 추가할 수 있는 메커니즘을 제공합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 @EventListener를 통한 종료 작업&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@Component
public class CustomShutdownListener {
    @EventListener(ContextClosedEvent.class)
    public void onShutdown() {
        System.out.println(&quot;Graceful shutdown logic executed...&quot;);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 DisposableBean을 통한 리소스 정리&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Component
public class ResourceCleanup implements DisposableBean {
    @Override
    public void destroy() {
        System.out.println(&quot;Releasing resources...&quot;);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot에서 Graceful Shutdown은 &lt;b&gt;애플리케이션 종료 시 요청 처리 완료와 리소스 정리를 보장&lt;/b&gt;하며, 이를 통해 안정적이고 신뢰할 수 있는 서비스를 제공합니다. 다음은 전체적인 과정과 각 단계에서 수행되는 작업입니다:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.1 등록 과정&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 초기화 중 Graceful Shutdown 관련 핸들러와 리스너가 등록됩니다.&lt;/li&gt;
&lt;li&gt;JVM Shutdown Hook을 통해 종료 작업이 실행될 준비를 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.2 Shutdown 발생 후&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Shutdown Hook 실행&lt;/b&gt; JVM의 Shutdown Hook을 통해 **SpringApplicationShutdownHook.run()*이 호출됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컨텍스트 종료&lt;/b&gt; 종료 이벤트(ContextClosedEvent)가 발행되며, 빈 소멸 및 리소스 정리가 이루어집니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tomcat Graceful Shutdown&lt;/b&gt; 현재 요청을 모두 처리한 뒤, 서버 연결이 안전히 종료됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.3 사용자 정의 작업&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@EventListener 또는 DisposableBean을 통해 종료 시 추가 작업을 설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.4 주요 이점&lt;/b&gt;&lt;/h3&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;: DB 커넥션, 스레드 풀 등 시스템 자원이 안전하게 정리됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서비스 안정성 강화&lt;/b&gt;: 장기 연결을 안전하게 닫아 클라이언트와 서버 간 신뢰를 유지합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring/Spring Boot</category>
      <category>spring graceful shutdown 동작원리</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/88</guid>
      <comments>https://sujl95.tistory.com/88#entry88comment</comments>
      <pubDate>Sun, 24 Nov 2024 23:34:03 +0900</pubDate>
    </item>
    <item>
      <title>Kotlin Default Argument 의 동작 원리</title>
      <link>https://sujl95.tistory.com/87</link>
      <description>&lt;h1&gt;Kotlin Default Argument의 동작 원리&lt;/h1&gt;
&lt;h2&gt;Default Arguments&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;함수를 호출할 때 명시적으로 인자(Argument) 지정할 필요가 없는 것을 Default Argument라고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;인자를 전달하지 않고 함수를 호출하면 Default Argument가 Function Parameter로 사용된다. 다른 경우에는 함수 호출 중에 argument가 전달되면 전달된 argument가 Function Parameter로 사용된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Function의 매개 변수는 해당 인자를 건너뛸 때 사용되는 기본 값을 가질 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이것은 오버로딩의 수를 줄여준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ex)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;fun method(arg1: Int) { 
    /*...*/ 
} 
fun method(arg1: Int, arg2: Int) {
    /*...*/ 
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Default Parameter는 &lt;code&gt;parameter name: type = value&lt;/code&gt; 로 아래와 같이 사용이 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;fun method(arg1: Int, arg2: Int = 0) {
   // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Default Argument가 사용되는 3가지 경우&lt;/h2&gt;
&lt;p&gt;Default Argument가 지정된 함수를 호출하는 동안&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;인자가 전달되지 않는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;함수를 호출하는 동안 인자가 전달되지 않으면 Default Argument가 함수 매개변수로 사용된다. 함수를 정의하는 동안 변수를 초기화해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;fun main() { 
    method() 
}
fun method(arg1: Int = 1, arg2: Int = 2) {
    /* ... */ 
    println(&amp;quot;arg1 = ${arg1}&amp;quot;)
    println(&amp;quot;arg2 = ${arg2}&amp;quot;)
    println(&amp;quot;인자가 전달되지 않는 경우&amp;quot;)
} 
// 출력 결과 
arg1 = 1 
arg2 = 2 
인자가 전달되지 않는 경우&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;부분 인자가 전달되는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;여기서 부분 인자는 함수를 호출하는 동안 전달되고 이들은 함수 매개 변수로 사용된다. 함수 호출에서 값을 가져오지 않으면 해당 매개변수에 대해 Default Value가 사용된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;fun main() {
val arg1 = 3
method(arg1)
}

fun method(arg1: Int = 1, arg2: Int = 2) {
    /* ... */
    println(&amp;quot;arg1 = ${arg1}&amp;quot;)
    println(&amp;quot;arg2 = ${arg2}&amp;quot;)
    println(&amp;quot;부분 인자가 전달 되는 경우&amp;quot;)
}

// 출력 결과
arg1 = 3
arg2 = 2
부분 인자가 전달 되는 경우&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;모든 인자가 전달되는 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;여기서 함수 정의에 정의된 대로 모든 인자를 전달해야 하지만 실제 인자의 데이터 유형은 선언된 인자의 데이터 유형과 순서가 동일해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code&gt;fun main() {
    val arg1 = 3
    val arg2 = 4
    method(arg1, arg2)
}

fun method(arg1: Int = 1, arg2: Int = 2) {
    /* ... */
    println(&amp;quot;arg1 = ${arg1}&amp;quot;)
    println(&amp;quot;arg2 = ${arg2}&amp;quot;)
    println(&amp;quot;모든 인자가 전달 되는 경우&amp;quot;)
}

// 출력 결과
arg1 = 3
arg2 = 4
모든 인자가 전달 되는 경우&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Kotlin Default Argument 동작 원리&lt;/h2&gt;
&lt;p&gt;위에는 사용법을 설명한 것이고 동작 원리를 알아보도록 하자.&lt;/p&gt;
&lt;h3&gt;Example Code&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;fun main() {
    method(
        param3 = &amp;quot;5&amp;quot;,
        param4 = &amp;quot;6&amp;quot;
    )
}

fun method(
    param1: String? = &amp;quot;1&amp;quot;,
    param2: String? = &amp;quot;2&amp;quot;,
    param3: String? = &amp;quot;3&amp;quot;,
    param4: String? = &amp;quot;4&amp;quot;,
    param5: String = &amp;quot;5&amp;quot;,
) {

}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;ByteCode&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XEmyN/btrUXprHHgF/W1Ymw8cKaHi1LdkKqn9010/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XEmyN/btrUXprHHgF/W1Ymw8cKaHi1LdkKqn9010/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XEmyN/btrUXprHHgF/W1Ymw8cKaHi1LdkKqn9010/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXEmyN%2FbtrUXprHHgF%2FW1Ymw8cKaHi1LdkKqn9010%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;1280&quot; height=&quot;574&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INVOKESTATIC example/DefaultArgumentMethodTestKt.method$default&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;int 값 19를 넣어주고 method명:default로 생성된 static method를 호출하는 부분이 있다.&lt;/li&gt;
&lt;li&gt;19는 왜 넣어주는 것인가? decompile 한 코드를 확인해 보자&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Decompile Code&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vT3Rq/btrU00LDzht/Y5HaAkjRnmakKsIoQ5nytk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vT3Rq/btrU00LDzht/Y5HaAkjRnmakKsIoQ5nytk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vT3Rq/btrU00LDzht/Y5HaAkjRnmakKsIoQ5nytk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvT3Rq%2FbtrU00LDzht%2FY5HaAkjRnmakKsIoQ5nytk%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;1280&quot; height=&quot;804&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;main 메서드 보면 default method를 호출할 때 우리가 선언한 5개 인자뿐 아니라 2개의 인자를 더 넣어주고 있다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;method$default_((String)_null_, (String)_null_, &amp;quot;5&amp;quot;, &amp;quot;6&amp;quot;, (String)_null_, 19, (Object)_null_);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;이렇게 넣어주고 있는데 19? 이게 무엇인지 궁금해할 것이다.&lt;/p&gt;
&lt;p&gt;method$default를 한번 살펴보자 이게 Default Argument 동작 원리의 핵심이다.&lt;/p&gt;
&lt;p&gt;6번째 인자인 &lt;code&gt;int var5&lt;/code&gt; 를 가지고 비트 연산을 하여 default value를 대입해주는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;왜 1, 2, 4, 8, 16인가?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;첫 번째 인자는 1&lt;/li&gt;
&lt;li&gt;두 번째 인자는 2&lt;/li&gt;
&lt;li&gt;세 번째 인자는 4&lt;/li&gt;
&lt;li&gt;네 번째 인자는 8&lt;/li&gt;
&lt;li&gt;다섯 번째 인자는 16&lt;/li&gt;
&lt;li&gt;여섯 번째 인자는 32&lt;/li&gt;
&lt;li&gt;일곱 번째 인자는 64&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 인자가 하나씩 증가할 때마다 2배씩 늘어난다.&lt;/p&gt;
&lt;p&gt;비트 연산으로 default 인자가 들어왔는지 체크하는 로직인데 십진수 &lt;code&gt;19&lt;/code&gt;를 2진수로 변환하면 &lt;code&gt;10011&lt;/code&gt; 가 나온다.&lt;/p&gt;
&lt;p&gt;이걸 비트 연산을 수행하면 3번째 4번째 조건문을 타지 않기 때문에 호출 시 직접 넣어준 value가 대입된다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;var2 = “3”, var3 = “4”&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;최종적으로 대입된 value들을 가지고 우리가 생성한 함수( &lt;code&gt;method(var0, var1, var2, var3, var4);&lt;/code&gt; )를 호출한다.&lt;/p&gt;
&lt;p&gt;+호출 시 null인 인자들을 체크한다.&lt;/p&gt;
&lt;h2&gt;궁금증 method명$default 메서드를 만들면 어떻게 될까?&lt;/h2&gt;
&lt;h3&gt;Example Code&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;fun main() {
    DefaultArgumentMethodTest().method(
        param3 = &amp;quot;5&amp;quot;,
        param4 = &amp;quot;6&amp;quot;
    )
}

class DefaultArgumentMethodTest {
    fun method(
        param1: String? = &amp;quot;1&amp;quot;,
        param2: String? = &amp;quot;2&amp;quot;,
        param3: String? = &amp;quot;3&amp;quot;,
        param4: String? = &amp;quot;4&amp;quot;,
        param5: String = &amp;quot;5&amp;quot;,
    ) {

    }

    fun `method$default`(
        var0: DefaultArgumentMethodTest,
        var1: String?,
        var2: String?,
        var3: String?,
        var4: String?,
        var5: String?,
        var6: Int,
        var7: Any?
    ) {
        var var1 = var1
        var var2 = var2
        var var3 = var3
        var var4 = var4
        var var5 = var5
        if (var6 and 1 != 0) {
            var1 = &amp;quot;1&amp;quot;
        }
        if (var6 and 2 != 0) {
            var2 = &amp;quot;2&amp;quot;
        }
        if (var6 and 4 != 0) {
            var3 = &amp;quot;3&amp;quot;
        }
        if (var6 and 8 != 0) {
            var4 = &amp;quot;4&amp;quot;
        }
        if (var6 and 16 != 0) {
            var5 = &amp;quot;5&amp;quot;
        }
        var0.method(var1, var2, var3, var4, var5!!)
    }

}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;이렇게 실행 시 에러가 터진다. 이렇게 메서드명을 지을 리가 없지만 하지 않도록 하자!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CEiQX/btrU1pLgpz6/oAmxLOWQr3RJn5417eWMY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CEiQX/btrU1pLgpz6/oAmxLOWQr3RJn5417eWMY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CEiQX/btrU1pLgpz6/oAmxLOWQr3RJn5417eWMY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCEiQX%2FbtrU1pLgpz6%2FoAmxLOWQr3RJn5417eWMY0%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;1280&quot; height=&quot;398&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kotlinlang.org/docs/functions.html#default-arguments&quot;&gt;https://kotlinlang.org/docs/functions.html#default-arguments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Kotlin/개념</category>
      <category>Kotlin Default Argument</category>
      <category>Kotlin Default Parameter</category>
      <category>Kotlin Named Argument</category>
      <category>코틀린</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/87</guid>
      <comments>https://sujl95.tistory.com/87#entry87comment</comments>
      <pubDate>Sun, 1 Jan 2023 01:47:13 +0900</pubDate>
    </item>
    <item>
      <title>Java 8 ZonedDateTime vs OffsetDateTime 어떤 상황에서 쓰는게 적합한가?</title>
      <link>https://sujl95.tistory.com/86</link>
      <description>&lt;h1&gt;Java 8 ZonedDateTime vs OffsetDateTime &lt;b&gt;어떤 상황에서 쓰는게 적합한가?&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가기 전에&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본 글은 회사 팀 내 세션 공유용 자료이며 &lt;a href=&quot;https://sujl95.tistory.com/85&quot;&gt;이전 글&lt;/a&gt; 내용과 이어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;OffsetDateTime&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P3j6J/btrJ70IlZ9l/4ODjrHVbXHvuRehyloF3r1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P3j6J/btrJ70IlZ9l/4ODjrHVbXHvuRehyloF3r1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P3j6J/btrJ70IlZ9l/4ODjrHVbXHvuRehyloF3r1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP3j6J%2FbtrJ70IlZ9l%2F4ODjrHVbXHvuRehyloF3r1%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;1280&quot; height=&quot;370&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&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;code&gt;OffsetDateTime&lt;/code&gt;은 &lt;b&gt;(LocalDateTime(날짜 + 시간) + ZoneOffset)&lt;/b&gt; 을 포함한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Instant&lt;/code&gt;와 같이 나노초 정밀도로 타임라인에 순간을 저장한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UTC/그리니치&lt;/code&gt;에서 오프셋을 추가하면 현지 날짜-시간을 얻을 수 있다.&lt;/li&gt;
&lt;li&gt;데이터베이스에 &lt;code&gt;Timestamp&lt;/code&gt;를 저장하거나 네트워크를 통해 XML 문서에 &lt;code&gt;Timestamp&lt;/code&gt; 를 통신하는 데 사용하는 것이 유리하다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ZoneOffset&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UTC 기준 시간을 표현한 것&lt;/li&gt;
&lt;li&gt;우리나라 &lt;code&gt;Timezone(Asia/Seoul)&lt;/code&gt; 기준 &lt;code&gt;UTC +09:00&lt;/code&gt;로 표기&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ZoneOffset&lt;/code&gt;은 &lt;code&gt;ZoneId&lt;/code&gt;를 상속 받았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ZonedDateTime&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boLkgY/btrJ6RFWT77/EYdDLcx3MOJPH9S6bqDUck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boLkgY/btrJ6RFWT77/EYdDLcx3MOJPH9S6bqDUck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boLkgY/btrJ6RFWT77/EYdDLcx3MOJPH9S6bqDUck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboLkgY%2FbtrJ6RFWT77%2FEYdDLcx3MOJPH9S6bqDUck%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;1280&quot; height=&quot;370&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&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;code&gt;ZonedDateTime&lt;/code&gt;은 (&lt;b&gt;OffsetDateTime + ZoneRegion)&lt;/b&gt;를 포함한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;ZoneRegion&lt;/code&gt; 은 &lt;code&gt;Timezone&lt;/code&gt;을 나타낸 것이라고 보면된다. (ex. &lt;code&gt;Asia/Seoul&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ZoneRegion&lt;/code&gt;은 &lt;code&gt;ZoneId&lt;/code&gt;를 상속 받았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2022-08-19T22:36:41.559512100+09:00[Asia/Seoul]&lt;/code&gt; 와 같이 &lt;a href=&quot;https://ko.wikipedia.org/wiki/ISO_8601&quot;&gt;ISO-8601&lt;/a&gt; 달력 시스템 표준대를 따라서 표기한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Instant&lt;/code&gt;와 같이 나노초 정밀도로 타임라인에 순간을 저장한다.&lt;/li&gt;
&lt;li&gt;여기까지만 보면 &lt;code&gt;OffsetDateTime&lt;/code&gt;과 차이가 무엇이냐는 궁금증이 있을 수 있다.&lt;br /&gt;&lt;code&gt;Asia/Seoul&lt;/code&gt;을 쓰건 &lt;code&gt;Asia/Tokyo&lt;/code&gt; 를 쓰건 어차피 &lt;code&gt;UTC+09&lt;/code&gt;인데 뭔 상관이냐?&lt;br /&gt;&lt;code&gt;OffsetDateTime&lt;/code&gt; 과 달리 &lt;code&gt;ZonedDateTime&lt;/code&gt;은 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%BC%EA%B4%91_%EC%A0%88%EC%95%BD_%EC%8B%9C%EA%B0%84%EC%A0%9C&quot;&gt;DST(Daylight Saving Time)&lt;/a&gt; 와 같은 써머 타임의 정보(&lt;code&gt;ZoneRules&lt;/code&gt;로 판단)가 들어가 있다. &lt;b&gt;이 이유가 가장 크다&lt;/b&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DST를 사용하는 지역&lt;/li&gt;
&lt;li&gt;DST를 한번도 사용 안한 지역&lt;/li&gt;
&lt;li&gt;과거에는 시행했지만 사용하지 않는 지역&lt;br /&gt;이렇게 지역이 3가지 분류로 나뉠 때 혼란스러워진다. 또한 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%A4%91%EC%95%99%EC%9C%A0%EB%9F%BD_%ED%91%9C%EC%A4%80%EC%8B%9C&quot;&gt;CET(중앙유럽 표준시)&lt;/a&gt; 와 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%A4%91%EC%95%99%EC%9C%A0%EB%9F%BD_%EC%9D%BC%EA%B4%91_%EC%A0%88%EC%95%BD_%EC%8B%9C%EA%B0%84%EB%8C%80&quot;&gt;CEST(중앙유럽 일광 절약 시간대)&lt;/a&gt; 를 사용 유무에 따라 계산하기 매우 까다롭다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이러한 부분을 &lt;code&gt;ZoneRules&lt;/code&gt;로 DST를 완전히 인식하고 처리한다.&lt;/li&gt;
&lt;li&gt;java는 CET와 CEST를 CET로 통일한다&lt;br /&gt;코드로 알아보자 1월 &lt;code&gt;CET&lt;/code&gt;, 6월 &lt;code&gt;CET&lt;/code&gt; 를 비교해보자&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;    ZonedDateTime winter = ZonedDateTime.of(2022, 1, 1, 0, 0, 0, 0, ZoneId.of(&quot;CET&quot;));
    System.out.println(&quot;winter = &quot; + winter);
    ZonedDateTime summer = ZonedDateTime.of(2022, 6, 1, 0, 0, 0, 0, ZoneId.of(&quot;CET&quot;));
    System.out.println(&quot;summer = &quot; + summer);

    winter = 2022-01-01T00:00+01:00[CET]
    summer = 2022-06-01T00:00+02:00[CET]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;예상 했던 것과 다르게 같은 Offset이 찍히지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OffsetDateTime&lt;/code&gt;은 이런 디테일한 부분을 처리할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스나 네트워크를 통신하는데 사용하기에는 &lt;code&gt;OffsetDateTime&lt;/code&gt;을 사용하는 것이 적절하고 글로벌 서비스에서는 &lt;code&gt;ZonedDateTime&lt;/code&gt;을 사용하는 것이 더욱 바람직하다고 생각한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/30234594/whats-the-difference-between-zoneddatetime-and-offsetdatetime&quot;&gt;https://stackoverflow.com/questions/30234594/whats-the-difference-between-zoneddatetime-and-offsetdatetime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html&quot;&gt;https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/time/OffsetDateTime.html&quot;&gt;https://docs.oracle.com/javase/8/docs/api/java/time/OffsetDateTime.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/java-zoneddatetime-offsetdatetime&quot;&gt;https://www.baeldung.com/java-zoneddatetime-offsetdatetime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://howtodoinjava.com/java/date-time/zoneddatetime-vs-offsetdatetime/&quot;&gt;https://howtodoinjava.com/java/date-time/zoneddatetime-vs-offsetdatetime/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java/개념</category>
      <category>instant</category>
      <category>OffsetDateTime</category>
      <category>ZonedDateTime</category>
      <category>ZonedDateTime vs OffsetDateTime</category>
      <category>ZonedDateTime과 OffsetDateTime 차이</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/86</guid>
      <comments>https://sujl95.tistory.com/86#entry86comment</comments>
      <pubDate>Fri, 19 Aug 2022 23:50:27 +0900</pubDate>
    </item>
    <item>
      <title>Java 8 LocalDateTime vs Instant 어떤 상황에서 쓰는게 적합한가?</title>
      <link>https://sujl95.tistory.com/85</link>
      <description>&lt;h1&gt;Java 8 LocalDateTime vs Instant 어떤 상황에서 쓰는게 적합한가?&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가기 전에&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본 글은 세션 공유용 자료이며 &lt;code&gt;LocalDateTime&lt;/code&gt;, &lt;code&gt;Instant&lt;/code&gt; 의 개념에 관한 짧은 글이 아니므로 양해 부탁드립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;포스팅 계기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최근 새 회사(이커머스 도메인)에 이직하게 되었고 코프링(코틀린 + 스프링)기반 프로젝트를 진행하고 있었다.&lt;/li&gt;
&lt;li&gt;프로젝트를 살펴보던 중 시간 관련 데이터들을 DB에 &lt;code&gt;LocalDateTime&lt;/code&gt; 로 넣고 있었다.&lt;br /&gt;&lt;code&gt;Instant&lt;/code&gt; 클래스가 아닌 &lt;code&gt;LocalDateTime&lt;/code&gt;로 넣는 이유가 궁금해서 팀 내에 공유할 겸 오랜만에 포스팅하게 되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그냥 LocalDateTime 쓰면 안되나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;독자들은 &lt;code&gt;LocalDateTime&lt;/code&gt; 이나 &lt;code&gt;Instant&lt;/code&gt; 아무거나 쓰면 안 되나? 이런 궁금증이 들 수 있다. &lt;code&gt;LocalDateTime&lt;/code&gt; 클래스는 개인 프로젝트를 진행할 때 시간 관련 &lt;code&gt;Type&lt;/code&gt;으로 사용했던 경험이 있을 것이다. 필자 또한 개인 프로젝트 하면서 해당 클래스를 사용했었다. 정확히 &lt;code&gt;Instant&lt;/code&gt; 클래스를 알기 전까지 사용했다. (전 회사에서는 &lt;code&gt;Instant&lt;/code&gt; 클래스를 사용했고 현 회사에서는 &lt;code&gt;LocalDateTime&lt;/code&gt; 을 사용하고 있었기에 의문이 들었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;LocalDateTime&lt;/code&gt;, &lt;code&gt;LocalDate&lt;/code&gt;, &lt;code&gt;LocalTime&lt;/code&gt; 간단한 사용법들은 &lt;a href=&quot;https://sujl95.tistory.com/4&quot;&gt;이전글&lt;/a&gt; 에서 포스팅 하였고 개념글 위주로 &lt;code&gt;LocalDateTime&lt;/code&gt;이 좋은지 &lt;code&gt;Instant&lt;/code&gt; 가 좋은지 더 깊게 공부해 보았고 포스팅하려고 한다.&lt;br /&gt;(혹시나 &lt;code&gt;Java 8&lt;/code&gt; 이상 사용 중이고 &lt;code&gt;Date&lt;/code&gt;나 &lt;code&gt;Calandar&lt;/code&gt;클래스를 사용하고 있다면 지양하는 것을 추천한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;a href=&quot;https://sujl95.tistory.com/2&quot;&gt;[Java] Date와 Calendar 클래스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sujl95.tistory.com/3&quot;&gt;[Java] Date, Calendar클래스가 왜 Deprecated됐는지?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sujl95.tistory.com/4&quot;&gt;[Java] Java 8에 추가된 LocalDate, LocalTime, LocalDateTime&lt;/a&gt; (간단하게 사용법들을 작성해놓았기에 개념 설명은 본 글에서 진행)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Instant&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tsSbe/btrJfX0o17W/rlqUBbkkEqqyQYqIEqt1Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tsSbe/btrJfX0o17W/rlqUBbkkEqqyQYqIEqt1Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tsSbe/btrJfX0o17W/rlqUBbkkEqqyQYqIEqt1Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtsSbe%2FbtrJfX0o17W%2FrlqUBbkkEqqyQYqIEqt1Gk%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;1548&quot; height=&quot;448&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진은 이해를 돕기 위한 이미지이다. (날짜 + 시간 + &lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%98%91%EC%A0%95_%EC%84%B8%EA%B3%84%EC%8B%9C&quot;&gt;UTC&lt;/a&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;code&gt;Instant&lt;/code&gt; 클래스는 단어의 의미와 같이 &lt;b&gt;순간, 즉시&lt;/b&gt; 를 의미한다.&lt;/li&gt;
&lt;li&gt;컴퓨팅을 하기 위해 &lt;code&gt;Timestamp&lt;/code&gt;로 기술적인 표현을 한 것이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 인간보다는 기계에 친화적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;long&lt;/code&gt; 형태로 &lt;code&gt;Unix Timestamp&lt;/code&gt;를 저장하기 때문에 연산이 빠르다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Unix Timestamp&lt;/code&gt;를 사용하면 되지 않나? 라는 의문을 가진 독자가 있을 것이다. &lt;code&gt;Unix Timestamp&lt;/code&gt;는 &lt;b&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/2038%EB%85%84_%EB%AC%B8%EC%A0%9C&quot;&gt;2038년 문제&lt;/a&gt;&lt;/b&gt;가 있다. 1970년 1월 1일 자정에서부터 &lt;a href=&quot;https://ko.wikipedia.org/wiki/2147483647&quot;&gt;2147483647&lt;/a&gt; 초가 지난 &lt;code&gt;2038년 1월 19일 화요일 03:14:07 UTC&lt;/code&gt; 까지 표현이 가능하다. 이것을 보완한 것이 &lt;code&gt;Instant&lt;/code&gt; 클래스이다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;  private static final long MIN_SECOND = -31557014167219200L; 
  private static final long MAX_SECOND = 31556889864403199L;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;지원할 수 있는 범위는 아래와 같다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;MIN_SECOND&lt;/code&gt; = &lt;code&gt;-10000000-01-01T00:00Z&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MAX_SECOND&lt;/code&gt; = &lt;code&gt;1000000000-12-31T23:59:59.99999999Z&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;현재 순간을 찍으려면 아래와 같이 객체를 생성하면 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Instant now = Instant.now();
println(now); 
// 2022-08-08T16:09:17.105081Z -- 2022년 08월 09일 01시 09분 17초&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;나노초까지 표현이 가능하다.&lt;/li&gt;
&lt;li&gt;뒤에 Z를 표현하는 것을 볼 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/ISO_8601&quot;&gt;ISO_8601&lt;/a&gt;은 날짜와 시간과 관련된 데이터 교환을 다루는 국제 표준이며 링크를 참고하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;앞에 &amp;ldquo;&lt;b&gt;인간보단 기계에 친화적이라고 했는데 읽기 편한데?&lt;/b&gt;&amp;rdquo;라고 생각할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Timestamp는 본래 long 타입이다. &lt;code&gt;toString()&lt;/code&gt; 을 재정의하여 &lt;code&gt;DateTimeFormatter&lt;/code&gt; 로 읽기 쉽게 해준 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;어떻게 UTC 기반으로 타임을 찍는가?&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;    public static Instant now() { 
        return Clock.currentInstant(); 
    } 
    public abstract class Clock { 
        private static final long OFFSET_SEED = System.currentTimeMillis() / 1000 - 1024; 
        private static long offset = OFFSET_SEED; 
        // 시스템의 현재 시간을 가져온다 나노초로 가져오는 것을 밀리초로 표현해준다. 
        static Instant currentInstant() { 
            long localOffset = offset; long adjustment = VM.getNanoTimeAdjustment(localOffset); 
            // 밀리초를 나노초로 변환해서 가져온다 
            ... 
            return Instant.ofEpochSecond(localOffset, adjustment); 
        } 
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LocalDateTime&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0MMT/btrI3xaJtvi/C3hFO3XN7e06WK2JqxpnH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0MMT/btrI3xaJtvi/C3hFO3XN7e06WK2JqxpnH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0MMT/btrI3xaJtvi/C3hFO3XN7e06WK2JqxpnH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq0MMT%2FbtrI3xaJtvi%2FC3hFO3XN7e06WK2JqxpnH0%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;1548&quot; height=&quot;448&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&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;code&gt;LocalDateTime&lt;/code&gt; 은 &lt;b&gt;날짜 + 시간&lt;/b&gt; 정보를 가지고 있다. &lt;code&gt;Timezone&lt;/code&gt;이 없는 것을 볼 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LocalDateTime&lt;/code&gt;은 인간에게 친화적인 타입이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Timezone&lt;/code&gt;이 없는데 &lt;code&gt;Instant&lt;/code&gt; 와 같이 &lt;code&gt;UTC&lt;/code&gt;로 넣는게 아닌가?라는 생각을 할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;LocalDateTime&lt;/code&gt;은 어떻게 시간을 나타내는지 보자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;  LocalDateTime now = LocalDateTime.now(); 
  println(now); 
  // 2022-08-09T01:09:17.113332 2022년 08월 09일 01시 09분 17초&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LocalDateTime&lt;/code&gt; 은 현재 로컬 시간에 맞춰서 시간을 표현하고 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부 코드를 살펴보자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;   public final class LocalDateTime { 
       public static LocalDateTime now() { 
           return now(Clock.systemDefaultZone()); 
       } 
   } 
   public static Clock systemDefaultZone() { 
       // 시스템의 ZoneId를 가져온다 
       return new SystemClock(ZoneId.systemDefault()); 
   } 
   public static LocalDateTime now(Clock clock) { 
       Objects.requireNonNull(clock, &quot;clock&quot;); 
       final Instant now = clock.instant(); 
       // ZoneId를 기반으로 Offset 정보를 가져온다. 
       ZoneOffset offset = clock.getZone().getRules().getOffset(now); 
       return ofEpochSecond(now.getEpochSecond(), now.getNano(), offset); 
   } 
   public static LocalDateTime ofEpochSecond(long epochSecond, int nanoOfSecond, ZoneOffset offset) { 
           Objects.requireNonNull(offset, &quot;offset&quot;); 
         NANO_OF_SECOND.checkValidValue(nanoOfSecond); 
         long localSecond = epochSecond + offset.getTotalSeconds(); 
         long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); 
         int secsOfDay = Math.floorMod(localSecond, SECONDS_PER_DAY); 
         LocalDate date = LocalDate.ofEpochDay(localEpochDay); 
         LocalTime time = LocalTime.ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + nanoOfSecond); 
         return new LocalDateTime(date, time); 
   }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;인스턴스의 &lt;code&gt;Timezone&lt;/code&gt; 기반으로 Offset을 생성한 후 현재 시점 기준으로 &lt;code&gt;LocalDate&lt;/code&gt;, &lt;code&gt;LocalTime&lt;/code&gt; 객체를 생성 후 &lt;code&gt;LocalDateTime&lt;/code&gt; 을 생성 해주는 것을 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LocalDateTime 을 사용 했을 때 문제점?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;LocalDateTime&lt;/code&gt; 코드를 살펴보았으니 한 가지 추론을 해보자 글로벌 서비스를 &lt;code&gt;Seoul(Asia/Seoul)&lt;/code&gt;, &lt;code&gt;LA(America/Los_Angeles)&lt;/code&gt; , &lt;code&gt;Tokyo(Asia/Tokyo)&lt;/code&gt; 에 운영하고 있다고 가정하면 각각 &lt;code&gt;Region&lt;/code&gt;마다 서버를 둘 것이고 타임존이 각각 다를 것이다. 각 인스턴스에서 &lt;code&gt;LocalDateTime.now()&lt;/code&gt;로 시간을 입력한다고 해보자 그럼 DB에는 어떻게 들어갈 것인가? 인스턴스의 &lt;code&gt;Timezone&lt;/code&gt;을 &lt;code&gt;UTC&lt;/code&gt;로 설정해 주지 않는 이상 각각 &lt;code&gt;Timezone&lt;/code&gt;에 맞춰 데이터가 들어갈 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떤 상황에 사용하는 것이 적합한가?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Instant 클래스를 사용하기 적합한 곳&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Timestamp를 UTC 형식으로 저장하는 곳 즉 DB, 벡엔드 비즈니스 로직, 데이터 교환, 직렬화 시나리오에 적합하다. (연산하기 쉽기 때문)&lt;/li&gt;
&lt;li&gt;글로벌 런칭한 서비스 비즈니스 앱 개발 시 &lt;code&gt;Instant&lt;/code&gt; 나 &lt;code&gt;ZonedDateTime&lt;/code&gt; 클래스 (&lt;code&gt;ZoneId&lt;/code&gt;+ &lt;code&gt;Instant&lt;/code&gt; )를 많이 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dthlSf/btrJe7hHo8V/QbmBNdiheo7X8BCELp8aCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dthlSf/btrJe7hHo8V/QbmBNdiheo7X8BCELp8aCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dthlSf/btrJe7hHo8V/QbmBNdiheo7X8BCELp8aCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdthlSf%2FbtrJe7hHo8V%2FQbmBNdiheo7X8BCELp8aCK%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;1280&quot; height=&quot;370&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LocalDateTime 을 사용하기 적합한 곳&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌 서비스가 아닌 단일 리전 서비스 일 때(타임존 X)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 특정 날짜와 시간을 여러 리전에서 적용하려는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;FrontEnd Service, 즉 Display(View) 에 좋다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개인적인 견해&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;글로벌 서비스일 경우 &lt;code&gt;LocalDateTime&lt;/code&gt;보다는 &lt;code&gt;Instant&lt;/code&gt;나 &lt;code&gt;ZonedDateTime&lt;/code&gt; 을 사용하는 것이 좋다고 생각한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Instant&lt;/code&gt; 클래스를 사용자 입장에서 &lt;code&gt;long&lt;/code&gt; 형태로 받기 때문에 읽기 힘들다고 할 수 있는데 &lt;code&gt;toString()&lt;/code&gt;으로 재정의가 되어있고 Converting 하거나 &lt;code&gt;Serialize&lt;/code&gt; 하여 보내주면 된다고 생각한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UTC&lt;/code&gt; 기반으로 DB에 넣어주고 &lt;code&gt;Timezone&lt;/code&gt; 기반으로 보여주면 될 것 같다고 생각이 든다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;User의 &lt;code&gt;Region&lt;/code&gt; , User가 속한 그룹의 &lt;code&gt;Region&lt;/code&gt; 들이 다르고 &lt;code&gt;Timezone&lt;/code&gt;을 다양하게 가져갈 수 있기 때문에 적절하게 사용하면 된다고 생각&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이직 후 첫 세션 공유 자리이므로 준비하는 시간이 생각보다 오래 걸렸지만 재미 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/32437550/whats-the-difference-between-instant-and-localdatetime&quot;&gt;https://stackoverflow.com/questions/32437550/whats-the-difference-between-instant-and-localdatetime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/39586311/java-8-localdatetime-now-only-giving-precision-of-milliseconds&quot;&gt;https://stackoverflow.com/questions/39586311/java-8-localdatetime-now-only-giving-precision-of-milliseconds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/44965545/best-practices-with-saving-datetime-timezone-info-in-database-when-data-is-dep&quot;&gt;https://stackoverflow.com/questions/44965545/best-practices-with-saving-datetime-timezone-info-in-database-when-data-is-dep&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.alibabacloud.com/blog/performance-issues-related-to-localdatetime-and-instant-during-serialization-operations_595605?spm=2c41.13786565.0.0&quot;&gt;https://www.alibabacloud.com/blog/performance-issues-related-to-localdatetime-and-instant-during-serialization-operations_595605?spm=2c41.13786565.0.0&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/dataseries/performance-issues-related-to-localdatetime-and-instant-during-serialization-operations-5f7931fe5b2a&quot;&gt;https://medium.com/dataseries/performance-issues-related-to-localdatetime-and-instant-during-serialization-operations-5f7931fe5b2a&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java/개념</category>
      <category>instant</category>
      <category>Java8</category>
      <category>LocalDateTime</category>
      <category>LocalDateTime vs Instant</category>
      <category>LocalDateTime 과 Instant 차이</category>
      <category>OffsetDateTime</category>
      <category>ZonedDateTime</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/85</guid>
      <comments>https://sujl95.tistory.com/85#entry85comment</comments>
      <pubDate>Tue, 9 Aug 2022 02:42:39 +0900</pubDate>
    </item>
    <item>
      <title>DynamoDB 장단점과 DynamoDB를 시작 전에 알면 좋은 11가지</title>
      <link>https://sujl95.tistory.com/84</link>
      <description>&lt;h1&gt;DynamoDB 장단점과 DynamoDB를 시작 전에 알면 좋은 11가지&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 포스팅 한 것에 이어 해당 포스팅도 사내에서 발표했던 내용을 정리하여 포스팅 한 것입니다.&lt;/p&gt;
&lt;h1&gt;DynamoDB 장단점&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 key-value 형태로 저장된다. JSON file로 저장되는 개념이라 사용하기 간편하다.&lt;/li&gt;
&lt;li&gt;key-value 형태 이므로 READ 속도가 빠르다. (10ms 이하의 읽기 및 쓰기 성능)&lt;/li&gt;
&lt;li&gt;확장성이 좋다.(수평적. 초당 수천 건의 요청 처리 가능)&lt;/li&gt;
&lt;li&gt;속성에 대한 추가와 변경이 자유롭다.&lt;/li&gt;
&lt;li&gt;완전 관리형 서비스이므로 운영 부담이 발생하지 않는다.&lt;/li&gt;
&lt;li&gt;요청 수에 따라 원활하게 확장되기 때문에 비용 효율적이고 IO 작업을 원활하게 지원한다.&lt;/li&gt;
&lt;li&gt;성능과 가용성을 위해 데이터를 3곳의 가용 영역에 복제하여 저장하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터들 간의 관계(relation)가 없기 때문에 같은 데이터가 여러 컬렉션에 중복되어 들어있을 수 있다. update가 일어날 경우 모든 테이블에서 작업해주어야 한다.&lt;/li&gt;
&lt;li&gt;큰 REST API 서비스를 운영할 경우 이를 처리할 수 있는 체계적인 API가 제공되지 않는다.&lt;/li&gt;
&lt;li&gt;ORM 지원 라이브러리도 없고, 있다 하더라도 메이저하지도 않아서 쓰기 힘들 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ORMODM : ORM과 동일하게 객체 관계로 정의한 내용을 NoSQL 형태로 매핑해주는 도우미 역할 ( MongoDB에는 Mongoose가 있다 )&lt;/li&gt;
&lt;li&gt;ORM : 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 도우미 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여러 쿼리에 대해 일관성, 원자성을 보장하지 않는다. 즉, Transaction 이 없다. 전략으로 사용해야한다.&lt;/li&gt;
&lt;li&gt;러닝커브&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 사례&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모바일 애플리케이션 - 애플리케이션 데이터 및 세션 상태 저장&lt;/li&gt;
&lt;li&gt;게임 애플리케이션 - 사용자 기본 설정 및 앱 상태 저장 / 플레이어의 게임 상태 저장&lt;/li&gt;
&lt;li&gt;애플리케이션 모니터링 - 애플리케이션 로그 및 이벤트 데이터 저장 / JSON 데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 사례&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;게임 서비스 사용 사례 및 설계 모델&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 글로벌 게임 서비스 업체들이 게임 상태, 플레이어 데이터, 세션 기록 및 리더보드 등 게임 플랫폼의 모든 부분에 DynamoDB를 사용하고있다. DynamoDB를 선택함으로써 얻는 이점은 수백만 명의 동시 사용자 및 요청을 지원하는 동시에 밀리초 수준의 액세스 지연 시간을 유지할 수 있다는 점이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EA : MySQL 클러스너로 구성되던 이전 DB에 비해 90%의 비용을 절감사용자 ID를 파티션 및 기본 키로 사용해 (1:1 모델링 패턴) 사용자 데이터 및 게임 인벤토리를 저장한다.&lt;/li&gt;
&lt;li&gt;PennyPop : 분당 얼마 처리하지 못했던 요청 수를 DynamoDB를 사용하면서 80,000회 까지 수준을 확대시켰다. 또한 완전 관리형 서비스를 사용함으로써 DB를 따로 관리할 여력이 되지 않는 기업이 추가 인력 없이도 서비스 개발에만 집중 할 수 있게 되었다.&lt;/li&gt;
&lt;li&gt;Riot Games : 날짜 및 시간 기준의 빠른 검색이 필요한 게임 플레이어의 데이터를 DynamoDB에 두고 구체화된 뷰를 생성하여 빠른 검색을 제공했다. 기존 DB(Vertica)에서 수 분이 걸리던 단일 키 검색 작업은 1초 미만으로 단축되었다. (1:M 모델링 패턴)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파티션 키 : 플레이어 ID&lt;/li&gt;
&lt;li&gt;정렬 키 : 마지막 로그인과 같은 날짜 및 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;DynamoDB 를 시작 전에 알면 좋은 11가지&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장점&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Data Modeling&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DynamoDB는 Document 지향 데이터 모델을 지원해준다. 테이블을 생성하기 위해 기본키를 정의하기만하면 된다. 동적 속성 집합을 사용하여 이러한 테이블에 항목을 추가가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DynamoDB의 Items은 SQL의 row에 해당하고 DynamoDB의 attributes는 SQL의 Column에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DynamoDB는 다음 Data Type을 지원한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스칼라 데이터 유형 : Number, String, Binary, , Boolean&lt;/li&gt;
&lt;li&gt;컬렉션 데이터 유형 : Set, List , Map&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Operational Ease(운영 용이성)&lt;/b&gt;&lt;/h2&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;실제로 인스턴스나 디스크와 같은 기본 인프라 구성 요소에 액세스 할 수 잇는 방법이 없다. DynamoDB 테이블을 사용하려면 사용자가 읽기 용량 단위( read capacity units(RCUs)) 및 write capacity units(WCU)를 미리 예약해야한다. 사용자는 예약된 처리 용량에 대해 시간당 요금이 부과가된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. &lt;b&gt;Linear Scalability (선형 확장성)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DynamoDB는 &lt;b&gt;Auto Sharding 과 load-balancing 을 지원&lt;/b&gt;한다. 이를 통해 애플리케이션은 계속 증가하는 대용량 데이터를 투명하게 저장이 가능하다. 그러나 특정 지점을 넘어서는 &lt;b&gt;천문학적 비용이 발생한다&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. &lt;b&gt;AWS Ecosystem Integration(&lt;/b&gt; AWS 생태계 통합)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DynamoDB는 &lt;b&gt;AWS 생태계에 잘 통합&lt;/b&gt;되어있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;통합의 몇가지 예시)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 쉽고 &lt;b&gt;효율적인 비용으로 S3에 백업&lt;/b&gt;을 할 수 있다.&lt;/li&gt;
&lt;li&gt;분석을 위해 Elastic MapReduce(EMR)로 데이터를 내보낼 수 있다.&lt;/li&gt;
&lt;li&gt;보안 및 액세스 제어가 &lt;b&gt;AWS IAM에 통합&lt;/b&gt;이된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단점&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Cost Effectiveness(비용 효율성)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DynamoDB의 요금 모델은 빠르게 성장하는 회사에서 가장 비싼 단일 AWS 서비스로 만들 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비싼이유 6가지 주요 이유&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;b&gt;Over-provisioning to handle hot partitions(&lt;/b&gt;Hot Partition 을 처리 하기 위한 오버 프로비저닝)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DynamoDB에서 프로비저닝된 총 &lt;b&gt;IOPS&lt;/b&gt;(Input/Output Operations Per Second, HDD, SSD, SAN같은 컴퓨터 저장 장치를 벤치마크하는 데 사용되는 성능 측정 단위) &lt;b&gt;는 모든 파티션에 고르게 분배&lt;/b&gt;된다. 따라서 이러한 파티션에 읽기 및 쓰기를 고르게 분배할 파티션 키를 선택하는 것이 매우 중요하다. 테이블에 더 많은 IOPS가 필요한 몇 개의 핫 파티션이 있는 경우 프로비저닝된 총 처리량은 &lt;b&gt;가장 핫한 파티션에 필요한 처리량으로 모든 파티션이 프로비저닝 되도록 충분히 높아야한다. 이로인해 비용이 크게 증가&lt;/b&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;b&gt;Cost explosion for fast growing datasets(빠르게 성장하는 데이터 세트에 대한 비용 폭발)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;&lt;a href=&quot;https://syslog.ravelin.com/you-probably-shouldnt-use-dynamodb-89143c1287ca&quot;&gt;DynamoDB를 사용하면 안된다&lt;/a&gt;&quot; 라는 게시물에서 DynamoDB가 빠르게 성장하는 데이터 세트에 적합하지 않는 이유를 강조한다. 데이터가 늘어남에 따라 데이터를 자동으로 확장하기 위해서 파티션수도 늘어나는데 테이블에 대해 프로비저닝된 총 처리량은 증가하지 않는다. 따라서 각 파티션에 사용할 수 있는 &lt;b&gt;처리량은 데이터가 증가함에 따라 지속적으로 감소&lt;/b&gt;되는데 &lt;b&gt;기존 쿼리 속도를 따라잡으려면 총 처리량이 지속적으로 증가해야하므로 비용이 몇 배나 증가&lt;/b&gt;하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;b&gt;Paid caching tier (유료 캐싱 계층)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DynamoDB의 latency가 충분히 낮지 않은 경우 &lt;b&gt;성능을 높이려면 캐시&lt;/b&gt;(&lt;a href=&quot;https://aws.amazon.com/ko/dynamodb/dax/&quot;&gt;**DAX&lt;/a&gt;[Amazon DynamoDB Accelerator]** 또는 ElasticCache)&lt;b&gt;로 지연 시간을 늘려야&lt;/b&gt;한다. 두 경우 모두 캐싱 계층은 데이터베이스 계층 외에 추가 비용이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. &lt;b&gt;Indexes may cost extra (인덱스 추가 비용)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본키의 일부가 아닌 속성에 대한 데이터를 쿼리하려는 애플리케이션은 보조 인덱스를 생성해야한다. DynamoDB는 &lt;a href=&quot;http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html&quot;&gt;Local Secondary Index&lt;/a&gt; 와 &lt;a href=&quot;http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html&quot;&gt;Global Secondary Index&lt;/a&gt; 를 지원한다. Local Secondary Index에는 추가 비용이 발생하지 않지만 &lt;b&gt;Global Secondary Index는 추가 read 및 write 용량 프로비저닝이 필요하므로 추가 비용이 발생&lt;/b&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. &lt;b&gt;Writes, strongly consistent reads and scans are expensive(Write, 읽기 및 스캔 비용이 많이든다)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫째, &quot;&lt;a href=&quot;https://aws.amazon.com/ko/dynamodb/pricing/&quot;&gt;DynamoDB Write 비용이 비싸다&lt;/a&gt;&quot;&lt;/li&gt;
&lt;li&gt;둘째, &lt;b&gt;강력한 일관성 write는 최종 일관성 읽기 비용의 두 배&lt;/b&gt; (&lt;a href=&quot;https://blog.yugabyte.com/11-things-you-wish-you-knew-before-starting-with-dynamodb/&quot;&gt;강력한 일관성 읽기, 최종적 일관성 읽기&lt;/a&gt;)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;강력한 일관성 읽기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;강력한 일관성 읽기를 요청하면 DynamoDB는 성공한 모든 이전 쓰기 작업의 업데이트가 반영된 최신 데이터를 포함하는 응답을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;최종적 일관성 읽기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DynamoDB 테이블에서 데이터를 읽을 때 , 응답에 최근 완료된 쓰기 작업의 결과가 반영되지 않을 수 있다. 응답에는 부실 데이터가 일부 포함이 될 수 있다. 잠시 후 읽기 요청을 반복하면 응답이 최신 데이터를 반환한다.&lt;/li&gt;
&lt;/ul&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 read capacity unit 이 실제로 읽은 바이트 수를 고려하기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. &lt;b&gt;Lack of lower cost test/dev tables(더 저렴한 테스트/ 개발 테이블 부족)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DynamoDB 는 관리형 서비스이므로 고객 대면 프로덕션 테이블과 &lt;b&gt;개발/테스트/스테이징 테이블을 실제로 구분하지 않는다&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. &lt;b&gt;Low Latency Reads(&lt;/b&gt;짧은 지연 시간 읽기)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9s4GM/btrmhKpbyj6/XLeNgL83Z2b3uhhycJ7nZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9s4GM/btrmhKpbyj6/XLeNgL83Z2b3uhhycJ7nZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9s4GM/btrmhKpbyj6/XLeNgL83Z2b3uhhycJ7nZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9s4GM%2FbtrmhKpbyj6%2FXLeNgL83Z2b3uhhycJ7nZ0%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;464&quot; height=&quot;346&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본으로 제공되는 분산 캐시가 없기 때문에 DynamoDB의 일반적인 지연시간은 10ms~20ms범위이다. 강력한 일관된 읽기는 일반적으로 최종 일관성 읽기보다 latency가 더 길다.&lt;/li&gt;
&lt;li&gt;이러한 Latency가 길어질 경우에 추가적으로 캐싱 계층(DAX)을 사용하거나 redis 를 사용한다. 또 다른 AWS 서비스인 별도의 ElasticCache를 프로비저닝하려면 1ms 이하의 지연 시간이 필요한 대규모 애플리케이션이 필요하다. 앱이 캐시를 채우고 데이터베이스와 캐시의 일관성을 유지하는 추가적인 복잡성을 처리해야 하기 때문에 이러한 서비스는 추가 비용이 들어가며 데이터 일관성 및 개발자 대비성이 저하된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. Geo-Distribution (지리적 분포)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2017년 이후 도입된 글로벌 테이블은 DynamoDB에 지리적 분포를 추가하기 위한 주요 기능이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 지역에는 동일하지만 독립적인 테이블이 있고 이러한 모든 테이블은 자동화된 비동기식 복제 메커니즘으로 연결되어 &quot;글로벌 테이블&quot; 이라는 개념으로 이어진다. &lt;b&gt;지역 간 동시 쓰기는 데이터 손실로 이어지고 읽기는 해당 지역에서 강력하게 일관될 수 없다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. &lt;b&gt;Development Agility (&lt;/b&gt;개발 민첩성)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마이크로 서비스 패러다임은 소프트웨어 개발 민첩성을 높이고 릴리즈 주기를 가속화 하기 위해 널리 채택되고 있다. 마이크로 서비스 지향 아키텍처에서 각 마이크로 서비스는 서로 독립적으로 데이터를 읽고 쓰는 경향이 있다. 예시로 데이터가 캐시되더라도 DB의 총 IOPS가 증가한다. 그 결과로 훨씬 더 높은 IOPS에 대해 테이블이 프로비저닝되어 비용이 매우 많이든다.&lt;/li&gt;
&lt;li&gt;강력한 CI/CD 파이프 라인을 설정하는 것은 릴리즈 주기를 가속화하는 또 다른 중요한 패러다임이다. &lt;b&gt;DynamoDB는 관리형 서비스이므로 CI/CD를 설정하기가 매우 어렵다&lt;/b&gt;.(예로 애플리케이션이 DB의 오류 영향을 받는지 안 받는지 확인한다)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. &lt;b&gt;Troubleshooting in Production (프로덕션에서의 문제 해결)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관리형 서비스는 모든 것이 동작할 때 좋은 제안이지만 그러나 문제 해결에 직면하여 처리하기 어려울 수 있다. &lt;b&gt;문제가 발생하면 서비스 공급자(AWS)의 지원에 의존&lt;/b&gt;해야한다. 이것은 느리거나 비용이 많이 들 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;안티 패턴&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;10. Strong Consistency with High Availability(고가용성을 통한 강력한 일관성)&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bunouJ/btrmjcd8sdJ/XQCqWCkkxFcHK6Ct900VgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bunouJ/btrmjcd8sdJ/XQCqWCkkxFcHK6Ct900VgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bunouJ/btrmjcd8sdJ/XQCqWCkkxFcHK6Ct900VgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbunouJ%2Fbtrmjcd8sdJ%2FXQCqWCkkxFcHK6Ct900VgK%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;373&quot; height=&quot;341&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;341&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CAP 이론 관점에서 DynamoDB는 최종 쓰기 일관성이 있는 사용 가능 및 파티션 허용(AP)데이터베이스이다. 읽기 전면에서는 최종 일관성 읽기와 강력한 읽기를 모두 지원한다. 그러나 DynamoDB의 강력한 일관성 읽기는 네트워크 지연 및 파티션이 있을때 가용성이 높지 않다. 이러한 오류는 AWS와 같은 퍼블릭 클라우드에서 실행되는 &lt;b&gt;다중 지역/글로벌 앱에서 일반적으로 DynamoDB는 단일 지역으로만 강력하게 일관된 읽기를 제한&lt;/b&gt;하여 이러한 오류를 줄이려고 한다. 결과적으로 &lt;b&gt;DynamoDB는 대부분의 다중 지역 앱에 적합하지 않고 단일 지역 앱에도 신뢰할 수 없는 솔루션이 된다&lt;/b&gt;.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. &lt;b&gt;ACID Transactions and Secondary Indexes(ACID 트랜잭션 및 보조 인덱스)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DynamoDB는 ACID와 호환되지 않는다. &lt;b&gt;ACID에서 'C'(일관성)와 'D'(내구성)만 제공&lt;/b&gt;한다. DynamoDB를 기반으로 ACID를 사용하는 방법의 &lt;a href=&quot;https://github.com/awslabs/dynamodb-transactions/blob/master/DESIGN.md&quot;&gt;예시&lt;/a&gt;이다. 그러나 애플리케이션 아키텍처가 매우 복잡해 질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DynamoDB의 Global Secondary Index는 최종적으로 일관성이 있으며 올바른 결과를 반환하지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 DynamoDB는 앱의 트랜잭션 부분을 처리하기 위해 별도의 RDBMS 계층이 필수인 대부분의 1세대 NoSQL 데이터베이스와 유사하다. FoundationDB 및 &lt;a href=&quot;https://www.yugabyte.com/yugabytedb/&quot;&gt;YugaByte DB&lt;/a&gt;(SQL도 지원) 와 같은 2세대 NoSQL 데이터베이스는 분산 트랜잭션 및 강력하게 일관된 보조 인덱스에 대한 기본 지원을 통해 이러한 문제를 해결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS re:Invent 2018 이후 업데이트 : &lt;a href=&quot;https://blog.yugabyte.com/nosql-databases-becoming-transactional-mongodb-dynamodb-faunadb-cosmosdb/&quot;&gt;NoSQL DB가 트랜잭션이 되는 이유는 무엇인가?&lt;/a&gt; 이후 DynamoDB는 엄격하게 제한된 방식으로 트랜잭션을 지원&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;li&gt;최대 10개 항목 또는 4MB 데이터로 제한&lt;/li&gt;
&lt;li&gt;클라이언트 제어 트랜잭션 없음&lt;/li&gt;
&lt;li&gt;트랜잭션이 지원되더라도 일관된 보조 인덱스 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;간단 요약표&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wQ2dT/btrmiDJNM1J/3wEKPzkWtcvWQsF1anC9x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wQ2dT/btrmiDJNM1J/3wEKPzkWtcvWQsF1anC9x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wQ2dT/btrmiDJNM1J/3wEKPzkWtcvWQsF1anC9x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwQ2dT%2FbtrmiDJNM1J%2F3wEKPzkWtcvWQsF1anC9x0%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;690&quot; height=&quot;791&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.yugabyte.com/11-things-you-wish-you-knew-before-starting-with-dynamodb/&quot;&gt;https://blog.yugabyte.com/11-things-you-wish-you-knew-before-starting-with-dynamodb/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Database/기타</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/84</guid>
      <comments>https://sujl95.tistory.com/84#entry84comment</comments>
      <pubDate>Sat, 27 Nov 2021 18:54:34 +0900</pubDate>
    </item>
    <item>
      <title>RDBMS의 한계와 NoSQL을 사용하는 이유 (3) NoSQL 장단점, 특징</title>
      <link>https://sujl95.tistory.com/83</link>
      <description>&lt;h1&gt;DB RDBMS의 한계와 NoSQL을 사용하는 이유 (3) NoSQL 장단점, 특징&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;NoSQL(Not Only SQL, 비-관계형 데이터베이스)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RDBMS의 복잡도와 용량 한계를 극복하기 위한 목적으로 등장했다.&lt;/li&gt;
&lt;li&gt;정해진 스키마가 없어 자유롭게 데이터를 저장할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnVwFc/btrmlXt03NL/22db8AI2QB49X7th6glTy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnVwFc/btrmlXt03NL/22db8AI2QB49X7th6glTy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnVwFc/btrmlXt03NL/22db8AI2QB49X7th6glTy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnVwFc%2FbtrmlXt03NL%2F22db8AI2QB49X7th6glTy0%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;568&quot; height=&quot;333&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&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;자유로운 Schema-less 구조&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;h3 data-ke-size=&quot;size23&quot;&gt;기대하는 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;높은 성능&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디스크 대신 메모리 기반으로 결과는 빠르게 리턴되어야하고 이는 Non-Blocking Write와 낮은 복잡성을 가진 알고리즘을 통해서 이룰 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청한 작업을 즉시 마칠 수 없다면 즉시 리턴해야한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Non-Blocking&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일관성&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;강한 일관성은 필요 없고 결과적인 일관성만 가지면된다 (Read-Your-Writes)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과적인 일관성&lt;/li&gt;
&lt;li&gt;Read-Your-Writes(RYW) 일관성은 레코드가 업데이트 되었을때 그 레코드에 대한 읽기 시도는 업데이트된 값을 돌려주는 것을 보장해주는 것의 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ETC&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포, 모델링, 쿼리의 유연함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스 일관성에 약하다. 이 일관성을 가용성, 분할 용인, 속도와 맞바꾸었다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분할 용인 : 네트워크 실패로 인하여 임의의 분할이 발생해도 시스템은 계속적으로 동작해야 한다. 임의의 메시지들의 손실 또는 시스템의 부분 실패에도 불구하고, 시스템은 지속적으로 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;key값에 대한 입출력만 지원한다.&lt;/li&gt;
&lt;li&gt;스키마가 정해져 있지 않아, 데이터에 대한 규격화가 되어 있지 않다.&lt;/li&gt;
&lt;li&gt;데이터가 여러 컬렉션에 중복되어 있어서 데이터를 &lt;b&gt;UPDATE 하는 경우 모든 컬렉션에서 수행&lt;/b&gt;해야하기 때문에 느리다.&lt;/li&gt;
&lt;li&gt;데이터 중복으로 인한 수정 작업의 번거로움&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;종류&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kKtTE/btrmgFa2t9M/XBfLBPrksvVoRFbqVJr4EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kKtTE/btrmgFa2t9M/XBfLBPrksvVoRFbqVJr4EK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kKtTE/btrmgFa2t9M/XBfLBPrksvVoRFbqVJr4EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkKtTE%2FbtrmgFa2t9M%2FXBfLBPrksvVoRFbqVJr4EK%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;718&quot; height=&quot;250&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;키-값 저장소(key-value)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Key와 Value으로 구성된 배열구조의 데이터베이스로 가장 단순한 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본적인 패턴으로 Key, Value가 하나의 묶음으로 저장되는 구조이기 때문에 속도가 빠르며 분산 저장에 용이하다&lt;/li&gt;
&lt;li&gt;값에 모든 데이터 타입이 허용가능하다.&lt;/li&gt;
&lt;li&gt;Key는 중복될 수 없고 이 Unique Key에 각각 하나의 Value를 가지고 있는 형태가 된다. 데이터를 조회 및 입력할 때, Key를 가지고 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 사용하는게 좋을지?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성능 향상을 위해 RDBMS 에서 캐싱 (Redis)&lt;/li&gt;
&lt;li&gt;장바구니 같은 웹애플리케이션에서 일시적인 속성 추적&lt;/li&gt;
&lt;li&gt;이미지나 오디오 파일 같은 대용량 객체 저장&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;AWS DynamoDB&lt;/li&gt;
&lt;li&gt;Riak&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래프 저장소(graph)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bahF1q/btrmhYHobAJ/6rpBdOtD83ncgkcxb3D6G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bahF1q/btrmhYHobAJ/6rpBdOtD83ncgkcxb3D6G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bahF1q/btrmhYHobAJ/6rpBdOtD83ncgkcxb3D6G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbahF1q%2FbtrmhYHobAJ%2F6rpBdOtD83ncgkcxb3D6G0%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;629&quot; height=&quot;716&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RDBMS보다 성능이 좋고 유연하며 유지보수에 용이한것으로 특징&lt;/li&gt;
&lt;li&gt;Social Networks, Network diagrams 등에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Neo4j&lt;/li&gt;
&lt;li&gt;Blazegraph&lt;/li&gt;
&lt;li&gt;OrientDB&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컬럼 저장소(column)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WTgcL/btrmgRhYoXd/foo83XzJL6JzzPb0A5Dzl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WTgcL/btrmgRhYoXd/foo83XzJL6JzzPb0A5Dzl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WTgcL/btrmgRhYoXd/foo83XzJL6JzzPb0A5Dzl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWTgcL%2FbtrmgRhYoXd%2Ffoo83XzJL6JzzPb0A5Dzl1%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;466&quot; height=&quot;238&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Key, Value와 유사한 형태이다. 단지 데이터가 내부적으로 Key를 기준으로 Sorting되서 저장되는 점이 차이가 있다. Order by를 제공하지 않는 NoSQL에서 다양한 방법으로 활용할 수 있다는 장점이 있다.&lt;/li&gt;
&lt;li&gt;컬럼과 로우로 구성된 데이터베이스로 컬럼은 이름과 값으로 구성되고 로우는 각기 다른 컬럼으로 구성이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실 사용 서비스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Twitter&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL &amp;rarr; Cassandra&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tumblr&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Shorten URL 에서 사용중&lt;/li&gt;
&lt;li&gt;MySQL &amp;rarr; Hbase &amp;rarr; Redis&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CassandraDB&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM기반 DB Engine이다&lt;/li&gt;
&lt;li&gt;Java로 개발되어 JVM위에 실행되기 때문에 다른 운영체제로의 우수한 이식성을 갖지만, 이것은 또한 JVM 기반 어플리케이션이 공통적으로 갖고 있는 메모리 관리나, Garbage Collection과 같은 필수 작업으로 인해 성능제약이 필연적이다&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문서 저장소(document)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Key, Value에서 확장된 형태이다. 저장되는 Value의 타입으로 Document라는 구조화된 데이터 타입(JSON, XML 등)이 사용된다. 복잡한 계층 구조를 잘 표현할 수 있다는 점이 장점이다. 제품에 따라 Sorting, Join, Grouping 등 RDB와 같은 추가 기능이 지원되기도 하다. 앞으로 정리할 MongoDB가 이 종류에 해당되며 여러 기능이 제공된다.&lt;/li&gt;
&lt;li&gt;JSON 기반으로 통신을 하는 HTTP 웹 서버의 경우 편리하게 데이터를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;NoSQL데이터베이스 중 가장 인기가 높다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;li&gt;_id : PK, RowID를 가짐&lt;/li&gt;
&lt;li&gt;컬럼 없음 , Schema 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 사용하면 좋을지?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대용량 데이터를 읽고 쓰는 웹 사이트용 벡엔드 지원&lt;/li&gt;
&lt;li&gt;JSON 데이터 구조를 사용하는 애플리케이션&lt;/li&gt;
&lt;li&gt;비정규화된 중첩 구조의 데이터를 사용하는 애플리케이션&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MongoDB&lt;/li&gt;
&lt;li&gt;Azure Cosmose DB&lt;/li&gt;
&lt;li&gt;Couch DB&lt;/li&gt;
&lt;li&gt;MarkLogin&lt;/li&gt;
&lt;li&gt;OrientDB&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Replica Set&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동 장애 극복 지원. &lt;b&gt;Master 장애시 Slave가 Master로 역할 전환&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;mongoDB에서는 primary, secondary 라고 부른다&lt;/li&gt;
&lt;li&gt;쓰기는 Master로 읽기는 Master, slave 모두 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실 사용 서비스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국내 무신사 : MongoDB 기반으로 AWS에서 출시한 AWS DocumentDB&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용 이유 : 갱신(Update) 구문을 수행할 때 기존 데이터의 갱신이 아닌 갱신될 데이터를 신규로 등록하고 기존의 데이터는 점유해제 하는 방식으로 처리한다&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;NoSQL 사용시 효율적인 상황&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아주 낮은 응답 지연시간(latency) 이 요구됨&lt;/li&gt;
&lt;li&gt;다루는 데이터가 비정형이라 관계형 데이터가 아니다&lt;/li&gt;
&lt;li&gt;데이터(JSON, YAML, XML등)를 직렬화나 역직렬화 할 수 있으면 된다.&lt;/li&gt;
&lt;li&gt;아주 많은 양의 데이터를 저장할 필요가 있을때&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 상황에 사용하는지?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터에 대한 캐시가 필요한 경우&lt;/li&gt;
&lt;li&gt;배열 형식의 데이터를 고속으로 처리할 필요가 있는 경우&lt;/li&gt;
&lt;li&gt;정확한 데이터 구조를 알 수 없거나 변경 / 확장 될 수 있는 경우&lt;/li&gt;
&lt;li&gt;읽기(read)처리를 자주하지만, 데이터를 자주 변경(update)하지 않는 경우 (즉, 한번의 변경으로 수십 개의 문서를 업데이트 할 필요가 없는 경우)&lt;/li&gt;
&lt;li&gt;데이터베이스를 수평으로 확장해야하는 경우 ( 막대한 양의 데이터를 다뤄야 하는 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;NoSQL DB vs RDBMS 간단 비교&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2021-11-27 오후 6.43.49.png&quot; data-origin-width=&quot;2122&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAX847/btrmhtOpdJ6/TtdP0Xe6AmDvCzHeFman7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAX847/btrmhtOpdJ6/TtdP0Xe6AmDvCzHeFman7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAX847/btrmhtOpdJ6/TtdP0Xe6AmDvCzHeFman7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAX847%2FbtrmhtOpdJ6%2FTtdP0Xe6AmDvCzHeFman7k%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;2122&quot; height=&quot;908&quot; data-filename=&quot;스크린샷 2021-11-27 오후 6.43.49.png&quot; data-origin-width=&quot;2122&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;NoSQL&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술 요소&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BASE&lt;/li&gt;
&lt;li&gt;(Basically Available, Soft State, Eventually Consistent)&lt;/li&gt;
&lt;/ul&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;Basically Available(가용성)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VoGEi/btrmgJc8ylA/eASXClupzZr4n9jOTBIZWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VoGEi/btrmgJc8ylA/eASXClupzZr4n9jOTBIZWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VoGEi/btrmgJc8ylA/eASXClupzZr4n9jOTBIZWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVoGEi%2FbtrmgJc8ylA%2FeASXClupzZr4n9jOTBIZWk%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;303&quot; height=&quot;132&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&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 data-ke-size=&quot;size16&quot;&gt;Master 서버에 장애 발생 시에도 여러 Slave 서버로 인해 무중단 서비스가 가능함&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;&lt;b&gt;Soft State(소프트 상태)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VCgVg/btrmhZzvs5a/u4Vf2vb95KScglJ41lZfl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VCgVg/btrmhZzvs5a/u4Vf2vb95KScglJ41lZfl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VCgVg/btrmhZzvs5a/u4Vf2vb95KScglJ41lZfl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVCgVg%2FbtrmhZzvs5a%2Fu4Vf2vb95KScglJ41lZfl0%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;303&quot; height=&quot;132&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Eventually Consistent(결과적 일관성)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rIkdf/btrmhtASzmH/O6lknKCfk6J9bKWvjPtQu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rIkdf/btrmhtASzmH/O6lknKCfk6J9bKWvjPtQu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rIkdf/btrmhtASzmH/O6lknKCfk6J9bKWvjPtQu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrIkdf%2FbtrmhtASzmH%2FO6lknKCfk6J9bKWvjPtQu0%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;303&quot; height=&quot;132&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&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;시스템 부하 및 네트워크 속도에 따라 서버에 복제하는 시간이 다를 수 있으나 최종적으로는 모든 서버에 데이터가 복제됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;a href=&quot;https://meetup.toast.com/posts/274&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RDBMS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술 요소&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ACID&lt;/li&gt;
&lt;li&gt;(Atomicity, Consistency, Isolation, Durability)&lt;/li&gt;
&lt;/ul&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;Atomicity(원자성)&lt;/b&gt;&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZWhPt/btrmmb6MD6J/7PC7QdkauJTEdFGhN33mgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZWhPt/btrmmb6MD6J/7PC7QdkauJTEdFGhN33mgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZWhPt/btrmmb6MD6J/7PC7QdkauJTEdFGhN33mgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZWhPt%2Fbtrmmb6MD6J%2F7PC7QdkauJTEdFGhN33mgK%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;303&quot; height=&quot;132&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&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;b&gt;Consistency(일관성)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ClUfw/btrmnmmM9tS/rPeCSY7IZuq9DBep3EtaP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ClUfw/btrmnmmM9tS/rPeCSY7IZuq9DBep3EtaP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ClUfw/btrmnmmM9tS/rPeCSY7IZuq9DBep3EtaP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FClUfw%2FbtrmnmmM9tS%2FrPeCSY7IZuq9DBep3EtaP0%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;302&quot; height=&quot;132&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Isolation(격리성)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uKZKv/btrmid5GmUF/lFmpyM54Yzy6ZG3guWebA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uKZKv/btrmid5GmUF/lFmpyM54Yzy6ZG3guWebA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uKZKv/btrmid5GmUF/lFmpyM54Yzy6ZG3guWebA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuKZKv%2Fbtrmid5GmUF%2FlFmpyM54Yzy6ZG3guWebA1%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;303&quot; height=&quot;132&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;132&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 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;b&gt;Durability(영속성)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0v03b/btrmmcEEoiB/oSeZi1KFpyxlmFARQkWQCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0v03b/btrmmcEEoiB/oSeZi1KFpyxlmFARQkWQCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0v03b/btrmmcEEoiB/oSeZi1KFpyxlmFARQkWQCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0v03b%2FbtrmmcEEoiB%2FoSeZi1KFpyxlmFARQkWQCK%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;302&quot; height=&quot;133&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&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;중점 사항 : 데이터의 일관성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://meetup.toast.com/posts/274&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;</description>
      <category>Database/기타</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/83</guid>
      <comments>https://sujl95.tistory.com/83#entry83comment</comments>
      <pubDate>Sat, 27 Nov 2021 18:48:37 +0900</pubDate>
    </item>
    <item>
      <title>RDBMS의 한계와 NoSQL을 사용하는 이유 (2)  RDBMS의 한계,  트랜잭션</title>
      <link>https://sujl95.tistory.com/82</link>
      <description>&lt;h1&gt;DB RDBMS의 한계와 NoSQL을 사용하는 이유 (2)&amp;nbsp;&amp;nbsp;RDBMS의&amp;nbsp;한계,&amp;nbsp;&amp;nbsp;트랜잭션&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RDBMS(Relational DataBase Management System, 관계형 데이터베이스)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관계형 데이터 모델은 데이터 간의 상관 관계에서 개체간의 관계를 2차원의 테이블 형태로 표현&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;목표&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RDBMS의 가장 큰 목표는 데이터 무결성을 높이는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무결성의 종류&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔터티 무결성(Entity Integrity) = 개체 무결성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 인스턴스는 고유한 값(=같은 값 존재 X)이거나, 널(Null) 값을 가지면 안 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;참조 무결성(Referential Integrity)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참조되는 엔터티의 주 식별자 값과 일치(=참조하는 기본키 값 중에 하나와 일치)하거나, 널(Null) 값이어야 함. 참조 무결성은 FK(Foreign Key) 제약에 의해 지켜짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;도메인 무결성(Domain Integrity)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;속성 값과 관련된 제약.&lt;/li&gt;
&lt;li&gt;같은 속성에 사용되느니 값들은 같은 성격의 값.&lt;/li&gt;
&lt;li&gt;기본 값이나 널 여부, 체크 조건 등으로 지켜질 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;종류&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL, MariaDB, Oracle, &lt;b&gt;PostgreSQL&lt;/b&gt; 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&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;/li&gt;
&lt;li&gt;&lt;b&gt;데이터의 일관성을 보증&lt;/b&gt;할 수 있다.&lt;/li&gt;
&lt;li&gt;복잡한 형태의 쿼리도 가능하다(Join 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스키마에 준수하지 않는 레코드는 추가할 수 없다. 컬럼을 자유롭게 추가하려면 테이블을 변경시키거나 새로운 테이블을 만들어야한다&lt;/li&gt;
&lt;li&gt;관계를 맺고 있기 때문에, JOIN문이 많은 매우 복잡한 쿼리가 만들어 질 수 있다&lt;/li&gt;
&lt;li&gt;수평적 확장이 어렵고, 대체로 수직적 확장만 가능하다.(즉 처리의 한계를 직면하게됨)&lt;/li&gt;
&lt;li&gt;대량의 데이터를 입력할 경우나 조회할 경우 성능이 저하 될 수 있고 Sharding과 Replication을 구축하는 비용이 많이 든다&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;트랜잭션&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 과정을 하나의 행위로 묶을 때 사용된다&lt;/li&gt;
&lt;li&gt;여러 단계를 수행했을때, 하나라도 실패하면 모두 취소되어야 한다. 이렇게 함으로써 데이터의 무결성을 보장한다. 모두 반영하거나 반영하지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;트랜잭션 사용 이유&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜잭션은 DB 서버에 여러 개의 클라이언트가 동시에 액세스 하거나 응용 프로그램이 갱신을 처리하는 과정에서 중단될 수 있는 경우 &lt;b&gt;데이터 부정합을 방지&lt;/b&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;/li&gt;
&lt;/ul&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;ACID&lt;/b&gt; : 데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하는 성질을 가리키는 약어&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원자성(Atomicity)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 작업이 반영되거나 모두 롤백되는 특성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일관성(Consistency)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜잭션이 성공적으로 완료되면 일관적인 DB상태를 유지한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 금액의 데이터 타입이 정수형(IntegerE)인데, 갑자기 문자열(string)이 되지 않는 것을 말한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 송금 전후 모두 금액의 데이터 타입은 정수형이여야 한다는 것이다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;격리성(Isolation)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜잭션을 수행 시 다른 트랜잭션의 연산 작업이 끼어들지 못하도록 보장하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;영구성(Durability)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번 저장된 트랜잭션을 영구히 보존된다&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;트랜잭션 격리 수준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;READ UNCOMMITTED : 다른 트랜잭션에서 커밋되지 않은 내용을 참조할 수 있다. &lt;b&gt;Dirty READ 발생&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;READ COMMITTED : 다른 트랜잭션에서 커밋된 내용만 참조할 수 있다. &lt;b&gt;NON-REPETABLE READ 발생&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;NON-REPETABLE READ&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 트랜잭션 내에서 똑같은 SELECT를 수행했을 때 항상 같은 결과를 반환해야한다는 REPEATABLE READ 정합성에 어긋난다. (입출금 처리 시 치명적)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EX)&lt;/li&gt;
&lt;li&gt;B 트랜잭션에서 1번 유저의 나이를 조회 = 27살&lt;/li&gt;
&lt;li&gt;A 트랜잭션에서 1번 유저의 나이를 27살 &amp;rarr; 28살로 바꾸고 COMMIT&lt;/li&gt;
&lt;li&gt;B 트랜잭션에서 1번 유저의 나이를 다시 조회 28살로 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REPETABLE READ: 트랜잭션에 진입하기 이전 커밋된 내용만 참조할 수 있다. &lt;b&gt;UPDATE 부정합, Phantom READ 발생&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;UPDATE 부정합&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;pre id=&quot;code_1638005470840&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;START TRANSACTION; -- transaction id : 1
SELECT * FROM Member WHERE name='thewing';

    START TRANSACTION; -- transaction id : 2
    SELECT * FROM Member WHERE name = 'thewing';
    UPDATE Member SET name = 'thewing2' WHERE name = 'thewing';
    COMMIT;

UPDATE Member SET name = 'thewing3' WHERE name = 'thewing'; -- 0 row(s) affected
COMMIT;​&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;트랜잭션 1에서 thewing 조회&lt;/li&gt;
&lt;li&gt;트랜잭션 2에서 thewing 조회 후 thewing2로 변경 후 커밋 name = thewing 는 UNDO 영역에 남겨놔야 한다&lt;/li&gt;
&lt;li&gt;트랜잭션 1이 바라보고 있는 것은 name = thewing 의 경우는 UNDO 영역의 데이터이고 UNDO 영역에 있는 데이터에 대해서는 쓰기 잠금을 걸 수 없다.&lt;/li&gt;
&lt;li&gt;결론적으로 thewing의 데이터는 존재하지 않으므로 변경이 일어나지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;Phantom READ&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 트랜잭션 내에서 같은 쿼리를 두 번 실행하는데 첫 번째 쿼리에서 없던 유령(Phantom) 레코드가 두 번째 쿼리에서 나타나는 현상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SERIALIZABLE : 트랜잭션에 진입하면 락을 걸어 다른 트랜잭션이 접근하지 못하게 한다. (성능 최저)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;적합 업무&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 무결성 및 일관성이 중요한 트랜잭션 업무&lt;/li&gt;
&lt;li&gt;온라인에서 다양한 집계 및 통계를 분석하는 업무&lt;/li&gt;
&lt;li&gt;복잡한 계산 및 실시간 데이터 정합성이 필요한 업무&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 상황에 사용하는지?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 경우에 관계형 데이터베이스를 사용하는 것이 안정&lt;/li&gt;
&lt;li&gt;관계를 맺고 있는 데이터가 자주 변경되는 애플리케이션일 경우&lt;/li&gt;
&lt;li&gt;변경될 여지가 없고 명확한 스키마가 사용자와 데이터에게 중요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RDBMS의 한계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채팅 데이터(Message)를 RDBMS(PSQL)에 넣게 된다면? (개인적인 견해)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;짧은 시간에 많은 데이터 입력이 처리가 된다.&lt;/li&gt;
&lt;li&gt;한정된 규모의 복잡성이 높은 데이터에서 단순한 대량의 데이터가 쌓이며 조회, 삽입이 빈번하게 일어난다.&lt;/li&gt;
&lt;li&gt;Lock이 자주 발생&lt;/li&gt;
&lt;li&gt;데이터가 쌓이게 되면 데이터베이스를 분산 시켜야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RDBMS는 분산을 하기 쉽지 않다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.oracle.com/kr/database/what-is-a-relational-database/&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;</description>
      <category>Database/기타</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/82</guid>
      <comments>https://sujl95.tistory.com/82#entry82comment</comments>
      <pubDate>Sat, 27 Nov 2021 18:35:08 +0900</pubDate>
    </item>
    <item>
      <title>RDBMS의 한계와 NoSQL을 사용하는 이유 (1) CAP, PACELC 이론</title>
      <link>https://sujl95.tistory.com/81</link>
      <description>&lt;h1&gt;DB RDBMS의 한계와 NoSQL을 사용하는 이유 (1)&amp;nbsp;CAP,&amp;nbsp;PACELC&amp;nbsp;이론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내에서 발표 했던 내용을 정리하여 포스팅 한 것입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CAP 이론&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분산 컴퓨팅 환경은 일관성(Consistency), 가용성(Availability), 분할 내성(Partitioning) 세 가지 특징을 가지고 있다, 이중 두 가지만 만족할 수 있다는 이론이다. NoSQL은 대부분 이 CAP 이론을 따른다.&lt;/li&gt;
&lt;/ul&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;817&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGTlHX/btrmhZ0Ag3f/PSkC9ybxFcqste7ab1AyE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGTlHX/btrmhZ0Ag3f/PSkC9ybxFcqste7ab1AyE1/img.png&quot; data-alt=&quot;CAP 이론&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGTlHX/btrmhZ0Ag3f/PSkC9ybxFcqste7ab1AyE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGTlHX%2FbtrmhZ0Ag3f%2FPSkC9ybxFcqste7ab1AyE1%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;490&quot; height=&quot;285&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CAP 이론&lt;/figcaption&gt;
&lt;/figure&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;900&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCVCYu/btrmieQ34vt/PPXOUUCzBRk9MkYUWLL111/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCVCYu/btrmieQ34vt/PPXOUUCzBRk9MkYUWLL111/img.png&quot; data-alt=&quot;원문 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCVCYu/btrmieQ34vt/PPXOUUCzBRk9MkYUWLL111/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCVCYu%2FbtrmieQ34vt%2FPPXOUUCzBRk9MkYUWLL111%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;496&quot; height=&quot;372&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;원문 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일관성 (Consistency)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 노드들이 동일 시간 동일 데이터를 사용자에게 보여줘야 하는 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ACID&lt;/b&gt; 의 'C'는 데이터는 항상 일관성 있는 상태를 유지해야 하고 데이터의 조작 후에도 무결성을 해치지 말아야 한다는 속성이다.&lt;/li&gt;
&lt;li&gt;쓰기 동작이 완료된 후 발생하는 읽기 동작은 마지막으로 쓰여진 데이터를 리턴 해야한다&lt;/li&gt;
&lt;li&gt;Consistency보단 Atomic 원자성이 더 맞는 특징을 나타낸다고 할 수 있다.&lt;/li&gt;
&lt;li&gt;일관성은 동시성, 동일성이라고도 하며 다중 클라이언트 환경에서 같은 시간에 조회하는 데이터는 항상 동일해야한다는 보증을 하는 것을 의미한다.&lt;/li&gt;
&lt;li&gt;RDBMS가 지원하는 가장 기본적인 기능이지만 일관성을 지원하지않는 NoSQL을 사용한다면 데이터의 일관성이 느슨하게 처리되어 동일한 데이터가 나타나지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;느슨하게 처리된다는 것은 데이터의 변경을 시간의 흐름에 따라 여러 노드에 전파하는 것을 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가용성 (Availability)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 노드에 장애가 발생해도 성공적으로 서비스를 지속하는 것&lt;/li&gt;
&lt;li&gt;데이터 저장소에 대한 모든 동작(read, write 등) 항상 성공적으로 리턴되어야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;분할 내성(Partition Tolerance)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파티션 허용, 분산화 가능 물리적인 네트워크 분산 환경에서도 시스템이 잘 작동해야 만족&lt;/li&gt;
&lt;li&gt;Availability 와 차이점은 Availability는 특정 노드가 &quot;장애&quot;가 발생한 상황에 대한 것이고 분할 내성은 노드의 상태는 정상이지만 네트워크 등의 문제로 서로간의 연결이 끊긴 상황에 대한 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DBMS 별 CAP&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CAP 이론에 따르면 분산 시스템 이중 &lt;b&gt;2가지만 만족&lt;/b&gt;할 수 있다. 단지 특정 데이터베이스가 무조건 CA, CP등 확정되지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDBMS는 일반적으로 CA를 만족한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산화 특성 보다는 데이터의 일관성과 가용성에 중점을 둔 형태라고 볼 수 있다. 즉, 시스템의 신뢰성이 높다는 특징을 가지게 된다. 분산 구조를 가져가는 것이 가능하지만 Replication 의 경우 퍼포먼스를 고려해야하고 Sharding의 경우 데이터의 관계가 무너질 수 있는 부담감이 있다. 이러한 경우 신뢰성이 깨지는 리스크가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL의 경우 Master - Slave 관계를 사용할 때 CA를, 클러스터 구성을 할 때 CP를 만족한다. Master - Slave 관계를 사용한다면 Slave중 새로운 마스터를 선택할 때 파티션 허용 오차가 생길 수 있다. 클러스터 구성을 한다면 라이브 노드 개수가 적을 때 클러스터가 종료될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1xmEa/btrmhtARUo4/uz8KAqj1RJk96MwScNXFqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1xmEa/btrmhtARUo4/uz8KAqj1RJk96MwScNXFqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1xmEa/btrmhtARUo4/uz8KAqj1RJk96MwScNXFqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1xmEa%2FbtrmhtARUo4%2Fuz8KAqj1RJk96MwScNXFqk%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;642&quot; height=&quot;294&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;425&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;966&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGUqpd/btrmlW9Iu4r/4RDR2wil4ne2zX9OmKHIik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGUqpd/btrmlW9Iu4r/4RDR2wil4ne2zX9OmKHIik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGUqpd/btrmlW9Iu4r/4RDR2wil4ne2zX9OmKHIik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGUqpd%2FbtrmlW9Iu4r%2F4RDR2wil4ne2zX9OmKHIik%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;672&quot; height=&quot;383&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://deveric.tistory.com/90&quot;&gt;이미지 참고&lt;/a&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;NoSQL은 CP, AP 형태를 가질 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CP , AP 형태를 선호하는 이유 &lt;b&gt;데이터의 신뢰성 보다는 분산에 중점을 둔 방식&lt;/b&gt; 이라 약간의 데이터의 유실, 변형등이 발생할 수 있다 하지만 빠르게 확장을 하여 대규모 데이터를 저장하고 핸들링 할 수 있는 구조를 갖출 수 있도록 한다. 기존의 RDBMS 형태에는 없는 특징을 가져가는 대신 C나 A하나를 포기해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CP형태를 가지는 NoSQL은 MongoDB, Redis등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AP 형태를 가지는 NoSQL은 DynamoDB, cassandra와 CouchDB가 있다. 일관성을 포기하여 데이터의 동기를 보장하지 못하지만 비동기화된 대용량 데이터 처리에 유리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 DB에서 다양한 조합을 살펴보고 CAP 우선순위를 정하여 선택하는 것이 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CAP 이론의 한계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;완벽한 CP , AP 시스템은 사용할 수 없다&lt;/li&gt;
&lt;li&gt;대부분 분산 시스템은 CP와 AP의 중간쯤이다&lt;/li&gt;
&lt;li&gt;모든 분산 시스템이 파티션을 사용하진 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;PACELC 이론&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CAP 이론으로 &lt;b&gt;부족한 부분을 보완&lt;/b&gt;하기 위해 네트워크 장애 상황과 정상 상황을 나누어서 설명하는 이론이다.&lt;/li&gt;
&lt;li&gt;P(네트워크 파티션)상황에서 A(가용성)과 C(일관성)의 상충 관계와 E(else, 정상) 상황에서 L(지연시간)과 C(일관성)의 상충 관계를 설명한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgiBVh/btrmifvDEA1/0b7dpdNyBuaBteXxb44zuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgiBVh/btrmifvDEA1/0b7dpdNyBuaBteXxb44zuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgiBVh/btrmifvDEA1/0b7dpdNyBuaBteXxb44zuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgiBVh%2FbtrmifvDEA1%2F0b7dpdNyBuaBteXxb44zuk%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;285&quot; height=&quot;200&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;390&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;560&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXArel/btrmiEPs1W3/9T7YfrtT0Q6yktf72U8o6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXArel/btrmiEPs1W3/9T7YfrtT0Q6yktf72U8o6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXArel/btrmiEPs1W3/9T7YfrtT0Q6yktf72U8o6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXArel%2FbtrmiEPs1W3%2F9T7YfrtT0Q6yktf72U8o6K%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;508&quot; height=&quot;185&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;204&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;800&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpKqpk/btrmirifbb4/73xXCkzxX4g4rsoUm6XGW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpKqpk/btrmirifbb4/73xXCkzxX4g4rsoUm6XGW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpKqpk/btrmirifbb4/73xXCkzxX4g4rsoUm6XGW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpKqpk%2Fbtrmirifbb4%2F73xXCkzxX4g4rsoUm6XGW1%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;560&quot; height=&quot;280&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PACELC 이론에 따른 NoSQL분류&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pjwn4/btrmmdQ4bT8/SrDJ4y5DwbKyz8Jl7jXTkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pjwn4/btrmmdQ4bT8/SrDJ4y5DwbKyz8Jl7jXTkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pjwn4/btrmmdQ4bT8/SrDJ4y5DwbKyz8Jl7jXTkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPjwn4%2FbtrmmdQ4bT8%2FSrDJ4y5DwbKyz8Jl7jXTkk%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;556&quot; height=&quot;305&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://itwiki.kr/w/PACELC_%EC%9D%B4%EB%A1%A0&quot;&gt;이미지 참고&lt;/a&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를 선택할땐 CAP, PACELC 이론에 맞춰 원하는 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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database/기타</category>
      <category>CAP이론</category>
      <category>PACELC이론</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/81</guid>
      <comments>https://sujl95.tistory.com/81#entry81comment</comments>
      <pubDate>Sat, 27 Nov 2021 18:27:14 +0900</pubDate>
    </item>
    <item>
      <title>DB에 기본값을 줄지 Server단에서 기본값을 줄지?</title>
      <link>https://sujl95.tistory.com/80</link>
      <description>&lt;h1&gt;보시기 전에&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;프로젝트 설계할 때 고민했던 것을 한번 올려본다&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;DB에 기본값을 줄지 entity에 기본값을 줄지?&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;DB에서 테이블을 생성할 때 DEFAULT 값을 지정해서 하는 방법과 entity에서 기본값을 지정하여 QUERY에 입력하는 방법 두가지가 있었는데 이 둘 중 무엇이 더 나은지에 대해 고민을 하다가 찾아보았다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;찾아보기 전에 생각해본것&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;애플리케이션에서 LocalDateTime.now() 와 쿼리 입력될때 now() 의 시간이 다르다&lt;/li&gt;
&lt;li&gt;default를 entity에서 지정해놨는데 코드를 누군가가 수정을해서 default값이 수정되거나 없어진다&lt;br /&gt;default 값이 바뀌었을때 스키마에서는 안바꿔주고 코드에서만 바뀌게 되면 어떻게 될지?&lt;/li&gt;
&lt;li&gt;기본값을 계산해서 입력하는 방식과 계산하지 않고 입력하였을때 애플리케이션에서 처리와 디비에서 처리가 어떤게 나을지?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;DB에서 계산&lt;/h2&gt;
&lt;h3&gt;&lt;b&gt;데이터 선택 및 집계&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;RDBMS는 &lt;b&gt;데이터 처리, 조회 및 집계&lt;/b&gt;를 위해 고도로 최적화 되어있다&lt;/li&gt;
&lt;li&gt;SQL을 사용하여 데이터를 쉽게 그룹화 정렬 필터링 및 집계 할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MIN, MAX, SUM 및 AVG와 같은 집계 함수는 Java 구현보다 매우 편리하고 빠르다&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;데이터를 집계하는 동안 인덱스를 활용하여 디스크 IO의 성능을 미세 조정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;데이터 양&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;대량의 데이터를 처리할 때 탁월한 성능을 제공한다&lt;/li&gt;
&lt;li&gt;애플리케이션에서 유사한 양의 데이터를 처리하려면 메모리 및 CPU처리와 같은 많은 리소스가 필요하다&lt;/li&gt;
&lt;li&gt;대역폭을 절약하려면 데이터베이스에서 데이터 중심 계산을 수행하여 네트워크를 통해 많은 양의 데이터를 전송하지 않도록 하는 것이 좋다&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Application에서 계산&lt;/h2&gt;
&lt;h3&gt;복잡성&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DB와 달리 Java와 같은 고급언어는 복잡한 계산을 처리하는데 잘 갖추어져있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;고급 데이터 분석 및 변환&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DB는 고급 데이터 분석 및 변환에 대한 제한된 지원을 제공한다. 애플리케이션 코드를 사용하여 복잡한 계산을 수행하는 것은 간단하다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;확장성&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;RDBMS는 확장만 가능하므로 데이터베이스 확장성을 달성하는 것은 어려운 작업이 될 수 있다. 그러나 응용 프로그램 코드는 보다 확장 가능한 솔루션을 제공한다&lt;/li&gt;
&lt;li&gt;로드 밸런서를 사용하여 앱 서버를 쉽게 확장하고 많은 요청 처리를 할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Database vs Application&lt;/h2&gt;
&lt;h3&gt;Database&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;데이터 선택, 집계 및 대용량 처리에 선호되는 선택이다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Application&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;복잡성, 고급 데이터 변환, 타사 통합 및 확장성과 같은 요소를 고려할 때 애플리케이션 코드에서 계산을 수행하는 것이 더 나은 후보로 보인다&lt;/li&gt;
&lt;li&gt;고급 언어는 로깅 , 디버깅, 오류 처리 및 단위 테스트 기능과 같은 추가 이점을 제공한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;복잡한 문제를 해결하기 위해 상황에 맞게 혼합하여 활용하는 것이 좋다&lt;/p&gt;
&lt;p&gt;즉, 데이터 선택 및 집계에 데이터베이스를 사용한 다음 유용한 데이터를 애플리케이션에 전송하고 효율적인 상위 수준 언어를 사용하여 복잡한 작업을 수행한다.&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/calculations-in-db-vs-app&quot;&gt;https://www.baeldung.com/calculations-in-db-vs-app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로젝트/기타</category>
      <author>TheWing</author>
      <guid isPermaLink="true">https://sujl95.tistory.com/80</guid>
      <comments>https://sujl95.tistory.com/80#entry80comment</comments>
      <pubDate>Fri, 30 Apr 2021 01:11:53 +0900</pubDate>
    </item>
  </channel>
</rss>