<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>cornarong의 블로그</title>
    <link>https://cornarong.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 10 Apr 2026 00:50:54 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>cornarong</managingEditor>
    <image>
      <title>cornarong의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/4817218/attach/75a8b783127c4f5b941cdfbce2cc16d8</url>
      <link>https://cornarong.tistory.com</link>
    </image>
    <item>
      <title>ZIP파일 GItHub에 신규 레포지토리로 등록하기</title>
      <link>https://cornarong.tistory.com/87</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;GitHub에서 다운로드한 ZIP 파일을 새 레포지토리로 다시 등록하려면 다음 단계를 따르면 됩니다.&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;&lt;b&gt;ZIP 파일 압축 해제&lt;/b&gt;: 먼저, 다운로드한 ZIP 파일의 압축을 해제합니다. 이 파일에는 원래의 Git 저장소가 포함되어 있을 수 있습니다.&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;새 Git 레포지토리 생성&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;GitHub 웹사이트에 로그인합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오른쪽 상단의 + 버튼을 클릭하고 New repository를 선택합니다.&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;Create repository 버튼을 클릭하여 새 레포지토리를 생성합니다.&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;&lt;b&gt;Git 초기화 및 최초 commit 등록&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;압축 해제한 폴더에서 파일 주소창에&amp;nbsp; &quot;cmd&quot;를 입력하여 터미널을 열거나&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;아래의 명령어 순서로 이 폴더의 Git 초기화 및 최초 commit 등록&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1. git init&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; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;2. git add .&lt;/span&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; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;3. git commit -m &quot;Inital commit&quot;&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;&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;GitHub에서 새로 생성한 레포지토리의 URL을 복사합니다 (HTTPS 또는 SSH).&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;div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. git remote add origin &lt;a href=&quot;https://github.com/username/new-repository.git&quot;&gt;https://github.com/username/new-repository.git&lt;/a&gt; &lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: left;&quot;&gt;git&lt;span&gt; &lt;/span&gt;&lt;/span&gt;branch -M main &lt;/span&gt;
&lt;div style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: left;&quot;&gt;git&lt;span&gt; &lt;/span&gt;&lt;/span&gt;push -u origin main &lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;color: #333333; text-align: left;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;* 여기서 &lt;a href=&quot;https://github.com/username/new-repository.git&quot;&gt;https://github.com/username/new-repository.git&lt;/a&gt;는 새 레포지토리의 HTTPS의 주소를 붙여넣습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Git</category>
      <category>git</category>
      <category>ZIP파일</category>
      <category>신규등록</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/87</guid>
      <comments>https://cornarong.tistory.com/87#entry87comment</comments>
      <pubDate>Tue, 6 Aug 2024 14:35:15 +0900</pubDate>
    </item>
    <item>
      <title>Mybatis 다중 DB 연결 방법 (main, sub)</title>
      <link>https://cornarong.tistory.com/86</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;기존 설정되어있는 SE 데이터베이스 외에 추가로 FC 데이터베이스 연결이 필요한 상황에서 사용한 방법입니다.&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;/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;&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 style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;나머지는 정보가 많이 있으니 검색해보시기 바랍니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존 사용중인 Database : &lt;span style=&quot;color: #f89009;&quot;&gt;Se&lt;/span&gt; / 신규 추가된 Database : &lt;span style=&quot;color: #f89009;&quot;&gt;Fc&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #7e98b1; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. properties 또는 yml 설정&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #ef5369;&quot;&gt;* 주의 : &lt;span style=&quot;text-align: start;&quot;&gt;프리픽스(prefix)&lt;/span&gt; 설정란에 데이터베이스 &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #ef5369;&quot;&gt;주소를&amp;nbsp;&quot;url&quot; 로 사용중이라면 &quot;jdbc-url&quot;로 변경&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# SE DB
spring.datasource.se.jdbc-url=jdbc:log4jdbc:postgresql://{SE_ADDRESS}:{SE_PORT}/{SE_DATABASENAME}?sslmode=require
spring.datasource.se.username={SE_NAME}
spring.datasource.se.password={SE_PASSWORD}
spring.datasource.se.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

# FC DB
spring.datasource.fc.jdbc-url=jdbc:log4jdbc:postgresql://{FC_ADDRESS}:{FC_PORT}/{FC_DATABASENAME}?sslmode=require
spring.datasource.fc.username={FC_NAME}
spring.datasource.fc.password={FC_PASSWORD}
spring.datasource.fc.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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: #ef5369; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;* 사용할 DB에 따라 디렉토리, 파일을 정확히 나누어놓고 config 설정만 잘 해놓으면 끝.&lt;/b&gt;&lt;/span&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;b&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;span&gt;2. 기본 디렉토리 및 파일 설정&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;* Se와 Fc 디렉토리를 나누어 디렉토리에 따라 각각 다른 데이터베이스를 사용하도록 설정합니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;java config 추가&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;java/{프로젝트명}/config/SeDatabaseConfig .java 파일 추가&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;java/&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;{프로젝트명}&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;/config/FcDatabaseConfig.java 파일 추가&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;java mppper interface 디렉토리 추가&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;java/&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;{프로젝트명}&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;/mapper/se 디렉토리 추가&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;java/&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;{프로젝트명}&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;/mapper/fc 디렉토리 추가&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;resources mapper xml 디렉토리 추가&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; resources/mapper/se디렉토리 추가 &lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; resources/mapper/fc 디렉토리 추가 &lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;resources config 추가&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;resources/mybatis 디렉토리 추가&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;resources/mybatis/ mybatis-config.xml 파일 추가&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; SeDatabaseConfig.java &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;* @Primary 어노테이션으로 해당 데이터베이스가 메인이되는 Database라고 선언해줍니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = &quot;{프로젝트명}.mapper.se&quot;, sqlSessionFactoryRef = &quot;seSqlSessionFactory&quot;, sqlSessionTemplateRef = &quot;seSqlSession&quot;)
public class SeDatabaseConfig {

