<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>HELLO RUNA!</title>
    <link>https://runa-nam.tistory.com/</link>
    <description>천방지축 어리둥절 빙글빙글 아무튼 돌아가는.</description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 16:12:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>R55</managingEditor>
    <image>
      <title>HELLO RUNA!</title>
      <url>https://tistory1.daumcdn.net/tistory/3916269/attach/a8e74868b9284a94bf461e37d21ba908</url>
      <link>https://runa-nam.tistory.com</link>
    </image>
    <item>
      <title>InnoDB 스토리지 아키텍처</title>
      <link>https://runa-nam.tistory.com/133</link>
      <description>&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MySQL 서버는 기본적으로 사람의 머리 역할을 담당하는 MySQL 엔진과 손발 역할을 담당하는 스토리지 엔진으로 구분할 수 있다. 그중 스토리지 엔진의 경우 InnoDB와 MyISAM이 있는데, InnoDB를 8.0 이후부터는 기본으로 제공하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;따라서 대체로 InnoDB를 거의 사용하게 되는데, 해당 아키텍처에 대해서는 자세히 알지 못하고 사용하는 경우가 많다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;InnoDB 스토리지 엔진의 특성&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;먼저, InnoDB가 가지고 있는 특성에 대해서 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;프라이머리 키에 의한 클러스터링&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;InnoDB의 모든 테이블은 기본적으로 PK를 기준으로 클러스터링되어 저장된다. (물리적으로)&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;따라서, PK를 사용한 레인지 스캔은 상당히 빨리 처리될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모든 세컨더리 인덱스는 레코드의 주소 대신 PK의 값을 논리적인 주소로 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MyISAM 스토리지 엔진에서는 클러스터링 키를 지원하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;외래 키 지원&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MyISAM이나 MEMORY 테이블에서는 지원하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;InnoDB에서는 부모 테이블과 자식 테이블 모두 해당 칼럼에 인덱스 생성이 필요하고, 변경 시에는 반드시 부모 테이블이나 자식 테이블에 데이터가 있는지 체크하는 작업이 필요하므로 잠금이 여러 테이블로 전파되고, 이로 인해 데드락이 발생할 때가 많으므로 개발할 때도 외래 키의 존재에 주의하는 것이 좋다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;MVCC (Multi Version Concurrency Control)&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반적으로 레코드 레벨의 트랜잭션을 지원하는 DBMS가 제공하는 기능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;가장 큰 목적 : 잠금을 사용하지 않는 일관적인 읽기 제공&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;InnoDB는 언두로그를 이용해 해당 기능을 구현한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Multi Version이라는 뜻은 하나의 레코드에 대해 여러 개의 버전이 동시에 관리된다는 의미.&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉, 하나의 레코드에 대한 여러 개의 언두 로그를 유지하고 있으며 자연히 언두 영역이 저장되는 시스템 테이블스페이스의 공간이 많이 늘어나는 상황이 발생할 가능성이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;언두 영역을 삭제하는 기준은 해당 언두 영역을 필요로 하는 트랜잭션이 더는 없을 시기를 말한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;잠금 없는 일관된 읽기 (Non-Locking Consistent Read)&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 MVCC를 통하여 구현하는 기능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;잠금을 걸지 않기 때문에 InnoDB에서 순수한 SELECT 작업은 다른 트랜잭션의 변경 작업과 관계없이 항상 잠금을 대기하지 않고 바로 실행된다. (SERIALIZABLE 격리 수준 제외)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;자동 데드락 감지&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;InnoDB 스토리지 엔진은 데드락 감지 스레드를 가지고 있어 주기적으로 감지하여 그중 하나를 강제로 종료한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;종료되는 스레드는 롤백이 가장 용이한(복구 작업이 가장 적은) 스레드로, 언두 로그 레코드를 더 적게 가진 트랜잭션이 일반적으로 롤백의 대상이 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;자동화된 장애 복구&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;InnoDB에는 손실이나 장애로부터 데이터를 복구하기 위한 여러 매커니즘이 존재한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 기본적으로 InnoDB 스토리지는 매우 견고하여 거의 이슈가 발생하지 않는다.&lt;/span&gt;&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;size26&quot;&gt;&lt;b&gt;언두 로그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;InnoDB 스토리지 엔진은 트랜잭션과 격리 수준을 보장하기 위해 DML(INSERT, UPDATE, DELETE)로 변경되기 전 데이터를 별도로 백업한다. 이렇게 백업된 데이터를 언두 로그라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;언두 로그의 사용처&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션 보장&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션이 롤백되었을 시 데이터를 변경 전 데이터로 롤백하기 위해 사용된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;격리 수준 보장&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;특정 커넥션에서 데이터를 변경하는 도중 다른 커넥션에서 데이터를 조회하면 트랜잭션 격리 수준에 맞게 변경 중인 레코드를 읽기 않고 언두 로그에 백업해둔 데이터를 읽어 반환한다.&lt;/span&gt;&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;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;InnoDB와 MyISAM, MEMORY 스토리지 엔진 비교&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;vs MyISAM&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지금까지는 MyISAM이 기본 스토리지 엔진인 경우가 많았다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MySQL 5.5부터 InnoDB가 기본으로 채택되었으나 일부는 MyISAM을 사용하고 있었고, 8.0부터 모든 시스템 테이블이 InnoDB로 변경되었다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 8.0 이후로 InnoDB스토리지 엔진에 대한 기능이 개선되는 만큼 MyISAM 스토리지 엔진만이 가지는 장점이 없는 상태이다. 이후 버전에서는 없어질 것으로 예상된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;vs MEMORY&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MEMORY 스토리지 엔진 역시 동시 처리 성능에 있어 InnoDB를 따라갈 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MEMORY 스토리지 엔진은 가별 길이 타입의 칼럼을 제공하지 않는다는 문제점이 있다.&lt;/span&gt;&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;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;REAL MySQL 4장&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부</category>
      <category>mysql</category>
      <category>스토리지 아키</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/133</guid>
      <comments>https://runa-nam.tistory.com/133#entry133comment</comments>
      <pubDate>Mon, 17 Oct 2022 19:43:25 +0900</pubDate>
    </item>
    <item>
      <title>N+1 문제 해결하기</title>
      <link>https://runa-nam.tistory.com/132</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;N+1 문제란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 개수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오는 현상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;발생 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N+1 문제가 발생하는 이유는 JPA가 JPQL을 분석해서 SQL을 생성할 때는 글로벌 Fetch 전략을 참고하지 않고 오직 JPQL 자체만을 사용하기 때문이다. 즉, 아래와 같은 순서로 동작하기 때문에 발생한다고 보면 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;findAll()을 한 순간 select t from Team t 이라는 JPQL 구문이 생성되고 해당 구문을 분석한 select * from team이라는 SQL이 생성되어 실행된다.&lt;/li&gt;
