<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>programmer story</title>
    <link>https://minisiri.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 20 Aug 2026 23:06:45 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>미니시리</managingEditor>
    <item>
      <title>[Spring Batch] ItemReader</title>
      <link>https://minisiri.tistory.com/127</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring&amp;nbsp;Batch에서&amp;nbsp;ItemReader는&amp;nbsp;데이터를&amp;nbsp;읽어오는&amp;nbsp;역할을&amp;nbsp;담당하며,&amp;nbsp;데이터&amp;nbsp;읽기&amp;nbsp;방식에는&amp;nbsp;Cursor&amp;nbsp;기반과&amp;nbsp;Page&amp;nbsp;기반의&amp;nbsp;두&amp;nbsp;가지&amp;nbsp;주요&amp;nbsp;옵션이&amp;nbsp;있습니다.&amp;nbsp;이&amp;nbsp;두&amp;nbsp;방식은&amp;nbsp;데이터&amp;nbsp;접근과&amp;nbsp;메모리&amp;nbsp;사용&amp;nbsp;방식에서&amp;nbsp;차이가&amp;nbsp;있으며,&amp;nbsp;각각의&amp;nbsp;특징과&amp;nbsp;사용&amp;nbsp;사례가&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size16&quot;&gt;1. Cursor 기반&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cursor&amp;nbsp;기반&amp;nbsp;접근은&amp;nbsp;데이터베이스&amp;nbsp;커서를&amp;nbsp;열어&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;한&amp;nbsp;개의&amp;nbsp;레코드를&amp;nbsp;순차적으로&amp;nbsp;읽어옵니다.&lt;/p&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;li&gt;데이터베이스 커서가 열려 있는 상태에서 작업을 진행한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;지속적인&amp;nbsp;연결
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스와의&amp;nbsp;연결이&amp;nbsp;작업이&amp;nbsp;완료될&amp;nbsp;때까지&amp;nbsp;유지됩니다. &lt;/li&gt;
&lt;li&gt;커밋 주기에 따라 트랜잭션이 갱신될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;간단한 설정: 대부분의 경우 기본적인 JdbcCursorItemReader 구현으로 충분하다&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용예시&lt;/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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;pre id=&quot;code_1733047886093&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
@StepScope
public JdbcCursorItemReader&amp;lt;MyEntity&amp;gt; cursorItemReader(DataSource dataSource) {
    JdbcCursorItemReader&amp;lt;MyEntity&amp;gt; reader = new JdbcCursorItemReader&amp;lt;&amp;gt;();
    reader.setDataSource(dataSource);
    reader.setSql(&quot;SELECT id, name, value FROM my_table&quot;);
    reader.setRowMapper(new BeanPropertyRowMapper&amp;lt;&amp;gt;(MyEntity.class));
    return reader;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Page 기반&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Page&amp;nbsp;기반&amp;nbsp;접근은&amp;nbsp;데이터를&amp;nbsp;페이지&amp;nbsp;단위로&amp;nbsp;읽어옵니다.&amp;nbsp;한&amp;nbsp;번의&amp;nbsp;데이터베이스&amp;nbsp;호출로&amp;nbsp;여러&amp;nbsp;레코드를&amp;nbsp;가져옵니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배치&amp;nbsp;읽기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스에서&amp;nbsp;설정된&amp;nbsp;크기(페이지&amp;nbsp;크기)만큼&amp;nbsp;데이터를&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;가져옵니다. &lt;/li&gt;
&lt;li&gt;커서 대신 LIMIT과 OFFSET을 사용해 페이징 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;끊어진&amp;nbsp;연결
&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스&amp;nbsp;연결&amp;nbsp;효율적:&amp;nbsp;필요할&amp;nbsp;때만&amp;nbsp;연결을&amp;nbsp;열어&amp;nbsp;데이터를&amp;nbsp;읽음. &lt;/li&gt;
&lt;li&gt;데이터&amp;nbsp;병렬&amp;nbsp;처리&amp;nbsp;가능:&amp;nbsp;페이지를&amp;nbsp;병렬&amp;nbsp;처리하거나&amp;nbsp;캐싱할&amp;nbsp;수&amp;nbsp;있음.&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;페이징&amp;nbsp;쿼리의&amp;nbsp;성능&amp;nbsp;문제:&amp;nbsp;대량&amp;nbsp;데이터에서&amp;nbsp;OFFSET이&amp;nbsp;클&amp;nbsp;경우&amp;nbsp;성능&amp;nbsp;저하. &lt;/li&gt;
&lt;li&gt;더&amp;nbsp;많은&amp;nbsp;메모리&amp;nbsp;필요:&amp;nbsp;한&amp;nbsp;페이지를&amp;nbsp;메모리에&amp;nbsp;로드한&amp;nbsp;후&amp;nbsp;처리.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터&amp;nbsp;양이&amp;nbsp;많거나&amp;nbsp;병렬&amp;nbsp;처리가&amp;nbsp;필요한&amp;nbsp;경우. &lt;/li&gt;
&lt;li&gt;데이터베이스&amp;nbsp;연결&amp;nbsp;유지&amp;nbsp;시간을&amp;nbsp;최소화하려는&amp;nbsp;경우. &lt;/li&gt;
&lt;li&gt;JpaPagingItemReader를&amp;nbsp;활용한&amp;nbsp;엔터프라이즈&amp;nbsp;애플리케이션.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;pre id=&quot;code_1733048023556&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
@StepScope
public JpaPagingItemReader&amp;lt;MyEntity&amp;gt; pagingItemReader(EntityManagerFactory entityManagerFactory) {
    JpaPagingItemReader&amp;lt;MyEntity&amp;gt; reader = new JpaPagingItemReader&amp;lt;&amp;gt;();
    reader.setEntityManagerFactory(entityManagerFactory);
    reader.setQueryString(&quot;SELECT e FROM MyEntity e&quot;);
    reader.setPageSize(100);
    return reader;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.&amp;nbsp;Cursor&amp;nbsp;기반과&amp;nbsp;Page&amp;nbsp;기반의&amp;nbsp;비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 144px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;특징&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Cursor&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기반&lt;/span&gt; &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Page 기반&lt;/span&gt; &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;데이터&lt;span&gt;&amp;nbsp;&lt;/span&gt;처리 단위&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;레코드 단위 처리&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;페이지 단위 처리&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;데이터베이스 연결&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;작업 내내 연결 유지&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;페이지 요청 시 연결&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;메모리 소비&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;낮음&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;높음 (페이지 크기만큼 데이터 유지)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;처리 속도&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;작은 데이터셋에 적합&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;대규모 데이터에 적합&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px; text-align: center;&quot;&gt;병렬 처리&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;어려움&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;가능 (페이지 병렬 처리)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;성능&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;커넥션 안정성이 중요&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;페이징 쿼리 성능이 중요&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;사용 사례&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;연속적인 읽기, 트랜잭션 중요 작업&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;대규모 데이터, 병렬 처리 필요 작업&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.&amp;nbsp;선택&amp;nbsp;기준&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 크기: 데이터가 크다면 Page 기반, 작다면 Cursor 기반&lt;/li&gt;
&lt;li&gt;데이터베이스 연결 시간: 연결을 짧게 유지하고 싶다면 Page 기반&lt;/li&gt;
&lt;li&gt;트랜잭션 요구사항: 트랜잭션이 중요한 경우 Cursor 기반&lt;/li&gt;
&lt;li&gt;성능 요구: 페이징 쿼리가 최적화된 경우 Page 기반이 더 효율적이다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;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;a href=&quot;https://joonfluence.tistory.com/834&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://joonfluence.tistory.com/834&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>개발/Spring</category>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/127</guid>
      <comments>https://minisiri.tistory.com/127#entry127comment</comments>
      <pubDate>Sun, 1 Dec 2024 23:00:44 +0900</pubDate>
    </item>
    <item>
      <title>[React] Life Cycle</title>
      <link>https://minisiri.tistory.com/126</link>
      <description>&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;h2 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 마운트&lt;/b&gt;&lt;/h2&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;constructor : 컴포넌트가 생성될 때 호출&lt;/li&gt;
&lt;li&gt;getDerivedStateFromProps : props에 있는 값을 state에 넣을 때 사용하는 메서드이다. 컴포넌트가 생성될 때와 새로운 props를 받았을 때 호출된다.&lt;/li&gt;
&lt;li&gt;render : 컴포넌트가 렌더링될 때 호출된다. 즉, 우리가 준비한 UI를 렌더링해주는 메서드이다.&lt;/li&gt;
&lt;li&gt;componentDidMount : 컴포넌트가 웹 브라우저상에 나타난 후 호출하는 메서드이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 업데이트&lt;/b&gt;&lt;/h2&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;props의 변경&lt;/li&gt;
&lt;li&gt;state의 변경&lt;/li&gt;
&lt;li&gt;부모 컴포넌트 리렌더링&lt;/li&gt;
&lt;li&gt;this.forceUpdate로 인한 강제 렌더링&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;getDerivedStateFromProps : 컴포넌트가 생성될 때와 새로운 props를 받았을 때 호출된다. props의 변화에 따라 state도 변화를 주고 싶을 때 사용한다.&lt;/li&gt;
&lt;li&gt;shouldComponentUpdate : 컴포넌트가 업데이트되어야 하는지 여부를 결정한다. 이 메서드는 true 혹은 false 값을 반환행야하며, true를 반환하면 라이프사이클 메서드를 계속 실행하고, false를 반환하면 작업을 중지한다. 즉, false일 경우 컴포넌트가 리렌더링 되지 않는다.&lt;/li&gt;
&lt;li&gt;render : 컴포넌트가 리렌더링될 때 호출된다.&lt;/li&gt;
&lt;li&gt;getSnapshotBeforeUpdate : render() 메서드 호출 직후, 실제로 DOM에 반영되기 전에 호출되는 메서드이다.&lt;/li&gt;
&lt;li&gt;componentDidUpdate : 컴포넌트가 업데이트된 후 호출된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 언마운트&lt;/b&gt; &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트는 컴포넌트의 state나 props가 변경될 때 발생하는 단계이다. 이 단계에서는 다음과 같은 메서드가 호출된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;componentWillUnmount : 컴포넌트가 웹 브라우저상에서 사라지기 전에 호출되는 메서드이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메서드 종류&lt;/b&gt;&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;&lt;b&gt;&lt;span&gt;render()&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;&lt;span&gt;render() 메소드는 Class Component의 필수적으로 들어가야 하는 메소드입니다.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React elements&amp;nbsp;일반적으로 JSX를 통해 생성됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div style=&quot;background-color: #1e1e1e; color: #d4d4d4;&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt; (&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;className&lt;/span&gt;=&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;wrap-movie&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;className&lt;/span&gt;=&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;movie-poster&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;MoviePoster&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;poster&lt;/span&gt;=&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;posterUrl&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;className&lt;/span&gt;=&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;box-movie&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;span&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;className&lt;/span&gt;=&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;movie-title&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;className&lt;/span&gt;=&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;movie-genres&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;genres&lt;/span&gt;.&lt;span style=&quot;color: #dcdcaa;&quot;&gt;map&lt;/span&gt;((&lt;span style=&quot;color: #9cdcfe;&quot;&gt;genre&lt;/span&gt;, &lt;span style=&quot;color: #9cdcfe;&quot;&gt;idx&lt;/span&gt;) &lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt; {&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;MovieGenre&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;genres&lt;/span&gt;=&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;genre&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;index&lt;/span&gt;=&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;idx&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;/&amp;gt;&lt;/span&gt;})&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;p&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;className&lt;/span&gt;=&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;movie-synopsis&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;synopsis&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;)&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Array and Fragments&amp;nbsp;Fragments를 사용하면 자식 컴포넌트들을 그룹화 할 수 있다.&amp;nbsp;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://reactjs.org/docs/fragments.html&quot;&gt;Fragments&lt;/a&gt;&amp;nbsp;를 자세히 알아 보실 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Portals&amp;nbsp;Portals 는 부모 Component의 DOM계층 외부에 존재하는 DOM 노드 자식에 렌더링하는 일급 방법을 제공한다. 자세히 알아 보고 싶다면&amp;nbsp;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://reactjs.org/docs/portals.html&quot;&gt;Portals&lt;/a&gt;&amp;nbsp;링크를 참조 하세요.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;String and numbers&amp;nbsp;이들은 텍스트 노드로 DOM에 렌더링 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Booleans or null&amp;nbsp;렌더링되면 안됩니다. 이 둘은 어떠한 판단이나 테스트를 위해 존재하며, 렌더링 될 요소는 아닙니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;추가적으로&amp;nbsp;&lt;b&gt;render()&lt;/b&gt;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;는 순수한 함수입니다. 즉, 입력이 동일하면 출력 또한 항상 동일 하여야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;constructor()&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;State를 초기화 하지 않고 메소드를 바인딩 하지 않는 다면&amp;nbsp;&lt;b&gt;constructor()&lt;/b&gt;를 쓸 필요&amp;nbsp;없습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Component가 마운트 되기전에&amp;nbsp;&lt;b&gt;constructor()&lt;/b&gt;가 먼저 호출 되며 구현 시에는&amp;nbsp;&lt;b&gt;super()&lt;/b&gt;가 다른 구문들 맨 앞에 오게 해야 합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그 이유는 그렇지 않으면&amp;nbsp;&lt;b&gt;this.props&lt;/b&gt;&amp;nbsp;가 정의 되지 않아 오류가 발생 할 수 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;일반적으로 React의 constructor는 두 가지 용도로만 사용됩니다.&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;this.state&amp;nbsp;Oject를&amp;nbsp;통해&amp;nbsp;local&amp;nbsp;state&amp;nbsp;(Component)&amp;nbsp;를&amp;nbsp;초기화&amp;nbsp;합니다. &lt;/li&gt;
&lt;li&gt;이벤트&amp;nbsp;처리기&amp;nbsp;메소드를&amp;nbsp;바인딩&amp;nbsp;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732458177196&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;constructor(props) {
  super(props);
  // Don't call this.setState() here!
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;componentDidMount()&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;div style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;&lt;span&gt;Component가 마운트 된 후 즉시 호출 됩니다. 그러므로 DOM 노드에서 필요한 초기화는 이 메소드에서 진행 되어야 합니다.&lt;/span&gt;&lt;/div&gt;
&lt;pre id=&quot;code_1732458219150&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class App extends Component {
  state = {};


  /**
   * Life cycle of React
   */
  componentDidMount() {
    this._getMovies();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;componentDidUpdate()&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;State가 업데이트 발생 한 직후 호출 됩니다. 초기 렌더링에서는 호출 되지 않습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732458238136&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; componentDidUpdate(prevProps, prevState, snapshot)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1732458243756&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;componentDidUpdate(prevProps) {
    // Typical usage (don't forget to compare props):
    if (this.props.userID !== prevProps.userID) {
      this.fetchData(this.props.userID);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;위 예제처럼 조건을 걸어 두지 않으면 무한 루프에 빠질 수 있습니다. 또는 조건을 제대로 정확하게 걸어 두지 않으면 불필요한 추가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;렌더링이 발생 할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;물론 유저들에게는 잘 보이지 않겠지만 이건 성능상 매우 치명적일 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;componentWillUnm&lt;/span&gt;&lt;span&gt;ount()&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;마운트가 해제되거나 소멸 되기 직전에 호출 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1732458293679&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; componentWillUnmount()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;마운트가 된 컴포넌트에 마운트가 해제 되면서 제거 되거나 정리 되어야 하는 요소들은 이 메소드에 작성 하여 주시면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;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;a href=&quot;https://seungwoohong.tistory.com/m/22&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://seungwoohong.tistory.com/m/22&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/126</guid>
      <comments>https://minisiri.tistory.com/126#entry126comment</comments>
      <pubDate>Sun, 24 Nov 2024 23:25:35 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] SELF_INTRODUCTION&amp;nbsp;CHARACTER&amp;nbsp;VARYING(255) 에러 수정</title>
      <link>https://minisiri.tistory.com/125</link>
      <description>&lt;h2 data-ke-size=&quot;size16&quot;&gt;에러내용&lt;/h2&gt;
&lt;pre id=&quot;code_1731767976336&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception 
[Request processing failed: org.springframework.dao.DataIntegrityViolationException: 
could not execute statement [Value too long for column &quot;SELF_INTRODUCTION CHARACTER VARYING(255)&quot;:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string을 JPA에서 쿼리문으로 변환할 때 VARCHAR(255)로 변환하는데 255자가 넘어가게 저장하면서 발생하는 에러&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결방법&lt;/h2&gt;
&lt;pre id=&quot;code_1731768205227&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Lob
private String textStorage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 발생했던 필드에 @Lob 어노테이션을 추가해주면 쿼리문으로 변환될 때 자동으로 BLOB나 CLOB로 변환해 주면서 에러 발생을 막아줄 수 있습니다.&lt;/p&gt;</description>
      <category>개발/Spring</category>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/125</guid>
      <comments>https://minisiri.tistory.com/125#entry125comment</comments>
      <pubDate>Sat, 16 Nov 2024 23:45:19 +0900</pubDate>
    </item>
    <item>
      <title>진짜 중복과 가짜 중복을 구분하는 기준에 대한 고민</title>
      <link>https://minisiri.tistory.com/124</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발하다가 고민했던 적이 있던걸 타 블로그에서 잘 풀어 설명해주었어서 생각날 때마다 편하게 보기 위해 제 블로그에 적었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을&amp;nbsp;하다&amp;nbsp;보면&amp;nbsp;특정&amp;nbsp;기능에&amp;nbsp;대한&amp;nbsp;변경이&amp;nbsp;다른&amp;nbsp;기능에도&amp;nbsp;영향을&amp;nbsp;주게&amp;nbsp;되고,&amp;nbsp;이러한&amp;nbsp;사이드이펙트로&amp;nbsp;인해&amp;nbsp;시스템에&amp;nbsp;문제가&amp;nbsp;발생하는&amp;nbsp;경우가&amp;nbsp;자주&amp;nbsp;있다.&amp;nbsp;당연하게도&amp;nbsp;문제가&amp;nbsp;생긴&amp;nbsp;기능을&amp;nbsp;빠르게&amp;nbsp;복구하는&amp;nbsp;것이&amp;nbsp;가장&amp;nbsp;중요하겠지만,&amp;nbsp;이러한&amp;nbsp;문제가&amp;nbsp;생긴&amp;nbsp;근본적인&amp;nbsp;원인을&amp;nbsp;찾는&amp;nbsp;것&amp;nbsp;또한&amp;nbsp;중요하다.&amp;nbsp;대게&amp;nbsp;어떠한&amp;nbsp;기능에&amp;nbsp;대한&amp;nbsp;변경이&amp;nbsp;의도하지&amp;nbsp;않은&amp;nbsp;사이드이펙트를&amp;nbsp;유발하는&amp;nbsp;것은&amp;nbsp;&lt;b&gt;단일&amp;nbsp;책임&amp;nbsp;원칙&lt;/b&gt;(&lt;b&gt;SRP&lt;/b&gt;,&amp;nbsp;Single&amp;nbsp;Responsibility&amp;nbsp;Principle)을&amp;nbsp;위반하기&amp;nbsp;때문일&amp;nbsp;것이다. &lt;br /&gt;예를&amp;nbsp;들어&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;배송&amp;nbsp;주소를&amp;nbsp;저장하는&amp;nbsp;ShippingAddress&amp;nbsp;라는&amp;nbsp;클래스가&amp;nbsp;존재하며,&amp;nbsp;ShippingAddress&amp;nbsp;객체는&amp;nbsp;집&amp;nbsp;주소와&amp;nbsp;회사&amp;nbsp;주소를&amp;nbsp;관리하는&amp;nbsp;상황이라고&amp;nbsp;하자.&amp;nbsp;그리고&amp;nbsp;배송&amp;nbsp;주소는&amp;nbsp;255자를&amp;nbsp;넘지&amp;nbsp;말아야&amp;nbsp;한다는&amp;nbsp;정책이&amp;nbsp;존재한다고&amp;nbsp;하면,&amp;nbsp;이러한&amp;nbsp;비즈니스&amp;nbsp;정책에&amp;nbsp;대한&amp;nbsp;검증을&amp;nbsp;객체가&amp;nbsp;생성되는&amp;nbsp;시점에&amp;nbsp;진행할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;pre id=&quot;code_1731164103690&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data class ShippingAddress(
    val homeAddress: String,
    val companyAddress: String,
) {

    init {
        if (isAddressTooLong(homeAddress)) {
            throw IllegalArgumentException(&quot;home address is too Long&quot;)
        }
        
        if (isAddressTooLong(companyAddress)) {
            throw IllegalArgumentException(&quot;company address is too Long&quot;)
        }
    }

    private fun isAddressTooLong(address: String): Boolean {
        return address.length &amp;gt; 255
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때&amp;nbsp;주소지가&amp;nbsp;너무&amp;nbsp;긴지를&amp;nbsp;검사하는&amp;nbsp;isAddressTooLong&amp;nbsp;메서드는&amp;nbsp;2번&amp;nbsp;호출되고&amp;nbsp;있는&amp;nbsp;상황이다.&amp;nbsp;그렇다면&amp;nbsp;isAddressTooLong는&amp;nbsp;SRP를&amp;nbsp;위반하는&amp;nbsp;것일까&amp;nbsp;아닐까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SRP에 대한 사실과 오해&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 먼저 SRP가 무엇이고 어떠한 의미를 지니는지부터 살펴볼 필요가 있다. 로버트 마틴은 SOLID 원칙 중에서 그 의미가 가장 전달되지 못한 원칙으로 SRP(Single Responsibility Principle, 단일 책임 원칙)를 뽑았다. 그리고 SRP는 &amp;ldquo;하나의 일만 해야한다&amp;rdquo;는 의미가 아니라 &quot;단일 모듈은 변경의 이유가 하나, 오직 하나뿐 이여야 한다.&quot;는 것이라고 설명했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 isAddressTooLong 메서드는 변경의 이유가 하나일까? 혹은 변경의 이유가 여러 개라서 SRP를 위반하는 상황일까? 해당 메서드가 SRP를 위반하는지는 비즈니스 정책에 따라 다를 것이다. 만약 집 주소와 회사 주소가 비즈니스적으로 동일한 주소라는 범주에 속하며, 주소지 길이를 1000자로 늘려달라는 요구사항이 들어왔을 때 모두 변경되어야 한다면 SRP를 준수하고 있는 것이다. 반면에 집 주소와 회사 주소가 비즈니스적으로 구분된 영역이며, 집 주소를 1000자로 늘려달라는 요구사항이 개별적으로 들어오는 상황이라면 SRP를 위반하는 상황이라고 볼 수 있다. 이러한 경우에는 다음과 같이 메서드를 분리하는 것이 적합한 상황이 될 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1731165370651&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data class ShippingAddress(
    val homeAddress: String,
    val companyAddress: String,
) {

    init {
        if (isHomeAddressTooLong()) {
            throw IllegalArgumentException(&quot;home address is too Long&quot;)
        }

        if (isCompanyAddressTooLong()) {
            throw IllegalArgumentException(&quot;company address is too Long&quot;)
        }
    }

    private fun isHomeAddressTooLong(): Boolean {
        return homeAddress.length &amp;gt; 255
    }

    private fun isCompanyAddressTooLong(): Boolean {
        return companyAddress.length &amp;gt; 255
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; 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;비즈니스를 고려하지 않고 위의 코드를 보면, 문자열 길이가 255가 넘는지를 검사하는 동일한 로직으로 보일 것이기 때문에 (가짜) 중복으로 해석될 수 있다. 하지만 우리가 작성한 코드들은 비즈니스 가치를 창출하기 위한 코드들이므로, 비즈니스적인 맥락을 고려하지 않으면 안 된다. 이렇듯 비즈니스적으로 다른 정책이며 코드는 비슷하게 보이지만 실질적인 의미가 다른 중복을 가짜 중복, 비즈니스적으로 동일한 정책이라서 중복에 해당하는 부분을 진짜 중복으로 구분할 수 있다. 이렇듯 중복이라는 것은 진짜 중복과 가짜 중복이라는 2가지의 내재적 개념을 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 해당 코드가 여러 개의 변경의 이유를 가지며 SRP를 위반하는지 혹은 중복에 해당하는 코드라서 중복을 제거해야 하는지 등을 판단하려면, 진짜 중복인지 가짜 중복인지를 먼저 구분해야 한다. 그리고 이를 위해서는 작성된 코드의 글자들 그 자체가 아니라 코드 이면의 비즈니스적인 부분도 반드시 함께 고려되어야 한다. 그렇기 때문에 개발자 역시 비즈니스를 잘 이해하고 있어야 불필요한 사이드이펙트를 예방할 수 있고, 비즈니스를 의도대로 문제 없이 구현할 수 있을 것이다.&lt;/p&gt;</description>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/124</guid>
      <comments>https://minisiri.tistory.com/124#entry124comment</comments>
      <pubDate>Sun, 10 Nov 2024 00:19:05 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 가상 스레드를 이용한 부하테스트</title>
      <link>https://minisiri.tistory.com/123</link>
      <description>&lt;h2 data-ke-size=&quot;size16&quot;&gt;Spring&amp;nbsp;Property&amp;nbsp;설정&lt;/h2&gt;
&lt;pre id=&quot;code_1730035901250&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.threads.virtual.enabled=true
server.tomcat.threads.max=1
server.tomcat.threads.min-spare=1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Spring에서 JDK 21을 이용할 때 가상 스레드를 활용하기 위해서는 spring.threads.virtual.enabled=true를 활성화해줍니다.부하테스트를 위해 Thread는 1개로 유지합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 작성&lt;/h2&gt;
&lt;pre id=&quot;code_1730035929465&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/test&quot;)
public String test() throws InterruptedException {
  Thread.sleep(50);
  return &quot;test&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;간단히&amp;nbsp;위와&amp;nbsp;같은&amp;nbsp;Controller&amp;nbsp;Method&amp;nbsp;하나를&amp;nbsp;작성해보고&amp;nbsp;Gatling에&amp;nbsp;Scala&amp;nbsp;코드로&amp;nbsp;동시&amp;nbsp;1000번&amp;nbsp;요청을&amp;nbsp;진행하는&amp;nbsp;방식으로&amp;nbsp;작성한&amp;nbsp;뒤&amp;nbsp;부하&amp;nbsp;테스트를&amp;nbsp;진행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730035941123&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package test
import scala.concurrent.duration._
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.jdbc.Predef._

class RecordedSimulation extends Simulation {

	val httpProtocol = http(&quot;testSimaulation&quot;)
		.get(&quot;http://127.0.0.1:8080/test&quot;)
    	.header(&quot;Client-Version&quot;, &quot;1&quot;)
    
	val scn = scenario(&quot;Scenario1&quot;)
		.exec(httpProtocol)
    
    setUp( 
        scn.inject(atOnceUsers(1000)) 
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;부하 테스트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 기존 Tomcat Thread를 활용한 방식&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBnOmr/btsKk8fAuKv/yHjFIIhcMf7khssix20Vsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBnOmr/btsKk8fAuKv/yHjFIIhcMf7khssix20Vsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBnOmr/btsKk8fAuKv/yHjFIIhcMf7khssix20Vsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBnOmr%2FbtsKk8fAuKv%2FyHjFIIhcMf7khssix20Vsk%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;758&quot; height=&quot;356&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Virtual Thread를 활용한 방식&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btDX6y/btsKk0hsbeM/dqdIqVJ9P4RERd8HL6NiGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btDX6y/btsKk0hsbeM/dqdIqVJ9P4RERd8HL6NiGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btDX6y/btsKk0hsbeM/dqdIqVJ9P4RERd8HL6NiGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtDX6y%2FbtsKk0hsbeM%2FdqdIqVJ9P4RERd8HL6NiGK%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;757&quot; height=&quot;406&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존&amp;nbsp;MVC&amp;nbsp;작업은&amp;nbsp;Tomcat&amp;nbsp;Thread&amp;nbsp;1개가&amp;nbsp;Sleep에&amp;nbsp;들어감에&amp;nbsp;따라&amp;nbsp;Sleep이&amp;nbsp;끝나야만&amp;nbsp;그&amp;nbsp;다음&amp;nbsp;작업이&amp;nbsp;처리&amp;nbsp;되므로&amp;nbsp;1000번째&amp;nbsp;요청인&amp;nbsp;하위&amp;nbsp;99퍼센트&amp;nbsp;요청은&amp;nbsp;55초째에&amp;nbsp;마무리된&amp;nbsp;것을&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp; &lt;br /&gt;(Thread Sleep을 50ms로 줬으니 50ms * 1000 = 50000ms)&amp;nbsp; &lt;br /&gt;하지만 Virtual Thread를 사용한 방식은 하위 99퍼센트의 요청도 0.4초안에 끝나는 것을 확인할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜&amp;nbsp;이런걸까?&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;가상&amp;nbsp;스레드&amp;nbsp;개념은&amp;nbsp;JVM에서&amp;nbsp;사용하는&amp;nbsp;Thread에&amp;nbsp;여러개의&amp;nbsp;가상&amp;nbsp;스레드를&amp;nbsp;붙인&amp;nbsp;뒤&amp;nbsp;kernel에&amp;nbsp;넘겨서&amp;nbsp;사용하는&amp;nbsp;개념입니다.&amp;nbsp; &lt;br /&gt;그러므로 가상 스레드는 OS Thread 내에서 실행됩니다.&amp;nbsp; &lt;br /&gt;하지만 가상 스레드에서 실행되는 코드가 블로킹 I/O 작업을 호출하면 자바 런타임은 다시 시작될 수 있을 때까지 가상 스레드를 일시 중지합니다.&amp;nbsp; &lt;br /&gt;그럼 OS Thread와 연결 됐었던 가상 스레드는 중지 상태에 돌아가지만 OS Thread는 다른 작업을 처리할 수 있는 구조라서 위와 같은 결과를 볼 수 있다고 보면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;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;a href=&quot;https://stir.tistory.com/478&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stir.tistory.com/478&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/123</guid>
      <comments>https://minisiri.tistory.com/123#entry123comment</comments>
      <pubDate>Sun, 27 Oct 2024 23:00:35 +0900</pubDate>
    </item>
    <item>
      <title>[Nginx] 특정 ip 허용/차단</title>
      <link>https://minisiri.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 허용된 ip만 접속에 권한을 주려면 /etc/nginx&amp;nbsp;폴더&amp;nbsp;하위에&amp;nbsp;default.conf&amp;nbsp;를&amp;nbsp;열어&amp;nbsp;아래와&amp;nbsp;같이&amp;nbsp;ip를&amp;nbsp;허용하거나&amp;nbsp;차단하면된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728784003407&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
  ...
  
  location / {
    ...
    
    include 111.222.333.444;
    deny 444.333.222.111;
    
  }
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;include&lt;/b&gt; 나 &lt;b&gt;deny&lt;/b&gt; 를 사용해서 허용/차단을 할 수 있고 &lt;b&gt;all&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;허용 또는 차단해야 할 ip 가 많을 경우 따로 파일로 빼서 관리를 해도 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1728784110566&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vi allow_ip

allow 123.123.123.123;
allow 321.321.321.321;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728784119485&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
  ...
  
  location / {
    ...
    
    include conf.d/allow_ip;
    deny all;
    
  }
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;allow_ip 라는 파일에 ip 리스트업을 하고 conf 파일을 위와 같이 재작성 했다.&lt;br /&gt;접근 허용한 ip 외에는 모두 거절하겠다고 선언하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 방화벽이 있는 경우, 해당 서버를 방화벽에 등록하여&amp;nbsp; http, https 접근을 허용해주고&lt;br /&gt;해당 ip만 접속되는지 확인해 보면 된다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;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;a href=&quot;https://jong-bae.tistory.com/108&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jong-bae.tistory.com/108&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/122</guid>
      <comments>https://minisiri.tistory.com/122#entry122comment</comments>
      <pubDate>Sun, 13 Oct 2024 23:00:43 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] Event 버블링과 캡처링</title>
      <link>https://minisiri.tistory.com/121</link>
      <description>&lt;h2 data-ke-size=&quot;size16&quot;&gt;이벤트 버블링이란&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;text-align: start;&quot;&gt;자바스크립트에서 이벤트가 발생한&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;b&gt;자식 요소&lt;/b&gt;에서 시작해&amp;nbsp;&lt;b&gt;부모 요소&lt;/b&gt;로&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;전파되는 방식이다. 물 속에서 발생한 기포 방울(Bubble)이 아래에서 위로 올라가는 이미지를 연상하면 쉽다. 즉, 가장 구체적인 요소에서 이벤트가 발생하면 그 이벤트가 상위 요소들로 차례차례 전달된다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;예를 들어, &amp;lt;div&amp;gt; 안에 있는 &amp;lt;button&amp;gt;을 클릭하면, 해당 버튼에 연결된 이벤트가 먼저 실행되고, 그 이벤트는 부모 요소인 &amp;lt;div&amp;gt;로 전파된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728096604590&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;parent&quot;&amp;gt;
  &amp;lt;button id=&quot;child&quot;&amp;gt;Click Me&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
  document.getElementById('child').addEventListener('click', () =&amp;gt; {
    console.log('버튼 클릭됨');
  });

  document.getElementById('parent').addEventListener('click', () =&amp;gt; {
    console.log('부모 div 클릭됨');
  });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;이 코드를 실행하면 버튼을 클릭할 때, &quot;버튼 클릭됨&quot;과 &quot;부모 div 클릭됨&quot;이 차례대로 출력된다. 이는 이벤트가 자식 요소에서 부모 요소로 전파되기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트 캡처링이란&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;b&gt;이벤트 캡처링(Event Capturing)&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;은 버블링과 반대되는 개념이다. 이벤트가&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;b&gt;부모 요소&lt;/b&gt;에서 시작해&amp;nbsp;&lt;b&gt;자식 요소&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;로 전달된다. 자바스크립트에서는 기본적으로 이벤트 버블링이 먼저 일어나지만, 캡처링 단계를 사용하려면 addEventListener 메서드의 세 번째 인자로 true를 전달하면 된다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1728096639681&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;parent&quot;&amp;gt;
  &amp;lt;button id=&quot;child&quot;&amp;gt;Click Me&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
  document.getElementById('parent').addEventListener('click', () =&amp;gt; {
    console.log('부모 div 클릭됨');
  }, true);

  document.getElementById('child').addEventListener('click', () =&amp;gt; {
    console.log('버튼 클릭됨');
  });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;이 코드를 실행하면 버튼을 클릭했을 때 &quot;부모 div 클릭됨&quot;이 먼저 출력되고, 그 다음에 &quot;버튼 클릭됨&quot;이 출력된다. 이는 캡처링 단계에서 부모 이벤트가 먼저 처리되기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;차이점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;버블링과 캡처링은 이벤트가 전파되는 순서에 따라 구분된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;버블링&lt;/b&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;은 자식 요소에서 부모 요소로 이벤트가 전달되는 방식이고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;캡처링&lt;/b&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;은 부모 요소에서 자식 요소로 이벤트가 전달된다. 자바스크립트는 기본적으로 버블링 방식을 사용하지만, addEventListener의 옵션을 통해 캡처링을 적용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;혼합 사용 예시&lt;/h2&gt;
&lt;pre id=&quot;code_1728096671204&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;outer&quot;&amp;gt;
  &amp;lt;div id=&quot;inner&quot;&amp;gt;
    &amp;lt;button id=&quot;btn&quot;&amp;gt;Click Me&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
  document.getElementById('outer').addEventListener('click', () =&amp;gt; {
    console.log('외부 div 클릭됨');
  }, true); // 캡처링

  document.getElementById('inner').addEventListener('click', () =&amp;gt; {
    console.log('내부 div 클릭됨');
  });

  document.getElementById('btn').addEventListener('click', () =&amp;gt; {
    console.log('버튼 클릭됨');
  });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;버튼을 클릭하면 &quot;외부 div 클릭됨&quot;이 먼저 출력되고, 그 다음에 &quot;버튼 클릭됨&quot;, 마지막으로 &quot; &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;내부 div&lt;/span&gt;&amp;nbsp;클릭됨&quot;이 출력된다. 이처럼 캡처링과 버블링이 차례로 일어나며 이벤트가 전파되는 과정을 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;참고&lt;/span&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;span style=&quot;color: #555555;&quot;&gt;&lt;a href=&quot;https://joon2974.tistory.com/entry/JavaScript-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81%EA%B3%BC-%EC%BA%A1%EC%B2%98%EB%A7%81&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://joon2974.tistory.com/entry/JavaScript-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81%EA%B3%BC-%EC%BA%A1%EC%B2%98%EB%A7%81&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;</description>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/121</guid>
      <comments>https://minisiri.tistory.com/121#entry121comment</comments>
      <pubDate>Sun, 6 Oct 2024 23:00:03 +0900</pubDate>
    </item>
    <item>
      <title>[Mysql] Prepared Statement</title>
      <link>https://minisiri.tistory.com/120</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;Prepared statement는 orm 도구에서 많이 사용되는 것으로 binding query라고도 불린다. 이는 하기와 같이 값이 바인딩 되는 변수에 물음표를 사용해서 SQL 문장을 작성하고 prepare 단계를 먼저 수행한 후 변수의 값을 바인딩해서 쿼리를 실행하는 형태이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727611968628&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pstmt = connection.prepareStatement(
  &quot;select * from exam_tab where id = ?&quot;
);
pstmt.setInt(1, 1234);
rs = pstmt.executeQuery();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;이처럼 prepared statement를 사용하여 쿼리를 작성하는 경우 빠르고 간단하게 sql injection 방지 처리를 구현할 수 있다. 그리고 쿼리 파싱 비용을 줄인다. 예를 들어 동일한 prepared statment 객체를 이용하는 경우 1번째 실행에서 쿼리의 parse-tree를 생성해서 cache하기 때문에 2번째 실행 이후부터는 쿼리 파싱 비용이 감소하는 것이다.(execution-plan은 cache되지 않음.) 하지만 parse-tree를 caching하는 메모리 공간이 추가로 필요하기 때문에 메모리 사용량이 조금 더 높아지고 1번째 쿼리 실행 시에는 실제 쿼리 실행 이전에 prepared statment 단계가 필요하기 때문에 mysql 서버로부터 통신이 한 번 더 필요해진다.(network round-trip)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MySQL&amp;nbsp;Server에서의&amp;nbsp;Prepared&amp;nbsp;Statment&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;mysql server는 cache된 prepared statment는 하나의 커넥션 내에서만 공유가 되기 때문에 각각 100개의 커넥션에서 동일한 쿼리를 수행해도 prepared statment 객체는 1개가 아닌 100개 필요하다. 그리고 mysql server의 prepared statment는 클라이언트 측과 서버 측 prepared statment로 구분이 된다. 둘 모두 sql injection을 예방하는 효과는 있지만 client-side prepared statment는 실제 생각하는 prepared statment 기능과는 다르다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;mysql server의 java connection에서 실질적인 prepared statment인 server-side prepared statment는 기본값으로 비활성화 되어 있다. 즉 useServerPreStmts 값을 true로 활성화해야 사용할 수 있게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MySQL에서&amp;nbsp;PreparedStatment와&amp;nbsp;ConnectionPool의&amp;nbsp;관계&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;mysql server의 preparedStatment는 하나의 Connection 내에서만 공유된다. 즉 하나의 connection 안에서만 preparedStatment Cache가 재사용될 수 있는 것이다. 때문에 connection이 많으면 많을수록 더 많은 preparedStatment 객체가 필요해진다. 예를 들어 mysql server의 전체 connection 갯수가 1,000개이고 unique한 query pattern이 100개를 프로그램에서 사용한다고 가정하면 mysql server에서는 약 100,000개 정도의 preparedStatment 객체를 메모리에 캐시해야 한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;또 mysql server는 약 16,000개 정도의 preparedStatment까지만 캐시할 수 있도록 설정되어 있다. 즉 16,000개의 캐시를 넘어서면 mysql server는 LRU 패턴을 이용해 캐시를 계속 정리하게 되고 클라이언트에서는 다시 preparedStatment 함수를 호출해서 쿼리 파싱 요청을 하게 된다. 그리고 connection pool이 자주 새로 생성되면 생성될수록 새로 파싱을 해야하기 때문에 쿼리 파싱 비용도 계속 높아질 것이다. 따라서 connection 갯수를 최소화하고 connecton 생명 주기를 최대한 길게 가져가야 preparedStatment가 실질적인 도움을 줄 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;결론적으로 connection이 많아지고 쿼리 패턴이 다양해지면 cache eviction이 발생하고 쿼리 파싱 횟수는 계속 증가하게 되므로 적절히 쿼리 파싱 횟수랑 preparedStatment 캐시 횟수를 확인해서 적절한 Max preparedStatment Count 변수를 설정하는 것이 필요하다. 그리고 쿼리가 복잡하면 복잡할수록 파싱 비용을 절약할 수 있는 preparedStatment가 도움이 되겠지만 일반적인 OLTP 환경은 쿼리들이 단순하기 때문에 preparedStatment의 장점이 줄어들 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;server-side preparedStatment는 예상한 것처럼 성능을 크게 높여주지 않는다. 오히려 메모리 사용량만 높이고 필요한 preparedStatment 갯수 대비 max preparedStatment count가 너무 부족하면 쿼리 파싱 효율도 많이 떨어진다.&amp;nbsp; 이처럼 mysql server에서는 server-side preparedStatment가 부작용이 심한 경우가 많다. 하지만 client-side preparedStatment는 server-side의 부작용을 가지지 않으면서 sql injection을 예방할 수 있는 효과도 있다.&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/데이터베이스</category>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/120</guid>
      <comments>https://minisiri.tistory.com/120#entry120comment</comments>
      <pubDate>Sun, 29 Sep 2024 23:00:37 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] Ubuntu - OOM(Out Of Memory) 메모리 부족과 대응</title>
      <link>https://minisiri.tistory.com/119</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;OOM은 시스템 메모리 부족 상태를 의미하며, 서버에서 가용 가능한 메모리가 없는 상태를 뜻한다. 해당 장애가 발생했을 때 무선운 점은 모든 프로세스 (OS, APP)가 멈출뿐 아니라, 대응하기도 어렵다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기본적으로 Linux 계열의 OS에서는 이러한 OOM 장애를 방지하기 위해 하나의 매커니즘이 동작하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;- OOM Killer&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;OOM Killer 는 Linux 커널이 메모리 부족상황에서 시스템을 안정적으로 유지하기 위해 Memory 를 회수하는 프로세스이다.&lt;br /&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;시스템 메모리가 부족하여 새로 메모리를 할당할 수 없는 상황에서 실행되게 된다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;OOM Killer 의 프로세스&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: circle; color: #555555; text-align: start;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li style=&quot;list-style-type: circle; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메모리를 많이 사용하거나, 중요도가 낮은 프로세스를 선택, 강제종료&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로세스가 종료되어 메모리가 해제되면, 다른 프로세스가 사용할 수 있게 함.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;System 의 중요한 프로세스는 보호된다(sshd 등)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉&amp;nbsp;&lt;b&gt;메모리 사용량, 프로세스 우선순위, oom_scope(값이 높을수록 OOM Killer 의 종료대상, 프로세스마다 보유)&lt;/b&gt;&amp;nbsp;해당 대상을 통해 OOM Killer 의 종료대상이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 메모리 사용량 확인&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1727012901060&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 전체, 사용, 캐시, 스왑 메모리 확인
$ free -h 


# CPU/ MEM 동시 실시간 확인
$ top

// free -h 커맨드는 단순 사용량, 가용량 들을 한눈에 파악하기 쉽고
// top 커맨드의 경우 사용량이 높은 프로세스나 더 자세한 내역을 확인하기 좋다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;3&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. Cache/buff 메모리 사용량이 높은 경우&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Cache/buff 메모리 사용량이 높고 자주 사용되고 있다면, 자주 사용하는 프로그램, 파일등에 메모리&amp;nbsp;&lt;b&gt;접근 속도를 높일 수 있다&lt;/b&gt;는 장점이 존재하지만, 다른 프로세스들이 메모리 사용률을 확보하지 못하거나 OOM 장애가 발생할 수 있는 여지가 존재한다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Cache/buff 메모리를 해제시켜주면 top 커맨드 사진처럼 가용가능한 메모리 자원이 생기기 때문에, &lt;b&gt;일정 주기로 Cache/buff 를 비워주는것이 좋다고 판단했다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2-1. Cache/buff 메모리 clear&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Cache/buff 메모리를 정리하기 위해서는 2가지의 커맨드가 필요하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727012997988&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 메모리에서 disk 로 저장 &amp;amp; 동기화
$ sync


# Cache/buff 메모리 정리
$ sysctl -w vm.drop_caches=3&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Linux 의 작업대상이 되는 데이터들은, System Memory 에서 작업이 이루어지기 때문에, 작업대상은 Memory 에 존재하게 된다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;따라서 별도의 저장이 없거나, 중간에 시스템이 다운되는 현상이 발생하게 되면, 저장되지 않은 작업대상들은 사라진다.&lt;br /&gt;&lt;/b&gt;이런 상황을 막기위해 Memory 의 작업대상 및 내역을 Disk 에 동기화(저장) 작업을 우선적으로 실행 해야 한다.(Flushing)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 2번째 명령어는 파일 시스템의 PageCache, dentries, inodes 캐시를 모두 해제하는 명령어 이다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #555555; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;PageCahce&lt;/b&gt;: 파일 데이터 캐시&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;inodes&lt;/b&gt;: 파일 시스템에서 파일에 대한 메타데이터 저장 구조체, 파일의 속성, 위치 정보를 담고있다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal; color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;dentries&lt;/b&gt;: 디렉토리 Entry Cache, 파일명과 inodes 를 매핑하는 역할.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1727013026223&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vm.drop_caches=3 작업의 경우 위의 3가지 항목을 해제 하게 되며, 숫자 옵션의 경우 아래와 같다.

drop_caches=1: PageCache 해제
drop_caches=2: dentries, inodes 해제
drop_caches=3: PageCache, dentries, inodes 모두 해제&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2-2. 위의 작업 한번에 진행하기&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2번의 명령어를 사용하지 않고, 한번에 진행하면 더욱 편리할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727013037370&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sync &amp;amp;&amp;amp; echo3 &amp;gt; /proc/sys/vm/drop_caches&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. Crontab 으로 주기적 Cache/buff 해제&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 너무 잦은 Cache/buff Clear 는 오히려 시스템의 성능에 영향을 미칠 수 있는 항목이기 때문에 일정, 특정 주기로 해당 작업을 자동화 하고 싶을 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; Linux 의 Crontab 기능을 사용하여 자동화 시킬 수 있다.(스케줄링) &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727013069581&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# CronTab 설정
$ crontab -e


# 매주 일요일 1시에 실행 - 분 시 일 월 요일
$ 0 1 * * 0 sync &amp;amp;&amp;amp; echo3 &amp;gt; /proc/sys/vm/drop_caches


# sudo 권한으로 실행 시키고 싶은 경우
$ sudo crontab -e

$ 0 1 * * 0 /bin/sync &amp;amp;&amp;amp; /sbin/sysctl -w vm.drop_caches=3&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵션의 경우 주석의 예시를 보고 설정 해주면 된다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;crontab 작성후 필히 저장해야한다.&lt;br /&gt;&lt;/b&gt;crontab 의 경우 기본적으로&amp;nbsp;&lt;b&gt;nano 편집기를 사용&lt;/b&gt;하여 작성이 된다./&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;vi, vim 의 사용법이 더욱 익숙한 사람들은 아래처럼 하면된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727013090484&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 한번만 vim 으로 실행
$ EDITOR=vim crontab -e


# 기본 편집기를 vim 으로 설정
$ export VISUAL=vim
$ export EDITOR=vim&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; 이후 크론탭이 정상적으로 등록 되었는지 확인 하게 되면 끝난다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727013100289&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# CronTab list 
$ crontab -l

# Sudo
$ sudo crontab -l&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2YffB/btsJHzSei6f/Qhxul6WgEUAvW88nc9BC51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2YffB/btsJHzSei6f/Qhxul6WgEUAvW88nc9BC51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2YffB/btsJHzSei6f/Qhxul6WgEUAvW88nc9BC51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2YffB%2FbtsJHzSei6f%2FQhxul6WgEUAvW88nc9BC51%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;1232&quot; height=&quot;94&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;94&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;span style=&quot;color: #000000; text-align: start;&quot;&gt; 해당 Crontab 이 실행됬는지 확인 하려면 log 로 확인도 가능하다.&amp;nbsp; &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727013118770&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ grep CRON /var/log/syslog


# 혹은 
grep drop_caches /var/log/syslog&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;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;a href=&quot;https://lucas-owner.tistory.com/76&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://lucas-owner.tistory.com/76&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>개발/리눅스</category>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/119</guid>
      <comments>https://minisiri.tistory.com/119#entry119comment</comments>
      <pubDate>Sun, 22 Sep 2024 23:00:10 +0900</pubDate>
    </item>
    <item>
      <title>NAT란</title>
      <link>https://minisiri.tistory.com/118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;NAT는 네트워크 주소 변환(Network Address Translation)의 줄임말 입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;NAT를 사용하는 이유는 주로 사설 네트워크에 속한 여러 호스트가 하나의 공인 IP 주소를 사용하여 인터넷에 접속하기 위해서 입니다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;IP에는 퍼블릭 IP와 프라이빗 IP가 있는데 이것들은 IPv4 주소의 낭비를 막고 공인 인터넷을 사용하지 않아도 되는 엔드포인트들에게 어떤 망이든 중복 사용이 가능한 IP를 주기 위해서 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1726403032170&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;10.0.0.0 ~ 10.255.255.255
172.16.0.0 ~ 172.31.255.255
192.168.0.0 ~ 192.168.255.255&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;위의 IP 주소들은 프라이빗 네트워크에서 사용 가능한 IP 대역이다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;같은 공간에 있는 공유기에 연결된 단말들이 사설 IP를 보유하게 됩니다. 그리고 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;사설 IP를 출발지 주소로 유지한 상태로 외부 인터넷으로 나갈 수 없기 때문에&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt; 인터넷은 공인 IP로만 통신이 가능합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;프라이빗 IP가 외부로 나가기 위해선 공유기가 프라이빗 IP를 가지고 있는 내 컴퓨터의 IP를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;공유기 자신의 공인 IP&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;변환&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;하게 됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;공유기를 지나서 외부 인터넷으로 나아갈 때 공유기의 공인 IP를 가지고 원하는 목적지 주소로 향하는 것이고 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;이것이 우리가 주로 사용하는 NAT이다.&lt;/span&gt; &lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;더 이해하기 쉽게 NAT 통신 과정을 아주 간단하게 시각적으로 설명해보면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 내부 네트워크에서 요청 시작&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;내부 장치 &amp;gt; 컴퓨터 A가 있다고 가정&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;게이트웨이 &amp;gt; A 컴퓨터는 내부 IP 주소를 가진 공유기로 요청을 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;A&amp;nbsp;컴퓨터&amp;nbsp;(내부&amp;nbsp;IP:&amp;nbsp;192.168.1.2)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;요청&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;게이트웨이&amp;nbsp;(공유기)&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;2. NAT 변환&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;NAT&amp;nbsp; 장비: 공유기는 A 컴퓨터의 내부 IP 주소를 기록하고 이를 공인 IP 주소로 변환한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;변환 테이블: NAT 장비는 내부 IP를 공인 IP와 매핑한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;A 컴퓨터 (내부 IP: 192.168.1.2)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;변환&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;게이트웨이 (공인 IP: 203.0.113.5)&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;3. 외부 네트워크로 요청 전송&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;요청 전송: NAT 장비는 변환된 공인 IP 주소로 외부 인터넷에 요청을 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;외부 서버 (예: 위키피디아)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;요청&amp;nbsp;수신&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;공인 IP: 203.0.113.5&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;4. 서버 응답&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;서버 응답: 외부 서버가 요청을 처리하고 응답을 NAT 장비의 공인 IP 주소로 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;외부&amp;nbsp;서버&amp;nbsp;(응답)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;응답&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;공인 IP: 203.0.113.5&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;5. NAT 장비에서 응답 변환&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;응답 수신: A 컴퓨터가 응답을 받아서 요청한 웹 페이지를 표시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;&amp;nbsp;게이트웨이 (공인 IP: 203.0.113.5)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;변환&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;A 컴퓨터 (내부 IP: 192.168.1.2)&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;6. 내부 네트워크에서 응답 수신&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;응답 수신: A 컴퓨터가 응답을 받아서 요청한 웹 페이지를 표시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;A&amp;nbsp;컴퓨터&amp;nbsp;(내부&amp;nbsp;IP:&amp;nbsp;192.168.1.2)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;응답&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;게이트웨이&amp;nbsp;(공유기)&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;종합적 그림&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;내부&amp;nbsp;네트워크&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NAT&amp;nbsp;장비&amp;nbsp;(공유기)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;외부&amp;nbsp;네트워크&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;A&amp;nbsp;컴퓨터&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[192.168.1.2]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;외부&amp;nbsp;서버&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;(192.168.1.2)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;변환&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[203.0.113.5]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;응답&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;요청&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr;&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[203.0.113.5]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;응답&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;변환&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr;&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;A 컴퓨터 (192.168.1.2:12345)&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;NAT%EC%9D%98%20%EC%A3%BC%EC%9A%94%20%ED%98%95%ED%83%9C-1&quot; style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://hskhsmm.tistory.com/entry/what-is-nat#NAT%EC%9D%98%20%EC%A3%BC%EC%9A%94%20%ED%98%95%ED%83%9C-1&quot;&gt;NAT의 주요 형태&lt;/a&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;1. Static NAT:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Static NAT는 내부 네트워크의 특정 장치에 대해 고정된 공인 IP 주소를 매핑한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;서버와 같이 외부에서 특정 IP 주소로 접근해야 하는 장치에 사용된다.&amp;nbsp;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;예를 들면 회사의 웹 서버를 외부에서 접근 가능하게 하려는 경우에 유용하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;2. Dynamic NAT:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Dynamic NAT는 내부 네트워크의 사설 IP 주소를 공인 IP 주소 풀에서 동적으로 할당한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;내부 장치들이 외부 네트워크와 연결할 때 공인 IP 주소를 동적으로 할당받아 사용한다,&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;공인 IP 주소가 제한적일 때 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;3. PAT:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;여러 내부 장치가 하나의 공인 IP 주소를 공유할 수 있도록 하며 포트 번호를 통해 트래픽을 구분한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;가정이나 소규모 사무실 네트워크에서 흔히 사용된다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;내부 네트워크의 장치들이 동일한 공인 IP 주소를 사용하되 포트 번호를 통해 각 장치의 트래픽을 구분한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;NAT%EC%9D%98%20%EC%9E%A5%EB%8B%A8%EC%A0%90-1&quot; style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://hskhsmm.tistory.com/entry/what-is-nat#NAT%EC%9D%98%20%EC%9E%A5%EB%8B%A8%EC%A0%90-1&quot;&gt;NAT의 장단점&lt;/a&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;장점:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;IP 주소 절약&lt;/b&gt;: 여러 장치가 하나의 공인 IP 주소를 공유함으로써 공인 IP 주소를 절약할 수 있다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;보안&lt;/b&gt;: 외부 네트워크에서 내부 네트워크의 사설 IP 주소를 직접 접근할 수 없게 하여 보안성을 높인다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;유연성&lt;/b&gt;: 내부 네트워크의 IP 주소를 변경해도 외부에서 접근하는 공인 IP 주소는 변경할 필요가 없어 관리가 용이하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;단점:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;트래픽 감시와 모니터링의 어려움&lt;/b&gt;: NAT는 패킷을 변환하므로, 네트워크 트래픽을 모니터링하고 분석하는 데 어려움이 있을 수 있다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;P2P와 일부 애플리케이션의 문제&lt;/b&gt;: NAT가 적용된 네트워크에서는 피어 투 피어 연결이나 일부 애플리케이션에서 문제가 발생할 수 있다.(P2P 연결에서는 두 장치가 직접적으로 서로의 IP 주소와 포트 번호를 알고 있어야 한다. NAT에선 그럴 수 없으므로 직접적인 P2P 연결이 어렵다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;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;a href=&quot;https://hskhsmm.tistory.com/entry/what-is-nat&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hskhsmm.tistory.com/entry/what-is-nat&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>미니시리</author>
      <guid isPermaLink="true">https://minisiri.tistory.com/118</guid>
      <comments>https://minisiri.tistory.com/118#entry118comment</comments>
      <pubDate>Sun, 15 Sep 2024 23:00:17 +0900</pubDate>
    </item>
  </channel>
</rss>