    @Primary
    @Bean(name = &quot;seDataSource&quot;)
    @ConfigurationProperties(prefix = &quot;spring.datasource.se&quot;)
    public DataSource seDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = &quot;seSqlSessionFactory&quot;)
    public SqlSessionFactory seSqlSessionFactory(@Qualifier(&quot;seDataSource&quot;) DataSource seDataSource, ApplicationContext applicationContext) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(seDataSource);
        sessionFactory.setConfigLocation(applicationContext.getResource(&quot;classpath:mybatis/mybatis-config.xml&quot;));
        sessionFactory.setVfs(SpringBootVFS.class);
        sessionFactory.setMapperLocations(applicationContext.getResources(&quot;classpath:mapper/se/*.xml&quot;));
        return sessionFactory.getObject();
    }

    @Primary
    @Bean(name = &quot;seSqlSession&quot;)
    public SqlSessionTemplate seSqlSession(@Qualifier(&quot;seSqlSessionFactory&quot;) SqlSessionFactory seSqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(seSqlSessionFactory);
    }

    @Primary
    @Bean(name = &quot;seTransactionManager&quot;)
    public DataSourceTransactionManager seTransactionManager(@Qualifier(&quot;seDataSource&quot;) DataSource seDataSource) {
        return new DataSourceTransactionManager(seDataSource);
    }
}&lt;/code&gt;&lt;/pre&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt; FcDatabaseConfig.java&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;* SeDatabaseConfig.java 의 @Primary 어노테이션만 제거하고, 경로만 Fc로 맞춰줍니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = &quot;{프로젝트명}.mapper.fc&quot;, sqlSessionFactoryRef = &quot;fcSqlSessionFactory&quot;, sqlSessionTemplateRef = &quot;fcSqlSession&quot;)
public class FcDatabaseConfig {

    @Bean(name = &quot;fcDataSource&quot;)
    @ConfigurationProperties(prefix = &quot;spring.datasource.fc&quot;)
    public DataSource fcDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = &quot;fcSqlSessionFactory&quot;)
    public SqlSessionFactory fcSqlSessionFactory(@Qualifier(&quot;fcDataSource&quot;) DataSource fcDataSource, ApplicationContext applicationContext) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(fcDataSource);
        sessionFactory.setConfigLocation(applicationContext.getResource(&quot;classpath:mybatis/mybatis-config.xml&quot;));
        sessionFactory.setVfs(SpringBootVFS.class);
        sessionFactory.setMapperLocations(applicationContext.getResources(&quot;classpath:mapper/fc/*.xml&quot;));
        return sessionFactory.getObject();
    }

    @Bean(name = &quot;fcSqlSession&quot;)
    public SqlSessionTemplate fcSqlSession(@Qualifier(&quot;fcSqlSessionFactory&quot;) SqlSessionFactory fcSqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(fcSqlSessionFactory);
    }

    @Bean(name = &quot;fcTransactionManager&quot;)
    public DataSourceTransactionManager fcTransactionManager(@Qualifier(&quot;fcDataSource&quot;) DataSource fcDataSource) {
        return new DataSourceTransactionManager(fcDataSource);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;mybatis-config.xml&lt;/span&gt;&lt;/b&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;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;!DOCTYPE configuration PUBLIC &quot;-//mybatis.org//DTD Config 3.0//EN&quot;
        &quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;&amp;gt;
&amp;lt;configuration&amp;gt;
    &amp;lt;settings&amp;gt;
        &amp;lt;!-- 캐시 활성화 여부 --&amp;gt;
        &amp;lt;setting name=&quot;cacheEnabled&quot; value=&quot;true&quot;/&amp;gt;
        &amp;lt;!-- null 값에 대해 setter를 호출할지 여부 --&amp;gt;
        &amp;lt;setting name=&quot;callSettersOnNulls&quot; value=&quot;rue&quot;/&amp;gt;        
        &amp;lt;!-- 지연 로딩 비활성화 --&amp;gt;
        &amp;lt;setting name=&quot;lazyLoadingEnabled&quot; value=&quot;false&quot;/&amp;gt;        
        &amp;lt;!-- 적극적인 지연 로딩 활성화 --&amp;gt;
        &amp;lt;setting name=&quot;aggressiveLazyLoading&quot; value=&quot;true&quot;/&amp;gt;        
        &amp;lt;!-- 다중 결과 집합 활성화 --&amp;gt;
        &amp;lt;setting name=&quot;multipleResultSetsEnabled&quot; value=&quot;true&quot;/&amp;gt;        
        &amp;lt;!-- 컬럼 레이블 사용 여부 --&amp;gt;
        &amp;lt;setting name=&quot;useColumnLabel&quot; value=&quot;true&quot;/&amp;gt;        
        &amp;lt;!-- 생성된 키 사용 여부 --&amp;gt;
        &amp;lt;setting name=&quot;useGeneratedKeys&quot; value=&quot;false&quot;/&amp;gt;        
        &amp;lt;!-- 자동 매핑 동작 설정 --&amp;gt;
        &amp;lt;setting name=&quot;autoMappingBehavior&quot; value=&quot;PARTIAL&quot;/&amp;gt;        
        &amp;lt;!-- 기본 실행기 타입 설정 --&amp;gt;
        &amp;lt;setting name=&quot;defaultExecutorType&quot; value=&quot;REUSE&quot; /&amp;gt;        
        &amp;lt;!-- 기본 문장 타임아웃(ms) 설정 --&amp;gt;
        &amp;lt;setting name=&quot;defaultStatementTimeout&quot; value=&quot;25000&quot; /&amp;gt;        
        &amp;lt;!-- null에 대한 JDBC 유형 설정 --&amp;gt;
        &amp;lt;setting name=&quot;jdbcTypeForNull&quot; value=&quot;NULL&quot;/&amp;gt;
    &amp;lt;/settings&amp;gt;
    &amp;lt;typeAliases&amp;gt;
        &amp;lt;typeAlias alias=&quot;hashMap&quot; type=&quot;java.util.HashMap&quot; /&amp;gt;
        &amp;lt;typeAlias alias=&quot;String&quot; type=&quot;java.lang.String&quot;/&amp;gt;
    &amp;lt;/typeAliases&amp;gt;
    &amp;lt;mappers&amp;gt;
    &amp;lt;/mappers&amp;gt;
&amp;lt;/configuration&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&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; 위 설정을 통해 Se와 Fc 데이터베이스를 구분하여 각각의 설정과 연결을 하고&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후로는 위에서 나누어놓은 디렉토리(SE, FC)에 따라 기존 방식대로 mybatis를 사용하면 됩니다.&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;Se mapper에 설정한 메소드, 쿼리는 SeDatabase를 바라보고 사용하고&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;Fc mapper에 설정한 메소드, 쿼리는 FcDatabase를 바라보고 사용하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>mybatis</category>
      <category>다중db</category>
      <category>샘플</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/86</guid>
      <comments>https://cornarong.tistory.com/86#entry86comment</comments>
      <pubDate>Thu, 4 Jul 2024 18:01:01 +0900</pubDate>
    </item>
    <item>
      <title>[JPA] 더티 체킹(Dirty Checking)? +  동시성 이슈/제어</title>
      <link>https://cornarong.tistory.com/85</link>
      <description>&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;1. 더티 체킹 개념&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;b&gt;더티 체킹(Dirty Checking)&lt;/b&gt;: JPA의 기능으로, 영속성 컨텍스트에 담긴 엔티티의 상태 변화를 자동으로 감지하여 트랜잭션 커밋 시점에 변경 사항을 데이터베이스에 반영하는 메커니즘.&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;2. 영속성 컨텍스트(Persistence Context)&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;b&gt;정의&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;&lt;b&gt;역할&lt;/b&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;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;3. 엔티티의 생명주기&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;b&gt;비영속 상태(New/Transient)&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;&lt;b&gt;영속 상태(Managed)&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;&lt;b&gt;준영속 상태(Detached)&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;&lt;b&gt;삭제 상태(Removed)&lt;/b&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;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;4. 더티 체킹 동작 원리&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;b&gt;변경 감지&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;&lt;b&gt;트랜잭션 커밋 시점&lt;/b&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;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;5. 트랜잭션 내에서의 더티 체킹 예제&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718263191916&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void updateUserName(Long userId, String newName) {
        // 1. 엔티티 조회: 영속성 컨텍스트에 담김
        User user = userRepository.findById(userId).orElseThrow(() -&amp;gt; new EntityNotFoundException(&quot;User not found&quot;));
        // 2. 엔티티 속성 변경: 더티 체킹 대상
        user.setName(newName);
        // 3. 트랜잭션 커밋: 변경 사항이 자동으로 데이터베이스에 반영
    }
}&lt;/code&gt;&lt;/pre&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;6. 주요 고려사항&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;b&gt;트랜잭션 사용&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;&lt;b&gt;영속성 컨텍스트&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;&lt;b&gt;명시적 저장&lt;/b&gt;: 경우에 따라 명시적으로 save 메서드를 호출하여 변경 사항을 저장할 수도 있음.&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 style=&quot;color: #000000; text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;7. &lt;b&gt;신규 엔티티와 더티 체킹&lt;/b&gt;&amp;nbsp;&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;신규 엔티티는 save 호출 시 영속성 컨텍스트에 담기며, 그 이후 변경 사항은 더티 체킹의 대상이 됨.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1718263325356&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void createUser(String name) {
    User user = new User();
    user.setName(name);
    userRepository.save(user);  // 엔티티가 영속성 컨텍스트에 담기고 데이터베이스에 저장됨
}&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;8. 동시성&amp;nbsp; 이슈&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;* 동시성 문제는 여러 사용자가 동시에 동일한 데이터를 수정할 때 발생할 수 있음. 예로 두 사용자가 동일한 엔티티를 동시에 수정하고 저장하는 경우 데이터 일관성 문제가 발생할 수 있다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;9. 해결(제어) 방법&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;- 여러가지 제어 방법이 있고 락을 사용하는 방법 두가지만 간략하게 정리했습니다. &lt;/b&gt;&lt;/span&gt;&lt;span&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;b&gt;1. 낙관적 락(optimistic lock) &lt;/b&gt;: 가장 일반적인 해결 방법으로, Optimistic Locking을 활용하여 충돌을 방지한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;엔티티에 버전 필드(ex. @Version)를 추가하여 버전이 일치하지 않는 경우에는 충돌로 간주하고 예외를 발생시키거나 복구한다.&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;/li&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;/li&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;/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;- optimistic lock &lt;/b&gt;예제)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718264998693&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
public class Entity {
    @Id
    private Long id;
    
    @Version
    private Long version; // Optimistic Locking을 위한 버전 필드
   
    // getters, setters, other fields...
}&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt; 2. 비관적 락(pessimistic lock) &lt;/b&gt;: 특정 엔티티를 읽을 때 잠금을 거는 방식. 이 방법은 특히 긴 트랜잭션 내에서 엔티티의 상태가 변경되지 않도록 보장한다. &quot;&lt;b&gt;LockModeType.PESSIMISTIC_WRITE&quot;&lt;/b&gt;를 사용하여 엔티티를 잠근다.&lt;/span&gt;&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;사용 방법&lt;/b&gt;: 비관적 락은 데이터가 수정될 가능성이 크거나 긴 시간 동안 데이터를 잠금(lock) 해야 할 때 사용된다. 데이터를 읽을 때 바로 데이터베이스에서 잠금을 걸어 다른 사용자가 해당 데이터를 수정할 수 없도록 한다.&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;장점&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;&lt;b&gt;적합한 상황&lt;/b&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;b&gt;&lt;b&gt;- pessimistic lock&lt;/b&gt; 예제)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1718265025323&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface EntityRepository extends JpaRepository&amp;lt;Entity, Long&amp;gt; {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query(&quot;SELECT e FROM Entity e WHERE e.id = :id&quot;)
    MyEntity findByIdWithPessimisticWriteLock(@Param(&quot;id&quot;) Long id);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;9.&amp;nbsp; 더티 체킹(Dirty Checking) 정리&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;div data-message-id=&quot;bcf9c687-d229-4e18-adf4-6867bda1da76&quot; data-message-author-role=&quot;assistant&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;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;* JPA 더티 체킹은 영속성 컨텍스트에서 엔티티의 상태 변화를 자동으로 감지하여 데이터베이스에 반영하는 핵심 기능.&lt;/span&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;/div&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. 자동 변경 감지&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;b&gt;2. 트랜잭션 커밋 시점&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;b&gt;3. 영속성 컨텍스트 필요&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;b&gt;4. 신규 엔티티의 저장&lt;/b&gt;: 신규 생성된 엔티티는 save() 메서드를 통해 데이터베이스에 저장되고, 이후에 발생하는 변경 사항도 더티 체킹을 통해 관리된다&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;5. 명시적 저장&lt;/b&gt;: 경우에 따라 명시적으로 save() 메서드를 호출하여 변경 사항을 데이터베이스에 반영할 수 있다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;10.&amp;nbsp; 동시성 해결 정리&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;b&gt;1. 낙관적 락 &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;b&gt;2. 비관적 락 &lt;/b&gt;: 충돌이 발생할 가능성이 크거나 데이터 일관성을 강제로 유지해야 할 때 사용하며, 데이터베이스 락을 활용 하여 데이터를 보호한다람쥐.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-message-id=&quot;bcf9c687-d229-4e18-adf4-6867bda1da76&quot; data-message-author-role=&quot;assistant&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JPA</category>
      <category>Dirty Checking</category>
      <category>JPA</category>
      <category>더티체킹</category>
      <category>동시성</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/85</guid>
      <comments>https://cornarong.tistory.com/85#entry85comment</comments>
      <pubDate>Thu, 13 Jun 2024 16:41:24 +0900</pubDate>
    </item>
    <item>
      <title>git clone - intellij 세팅</title>
      <link>https://cornarong.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&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;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;File -&amp;gt; new -&amp;gt; Get from Version Control (레포지토리 url 또는 깃허브 계정 연결 시 레포지토리 선택)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;* 레포지토리 구조 그대로 따올경우 gradle 인식 못하는 경우 발생&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;-&amp;gt; build.gradle 우클릭 gradle link? 머머~ 클릭&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Project Structure -&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;java version 설정&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Setting -&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Enable annotation processing 체크&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Build Tools -&amp;gt; gradle -&amp;gt; intelliJ IDEA로 설정(Build and run using, Run tests using)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;---------------------------&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;main 레포지토리 fork 후 내 계정에서 관리&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;브런치 하나 만들어서 개발 후&amp;nbsp; Pull requests 요청 팀장급 승인 후 merge 및 코멘트&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Git</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/84</guid>
      <comments>https://cornarong.tistory.com/84#entry84comment</comments>
      <pubDate>Mon, 30 May 2022 19:09:10 +0900</pubDate>
    </item>
    <item>
      <title>[Security] Success/Failure Handler Custom (인증성공,  인증실패 핸들러 커스텀)</title>
      <link>https://cornarong.tistory.com/83</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;b&gt;: Security에서 지원하는 인증성공, 인증실패 핸들러를 상속한 클래스를 직접 커스텀하여 사용해보자.&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;1. 인증성공 핸들러 커스텀&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;b&gt;2. 인증실패 핸들러 커스텀&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;b&gt;3. config 설정&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;b&gt;4. 익명클래스로 처리하는 방법&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;span style=&quot;color: #f89009;&quot;&gt;1. 인증성공 핸들러 커스텀&lt;/span&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;1. &lt;b&gt;SimpleUrlAuthenticationSuccessHandler&lt;/b&gt;를 상속한 커스텀 클래스 &lt;b&gt;CustomAuthenticationSuccessHandler&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;2. &lt;b&gt;requestCache&lt;/b&gt;와 &lt;b&gt;RedirectStragey&lt;/b&gt;를 사용하여 사용자가 인증 요청 성공시 이전에 접근하려 했던 자원(리소스)의 경로로 바로 보내기 위해서 설정해줍니다. 이전의 접근하려 했던 자원(리소스)가 없는 경우 null을 반환하도록 하여 &lt;b&gt;setDefaultTargetUrl&lt;/b&gt;에 설정해준 url로 보내줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1640697085258&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 인증 성공 핸들러 : CustomAuthenticationSuccessHandler
@Component
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private RequestCache requestCache = new HttpSessionRequestCache();

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {

        //기본 url 설정, savedRequest가 null일 경우 설정한 페이지로 보내기 위함이다.
        setDefaultTargetUrl(&quot;/&quot;);

        // 사용자가 인증을 시도하기 이전에 접근을 시도했던 자원이 없을경우 savedRequest는 null로 반환된다.
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if(savedRequest != null) {
            String targetUrl = savedRequest.getRedirectUrl();
            redirectStrategy.sendRedirect(request, response, targetUrl);
        }else{
            redirectStrategy.sendRedirect(request, response, getDefaultTargetUrl());
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #f89009; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. 인증실패 핸들러 커스텀&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;1. &lt;b&gt;SimpleUrlAuthenticationFailureHandler&lt;/b&gt;를 상속한 커스텀 클래스 &lt;b&gt;CustomAuthenticationFailureHandler&lt;/b&gt;를 만듭니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1640697195694&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 인증 실패 핸들러 : CustomAuthenticationFailureHandler
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

        String errorMessage = &quot;Invalid Username or Password&quot;; // 기본 예외 메시지
        
        // exceprion 처리
        if(exception instanceof BadCredentialsException) { 
            errorMessage = &quot;Invalid Username or Password&quot;;
        }else if(exception instanceof InsufficientAuthenticationException) {
            errorMessage = &quot;Invalid Secret Key&quot;;
        }
        // 파라미터로 error와 exception을 보내서 controller에서 처리하기 위함.
        setDefaultFailureUrl(&quot;/login?error=true&amp;amp;exception=&quot; + errorMessage);

        // 부모클래스의 onAuthenticationFailure로 처리를 위임하자.
        super.onAuthenticationFailure(request, response, exception);
    }
}&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;&lt;b&gt;successHandler&lt;/b&gt;와 다르게 &lt;b&gt;failureHandler&lt;/b&gt;에서 &lt;b&gt;super&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;-&amp;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;gt; 모두 super 클래스를 호출하지 않아도 상관은 없다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;3. SecurityConfig 설정&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1640697928437&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  @Autowired
  private final AuthenticationSuccessHandler customAuthenticationSuccessHandler;
  @Autowired
  private final AuthenticationFailureHandler customAuthenticationFailureHandler;

  http
  .formLogin()
    . // ~생략
    .successHandler(customAuthenticationSuccessHandler) // 1. 성공시 custom success 핸들러를 호출한다.
    .failureHandler(customAuthenticationFailureHandler) // 1. 실패시 custom failure 핸들러를 호출한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #f89009; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;4. 익명클래스로 SecurityConfig내에서 처리하기.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1640698185068&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  http
    .formLogin()
    . // ~생략
    .successHandler(new AuthenticationSuccessHandler() { // 2. 성공시 success 핸들러를 호출한다. 추가로 사용해보자
      // 로그인 성공시 authentication 정보를 매개변수로 -
      @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        RequestCache requestCache = new HttpSessionRequestCache();
        SavedRequest savedRequest = requestCache.getRequest(request, response); // savedRequest 안에 사용자가 가고자 했던 정보가 들어 있다.
        String redirectUrl = savedRequest.getRedirectUrl();
        // 인증에 성공하면 세션에 저장되어 있던 이전 정보(가고자 했던 경로)를 꺼내와서 이동 시킨다.
        response.sendRedirect(redirectUrl);
      }
    })
    .failureHandler(new AuthenticationFailureHandler() { // 2. 실패시 fail 핸들러를 호출한다. 추가로 사용해보자
      // 로그인 실패시 exception 정보를 매개변수로 -
      @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.sendRedirect(&quot;/loginPage&quot;);
      }
    });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;figure id=&quot;og_1640698799154&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 인프런 | 강의&quot; data-og-description=&quot;초급에서 중.고급에 이르기까지 스프링 시큐리티의 기본 개념부터 API 사용법과 내부 아키텍처를 학습하게 되고 이를 바탕으로 실전 프로젝트를 완성해 나감으로써 스프링 시큐리티의 인증과 &quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wJeHY/hyMSTMqqrC/rqgQh9XKNWx99na2Do5RT1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/FjcsL/hyMS2QaOhV/4yMGQE3qTaR8w49t7k6Gq0/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wJeHY/hyMSTMqqrC/rqgQh9XKNWx99na2Do5RT1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/FjcsL/hyMS2QaOhV/4yMGQE3qTaR8w49t7k6Gq0/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;초급에서 중.고급에 이르기까지 스프링 시큐리티의 기본 개념부터 API 사용법과 내부 아키텍처를 학습하게 되고 이를 바탕으로 실전 프로젝트를 완성해 나감으로써 스프링 시큐리티의 인증과&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Security</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/83</guid>
      <comments>https://cornarong.tistory.com/83#entry83comment</comments>
      <pubDate>Tue, 28 Dec 2021 22:43:18 +0900</pubDate>
    </item>
    <item>
      <title>[Security] 세션 제어 필터 : SessionManagementFilter, ConcurrentSessionFilter</title>
      <link>https://cornarong.tistory.com/82</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;세션 제어 필터 : SessionManagementFilter, ConcurrentSessionFilter&lt;/b&gt;&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;/h3&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;1. SessionManagementFilter의 핵심적인 4가지 기능&lt;/b&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;b&gt;1-1. 세션 관리&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;b&gt;1-2. 동시적 세션 제어&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;b&gt;1-3. 세션 고정 보호&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;b&gt;1-4. 세션 생성 정책&lt;/b&gt; : Always, If_Required, Never, Stateless&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;2. ConcurrentSessionFilter의 기능&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;color: #f89009;&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;/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;b&gt;session.isExpired() == true ( : 세션 만료되었습니까? == 참)&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;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&quot;This session has been expired&quot;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;-&amp;gt; SessionManagementFilter에서 이전 사용자 세션 만료 처리&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;gt; ConcurrentSessionFilter가 매 요청마다 세션 만료 여부 체크&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;정리하면 &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;b&gt;SessionManagementFilter&lt;/b&gt;와 &lt;b&gt;ConcurrentSessionFilter&lt;/b&gt;는 서로 연계하여&amp;nbsp;&lt;b&gt;1-2.동시적 세션 제어&lt;/b&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-origin-width=&quot;911&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN2pJM/btrnuXgowVz/insaX79lwWUSYcd1VOj9ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN2pJM/btrnuXgowVz/insaX79lwWUSYcd1VOj9ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN2pJM/btrnuXgowVz/insaX79lwWUSYcd1VOj9ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN2pJM%2FbtrnuXgowVz%2FinsaX79lwWUSYcd1VOj9ok%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;911&quot; height=&quot;420&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아래의 그림에서 user1과 user2를 예시로 세션 처리 과정의 전반적인 흐름을 그림으로 살펴보겠습니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNhcUn/btrns2hTpBb/rfr5TNgL0lK3yhuYBNVuYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNhcUn/btrns2hTpBb/rfr5TNgL0lK3yhuYBNVuYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNhcUn/btrns2hTpBb/rfr5TNgL0lK3yhuYBNVuYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNhcUn%2Fbtrns2hTpBb%2Frfr5TNgL0lK3yhuYBNVuYk%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;1031&quot; height=&quot;461&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;461&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;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;user1&lt;/span&gt; &lt;/b&gt;-&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;user2&lt;/span&gt; &lt;/b&gt;-&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;user1&lt;/span&gt; &lt;/b&gt;&amp;nbsp;순서대로 로그인 및 Get호출을 할 경우 세션이 처리되는 과정을 간략하게 글로 설명해보면 아래와 같습니다.&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;&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;user1&lt;/span&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;-&amp;gt; &lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;&lt;b&gt;ConcurrentSessionControlAuthenticationStrategy&lt;/b&gt;에서 세션 허용 개수 확인&lt;/span&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; &lt;b&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;ChangeSessionId&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;&lt;b&gt;AuthenticationStrategy&lt;/b&gt;에서 세션 고정 보호 기능 처리&lt;/span&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 style=&quot;color: #000000;&quot;&gt;-&amp;gt; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;RegisterSession&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;&lt;b&gt;AuthenticationStrategy&lt;/b&gt;에서 세션 정보 등록&lt;/span&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; 인증 성공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;user2&lt;/span&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 style=&quot;color: #000000;&quot;&gt;-&amp;gt; &lt;b&gt;ConcurrentSessionControlAuthenticationStrategy&lt;/b&gt;에서 세션 허용 개수 확인 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;-&amp;gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;허용 개수 초과 발생!&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1번 전략&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;b&gt;인증 실패 전략&lt;/b&gt; -&amp;gt; &lt;span style=&quot;letter-spacing: 0px; color: #ff0000;&quot;&gt;&lt;b&gt;SessionAuthenticationException&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 예외 발생 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px; color: #ff0000; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&amp;gt; 인증 실패&lt;/span&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;2번 전략 : 이떄 &lt;b&gt;SessionManagementFilter&lt;/b&gt;와 &lt;b&gt;ConcurrentSessionFilter&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;b&gt; 세션 만료 전략&lt;/b&gt; -&amp;gt; &lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;&lt;b&gt;session.expireNow()&lt;/b&gt;로 &lt;b&gt;기존 사용자 세션 만료 &lt;/b&gt;&lt;/span&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; 위에 동일 하게 user2의 세션 고정 보호 기능 처리 및 세션 정보 등록 &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; 인증 성공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;user1 &lt;/span&gt;&lt;/b&gt;Get호출 (세션 만료 전략으로 인해 user1 사용자의 세션은 만료 상태)&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 style=&quot;color: #000000;&quot;&gt;-&amp;gt; session. isExpired() == true &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;-&amp;gt; 세션 만료&lt;/span&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; 로그인 실패 (&lt;span style=&quot;letter-spacing: 0px; color: #ee2323;&quot;&gt;&lt;b&gt;This session has been expired&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1639050768522&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 인프런 | 강의&quot; data-og-description=&quot;초급에서 중.고급에 이르기까지 스프링 시큐리티의 기본 개념부터 API 사용법과 내부 아키텍처를 학습하게 되고 이를 바탕으로 실전 프로젝트를 완성해 나감으로써 스프링 시큐리티의 인증과 &quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b66p9Y/hyMEgobyv5/MLsWC9l8EDydM2m4OPspP1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/BVtOn/hyMEhAC1EH/S4C4jF5roYtKb0lFC1nFn0/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b66p9Y/hyMEgobyv5/MLsWC9l8EDydM2m4OPspP1/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/BVtOn/hyMEhAC1EH/S4C4jF5roYtKb0lFC1nFn0/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;초급에서 중.고급에 이르기까지 스프링 시큐리티의 기본 개념부터 API 사용법과 내부 아키텍처를 학습하게 되고 이를 바탕으로 실전 프로젝트를 완성해 나감으로써 스프링 시큐리티의 인증과&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Security</category>
      <category>ConcurrentSessionFilter</category>
      <category>SessionManagementFilter</category>
      <category>세션 제어 필터</category>
      <category>스프링 시큐리티</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/82</guid>
      <comments>https://cornarong.tistory.com/82#entry82comment</comments>
      <pubDate>Thu, 9 Dec 2021 20:54:21 +0900</pubDate>
    </item>
    <item>
      <title>[Security] 동시세션제어, 세션고정보호, 세션정책</title>
      <link>https://cornarong.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #ef6f53;&quot;&gt;&lt;b&gt;1. 동시 세션 제어&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&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;gt; 시큐리티에서 2가지 전략을 제공합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 이전 사용자 세션 만료&amp;nbsp;&lt;/span&gt;&lt;/b&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 현재 사용자 인증 실패&lt;/span&gt;&lt;/b&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용 방법)&lt;/span&gt;&lt;/b&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: #f89009;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;http.sessionManagement() : 세션 관리 기능이 작동&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement()
         .maximumSessions(1) // 최대 허용 가능 세션 수, -1 : 무제한 로그인 세션 허용
         .maxSessionsPreventsLogin(true) // 동시 로그인 차단, false : 기존 세션 만료(default)
         .invalidSessionUrl(&quot;/invalid&quot;) // 세션이 유효하지 않을 대 이동 할 페이지
         .expiredUrl(&quot;/expired&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;1번 전략 : .maxSessionsPreventsLogin(false) 사용시 기존 사용자 세션 만료&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번 전략 : .maxSessionsPreventsLogin(true) 사용시 새로운 사용자 인증예외 발생&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638968694763&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;exception : Maximum sessions of 1 for this principal exceeded&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #ef6f53;&quot;&gt;&lt;b&gt;2. 세션 고정 보호&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&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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용방법)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;http.sessionManagement()
        .sessionFixation().changeSessionId(); // 기본 값 (서블릿 3.1 이상의 기본 값)

        // 새로운 세션 할당, 기존 세션의 모든 어트리뷰트가 새로운 세션으로 이동한다. (서블릿 3.1 이하의 기본 값)
        //.sessionFixation().migrateSession()

        // 새로운 세션 생성, 기존 세션의 모든 어트리뷰트는 새로운 세션으로 옮겨지지 않는다.
        //.sessionFixation().newSession() // 새로운 세션 생성

        // 설정해제, 공격에 방치된다.
        //.sessionFixation().none();&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 세션 정책&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&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;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;http.sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
		//.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
  		//.sessionCreationPolicy(SessionCreationPolicy.NEVER)
		//.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    }&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;.sessionCreationPolicy(SessionCreationPolicy.&lt;b&gt;IF_REQUIRED&lt;/b&gt;) : &lt;span&gt;스프링 시큐리티가 필요 시 생성(기본 값)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;.sessionCreationPolicy(SessionCreationPolicy.&lt;b&gt;ALWAYS&lt;/b&gt;) :&amp;nbsp; 스프링 시큐리티가 항상 세션 생성&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;.sessionCreationPolicy(SessionCreationPolicy.&lt;b&gt;NEVER&lt;/b&gt;) : 스프링 시큐리티가 생성하지 않지만 이미 존재하면 사용&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;.sessionCreationPolicy(SessionCreationPolicy.&lt;b&gt;STATELESS&lt;/b&gt;) : 스프링 시큐리티가 생성하지도 않고 존재해도 사용하지 않음&lt;/span&gt;&lt;/p&gt;</description>
      <category>Spring Security</category>
      <category>동시 세션 제어</category>
      <category>세션 고정 보호</category>
      <category>세션 정책</category>
      <category>스프링 시큐리티</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/81</guid>
      <comments>https://cornarong.tistory.com/81#entry81comment</comments>
      <pubDate>Wed, 8 Dec 2021 22:43:14 +0900</pubDate>
    </item>
    <item>
      <title>[HTTP] 캐시(cache)와 조건부 요청</title>
      <link>https://cornarong.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;&quot;&gt;&lt;b&gt;학습내용&lt;/b&gt;&lt;/span&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;/li&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;/li&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;/li&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;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&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;&lt;b&gt;1. 캐시가 없을 경우&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;/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;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. 캐시를 적용할 경우&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;/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;gt; 유효시간이 남아있는 경우에도 매번 검증이 필요한 경우 네트워크 통신이 이루어지지만 Header의 메타 데이터만 주고 받아 데이터 원문이 담긴 Body는 생략되기 때문에 가볍다, 물론 데이터의 수정이 이루어진 경우를 대비해 검증 Header를 가지고 조건부 요청을 해야한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;검증 헤더 : Last-Modifed ,ETag&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조건부 요청 헤더 : If-Modified-Since, If-None-Match&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;비싼 네트워크 사용량을 줄일 수 있다.&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;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;캐시 유효 시간이 초과하면, 서버를 통해 데이터를 다시 조회하고 캐시를 갱신한다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #f89009;&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;&lt;b&gt;캐시 유효 시간이 초과해도, 서버의 데이터가 수정(갱신)되지 않으면 서버에서 304 Not Modified로 Header의 메타 정보만 응답한다. (Body는 생략)&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;&lt;b&gt;클라이언트는 서버측에서 보낸 응답 Header의 메타 정보로 캐시의 메타 정보를 갱신한다.&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;클라이언트는 캐시에 저장되어 있는 데이터를 재활용한다.&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;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;Last-Modified, ETag&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;&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;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;조건이 만족하면 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;조건이 만족하지 않으면 304 Not Modified&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Last-Modifed의 단점&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초 미만(0.x초)단위로 캐시 조정이 불가능 -&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;날짜 기반의 로직 사용&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 style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;데이터를 수정했지만 최총 데이터 결과가 똑같은 경우는 비효율적&lt;/b&gt;&lt;/span&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;/ul&gt;
&lt;/li&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;/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;ETag 정리&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;진짜 단순하게 ETag만 서버에 보내서 같으면 유지, 다르면 다시 받는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;캐시 제어 로직을 서버에서 완전히 관리한다.&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;&lt;b&gt;클라이언트는 단순히 이 값을 서버에 제공만 한다&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;예)&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;서버 배타 오픈 기간인 3일 동안 파일이 변경되어도 ETag를 동일하게 유지&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Cache-Control : 캐시 제어&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;Pragma : 캐시 제어(하위 호환) -&amp;gt; 정리 생략, 필요시 구글링(중요x)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Expires : 캐시 유효 기간(하위 호환) -&amp;gt; 정리 생략, 필요시 구글링(중요x)&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'; color: #f89009;&quot;&gt;&lt;b&gt;Cache-Control (캐시 지시어/directives)&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 : max-age&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;&lt;b&gt;Cache-Control : no-cahe&lt;/b&gt; : 데이터는 캐시해도 되지만, 항상 원서버(origin서버)에 검증하고 사용&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 : no-store&lt;/b&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;&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;클라이언트가 서버에 요청시 서버(원서버)가 먼곳에 위치할 경우(예를들어 한국-&amp;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;이때 사용되는 캐시제시어 public, private등이 있다.-&amp;gt; 정리 생략 필요시 구글링(중요x)&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #f89009;&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;/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;확실한 캐시 무효화 응답이 필요&lt;/b&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;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;Cache-Control : &lt;b&gt;no-cache&lt;/b&gt;, &lt;b&gt;no-store&lt;/b&gt;, &lt;b&gt;must-revalidate&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;Pragma:&amp;nbsp;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;letter-spacing: 0px; 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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. &lt;b&gt;Cache-Control : no-cache&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; -&amp;gt; 데이터는 캐시해도 되지만, &lt;span style=&quot;color: #ef6f53;&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;2. &lt;b&gt;Cache-Control : no-store&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; -&amp;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;3. &lt;b&gt;Cache-Control : must-revalidate&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; -&amp;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;&amp;nbsp; &amp;nbsp; -&amp;gt; 원 서버 접근 실패시 반드시 오류가 발생해야 한다. (504 Gateway Timeout)&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; -&amp;gt; 통신 구조 : 클라이언트 --&amp;gt; 프록시 서버 --&amp;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;4. &lt;b&gt;Pragma : no-cache&amp;nbsp;&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; -&amp;gt; HTTP 1.0 하위 호환이므로 낮은 버전 브라우저 호환을 위해 추가해준다.&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'; color: #f89009;&quot;&gt;&lt;b&gt;여기서 no-cache와 must-revalidate가 같은거 아닌가?&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;b&gt;프록시캐시 --&amp;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;&lt;b&gt;no-cache&lt;/b&gt; : 원서버에 검증을 후 클라이언트로 데이터를 보내야 하는데 네트워크가 알수 없는 문제로 순간 단절 됬으므로 우선 프록시 캐시 서버에 있는 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;오래된 데이터라도 보여주자&lt;/b&gt;&lt;/span&gt;. -&amp;gt; 200 OK&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;must-revalidate&lt;/b&gt; : 원서버에 검증을 해야 하는데 접근이 안된다. 중요한 데이터니까 항상 최신화된 데이터를 보내줘야 해! 클라이언트에 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;오류를 발생하자&lt;/b&gt;&lt;/span&gt;. -&amp;gt; 504 Gateway Timeout&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;figure id=&quot;og_1638776545853&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;모든 개발자를 위한 HTTP 웹 기본 지식 - 인프런 | 강의&quot; data-og-description=&quot;실무에 꼭 필요한 HTTP 핵심 기능과 올바른 HTTP API 설계 방법을 학습합니다., 웹 기술을 사용하는 개발자라면 누구나 OK!꼭 필요한 HTTP의 핵심을 알려드립니다.   확인해주세요!본 강의는 자바 스&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard&quot; data-og-url=&quot;https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bpbusU/hyMBZy6p8r/dEv8Bbo2FlikyF4MdeG8RK/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/kGxXy/hyMB9aDmtI/8BuxJ6VXqqY9OK6RRfciaK/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/x3VWl/hyMCb7mQYs/HMf9cyaKjkCxpFiDmjeSY1/img.jpg?width=1200&amp;amp;height=666&amp;amp;face=0_0_1200_666&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bpbusU/hyMBZy6p8r/dEv8Bbo2FlikyF4MdeG8RK/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/kGxXy/hyMB9aDmtI/8BuxJ6VXqqY9OK6RRfciaK/img.jpg?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/x3VWl/hyMCb7mQYs/HMf9cyaKjkCxpFiDmjeSY1/img.jpg?width=1200&amp;amp;height=666&amp;amp;face=0_0_1200_666');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;모든 개발자를 위한 HTTP 웹 기본 지식 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;실무에 꼭 필요한 HTTP 핵심 기능과 올바른 HTTP API 설계 방법을 학습합니다., 웹 기술을 사용하는 개발자라면 누구나 OK!꼭 필요한 HTTP의 핵심을 알려드립니다.   확인해주세요!본 강의는 자바 스&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>HTTP</category>
      <category>HTTP</category>
      <category>캐시</category>
      <category>캐시와 조건부 응답</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/80</guid>
      <comments>https://cornarong.tistory.com/80#entry80comment</comments>
      <pubDate>Mon, 6 Dec 2021 16:45:22 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 웹 스코프(request scope) 와 Provider / 프록시</title>
      <link>https://cornarong.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&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;/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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;웹 스코프의 종류&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;request&lt;/b&gt; : HTTP 요청 하나가 들어오고 나갈 떄 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리됩니다&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;session&lt;/b&gt; : HTTP Session과 동일한 생명주기를 가지는 스코프&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;application&lt;/b&gt; : 서블릿 컨텍스트(ServletContext)와 동일한 생명주기를 가지는 스코프&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;websocket&lt;/b&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;b&gt; requset 스코프를 기준&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;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCbHYS/btrmxGgkTsi/MZ4vxEOYG47Zqb4lYNvx50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCbHYS/btrmxGgkTsi/MZ4vxEOYG47Zqb4lYNvx50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCbHYS/btrmxGgkTsi/MZ4vxEOYG47Zqb4lYNvx50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCbHYS%2FbtrmxGgkTsi%2FMZ4vxEOYG47Zqb4lYNvx50%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;582&quot; height=&quot;296&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;329&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;그림과 같이 클라이언트 A와 B가 동시에 싱글톤 Controller에 HTTP request요청을 하였지만 &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;requset scope인 MyLogger 호출 시 각각 A, B 전용 객체가 생성됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #f89009;&quot;&gt;&lt;b&gt;* 싱글톤 빈과 함께 사용시 문제점&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&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;b&gt;LogDemoController&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;b&gt;LogDemoController&lt;/b&gt;에서 '&lt;b&gt;LogDemoService&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;b&gt;MyLogger&lt;/b&gt;'라는 request스코프 객체는 의존성 주입이 되지 않아 실행시 오류를 발생시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638262610151&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequiredArgsConstructor
public class LogDemoController {

    private final LogDemoService logDemoService; // 싱글톤 스코프
    private final MyLogger myLogger; // request 스코프
    
    @RequestMapping(&quot;log-demo&quot;)
    @ResponseBody
    public String logDemo(HttpServletRequest request) {
    
        myLogger.log(&quot;controller test&quot;);
        logDemoService.logic(&quot;service test&quot;);
        
        return &quot;OK&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1638263202329&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@Scope(value = &quot;request&quot;)
public class MyLogger {

	// 생략
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오류&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638262666767&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Error creating bean with name 'myLogger': Scope 'request' is not active for the
current thread; consider defining a scoped proxy for this bean if you intend to
refer to it from a singleton;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&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;/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;하지만 request 스코프 빈은 아직생성되지 않았기 때문에 스프링 컨테이너에서 찾을 수 없다.&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 스코프 빈은 실제 고객의 요청이 와야 생성 및 초기화 할 수 있다. (HTTP requset 요청)&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: #f89009;&quot;&gt;&lt;b&gt;Scope가 request이기 떄문에 실제 생성 및 초기화 시점은 HTTP requst 시점이 된다.&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;&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;1. &lt;b&gt;ObjectProvider를 사용한 Provider 방식&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;2. &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;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. ObjectProvider을 이용한 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638263462344&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequiredArgsConstructor
public class LogDemoController {

    private final LogDemoService logDemoService; // 싱글톤 스코프
    
    // 1. Provider 사용
    // 의존성 주입(DI) 시점에 MyLogger을 조회할 수 있는 provider을 제공 받는다.
    private final ObjectProvider&amp;lt;MyLogger&amp;gt; myLoggerProvider;
    
    @RequestMapping(&quot;log-demo&quot;)
    @ResponseBody
    public String logDemo(HttpServletRequest request) {
    
        // 1. Provider 사용
        MyLogger myLogger = myLoggerProvider.getObject();
        myLogger.log(&quot;controller test&quot;);
        logDemoService.logic(&quot;service test&quot;);
        
        return &quot;OK&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 프록시를 이용한 방식&lt;/span&gt;&lt;/b&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 scope 객체에 proxyMode 설정만 추가해주면 된다.&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;TARGET_CLASS&lt;/b&gt; 를 선택하고&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;적용 대상이 인터페이스면 &lt;b&gt;INTERFACES&lt;/b&gt; 를 선택한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638263881456&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@Scope(value = &quot;request&quot;, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {

	// 생략
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 하면 MyLogger의 가짜 프록시 클래스를 만들어두고 HTTP request와 상관 없이 가짜 프록시 클&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;래스를&amp;nbsp;다른&amp;nbsp;빈에&amp;nbsp;미리&amp;nbsp;주입해&amp;nbsp;둘&amp;nbsp;수&amp;nbsp;있다.&lt;/span&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;pre id=&quot;code_1638263991966&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequiredArgsConstructor
public class LogDemoController {

    private final LogDemoService logDemoService; // 싱글톤 스코프
    private final MyLogger myLogger; // request 스코프
    
    @RequestMapping(&quot;log-demo&quot;)
    @ResponseBody
    public String logDemo(HttpServletRequest request) {
    
        myLogger.log(&quot;controller test&quot;);
        logDemoService.logic(&quot;service test&quot;);
        
        return &quot;OK&quot;;
    }
}&lt;/code&gt;&lt;/pre&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;&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;가짜&amp;nbsp;프록시&amp;nbsp;객체는&amp;nbsp;요청이&amp;nbsp;오면&amp;nbsp;그때&amp;nbsp;내부에서&amp;nbsp;진짜&amp;nbsp;빈을&amp;nbsp;요청하는&amp;nbsp;위임&amp;nbsp;로직이&amp;nbsp;들어있다.&lt;/b&gt;&lt;/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;가짜 프록시 객체는 내부에 진짜 myLogger를 찾는 방법을 알고 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트가 myLogger.logic() 을 호출하면 사실은 가짜 프록시 객체의 메서드를 호출한 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;가짜 프록시 객체는 request 스코프의 진짜 myLogger.logic() 를 호출한다.&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;사실&amp;nbsp;원본인지&amp;nbsp;아닌지도&amp;nbsp;모르게,&amp;nbsp;동일하게&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다(다형성) &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;&lt;b&gt;동작&amp;nbsp;정리&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;CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.&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;프록시&amp;nbsp;객체는&amp;nbsp;실제&amp;nbsp;request&amp;nbsp;scope와는&amp;nbsp;관계가&amp;nbsp;없다.&amp;nbsp;그냥&amp;nbsp;가짜이고,&amp;nbsp;내부에&amp;nbsp;단순한&amp;nbsp;위임&amp;nbsp;로직만&amp;nbsp;있 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;고,&amp;nbsp;싱글톤&amp;nbsp;처럼&amp;nbsp;동작한다.&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;&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;프록시 객체 덕분에 클라이언트는 마치 싱글톤 빈을 사용하듯이 편리하게 request scope를 사용할 수 있 &lt;/span&gt;&lt;br /&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;Provider를&amp;nbsp;사용하든,&amp;nbsp;프록시를&amp;nbsp;사용하든&amp;nbsp;핵심&amp;nbsp;아이디어는&amp;nbsp;진짜&amp;nbsp;객체&amp;nbsp;조회를&amp;nbsp;꼭&amp;nbsp;필요한&amp;nbsp;시점까지&amp;nbsp;지연 &lt;/span&gt;&lt;br /&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;애노테이션&amp;nbsp;설정&amp;nbsp;변경만으로&amp;nbsp;원본&amp;nbsp;객체를&amp;nbsp;프록시&amp;nbsp;객체로&amp;nbsp;대체할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;이것이&amp;nbsp;바로&amp;nbsp;다형성과&amp;nbsp;DI&amp;nbsp;컨 &lt;/span&gt;&lt;br /&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;웹&amp;nbsp;스코프가&amp;nbsp;아니어도&amp;nbsp;프록시는&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다. &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;&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;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이런&amp;nbsp;특별한&amp;nbsp;scope는&amp;nbsp;꼭&amp;nbsp;필요한&amp;nbsp;곳에만&amp;nbsp;최소화해서&amp;nbsp;사용하자,&amp;nbsp;무분별하게&amp;nbsp;사용하면&amp;nbsp;유지보수하기&amp;nbsp;어려워 &lt;/span&gt;&lt;br /&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;figure id=&quot;og_1638264381914&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;스프링 핵심 원리 - 기본편 - 인프런 | 강의&quot; data-og-description=&quot;스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 개발자가 되어보세요!   수강 전 &quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bZz7om/hyMyikwaXW/Y0RTaiTqp5QuA3xQEvWeK1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/KBkcy/hyMygG0Xce/gtrEvb0qS5v8ktk1xl2oTK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/yctTa/hyMymAsKIm/slITM6dPpmgitqws8uLBk0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bZz7om/hyMyikwaXW/Y0RTaiTqp5QuA3xQEvWeK1/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/KBkcy/hyMygG0Xce/gtrEvb0qS5v8ktk1xl2oTK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/yctTa/hyMymAsKIm/slITM6dPpmgitqws8uLBk0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 핵심 원리 - 기본편 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 개발자가 되어보세요!   수강 전&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;</description>
      <category>Spring</category>
      <category>Provider</category>
      <category>Request Scope</category>
      <category>Spring</category>
      <category>web scope</category>
      <category>프록시</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/79</guid>
      <comments>https://cornarong.tistory.com/79#entry79comment</comments>
      <pubDate>Tue, 30 Nov 2021 18:41:39 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 빈 스코프(프로토타입 스코프 / prototype scope)</title>
      <link>https://cornarong.tistory.com/78</link>
      <description>&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;프로토타입(prototype) 스코프&lt;/b&gt;&lt;/span&gt;&lt;/h4&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;빈 스코프란?&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&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;&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;&lt;b&gt;싱글톤&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;&lt;b&gt;프로토타입&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;&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;b&gt;request&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;&lt;b&gt;session&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;&lt;b&gt;application&lt;/b&gt; : 웹의 서블릿 컨텍스와 같은 범위로 유지되는 스코프&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빈 스코프는 다음과 같이 지정할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638191789850&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 컴포넌트 스캔 자동 등록
@Scope(&quot;prototype&quot;)
@Component
public class HelloBean {
}

// 수동 등록
@Scope(&quot;prototype&quot;)
@Bean
PrototypeBean HelloBean() {
   return new HelloBean();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;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;일반적으로 싱글톤 스코프의 빈을 조회하면 스프링 컨테이너는 &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;반면에 프로토타입 스코프의 빈을 조회하면 스프링 컨테이너는 &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;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;1. 클라이언트가 프로토타입 스코프의 빈을 스프링 컨테이너에 요청&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;2. 스프링 컨테이너는 이 시점에 프로토타입 빈을 생성하고, 필요한 의존관계를 주입&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. 이후에 스프링 컨테이너에 같은 요청이 오면 다시 새로운 프로토타입 빈을 생성해서 반환&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;color: #333333; 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;color: #f89009; 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;* 싱글톤 스코프/프로토타입 스코프 빈 테스트&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. 싱글톤 스코프 테스트)&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;참고) @Component가 없는데요? &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;: AnnotationConfigApplicationContext에서 class를 지정해주면 해당 class를 component 자동으로 등록됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638192441302&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class SingletonTest {

    @Test
    void singletonBeanFind() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class);
        
        SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
        SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
        System.out.println(&quot;singletonBean1 = &quot; + singletonBean1);
        System.out.println(&quot;singletonBean2 = &quot; + singletonBean2);
        Assertions.assertThat(singletonBean1).isSameAs(singletonBean2);

        ac.close();
    }

    @Scope(&quot;singleton&quot;)
    static class SingletonBean {
        @PostConstruct
        public void init() {
            System.out.println(&quot;SingletonBean.init&quot;);
        }

        @PreDestroy
        public void destroy() {
            System.out.println(&quot;SingletonBean.destroy&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;&lt;b&gt;결과)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638192555905&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SingletonBean.init
singletonBean1 = hello.core.scope.SingletonTest$SingletonBean@409c54f
singletonBean2 = hello.core.scope.SingletonTest$SingletonBean@409c54f
22:28:40.693 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - //생략
SingletonBean.destroy&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. 프로토타입 스코프 &lt;b&gt;테스트)&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638192680338&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class prototypeTest {

    @Test
    void prototypeBeanFind() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);

        System.out.println(&quot;find prototypeBean1&quot;);
        PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
        System.out.println(&quot;find prototypeBean2&quot;);
        PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);

        System.out.println(&quot;prototypeBean1 = &quot; + prototypeBean1);
        System.out.println(&quot;prototypeBean2 = &quot; + prototypeBean2);
        Assertions.assertThat(prototypeBean1).isNotSameAs(prototypeBean2);

        ac.close();
        // PreDestroy 가 호출 안된다 -&amp;gt; 만들고 버린다.

    }
    
    @Scope(&quot;prototype&quot;)
    static class PrototypeBean {
        @PostConstruct
        public void init() {
            System.out.println(&quot;PrototypeBean.init&quot;);
        }

        @PreDestroy
        public void destroy() {
            System.out.println(&quot;PrototypeBean.destroy&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;&lt;b&gt;결과)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1638192784114&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;find prototypeBean1
PrototypeBean.init
find prototypeBean2
PrototypeBean.init
prototypeBean1 = hello.core.scope.prototypeTest$PrototypeBean@48d61b48
prototypeBean2 = hello.core.scope.prototypeTest$PrototypeBean@68d279ec&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;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;프로토타입 빈을 2번 조회 했으므로 완전히 다른 스프링 빈이 생성되고, 초기화도 2번 실행된 것을 확인할 수 있다.&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;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지&amp;nbsp;않는다.&amp;nbsp;따라서&amp;nbsp;프로토타입&amp;nbsp;빈은&amp;nbsp;스프링&amp;nbsp;컨테이너가&amp;nbsp;종료될&amp;nbsp;때&amp;nbsp;@PreDestory&amp;nbsp;같은&amp;nbsp;종료&amp;nbsp;메서드가&amp;nbsp;전혀 &lt;/span&gt;&lt;br /&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #f89009;&quot;&gt;프로토타입 빈의 특징 정리&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #f89009;&quot;&gt;스프링 컨테이너에 요청할 때 마다 새로 생성된다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #f89009;&quot;&gt;스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입 그리고 초기화 까지만 관여한다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #f89009;&quot;&gt;종료 메서드가 호출되지 않는다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #f89009;&quot;&gt;그래서 프로토타입 빈은 조회한 클라이언트가 직접 관리해야 한다. 즉 종료 메서드에 대한 호출도 클라이언트가 직접 해야한다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1638193359301&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;스프링 핵심 원리 - 기본편 - 인프런 | 강의&quot; data-og-description=&quot;스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 개발자가 되어보세요!   수강 전 &quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9S2ff/hyMxrIULJw/kQzCMuIATS67c7eQHYSHAK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/zp2y2/hyMxrB9nd6/QD6wGVx3cUKA76n3XPOC41/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/cnE5NL/hyMxCDGxAz/MgLcjwlP3JCLwbVoaA08a0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9S2ff/hyMxrIULJw/kQzCMuIATS67c7eQHYSHAK/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/zp2y2/hyMxrB9nd6/QD6wGVx3cUKA76n3XPOC41/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500,https://scrap.kakaocdn.net/dn/cnE5NL/hyMxCDGxAz/MgLcjwlP3JCLwbVoaA08a0/img.png?width=768&amp;amp;height=500&amp;amp;face=0_0_768_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;스프링 핵심 원리 - 기본편 - 인프런 | 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 개발자가 되어보세요!   수강 전&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring</category>
      <category>Prototype</category>
      <category>빈스코프</category>
      <category>스프링</category>
      <category>프로토타입</category>
      <author>cornarong</author>
      <guid isPermaLink="true">https://cornarong.tistory.com/78</guid>
      <comments>https://cornarong.tistory.com/78#entry78comment</comments>
      <pubDate>Mon, 29 Nov 2021 22:41:30 +0900</pubDate>
    </item>
  </channel>
</rss>