&lt;li&gt;DB의 결과를 받아 team 엔티티의 인스턴스들을 생성한다.&lt;/li&gt;
&lt;li&gt;team과 연관되어 있는 user 도 로딩을 해야 한다.&lt;/li&gt;
&lt;li&gt;영속성 컨텍스트에서 연관된 user가 있는지 확인한다.&lt;/li&gt;
&lt;li&gt;영속성 컨텍스트에 없다면 2에서 만들어진 team 인스턴스들 개수에 맞게 select * from user where team_id =?이라는 SQL 구문이 생성된다. ( N+1 발생 )&lt;/li&gt;
&lt;/ol&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해결법&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Fetch Join&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPQL을 사용하여 DB에서 데이터를 가져올 때 처음부터 연관된 데이터까지 같이 가져오게 하는 방법이다. (SQL Join 문을 생각하면 된다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 메소드를 만들어줘야 하며 @Query 어노테이션을 사용해서 join fetch 엔티티.연관관계_엔티티 구문을 만들어 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Fetch Join과 페이지네이션&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 JPA에서 paging을 하게 되면, OneToMany, ManyToMany와 같은 컬렉션 관계는 fetch join이 불가능해진다. 만약 적용하게 되더라도 경고 로그가 남으면서 모든 데이터를 메모리에 불러와 페이징을 적용하는 것을 볼 수 있다. 즉, &lt;b&gt;OOM(Out of Memory)이 발생할 확률이 굉장히 높아지는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이런 일이 발생하는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Join의 결과를 생각해보면 된다. ~toMany의 관계에서는 ~대다 관계를 조인하면서 데이터의 개수가 바뀌기 때문이다. 그렇기 때문에 JPA에서는 기본적으로 이를 막아두게 된다. &lt;a href=&quot;https://www.inflearn.com/questions/14663&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고용 영한님의 인프런Q&amp;amp;A)&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;해결책&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;~ToOne &amp;nbsp;관계의 엔티티는 Fetch Join해도 괜찮다.&lt;/li&gt;
&lt;li&gt;~ToMany 관계의 엔티티인 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@BatchSize 혹은&amp;nbsp;spring.jpa.properties.hibernate.default_batch_fetch_size&amp;nbsp;옵션을 적용하여 쿼리의 개수를 줄인다.
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;X 타입 엔티티가 지연 로딩된&amp;nbsp;~ToMany&amp;nbsp;관계의 Y 타입 컬렉션을 최초 조회할 때&lt;/li&gt;
&lt;li&gt;이미 조회한 X 타입 엔티티(즉, 영속성 컨텍스트에서 관리되고 있는 엔티티)들의 ID들을 모아서&lt;/li&gt;
&lt;li&gt;WHERE Y.X_ID IN (?, ?, ?...)&amp;nbsp;와 같은 SQL IN 구문에 담아 Y 타입 데이터 조회 쿼리를 날린다.&lt;/li&gt;
&lt;li&gt;X 타입 엔티티들이 필요로 하는 모든 Y 타입 데이터를 한 번에 조회한다.&lt;/li&gt;
&lt;/ol&gt;
여기서 Batch Size 옵션에 할당되는 숫자는&amp;nbsp;&lt;b&gt;IN 구문에 넣을 부모 엔티티 Key(ID)의 최대 개수&lt;/b&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Batch Size&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명했듯 이 옵션은 정확히는 N+1 문제를 안 일어나게 하는 방법은 아니고 N+1 문제가 발생하더라도 select * from user where team_id = ? 이 아닌 select * from user where team_id in (?, ?, ? ) 방식으로 N+1 문제가 발생하게 하는 방법이다. 이렇게 하면 100번 일어날 N+1 문제를 1번만 더 조회하는 방식으로 성능을 최적화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/165&quot;&gt;https://jojoldu.tistory.com/165&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmer93.tistory.com/83&quot;&gt;https://programmer93.tistory.com/83&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2020-10-21-jpa-fetch-join-paging/&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-10-21-jpa-fetch-join-paging/&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <category>batch</category>
      <category>Fetch Join</category>
      <category>N+1</category>
      <category>Pagination</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/132</guid>
      <comments>https://runa-nam.tistory.com/132#entry132comment</comments>
      <pubDate>Fri, 14 Oct 2022 02:15:56 +0900</pubDate>
    </item>
    <item>
      <title>스프링 트랜잭션 전파속성</title>
      <link>https://runa-nam.tistory.com/131</link>
      <description>&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;@Transactional 어노테이션(선언적 트랜잭션)을 사용하면 6가지 속성을 지정해 트랜잭션을 세부적으로 이용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;readOnly&lt;/li&gt;
&lt;li&gt;isolation&lt;/li&gt;
&lt;li&gt;propagation&lt;/li&gt;
&lt;li&gt;timeout&lt;/li&gt;
&lt;li&gt;rollbackFor, noRollbackFor (트랜잭션 롤백/커밋 예외)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이중 Propagation은 새로운 트랜잭션을 시작할지 기존의 트랜잭션에 참여할지 등 트랜잭션의 진행 방식에 대한 내용을 결정하게 된다. Spring이 지원하는 전파속성은 다음의 7가지이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;REQUIRED&lt;/li&gt;
&lt;li&gt;SUPPORTS&lt;/li&gt;
&lt;li&gt;MANDATORY&lt;/li&gt;
&lt;li&gt;REQUIRES_NEW&lt;/li&gt;
&lt;li&gt;NOT_SUPPORTED&lt;/li&gt;
&lt;li&gt;NEVER&lt;/li&gt;
&lt;li&gt;NESTED&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;물리 트랜잭션과 논리 트랜잭션&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;물리 트랜잭션 : 실제 커넥션 객체를 사용하여 DB에 적용되는 트랜잭션으로, 커넥션을 통해 commit이나 rollback이 진행되는 단위.&lt;/li&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;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LyMds/btrOyBKzd8w/7dRCnBrIkgZ4XmptuI59vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LyMds/btrOyBKzd8w/7dRCnBrIkgZ4XmptuI59vK/img.png&quot; data-alt=&quot;이미지 출처 :&amp;amp;nbsp;https://mangkyu.tistory.com/269&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LyMds/btrOyBKzd8w/7dRCnBrIkgZ4XmptuI59vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLyMds%2FbtrOyBKzd8w%2F7dRCnBrIkgZ4XmptuI59vK%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;449&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 출처 :&amp;nbsp;https://mangkyu.tistory.com/269&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;REQUIRED&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;디폴트 속성으로,&amp;nbsp;미리 시작된 트랜잭션이 있으면 참여하고 없으면 새로 시작한다.&lt;/li&gt;
&lt;li&gt;가장 간단하고 자연스러운 트랜잭션 전파 방식이지만 매우 강력하며 유용하다.&lt;/li&gt;
&lt;li&gt;만약 REQUIRED 속성일 때 하나의 트랜잭션이 시작된 후 다른 트랜잭션 경계가 설정된 메소드를 호출하면 자연스럽게 같은 트랜잭션으로 묶인다. 즉, 새로운 물리 트랜잭션을 사용하지 않는다.&lt;br /&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;REQUIRES_NEW&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;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;li&gt;만약 이미 시작된 트랜잭션이 있으면 트랜잭션을 잠시 보류시킨다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 JPA 트랜잭션 매니저를 사용한다면 서버의 트랜잭션 매니저에 트랜잭션 보류가 가능하도록 설정되어 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SUPPORTS&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;/li&gt;
&lt;li&gt;트랜잭션이 없기는 하지만 해당 경계 안에서 Connection 객체나 하이버네이트의 Session 등은 공유를 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MANDATORY&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;/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;b&gt;NOT_SUPPORTED&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;/li&gt;
&lt;li&gt;이미 진행중인 트랜잭션이 있으면 이를 보류시키고, 트랜잭션을 사용하지 않도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NEVER&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;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NESTED&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;NESTED는&amp;nbsp;이미 진행중인 트랜잭션이 있으면 중첩 트랜잭션을 시작한다.
&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;독립적인 트랜잭션을 만드는 REQUIRES_NEW와는 다르다.&lt;/li&gt;
&lt;li&gt;NESTED에 의한 중첩 트랜잭션은&amp;nbsp;먼저 시작된 부모 트랜잭션의 커밋과 롤백에는 영향을 받지만, 자신의 커밋과 롤백은 부모 트랜잭션에게 영향을 주지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;부모의 트랜잭션은 자식의 작업에 영향을 줘야하지만 자식의 트랜잭션은 부모에 영향을 주지 않아야 할 때 NESTED 전파 속성을 이용할 수 있다.&lt;/li&gt;
&lt;li&gt;ex) 어떤 중요한 작업을 진행하면서 작업 로그를 DB에 저장해야 한다고 해보자. 그런데 로그를 저장하는 작업은 실패를 하더라도 메인 작업의 트랜잭션까지는 롤백하지 말아야 하는 경우가 있다. 왜냐하면 힘들게 처리한 중요한 작업을 로그를 남기지 못해서 모두 실패로 만들 수 없기 때문이다. 반면에 핵심 작업에서 예외가 발생한다면 이때는 저장된 로그도 제거해야 한다.&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;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U2Afa/btrOxn7l1CN/cUJ94Cf9mkbt7k2FKUgEk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U2Afa/btrOxn7l1CN/cUJ94Cf9mkbt7k2FKUgEk1/img.png&quot; data-alt=&quot;이미지 출처 :&amp;amp;nbsp;https://mangkyu.tistory.com/269&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U2Afa/btrOxn7l1CN/cUJ94Cf9mkbt7k2FKUgEk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU2Afa%2FbtrOxn7l1CN%2FcUJ94Cf9mkbt7k2FKUgEk1%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;673&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;673&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 출처 :&amp;nbsp;https://mangkyu.tistory.com/269&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;참고자료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777;&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/269&quot;&gt;https://mangkyu.tistory.com/269&lt;/a&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: #777777;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#tx-propagation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#tx-propagation&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/131</guid>
      <comments>https://runa-nam.tistory.com/131#entry131comment</comments>
      <pubDate>Fri, 14 Oct 2022 00:03:51 +0900</pubDate>
    </item>
    <item>
      <title>테스트 코드 시간 줄이기</title>
      <link>https://runa-nam.tistory.com/130</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;테스트 코드의 속도&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;TDD를 이용하여 개발을 진행하다 보면 테스트 속도가 개발 과정에서 영향을 미치는 일이 점점 많아진다. 회고덕 프로젝트를 진행하는 과정에서 테스트 속도를 최적화했던 과정을 공유하고자 한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DirtiesContext 제거이전&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이는 굉장히 초반에 제거한 부분으로, 테스트 속도의 개선이 가장 눈에 띄었던 부분이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DirtiesContext란?&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@DirtiesContext는 인수테스트시에 사용하는 메서드인데, 이는 개발자가 정한 기준 (테스트 수행 전/후, 각 테스트 케이스 수행 전/후 등)에 맞춰서 Context를 매번 재생성하여 테스트 격리를 만드어두는 것이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html&quot; target=&quot;_self&quot;&gt;스프링 공식문서 링크&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테스트 결과&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;과거 코드의 경우에는 테스트코드가 굉장히 적어서, 현재 회고덕 코드에 DirtiesContext를 켜고 테스트를 해본 결과 각 AcceptanceTest의 개수만큼 context가 만들어진 것을 볼 수 있다. (acceptancetest의 메서드 21개 &amp;rarr; 21개가 만들어졌다. )&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결과적으로 테스트 코드 296개를 실행하는데 실행 시간만 5분 15초, 이를 위해서 준비하는 시간까지 포함하자면 10분 이상의 시간이 들게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ouaAX/btrN33OujU5/0Wdlq7vfbtFNlak0CstzbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ouaAX/btrN33OujU5/0Wdlq7vfbtFNlak0CstzbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ouaAX/btrN33OujU5/0Wdlq7vfbtFNlak0CstzbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FouaAX%2FbtrN33OujU5%2F0Wdlq7vfbtFNlak0CstzbK%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;638&quot; height=&quot;342&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;790&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;424&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HuZpg/btrN39nt5md/u4y7KnrLAZDb3pIFX3tq11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HuZpg/btrN39nt5md/u4y7KnrLAZDb3pIFX3tq11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HuZpg/btrN39nt5md/u4y7KnrLAZDb3pIFX3tq11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHuZpg%2FbtrN39nt5md%2Fu4y7KnrLAZDb3pIFX3tq11%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;424&quot; height=&quot;82&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;82&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;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DirtiesContext 제거&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DirtiesContext의 경우에는 context를 매번 새로 띄우는 방식이 아닌 다른 방식으로 테스트 격리를 해결하면 되는데, 회고덕의 경우에는 truncate.sql을 매번 실행하는 것으로 해결하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Elehh/btrN2ZNpuUU/0yUQXKLgeG5XDKV1N1TWgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Elehh/btrN2ZNpuUU/0yUQXKLgeG5XDKV1N1TWgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Elehh/btrN2ZNpuUU/0yUQXKLgeG5XDKV1N1TWgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FElehh%2FbtrN2ZNpuUU%2F0yUQXKLgeG5XDKV1N1TWgk%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;715&quot; height=&quot;113&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DirtiesContext 제거 이후&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본적으로 Spring으로 작성된 application의 integration test를 돌리기 위해서는 ApplicationContext가 필요하다. Junit 4를 기반으로 작성되었을 경우 다음과 같이 실행된다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테스트 클래스의 instance를 생성한다. 이때 instance는 no-args constructor를 통해 생성된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테스트에 필요한 bean으로 ApplicationContext를 구성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2의 테스트 instance에, 1에서 생성한 ApplicationContext를 활용하여 필요한 bean을 주입한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DirtiesContext를 실행하였을 때에는 모든 context를 메서드별로 생성하였다. 그렇다면 DirtiesContext를 제거한 이후에는 Context가 어떻게 생성되었을까?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이를 위해서는 ApplicationContext가 캐싱되는 기준에 대해서 알아보아야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ApplicationContext 캐싱 기준&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing-ctx-management&quot; target=&quot;_self&quot;&gt;공식문서 링크&lt;/a&gt; / &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testcontext-ctx-management-caching&quot; target=&quot;_self&quot;&gt;공식문서 컨텍스트 캐싱&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본적으로 한번 로드된 Appliacation은 캐싱되어 각 테스트에 재사용된다. 이때 캐싱이 작동되는 방식을 이해하려면 &amp;ldquo;고유한&amp;rdquo; &amp;ldquo;test suite(제품군)&amp;rdquo;라는 개념을 이해하는 것이 필요하다. 이는 아래 두 가지 조건을 충족하는 것을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;같은 bean의 조합을 필요로 하고&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전 테스트에서 ApplicationContext가 오염되지 않은 경우&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그렇다면 1에서 말하는 같은 bean의 조합을 충족시키는 것은 어떻게 판별할까? 이는 곧 context caching에서 cache key가 무엇으로 이루어지는 지에 대한 질문과도 연결된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring에서는 테스트 클래스의 여러 configuration으로 이를 파악한다. 자세히는 아래와 같다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;locations (from @ContextConfiguration)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;classes (from @ContextConfiguration)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;contextInitializerClasses (from @ContextConfiguration)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;contextCustomizers (from ContextCustomizerFactory) &amp;ndash; this includes @DynamicPropertySource methods as well as various features from Spring Boot&amp;rsquo;s testing support such as @MockBean and @SpyBean.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;contextLoader (from @ContextConfiguration)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;parent (from @ContextHierarchy)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;activeProfiles (from @ActiveProfiles)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;propertySourceLocations (from @TestPropertySource)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;propertySourceProperties (from @TestPropertySource)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;resourceBasePath (from @WebAppConfiguration)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때 대체로 많이 적용될 부분이 바로 &lt;b&gt;어떤 bean을 mock으로 처리했느냐(Mockito의 @MockBean을 사용했느냐)가 ApplicationContext 재사용 여부에 영향을 미친다는 것이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Mockito의 @MockBean을 사용할 경우 contextCustomizers에 MockitoContextCustomizer가 추가되는데, 이 때문에 테스트 클래스에서 @MockBean 처리한 bean의 조합이 달라질 경우 cache key가 달라지게 된다. 따라서 비록 같은 @ContextConfiguration classes attributes를 가졌다고 하더라도 @MockBean의 조합이 달라지면 Spring TestContext는 ApplicationContext를 재사용하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;변경 과정 &amp;amp; 테스트 결과&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;회고덕의 경우에는 Controller Test에서 해당 문제점을 파악해볼 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;기존&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oO7Ld/btrN33gGwyR/XYmk2IvhUJt54cUT7f4Ji1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oO7Ld/btrN33gGwyR/XYmk2IvhUJt54cUT7f4Ji1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oO7Ld/btrN33gGwyR/XYmk2IvhUJt54cUT7f4Ji1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoO7Ld%2FbtrN33gGwyR%2FXYmk2IvhUJt54cUT7f4Ji1%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;789&quot; height=&quot;156&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;309&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;1560&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GtxpB/btrN23B4QYS/Xbd9KLAuz2kNothQnzkQh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GtxpB/btrN23B4QYS/Xbd9KLAuz2kNothQnzkQh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GtxpB/btrN23B4QYS/Xbd9KLAuz2kNothQnzkQh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGtxpB%2FbtrN23B4QYS%2FXbd9KLAuz2kNothQnzkQh1%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;829&quot; height=&quot;190&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;각 ControllerTest마다 bean의 조합이 달라서 각 테스트 별로 spring context를 띄워주고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;수정&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clRUW6/btrN2wdHDqb/ChdR86SeFl2tElZlJdkCe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clRUW6/btrN2wdHDqb/ChdR86SeFl2tElZlJdkCe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clRUW6/btrN2wdHDqb/ChdR86SeFl2tElZlJdkCe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclRUW6%2FbtrN2wdHDqb%2FChdR86SeFl2tElZlJdkCe1%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;819&quot; height=&quot;141&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시간 차이는 dirties context를 제거하였을 때보다 훨씬 적지만, spring context를 각 controllerTest별로 띄우지 않는 것을 확인할 수 있었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한, 인텔리제이에서 확인할 수 있는 테스트 시간은 실제로 테스트가 진행된 시간이기 때문에 context가 띄워지는 등의 시간은 포함되지 않아 실제 jenkins에서 확인한 시간은 차이가 다소 나는 편이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;jenkins&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqFp5q/btrN24130Pm/hkpFZTwnCg1jZ3F8Cv1xUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqFp5q/btrN24130Pm/hkpFZTwnCg1jZ3F8Cv1xUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqFp5q/btrN24130Pm/hkpFZTwnCg1jZ3F8Cv1xUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqFp5q%2FbtrN24130Pm%2FhkpFZTwnCg1jZ3F8Cv1xUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;259&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Jenkins에서 확인하였을 때에는 기존 1분 30초대가 걸리던 것에 비해서 대략 5초~10초 정도의 시간을 절약했다는 것을 알 수 있었다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://bperhaps.tistory.com/entry/테스트-코드-최적화-여행기-3&quot; target=&quot;_self&quot;&gt;https://bperhaps.tistory.com/entry/테스트-코드-최적화-여행기-3&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://suhwan.dev/2019/03/27/spring-test-context-management-and-caching/&quot; target=&quot;_self&quot;&gt;https://suhwan.dev/2019/03/27/spring-test-context-management-and-caching/&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://gocheat.github.io/spring/spring_test-2/&quot; target=&quot;_self&quot;&gt;https://gocheat.github.io/spring/spring_test-2/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부</category>
      <category>Application Context</category>
      <category>dirties context</category>
      <category>테스트</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/130</guid>
      <comments>https://runa-nam.tistory.com/130#entry130comment</comments>
      <pubDate>Fri, 7 Oct 2022 18:03:37 +0900</pubDate>
    </item>
    <item>
      <title>JpaRespository가 아닌 Repository를 사용한 이유</title>
      <link>https://runa-nam.tistory.com/129</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Spring Data Jpa의 Repository들&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring Data JPA를 사용하면 대체로 JpaRepository 인터페이스를 사용하게 된다. 하지만 내부를 살펴보면 더 많은 Repository가 존재한다는 것을 알 수 있다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Repository - 기본적인 Repository이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CrudRepository - CRUD 기능을 제공한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;PagingAndSortingRepository - 페이지네이션과 sort 기능을 제공한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JpaRepository - 영속성 context를 flush하거나 Batch등의 기능을 선언하지 않고 사용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 Repository들은 상속관계로 연결되어있어, 결과적으로 JpaRepository는 상위의 모든 Repository가 제공하는 기능들을 제공한다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이는 분명 편리한 기능이지만, 한번 더 생각해본다면, &lt;b&gt;우리에게 필요하지 않은 메서드들도 제공되고 있다&lt;/b&gt;는 뜻을 의미한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그렇다면 이는 과연 장점만 가지고 있을까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/esfeUB/btrNAyhINKh/rdX9oS2ekLhvZdCD8Tksck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/esfeUB/btrNAyhINKh/rdX9oS2ekLhvZdCD8Tksck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/esfeUB/btrNAyhINKh/rdX9oS2ekLhvZdCD8Tksck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FesfeUB%2FbtrNAyhINKh%2FrdX9oS2ekLhvZdCD8Tksck%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;602&quot; height=&quot;391&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;391&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Repository를 선택한 이유&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 문제를 처음 고민한 것은 회고덕 프로젝트에서 리팩토링을 진행하는 과정에서였는데, &lt;b&gt;필요하지 않은 메서드들도 전부 열어주는 것이 맞는가?&lt;/b&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JpaRepository는 대략 20개의 메서드를 제공하고 있는데, 해당 메서드 중 실제로 사용하고 있는 것은 4~5개 정도였다. 그러다 보니, 리팩토링 과정에서 실제로는 사용하지 않게 된 메서드를 테스트하고 있는 등의 문제가 발생한 것이다. (컴파일 타임에 확인할 수 없다 보니 알아보는 것이 늦었다.)&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;반면, Repository는 우리가 사용하는 메서드를 반드시 선언해야 하기 때문에 save, delete, update 등 데이터에 변경을 가할 수 있는 메서드를 최소한으로 공개할 수 있다. 이를 통해서 의도하지 않은 메서드가 실행되는 것을 막는 것이다.&amp;nbsp;&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JpaRepository를 Repository로 변경하는 리팩토링 과정을 겪으면서 추가적인 이점도 얻게 되었는데, 메서드 호출 방식의 일관화이다. JpaRepository에서 제공하는 delete메서드가 id, 객체 자체등 여러 방식으로 삭제할 수 있도록 제공해주다 보니 삭제 방식에 통일감이 없었는데 겸사겸사 관련 컨벤션을 맞출 수 있었다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 글은 JpaRepository의 사용이 무조건 나쁘다는 의도로 작성된 것은 아니다. JpaRepository를 사용하는 것은 확실히 편하고, 유연하게 사용할 수 있다는 장점을 가지고 있다. 하지만, 대체로 많은 자료가 JpaRepository를 사용하니까, 라는 이유만으로 JpaRepository를 쓰고 있었다면&amp;nbsp;Repository를 통해서 메서드를 개발자가 필요한 것들만 열어두는 것 역시 프로젝트의 상황에 맞춰서 고려해보는 것이 좋다고 생각한다.&amp;nbsp;&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;&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;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/spring-data-repositories&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.baeldung.com/spring-data-repositories&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=MMH_ht8pf8U&quot;&gt;https://www.youtube.com/watch?v=MMH_ht8pf8U&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <category>JPA</category>
      <category>JpaRepository</category>
      <category>Repository</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/129</guid>
      <comments>https://runa-nam.tistory.com/129#entry129comment</comments>
      <pubDate>Mon, 3 Oct 2022 16:49:55 +0900</pubDate>
    </item>
    <item>
      <title>무중단 배포란?</title>
      <link>https://runa-nam.tistory.com/128</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;무중단 배포란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;말그대로 &lt;b&gt;서비스를 중단하지 않고 배포&lt;/b&gt;하는 것을 의미한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;관련 단어들&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컴파일 : 프로그래머가 작성한 소스코드를 기계어로 변환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빌드 : 소스 코드 파일을 컴퓨터에서 실행할 수 있는 소프트웨어 산출물로 변환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배포 : 빌드의 결과물을 사용자가 접근할 수 있는 환경에 배치&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;무중단 배포가 필요한 이유&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjuL8u/btrM7ofoQWQ/7D68et5zCKZvABk1w3XkC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjuL8u/btrM7ofoQWQ/7D68et5zCKZvABk1w3XkC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjuL8u/btrM7ofoQWQ/7D68et5zCKZvABk1w3XkC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjuL8u%2FbtrM7ofoQWQ%2F7D68et5zCKZvABk1w3XkC0%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;567&quot; height=&quot;273&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;같은 포트에서 v1 서비스를 제공하다가 이를 중지하고 새로운 v2 버전을 시작하기까지 서비스는 자연스럽게 중단되게 된다. 이렇게 서비스가 중단되는 시간을 &lt;b&gt;다운타임(Down time)&lt;/b&gt;이라고 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;물론 유저가 없는 시간에 진행하는 등의 방법으로 회피할 수는 있겠으나, 유저에게 최상의 경험을 제공하기 위해서는 이러한 다운 타임이 없는 무중단 배포를 지원하는 것이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;리버스 프록시와 로드밸런싱&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMil9g/btrM6NmurqY/k5TWhUnqiklA9I17BO4PoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMil9g/btrM6NmurqY/k5TWhUnqiklA9I17BO4PoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMil9g/btrM6NmurqY/k5TWhUnqiklA9I17BO4PoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMil9g%2FbtrM6NmurqY%2Fk5TWhUnqiklA9I17BO4PoK%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;565&quot; height=&quot;356&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;리버스 프록시&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때 무중단 배포를 지원하기 위해서는 최소한 서버가 띄워지는 곳을 두 곳 이상으로 분리해야 하는데, 이를 위해서는 클라이언트와 서버 사이에 중계하는 누군가가 필요하게 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이를 책임지는 것이 바로 &lt;b&gt;리버스 프록시&lt;/b&gt;이다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;리버스 프록시란?&lt;/b&gt;&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인터넷과 서버 사이에 위치한 중계 서버&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트가 요청한 내용을 캐싱&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버 정보를 클라이언트로부터 숨길 수 있어 보안에 용이&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로드밸런싱&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버에 가해지는 부하를 분산시켜주는 역할&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하나의 서버가 멈추더라도 서비스 중단 없이 다른 서버가 서비스를 유지할 수 있도록 하여 무중단 배포를 할 수 있도록 해준다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;적용 방법&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AWS에서 Blue-Green 무중단 배포&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도커를 이용한 무중단 배포&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;L4, L7 스위치를 이용한 무중단 배포&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx를 이용한 무중단 배포&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Nginx를 이용한 방법은 가장 저렴하고 단순한 편이어서 많은 사람들이 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;무중단 배포 방식&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;롤링(Rolling)&lt;/span&gt;&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;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yyIn1/btrM6XPT5Ai/9M47akdtKFzBpxpw14GCRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yyIn1/btrM6XPT5Ai/9M47akdtKFzBpxpw14GCRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yyIn1/btrM6XPT5Ai/9M47akdtKFzBpxpw14GCRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyyIn1%2FbtrM6XPT5Ai%2F9M47akdtKFzBpxpw14GCRK%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;609&quot; height=&quot;310&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구 버전에서 신 버전으로 트래픽을 점진적으로 전환하는 배포&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;장점&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인스턴스를 추가하지 않아도 돼서 관리가 간편&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단점&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배포 중 한쪽 인스턴스에 트래픽이 몰릴 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구버전과 신버전의 공존으로 인한 호환성 문제&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;블루 그린(Blue Green)&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uYahu/btrM8J4da39/IC5gpjEWsc9lSH75zQ6lGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uYahu/btrM8J4da39/IC5gpjEWsc9lSH75zQ6lGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uYahu/btrM8J4da39/IC5gpjEWsc9lSH75zQ6lGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuYahu%2FbtrM8J4da39%2FIC5gpjEWsc9lSH75zQ6lGk%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;648&quot; height=&quot;483&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구 버전을 블루, 신 버전을 그린 이라고 해서 붙여진 이름이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;운영 환경에서 구버전과 동일하게 신 버전을 배포하고 일제히 전환해 모든 트래픽을 신버전으로 변경하는 배포 방식이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;장점&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배포하는 속도가 빠르다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;신속하게 롤백이 가능하다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;남아있는 기존 버전의 환경을 다음 배포에 재사용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단점&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템 자원이 2배로 필요하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;카나리(Canary)&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfQ4ut/btrM6JRWazD/ISkUOnJXKA6XwyUUkAuFo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfQ4ut/btrM6JRWazD/ISkUOnJXKA6XwyUUkAuFo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfQ4ut/btrM6JRWazD/ISkUOnJXKA6XwyUUkAuFo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfQ4ut%2FbtrM6JRWazD%2FISkUOnJXKA6XwyUUkAuFo1%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;575&quot; height=&quot;287&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;카나리 배포 방식은 광산에서 유독가스 누출의 위험을 알리는 용도로 사용됐던 &amp;lsquo;카나리아&amp;rsquo;에서 기원한 방식이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;따라서 위험을 빠르게 감지할 수 있는 특징을 가지고 있으며, 신 버전을 특정 서버 혹은 유저에게만 제공하였다가 점점 넓혀갈 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;장점&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문제 상황을 빠르게 감지 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;A/B 테스트로 활용 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단점&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모니터링 관리 비용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구버전과 신버전의 공존으로 인한 호환성 문제&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;프로젝트 적용 방식&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;무중단 배포의 경우에는 회고덕 프로젝트에서도 적용되었는데, 우리 서버의 경우 기본적으로 Nginx + BlueGreen 방식을 사용하였다. 하지만, 서버를 두대 띄울 정도의 리소스가 필요하지는 않다고 생각하였기 때문에 위에서 설명한 BlueGreen과는 조금 다른 방식으로 진행되었다. 간단히 정리하자면 다음과 같다.&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존 서버의 포트를 확인한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다른 포트에 서버를 띄운다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;health check를 통해 서버가 잘 띄워졌는지 확인한다.&lt;/span&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;성공 &amp;rarr; 기존에 띄워져 있던 포트를 내리고, 새로 띄운 포트로 포트를 변환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실패 &amp;rarr; 기존에 띄워져있던 서버를 유지한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자세한 적용 내용은 하단에 정리해두었다.&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;## Deploy.sh

