<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Youngble_개발스토리</title>
    <link>https://youngble.tistory.com/</link>
    <description>&amp;ldquo;기억보단 기록을&amp;rdquo;
깊이 알아가는 즐거움
개발자 성장과정</description>
    <language>ko</language>
    <pubDate>Wed, 15 Jul 2026 15:27:44 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>youngble</managingEditor>
    <image>
      <title>Youngble_개발스토리</title>
      <url>https://tistory1.daumcdn.net/tistory/4853574/attach/755ebaea3cc74d309e565239d6b7581a</url>
      <link>https://youngble.tistory.com</link>
    </image>
    <item>
      <title>전통 락킹(2PL) vs MVCC :동시성 제어 방식 비교</title>
      <link>https://youngble.tistory.com/255</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;현재 읽고있는 &lt;i&gt;&lt;b&gt;데이터베이스 개론 3판&lt;/b&gt;&lt;/i&gt; 읽으며 동시성제어에 관한 짤막한 정리해놓기&lt;/p&gt;
&lt;h2 id=&quot;요약표2pl-vs-mvcc&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;요약표(2PL vs MVCC)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oa7Q5/dJMcacIgEQj/G5qze82E8wzv3O5luAZp0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oa7Q5/dJMcacIgEQj/G5qze82E8wzv3O5luAZp0K/img.png&quot; data-alt=&quot;ChatGPT 활용 표작성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oa7Q5/dJMcacIgEQj/G5qze82E8wzv3O5luAZp0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foa7Q5%2FdJMcacIgEQj%2FG5qze82E8wzv3O5luAZp0K%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;812&quot; height=&quot;335&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ChatGPT 활용 표작성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;2pl&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2PL(2-Phase Locking)&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2PL 전통방식으로 read, write 모두 lock을 가짐, read는 S-lock(공유락), writ는 X-lock(배타락)&lt;br /&gt;S-lock은 read와 read끼리는 공유가능하여 트랜잭션중에도 해당 데이터에 대한 접근 가능.&lt;br /&gt;하지만 read시에 Write불가하여 X-lock을 얻기위해 read가 끝나도로 기다려야하고, 반대로 write시에도 read할수없도록 하기위해 X-lock이 반납되어 S-lock을 얻을때까지 기다려야한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;S: Shared 공유, X: Exclusive 배타적&lt;/p&gt;
&lt;h3 id=&quot;mvccmulti-version-concurrency-control-다중버전-동시성-제어&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;MVCC(Multi-Version Concurrency Control, 다중버전 동시성 제어)&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 현대의 DBMS에 오면서 Read시에는 굳이 로킹기법(2PL)을 적용하면 병렬성이 낮아진다는 단점이있어 Read시에는 락을 걸지않고 Write시에만 부분적으로 락을 걸도록 바뀐게 MVCC이다.&lt;br /&gt;read시에는 락을 사용하지않기때문에 read 트랜잭션을 원할시 항상 스냅샷 값(이전값)만 가져오도록하고, write일시 write끼리 서로 접근할수없도록 X-lock을 사용하는것이다. 이렇게함으로써 데이터베이스의 일관성은 보장하면서도 병렬성을 높여 동시성을 높일수있다.&lt;/p&gt;</description>
      <category>공부/DB</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/255</guid>
      <comments>https://youngble.tistory.com/255#entry255comment</comments>
      <pubDate>Wed, 19 Nov 2025 01:36:21 +0900</pubDate>
    </item>
    <item>
      <title>AOP란 무엇이고 필요한 이유, 프록시패턴/데코레이터 패턴</title>
      <link>https://youngble.tistory.com/254</link>
      <description>&lt;div style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;