#!/bin/bash

# find IDLE PROFILE

echo &quot;&amp;gt; 현재 구동중인 profile 확인&quot;
CURRENT_PROFILE=$(curl -s http://{profile_uri})
echo &quot;&amp;gt; $CURRENT_PROFILE&quot;

if [ $CURRENT_PROFILE == was1 ]
then
    IDLE_PROFILE=was2
    IDLE_PORT=포트2
elif [ $CURRENT_PROFILE == was2 ]
then
    IDLE_PROFILE=was1
    IDLE_PORT=포트1
else
    IDLE_PROFILE=was1
    IDLE_PORT=포트1
        echo &quot;&amp;gt; 일치하는 Profile이 없습니다. Profile: $CURRENT_PROFILE&quot;
      echo &quot;&amp;gt; was1을 할당합니다. IDLE_PROFILE: was1&quot;
fi

# deploy in IDLE_PORT

IDLE_PID=$(pgrep -f $IDLE_PROFILE)

CURRENT_PID=$(pgrep -f $CURRENT_PROFILE)

echo &quot;&amp;gt; $IDLE_PORT 가 사용 중인 PID 확인 :  $IDLE_PID&quot;
if [ -z $IDLE_PID ]; then
    echo &quot;&amp;gt; 현재 $IDLE_PORT 가 사용 중인 애플리케이션이 없으므로 종료하지 않습니다.&quot;
else
        echo &quot;&amp;gt; kill -9 $IDLE_PID&quot;
        kill -9 $IDLE_PID
        sleep 5
fi

echo &quot;chmod +x jar 변경&quot;
chmod +x /home/ubuntu/deploy/{projectName}-0.0.1-SNAPSHOT.jar

echo &quot;&amp;gt; $IDLE_PROFILE 를 $IDLE_PORT 포트로 실행합니다.&quot;
nohup java -jar /home/ubuntu/deploy/{projectName}-0.0.1-SNAPSHOT.jar --spring.profiles.active=$IDLE_PROFILE 1&amp;gt; /dev/null 2&amp;gt;&amp;amp;1  &amp;amp;

echo &quot;&amp;gt; $IDLE_PROFILE 10초 후 Health check 시작&quot;
echo &quot;&amp;gt; curl -s http://localhost:$IDLE_PORT/{healthCheck용 url}&quot;
sleep 10

for retry_count in {1..10}
do
    response=$(curl -s http://localhost:$IDLE_PORT/{healthCheck용 url})
    up_count=$(echo $response | grep 'UP' | wc -l)

    if [ $up_count -ge 1 ]
        then
        echo &quot;&amp;gt; Health check 성공&quot;
        break
    else
        echo &quot;&amp;gt; Health check의 응답을 알 수 없거나 혹은 status가 UP이 아닙니다.&quot;
        echo &quot;&amp;gt; Health check: ${response}&quot;
    fi

    if [ $retry_count -eq 10 ]
        then
        echo &quot;&amp;gt; Health check 실패. &quot;
        echo &quot;&amp;gt; Nginx에 연결하지 않고 배포를 종료합니다.&quot;
        exit 1
    fi

    echo &quot;&amp;gt; Health check 연결 실패. 재시도...&quot;
    sleep 10
done

# nginx $service_url switching

bash /home/ubuntu/deploy/switch-serve.sh ${IDLE_PORT}
sudo service nginx reload

echo &quot;&amp;gt; kill -9 $CURRENT_PID&quot;
kill -9 $CURRENT_PID
sleep 5

curl -k http://localhost/profile
PROXY_PORT=$(curl -k http://localhost/{profile 확인용url})
echo &quot;&amp;gt; Nginx Current Proxy Port: $PROXY_PORT&quot;
sleep 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때 active한 서비스의 포트를 변환하기 위한 파일은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;## switch-serve.sh

echo &quot;set \$service_url http://127.0.0.1:$1;&quot; | sudo tee /etc/nginx/conf.d/service-url.inc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;echo를 통해서 입력값을 받고 ($1부분), tee를 통해서 파일에 출력을 진행한다. (&amp;gt;&amp;gt;를 통해서도 출력할 수 있으나, 관리자 권한이 필요했기 때문에 tee를 사용하였다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 변경한 service_url을 nginx에 연결시키면 nginx를 통한 무중단 배포가 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고자료&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://velog.io/@znftm97/무중단-배포를-위한-환경-이해하기&quot;&gt;https://velog.io/@znftm97/무중단-배포를-위한-환경-이해하기&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://loosie.tistory.com/781&quot;&gt;https://loosie.tistory.com/781&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부</category>
      <category>bluegreen</category>
      <category>nginx</category>
      <category>무중단 배포</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/128</guid>
      <comments>https://runa-nam.tistory.com/128#entry128comment</comments>
      <pubDate>Tue, 27 Sep 2022 00:58:07 +0900</pubDate>
    </item>
    <item>
      <title>VPC란 무엇인가?</title>
      <link>https://runa-nam.tistory.com/127</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존에는 VPC 같은 개념에 대해서 접할 일이 많지 않았는데, 우테코 활동을 하며 AWS 내의 VPN, 서브넷과 같은 개념을 접하게 되어 간단하게 정리해보고자 한다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPC란&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Virtual Private Cloud의 약자로 AWS 클라우드 내에서 논리적으로 &lt;b&gt;격리된 가상 네트워크&lt;/b&gt;를 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 네트워크 내에서 EC2 인스턴스와 같은 리소스를 실행할 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;간단하게 정리하자면 아래 이미지, 그리고 세 가지를 생각하면 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t1ati/btrMzQJwpJm/KofpcZ9m2InkVgjK55E3BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t1ati/btrMzQJwpJm/KofpcZ9m2InkVgjK55E3BK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t1ati/btrMzQJwpJm/KofpcZ9m2InkVgjK55E3BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft1ati%2FbtrMzQJwpJm%2FKofpcZ9m2InkVgjK55E3BK%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;559&quot; height=&quot;446&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;698&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AWS에 사설 네트워크 구축&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;세심한 네트워크 설정 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모든 리전에서 이용 가능&lt;/span&gt;&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;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프라이빗 IP 주소, 퍼블릭 IP 주소, Elastic IP 주소&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Private IP : VPC 내부에서만 사용할 수 있는 IP 주소&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서브넷의 범위에서 자동으로 할당되며, 동일 네트워크에서 인스턴스 간 통신에 사용 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Public IP : 인터넷을 통해 연결할 수 있는 IP 주소&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Elastic IP 연결하지 않을 시 재부팅되면 새로 할당됨.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Elastic IP : 동적 컴퓨팅을 위해 사용하는 동적 퍼블릭 IP 주소.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPC와 Subnet&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MBh3Y/btrMw6mAMql/8rjr9pmVNTkUSnwGadwkhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MBh3Y/btrMw6mAMql/8rjr9pmVNTkUSnwGadwkhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MBh3Y/btrMw6mAMql/8rjr9pmVNTkUSnwGadwkhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMBh3Y%2FbtrMw6mAMql%2F8rjr9pmVNTkUSnwGadwkhK%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;673&quot; height=&quot;341&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;444&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Subnet : VPC 내부의 네트워크에서 서비스 목적에 따라 IP Block으로 나누어 구분한 것.&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단일 가용 영역에만 사용할 수 있으며, 여러 가용 영역으로 확장될 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서브넷을 나누는 이유는 더 많은 네트워크 망을 만들기 위해서이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPC와 Subnet의 사이즈&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPC를 생성할 때 지정한 VPC에서 사용할 IP 주소의 범위는 CIDR(Classless Inter-Domain Routing) 블록 형태로 지정해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ex) 10.0.0.0/24 &amp;rarr; 256개의 IP 주소 지원, CIDR 블록을 각각 128개의 주소를 지원하는 2개의 서브넷으로 나눌 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;퍼블릭 서브넷과 프라이빗 서브넷&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;퍼블릭 서브넷 : 네트워크 트래픽이 인터넷 게이트웨이로 라우팅 되는 경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프라이빗 서브넷 : 인터넷 게이트웨이로 라우팅 되지 않는 경우&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;EC2 인스턴스가 IP를 통해 인터넷과 통신할 수 있게 하려면 퍼블릭 IP나 탄력적 IP가 있어야 한다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반적으로 웹서버는 퍼블릭 서브넷 / DB 서버는 프라이빗 서브넷에 연결한다 (for 보안)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;라우팅 테이블&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lGoyL/btrMyfQAfUf/TM5yqVBGUKTmutYQ2XFeHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lGoyL/btrMyfQAfUf/TM5yqVBGUKTmutYQ2XFeHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lGoyL/btrMyfQAfUf/TM5yqVBGUKTmutYQ2XFeHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlGoyL%2FbtrMyfQAfUf%2FTM5yqVBGUKTmutYQ2XFeHk%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;649&quot; height=&quot;322&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;434&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;라우팅 테이블 : 서브넷 외부로 나가는 아웃바운드 트래픽에 대해 허용된 경로들&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPC의 서브넷 내에서 생성된 네트워크 패킷이 목적지 주소로 가기 위해 어떤 경로로 이동되어야 하는지 알려줌&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서브넷 간의 통신이나 VPC 간의 원활한 통신을 위해 사용함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주요 서비스&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;보안 그룹과 네트워크 액세스 제어 목록&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;네트워크 통신과 트래픽에 대해 IP와 포트를 기준으로 허용 및 차단하는 기능 제공&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구분&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;보안그룹&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;네트워크 ACL&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 범위&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인스턴스 레벨에 적용&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서브넷 레벨에 적용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;적용 정책&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Allow (허용) 규칙만 적용&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Allow(허용) 규칙과 Deny(거부)규칙 적용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구동 방식&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;규칙에 상관없이 반환 트래픽 허용&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;반환 트래픽이 별도로 허용되어야 함&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;룰 검토 / 적용&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 객체 내 모든 룰 검토&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 객체 내 룰을 번호 순으로 처리&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;적용 방법&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인스턴스에 보안그룹 추가&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;연결된 서브넷에 모든 인스턴스 자동 적용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPC 피어링 연결&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;비공개적으로 두 VPC 간에 트래픽을 라우팅 할 수 있게 하기 위한 서로 다른 VPC 간의 네트워크 연결.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NAT 게이트웨이&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부 네트워크에 알려진 것과 다른 IP 주소를 사용하는 내부 네트워크에서, 내부 IP 주소를 외부 IP 주소로 변환하는 작업을 수행하는 서비스.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프라이빗 서브넷 내에 있는 인스턴스를 인터넷 또는 다른 AWS 서비스에 연결하고, 외부망 또는 인터넷에서는 해당 인스턴스에 접근하지 못하도록 하는 것이다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NAT 게이트를 생성하기 위한 퍼블릭 서브넷 지정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NAT 게이트웨이와 연결할 Elastic IP 필요&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NAT 게이트웨이를 만든 후 인터넷 트래픽이 NAT 게이트웨이로 통신이 가능하도록 프라이빗 서브넷과 연결된 라우팅 테이블 업데이트&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPN 연결&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본적으로 Amazon VPN에서 서비스되는 인스턴스는 On-Premise에 있는 서버나 IDC 내의 시스템과 통신할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPC 내 인스턴스와 IDC 내 시스템 간의 데이터 통신을 위해 VPC에 가상의 프라이버시 게이트웨이를 연결하고 사용자 지정 라우팅 테이블을 생성하며 보안 그룹의 규칙을 업데이트하고, AWS 관리형 VPN 연결을 생성하여 VPC에서 원격의 네트워크에 접속 가능하도록 하이브리드 클라우드 환경을 구성할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;VPN 연결은 VPC와 자체 네트워크 사이의 연결을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고자료&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/harrythegreat/aws-가장쉽게-vpc-개념잡기-71eef95a7098&quot;&gt;https://medium.com/harrythegreat/aws-가장쉽게-vpc-개념잡기-71eef95a7098&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아마존 웹 서비스 AWS Discovery Book 책&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부</category>
      <category>AWS</category>
      <category>VPC</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/127</guid>
      <comments>https://runa-nam.tistory.com/127#entry127comment</comments>
      <pubDate>Tue, 20 Sep 2022 01:51:41 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] BIO NIO 스레드 풀</title>
      <link>https://runa-nam.tistory.com/126</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;931&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyDCdw/btrMePEFhDW/2tkS8tXVKwW5O23c5rk5C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyDCdw/btrMePEFhDW/2tkS8tXVKwW5O23c5rk5C1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyDCdw/btrMePEFhDW/2tkS8tXVKwW5O23c5rk5C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyDCdw%2FbtrMePEFhDW%2F2tkS8tXVKwW5O23c5rk5C1%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;608&quot; height=&quot;370&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;931&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Tomcat의 핵심 요소로는 저번에 Servlet Container 인&amp;nbsp;Catalina와 Connector Framework인&amp;nbsp;Coyote가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이중, Servlet Container가 외부와의 소통을 가능하게 하는 Connector에 대해서 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Connector의 역할&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;611&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7K8q6/btrMgmBNs6D/ZPWsaQLPoKjLmXjMjz6yh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7K8q6/btrMgmBNs6D/ZPWsaQLPoKjLmXjMjz6yh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7K8q6/btrMgmBNs6D/ZPWsaQLPoKjLmXjMjz6yh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7K8q6%2FbtrMgmBNs6D%2FZPWsaQLPoKjLmXjMjz6yh1%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;1530&quot; height=&quot;611&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;611&quot;/&gt;&lt;/span&gt;&lt;/figure&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우선, port listen을 통해 Socket Connection을 얻는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Socket Connection으로부터 데이터 패킷을 획득.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터 패킷을 파싱해서 ServletRequest Object를 생성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;얻어진 ServletRequest Object를 알맞은 Servlet Container에게 보낸다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉, Connector의 역할은 network port를 listen 하여 connection을 얻은 후, 데이터를 파싱하여 알맞은 servlet request로 바꿔주는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Tomcat에서의 Connector&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;649&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE5HAZ/btrMbxS1cRx/KmSMKy0mnferJbPLt6syS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE5HAZ/btrMbxS1cRx/KmSMKy0mnferJbPLt6syS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE5HAZ/btrMbxS1cRx/KmSMKy0mnferJbPLt6syS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE5HAZ%2FbtrMbxS1cRx%2FKmSMKy0mnferJbPLt6syS0%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;693&quot; height=&quot;294&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;649&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://tomcat.apache.org/tomcat-8.0-doc/config/http.html#SSL_Support_-_BIO,_NIO_and_NIO2&quot;&gt;공식문서 링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위에서 설명한 Connector는 Blocking IO방법인 BIO Connector와 NonBlocking IO방법인 NIO Connector가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;BIO Connector는 요청시 하나의 thread가 할당되어 요청을 처리한다. 하지만 connection이 닫힐 때까지 하나의 thread는 특정 connection에 할당되어 있기 때문에 idle(아무것도 하지 않는) 상태로 낭비되는 시간이 많아져 효율적으로 자원을 사용하지 못하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이를 해결하기 위해 NIO Connector가 등장하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;BIO Connector&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTnaoR/btrMdNVdIvX/5WNkP5TpyA3O4XTmt31Gsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTnaoR/btrMdNVdIvX/5WNkP5TpyA3O4XTmt31Gsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTnaoR/btrMdNVdIvX/5WNkP5TpyA3O4XTmt31Gsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTnaoR%2FbtrMdNVdIvX%2F5WNkP5TpyA3O4XTmt31Gsk%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;545&quot; height=&quot;423&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고로 Tomcat 9부터는 Deprecated 된 기술이다. BIO Connector는 접속자 한 명당 하나의 스레드를 생성하는 구조로, 커넥션이 닫힐 때까지 하나의 스레드는 특정 커넥션에 할당되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;크게 3가지 요소로 이루어진다.&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;HTTP11Protocol
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;acceptor 스레드와 worker 스레드를 가지고 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Acceptor는 소켓을 획득하고 worker thread pool 에서 socket을 처리하기 위한 idle상태인 worker thread를 찾는다.&amp;nbsp;&lt;b&gt;만약 Worker thread pool에 idle thread가 없다면, 요청을 처리할 thread가 없기 때문에 Acceptor는 block된다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉, 동기 처리가 된다는 점이 NIO와의 가장 큰 차이점이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;worker thread가 socket을 받은 후, Http11Processor object pool에서 Http11Processor를 얻고 요청을 처리한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CoyoteAdapter&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HTTP요청을 httpServlet Request Object로 반환하는 역할.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;적절한 Container에 바인딩&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;세션 관리의 역할.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Mapper&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HTTP Request에 상응하는 Servlet에 바인딩하기 위해 사용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;요청 처리 흐름&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트 요청 시 Acceptor가 port listener를 통해 소켓을 얻는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 소켓을 worker thread poold에서 idle한 worker thread를 할당받는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;worker thread는 Http11ConnectionHandler에서 Http11Processor object을 받는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Http11Processor object에서 CoyoteAdapter를 통해 http요청을 HttpServletRequest로 변환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요청에 맞는 Servlet 호출.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NIO Connector&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mub7y/btrMbzpRbGI/jubGueKhD3ie1yrfShDgK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mub7y/btrMbzpRbGI/jubGueKhD3ie1yrfShDgK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mub7y/btrMbzpRbGI/jubGueKhD3ie1yrfShDgK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmub7y%2FbtrMbzpRbGI%2FjubGueKhD3ie1yrfShDgK1%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;528&quot; height=&quot;437&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Tomcat 5부터 지원하고 있다. BIO의 다이어그램과는 다른 점은 &lt;b&gt;Poller라는 개념이 등장한 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;BIO에서는 Connection과 Thread가 1:1 관계였기 때문에 IO 작업과 같이 대기가 긴 작업에서도 해당 처리가 끝날 때까지 idle인 thread가 낭비되었다. 하지만, Poller는 Socket들을 캐시로 들고 있다가 해당 Socket에서 data에 대한 처리가 가능한 순간에만 thread를 할당하는 방식을 사용해서 thread이 idle 상태로 낭비되는 시간을 줄일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주된 세 가지 요소&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;Http11NioProtocol&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Mapper&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CoyoteAdapter&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NIO Endpoint의 내부 동작&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;647&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4URTT/btrMfNfgoxB/G3IeOD0L1BJJBGFUHc8B1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4URTT/btrMfNfgoxB/G3IeOD0L1BJJBGFUHc8B1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4URTT/btrMfNfgoxB/G3IeOD0L1BJJBGFUHc8B1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4URTT%2FbtrMfNfgoxB%2FG3IeOD0L1BJJBGFUHc8B1K%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;626&quot; height=&quot;265&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;647&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본적으로 Producer-Consumer mode을 사용하며,&amp;nbsp;Acceptor와&amp;nbsp;Poller&amp;nbsp;thread들은 queue를 통해 소통한다. 즉,Acceptor는 Event Queue의 producer이고 Poller는 Event Queue의 consumer가 된다고 보면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Acceptor&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;rArr; event queue의 producer&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Acceptor는 이름 그대로 Socket Connection을 accept한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NIO connector이지만 소켓을 받는 것은 여전히 전통적인&amp;nbsp;serverSocket.accept()&amp;nbsp;방식을 사용하고 있다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Poller&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;rArr; event queue의 consumer로서 Event queue로부터 PollerEvent를 받는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Poller는 NIO 구현에 있어서 주요한 Thread이다. (당연히 poller도 thread이다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NIO Connector는 Selector 기반으로 되어있다.(즉, 하나의 thread로 여러 가지) 그래서 &lt;b&gt;Poller thread 속에는 Selector Object가 있다&lt;/b&gt;. 하나의 Connector에 하나 이상의 Selector가 있을 수도 있다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Selector에 PollerEvent 속 Channel을 등록한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Poller의 selector는 select동작을 수행하여 Worker Thread Pool에서 이용할 수 있는 Woker Thread를 얻어서 해당 소켓을 worker thread에게 넘긴다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 처리 방식은 전형적인 NIO를 활용한 구현으로, BIO Connector와 달리 Selector에 등록해두었다가 데이터 처리가 가능한 소켓을 별도로 worker 스레드에 할당시켜 줌으로써 좀 더 효율적으로 스레드를 사용할 수 있게 된다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Worker&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;rArr; Poller에 의해 넘겨받은 소켓을 프로세서 오브젝트로 캡슐화&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Poller에 의해 소켓을 넘겨받은 후, Worker Thread 내에서 소켓에서 얻은 Http 요청을 처리하는 작업을 끝내고&amp;nbsp;HttpServletReqeust&amp;nbsp;Object로 변환 후, 알맞은 Servelt에게 Reqeust Object를 전달해서 servlet 작업이 완료한 후 가지고 있던 소캣을 통해 클라이언트에게 응답을 돌려주게 된다. 사실상 Poller로 받는 것을 제외하고는 BIO와 동일하다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;많은 요청이 들어올 때 Tomcat의 처리 방식&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 현재 이용할 수 있는 스레드들보다 더 많은 요청이 동시에 올 경우, 최대&amp;nbsp;maxThreads&amp;nbsp;속성 값까지 추가 thread가 생성된다. 여전히 더 많은 요청이 올 경우, Connector의 Server Socket 내부에 최대 acceptCount속성 값까지 쌓이게 된다. 이 이상 더 많은 요청들을 받게 되면 요청을 처리할 자원이 있을 때까지 &quot;Connection Refused&quot; 에러를 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요약&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;NIO 기반의 Connector는 BIO Connector에 비해 더 적은 Thread를 사용한다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Java Nio Selector를 사용해서 data 처리가 가능할 때만 Thread를 사용하기 때문에 idle 상태로 낭비되는 Thread가 줄어든다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고자료&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://velog.io/@jihoson94/BIO-NIO-Connector-in-Tomcat&quot;&gt;https://velog.io/@jihoson94/BIO-NIO-Connector-in-Tomcat&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://velog.io/@hyunjong96/Spring-NIO-Connector-BIO-Connector&quot;&gt;https://velog.io/@hyunjong96/Spring-NIO-Connector-BIO-Connector&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>thread pool</category>
      <category>Tomcat</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/126</guid>
      <comments>https://runa-nam.tistory.com/126#entry126comment</comments>
      <pubDate>Thu, 15 Sep 2022 22:43:36 +0900</pubDate>
    </item>
    <item>
      <title>캐시란?</title>
      <link>https://runa-nam.tistory.com/125</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;캐시란?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시란 자주 사용하는 데이터나 값을 미리 복사해놓는 임시 장소를 가리킨다. 저장 공간이 작고 비싼 대신, 빠른 성능을 제공한다. 즉, 반복적으로 사용되는 데이터를 불러올 때, 매번 DBMS나 서버에 요청하는 것이 아니라 메모리에 저장하였다가 쓰는 것을 의미한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;캐시 적용 / 미적용 시나리오&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnOjd9/btrL1AhDKgn/ONTsiyDd0uw4GuKs3WzKuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnOjd9/btrL1AhDKgn/ONTsiyDd0uw4GuKs3WzKuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnOjd9/btrL1AhDKgn/ONTsiyDd0uw4GuKs3WzKuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnOjd9%2FbtrL1AhDKgn%2FONTsiyDd0uw4GuKs3WzKuk%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;571&quot; height=&quot;411&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시가 없을 때&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터가 변경되지 않아도 계속 네트워크를 통해서 데이터를 다운로드하여야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인터넷 네트워크는 매우 느리고 비싸다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;브라우저 로딩 속도가 느리다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;느린 사용자 경험&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 적용&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시덕분에 캐시 가능 시간 동안 네트워크를 사용하지 않아도 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;비싼 네트워크 사용량을 줄인다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;브라우저 로딩 속도를 빠르게 할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빠른 사용자 경험&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 시간 초과&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 유효 시간이 초과 &amp;rarr; 서버를 통해 데이터 다시 조회 및 캐시 갱신&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;네트워크 다운로드가 이때 다시 진행됨.&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터가 동일하다면? &amp;rarr; 굳이 다운로드할 필요가 X&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;검증 헤더와 조건부 요청&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;검증헤더&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;: 캐시 데이터와 서버 데이터가 같은지 검증하는 데이터&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Last-Modified, ETag&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조건부 요청 헤더&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;: 검증 헤더로 조건에 따른 분기&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;If-Modified-Since: Last-Modified 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;If-None-Match: Etag 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조건이 만족 &amp;rarr; 200 OK&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만족 X &amp;rarr; 304 Not Modified&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Last-Modified 사용&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;If-Modified-Since 이후에 데이터가 수정되었는지 확인&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 유효시간이 초과해도 데이터가 갱신되지 않으면 (캐시 = 서버)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;rarr; 304 Not Modified + 헤더 메타 정보만 응답 (Body)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;= 네트워크 부하가 줄어들게 됨. (실용적인 해결책!)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1초 미만 단위로 캐시 조정 불가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;날짜 기반의 로직 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 데이터 결과가 똑같은 경우 (A &amp;rarr; B &amp;rarr; A 일 경우, 콘텐츠는 변경된 건 아님.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버에서 별도의 캐시 로직을 관리하고 싶은 경우 (스페이스 등을 무시)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ETag 사용&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시용 데이터에 임의의 고유한 이름 달아둠&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ex) Etag: &amp;ldquo;v1.0&amp;rdquo;, Etag: &amp;ldquo;ag123123&amp;rdquo;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터가 변경 &amp;rarr; 이름을 변경함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ETag를 보내서 같으면 유지, 다르면 다시 받음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 제어 로직을 서버에서 완전히 관리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;rarr; 클라이언트는 단순히 이 값을 서버에 제공하게 됨.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;적용하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;RFC(Request for Comments) 표준 방식에 의해 캐시 validation 방식이 변경됨&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존 : etag, last-modified 둘 다 비교&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;변경: etag만 비교 (ShallowEtagFIlter를 사용할 수도 있음)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;캐시와 조건부 요청 헤더&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 제어 헤더&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cache-Control&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;max-age : 캐시 유효 시간, 초단위 (Expires보다 더 유연함)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;no-cache : 데이터는 캐시 해도 되지만, 항상 &lt;b&gt;원(origin) 서버에 검증&lt;/b&gt;하고 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;no-store : 데이터에 민감한 정보가 있으므로 &lt;b&gt;저장 X&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Pragrma (하위 호환)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Expires - 캐시 만료일 지정 (하위 호환)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;휴리스틱 캐시&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;휴리스틱 캐시란?&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cache-Control이 지정되지 않더라도, 기본적으로 cache는 최대한 사용해 성능을 높이는 것이 추천되기 때문에 기본적으로 적용되는 캐시 정책.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본적으로 1년 동안 업데이트되지 않은 컨텐츠는 잘 업데이트 되지 않는 것으로 알려져 있다. 따라서, 클라이언트는 max-age가 설정되지 않았더라도 어느 정도 사용되게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;재사용 기간은 구현에 따라 다르지만, 기본적으로 0.1년 정도를 권장한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 응답이 Cache-control : max-age 헤더나 expires 헤더들을 포함하지 않고 있다면 캐시는 경험적인 방법으로 heuristic 최대 나이를 게 산할 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;검증헤더와 조건부 요청 헤더&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;검증 헤더 &amp;rarr; Etag, LastModified&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조건부 요청 헤더&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Etag &amp;rarr; If-Match, If-None-Match&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Last-Modified &amp;rarr; If-Modified-Since, If-Unmodified-Since&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;프록시 캐시&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9sXIM/btrL2nvIbl0/9TgGeKaoMtE2yapACnyn20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9sXIM/btrL2nvIbl0/9TgGeKaoMtE2yapACnyn20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9sXIM/btrL2nvIbl0/9TgGeKaoMtE2yapACnyn20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9sXIM%2FbtrL2nvIbl0%2F9TgGeKaoMtE2yapACnyn20%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;846&quot; height=&quot;379&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트들과 오리진 서버의 거리가 너무 멀리 떨어져 있어서 응답 시간이 많이 소요되는 경우 고려되는 기술로, 클라이언트들과 원 서버 사이에 프록시 캐시 서버를 둠으로써 응답시간을 줄이는 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트로부터 요청되는 자원에 대해 프록시 캐시서버에서 캐시를 저장하여 제공함으로써 여러 사람이 찾는 자료일수록 이미 캐시에 등록되어 있기에 빠른속도로 자원을 받을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트에서 사용되는 캐시는 해당 클라이언트에서만 사용되기에 &lt;b&gt;private 캐시&lt;/b&gt;라 부르고 프록시 캐시 서버에서 사용되는 캐시는 여러 클라이언트들에게 노출되야 하기에&lt;b&gt; public 캐시&lt;/b&gt;라 부른다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Cache-Control: public :&lt;/b&gt; 응답이 public 캐시에 저장되어도 됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Cache-Control: private :&lt;/b&gt; 응답이 해당 사용자만을 위한 것임, private 캐시에 저장해야 함(기본값)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Cache-Control: s-maxage :&lt;/b&gt; 프록시 캐시에만 적용되는 max-age&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Age: 60&lt;/b&gt;&amp;nbsp;(HTTP 헤더) : 오리진 서버에서 응답 후 프록시 캐시 내에 머문 시간(초)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;캐시 무효화&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;Cache-Control: no-cache, no-store, must-revalidate &lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;Pragma: no-cache&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;통장 잔고와 같이 캐시 활용이 돼서는 안 되는 서비스의 경우 캐시를 확실하게 무효화해야 하는데, 하위 호환까지 고려해서 위와 같이 작성을 하면 된다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cache-Control: no-cache&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터는 캐시 해도 되지만, 항상&amp;nbsp;원 서버에 검증하고 사용(이름에 주의!)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cache-Control: no-store&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터에 민감한 정보가 있으므로 저장하면 안 됨(메모리에서 사용하고 최대한 빨리 삭제)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다만, 해당 내용보다 no-cache가 조금 더 확실하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Cache-Control: must-revalidate&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐시 만료 후 최초 조회 시&amp;nbsp;원 서버에 검증해야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원 서버 접근 실패 시 반드시 오류가 발생해야 함 - 504(Gateway Timeout)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;must-revalidate는 캐시 유효 시간이라면 캐시를 사용함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Pragma: no-cache&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HTTP 1.0 하위 호환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;b&gt;HTTP Compression 설정하기&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.webserver.enable-response-compression&quot;&gt;Enable HTTP Response Compression&lt;/a&gt;를 참고&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HTTP 응답을 압축하여 웹 사이트 성능 향상&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>우아한 테크코스</category>
      <category>캐시</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/125</guid>
      <comments>https://runa-nam.tistory.com/125#entry125comment</comments>
      <pubDate>Tue, 13 Sep 2022 22:54:32 +0900</pubDate>
    </item>
    <item>
      <title>회고덕 팀 1주차 회고</title>
      <link>https://runa-nam.tistory.com/124</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;방학 때 한 공부에 대해서는 블로그 글을 많이 적지 못해서 아쉬웠던 터라 (이사 준비 등으로 바빠서... 그냥 노션 정도에만 정리하는 것으로 그쳤다.) 이번에는 고민하는 과정 등을 블로그에 한번 정리하면서 레벨 3의 과정을 정리해보고자 한다. &lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;방학기간, 아이디어 회의&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우리 팀의 경우에는 첫 아이디어를 고르는 데에도 꽤 많은 고심을 했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;현재 잡힌 아이디어인 '회고 플랫폼' 회고덕을 포함하여 정말 많은 아이디어들을 팀원들과 논의하였는데, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;각 아이디어들의 장단점이나 특징들을 표에 정리하면서 투표를 진행한 결과, 회고덕으로 진행되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCnxx6/btrGg0Tb4p2/hO9j9AV6HCW4ypgAOk3S0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCnxx6/btrGg0Tb4p2/hO9j9AV6HCW4ypgAOk3S0k/img.png&quot; data-alt=&quot;사용하지 않는 아이디어도 있어서 모자이크 처리. 이것말고도 정말 많이 정리했다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCnxx6/btrGg0Tb4p2/hO9j9AV6HCW4ypgAOk3S0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCnxx6%2FbtrGg0Tb4p2%2FhO9j9AV6HCW4ypgAOk3S0k%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;514&quot; height=&quot;602&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사용하지 않는 아이디어도 있어서 모자이크 처리. 이것말고도 정말 많이 정리했다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내가 먼저 제시했던 기능이니만큼 나 자신이 필요하다고 여겼던 부분은 바로 회고를 모아보고, 또 쉽게 만드는 기능이었다. 아마 앞으로도 그런 부분에 강점을 두고 계속 기획이나 개발이 진행될 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;특히나 내가 회고라는 개념 자체를 우테코에 와서 제대로 이해하게 되고, 이를 적용하였기 때문에 더 인상 깊은 프로젝트가 될 것 같다. &lt;span&gt;이후에 우테코에 들어오는 크루들도 우리 서비스를 쓸 수 있기를 바라는 바람을 가지고 아이디어와 기획의 시작을 진행하고자 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;회고덕 팀&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사실 레벨 3을 시작하며 가장 많이 고민한 것은 팀에 대한 문제일 것이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사실 우테코에서 만난 사람들 중 배울만한 점이 없는 사람은 단 한 명도 없었고, 그렇기에 늘 상대 크루에게 배우면서 감사한 마음으로 페어나 프로그램에 참여하였다. 하지만 이번에는 아무래도 팀으로 한 레벨을 함께하는 사이가 될 테니, 가능하면 서로 많은 도움을 줄 수 있는 크루들과 팀이 되기를 바랐다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결과적으로 말하자면, 개인적으로 팀에는 매우 매우 만족하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;판다, 소주캉, 아리, 콤피, 돔하디까지! 전부 서로의 의견을 잘 들어주고, 회의에서는 진지하게 더 올바른 방향을 고민하며 사적으로는 조금 더 친하고 편한 관계가 되어가는 것 같아서 기쁘다. 나 또한 팀원들에게 많이 배워갈 수 있도록 도와주는 페어가 되기 위해서 더 노력하고, 공부한 것들을 공유해보려고 노력할 예정이다.  &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;UI UX 특강&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;솔직해지자면 개인적으로 초반에는 정말... 졸렸다. 많은 팀들이 쉬는 시간이 거의 없는 채로 5시간 정도의 강의를 듣다 보니 살짝 피곤한 것도 있었던 것 같다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그러나, 2회 차 때 우리 팀의 주제로 진행하면서 실제 크루들과 인터뷰를 진행하면서부터는 많은 것을 얻어갈 수 있었다. 특히, 기존에 기획했을 때 주요하게 생각했던 부분과는 달리, '템플릿' 기능을 많은 크루들이 원하고 필요하다고 생각했다는 사실을 알게 된 점이 가장 큰 소득이었다. (회고의 왕, 준 코치께 조언을 구할 수 있는 기회도 얻었다.) &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1주차를 마치며&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;늘 생각하는 것이지만 우테코에서는 늘 첫 주가 가장 바쁘다. 기획, 레벨 인터뷰, UX 워크샵, 포수타 등등... 여러 가지 일들이 지나가다 보니 코드는 제대로 쓰지도 못하고 지나가버린 점이 조금 아쉽게 느껴지기도 한다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이번 주에 논의하고 같이 고민해본 내용들을 기반으로, 이제 다음주부터는 기능을 하나씩 만들고, 문제들을 접하면서 이를 팀원들과 함께 풀어나가는 과정 역시 회고에 담을 수 있으면 좋을 것 같다. (가능하다면, 우리 프로젝트에도!)&lt;/span&gt;&lt;/p&gt;</description>
      <category>우아한 테크코스/회고</category>
      <category>우테코</category>
      <category>회고</category>
      <category>회고덕</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/124</guid>
      <comments>https://runa-nam.tistory.com/124#entry124comment</comments>
      <pubDate>Mon, 4 Jul 2022 00:48:59 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] DB ConnectionPool이란?</title>
      <link>https://runa-nam.tistory.com/123</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DB Connection&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB를 사용하기 위해 DB와 애플리케이션 간 통신을 할 수 있는 수단.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 드라이버와 DB 연결정보를 담은 URL이 필요하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DB 커넥션풀&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 커넥션 객체를 여러 개 생성하여 풀(Pool)에 담아놓고 필요할 때 꺼내쓰는 방식. 즉, 자주 쓰는 객체를 미리 만들고, 사용한 다음 필요할 때마다 가져간 다음에 반납하는 방식(=Pooling)을 의미한다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;간단히 정리하자면 다음과 같다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여러 개의 DB Connection을 하나의 Pool에 모아놓고 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 커넥션 객체를 여러 개 생성한 뒤 Pool에 담아놓고 필요할 때 불러와서 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약, 빌려줄 수 있는 Connection이 없다면 Connection 객체가 반환할 때 까지 클라이언트는 대기 상태로 전환&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용이 끝난 커넥션 객체는 다른 작업에서 다시 사용할 수 있도록 Pool에 반환&lt;/span&gt;&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;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;싱글 커넥션의 문제&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그런데, 굳이 여러개를 생성하지 않고 하나만 생성해서 진행해도 되지 않을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 방식을 '싱글 커넥션'이라 하였을 때, 아래의 이미지를 확인해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVayW7/btrC8BgTDs7/C0g1CnjIesuJaup3i5ySMK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVayW7/btrC8BgTDs7/C0g1CnjIesuJaup3i5ySMK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVayW7/btrC8BgTDs7/C0g1CnjIesuJaup3i5ySMK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVayW7%2FbtrC8BgTDs7%2FC0g1CnjIesuJaup3i5ySMK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;321&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;625&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아래의 그림에서 DAO 1&amp;rarr;2&amp;rarr;3의 순서로 이루어졌다고 하였을 때, 3에서 롤백이 이루어진다면 DAO 1, 2에서 진행된 내용도 이전 상태로 되돌려지게 된다. 이는 굉장히 &lt;b&gt;치명적인 단점&lt;/b&gt;이 된다. 그렇기 때문에 우리는 각각 다른 Connection 연결을 가져야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;해결책&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. 매번 SQL 작업을 할 때마다 DB 커넥션 객체를 만든다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1653462226831&quot; class=&quot;pgsql&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Connection conn = null;
PreparedStatement  pstmt = null;
ResultSet rs = null;

try {
    sql = &quot;SELECT * FROM T_BOARD&quot;

    // 1. 드라이버 연결 DB 커넥션 객체를 얻음
    connection = DriverManager.getConnection(DBURL, DBUSER, DBPASSWORD);

    // 2. 쿼리 수행을 위한 PreparedStatement 객체 생성
    pstmt = conn.createStatement();

    // 3. executeQuery: 쿼리 실행 후
    // ResultSet: DB 레코드 ResultSet에 객체에 담김
    rs = pstmt.executeQuery(sql);
    } catch (Exception e) {
    } finally {
        conn.close();
        pstmt.close();
        rs.close();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 방식처럼 만들게 되면, 실행 속도가 느려지는 단점이 생긴다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;커넥션을 맺을 때마다 DB 서버가 사용자 인증 및 권한 검사, 요청 처리를 위한 준비 작업까지 진행되기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. DB 커넥션 풀&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 커넥션 풀은 각 요청에 대해 별도의 커넥션 객체를 사용하기에 다른 작업에 영향을 주지 않으며, 사용한 커넥션 객체는 버리지 않고 풀에 보관되므로 속도에도 영향을 주지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;831&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w8gV8/btrC4OPTLX8/oNWfpot5hfHgrrXfqTDsG1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w8gV8/btrC4OPTLX8/oNWfpot5hfHgrrXfqTDsG1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w8gV8/btrC4OPTLX8/oNWfpot5hfHgrrXfqTDsG1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw8gV8%2FbtrC4OPTLX8%2FoNWfpot5hfHgrrXfqTDsG1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;453&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;831&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바에서는 기본적으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;DataSource&lt;/b&gt; &lt;/span&gt;인터페이스를 사용하여 커넥션풀을 관리하며, Spring에서는 사용자가 직접 관리할 필요 없이 자동화된 기법을 제공한다. 참고로 PSring 2.0 이전에는 tomcat-jdbc, 2.0 이후에는 &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&quot;org.springframework.boot:spring-boot-starter-jdbc&quot;&lt;/span&gt; 내에 HikariCP를 기본 옵션으로 제공하고 있다. (참고 -&lt;a href=&quot;https://github.com/brettwooldridge/HikariCP-benchmark&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt; HikariCP의 장점&lt;/a&gt; &amp;rarr; 커넥션 풀의 관리가 잘 되어있다.)&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바 웹 개발 워크북 - 5.12 DB 커넥션 풀&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <category>DB 커넥션 풀</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/123</guid>
      <comments>https://runa-nam.tistory.com/123#entry123comment</comments>
      <pubDate>Wed, 25 May 2022 16:07:13 +0900</pubDate>
    </item>
    <item>
      <title>[우테코] 레벨 2 지하철 노선도 미션 회고</title>
      <link>https://runa-nam.tistory.com/122</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지하철 노선도 미션은... 여러모로 대박이었다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epdBSC/btrCpQtQ9RG/6Kn4zlA9yQVIl9P72ffMOk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epdBSC/btrCpQtQ9RG/6Kn4zlA9yQVIl9P72ffMOk/img.jpg&quot; data-alt=&quot;네이버 웹툰 - 모죠의 일지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epdBSC/btrCpQtQ9RG/6Kn4zlA9yQVIl9P72ffMOk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FepdBSC%2FbtrCpQtQ9RG%2F6Kn4zlA9yQVIl9P72ffMOk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;414&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1168&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네이버 웹툰 - 모죠의 일지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;먼저, 아리와 페어 프로그래밍을 하였다는 점이다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사실 이번 미션은 페어 초반부터 코로나 이슈가 터져 갑자기 집으로 돌려보내질 위기에 있었는데 (&lt;s&gt;통학시간은 왕복 세시간이다.&lt;/s&gt;) 아리 덕분에 페어 프로그래밍을 얼추 끝내고 집에 도착할 수 있었다. 테스트 코드가 통과하자 카페에서 박수쳤던게 아직도 기억난다. 레벨 로그 말하기에서 시작한 인연이 이렇게 잘 이어지고 좋은 동료와 친해질 수 있어서 감사한 시간이었다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;두 번째는 스프링에 또 부딪혔다는 점이다...&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;레벨 1에서 분명 TDD와 객체지향 프로그래밍에 익숙해졌다고 생각했는데 스프링과 웹을 진행하다 보니 다시 돌아온거같은 기분이다. 내가 객체지향적인 설계를 잘 못하는 건가? 내가 조금 더 깊게 생각을 했어야 했나? 같은 생각을 계속하게 되었던 것 같다. 웹 자체에도 익숙해져야 할 텐데... 앞으로도 배워야 될 것들이 한가득이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래도, 이런 고민들은 브리랑 면담을 하며 어느정도 정리된듯한 느낌이다. 확실히 우테코에서 코치분들께 힘든 부분들은 의지하며 활동하다 보니 이렇게 흔들릴 때마다 도움을 청할 수 있어서 좋은 것 같다. 결국 우테코의 교육은 크루들의 자유를 보장하는 것이지, 방임하는 것이 아님을 한번 더 깨닫게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;공부한 내용 정리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/118&quot;&gt;[Spring] @Component vs @Bean&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/119&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Spring] 의존 자동 주입, @AutoWired&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/120&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;도메인이란 무엇인가?&lt;/a&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아무래도 비즈니스 로직을 만들고 짜는데 시간을 오래 쓰다보니 개인적으로 정리한 내용이 많지는 않은 것 같다.&amp;nbsp;&lt;/span&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1027&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CwISh/btrCrPOFLdH/qvJry06gGF6e0I6eITSf51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CwISh/btrCrPOFLdH/qvJry06gGF6e0I6eITSf51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CwISh/btrCrPOFLdH/qvJry06gGF6e0I6eITSf51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCwISh%2FbtrCrPOFLdH%2FqvJry06gGF6e0I6eITSf51%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;1027&quot; height=&quot;662&quot; data-origin-width=&quot;1027&quot; data-origin-height=&quot;662&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;1588&quot; data-origin-height=&quot;651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBB6bm/btrCs3lnbK8/xGh0hjGHf2mEY9o9RYWkxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBB6bm/btrCs3lnbK8/xGh0hjGHf2mEY9o9RYWkxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBB6bm/btrCs3lnbK8/xGh0hjGHf2mEY9o9RYWkxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBB6bm%2FbtrCs3lnbK8%2FxGh0hjGHf2mEY9o9RYWkxK%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;1588&quot; height=&quot;651&quot; data-origin-width=&quot;1588&quot; data-origin-height=&quot;651&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@DirtiesContext와 @Transactional 때문에 에러 처리에도 시간을 꽤 쏟았다. truncate를 사용하는 방식으로 수정하였으나, 해당 부분은 조금 더 공부해서 제대로 정리해보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;추가적인 회고&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이번 미션에서는 Spring을 어떻게 공부하는 것이 맞는지 나만의 템포를 찾아가는 것이 오래걸린 것 같다. 아직 스프링으로 웹 개발을 하며 다른 크루들보다 부족한 점이 더 많이 보이는 것 같기도 하다. 그래도 레벨 2 시작시기 보다는 나아졌다는 것에 초점을 두고 남과 비교하지 않고 나 자신의 성장에 초점을 더 맞춰보려고 한다.  &lt;/span&gt;&lt;/p&gt;</description>
      <category>우아한 테크코스/회고</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/122</guid>
      <comments>https://runa-nam.tistory.com/122#entry122comment</comments>
      <pubDate>Tue, 24 May 2022 15:28:21 +0900</pubDate>
    </item>
    <item>
      <title>[TDD] 테스트 코드 작성 순서와 종류</title>
      <link>https://runa-nam.tistory.com/121</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;레벨 2가 되고 Spring을 사용하다 보니 TDD에 대한 부분이 다소 무너지는 것 같아서 테스트 주도 개발 시작하기 (최범균 저)의 내용을 간단하게 정리하여 이야기해보고자 한다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;테스트 코드 작성 순서&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;쉬운 경우에서 어려운 경우로 진행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예외적인 경우에서 정상인 경우로 진행&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;초반에 복잡한 테스트부터 시작하면 안 되는 이유&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;초반부터 다양한 조합을 검사해야 하는 코드를 만들게 되면, 해당 테스트를 통과시키기 위한 코드가 많아진다. 한 번에 완벽한 코드를 짤 수는 없기 때문에 작성하기 쉬운 코드를 작성하는 것이 개발 속도에 많은 도움이 된다고 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;한 번에 구현하는 시간이 짧아지면 디버깅할 때 유리하고, 원인을 찾기도 더 빠르다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;예외상황을 먼저 테스트해야 되는 이유&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예외상황은 복잡한 if 블록을 동반할 때가 많다. 예외 상황을 전혀 고려하지 않은 코드에 예외상황을 반영한다면 중간에 코드를 뒤집는 등의 문제가 발생할 수 있다. 이는 코드를 복잡하게 만들 가능 성이 있다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한, 예외상황을 발견한 이후에 테스트에 반영한다면 프로그램 실행 중에 심각한 오류가 발생할 가능성이 있으나, 미리 예외상황을 정의하고 그에 따라서 테스트 코드를 작성하면 많은 도움이 될 수 있다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;테스트 범위와 종류&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;테스트 범위&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반적인 웹 어플리케이션은 아래와 같은 구조를 갖는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UDHwY/btrCxGE1h4C/KtZIOoDKloBjgk1qrnjtrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UDHwY/btrCxGE1h4C/KtZIOoDKloBjgk1qrnjtrk/img.png&quot; data-alt=&quot;웹 어플리케이션의 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UDHwY/btrCxGE1h4C/KtZIOoDKloBjgk1qrnjtrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUDHwY%2FbtrCxGE1h4C%2FKtZIOoDKloBjgk1qrnjtrk%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;716&quot; height=&quot;263&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;웹 어플리케이션의 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 구조를 기반으로 테스트 범위에 따른 테스트의 종류를 정리해보면 다음과 같은 이미지가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQyQhR/btrCyZxmdJh/5WbSYmPIUTYgdrzTxpM1qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQyQhR/btrCyZxmdJh/5WbSYmPIUTYgdrzTxpM1qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQyQhR/btrCyZxmdJh/5WbSYmPIUTYgdrzTxpM1qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQyQhR%2FbtrCyZxmdJh%2F5WbSYmPIUTYgdrzTxpM1qK%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;614&quot; height=&quot;415&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 적힌 테스트의 용어는 종종 다르게 적혀있기도 한데, 테스트 관련 용어가 문맥이나 사용자에 따라 다양하게 사용되기 때문이다. 개발 완료 후에 진행되는 최종 테스트를 '통합 테스트'라고 부르기도 하고, 고객의 입장에서 요구한 기능을 올바르게 수행했는지 수행하는 테스트를 '인수 테스트'라고 부르나, 요건을 완료했는지 정의하기 위한 테스트를 '인수 테스트'라고 부르기도 한다. 용어 하나하나의 글자에 집중하기보다는 관련된 개념을 정확히 아는 것이 더 중요할 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;테스트 종류&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;기능테스트와 E2E 테스트&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;사용자 입장에서 시스템이 제공하는 기능이 올바르게 동작하는지 확인. (= 인수 테스트)&lt;/blockquote&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;비즈니스 쪽에 초점을 둔다 &amp;rarr; 누가, 어떤 목적으로, 무엇을 하는가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모든 구성요소를 하나로 엮어서 사용자의 관점에서 테스트한다.&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;소프트웨어 내부 코드에 관심을 갖지 않는 블랙박스 테스트이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;끝에서 끝(End to End, E2E)까지 모든 구성요소가 올바른지 검사한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Java에서는&amp;nbsp;RestAssured,&amp;nbsp;MockMvc&amp;nbsp;같은 도구를 활용하여 인수 테스트를 작성할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ex - 회원가입 기능이 올바르게 작동하는지 확인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;통합 테스트&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;시스템의 각 구성요소가 올바르게 연동되는지 확인&lt;/blockquote&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가 변경할 수 없는 부분(ex. 외부 라이브러리)까지 묶어서 검증&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 접근 등 전체 코드와 다양한 환경이 제대로 작동하는지 확인하는데 필요한 모든 작업 수행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;통합 테스트 수행 &amp;ne; 응용 프로그램의 완전한 작동&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실제 환경에서 발생하는 버그까지는 확인할 수 없음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 부트에서는 클래스 상단에&amp;nbsp;@SpringBootTest&amp;nbsp;어노테이션을 붙여 통합 테스트를 수행할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ex - 서버의 회원가입 코드를 직접 테스트&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;단위 테스트&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;개별 코드나 컴포넌트가 기대한 대로 동작하는지 확인&lt;/blockquote&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스 or 메서드 수준으로 대상 단위를 작게 설정해 테스트를 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내부 코드에 대한 지식을 반드시 알고 있어야 하는 화이트박스 테스트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Java는 주로&amp;nbsp;JUnit으로 테스트한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;테스트 범위에 따른 테스트 코드 개수와 시간&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테스트의 제약 수준 : 기능 테스트 &amp;gt; 통합 테스트 &amp;gt; 단위 테스트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테스트 실행 시간 : 기능 테스트 &amp;gt; 통합 테스트 &amp;gt; 단위 테스트&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;물론 이러한 코드를 기능 테스트나 통합 테스트에서 진행할 수 있으나, 테스트 속도는 통합 테스트보다 단위 테스트가 더 빠르기에 단위 테스트에서 다양한 상황을 다루고, 통합 테스트나 기능 테스트는 주요 상황에 초점을 맞춰야 한다. 테스트가 느려진다는 것은 결국 피드백이 느려지고, 개발 속도를 늦어지게 하는 원인이 될 수 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍 공부</category>
      <category>TDD</category>
      <category>기능 테스트</category>
      <category>단위 테스트</category>
      <category>인수 테스트</category>
      <category>테스트 코드</category>
      <category>통합 테스트</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/121</guid>
      <comments>https://runa-nam.tistory.com/121#entry121comment</comments>
      <pubDate>Thu, 19 May 2022 16:04:54 +0900</pubDate>
    </item>
    <item>
      <title>도메인이란 무엇인가?</title>
      <link>https://runa-nam.tistory.com/120</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발을 진행함에 있어서 '도메인' 이라는 개념을 처음 들어본 사람은 없을 것이라고 생각한다.&amp;nbsp;&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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 style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인이란?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;먼저, 사전적 정의를 얘기해보자&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; A domain is a field of study that defines a set of common requirements, terminology, and functionality for any software program constructed to solve a problem in the area of computer programming, known as domain engineering.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;컴퓨터 프로그래밍으로 문제를 해결하기 위해 만들 소프트웨어 프로그램을 위한 요구사항, 용어, 기능을 정의하는 학문 영역이 도메인 공학이다. - 위키백과&lt;/span&gt;&lt;/blockquote&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;간단하게 말하자면, &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;해결하고자 하는 문제의 영역 = 도메인&lt;/b&gt;&lt;/span&gt;이라고 생각하면 된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;SW 개발의 입장&lt;/b&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 도메인은 상위 - 하위 도메인 관계로도 구성될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때, 도메인을 설계함에 있어서 클래스 다이어그램이나, 상태 다이어그램과 같은 포함하여 다양한 방식을 사용할 수 있다. 다만, 이러한 방식보다 중요한 점은, 도메인 모델이 기본적으로 &lt;b&gt;도메인 자체를 이해하기 위한 개념 모델&lt;/b&gt;이라는 것이다.&amp;nbsp;&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;더하여, 이러한 도메인을 어떻게 보는가, 그리고 어떻게 구성하는가는 개발자 스스로 경험치를 높여서 알아봐야 하는 것이라고 생각한다. 현재 개인적으로는&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;i&gt;&amp;nbsp;'요구사항을 분석하여 상위 도메인부터 하위 도메인으로 나누어 분석한다'&amp;nbsp;&lt;/i&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 모델&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 도출한 모델은 크게 엔티티(Entity)와 Value로 나뉜다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1) Entity&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Entity는 실제, 객체라는 의미를 담고 있으며, 업무에 필요하고 유용한 정보를 저장 및 관리하기 위한 집합적인 것이라고 볼 수 있다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 가장 큰 특징은 &lt;b&gt;식별자를 갖는 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; Java에서는 eqauals()메서드와 hashCode()메서드를 구현한다면 각 객체를 식별할 수 있을 것이다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;+ 다른 권위자들의 정의&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222426; font-family: 'Noto Serif KR';&quot;&gt;- 변별할 수 있는 사물 (Peter Chen, 1976)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222426; font-family: 'Noto Serif KR';&quot;&gt;- 정보를 저장할 수 있는 어떤것 (James Martin, 1989)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222426; font-family: 'Noto Serif KR';&quot;&gt;- 정보를 저장할 수 있는 사람, 장소, 물건, 사건 그리고 개념 등(Thomas Bruce, 1992)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2) Value&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;값 객체는 식별자를 가지지 않으며, 말 그대로&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt; 값 그 자체&lt;/b&gt;&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Entity의 경우 식별자로 구분되기 때문에 식별자에 들어가지 않는 데이터가 변경된다고 다른 객체가 되는 것은 아니다. 하지만, Value의 경우에는 식별자가 없으므로 하나의 데이터라도 변경되게 된다면 다른 객체가 된다. 따라서, 값객체는 불변하게 구현하는 것이 좋다. 즉, setter를 구현하지 않고, 생성자를 통해서만 넣을 수 있도록 구성한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;두 개념간의 차이에 대한 설명은 (영어긴 하지만)&lt;a href=&quot;https://enterprisecraftsmanship.com/posts/entity-vs-value-object-the-ultimate-list-of-differences/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; 해당 글&lt;/a&gt;에 잘 설명되어있어서 간단한 요약정보만 가져오려고 한다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Entity는 식별자를 가지나, 값 객체는 갖지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;값 객체는 변경할 수 없으며 매번 새롭게 생성해야 하나,&amp;nbsp;엔티티는 변경 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;값 객체는 항상 하나 또는 여러 엔터티에 속해야 하며 자체적으로는 DB에 들어갈 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;값 객체는 데이터베이스에 자체 테이블이 없어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;추가 자료 - &quot;객체지향의 설계와 사실&quot; 저자이신 조영호 개발자님의 인터뷰&amp;nbsp;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;span style=&quot;background-color: #fafafa; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조영호 님 : 도메인은 저희 입장에서 &lt;b&gt;소프트웨어를 개발하는 대상 영역&lt;/b&gt;정도로 생각해도 무방합니다. 택시 앱을 만든다면 택시 기사님께 콜을 하고, 탑승하고, 요금을 지불하는 전 과정이 도메인이 됩니다. 물론 프로젝트를 할 때는 이 중에서 소프트웨어로 개발될 범위로 한정해서 범위를 좁하게 됩니다.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;이렇게 개발 대상과 범위를 간단히 도메인이라고 봐도 무방할 것 같습니다. 도메인 모델이란 도메인을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화시킨 것이라고 보시면 됩니다. 이게 꼭 클래스 다이어그램의 형식으로 표현될 필요는 없지만 객체지향 프로그래밍을 하는 경우에는 일반적으로 클래스 다이어그램의 표기법을 사용해서 도메인 모델을 정리하는게 여러모로 유용합니다.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;이렇게 하는 이유는 객체지향 패러다임에서 사용하는 유사한 기법에 기반하는게 코드와 모델을 유사한 형태로 유지하는데 이롭기도 하고 일단 도메인 모델을 이해하면 그 모델을 기반으로 코드를 쉽게 이해하고 수정할 수 있기 때문이죠. 다른 패러다임인 경우에는 그 패러다임에서 구현하기 쉬운 형태로 작성하면 되겠죠.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;마지막에 아키텍처 상에서 말하는 도메인 모델은 마틴 파울러가 PEAA에서 언급한 것으로 도메인 레이어를 객체지향적으로 구현하는 패턴을 가리키는 용어입니다. 즉 패턴의 일종이고 원래의 도메인 모델과는 약간 거리가 있습니다.&lt;/span&gt;&lt;/div&gt;
&lt;/div&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;a href=&quot;https://enterprisecraftsmanship.com/posts/entity-vs-value-object-the-ultimate-list-of-differences/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://enterprisecraftsmanship.com/posts/entity-vs-value-object-the-ultimate-list-of-differences/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://doing7.tistory.com/79&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://doing7.tistory.com/79&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부</category>
      <category>Entity</category>
      <category>value</category>
      <category>도메인</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/120</guid>
      <comments>https://runa-nam.tistory.com/120#entry120comment</comments>
      <pubDate>Sun, 15 May 2022 00:14:20 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 의존 자동 주입, @AutoWired</title>
      <link>https://runa-nam.tistory.com/119</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@Autowired 어노테이션을 이용한 의존 자동 주입&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대체로 스프링에서 자동주입이라고 말하면 @Autowired 어노테이션이 붙어있는 경우를 말한다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Autowired 를 해당 필드나 메서드에 붙이게 되면 스프링은 타입이 일치하는 빈 객체를 찾아서 주입하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 생성자 주입을 사용한다면, 생성자가 하나일 경우 @AutoWired 를 생략해도 자동 주입된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class MemberInfoPrinter {

	private MemberDao memDao;
	private MemberPrinter printer;

	public void printMemberInfo(String email) {
		Member member = memDao.selectByEmail(email);
		if (member == null) {
			System.out.println(&quot;데이터 없음\\n&quot;);
			return;
		}

		printer.print(member);
		System.out.println();
	}

	@Autowired // 의존 자동 주입
	public void setMemberDao(MemberDao memberDao) {
		this.memDao = memberDao;
	}

	@Autowired // 의존 자동 주입
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
}
&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;일치하는 빈이 없는 경우&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그렇다면, &lt;b&gt;@Autowired 애노테이션을 적용한 대상에 일치하는 빈이 없으면&lt;/b&gt; 어떻게 될까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&amp;rarr; 어찌보면 당연하게도 에러가 발생한다.&lt;/i&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;반대로 @Autowired 애노테이션을 적용한 대상에 일치하는 빈이 두개 이상이면?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&amp;rarr; 두개의 빈을 발견했다는 에러가 발생한다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;해결책&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Autowired 필드 명 매칭&lt;/span&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;타입 매칭 시도 &amp;rarr; 그 결과에 여러 빈이 있을 때 파라미터 명으로 빈 매칭&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Qualifier &amp;rarr; @Qualifier끼리 매칭 &amp;rarr; 빈 이름 매칭&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Primary 사용 &amp;rarr; 우선권 제공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Autowired
private DiscountPolicy discountPolicy -&amp;gt; rateDiscountPolicy // 필드명 변경

@Bean
@Qualifier(&quot;summaryPrinter&quot;) // 추가 구분자 생성. 이름을 변경하는 것은 아니다!
public MemberSummaryPrinter memberPrinter2() {
	return new MemberSummaryPrinter();
}

@Component
@Primary // 해당 빈이 가장 우선권을 가진다.
public class RateDiscountPolicy implements DiscountPolicy {
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 내용들끼리 겹칠 경우, &lt;b&gt;스프링의 특징&lt;/b&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링은 자동보다는 수동이, 넒은 범위의 선택권 보다는 좁은 범위의 선택권이 우선 순위가 높다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;따라서 여기서도&lt;b&gt;&amp;nbsp;@Qualifier&amp;nbsp;가 우선권이 높다&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@Autowired 어노테이션의 필수 여부&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자동 주입할 대상이 필수가 아닌 경우 = required 속성을 false로 지정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 5 버전부터는 Optional을 사용하는 경우가 더 많다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;required 속성을 지정하는 경우에는 자동 주입할 빈이 없을때 아예 호출하지 않지만, @Nullable 의 경우에는 자동 중비할 빈이 존재하지 않아도 메서드로 호출된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 매칭되는 빈이 없는 경우에는?

//호출 안됨
@Autowired(required = false)
public void setNoBean1(Member member) {
    System.out.println(&quot;setNoBean1 = &quot; + member);
}

//null 호출
@Autowired
public void setNoBean2(@Nullable Member member) {
    System.out.println(&quot;setNoBean2 = &quot; + member);
}

//Optional.empty 호출
@Autowired(required = false)
public void setNoBean3(Optional&amp;lt;Member&amp;gt; member) {
    System.out.println(&quot;setNoBean3 = &quot; + member);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;세 메서드의 결과&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;setNoBean1()&amp;nbsp;은&amp;nbsp;@Autowired(required=false)&amp;nbsp;이므로 호출 자체가 안된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;setNoBean2 = null &amp;rarr; 주입할 빈이 없어도 메서드는 호출된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;setNoBean3 = Optional.empty&lt;/span&gt;&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;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;자동주입과 수동주입&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링은&amp;nbsp;@Component&amp;nbsp;뿐만 아니라&amp;nbsp;@Controller&amp;nbsp;,&amp;nbsp;@Service&amp;nbsp;,&amp;nbsp;@Repository&amp;nbsp;처럼 계층에 맞추어 일반적인 애플리케이션 로직을 자동으로 스캔할 수 있도록 지원한다. 거기에 더해서 최근 스프링 부트는 컴포넌트 스캔을 기본으로 사용하고, 스프링 부트의 다양한 스프링 빈들도 조건이 맞으면 자동으로 등록하도록 설계했다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한, 수동으로 빈을 등록하고 관리하는 과정은 상당히 번거로우며, 관리할 빈이 많아지면 부담이 될 수 있다. 그리고 결정적으로 자동 빈 등록을 사용해도 OCP, DIP를 지킬 수 있다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;따라서, 데이터베이스 연결이나, 공통 로그 처리 처럼 업무 로직을 지원하기 위한 하부 기술이나 공통 기술과 같이 애플리케이션에 광범위하게 영향을 미치는 &lt;b&gt;기술 지원 객체&lt;/b&gt; 정도만 수동 빈으로 등록해서 설정 정보에 바로 나타나게 하는 것이 유지보수 하기 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;편리한 자동 기능을 기본으로 사용하자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;직접 등록하는 기술 지원 객체는 수동 등록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자&lt;/span&gt;&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;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://velog.io/@lychee/스프링-핵심-원리-07.-의존관계-자동-주입&quot;&gt;https://velog.io/@lychee/스프링-핵심-원리-07.-의존관계-자동-주입&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링5 프로그래밍 입문 챕터 4&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <category>Autowired</category>
      <category>의존 자동 주입</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/119</guid>
      <comments>https://runa-nam.tistory.com/119#entry119comment</comments>
      <pubDate>Sat, 7 May 2022 00:49:12 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] @Component vs @Bean</title>
      <link>https://runa-nam.tistory.com/118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;저번 포스팅 (&lt;a href=&quot;https://runa-nam.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Spring] Java Bean vs Spring Bean&lt;/a&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링 MVC에서는&amp;nbsp;@Controller,&amp;nbsp;@Service,&amp;nbsp;@Repository&amp;nbsp;등으로 빈으로 등록할 수 있으며, configuration 관련 객체들은&amp;nbsp;@Bean과&amp;nbsp;@Component&amp;nbsp;으로 스프링 컨테이너에 객체를 빈으로 등록할 수 있다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼&amp;nbsp;@Bean&amp;nbsp;과&amp;nbsp;@Component의 차이는 무엇일까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정답은 &lt;b&gt;No&lt;/b&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;@Bean과 @Component는 각자 선언할 수 있는 타입이 정해져있어&lt;/b&gt;&lt;/span&gt;&amp;nbsp;해당 용도외에는 컴파일 에러를 발생시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@Component&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가&amp;nbsp;직접 컨트롤이 가능한 Class들의 경우에사용하는 싱글톤 클래스 빈 생성 어노테이션&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Service, @Repository 어노테이션이 다음과 같은 Component 어노테이션에 포함된다. 대체로 한번 써봤으면 알듯이, 클래스에서 사용하게 된다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 어노테이션은 선언적(Declarative)인 어노테이션이다. 즉, 패키지 스캔 안에 이 어노테이션은 &quot;이 클래스를 정의했으니 빈으로 등록해줘.&quot; 라는 뜻이 된다. 이로써 스프링이 런타임시에 컴포넌트스캔을 하여 자동으로 빈을 찾고(detect) 등록하게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@Component
public class Utility {
   // ...
}
&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@Bean&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가 컨트롤이 불가능한&amp;nbsp;외부 라이브러리들을 Bean으로 등록하고 싶은 경우 에 사용&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@Bean의 경우 주로 @Configuration 어노테이션이 들어간 Spring 을 설정하는 클래스 내에 들어가는 메소드에서 선언한다. 즉, 해당 메서드에서 반환되는 객체(인스턴스)를 등록하도록 하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class AppConfig {
   @Bean
   public MemberService memberService() {
      return new MemberServiceImpl();
   }
}
&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;간단 정리&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 100px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 48.1395%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Bean&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.6279%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Component&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 48.1395%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메서드에 사용&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.6279%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스에 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 48.1395%; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가 컨트롤이 불가능한 외부 라이브러리 사용시 사용&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.6279%; height: 40px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가 직접 컨트롤이 가능한 내부 클래스에 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 48.1395%; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292e; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;setter나 builder 등을 통해서 사용자가 프로퍼티를 변경해서 생성한 인스턴스를 스프링에게 관리하라고 맡기는것&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.6279%; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292e; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스를 스프링이 알아서 인스턴스 생성한 후, 등록(bean으로) 하라고 맡기는 것&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://youngjinmo.github.io/2021/06/bean-component/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://youngjinmo.github.io/2021/06/bean-component/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jojoldu.tistory.com/27&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <category>bean</category>
      <category>component</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/118</guid>
      <comments>https://runa-nam.tistory.com/118#entry118comment</comments>
      <pubDate>Fri, 6 May 2022 16:21:50 +0900</pubDate>
    </item>
    <item>
      <title>[우테코] 레벨 2 체스미션 회고</title>
      <link>https://runa-nam.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우테코 레벨 2가 시작되고 벌써 첫 번째 미션이 마무리되었다.&amp;nbsp;&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/woowacourse/jwp-chess/pull/443&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;체스 2단계 PR 링크&lt;/a&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링을 해보긴 했으나, 이걸 했다고 말할 수 있을지 부끄러울 정도로 깊이가 얕았다는 것을 새삼 깨닫는 요즘이다. 집에 돌아와서 늘 한 시간 정도는 순수하게 스프링에 대한 내용만 공부하는 시간을 가지려고 노력하는데, 그것도 쉽지는 않다. 분명히 영한님께 배웠는데... 그게 다 어디간걸까...&amp;nbsp;&lt;/span&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9RWch/btrA6xOTR8H/QtzNS4sncYfuDoOoqVKTpK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9RWch/btrA6xOTR8H/QtzNS4sncYfuDoOoqVKTpK/img.jpg&quot; data-alt=&quot;공부뿐이어야 하는데... 이게 뭔소리야? 만 반복한 것 같기도.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9RWch/btrA6xOTR8H/QtzNS4sncYfuDoOoqVKTpK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9RWch%2FbtrA6xOTR8H%2FQtzNS4sncYfuDoOoqVKTpK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;355&quot; height=&quot;364&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공부뿐이어야 하는데... 이게 뭔소리야? 만 반복한 것 같기도.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;왜 이런 어노테이션을 쓰지? 왜 Spring에서는 Service, Controller, Dao(Repository)를 나누지? 왜 에러코드가 400이어야 할까? 와 같이 질리도록 '왜?' 라는 질문을 했던 것도 이번 미션이 가장 심했던 것 같다. 나름 성실해지려고 노력은 했는데, 그만큼의 성과가 나오지 않는 것 같아서 (아직도 스프링은... 어렵다) 힘들기도 했던 2주였다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;공부한 내용 정리&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;어노테이션 관련&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/111&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@RequestMapping&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/112&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@Controller와 @RestController&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/114&quot;&gt;@ExceptionHandler 와 @ControllerAdvice&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/116&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Controller에서 데이터 받기&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;스프링 개념 관련&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/113&quot;&gt;DI(Dependency Injection, 의존성 주입)이란?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://runa-nam.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Java Bean vs Spring Bean&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Controller&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트의 요청을 받는 역할&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요청에 대한 처리는&amp;nbsp;Service에게 전담&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Service에게 값을 받아서 다시 클라이언트에게 응답&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Service&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컨트롤러에게 받은 사용자의 요구사항 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인을 활용하여 로직 처리 (비즈니스 로직의 처리)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 정보가 필요할 때는&amp;nbsp;Repository에게 전담&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Repository&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB 관리(연결, 해제, 자원 관리)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DB CRUD 작업 처리&lt;/span&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;820&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GYbIw/btrA3s9kc6c/YKNvl0qrsRLIM7CxoWDlP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GYbIw/btrA3s9kc6c/YKNvl0qrsRLIM7CxoWDlP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GYbIw/btrA3s9kc6c/YKNvl0qrsRLIM7CxoWDlP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGYbIw%2FbtrA3s9kc6c%2FYKNvl0qrsRLIM7CxoWDlP1%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;820&quot; height=&quot;174&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;174&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;추가적인 회고&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;695&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tks8X/btrA7PIiMQL/V0BFb9ZIee3WhJabLWre51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tks8X/btrA7PIiMQL/V0BFb9ZIee3WhJabLWre51/img.png&quot; data-alt=&quot;밑에 더 있다...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tks8X/btrA7PIiMQL/V0BFb9ZIee3WhJabLWre51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTks8X%2FbtrA7PIiMQL%2FV0BFb9ZIee3WhJabLWre51%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;695&quot; height=&quot;890&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;밑에 더 있다...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;레벨 2에서의 목표는 내가 사용하는 기술들에 있어서 왜? 라고 생각한 부분들을 적어도 블로그 글이라도 한 번씩은 읽어보는 것으로 잡았다. 지하철에서라도 하나씩 읽을까 하는데, 막상 지하철에 타고나면 자거나 웹툰을 보고 있는 경우가 많다.&amp;nbsp;&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;얼마나, 어디까지 할 수 있을지 막막하다. 하지만, 리뷰어가 보내준 대로, 아직은 막막해도 2레벨동안 고민하고 또 크루들의 도움을 받으면서 발전할 수 있기를 바란다.  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>우아한 테크코스/회고</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/117</guid>
      <comments>https://runa-nam.tistory.com/117#entry117comment</comments>
      <pubDate>Tue, 3 May 2022 00:33:33 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Controller에서 데이터 받기</title>
      <link>https://runa-nam.tistory.com/116</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대체로 Spring으로 애플리케이션을 구성한다면, Controller에서 값을 받아서 비즈니스 로직을 처리하도록 넘기게 된다. 이때, 이러한 Method Parameter를 어떻게 받는지에 대해서 이야기해보고자 한다.&amp;nbsp;&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어찌보면 간단한 개념을 왜 정리하지? 싶을 수도 있으나, &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;를 보게되면 굉장히 많은 Method Argument를 제공하고 있다는 것을 볼 수 있다.&amp;nbsp;그렇기에, 일단 지금 쓰는 것을 제대로 소화하고, 그 이유를 이해한 뒤에 추가적으로 다른 개념들을 이해하면 더 좋을 것 같아서 정리해본다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@RequestParam&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1651071002648&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequestMapping(&quot;/pets&quot;)
public class EditPetForm {
    // ...

    @GetMapping
    public String getCatInfo(@RequestParam(&quot;catName&quot;) String catName, Model model) { 
        Cat cat = this.clinic.getCat(catName);
        model.addAttribute(&quot;cat&quot;, cat);
        return &quot;catInfo&quot;;
    }

    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 예시는 catName을 가지고 cat을 가져오도록 @Request Param을 설정한 경우이다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@RequestParam에서는 아래와 같은 세 타입의 옵션을 추가할 수 있다. (위의 예시는 name이 추가된 형태) 아래에 나와있듯이 기본 required 값이 true이기 때문에 만약 해당 값이 안들어가있다면 400에러가 바로 발생하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;이름&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;타입&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;name, value (Alias for name)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;String&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;파라미터 이름&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;required&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;bollean&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 파라미터의 필수 여부 (기본값 = true)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;defaultValue&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;String&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@RequestBody&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1651071215479&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/accounts&quot;)
public void handle(@RequestBody Account account) {
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;파라미터를 객체로 받게 되며, 이는 MessageConverter를 통해서 자바의 객체로 변환된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이로 인해서 Request가 가지는 특징은 다음과 같다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. POST요청과 함께 사용되어야한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;: MessageConverter는&amp;nbsp;HTTP 요청의 Body내용을 자바의 객체로 변환시킨다. GET방식의 메서드는 &lt;b&gt;애초에 Body가 존재하지 않기때문에&lt;/b&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. JSON 데이터받을 때 주로 사용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;: JSON이나 XML과 같은 데이터를&amp;nbsp;MessageConverter를 이용해서 자바의 객체로 변환한다.&amp;nbsp;&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3. Setter가 없어도 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;:&amp;nbsp;@ModelAttribute는 자바의 객체로 1:1&amp;nbsp;&lt;b&gt;매핑&lt;/b&gt;이기에&amp;nbsp;Setter가 필수지만 @RequestBody는 MessageConverter를 통한 자바의 객체로&amp;nbsp;&lt;b&gt;변환&lt;/b&gt;이기때문에 Setter가 없어도 괜찮다.&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다만 직렬화를 위해 기본 생성자는 필수다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한 데이터 바인딩을 위한 필드명을 알아내기 위해 getter나 setter 중 1가지는 정의되어 있어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여담을 조금 적자면 나는 스프링을 처음 공부할때 일단 뭔지도 모르고 쓰다보니 GET 메서드에서 해당 어노테이션을 썼다가 한동안 헤멘적이 있었다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@ModelAttribute&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1651071442419&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/owners/{ownerId}/pets/{petId}/edit&quot;)
public String processSubmit(@ModelAttribute Pet pet) {
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@RequestParam과 비슷한데, 차이점은 1:1로 하나씩 파라미터를 받을 때는 @RequestBody, 여러 파라미터들을 받아서 자바의 객체로 매핑하는 경우는 @ModelAttribute 라고 한다. 또한, @RequestBody와는 달리 Setter가 필수적으로 요구된다고 한다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;공식문서에 따르면, 위의 Pet 인스턴스의 경우 다음 방법중 하나로 소싱된다고 한다.&amp;nbsp;&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-modelattrib-methods&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@ModelAttribute메서드&lt;/a&gt; 에 의해 추가되었을 수 있는 모델에서 검색&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모델 특성이 @SessionAttributes 주석에 나열된 경우 HTTP 세션에서 검색&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모델 속성 이름이 경로 변수 또는 요청 매개변수와 같은 요청 값의 이름과 일치&amp;nbsp;하는 곳을 검색&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본 생성자로 인스턴스화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서블릿 요청 매개변수와 일치하는 인수를 사용하여 &quot;기본 생성자&quot;를 통해 인스턴스화&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개인적으로 ModelAttribute는 거의 사용해본 적이 없던터라, @RequestBody와 @ModelAttribute를 비교한 좋은 테코블 포스팅도 함께 공유한다. &lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-05-11-requestbody-modelattribute/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&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;&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;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;어노테이션이 없으면?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어노테이션이 없어도 값을 받을 수 있다. 다만, 이 경우에는 변수명과 동일한 파라미터 값만을 받을 수 있으며, String과 Long 타입은 @RequestParam으로 취급하지만 그 이외에는 @ModelAttribute로 취급하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1651072448071&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/getWithdoutAnnotation&quot;);
public void getWithdoutAnnotation(String id, UserVO user){
    log.info(&quot;get parameter&quot; + id);
    log.info(&quot;get parameter&quot; + user.getId());
}&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-methods&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-methods&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://doing7.tistory.com/10&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://doing7.tistory.com/10&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <category>@ModelAttribute</category>
      <category>@RequestBody</category>
      <category>@RequestParam</category>
      <category>controller</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/116</guid>
      <comments>https://runa-nam.tistory.com/116#entry116comment</comments>
      <pubDate>Thu, 28 Apr 2022 13:27:38 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Java Bean vs Spring Bean</title>
      <link>https://runa-nam.tistory.com/115</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링으로 개발을 진행하다보면, Bean이라는 개념을 정말 자주 만나게 된다. 무슨 느낌인지 대충은 알겠는데... 그래서 대체 Bean은 어떻게 만들어지는 것이고, 무슨 개념일까? 라고 물으면 막상 대답하기는 쉽지 않다. 그래서 일단 &lt;b&gt;Bean이라는 것이 무엇인지&lt;/b&gt;부터 알아보면서 Bean에 대해서 하나씩 정리해보고자 한다.&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Java Bean&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Bean은 스프링에서만 나오는 개념 아니야? 라고 물어볼 수 있으나, 자바에도 빈이라는 개념은 존재한다. &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94%EB%B9%88%EC%A6%88&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;위키피디아 링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; Java Bean은 데이터 표현을 목적으로 하는 특정 형태의 클래스라고 이해하면 된다. DTO나 VO와도 유사한 점이 있어 보인다. 개발 툴이나 응용프로그램에서 사용하고자 하는 개념으로 출발했다고 하나, 현재는 그 의미로는 거의 사용하지 않는 것으로 보인다. (만약 아니라면... 댓글로 달아주세요!)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;자바 빈이 지켜야 할 관례&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스는 직렬화되어야 한다.(클래스의 상태를 지속적으로 저장 혹은 복원 시키기 위해)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스는 기본 생성자를 가지고 있어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스의 속성들은&amp;nbsp;&lt;i&gt;get&lt;/i&gt;,&amp;nbsp;&lt;i&gt;set&lt;/i&gt;&amp;nbsp;혹은 표준 명명법을 따르는 메서드들을 사용해 접근할 수 있어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스는 필요한 이벤트 처리 메서드들을 포함하고 있어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어떤 입장에서는 POJO(Plain Old Java Object)와 유사한 개념으로 보는 입장도 있는 것 같다.&amp;nbsp;&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 말로 설명하면 이해가 잘 안되니, 코드로 설명하자면 다음과 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650964935498&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SampleJavaBean {

	// 필드는 private로 선언
    private String bean;
    private int beanValue;

	// 전달 인자가 없는(no-argument) 생성자
    public SampleJavaBean() {
    
    }
		
	// getter
    public String getBean() {
        return beanName;
    }
    
	// setter
    public void setBean(String bean) {
        this.bean = bean;
    }

    public int getBeanValue() {
        return beanValue;
    }

    public void setBeanValue(int beanValue) {
        this.beanValue = beanValue;
    }
}&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Spring Bean&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링에서의 Bean은 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;스프링 IoC 컨테이너가 관리하는 Java 객체&lt;/b&gt;&lt;/span&gt;를 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;=&amp;nbsp; &lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;스프링에 의해 생성되고, 라이프 사이클을 수행하고, 의존성 주입이 일어나는 객체&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 Spring Bean을 등록하는 방법은 크게 두가지로 나뉘어진다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Component Scan을 통해서 등록하기 (Annotation을 이용한 방법)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빈 설정파일에 직접 등록하기&lt;/span&gt;&lt;/li&gt;
&lt;/ol&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://jjingho.tistory.com/10&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jjingho.tistory.com/10&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <category>bean</category>
      <category>스프링 빈</category>
      <category>자바 빈</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/115</guid>
      <comments>https://runa-nam.tistory.com/115#entry115comment</comments>
      <pubDate>Tue, 26 Apr 2022 18:37:42 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] @ExceptionHandler 와 @ControllerAdvice</title>
      <link>https://runa-nam.tistory.com/114</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;예외처리&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로그래밍에서 예외처리는 매우 중요하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그렇지만 try-catch나 if문으로 예외를 잡거나 상위메서드로 예외처리를 위임하는 등의 작업을 진행하더라도 코드는 쉽게 복잡해질 수 있고, 이는 비즈니스 로직보다 예외처리를 위한 코드에 더 힘쓰게 되는 결과를 초래한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5TPah/btrAqrijQej/Q2Q3TvXvddk2q0gXnPp8eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5TPah/btrAqrijQej/Q2Q3TvXvddk2q0gXnPp8eK/img.png&quot; data-alt=&quot;리뷰어의 코멘트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5TPah/btrAqrijQej/Q2Q3TvXvddk2q0gXnPp8eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5TPah%2FbtrAqrijQej%2FQ2Q3TvXvddk2q0gXnPp8eK%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;535&quot; height=&quot;653&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리뷰어의 코멘트&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;나 역시 해당 부분에 있어서 잘 모르는 부분이 존재했고, 또한 기존 Spark 구현 코드를 Spring으로 이식하는 과정에서 예외처리와 관련된 문제들을 해결하기 위해 @ExceptionHandler와 @ControllerAdvice라는 키워드를 조언받았기에, 이에 대해서 간단하게 정리해보고자 한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@ExceptionHandler&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@ExceptionHandler의 경우는 @Controller, @RestController가 적용된 Bean 내에서 발생하는 예외를 잡아 하나의 메서드에서 처리하는 기능을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650942203144&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
public class RestController { 
	//......

	// 하나만 지정할 경우 
    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity&amp;lt;String&amp;gt; myException(Exception e) {
        //...
    }
    
	// 여러개 지정도 OK 
    @ExceptionHandler({FileSytemException.class, RemoteException.class})
    public ResponseEntity&amp;lt;String&amp;gt; myException2(Exception e) {
        //...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;주의사항/알아 둘 것&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Controller, RestController에만 적용 가능하다. (@Service 같은 빈에서는 적용되지 않는다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;리턴 타입은 자유롭게 해도 된다. (Controller내부에 있는 메서드들은 여러 타입의 response를 할 것이다. 해당 타입과 전혀 다른 리턴 타입이어도 상관없다.) &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-return-values&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서에 나와있는 반환 값 링크&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메서드의 파라미터로 Exception을 받아왔는데 이것 또한 자유롭게 받아와도 된다. &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-args&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서의 메서드 인수 링크&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@ExceptionHandler를 등록한 Controller에만 적용된다. 다른 Controller에서 NullPointerException이 발생하더라도 예외를 처리할 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여기서 마지막 사항을 눈여겨보자. @ExceptionHandler만 존재할 경우, 해당 Controller에서만 적용된다는 단점이 존재한다. 즉, 비슷한 에러를 처리하기 위한 비슷한 코드가 중복으로 구성될 수 있다는 것이다. 이를 해결할 수 있는 방법이 바로 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;@ControllerAdvice&lt;/b&gt;&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;&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;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;@ControllerAdvice와 @RestControllerAdvice&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Spring은 앞서 말한 @ExceptionHandler의 한계를 극복하는 일환으로 전역적으로 예외를 처리하는 @ControllerAdvice와 @RestControllerAdvice를 각각 Spring3.2, Spring4.3부터 제공하고 있다.&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;각각의 차이는 앞에 Rest가 붙었다는 점에서 눈치챌 수 있듯, @Controller와 @RestController의 차이와 유사하다. (&lt;a href=&quot;https://runa-nam.tistory.com/112&quot;&gt;관련 글 - [Spring] @Controller와 @RestController)&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉, @RestControllerAdvice는 @ResponseBody가 붙어있는 형식으로, 응답 형식이 Json이라고 보면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650943064249&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody // 틀린그림찾기처럼 쳐다보다보면 이부분이 추가된 것을 볼 수 있다.
public @interface RestControllerAdvice { ...
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice { ...
}&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ControllerAdvice는 여러 컨트롤러에 대해 전역적으로 ExceptionHandler를 동일하게 적용해준다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;ControllerAdvice의 장점&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하나의 클래스로 모든 컨트롤러에 대해 전역적으로 예외 처리가 가능함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;직접 정의한 에러 응답을 일관성 있게 클라이언트에게 내려줄 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;별도의 try-catch문이 없어 코드의 가독성이 높아짐&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;ControllerAdvice의 주의점&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;한 프로젝트당 하나의 ControllerAdvice만 관리하는 것이 좋다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 여러 ControllerAdvice가 필요하다면 basePackages나 annotations 등을 지정해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;직접 구현한 Exception 클래스들은 한 공간에서 관리한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-05-10-controller_advice_exception_handler/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-05-10-controller_advice_exception_handler/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://jeong-pro.tistory.com/195&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jeong-pro.tistory.com/195&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/204&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mangkyu.tistory.com/204&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 공부/Spring</category>
      <category>controlleradvice</category>
      <category>exceptionHandler</category>
      <category>예외처리</category>
      <author>R55</author>
      <guid isPermaLink="true">https://runa-nam.tistory.com/114</guid>
      <comments>https://runa-nam.tistory.com/114#entry114comment</comments>
      <pubDate>Tue, 26 Apr 2022 17:19:36 +0900</pubDate>
    </item>
  </channel>
</rss>