&lt;div style=&quot;color: #212529;&quot;&gt;
&lt;div&gt;
&lt;h2 id=&quot;요약&quot; data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 횡단 관심사와 핵심 관심사가 섞이면 코드 중복과 결합도 증가하고 핵심 의도를 파악하기 어려워 가독성 과 유지보수가 저하된다. 따라서 핵심 관심사로부터 횡단 관심사를 분리하기 위한 목적으로 AOP를 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOP는 실제 핵심 관심사를 프록시로 감싸 부가기능을 주입하는 구조를 가지고,&lt;br /&gt;AOP가 어떤 패턴인지에 대해서는 의도에 따라 달라지는데, 제어가 목적이면 프록시패턴, 부가 기능 확장이 목적이면 데코레이터 패턴으로 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 개념을 바탕으로 스프링에서는 @Aspect, @Around 같은 어노테이션을 통해 AOP를 구현할 수있으며, @Transactional또한 프록시 패턴기반의 AOP가 적용된 사례이다.&lt;/p&gt;
&lt;h2 id=&quot;필요한-이유&quot; data-ke-size=&quot;size26&quot;&gt;필요한 이유&lt;/h2&gt;
&lt;h3 id=&quot;횡단-관심사&quot; data-ke-size=&quot;size23&quot;&gt;횡단 관심사&lt;/h3&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI9vjs/dJMcagRmVHw/knEZBdBlnhdyChcKv0GxN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI9vjs/dJMcagRmVHw/knEZBdBlnhdyChcKv0GxN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI9vjs/dJMcagRmVHw/knEZBdBlnhdyChcKv0GxN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI9vjs%2FdJMcagRmVHw%2FknEZBdBlnhdyChcKv0GxN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;760&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;여러부분에 공통적으로 적용되는 (부가적인) 기능&lt;/p&gt;
&lt;h3 id=&quot;횡단-관심사와-핵심-관심사-코드-구조&quot; data-ke-size=&quot;size23&quot;&gt;횡단 관심사와 핵심 관심사 코드 구조&lt;/h3&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;826&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blE9aM/dJMcah3Ov9M/XCi7Emf2rdDq9WetDPcegk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blE9aM/dJMcah3Ov9M/XCi7Emf2rdDq9WetDPcegk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blE9aM/dJMcah3Ov9M/XCi7Emf2rdDq9WetDPcegk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblE9aM%2FdJMcah3Ov9M%2FXCi7Emf2rdDq9WetDPcegk%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;826&quot; height=&quot;257&quot; data-origin-width=&quot;826&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;횡단 관심사코드가 섞인 예시코드&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;public class UserService {

    public void signUp(UserRequest request) {
        log.info(&quot;회원가입 시도: {}&quot;, request.email());

        try {
            transactionManager.begin();
            userRepository.save(request.toEntity());
            transactionManager.commit();
        } catch (Exception e) {
            transactionManager.rollback();
            log.error(&quot;회원가입 실패&quot;, e);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러개의 클래스 객체의 핵심 로직에서 부가적으로 반복해서 사용되는 횡단 관심사 코드(로깅, 보안 검사, 트랜잭션 관리 등)는 중복코드가 발생할뿐아니라 해당 관심사에 관한 로직만 있는게아닌 여러 관심사가 섞어서 다루기 때문에 비대해지고 강한 결합도를 가지고 응집도를 낮추어 좋지않다.&lt;br /&gt;또한 하나의 모듈에 여러 관심사가 섞이니 무엇이 핵심 로직(행동)인지 보기어렵게 하기때문에 코드가독성, 유지보수성에도 좋지않다.&lt;/p&gt;
&lt;h3 id=&quot;aop-적용-전-후&quot; data-ke-size=&quot;size23&quot;&gt;AOP 적용 전 후&lt;/h3&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UJbcg/dJMcah3Ov9K/qGWrS1bHVJfFUkSzc2o4oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UJbcg/dJMcah3Ov9K/qGWrS1bHVJfFUkSzc2o4oK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UJbcg/dJMcah3Ov9K/qGWrS1bHVJfFUkSzc2o4oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUJbcg%2FdJMcah3Ov9K%2FqGWrS1bHVJfFUkSzc2o4oK%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;443&quot; height=&quot;500&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이러한 부가적인 공통 관심사를 핵심 관심사 코드로부터 분리하여 aspect로 모듈화하여 사용한다. 이렇게 핵심 관심사와 공통 관심사를 나누기때문에 각각의 관심사만 집중한 코드를 작성할수있고 흐터져있던 코드를 하나의 장소에 응집되기때문에 재사용이 가능하고 하나의 관심사로 응집도가 높아지기에 유지보수하기 좋게해주기때문에 객체지향 프로그래밍에 적합(여러가지 원칙중 SRP 단일 책임 원칙이 해당)하게해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOP(Aspect Oriented Programming)&lt;br /&gt;관점지향 프로그래밍, 핵심 관심사, 부가 관심사(횡단 관심사)를 분리하는 관점을 기준으로 프로그래밍 한다는 의미를 강조하기위해 붙여진 이름&lt;/p&gt;
&lt;h3 id=&quot;그래서-어떻게-분리할것이냐&quot; data-ke-size=&quot;size23&quot;&gt;그래서 어떻게 분리할것이냐?&lt;/h3&gt;
&lt;h4 id=&quot;aop-구성&quot; data-ke-size=&quot;size20&quot;&gt;AOP 구성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOP는 프록시(Proxy)구조 기반으로 동작한다.&lt;/p&gt;
&lt;h4 id=&quot;프록시&quot; data-ke-size=&quot;size20&quot;&gt;프록시&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Proxy는 대리인, 대변인이라는 뜻으로 실제 대상(타깃)인것처럼 위장하여 요청에 대해서 대신 받아 수행하는 것이다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DXkMP/dJMcaiaz2Xk/JvzWe1xNrIR07jSVMxrGkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DXkMP/dJMcaiaz2Xk/JvzWe1xNrIR07jSVMxrGkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DXkMP/dJMcaiaz2Xk/JvzWe1xNrIR07jSVMxrGkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDXkMP%2FdJMcaiaz2Xk%2FJvzWe1xNrIR07jSVMxrGkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;261&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1193&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tEK32/dJMb99SffVs/1xYv56Ba9sOADKKxLV5YU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tEK32/dJMb99SffVs/1xYv56Ba9sOADKKxLV5YU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tEK32/dJMb99SffVs/1xYv56Ba9sOADKKxLV5YU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtEK32%2FdJMb99SffVs%2F1xYv56Ba9sOADKKxLV5YU0%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;1193&quot; height=&quot;263&quot; data-origin-width=&quot;1193&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 타깃에는 핵심 관심사 코드만 수행하도록 코드가 작성하게 되고, 프록시에는 횡단관심사(부가기능)을 추가적으로 넣음으로써 클라이언트입장에서는 마치 실제 타깃이 하는것처럼 보이는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 것은 프록시를 어떤 목적으로 사용하는지에 따라 프록시패턴 또는 데코레이터 패턴이 된다.&lt;/p&gt;
&lt;h4 id=&quot;프록시패턴&quot; data-ke-size=&quot;size20&quot;&gt;프록시패턴?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프록시 패턴은 프록시의 대리수행의 목적이 클라이언트가 타깃에 접근 제어(지연, 캐싱, 보안 등)의 목적을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 보안이라는 대리수행을 한다면, 클라이언트가 타깃에 요청해 작업을 수행할 수있는 권한이 있는지 미리 검사하고 긍정적일때만 클라이언트 요청을 타깃에게 전달하는 것이다.&lt;/p&gt;
&lt;h4 id=&quot;데코레이터-패턴-&quot; data-ke-size=&quot;size20&quot;&gt;데코레이터 패턴 ?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데코레이터 패턴은 대리수행의 목적이 타깃에 부가기능 혹은 확장기능을 감싸서 사용하는 목적을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;두가지의 공통된 구조&lt;/b&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OYGjY/dJMcaiaz2Xl/5d4HtTbfPNf1fG3IB68e0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OYGjY/dJMcaiaz2Xl/5d4HtTbfPNf1fG3IB68e0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OYGjY/dJMcaiaz2Xl/5d4HtTbfPNf1fG3IB68e0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOYGjY%2FdJMcaiaz2Xl%2F5d4HtTbfPNf1fG3IB68e0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;820&quot; height=&quot;550&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;pre class=&quot;aspectj&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;public interface Service {
    void execute();
}

public class RealService implements Service {
    @Override
    public void execute() {
        System.out.println(&quot;실제 로직 수행&quot;);
    }
}

public class ServiceProxy implements Service { // 또는 Decorator
    private final Service target;

    public ServiceProxy(Service target) {
        this.target = target;
    }

    @Override
    public void execute() {
    	// 앞뒤로 무언가 추가
        System.out.println(&quot;부가 로직: 로그 시작&quot;);
        target.execute(); // 핵심 로직 위임
        System.out.println(&quot;부가 로직: 로그 종료&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;그래서-aop는-무슨-패턴이라는건데&quot; data-ke-size=&quot;size20&quot;&gt;그래서 AOP는 무슨 패턴이라는건데?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도와 목적에 따라서 프록시패턴이 될수도 데코레이터 패턴이 될수도있다.&lt;br /&gt;트랜잭션이나 로깅 등 핵심 로직을 감싸는 부가 기능이지만, AOP 프록시가 어떻게 의도적으로 사용하는지에 따라 패턴이 결정되는것이다.&lt;br /&gt;따라서 트랜잭션은 Proxy패턴, 로깅은 Decorator 패턴으로 분류한다. 때문에 AOP를 설명할때는 하나의 패턴이라 하지않고 프록시구조를 사용한 프록시패턴 혹은 데코레이터 패턴으로 횡단 관심사를 핵심 관심사에서 분리하는 것이다 라고 해야한다.&lt;/p&gt;
&lt;h3 id=&quot;실제-애플리케이션-개발-사례&quot; data-ke-size=&quot;size23&quot;&gt;실제 애플리케이션 개발 사례&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용하기 전 서비스 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;public class OrderService {

    private final TransactionManager txManager;
    private final InventoryService inventoryService;
    private final PaymentService paymentService;

    public void placeOrder() {
        Transaction tx = txManager.begin();
        try {
            inventoryService.decreaseStock(...);
            paymentService.charge(...);
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            throw e;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재고를 감소시키고, 결제를 부가하고 난후 commit으로 DB를 확정짓는 코드이다.&lt;br /&gt;Service라는 조율자는 도메인 로직을 어떤 순서로 어떻게 실행할지 조율하는 것이지 트랜잭션과같은 실행 제어까지는 다루는 목적의 레이어가 아니기때문에 이를 분리해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링 AOP 의 @Transactional&lt;/b&gt;&lt;br /&gt;트랜잭션 관리를위한트랙잭션 Proxy AOP&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

    private final InventoryService inventoryService;
    private final PaymentService paymentService;

    public OrderService(InventoryService inventoryService,
                        PaymentService paymentService) {
        this.inventoryService = inventoryService;
        this.paymentService = paymentService;
    }

    @Transactional
    public void placeOrder(OrderRequest request) {
        // 오직 비즈니스 흐름만
        inventoryService.decreaseStock(request.itemId(), request.quantity());
        paymentService.charge(request.userId(), request.totalPrice());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AOP가 대신 트랙잭션 제어시&lt;br /&gt;@Transactional의 AOP(프록시패턴) 실제 내부구조 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;class OrderServiceProxy implements OrderService {
    private final OrderService target;
    private final TransactionManager txManager;

    public void placeOrder() {
        TransactionStatus tx = txManager.begin();
        try {
            target.placeOrder(); // 핵심 비즈니스 수행
            txManager.commit(tx);
        } catch (Exception e) {
            txManager.rollback(tx);
            throw e;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;더나아가-advanced&quot; data-ke-size=&quot;size23&quot;&gt;더나아가 Advanced...&lt;/h3&gt;
&lt;h4 id=&quot;다중-부가기능-조합-aop를-원한다면&quot; data-ke-size=&quot;size20&quot;&gt;다중 부가기능 조합 AOP를 원한다면?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링 AOP 사용&lt;br /&gt;@Aspect, @Around 사용 스프링 AOP 등록&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;@Aspect
@Component
public class LoggingAspect {

    @Around(&quot;execution(* com.example.order.service..*(..))&quot;)
    public Object log(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println(&quot;[Log] 시작&quot;);

        Object result = pjp.proceed();  // 실제 대상(OrderService) 실행

        System.out.println(&quot;[Log] 종료&quot;);
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 관심사 서비스 로직&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

    private final InventoryService inventoryService;
    private final PaymentService paymentService;

    public OrderService(InventoryService inventoryService,
                        PaymentService paymentService) {
        this.inventoryService = inventoryService;
        this.paymentService = paymentService;
    }

    @Transactional
    public void placeOrder(OrderRequest request) {
        // 오직 비즈니스 흐름만
        inventoryService.decreaseStock(request.itemId(), request.quantity());
        paymentService.charge(request.userId(), request.totalPrice());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링 AOP 내부 동작 구조 간단 예시 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;OrderService real = new RealOrderService();

TransactionManager txManager = ...;

OrderService txProxy     = new TransactionOrderServiceProxy(real, txManager);
OrderService loggingProxy = new LoggingOrderServiceProxy(txProxy);

// 클라이언트는 loggingProxy만 봄
loggingProxy.placeOrder(&quot;ORDER-123&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;흐름도&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot; style=&quot;background-color: #fbfcfd;&quot;&gt;&lt;code&gt;Client
 └─▶ LoggingAspect (@Aspect)
     └─▶ TransactionInterceptor (@Transactional)
         └─▶ OrderService.placeOrder()&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;background-color: #f8f9fa; color: #212529;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트&lt;br /&gt;&amp;darr;&lt;br /&gt;(스프링이 만든 프록시 체인)&lt;br /&gt;&amp;darr;&lt;br /&gt;[LoggingAspect @Around]&lt;br /&gt;&amp;darr;&lt;br /&gt;[TransactionInterceptor (@Transactional 처리)]&lt;br /&gt;&amp;darr;&lt;br /&gt;[진짜 OrderService.placeOrder()]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;레퍼런스&lt;br /&gt;&lt;/b&gt;&lt;a href=&quot;https://adjh54.tistory.com/133&quot;&gt;https://adjh54.tistory.com/133&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leeeeeyeon-dev.tistory.com/49&quot;&gt;https://leeeeeyeon-dev.tistory.com/49&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%94%84%EB%A1%9D%EC%8B%9CProxy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot;&gt;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%94%84%EB%A1%9D%EC%8B%9CProxy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>라이브러리&amp;amp;프레임워크/Spring</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/254</guid>
      <comments>https://youngble.tistory.com/254#entry254comment</comments>
      <pubDate>Sun, 9 Nov 2025 21:37:46 +0900</pubDate>
    </item>
    <item>
      <title>Spring Framework가 탄생하게 된 배경/ 웹 변천사 및 동작 원리</title>
      <link>https://youngble.tistory.com/253</link>
      <description>&lt;h2 id=&quot;스프링-탄생에-앞서-웹-역사&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;스프링 탄생에 앞서 웹 역사..&lt;/h2&gt;
&lt;h4 id=&quot;웹-초기-정적페이지-기반-사이트&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;웹 초기 정적페이지 기반 사이트&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egZzCJ/dJMcafSpQEJ/NK0HQRdMxyiTWIJbkUidGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egZzCJ/dJMcafSpQEJ/NK0HQRdMxyiTWIJbkUidGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egZzCJ/dJMcafSpQEJ/NK0HQRdMxyiTWIJbkUidGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FegZzCJ%2FdJMcafSpQEJ%2FNK0HQRdMxyiTWIJbkUidGK%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;670&quot; height=&quot;169&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;초기 인터넷 보급이 크지않았고 발달할 당시에는 간단한 HTML 방식의 정적페이지에서 이미 만들어져있는 css, image 등을 통해 정보를 제공하였다.&lt;/p&gt;
&lt;h4 id=&quot;동적-페이지-기반-사이트&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;동적 페이지 기반 사이트&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjdJCx/dJMcaacu8Vx/7KlKcl9FnvJ5Ex9RiFj5Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjdJCx/dJMcaacu8Vx/7KlKcl9FnvJ5Ex9RiFj5Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjdJCx/dJMcaacu8Vx/7KlKcl9FnvJ5Ex9RiFj5Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjdJCx%2FdJMcaacu8Vx%2F7KlKcl9FnvJ5Ex9RiFj5Q0%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;696&quot; height=&quot;191&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 인터넷과 Web이라는 시장이 커지면서 2000년 초 HTML을 통해 정적인 파일들을 화면에 표시하는 정적 페이지 떠나 사용자의 반응에 동작(인터렉션)하는 동적 페이지가 필요했다.&lt;/p&gt;
&lt;h4 id=&quot;cgi&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;CGI&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CNMrx/dJMcafSpQEL/M5JMvwdSJhiGRYgObCokW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CNMrx/dJMcafSpQEL/M5JMvwdSJhiGRYgObCokW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CNMrx/dJMcafSpQEL/M5JMvwdSJhiGRYgObCokW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCNMrx%2FdJMcafSpQEL%2FM5JMvwdSJhiGRYgObCokW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;424&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;561&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5afhA/dJMcagKywHA/I7EDVhKQckizxdhqkAEsVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5afhA/dJMcagKywHA/I7EDVhKQckizxdhqkAEsVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5afhA/dJMcagKywHA/I7EDVhKQckizxdhqkAEsVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5afhA%2FdJMcagKywHA%2FI7EDVhKQckizxdhqkAEsVK%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;660&quot; height=&quot;561&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;561&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이러한 동적페이지를 만들기위해 CGI(Common Gateway Interface)이라는 개념이 등장했다. CGI는 외부 프로그램과 연결해주는 인터페이스(API) 표준 방법 개념이고 이걸 바탕으로 만든 프로그램을 CGI 프로그램 이라한다. 사용자 요청에 웹서버가 파라미터를 통해 CGI로된 프로그램 애플리케이션(c,Perl, pyhton 등)에 요청을 보내고, 요청한 정보 바탕으로 동적인 컨텐츠를 완성된 정적페이지(HTML)생성후에 webserver에 전달하여 정보를 제공하였다.&lt;/p&gt;
&lt;h2 id=&quot;servlet&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Servlet&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgw0Qd/dJMcadmLgw7/bkQrKhF2oVIZ0MKGSc7NMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgw0Qd/dJMcadmLgw7/bkQrKhF2oVIZ0MKGSc7NMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgw0Qd/dJMcadmLgw7/bkQrKhF2oVIZ0MKGSc7NMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdgw0Qd%2FdJMcadmLgw7%2FbkQrKhF2oVIZ0MKGSc7NMk%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;844&quot; height=&quot;373&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 CGI의 경우 매 요청마다 CGI프로그램(프로세스)를 새롭게 생성하기때문에 다수의 요청이 발생하면 서버 과부하가 생기는 단점이 있었다. 이러한 한계를 해결하기 위해, 외부프로그램 실행 방식이 아닌 Java언어 기반으로 JVM내부에서 쓰레드를 재사용으로 동작하는 Servlet이라는 표준기술이 등장하였다. 이 Servlet 기술을 기반으로 WAS가 발전했으며, 이를 통해 동적페이지 생성뿐 아니라 비즈니스로직 처리, 데이터베이스 연동, 트랜잭션 관리 등의 기능을 수행할 수 있게 되었다. 이렇게 웹시장과 기술/사용자 요구사항이 거대해지고 복잡해지면서 높은 이해도와 작성이 필요했다.&lt;/p&gt;
&lt;h2 id=&quot;ejbenterprise-java-bean-등장&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;EJB(Enterprise Java Bean) 등장&lt;/h2&gt;
&lt;h4 id=&quot;servlet-시절-예시-코드&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Servlet 시절 예시 코드&lt;/h4&gt;
&lt;pre class=&quot;pgsql&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot;&gt;&lt;code&gt;// 트랜잭션 직접 처리
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false);
try {
    orderDao.save(order);
    paymentDao.process(order);
    conn.commit();
} catch (Exception e) {
    conn.rollback();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 상황에서 기업 규모의 프로젝트를 만들고 유지한다는것은 Servlet/JSP만으론 트랜잭션, 보안, 동시성, 세션 관리 코드를 직접 구현하고 관리하기엔 너무 복잡하고 거대하여 유지보수가 쉽지않았다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 생상선과 유지보수를 높이기 위해, 개발자가 저수준(low-level) 코드(DB연결, 트랙잰션, 스레드 관리 등)를 직접 구현하지않고 고수준(high-level) 추상화 API만 사용하도록 등장한 것이 EJB(Enterprise JavaBeans)이다.&lt;/p&gt;
&lt;h4 id=&quot;ejb-이후-코드&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;EJB 이후 코드&lt;/h4&gt;
&lt;pre class=&quot;less&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot;&gt;&lt;code&gt;@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class OrderService {
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void processOrder(Order order) {
        orderDao.save(order);
        paymentDao.process(order);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;내부구조-및-흐름&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;내부구조 및 흐름&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2319E/dJMcagKywHz/D1DS93uW2k5xgacvIZUxP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2319E/dJMcagKywHz/D1DS93uW2k5xgacvIZUxP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2319E/dJMcagKywHz/D1DS93uW2k5xgacvIZUxP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2319E%2FdJMcagKywHz%2FD1DS93uW2k5xgacvIZUxP1%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;561&quot; height=&quot;376&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yyCxU/dJMcafSpQEK/nvLoXEWj9LtSirbvrBkzB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yyCxU/dJMcafSpQEK/nvLoXEWj9LtSirbvrBkzB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yyCxU/dJMcafSpQEK/nvLoXEWj9LtSirbvrBkzB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyyCxU%2FdJMcafSpQEK%2FnvLoXEWj9LtSirbvrBkzB1%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;577&quot; height=&quot;303&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;445&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMRp9A/dJMcafSpQEI/dflesxR0EOfxcq7mefjnj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMRp9A/dJMcafSpQEI/dflesxR0EOfxcq7mefjnj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMRp9A/dJMcafSpQEI/dflesxR0EOfxcq7mefjnj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMRp9A%2FdJMcafSpQEI%2FdflesxR0EOfxcq7mefjnj1%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;445&quot; height=&quot;268&quot; data-origin-width=&quot;445&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;EJB는 Micro System 사에서 개발한 엔터프라이즈용 서버 컴포넌트 모델이다.&lt;br /&gt;비즈니스 로직을 객체(Bean)단위로 분리하고, 트랜잭션, 보안, 분산등은 컨테이너가 대신 처리하도록하는 구조.&lt;/p&gt;
&lt;pre class=&quot;xml&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot;&gt;&lt;code&gt;&amp;lt;enterprise-beans&amp;gt;
  &amp;lt;session&amp;gt;
    &amp;lt;ejb-name&amp;gt;OrderServiceBean&amp;lt;/ejb-name&amp;gt;
    &amp;lt;home&amp;gt;com.example.OrderServiceHome&amp;lt;/home&amp;gt;
    &amp;lt;remote&amp;gt;com.example.OrderService&amp;lt;/remote&amp;gt;
    &amp;lt;ejb-class&amp;gt;com.example.OrderServiceBean&amp;lt;/ejb-class&amp;gt;
  &amp;lt;/session&amp;gt;
&amp;lt;/enterprise-beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;EJB를 사용하면 단지 작은 규모의 역할/기능을 초기 세팅이 방대(XML)하고 보일러플레이트가 매우 번거롭고 컸다. 이렇기 때문에 매우 무겁다라고 말이 많았다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 Java 그자체로 작성한다기보단 EJB라는 기술에 의존성이 강하여 응집도는 낮고 결합도는 커지게되었다.&lt;/p&gt;
&lt;h4 id=&quot;상속강제pojo-아님&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;상속강제(POJO 아님)&lt;/h4&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot;&gt;&lt;code&gt;public class OrderServiceBean implements SessionBean {
    public void ejbCreate() { ... } // 컨테이너가 호출
    public void ejbRemove() { ... }
    public void ejbActivate() { ... }
    public void ejbPassivate() { ... }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;객체지향이라는 패러다임을 실제로 반영할수없는 구조가 되어버리다보니 유연하지 못하고 유지보수가 매우 번거러워지게됨. 또한 EJB 자체 규모가 크다보니 테스트나 배포를 위한 소요시간도 매우 길었다.&lt;/p&gt;
&lt;h3 id=&quot;스프링&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;스프링&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 복잡함에 한계를 느낀 로드존슨 EJB 없이도 충분히 객체지향을 만들 수있다는 철학을 바탕으로 POJO(Plain Old Java Object)기반 확장가능하도록 설계한것이 경량 프레임워크를 제안하게되었다. 2002년 Expert-One-on-One J2EE Design and Development라는 책을 출간하면서 EJB문제점과 대안을 제시하였다. 이를 공감한 '유겐 휠러', '얀 카로프'가 오픈소스 프로젝트를 제안하고 발전시키며 이제는 겨울(EJB)이지나 봄이 왔다라고 해서 Spring이라고 이름을 만들었다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;J2EE : Java 2 Enterprise Editio으로 기업용 자바 어플리케이션 개발을 위한 표준 플랫폼/표준 기술 집합을 말한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스프링은 EJB를 그대로 사용하는 대신, EJB에서 사용된 핵심 개념(Bean 컨테이너, DI, AOP, 트랜잭션 관리 등)은 계승하고, 복잡한 설정과 무거운 런타임 의존성을 제거하여 POJO 기반으로 단순화한 경량 프레임워크이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MWB2I/dJMcaacu8Vy/DkdACaQK48qk1DfVO0LkLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MWB2I/dJMcaacu8Vy/DkdACaQK48qk1DfVO0LkLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MWB2I/dJMcaacu8Vy/DkdACaQK48qk1DfVO0LkLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMWB2I%2FdJMcaacu8Vy%2FDkdACaQK48qk1DfVO0LkLk%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;945&quot; height=&quot;252&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C1OGu/dJMcadmLgw8/AL8dR2THxmZySFSoSSz6w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C1OGu/dJMcadmLgw8/AL8dR2THxmZySFSoSSz6w1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C1OGu/dJMcadmLgw8/AL8dR2THxmZySFSoSSz6w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC1OGu%2FdJMcadmLgw8%2FAL8dR2THxmZySFSoSSz6w1%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;720&quot; height=&quot;400&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dispatcherServlet : 스프링 진입점, 스프링 컨텍스트 로딩, 요청 URL에 맞는 컨트롤러 찾아서 호출,서블릿 컨테이너에 등록되어 관리 실행&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>라이브러리&amp;amp;프레임워크/Spring</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/253</guid>
      <comments>https://youngble.tistory.com/253#entry253comment</comments>
      <pubDate>Thu, 30 Oct 2025 23:05:29 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 공부 책 목록</title>
      <link>https://youngble.tistory.com/notice/252</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;객제지향의 진실과 오해 (1회독 완)&lt;br /&gt;Real MySQL 8.0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 개론 3판&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sql 레벨업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 ORM표준 JPA 프로그래밍&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자 웹 개발자를 위한 스프링5 프로그래밍 입문&lt;br /&gt;JVM 밑바닥까지 파헤치기&lt;br /&gt;오브젝트&lt;br /&gt;Clean Code&lt;br /&gt;가상면접 사례로 배우는 대규모 시스템 설계기초1&lt;br /&gt;가상면접 사례로 배우는 대규모 시스템 설계기초2&lt;br /&gt;Java의 정석1&lt;br /&gt;Java의 정석2&lt;br /&gt;운영체제&lt;br /&gt;컴퓨터 네트워킹&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;처음부터 제대로 배우는 도커/쿠버네티스 컨테이너 개발과 운영&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;(자바/스프링/서블릿 등 관련 깊이)&lt;br /&gt;웹 프로그래머를 위한 서블릿 컨테이너의 이해&lt;br /&gt;Head First Servlets &amp;amp; JSP&lt;br /&gt;톰켓 최종분석&lt;br /&gt;&amp;nbsp;&lt;br /&gt;(DDD관련)&lt;br /&gt;도메인 주도 설계 핵심 (Domain-Driven Designed Distilled)&lt;br /&gt;클린아키텍처&lt;br /&gt;&amp;nbsp;&lt;br /&gt;(EDD관련)&lt;br /&gt;이벤트기반 마이크로서비스 구축(Building Event-Driven Microservices)&lt;br /&gt;&amp;nbsp;&lt;br /&gt;(&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;데이터베이스와 분산시스템)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;데이터 중심 어플리케이션 설계(&lt;/span&gt;&lt;/span&gt;Designing Data-Intensive Applications)&lt;br /&gt;&amp;nbsp;&lt;br /&gt;(TDD관련)&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #595959;&quot;&gt;테스트 주도 개발(Test-Driven Developlment)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #595959;&quot;&gt;(유스케이스 관련)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #595959;&quot;&gt;앨리스터 코오번의 유스케이스&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #595959;&quot;&gt;코드컴플리트&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #595959;&quot;&gt;리펙터링&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #595959;&quot;&gt;책추천 100선&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://blog.insightbook.co.kr/2010/08/11/%EC%B6%94%EC%B2%9C%ED%95%A0%EB%A7%8C%ED%95%9C-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EC%B1%85-100%EC%84%A0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #595959;&quot;&gt;https://blog.insightbook.co.kr/2010/08/11/%EC%B6%94%EC%B2%9C%ED%95%A0%EB%A7%8C%ED%95%9C-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EC%B1%85-100%EC%84%A0/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;추천! 소프트웨어 엔지니어링 책 100선&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;괜찮은 책을 찾는 방법 중 하나는 신뢰도 있는 목록을 찾는 것입니다. 졸트 어워드(Jolt Award) 같은 수상작 모음도 있을테고, 베스트셀러의 참고 문헌 목록도 있습니다. 아마존 같은 대형 서점의 &quot; data-og-host=&quot;blog.insightbook.co.kr&quot; data-og-source-url=&quot;https://blog.insightbook.co.kr/2010/08/11/%EC%B6%94%EC%B2%9C%ED%95%A0%EB%A7%8C%ED%95%9C-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EC%B1%85-100%EC%84%A0/&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/dvS5X8/hyZMZN1l0m/AAAAAAAAAAAAAAAAAAAAAO2DkQBduPE9UAqx6Nwmnm4YsrWOO2jVmLmnpPRGF9MD/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=S6juvHOT5%2BnNnwQ9ojdp8IV9u7c%3D&quot; data-og-url=&quot;http://blog.insightbook.co.kr/2010/08/11/%ec%b6%94%ec%b2%9c%ed%95%a0%eb%a7%8c%ed%95%9c-%ec%86%8c%ed%94%84%ed%8a%b8%ec%9b%a8%ec%96%b4-%ec%97%94%ec%a7%80%eb%8b%88%ec%96%b4%eb%a7%81-%ec%b1%85-100%ec%84%a0/&quot;&gt;&lt;a href=&quot;http://blog.insightbook.co.kr/2010/08/11/%ec%b6%94%ec%b2%9c%ed%95%a0%eb%a7%8c%ed%95%9c-%ec%86%8c%ed%94%84%ed%8a%b8%ec%9b%a8%ec%96%b4-%ec%97%94%ec%a7%80%eb%8b%88%ec%96%b4%eb%a7%81-%ec%b1%85-100%ec%84%a0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.insightbook.co.kr/2010/08/11/%EC%B6%94%EC%B2%9C%ED%95%A0%EB%A7%8C%ED%95%9C-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EC%B1%85-100%EC%84%A0/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/dvS5X8/hyZMZN1l0m/AAAAAAAAAAAAAAAAAAAAAO2DkQBduPE9UAqx6Nwmnm4YsrWOO2jVmLmnpPRGF9MD/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=S6juvHOT5%2BnNnwQ9ojdp8IV9u7c%3D');&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;추천! 소프트웨어 엔지니어링 책 100선&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;괜찮은 책을 찾는 방법 중 하나는 신뢰도 있는 목록을 찾는 것입니다. 졸트 어워드(Jolt Award) 같은 수상작 모음도 있을테고, 베스트셀러의 참고 문헌 목록도 있습니다. 아마존 같은 대형 서점의&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.insightbook.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>기타/독서</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/notice/252</guid>
      <pubDate>Thu, 23 Oct 2025 23:43:23 +0900</pubDate>
    </item>
    <item>
      <title>객체지향의 사실과 오해</title>
      <link>https://youngble.tistory.com/251</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;들어가기전에&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가아닌 글로써 객체지향에 대한 식견을 설명을 정말 잘한 책이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽으면서 느낀점이나 기억남는것을 써내려가기때문에 정확한 해석이나 순서는 보장하지않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 개념의 정의로만 알던 혹은 오히려 클래스와 객체를 만들고 직접 코드를 짜면서도 단순히 모호한 형태의 덩어리처럼&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이해하던 객체지향을 글을 통해 좀더 깊게 생각하게 되었다. 다독을 통해 처음본것보다 2번째 봤을때 느끼고 이해가는게 다르듯이 한번 보고 나중에 한번더 읽으면 좋을거같다. 해당 책의 일부분만 적은것이고 추후 느낀점을 더 쓸거같다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이상한나라의 앨리스&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;1016&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdWQPM/dJMb9MiA1ky/AZU1egUK6xbOyl0qumo6b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdWQPM/dJMb9MiA1ky/AZU1egUK6xbOyl0qumo6b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdWQPM/dJMb9MiA1ky/AZU1egUK6xbOyl0qumo6b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdWQPM%2FdJMb9MiA1ky%2FAZU1egUK6xbOyl0qumo6b0%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;488&quot; height=&quot;470&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;1016&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이책은 이상한나라의 앨리스를 예시를 통해 객체지향의 특색과 의도 목적 등을 다양한 시각으로 설명을 시작하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앨리스가 시계를 보며 허겁지겁 달려가던 토끼를 보고 따라가면서 생기게 되는 에피소드를 객체지향의 설명을 한다는것에서 감탄했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;상태와 행동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앨리스의 행동과 상태로 객체라는 것을 표현하다니 재미있네. '작은 문을 통과하기위해 음료수를 마신다거나 부채질을 해야만 키가 작아지고 작아진다음에야 작은문을 통과할수있다.' 라는 에피소드를 이야기하는데 단순히 이야기로만 본다면 아무 특색도없다. 당연한 스토리같기도하다. 문을 통과할수없으니 방법을 찾고 그 방법이 음료수를 마시거나 부채질을 한다라는 것이기 때문이다. 하지만 이를 객체를 이해하기위해 접목한다면 신선하다고 생각했다. 어떠한 행동에따라 엘리스는 달라지고 그 달라진 상태로인해 또다른 결과를 만들수있다는 것이다. 이는 객체의 필드, 메서드를 이해할수있으면서 결과는 필드의 값에 따라 달라진다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;객체: 앨리스&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태: 키, 위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행동: 마시기, 부채질하기, 문에 들어가기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 객체는 상태를 가지며 상태는 변경가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 객체의 상태를 바꾸는 것은 행동이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-1. 행동의 결과는 상태에 의존적이며 상태에 의해 설명된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-2. 행동의 순서가 결과에 영향을 준다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이부분에서 캡슐화, 자율적인 행동에 대해서 정말 표현을 잘했다고생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 캡슐화라고 한다면 정의는 딱딱하고 이해하기어렵다. 상태와 행동을 묶고 외부에서 접근할수있는 방법은 오로지 행동을 통해서만이고 이로인해 정보은닉을 하고 등등.. 아예 모르진 않다 하지만 와닿지가 않았다. 그걸 왜하는데? 이런느낌? 당연히 코드를 짜고 계속해서 설계해가다보면 자연스럽게 체득하겠지만 이는 새로운 시각이나 인사이트를 주지못하고 그저 코드에 갖히게 되는것같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이책에서는 기계라는 것을 이용해 이를 설명했는데 캡슐화에 대해 더 잘이해하게 된거같다. 흔히 우리는 기계는 버튼이있고 그 버튼을 통해 동작을한다. 하지만 그 동작의 내부구현을 알지 못할뿐더러 어떻게 그렇게 되는지 직접 분해해서 보지않는다. 우리는 버튼을 통해 작동하게하고 그 작동의 결과를 눈으로 보면 되는 것이다. 이게 다이다. 캡슐화가 말하고자하는것과 이해는 이 예시로 끝났다. 우리가 그 기계의 상태를 알고싶다고 분해해서 직접 접근하지도 않거니와 어떻게 동작하는지는 알필요도없다. 그저 버튼을 통해 무엇을 동작하는지만 알면 되는 것이다. 즉 캡슐화, 정보은닉, 행동(메서드)를 알게 되는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;식별자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 식별 동일성, 동등성을 이해하는데도 좋았다. 같은 기계가 두개있고 같은 동작, 같은 상태를 갖고있다고해도 우리는 이것을 하나의 같은 객체 라고보지않고 각각의 고유객체로 본다. 이유는 같은 동작이나 상태가 동일한 객체라고 볼수없기때문이다. 이처럼 우리가 사용하는 소프트웨어의 객체는 식별자를 가지고있고 이 식별자를 통해 우리는 동일한지 동등한지를 알게되는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;도시전설(오해)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향의 초보자는 객체의 상태를 먼저 생각하고 만든다음 그에 맞는 행동을 설계한다고한다. 이게 무슨말이지 싶었다. 또는 당연하다고 생각했다. 객체(클래스)를 설계할때 그 객체가 가지고있는 필드부터 생각하지않나? 라고말이다. 이유가 궁금했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 바로 이것이 도시전설 이라고하면서 우리가 알고있는 정통적인 객체지향이라 함은 현실세계의 객체를 모방한다라는 오해에서 시작하기 때문이라고한다. 보통 정통적으로는 현실의 객체를 모방함으로써 소프트웨어의 객체를 만든다고 생각하지만, 오히려 이러한 도시전설로인해서 객체지향의 특색을 약하게 한다고한다. 모방하기위해선 실 객체를 잘 관찰해야되고 그 특성을 잘 추출해서 추상화해야한다. 이러다보면 우리가 만들고자하는 목적보단 객체라는 실체에만 집중하게 된다. 우리가 객체를 설계하는 이유는 어떠한 동작과 행동을 토대로 시스템을 구축해 나가는 것인데 객체에 매몰된다는 것이다. 이렇기 때문에 부자연스러운 객체 설계로인해 객체 자체는 고립이되고 협력관계도 약해지고 필요없는 행동들이 생겨나게 된다는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태위주가 아닌 행동위주로 생각함으로써 우리가 이루고자하는 그객체가 무엇을 하기위해 존재하는지(행동)을 먼저 생각하고 그 행동에 필요한 상태나 협력을 생각해야한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머리를 한대 맞은것처럼 충격적이면서도 이해가 되었다. 나는 무엇을 만드는가 필드가 중요한가 그 객체가 하는것이 중요한가. 무엇을 하는지가 중요하고 그 행동에 따른 필드가 필요한 것이다. 이게바로 전통적인 객체지향의 오해인것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러기 때문에 실세계의 모방이 아닌 단순히 참조를 해서 소프트웨어 객체를 창조하고 만들어 가는것이 객체지향의 설계에 더 맞다라는 것이다.&amp;nbsp; 이상한 나라의 앨리스에 나오는 토끼, 앨리스, 카드 왕 등은 현실세계의 인물들을 모방한것이 아닌 참고를 하여 새로운 형태를 창조한것이다. 만약 실세계를 완벽히 모방하는 것만으로 객체를 이루고자한다면 한계가 있고 우리가 이루고자하는 객체지향의 설계와 도메인은 나올수없다는 것이다. 즉 현실세계를 객체에 모두 투영하려는 과오로 고립되거나 한계를 만들지 말라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;은유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 참조라는것은 마치 은유를 통해 설명하듯이 객체도 이뤄질수있다는 것이다. '그 여자는 토끼와 같은 사람이야' 라는 은유를 통해서 그 여자가 온순한 성격을 가졌다는 특징을 알수있고 '그 남자는 사자같은 사람이야' 라고한다면 사납고 불같은 성격을 가지고있다는 것을 이해할수있다. 이처럼 객체 또한 현실세계의 모방이나 같음이 아닌 그 특징을 참고하여 재창조한다는 것이다. 자동차라는 은유와 참고를 통해 우리가 원하는 소프트웨어를 만들수있는것이고, 그것이 설령 실제 자동차와 달라지더래도 우리가 이루고자하는 소프트웨어의 목표라면 그렇게 하라는 것이다. 이 얼마나 자유롭고 유연한 사고로 비롯된 것일까 생각이 들었다. 틀에 갖히기보단 자유롭게 사고할수있는 것이라고 생각했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단 의도와 역할에 대한 일관성은 유지해야한다. 예를들어 go(), back()이라는 메서드를 보고 앞으로가거나 뒤로간다라는 역할임을 알수있고 이를 따르는것은 정도다. 하지만 창조라는것을 위해 go라하고뒤로가거나 특이한 행동을 한다면 여러 객체들간의 협력 또는 코드를 이해함에 있어서 정말 최악인것이다. 정도를 알고 오해없이 제대로 이해해야지만 골고루 분포된 객체지향이 되는것같다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 책을 다 읽고 쓴것도 아니거니와 객체지향에 대해 더 알고싶고 이책의 추천도 많아서 읽었는데 인사이트가 느는거 같아서 재미있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배울수록 재미있어진다. 이거 읽고 같은 저자가 쓴 '오브젝트'도 읽을려고 샀고 DDD관련된 책도 읽고싶다. 요새 테스트코드 작성하면서 이거와 맞물리는것도 재미있다 어떠한 의도를 볼것인지, 무엇을 테스트할건지 등등 Classicist vs mockist, 그리고 그 작성할때 생각하는게 도메인적인 관점, 행동의 관점 등 여러가지 맞물리면서 많이 알수록 좋은 설계를 할수있겠구나 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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>기타/독서</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/251</guid>
      <comments>https://youngble.tistory.com/251#entry251comment</comments>
      <pubDate>Mon, 20 Oct 2025 23:28:53 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] MSA, API Gateway , JAVA, Nodejs 스텍 고민 꿩먹고알먹고</title>
      <link>https://youngble.tistory.com/250</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;현재 프로젝트를 스프링부트, JPA, mysql 진행중인데&lt;br /&gt;숙련도나 아직 이해못한것도 있으니 꾸준히 디벨롭할 생각인데 문득생각든게 java냐 node냐 각 차이, nosql 등 사용경험을 가져갈려면 또다른 프로젝트를 진행하고나 그래야되는데 그건 솔직히 시간적, 스케일적으로 불필요하게 소모하고 낭비해야될게 많기 때문에 적용하기 힘들다. &lt;br /&gt;&lt;br /&gt;그러다 문득 든생각이 서버만 다르게하고 도메인이나 기능을 해당 언어나 프레임워크로하면 되지않나 생각하게되었고 결국 그렇게 사용할때 API Gateway를 쓰는거란걸 알았다.&lt;br /&gt;&lt;br /&gt;지금당장에 구현되어있는 api 앤드포인트 도메인중 관심종목이나 검색 기능의 경우는 간단한 로직이기도 하고 get 호출인 경우기때문에 1차적으론 monolithic 하게 배포하고 운영하고 테스트해보면 되지만 이후에는 node 로 바꿔서 하면 될거같다.&lt;br /&gt;&lt;br /&gt;요새 MSA라고 하는데 솔직히 MSA가 별거냐 결국 도메인이나 기능별 쪼개서 서비스하는 것이고 독립된 환경에서 좀더 자유롭지만 통합해서 사용할수있게하는것이니&lt;br /&gt;오히려 내가 공부할때 이방법이 최적일거라 생각이 들었다&lt;br /&gt;&lt;br /&gt;이미 프로젝트 설계는 끝났으니 쪼개기만하면 될거같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Drawing 2025-03-06 19.54.06.excalidraw.png&quot; data-origin-width=&quot;1514&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbihKE/btsMD4OaERl/ND77SEHwj0GF0NgfrdDyTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbihKE/btsMD4OaERl/ND77SEHwj0GF0NgfrdDyTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbihKE/btsMD4OaERl/ND77SEHwj0GF0NgfrdDyTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbihKE%2FbtsMD4OaERl%2FND77SEHwj0GF0NgfrdDyTK%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;1514&quot; height=&quot;437&quot; data-filename=&quot;Drawing 2025-03-06 19.54.06.excalidraw.png&quot; data-origin-width=&quot;1514&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이렇게되면 이미 난 typescript를 써봤고 백엔드쪽 로직의 큰그림도 알기때문에 nodejs 를 도입하면서 typescript로 진행하면될거같고 앤드포인트나 각 oop형식도 잘 녹여낼수있을거같다. 보니깐 nestjs가 스프링기반과 같은 계층 도입할수있는 DI 가능하게하던데 잠깐 봤는데도 비슷한거보니 이걸로하면 도입이 더 쉬울듯, 함수형보단 OOP방식으로!&lt;br /&gt;&lt;br /&gt;이제 API Gateway로 진행하면 될듯하다.&lt;br /&gt;솔직히 각 특성이나 장단점을 알고 그거에 맞게 써야하지만 아직은 각 이점을 내가 써본적이없기 땨문에 오히려 기존에있는걸 적용해보면서 차이를 알면될거같고 추후 추가되는 기능이나 컨텐츠에 맞게 이제 골라서 spring쪽이나 node쪽 앤드포인트로 서비스를 추가하면 될거같다. &lt;br /&gt;확장성도 좋고 독립적으로 개발할수있는 환경으로 분산고 되니 집중도나 다양한 스펙트럼을 얻을수있을거 같다&lt;br /&gt;정말 꿩먹고 알먹고다&lt;br /&gt;강제로라도 node를 쓰며 공부할수있으면서 각차이도 알고 경험도 쌓고 뭐가 맞는지도 고를수있고&lt;br /&gt;&lt;br /&gt;그러다 보면 추후에는 하나의 db가 아닌 그 특성에 맞는 nosql 냐 rdbms냐 골라서 추가하면되니깐 좋을거같다&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;나이스!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/250</guid>
      <comments>https://youngble.tistory.com/250#entry250comment</comments>
      <pubDate>Thu, 6 Mar 2025 14:58:50 +0900</pubDate>
    </item>
    <item>
      <title>모의주식 거래(트랜잭션, transaction) 절차 고민</title>
      <link>https://youngble.tistory.com/249</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드쪽 작은단위 도메인 서비스와 컨트롤러들은 완성했고, 초반엔 익숙치않고 알아야하는 개념들이 있어 진도가 많이못나갔지만 익숙해지니 간단한 컨트롤러나 도메인은 2-3개씩 금방금방 적용한거같다.&amp;nbsp;&lt;br&gt;검색CRUD, 검색히스토리, 소셜로그인(카카오), 일반로그인, spring security Authenciation filter, 관심종목CRUD, 실시간 종목 주가시세 웹소켓 및 단일 http, 과거 주가시세, 일부 도메인 통합테스트 진행한상태&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;이제 마지막 피날레 메인 도메인이될 매수,매도 프로세싱을 진행하기전 프로세싱 고민과 진행 후에는 이 예측이 어떻게 달라질지 TIL방식으로 간단히 적어볼려고한다. 매수매도를 진행하기위해선 계좌, 계좌내역, 주문, 주문히스토리 등이 한번에 이루워 져야하기때문에 복잡하고 설계와 플로우 로직을 고민을 많이해야한다. DB 스키마 작업시 서로 연동되는 부분들을 잘해놔야한다. 일단은 FK 개념으로 연결짓고 트랜잭션의 경우는 JPA를 통해 진행할려고한다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;DB설계 고민&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;지금 상태론 일단 Account, AccountHistory, User DB에 대한 스키마를 작성하고 서로 연관되도록 해논상태다. 먼저 Account 테이블의 경우 신규 유저가 생성된다면 무조건적으로 OneToOne으로 계좌를 가지고있도록하기위해서 JPA Entity를 이용하여 영속성전이 cascade를 통해 부모생성시 자식도 자동으로 생성되도록해놓았다. accountHistory의 경우는 account와 fk로 연관지었지만 없을수도있기때문에 Zero or Many로 적용해놓았다. 나머지 적용하지못한 Order, OrderHistory, TransactionHistory가 있는데 Transaction 부분과 Order부분의 경계를 어떻게 나눠야할지가 고민이다.&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;트랜잭션고민&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;db에 데이터를 업데이트할때 transaction을 통해 한번에 이뤄져야하는 부분들이있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;가상주식의 경우는 매수 주문 요청-&amp;gt; 주문처리시 :계좌 변동, 주문 상태변경(PROCESSING)-&amp;gt; 매수 완료시: 주식 보유량 변경, 주문상태(COMPLETED)변경, 주문내역에 COMPLETED기록 , 매수실패시 :주문 상태 변경(FAILED), 주문내역에 FAILED기록, 차감된 금액 rollback 이런식으로 된다. 정합성을 위해선 모든 과정을한  한번의 트랜잭션 내에서 이루워져야하는데 한가지 예외가 있어 고민되는 부분이 매수매도할때 바로사는게 아닌 PENDING을하는경우이다. PENDING을하다가 일정시간이 지나면 주식에서는 주문실패라 주문취소로 남게된다. 결국 트랜잭션의 핵심 개념과 원칙인 ACID에 대해서 좀더 공부하고 탐구해보자&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만 rollback을 시킨다면 모든 트래잭션이 원복이되니 돈뿐만아니라 order에 대한 히스토리나 요청도 이전으로 돌아가기때문이다. PENDING이아닌 비즈니스적 오류로인한경우는 주문요청때부터 체결전까지 프로세싱을 예기치않은 오류(critical error) 모두 rollback해야하지만, 이경우는 다르다.&lt;br&gt;&amp;nbsp;&lt;br&gt;예를들어 내가사고자하는 금액은 300원이고 현재 주식시세가 310원이라면 주문요청은 들어가지만 나머지 진행상태는 대기(PENDING)되다가 진행되어 체결되어야한다.&amp;nbsp;&lt;br&gt;트랜잭션이 하나라면 아무것도 진행하지못하기때문에 lock이 걸릴수도있다. 또한 대기라는 상태가없다고하더래도 주문요청 트랜잭션과 주문체결 트랜잭션을 별개로 가져가야 한다. 따라서 트랜잭션을 1개로 두는게 아니라 2개로 분리해야한다. 그리고 체결이 이루워지지않았다면 체결 트랜잭션은 롤백시키고(계좌 금액), 주문요청은 CANCELLED 로 업데이트해야한다. 이때역시 주기적인 주가 체크(배치 Job schedule)를 하여 살수있는 시세가 되었는지 할지 실시간 웹소켓으로 체크할지도 정해야한다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;한가지 아마 컨트롤이 불가능한 에러도있을것이다. 한국투자 API를 사용하는데 초당 제한 요청건이 정해져있고 이를 넘으면 에러처리가된다. 보통 API를 개인적으로 사용하여 주식을 매수매도하는 한명이라면 상관없지만 이를 이용해서 서비스하는 입장에선 5명의 유저만이라도 동시에 매수/매도 요청을한다면 1초당 5번의 요청이라 몇명은 적절한 요청이더래도 실패할경우다. 이경우 다른 API사용시처럼 어쩔수없이 synchroized 동기화식에 sleep 500(0.5초)를 둬야한다. 만약 100명의 유저가 동시에 요청한다면 마지막사람은 50초후에 진행되더라도 컨트롤불가능한 상황에서 에러보단 처리를 하게 만들어줘야하기떄문이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;아마 이거를 집중해서한다면 빠르면 1주일, 늦으면 2주일정도 걸릴거같다. 아 추가로 내역도봐야하고 계좌도봐야하니 부부부분 컨트롤러까지 완성하면 3주정도걸릴거같다. 목표는 3월안에 끝내기. 이부분을 끝내고나면 JPA, DB에대한 강의와 책을 통해 좀더 보완하고 이를통해 과부화테스트(JMeter같은걸로 )를 진행하여 트랜잭션이나 일반 DB 사용 최적화를 진행해볼려고한다. 그리고&amp;nbsp; 기본 자바사용법이나 OOP관련해서 한번더 짚고 갈려고한다.&lt;br&gt;이렇게 되면 백엔드 과정은 모두 끝낼예정이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/249</guid>
      <comments>https://youngble.tistory.com/249#entry249comment</comments>
      <pubDate>Mon, 3 Mar 2025 08:23:42 +0900</pubDate>
    </item>
    <item>
      <title>XSS, CSRF(로그인 쿠키기반, JWT 토큰 기반, refresh token 전략)</title>
      <link>https://youngble.tistory.com/248</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;&amp;nbsp;로그인기능을 구현할때 쿠키사용시에는 보안관련해서 secure, httponly, csrf.disable, samesite=strict 등 기본 설정이 필요한데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;이를위해서 기존에 들었던 XSS와 CSRF가 무엇인지 제대로 공부하게되었다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;먼저 세션기반(쿠키사용)으로 관리할지 JWT 토큰기반으로 관리할지 고민하다가 세션기반의 경우는 서버가 직접관리하면서도 확장성(여러 서버가 생긴다면)이를 통합해야하는 개념과 서버에 과부화가 걸릴수도있는 문제등을 생각했을때 해보고싶은 경험도 중요했지만 이부분은 프리티어 기준으로 너무 신경쓰다보면 꼬일거같아서 과부화와 확장성을위해 좀더 편하게 관리하기위해 JWT 기반으로 사용하기로했다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;JWT를 사용한다면 보통 access token과 refresh token을 사용하게 되는데, 이렇게 사용하는 이유과 결국 물리적으로 해당 토큰을 탈취하게된다면 무한정으로 리소스를 보는데 사용할수있기때문에 access token의 만료기간을 짧게 가져가고 refresh token은 길게 가져가서 주기적으로 업데이트를 하는 경우이다. 하지만 refresh token마져도 탈취가능하다면 access token만 탈취했을때보다 더 위험한경우라 만약 access token과 refresh token을 사용한다면 만료시간을 짧게 가져가는 access token은 탈취가 어느정도 가능할지라도 refresh token만은 탈취되지 않도록해야한다. 이를 위해서 refresh token의 경우는 물리적으로 탈취하지못하도록 httponly 를 통한 쿠키방식으로 사용하기로 결정하게되었다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;이를위해 자바스프링 코드는 응답에 대하여 다음과 같이 설정하게 되는데 각각의 메서드가 무엇을 의미하는지를 알아야했고 이를 통해 XSS와 CSRF를 방지하는거에 대한 고민을 하게 된것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739685403396&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ResponseCookie jwtCookie = ResponseCookie.from(&quot;jwtToken&quot;, jwt)
        .httpOnly(true)  // ✅ JavaScript에서 접근 불가능
        .secure(true)  // ✅ HTTPS에서만 전송
        .sameSite(&quot;Strict&quot;)  // ✅ CSRF 방지
        .path(&quot;/&quot;)
        .maxAge(3600)  // ✅ 만료 시간 설정
        .build();
response.addHeader(HttpHeaders.SET_COOKIE, jwtCookie.toString()&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;Secure&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;가장먼저 httpOnly와 secure부터 보면 좋을게 이전 글에도 설명한부분 http 와 https차이에 대해서 무엇인지 설명하였는데, secure의 경우는 https의 경우에만 쿠키를 담아 요청 응답할수있도록 하는것이고 이에 대한 이유는 이전글을 보면 알수있다 http의경우는 전송중간에 탈취하게되면 데이터가 암호화되어있지않기 때문에 따로 복호화없이도 이러한 정보들을 사용할수있게 되기때문인데 https인경우는 SSL/TLS handshake가 이루워지기때문에 데이터가 암호화되고 복호화과정이 필요하기때문에 중간에 탈취해도 데이터를 볼수없기때문에 설정하는 것이다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;httpOnly&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;하지만 전송 중간에 탈취하는게 아닌 물리적인 방법으로 쿠키나 토큰을 탈취한다면 해당 쿠키와 토큰을 이용하여 서버정보에 요청하여 가져갈수있게 된다 따라서 이러한 물리적인 방법으로 탈튀하지못하도록 (프로그래밍 언어를 통한 접근) http통신할때만 요청에 해당 쿠키값이 포함되어 보내지도록 하는것이다. 이는 다음에 설명할 XSS 방어중 한 방법이다. 물리적으로 코드를 통해 탈취할수 없게 하기위해 쓰는 메서드이다.&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;sameSite&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;이제 CSRF방어를 위한 설정에 대한 메서드를 알아보자. 아래에 좀더 설명하겠지만 CSRF라는 말은 요청을 위조해서 탈취하는 방법인데 예를들어 악성사이트에서 특정 사이트에 계좌이체를 요청하는 코드를 넣었다고 치자, 그리고 해당 사이트는 쿠키를 통해 인가를 하고있다면, 아무설정없이도 요청하는 코드만 있으면 자동으로 쿠키값으로 계좌이체 요청이 성공하게 된다. httpOnly더라도 samesite 가 none이라면 자동으로 요청이 가능하다는것이다. 따라서 이를 막기위해 samesite라는 부분을 strict으로하여 자동으로 쿠키를 보내주는게아니라 같은 도메인에서 요청일때만 요청하도록한다. 그런데 CORS환경의 서비스일경우, 즉 우리가 백엔드도메인과 프론트도메인이 각각 다른환경에서 api를 요청한다면 동일한 도메인이 아니기때문에 strict으로 하면 쿠키를 주거나 받을수없다. 어쩔수없이 외부 api에서 서버로 요청해서 쓰게해야한다면 samesite를 none으로해야하는데 이때는 그럼 CSRF 토큰을 사용해서 요청할때 사용하도록하고 만약 CSRF토큰이없다면 서버쪽에서 이를 forbidden처리하도록 하는것이다. (근데 결국 CSRF도 토큰이니, XSS에 취약한거아닌가? 거기다가 set-cookie라하면 CSRF자체 취약도되는거고..), 결론적으론 samesite는 None 설정, Secure를 true로 해줘야 실제 배포환경에서 쿠키를 담아서 요청할수있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;참고할점은 localhost일 경우는 Secure를 true로 해주어도 http인곳에서도 쿠키를 담아 보내지게된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;XSS(Cross Site Scripting)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;그래서 XSS는 뭔데? 라고한다면 우리가 토큰값을 가져올때 document.cookie라던가 localStorage.getItem() 과같은 코드를 입력해서 가져올수있게 해놨다. 그렇다면 해킹하는 입장에서도 해당 코드만 주입해줄수만있다면 탈취가 가능하다는 얘기인데 이를 방지하고자 HttpOnly를 쓰게되는것이다. 그렇게하면 쿠키에 접근하더래도 아무값도 볼수없다. 하지만 access token의 경우는 쿠키가아닌 storage에 담아서 사용하기때문에 탈취 위험은 있긴하지만 이를 최소화하기위해 만료일을 짧게 두는게 전략인것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;CSRF(Cross Site Request Forgey)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;CSRF는 뭔데? 라고한다면 만약 쿠키값을 자동으로 api요청할때 보내줄수만있다면 다른 도메인에서 해당 api를 요청하기만하면된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;즉 다른 도메인에서 해당 api를 호출하게만들어서 정보를 탈취하는건데 이때 따로 cookie값이나 토큰을 탈취할필요없이 api만 요청하면 자동으로 쿠키의 경우는 요청에 들어가지니 그런점을 이용한 해킹방법이다. 이를 막기위해서 사용하는것이 sameSite 값을 조절하는것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;strict으로 할 경우 같은 도메인에서의 요청이여야지만 쿠키값이 들어가지도록 설정해주는것이고 Lax는 Get요청및 링크 태그&amp;lt;a&amp;gt; 같은곳에서 요청할때만 가능하도록 해주는거기때문에 보통 다른사이트에서 테스트요청을 해야할때 사용한다고한다. 단 POST, PUT, DELETE같은 상태변화 요청에는 쿠키가 자동으로 포함될수없는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/CS</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/248</guid>
      <comments>https://youngble.tistory.com/248#entry248comment</comments>
      <pubDate>Tue, 18 Feb 2025 14:11:10 +0900</pubDate>
    </item>
    <item>
      <title>HTTPs사용시 SSL/TLS Handshake 과정(feat. tcp 3-way handshake)</title>
      <link>https://youngble.tistory.com/247</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS의 ssl/tls이 어떻게 동작하는지 원리를 알고싶어서 공부하게되었다. 사실 EC2가 아니더래오 어덯게 동작하는지 알면좋다고생각했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;HTTP와 HTTPS는 무엇이 다르고 HTTPS는 뭐였지? -&amp;gt; Https는 secure이니 http에 보안을 더한거지-&amp;gt; 그래서 무슨 보안을 더한건데? 보안넣고 통신하는거랑 안하고하는거랑 무슨차이인데? ssl인증서는 뭐야?&lt;br&gt;&amp;nbsp;&lt;br&gt;이렇게 꼬리에 꼬리름 물어가며 생각하게되었던거같다. 그리고 찾아가면서 참 재미있는 주제로 공부했다고 생각도든다.&lt;br&gt;&amp;nbsp;&lt;br&gt;추후에 내가 머리로 해당 원리를 머리에 그려갈때쯤, 제일 정확하게 확고하고 쉽게 설명한 글을 발견하였다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;a href=&quot;https://nuritech.tistory.com/25#google_vignette&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://nuritech.tistory.com/25#google_vignette&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;HTTPS 통신 원리 쉽게 이해하기 (Feat. SSL Handshake, SSL 인증서)&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;이 글을 쓰게 된 이유는,, 나의 평소 HTTPS 에 대한 지식은 HTTPS 가 암호화된 네트워크 통신 프로토콜이고 HTTPS 를 사용한 네트워크 통신에서는 주고받는 패킷을 까도 데이터가 암호화되어 있어 안&quot; data-og-host=&quot;nuritech.tistory.com&quot; data-og-source-url=&quot;https://nuritech.tistory.com/25#google_vignette&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/23XsW/hyX7TB5fQe/qWvt77mywPcI6VHf08Wyfk/img.png?width=800&amp;amp;height=457&amp;amp;face=0_0_800_457,https://scrap.kakaocdn.net/dn/bv6jKB/hyX7ThLZwp/XmjETAJAladWK5yDVkEdUK/img.png?width=800&amp;amp;height=457&amp;amp;face=0_0_800_457&quot; data-og-url=&quot;https://nuritech.tistory.com/25&quot;&gt;&lt;a href=&quot;https://nuritech.tistory.com/25&quot; target=&quot;_blank&quot; data-source-url=&quot;https://nuritech.tistory.com/25#google_vignette&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/23XsW/hyX7TB5fQe/qWvt77mywPcI6VHf08Wyfk/img.png?width=800&amp;amp;height=457&amp;amp;face=0_0_800_457,https://scrap.kakaocdn.net/dn/bv6jKB/hyX7ThLZwp/XmjETAJAladWK5yDVkEdUK/img.png?width=800&amp;amp;height=457&amp;amp;face=0_0_800_457')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;HTTPS 통신 원리 쉽게 이해하기 (Feat. SSL Handshake, SSL 인증서)&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;이 글을 쓰게 된 이유는,, 나의 평소 HTTPS 에 대한 지식은 HTTPS 가 암호화된 네트워크 통신 프로토콜이고 HTTPS 를 사용한 네트워크 통신에서는 주고받는 패킷을 까도 데이터가 암호화되어 있어 안&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;nuritech.tistory.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;br&gt;HTTPS이전에 TCP 통신커넥트를 간략히 보여주고 SSL처리과정에 대해서 설명하는데&lt;br&gt;&amp;nbsp;&lt;br&gt;먼저 TCP 통신할기위해서 3-way handshake를 하는데&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;1. 클라이언트 -&amp;gt; 서버로 Sync 패킷보냄&lt;br&gt;2. 이를 받은 서버 -&amp;gt; 클라이언트 ACK + Sync 패킷 보냄&lt;br&gt;3. 이를 받은 클라이언트 -&amp;gt; 서버 ACK 패킷을 보냄으로써 TCP기반 통신이 완료된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이 이후에 HTTPs가 이루워지는데&lt;br&gt;&amp;nbsp;&lt;br&gt;여기서 &lt;b&gt;SSL handshake의 목적성&lt;/b&gt;을 먼저 알아야한다. &lt;b&gt;왜 SSL handshake를 하느냐?&lt;/b&gt;&amp;nbsp;&lt;br&gt;1. 주고받을 데이터를 어떤 암호화 알고리즘을 쓸지 협의해야한다.&lt;br&gt;2. 클라이언트와 서버가 데이터 암호화에 쓰일 대칭키를 공유해야한다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;말그대로 데이터를 암호화해서 서로 주고받고 암호화/복호화해야하는데 어떤 알고리즘을 쓰는지도 모르고 만약 그 알고리즘을 쓴다고했을때 그걸 복호화할수있는 키(세션키)를 어떻게 그럼 서로에게 넘겨주느냐가 관건이다. 그냥 넘겨준다면 보안의 문제로 인해 이것마저도 방법이 필요하기때문이다. 그래서 데이터를 주고받기전에 SSL handshake과정이 이루워지는것이다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;이때 사용하는 방식이 비대칭키(공개키+비공개키) + 대칭키(공개키, 서로같은 공개키) 암호화방식을 혼합하여 사용한다. 이 비대칭키는 무엇이고 대칭키는 무엇인지 아래에 좀더 추가로설명하겠다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;SSL/TLS Handshake 과정&lt;/h4&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;1087&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvgDIG/btsL7MtCLmo/f29bJ52URTuqwuU9BhiaS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvgDIG/btsL7MtCLmo/f29bJ52URTuqwuU9BhiaS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvgDIG/btsL7MtCLmo/f29bJ52URTuqwuU9BhiaS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvgDIG%2FbtsL7MtCLmo%2Ff29bJ52URTuqwuU9BhiaS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1267&quot; height=&quot;1087&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;1087&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;참고로 SSL 은 구버전이고 현재는 대부분 TLS 을 사용한다고함&lt;br&gt;&amp;nbsp;&lt;br&gt;1. Client Hello: 먼저 클라이언트가 서버로 요청을 보낼때 자신이 사용할수있는 암호화 알고리즘 리스트를 패킷에 담아 보낸다.&lt;br&gt;&amp;nbsp;&lt;br&gt;2. Certificate: 서버는 이패킷을 받고 클라이언트가준 목록중 사용할 알고리즘 정보를 담은 패킷(Server Hello)과 CA를 통해 얻은 SSL인증서 패킷을 클라이언트에게 보낸후 끝냈음을 알린다(Server Hello Done). 이때 SSL인증서에는 비대칭키에 쓰이는 공개키정보, 만료일, 서명정보 등을 담고있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;2-1. Server Exchage: 만약 Certificate에 공개키정보가없다면 서버가 직접 공개키를 전달하고 그게아니라면 스킵&lt;br&gt;&amp;nbsp;&lt;br&gt;3. SSL인증서를 받은 클라이언트(브라우저)는 해당 인증서가 신뢰할수있는지 검증을 거친다. 이때 검증은 각 브라우저마다 인증기관,CA(Certificate &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #474747;&quot;&gt;Authority&lt;/span&gt;&lt;/span&gt;)리스트들이 있다고한다. 이를 통해 신뢰할수있는 인증인지를 검증을한후 검증이 완료가 되면 이제 서로 사용하기로한 암호화알고리즘을 이용한 세션키(대칭키)를 서버쪽에 보내준다. 근데 이걸 보낼때 그냥보내면 탈취했을때 그냥 사용할수있기때문에 탈취하더래도 복호화할수있는 사람만 쓸수있도록하기위해서 아까 서버가 보내준 비대칭키의 공개키를 해당 대칭키(세션키)를 암호화해서 보내준다.&lt;br&gt;&amp;nbsp;&lt;br&gt;4. Change Cipher Specs: 이제 데이터를 암호화하여 보내라고 요청후 끝낸다(finshed)&lt;br&gt;&amp;nbsp;&lt;br&gt;5. 서버는 공개키로 암호화된 대칭키(세션키)를 자신이 가지고있는 비공개키로 대칭키를 복호화한다.&lt;br&gt;6. Change Cipher Specs: 클라이언트에게 똑같이 데이터를 암호화하여 보내라는요청후 끝낸다(finished)&lt;br&gt;7. 이제 서로 데이터암호화하는데 사용할 대칭키를 가지고있기때문에 Https 사용 준비를 끝내고 이제 데이터를 주고받는다.&lt;br&gt;&amp;nbsp;&lt;br&gt;여기서 이제 비대칭키와 대칭키가 무엇인지 간략하게 얘기해보면&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;비대칭키 암호화&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;비대칭키는 말처럼 대칭하지않은 비대칭하다는 뜻을 가진다. 공개키와 비공개키를 통해 암호화를한다면 한쪽은 공개키 한쪽은 비공개키를 가지고있기때문에 결국 비대칭하다는 뜻이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이때 공개키는 클라이언트에서 가지고있고 비공개키는 서버에서 가지고있는다, 공개키는 많은 클라언트가 있기때문에 누구든 암호화하는데 사용할수있도록 제공하는것이고 비공개키는 결국 그걸 복호화할수있는건 오직 서버뿐이기때문에 서버만이 가지고있는것이다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;대칭키 암호화&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이제 반대로 대칭키는 서로 같은 키를 가지고있는걸 말한다. 클라이언트가 서버로 서버가 클라이언트로 데이터를 보낼때 서로 같은 키를 가지고있어야 이를 해독/복호화할수 있기때문에 서로 같은걸 가지고있어야한다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;여기까지 설명을 하면 결국 SSL Handshake는 대칭키를 주고받기위한 행위, 그리고 그 대칭키가 어떤알고리즘을 쓸건지 정하는 공유가 주목적이되는거다. 그래서 데이터를 보내기전에 대칭키를 서로 공유하기위해서 서버가 제공한 비대칭키를 통해 클라이언트는 대칭키를 암호화해서 서버에게 보내주고 이를 서버가 비공개키로 해독하는 과정인것이다.&lt;br&gt;&lt;br&gt;TLS과정에서 하이브리드(혼합) 방식을 사용하는 이유는 각각의 장단점이 있기때문에 장점을 살리기위해서 이다.&lt;br&gt;비대칭키 암호화방식의 경우는 모든 데이터를 비대칭으로 암호화하면 성능이 너무 떨어지고, 대칭키 암호화방식은 데이터전송시 대칭키를 이용하면 속도가 빠르지만 안전하게 대칭키를 주기가 힘들다. 따라서 속도+보안을 위해 하이브리드 형식으로 쓰는것이다&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/네트워크</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/247</guid>
      <comments>https://youngble.tistory.com/247#entry247comment</comments>
      <pubDate>Tue, 4 Feb 2025 16:34:57 +0900</pubDate>
    </item>
    <item>
      <title>AWS IPv4 프리티어제외 유료화</title>
      <link>https://youngble.tistory.com/246</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size18&quot;&gt;어느순간부터 AWS에서 비용이 청구되었다.&lt;br&gt;현재 프리티어 계정인데 1월 부터 비용이 청구되는것을 보고 뭐지싶었다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GDFq1/btsL4Nmu4mb/pKWgJbtyet7EIF2WAyHjk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GDFq1/btsL4Nmu4mb/pKWgJbtyet7EIF2WAyHjk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GDFq1/btsL4Nmu4mb/pKWgJbtyet7EIF2WAyHjk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGDFq1%2FbtsL4Nmu4mb%2FpKWgJbtyet7EIF2WAyHjk0%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;714&quot; height=&quot;372&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;AWS에서 각각의 서비스들의 한가지씩은 모두 사용하더래도 무료이기때문에 돈이 나갈일이없어야하는데 1월26일자 기준으로 1월은 2.37달러, 2월은 예상 7달러가 나오길래 혹시모르는 내 설정이나 과도한 트래픽이 문제인가 생각하게되었다. 웹소켓을 통해 실시간 주식정보를 받는것도있고 EC2안에서 프로젝트를 시작할때 약 2700개의 주식정보들을 인메모리에 저장하여 쓰기위해서 DB에서 가져오는데 테스트할때는 프로젝트를 껏다가 배포하고 껏다가 배포하고 하니깐 많은 쿼리가 발생한다고 생각했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;먼저 떠올린건 로드밸런서 도입이후기때문에 로드밸런서가 공짜가 아닌가? 생각을했고 원래부터 프로젝트로는 비용을 내지말자라는 생각이였는데 만약 로드밸런서를 활용했지만 돈이나간다면 이를 해지하고 다시 EC2에서 별도로 리버스프록시를 만들어서 SSL을 처리하도록 해야겠다고 생각했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이추측은 틀린말은 아니였다. 하지만 로드밸런서 자체로는 비용이 청구되고있지않았다. 계속해서 VPC 이용에 대한 비용과 Route53에 대해 비용이 발생하길래 VPC의 경우 딱히 내가 설정했다기보단 로드밸런서를 생성하면서 만들었던거라 이리저리 검색도해보고 했는데 한 커뮤니티에서 참고하라고 알려준 글중하나가 IPv4에 대한 비용이였다.&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만 이게 정확히 나한테 해당하는건지 몰랐고 내가 IPv4를 따로 구축한게 있는지를 몰랐었다. 그럼 어디서 비용이 나오는지 보는 방법을 찾아보게되었는데, 청구서 -&amp;gt; 서비스별 요금에서 좀더 정확한 리스트&lt;br&gt;가 나오는걸 알수있었다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1507&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6rmcf/btsL3lko7rA/JVs9rY0lTJ5KX6cQYAVo40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6rmcf/btsL3lko7rA/JVs9rY0lTJ5KX6cQYAVo40/img.png&quot; data-alt=&quot;청구서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6rmcf/btsL3lko7rA/JVs9rY0lTJ5KX6cQYAVo40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6rmcf%2FbtsL3lko7rA%2FJVs9rY0lTJ5KX6cQYAVo40%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;1507&quot; height=&quot;541&quot; data-origin-width=&quot;1507&quot; data-origin-height=&quot;541&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;청구서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;해당 목록을 보면 VPC에서 IPv4 Address라는 항목이 있는데 EC2 Free Tier라는 부분을 제외한 아래부분에서 시간당 0.005달러가 발생한다는걸 알수있었다. 그리고 route53의 경우도 호스팅을 route53으로 잡을시 호스트당(첫 25개까지) 0.5라는 내용이 있었다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;커뮤니팅서 말한 부분이 맞았고 아래 AWS 의 Public IPv4에 대한 변경 공지이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/korea/new-aws-public-ipv4-address-charge-public-ip-insights/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://aws.amazon.com/ko/blogs/korea/new-aws-public-ipv4-address-charge-public-ip-insights/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;공지 – AWS Public IPv4 주소 요금 변경 및 Public IP Insights 기능 출시 | Amazon Web Services&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;AWS에서 퍼블릭(Public) IPv4 주소에 대한 새로운 요금이 도입됩니다. 2024년 2월 1일부터 서비스 연결 여부에 관계없이 모든 퍼블릭 IPv4 주소에 대해 시간당 IP당 0.005 USD의 요금이 부과됩니다. 계정에 &quot; data-og-host=&quot;aws.amazon.com&quot; data-og-source-url=&quot;https://aws.amazon.com/ko/blogs/korea/new-aws-public-ipv4-address-charge-public-ip-insights/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WYJZR/hyYcdS3HxB/0H7ju1ORkRiBgfFWp38esk/img.jpg?width=1260&amp;amp;height=601&amp;amp;face=0_0_1260_601,https://scrap.kakaocdn.net/dn/cZJKw9/hyYb94bWUl/LRZDKw9fcITpCyqcrkEnqK/img.jpg?width=1260&amp;amp;height=601&amp;amp;face=0_0_1260_601,https://scrap.kakaocdn.net/dn/bmiHuN/hyYckkleTT/H44wC7TsZE2U791MjQpLjK/img.png?width=900&amp;amp;height=767&amp;amp;face=0_0_900_767&quot; data-og-url=&quot;https://aws.amazon.com/ko/blogs/korea/new-aws-public-ipv4-address-charge-public-ip-insights/&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/korea/new-aws-public-ipv4-address-charge-public-ip-insights/&quot; target=&quot;_blank&quot; data-source-url=&quot;https://aws.amazon.com/ko/blogs/korea/new-aws-public-ipv4-address-charge-public-ip-insights/&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WYJZR/hyYcdS3HxB/0H7ju1ORkRiBgfFWp38esk/img.jpg?width=1260&amp;amp;height=601&amp;amp;face=0_0_1260_601,https://scrap.kakaocdn.net/dn/cZJKw9/hyYb94bWUl/LRZDKw9fcITpCyqcrkEnqK/img.jpg?width=1260&amp;amp;height=601&amp;amp;face=0_0_1260_601,https://scrap.kakaocdn.net/dn/bmiHuN/hyYckkleTT/H44wC7TsZE2U791MjQpLjK/img.png?width=900&amp;amp;height=767&amp;amp;face=0_0_900_767')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;공지 – AWS Public IPv4 주소 요금 변경 및 Public IP Insights 기능 출시 | Amazon Web Services&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;AWS에서 퍼블릭(Public) IPv4 주소에 대한 새로운 요금이 도입됩니다. 2024년 2월 1일부터 서비스 연결 여부에 관계없이 모든 퍼블릭 IPv4 주소에 대해 시간당 IP당 0.005 USD의 요금이 부과됩니다. 계정에 &lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;aws.amazon.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;br&gt;그렇다면 내가 IPv4를 프리티어 제외하고 쓰고있다는건데 나는 내 도메인이외엔 쓴게있나? 생각을했고 이리저리 클릭해서보니 VPC에서 서브넷이 4개가 잡혀있었다. 2개는 로드밸런서를 사용하기위해선 설정해야한다고해서 만든거같은데 나머지2개는 왜생겼는지는 아직까지 의문이다, 뭔가 만들면서 생겼을거같긴한데 크게 생각못했던부분이라..&lt;br&gt;어쨌든 EC2자체와 로드밸런서 유지를위한 서브넷을 제외한 나머지 두개는 사용하고있지않아서 삭제하였고 아래와같이 구축해놨다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cod9fv/btsL4rjLhhV/rqijCVBrKRiaLF5o7f2j60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cod9fv/btsL4rjLhhV/rqijCVBrKRiaLF5o7f2j60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cod9fv/btsL4rjLhhV/rqijCVBrKRiaLF5o7f2j60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcod9fv%2FbtsL4rjLhhV%2FrqijCVBrKRiaLF5o7f2j60%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;1086&quot; height=&quot;175&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;IPv4에 대한 비용을 2024년부터 청구한이유가 이미 IPv4를 통한 주소를 얻는게 힘들고 고갈되었다는 이유였다. IPv4는 0~255범위에서 0.0.0.0 과같은 범위에서만 가능한데 처음IPv4도입당시에는 충분하겠다고 생각했지만 이는 현대에서는 부족하다는거였다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서 퍼블릭 IPv4가 계속있는한 돈이 발생하기때문에 방법을 생각했던게 IPv4가아닌 IPv6를 쓰는걸 고려했지만 이는 IPv4와 IPv6간의 통신은 불가능하다는거였고 이를 또 구축하려면 뭔가 다 뜯고 수정해야될거같아서, 아싸리 로드밸런서를 없애버리고 프로젝트로 사용하는 EC2 인스턴스안에 구축해야겠다고 생각하게되었다. 이부분은 곧 구축할예정이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;만약 로드밸런서를 사용하지않는다면 Route53으로도 호스팅하지않고 무료 호스팅가능한 가비아로 옮기는것도 고려중이다.&lt;br&gt;만약 활용하기에 Route53을 유지해야한다면 그렇지만 그게아니라면 옮길까 생각중인데 트래픽 연습으로 오토스케일링이나 다른 AWS서비스에 문제가없다면 이것도 바꿀예정이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;실무라면 어느정도 예산이나 사용할 여분이있겠지만 개인/소규모 사이드프로젝트로는 최소한의 비용이 발생해야하기때문에 굳이 필요한 스펙이아니면 공부/인사이트를 제외하고 과감히 포기해야하는 아키텍처역할도 해야할거같다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL</category>
      <author>youngble</author>
      <guid isPermaLink="true">https://youngble.tistory.com/246</guid>
      <comments>https://youngble.tistory.com/246#entry246comment</comments>
      <pubDate>Sun, 2 Feb 2025 16:56:37 +0900</pubDate>
    </item>
  </channel>
</rss>