<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>wangkisa의 기술블로그</title>
    <link>https://wangkisa.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 18 Aug 2026 03:52:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>wangkisa</managingEditor>
    <item>
      <title>글또 8기 후기</title>
      <link>https://wangkisa.tistory.com/86</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;벌써 마지막이라니..&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글또 8기가 시작한지 얼마 안된것 같은데 벌써 마지막이라니 6개월 기간이 한 기수인데&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;글또 활동 돌아보기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_스크린샷 2023-07-16 21.08.11.png&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIqxxd/btsnIKtt7xP/X5qO5bxmTmq3dRImAgSD91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIqxxd/btsnIKtt7xP/X5qO5bxmTmq3dRImAgSD91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIqxxd/btsnIKtt7xP/X5qO5bxmTmq3dRImAgSD91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIqxxd%2FbtsnIKtt7xP%2FX5qO5bxmTmq3dRImAgSD91%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;654&quot; height=&quot;69&quot; data-filename=&quot;edited_스크린샷 2023-07-16 21.08.11.png&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;69&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지난 활동들을 돌아보니,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번은 제출을 하지 못했었고, 패스는 2번 다 사용하고, 제출은 총 8번 냈던 것으로 확인하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로 글또 했던 것을 돌이켜보면, 한마디로 생각보다 열심히 하지 못하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마감만 지키게 되는 아쉬운 활동이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;없어서 약간 형식적으로만 하기도 한 느낌이었다.&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;시간이 지나고 보니 왜 그런지 알 수 있던 시간이었다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;백엔드 반상회&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;여러 백엔드 개발자들을 만나는 시간을 가질 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SQS 에 대해 발표하신 채널톡 개발자 분을 보며 경력이 되게 짧으신데도, 굉장한 내공을 가지고 계셔서&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대단하신 분을 보니 굉장히 신기하다는 생각이 많이 들었었고,&amp;nbsp;&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;</description>
      <category>좋은글</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/86</guid>
      <comments>https://wangkisa.tistory.com/86#entry86comment</comments>
      <pubDate>Sun, 16 Jul 2023 21:36:55 +0900</pubDate>
    </item>
    <item>
      <title>카프카 프로듀서(producer), 컨슈머(consumer) (2)</title>
      <link>https://wangkisa.tistory.com/85</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스프링 카프카&lt;/b&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;기존 카프카 클라이언트 라이브러리를 래핑하여 만든 스프링 카프카 라이브러리는 카프카 클라이언트에서 사용하는 여러 가지 패턴을 미리 제공한다.&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;스프링 카프카 라이브러리를 사용하기 위해서 다음과 같이 build.gradle에 디펜던시를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688303638064&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.springframework.kafka:spring-kafka:2.5.10.RELEASE'
    implementation 'org.springframework.boot:spring-boot-starter:2.4.0'
}&lt;/code&gt;&lt;/pre&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;b&gt;1. 스프링 카프카 프로듀서&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;스프링 카프카 프로듀서는 '카프카 템플릿'이라고 불리는 클래스를 사용하여 데이터를 전송할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;카프카 템플릿을 사용하는 방법은 크게 두가지가 있다. 첫 번째는 스프링 카프카에서 제공하는 기본 카프카 템플릿을 사용하는 방법이고,&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두번째는 직접 사용자가 카프카 템플릿을 프로듀서 팩토리로 생성하는 방법이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;application.yaml 내용&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688304249434&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  kafka:
    producer:
      bootstrap-servers: my-kafka:9092
      acks: all&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;다음은 스프링 부트 애플리케이션을 실행하고 test0부터 test9까지 메시지 값을 클러스터로 보내는 프로듀서 애플리케이션을 작성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688304372915&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.kafka.core.KafkaTemplate;

@SpringBootApplication
public class SpringProducerApplication implements CommandLineRunner {

    private static String TOPIC_NAME = &quot;test&quot;;

    @Autowired
    private KafkaTemplate&amp;lt;Integer, String&amp;gt; template;

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SpringProducerApplication.class);
        application.run(args);
    }

    @Override
    public void run(String... args) {
        for (int i = 0; i &amp;lt; 10; i++) {
            template.send(TOPIC_NAME, &quot;test&quot; + i);
        }
        System.exit(0);
    }
}&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;여기서 send() 메서드를 사용해서 토픽 이름과 메시지 값을 넣어 전송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카 프로듀서의 send()메서드와 유사한 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1688304436307&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        for (int i = 0; i &amp;lt; 10; i++) {
            template.send(TOPIC_NAME, &quot;test&quot; + i);
        }&lt;/code&gt;&lt;/pre&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;b&gt;1.1 커스텀 카프카 템플릿&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;커스텀 카프카 템플릿은 프로듀서 팩토리를 통해 만든 카프카 템플릿 객체를 빈으로 등록하여 사용하는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로듀서에 필요한 각종 옵션을 선언하여 사용할 수 있으며, 한 스프링 카프카 애플리케이션 내부에 다양한 종류의 카프카 프로듀서 인스턴스를 생성하고 싶다면 이 방식을 사용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어, A클러스터로 전송하는 카프카 프로듀서와 B클러스터로 전송하는 카프카 프로듀서를 동시에 사용하고 싶다면,&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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688305022891&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example;

import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.*;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class KafkaTemplateConfiguration {

    @Bean
    public KafkaTemplate&amp;lt;String, String&amp;gt; customKafkaTemplate() {

        Map&amp;lt;String, Object&amp;gt; props = new HashMap&amp;lt;&amp;gt;();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, &quot;my-kafka:9092&quot;);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.ACKS_CONFIG, &quot;all&quot;);

        ProducerFactory&amp;lt;String, String&amp;gt; pf = new DefaultKafkaProducerFactory&amp;lt;&amp;gt;(props);

        return new KafkaTemplate&amp;lt;&amp;gt;(pf);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1688305058095&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.kafka.core.KafkaProducerException;
import org.springframework.kafka.core.KafkaSendCallback;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.util.concurrent.ListenableFuture;

@SpringBootApplication
public class SpringProducerApplication implements CommandLineRunner {

    private static String TOPIC_NAME = &quot;test&quot;;

    @Autowired
    private KafkaTemplate&amp;lt;String, String&amp;gt; customKafkaTemplate;

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SpringProducerApplication.class);
        application.run(args);
    }

    @Override
    public void run(String... args) {
        ListenableFuture&amp;lt;SendResult&amp;lt;String, String&amp;gt;&amp;gt; future = customKafkaTemplate.send(TOPIC_NAME, &quot;test&quot;);
        future.addCallback(new KafkaSendCallback&amp;lt;String, String&amp;gt;() {
            @Override
            public void onSuccess(SendResult&amp;lt;String, String&amp;gt; result) {

            }

            @Override
            public void onFailure(KafkaProducerException ex) {

            }
        });
        System.exit(0);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;b&gt;2. 스프링 카프카 컨슈머&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;스프링 카프카 컨슈머는&lt;span&gt; 기존 카프카 컨슈머를 2개의 타입으로 나누고 커밋을 7가지로 나누어 세분화했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;우선, 타입은 레코드 리스너(MessageListener)와 배치 리스너(BatchMessageListener) 가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;리스너 종류에 따라 한번 호출하는 메서드에서 처리하는 레코드의 개수가 달라진다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;- 단 1개의 레코드를 처리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;- 기존 카프카 클라이언트 라이브러리의 &lt;b&gt;poll() 메서드&lt;/b&gt;로 리턴받은 ConsumerRecords처럼 한 번에 여러 개 레코드들을 처리할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;스프링 카프카 컨슈머의 기본 리스너 타입은 레코드 리스너이다. 레코드 리스너와 배치 리스너 외에도 각 리스너에서 파생된 형태로 다음과 같은 형태가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RECORD Type: Record 인스턴스 단위로 프로세싱&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: left;&quot;&gt;
&lt;div&gt;
&lt;table style=&quot;color: #333333; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;리스너 이름&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;생성 메서드 파라미터&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;MessageListener&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;onMessage(ConsumerRecord&amp;lt;K, V&amp;gt; data)&lt;br /&gt;&lt;br /&gt;onMessage(V data)&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;오토 커밋 또는 컨슈머 컨테이너의 AckMode를 사용하는 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AcknowledgingMessageListener&lt;/td&gt;
&lt;td&gt;onMessage(ConsumerRecord&amp;lt;K, V&amp;gt; data, Acknowledgment acknowledgment)&lt;br /&gt;&lt;br /&gt;onMessage(V data, Acknowledgment acknowledgment)&lt;/td&gt;
&lt;td&gt;매뉴얼 커밋을 사용하는 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;ConsumerAwareMessageListener&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;onMessage(ConsumerRecord&amp;lt;K, V&amp;gt; data, Consumer&amp;lt;K, V&amp;gt; consumer)&lt;br /&gt;&lt;br /&gt;onMessage(V data, Consumer&amp;lt;K, V&amp;gt; consumer)&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;컨슈머 객체를 활용하고 싶은 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AcknowledgingConsumerAwareMessageListener&lt;/td&gt;
&lt;td&gt;onMessage(ConsumerRecord&amp;lt;K, V&amp;gt; data, Acknowledgment acknowledgment, Consumer&amp;lt;?, ?&amp;gt; consumer)&lt;br /&gt;&lt;br /&gt;onMessage(V data, Acknowledgment acknowledgment, Consumer&amp;lt;K, V&amp;gt; consumer)&lt;/td&gt;
&lt;td&gt;매뉴얼 커밋을 사용하고 컨슈머 객체를 활용하고 싶은 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BATCH Type: Records 인스턴스 단위로 프로세싱&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: left;&quot;&gt;
&lt;div&gt;
&lt;table style=&quot;color: #333333; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;리스너 이름&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;파라미터&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;BatchMessageListener&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;onMessage(ConsumerRecords&amp;lt;K, V&amp;gt; data)&lt;br /&gt;&lt;br /&gt;onMessage(List&amp;lt;V&amp;gt; data)&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;오토 커밋 또는 컨슈머 컨테이너의 AckMode를 사용하는 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BatchAcknowledgingMessageListener&lt;/td&gt;
&lt;td&gt;onMessage(ConsumerRecords&amp;lt;K, V&amp;gt; data, Acknowledgment acknowledgment)&lt;br /&gt;&lt;br /&gt;onMessage(List&amp;lt;V&amp;gt; data, Acknowledgment acknowledgment)&lt;/td&gt;
&lt;td&gt;매뉴얼 커밋을 사용하는 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;BatchConsumerAwareMessageListener&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;onMessage(ConsumerRecords&amp;lt;K, V&amp;gt; data, Consumer&amp;lt;K, V&amp;gt; consumer)&lt;br /&gt;&lt;br /&gt;onMessage(List&amp;lt;V&amp;gt; data, Consumer&amp;lt;K, V&amp;gt; consumer)&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7;&quot;&gt;컨슈머 객체를 활용하고 싶은 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BatchAcknowledgingConsumerAwareMessageListener&lt;/td&gt;
&lt;td&gt;onMessage(ConsumerRecords&amp;lt;K, V&amp;gt; data, Acknowledgment acknowledgment, Consumer&amp;lt;K, V&amp;gt; consumer)&lt;br /&gt;&lt;br /&gt;onMessage(List&amp;lt;V&amp;gt; data, Acknowledgment acknowledgment, Consumer&amp;lt;K, V&amp;gt; consumer)&lt;/td&gt;
&lt;td&gt;매뉴얼 커밋을 사용하고 컨슈머 객체를 활용하고 싶은 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;카프카 컨슈머에서 커밋을 직접 구현할 때는&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오토 커밋, 동기 커밋, 비동기 커밋&lt;/b&gt; 크게 세가지로 나뉘지만 실제 운영환경에서는 다양한 종류의 커밋을 구현해서 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;예를 들어, 특정 타이밍마다 커밋을 하거나 레코드 개수에 따라 커밋을 하는 규칙을 적용할 경우에는 로직을 새로 작성해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 카프카에서는 커밋이라고 부르지 않고 'AckMode'라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 카프카 컨슈머의 AckMode 기본값은 BATCH이고 컨슈머의 enable.auto.commit 옵션은 false로 지정된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;AckMode 종류와 설명&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: left; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;RECORD&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.6047%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;레코드 단위로 프로세싱 이후 커밋&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f7f7f7; width: 21.2791%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;BATCH&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7; width: 78.6047%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;poll() 메서드로 호출된 레코드가 모두 처리된 이후 커밋&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스프링 카프카 컨슈머의 AckMode 기본값&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;TIME&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.6047%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특정 시간 이후에 커밋&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 옵션을 사용할 경우 시간 간격을 선언하는 AckTime 옵션을 설정해야 한다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f7f7f7; width: 21.2791%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;COUNT&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7; width: 78.6047%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특정 개수만큼 레코드가 처리된 이후 커밋&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 옵션을 사용할 경우에는 레코드 개수를 선언하는 AckCount 옵션을 설정해야 한다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;COUNT_TIME&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.6047%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TIME, COUNT 옵션 중 맞는 조건이 하나라도 나올 경우 커밋&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #f7f7f7; width: 21.2791%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;MANUAL&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f7f7f7; width: 78.6047%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Acknowledgement.acknowledge() 메서드가 호출되면 다음번 poll() 때 커밋을 한다. 매번 acknowledge() 메서드를 호출하면 BATCH 옵션과 동일하게 동작한다. 이 옵션을 사용할 경우에는 &lt;br /&gt;AcknowledgingMessageListener 또는 BatchAcknowledgingMessageListener를 리스너로 사용해야 한다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;MANUAL_IMMEDIATE&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.6047%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Acknowledgement.acknowledge() 메서드를 호출한 즉시 커밋한다. &lt;br /&gt;이 옵션을 사용할 경우에는 AcknowledgingMessageListener 또는 &lt;br /&gt;BatchAcknowledgingMessageListener를 리스너로 사용해야 한다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 리스너 컨네이너&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1688307932436&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# application.yaml
spring:
  kafka:
    consumer:
      bootstrap-servers: my-kafka:9092
    listener:
      type: RECORD&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;레코드 리스너를 활용한 코드&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;SpringConsumerApplication.java&lt;/p&gt;
&lt;pre id=&quot;code_1688311161991&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.annotation.PartitionOffset;
import org.springframework.kafka.annotation.TopicPartition;

@SpringBootApplication
public class SpringConsumerApplication {
    public static Logger logger = LoggerFactory.getLogger(SpringConsumerApplication.class);


    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SpringConsumerApplication.class);
        application.run(args);
    }

    @KafkaListener(topics = &quot;test&quot;,
            groupId = &quot;test-group-00&quot;)
    public void recordListener(ConsumerRecord&amp;lt;String,String&amp;gt; record) {
        logger.info(record.toString());
    }

    @KafkaListener(topics = &quot;test&quot;,
            groupId = &quot;test-group-01&quot;)
    public void singleTopicListener(String messageValue) {
        logger.info(messageValue);
    }

    @KafkaListener(topics = &quot;test&quot;,
            groupId = &quot;test-group-02&quot;, properties = {
            &quot;max.poll.interval.ms:60000&quot;,
            &quot;auto.offset.reset:earliest&quot;
    })
    public void singleTopicWithPropertiesListener(String messageValue) {
        logger.info(messageValue);
    }

    @KafkaListener(topics = &quot;test&quot;,
            groupId = &quot;test-group-03&quot;,
            concurrency = &quot;3&quot;)
    public void concurrentTopicListener(String messageValue) {
        logger.info(messageValue);
    }

    @KafkaListener(topicPartitions =
            {
                    @TopicPartition(topic = &quot;test01&quot;, partitions = {&quot;0&quot;, &quot;1&quot;}),
                    @TopicPartition(topic = &quot;test02&quot;, partitionOffsets = @PartitionOffset(partition = &quot;0&quot;, initialOffset = &quot;3&quot;))
            },
            groupId = &quot;test-group-04&quot;)
    public void listenSpecificPartition(ConsumerRecord&amp;lt;String, String&amp;gt; record) {
        logger.info(record.toString());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;스프링 카프카 배치 리스너&lt;/p&gt;
&lt;pre id=&quot;code_1688311309622&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# application.yaml
spring:
  kafka:
    consumer:
      bootstrap-servers: my-kafka:9092
    listener:
      type: BATCH&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688311284849&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example;

import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.kafka.annotation.KafkaListener;

import java.util.List;

@SpringBootApplication
public class SpringConsumerApplication {
    public static Logger logger = LoggerFactory.getLogger(SpringConsumerApplication.class);

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SpringConsumerApplication.class);
        application.run(args);
    }

    @KafkaListener(topics = &quot;test&quot;,
            groupId = &quot;test-group-01&quot;)
    public void batchListener(ConsumerRecords&amp;lt;String, String&amp;gt; records) {
        records.forEach(record -&amp;gt; logger.info(record.toString()));
    }

    @KafkaListener(topics = &quot;test&quot;,
            groupId = &quot;test-group-02&quot;)
    public void batchListener(List&amp;lt;String&amp;gt; list) {
        list.forEach(recordValue -&amp;gt; logger.info(recordValue));
    }

    @KafkaListener(topics = &quot;test&quot;,
            groupId = &quot;test-group-03&quot;,
            concurrency = &quot;3&quot;)
    public void concurrentBatchListener(ConsumerRecords&amp;lt;String, String&amp;gt; records) {
        records.forEach(record -&amp;gt; logger.info(record.toString()));
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;* 출처: 아파치 카프카 애플리케이션 프로그래밍 with 자바 책&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 출처: &lt;a href=&quot;https://hanseom.tistory.com/174&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hanseom.tistory.com/174&lt;/a&gt;&lt;/p&gt;</description>
      <category>Kafka</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/85</guid>
      <comments>https://wangkisa.tistory.com/85#entry85comment</comments>
      <pubDate>Sun, 2 Jul 2023 21:40:38 +0900</pubDate>
    </item>
    <item>
      <title>DDD 도메인 주도 설계(Domain-Driven Design)란?</title>
      <link>https://wangkisa.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 도메인 주도 설계란?&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DDD (도메인 주도 설계)는 해당 도메인과 일치하도록 소프트웨어를 모델링하는 데 중점을 둔 소프트웨어 설계 접근 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무슨 말인지 이해가 안가니 하나 하나 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용들은 최범균 님의 '도메인 주도 개발 시작하기' 책을 참고한 내용들이 많음을 미리 일러둔다.&lt;/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;b&gt;1.1 도메인이란?&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 얘기하는 온라인 서점은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어로 해결하고자 하는 문제 영역, 도메인(domain)에 해당한다.&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;b&gt;1.2 도메인 모델&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;
&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;1.3 도메인 모델 패턴&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;Presentation&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사용자의 요청을 처리하고, 사용자에게 정보를 보여준다. 여기서 사용자는 소프트웨어를 사용하는 사람뿐만 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 시스템일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application&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;Domain&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;Infrastructure&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;b&gt;1.4 엔티티와 밸류&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;
&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;주문, 회원, 상품과 같이 도메인의 고유한 개념을 표현한다.&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;사용된다. 배송지 주소를 표현하기 위한 주소나 구매 금액을 위한 금액과 같은 타입이 밸류 타입이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티의 속성으로 사용할 뿐만 아니라 다른 밸류 타입의 속성으로도 사용할 수 있다.&lt;/p&gt;</description>
      <category>아키텍처</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/84</guid>
      <comments>https://wangkisa.tistory.com/84#entry84comment</comments>
      <pubDate>Sun, 4 Jun 2023 22:41:17 +0900</pubDate>
    </item>
    <item>
      <title>카프카 개요(1)</title>
      <link>https://wangkisa.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사내에서 기존 시스템 개선을 하기 위해 카프카를 도입하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 카프카란 무엇인지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;apache-kafka&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Apache Kafka&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카는 링크드인(LinkedIn) 에서 링크드인 서비스 내에서 발생하고 있는 이슈들을 해결하기 위해서 만들었다고 한다.&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;파이프라인&amp;nbsp;확장의&amp;nbsp;어려움,&amp;nbsp;이기종&amp;nbsp;간의&amp;nbsp;호환성,&amp;nbsp;고성능&amp;nbsp;기반의&amp;nbsp;실시간&amp;nbsp;데이터&amp;nbsp;처리의&amp;nbsp;어려움&amp;nbsp;등의&amp;nbsp;문제를&amp;nbsp;해결&amp;nbsp;하기&amp;nbsp;위해&amp;nbsp;2010년&amp;nbsp;개발된&amp;nbsp;카프카는&amp;nbsp;일년&amp;nbsp;뒤인&amp;nbsp;2011년&amp;nbsp;아파치(Apache)&amp;nbsp;오픈소스로&amp;nbsp;세상에&amp;nbsp;처음&amp;nbsp;공개가&amp;nbsp;되었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;카프카를 쉽게 잘 설명하는 그림이 최범균님의 'kafka 조금 아는 척하기 1' 영상에서 나온 다음 그림이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7Knp7/btsgFvcD4YJ/sSnBNcxC5U5j9ZDqBTsPPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7Knp7/btsgFvcD4YJ/sSnBNcxC5U5j9ZDqBTsPPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7Knp7/btsgFvcD4YJ/sSnBNcxC5U5j9ZDqBTsPPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7Knp7%2FbtsgFvcD4YJ%2FsSnBNcxC5U5j9ZDqBTsPPK%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;1157&quot; height=&quot;586&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: &lt;a href=&quot;https://www.youtube.com/watch?v=0Ssx7jJJADI&amp;amp;ab_channel=%EC%B5%9C%EB%B2%94%EA%B7%A0&quot;&gt;https://www.youtube.com/watch?v=0Ssx7jJJADI&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 그림이 카프카 전체적인 구조를 잘 나타내는 그림이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;apache-kafka&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;주요 용어 정리&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;프로듀서&amp;nbsp;-&amp;nbsp;데이터를&amp;nbsp;브로커에&amp;nbsp;보내는&amp;nbsp;주체&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;브로커&amp;nbsp;-&amp;nbsp;프로듀서,&amp;nbsp;컨슈머&amp;nbsp;사이에서&amp;nbsp;데이터를&amp;nbsp;전달하는&amp;nbsp;서비스&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;컨슈머&amp;nbsp;-&amp;nbsp;브로커로&amp;nbsp;부터&amp;nbsp;데이터를&amp;nbsp;취득하는&amp;nbsp;주체&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;큐 - 브로커는 큐 모델을 사용하여 메시지를 관리한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;토픽&amp;nbsp;-&amp;nbsp;메시지를&amp;nbsp;관심사(토픽)에&amp;nbsp;따라&amp;nbsp;분리할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp; 프로듀서와&amp;nbsp;컨슈머는&amp;nbsp;특정&amp;nbsp;토픽의&amp;nbsp;메시지를&amp;nbsp;송수신할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;파티션&amp;nbsp;-&amp;nbsp;토픽의&amp;nbsp;분산&amp;nbsp;처리&amp;nbsp;지원&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;카프카&amp;nbsp;클라이언트&amp;nbsp;-&amp;nbsp;카프카&amp;nbsp;운영에&amp;nbsp;필요한&amp;nbsp;도구를&amp;nbsp;제공한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;카프카&amp;nbsp;클러스터&amp;nbsp;-&amp;nbsp;브로커,&amp;nbsp;주키퍼에&amp;nbsp;의해&amp;nbsp;구성된&amp;nbsp;클러스터&amp;nbsp;시스템&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;apache-kafka&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;카프카의 주요 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;&lt;b&gt;&lt;span&gt;높은 처리량과 낮은 지연시간&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;카프카를 선택하는 가장 큰 이유 일 것 입니다. 앞에 사례에서도 그렇고 카프카는 매우 높은 처리량과 낮은 지연시간(latency) 을 자랑한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;컨플루언트의 블로그에서 메세지 시스템 간의 성능을 비교한 자료&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sOWcw/btsgTi38f01/radzT8AEVLoTFybGhAfjNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sOWcw/btsgTi38f01/radzT8AEVLoTFybGhAfjNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sOWcw/btsgTi38f01/radzT8AEVLoTFybGhAfjNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsOWcw%2FbtsgTi38f01%2FradzT8AEVLoTFybGhAfjNk%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;2541&quot; height=&quot;1000&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #444444;&quot;&gt;[&lt;a style=&quot;color: #444444;&quot; href=&quot;https://www.confluent.io/ko-kr/blog/kafka-fastest-messaging-system/&quot;&gt;confluent.io/kafka-fastest-messaging-system&lt;/a&gt;]&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;위의 이미지에서 아파치 카프카, 북키퍼(BookKeeper) 기반의 스토리지를 이용한 펄사(Pulsar), 전통적인 메세지 큐 시스템인 래빗MQ(RabbitMQ), 총 세가지 메세징 시스템의 비교한 내용을 확인 할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;위의 벤치마크의 결과에서 확인 할 수 있듯이 처리량과 응답속도를 같이 비교하면 카프카가 다연 독보적으로 좋다고 할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #444444;&quot;&gt;높은 확장성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;카프카는 손쉬운 확장이 가능하고 잘 설계된 애플리케이션이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;링크드인이 비즈니스 적으로 급성장 하면서 애플리케이션 확장의 필요성을 느끼게 되었는데 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;확장하기 어려운 애플리케이션에서 어려움을 겪으면서 이러한 문제점을 개선하고자 카프카 초기부터 확장이 가능하도록 설계를 하게 되었다고 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #444444;&quot;&gt;고가용성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;카프카 초기버전에서는 무엇보다 메세지를 빠르게 처리하는 것이 목표였지만, 점차 시간이 지나면서 고가용성 측면도 중요하게 생각하게 되었다. 카프카는 2013년 에 클러스터 내 레플리케이션 기능을 추가하였다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;고가용성(High Availability) 라는 개념은 간단해 보이지만, 내부 레플리케이션에 대한 로직은 매우 어렵고 복잡하다고 한다. 이러한 복잡하지만 좋은 기능은 고가용성 기능을 카프카는 지원하고 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #444444;&quot;&gt;내구성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;프로듀서는 카프카로 메세지를 전송할 때, 프로듀서의 acks 라는옵션을 조정하여 메세지의 내구성을 강화할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;강력한 메세지의 내구성을 원한다면 옵션에서 acks=all 로 사용할 수 도 있다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;이렇게 프로듀서에 의해 카프카로 전송되는 모든 메세지는 안전한 저장소인 카프카 로컬 디스크에 저장되게 된다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;카프카의 경우에는 컨슈머가 메세지를 가져가더라도 메세지는 삭제되지 않고 지정한 설정 시간 또는 로그의 크기 만큼 로컬 디스크에 보관되므로 코드의 버그나 장애가 발생하더라도 과거의 메세지들을 불러와 재처리 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;장애 발생에 따른 재처리는 운영에서 매우 중요한 요소이므로 향후 코드로 다시 설명할 예정이다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #444444;&quot;&gt;개발 편의성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;카프카는 메세지를 전송하는 역할을 하는 프로듀서 와 메세지를 가져오는 역할을 하는 컨슈머가 완벽하게 분리 되어 동작하고 서로 영향을 주지도 받지도 않는다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;따라서 프로듀싱을 원하는 개발자는 프로듀싱만 보면 되고, 컨슈밍을 원하는 개발자는 컨슈머만 보면 된다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;또한 개발 편의성을 제공하기 위해 카프카에서는 카프카 커넥트(Kafka Connect) 와 스키마 레지스트리 를 제공 하고 있다.&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #444444;&quot;&gt;운영 및 관리 편의성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3d4e59; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #444444;&quot;&gt;카프카는 앞서 언급한 여러 장점으로 인해 중앙 메인 데이터 파이프라인 역할을 하게 되는데, 운영이나 관리의 편의성이 떨어진다면 그와 같은 주요 역할을 맡기가 부담스러울수도 있다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;중요한 역할을 하는 애플리케이션이라면 성능 확장을 위한 증설 작업이 쉽고 간단해야 하며, 최신 버전이 릴리즈되는 경우 무중단으로 버전 업그레이도 가능해야 하고 , 버전 업그레이드 작업 역시 단순 해야 한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444;&quot;&gt;이런 측면에서는 카프카는 다양한 기능과 3rd Party 를 통해 관리 편의성을 높일수 있는 부분이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 시간에는 실제 프로젝트 코드를 통해 카프카 동작하는 걸 알아보기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;출처:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://youngwonhan-family.tistory.com/entry/Kafka-%EA%B0%9C%EC%9A%94-%EC%A3%BC%EC%9A%94-%EC%9A%A9%EC%96%B4-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC&quot;&gt;https://youngwonhan-family.tistory.com/entry/Kafka-%EA%B0%9C%EC%9A%94-%EC%A3%BC%EC%9A%94-%EC%9A%A9%EC%96%B4-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: &lt;a href=&quot;https://hoing.io/archives/4907&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hoing.io/archives/4907&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kafka</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/83</guid>
      <comments>https://wangkisa.tistory.com/83#entry83comment</comments>
      <pubDate>Sun, 21 May 2023 21:21:42 +0900</pubDate>
    </item>
    <item>
      <title>@Transactional 에 대한 고찰</title>
      <link>https://wangkisa.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;스프링에서 &lt;b&gt;트랜잭션&lt;/b&gt; 처리를 지원하는데 어노테이션 방식으로 @Transactional 을 선언하여 사용하는데,&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X0Kf9/btr5QnwoSUp/WeDTbKFpEw3NH9AoVpkXX0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X0Kf9/btr5QnwoSUp/WeDTbKFpEw3NH9AoVpkXX0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X0Kf9/btr5QnwoSUp/WeDTbKFpEw3NH9AoVpkXX0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX0Kf9%2Fbtr5QnwoSUp%2FWeDTbKFpEw3NH9AoVpkXX0%2Fimg.webp&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;710&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@Transactional 이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드나 클래스에 @Transactional 추가하게 되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동적으로 트랜잭션을 시작하고, 정상 여부에 따라 Commit 또는 Rollback 하게 된다.&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;위에서 얘기한 @Transactional 추가 위치에 따른 처리 차이에 대한 내용이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@Transactional 사용시 주의점&lt;/b&gt;&lt;/h3&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;/b&gt;님의 '&lt;b&gt;프로그래밍 초식 : 초심자가 저지르기 쉬운 DB 코딩 실수 3가지&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;/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;pre id=&quot;code_1679837291983&quot; class=&quot;cpp&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Transactional
    public void registerMembers(List&amp;lt;Req&amp;gt; reqs) {
        reqs.forEach(req -&amp;gt; {
           try {
               registerService.register(req);
               logDao.insert(createSuccessLog(req, ex));
           } catch (Exception ex) {
               logDao.insert(createFailLog(req, ex));
           }
        });
    }&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;2번&lt;/p&gt;
&lt;pre id=&quot;code_1679837298079&quot; class=&quot;aspectj&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Transactional
    public void register(Req req) {
        anyRepository.save(toData(req));
        if (check()) {
            throw new RuntimeException(&quot;!&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;위 '1번'처럼 @Transactional 안에 try catch를 하게 되면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도한 대로 동작이 안되게 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;loop를 돌면서 register 메서드를 호출하게 되는데&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exception이 발생하지 않으면 아무 문제가 없는 코드이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;register 에서 Exception이 발생하게 되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;throw new RuntimeException(&quot;!&quot;); 을 호출하면서 트랜잭션이 '&lt;b&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;이 상태가 되면 나머지 register와 insert는 롤백 상태에서 진행하게 되기 때문에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&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;그 후 forEach 문이 끝날때 커밋을 시도하게 되는데 지금 트랜잭션 상태가 롤백 상태이기 때문에&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;여기서 의도는 forEach 를 하면서 정상적인 수행과 실패가 난 사항들을 디비에 &lt;span&gt;따로따로&lt;/span&gt;&amp;nbsp;기록하는 것이 의도였는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;@Transactional&lt;span&gt;&amp;nbsp;이 있다보니 의도대로 동작하지 않는 것을 알 수 있다.&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;이 케이스는 중첩된 @Transactional과 try-catch에 대한 내용인데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드에 @Transactional 설정하고, 안에서 try-catch로 Exception을 처리하고 전파하지 않으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 내용이 확인 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 중첩된 @Transactional이 있는지&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 의도하지 않은 롤백 가능성이 없는지&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 의도한 롤백이 안될 가능성은 없는지&amp;nbsp;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@Transactional 분리 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TBD&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: &lt;a href=&quot;https://morioh.com/p/a97ace580213&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://morioh.com/p/a97ace580213&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: &lt;a href=&quot;https://goddaehee.tistory.com/167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goddaehee.tistory.com/167&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: &lt;a href=&quot;https://www.youtube.com/watch?v=n85UzIReFjY&amp;amp;t=313s&amp;amp;ab_channel=%EC%B5%9C%EB%B2%94%EA%B7%A0&quot;&gt;https://www.youtube.com/watch?v=n85UzIReFjY&amp;amp;t=313s&amp;amp;ab_channel=%EC%B5%9C%EB%B2%94%EA%B7%A0&lt;/a&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>spring boot</category>
      <category>Transactional</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/81</guid>
      <comments>https://wangkisa.tistory.com/81#entry81comment</comments>
      <pubDate>Sun, 26 Mar 2023 21:43:00 +0900</pubDate>
    </item>
    <item>
      <title>트랜잭션 격리 수준(Isolation level)</title>
      <link>https://wangkisa.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;트랜잭션의&amp;nbsp;격리&amp;nbsp;수준(Isolation&amp;nbsp;level)&lt;/b&gt;이란&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 여러 트랜잭션이 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1679&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sp6jD/btr0HSIvEkv/Z9Vj1nxQ3CpyTcjB2GWAaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sp6jD/btr0HSIvEkv/Z9Vj1nxQ3CpyTcjB2GWAaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sp6jD/btr0HSIvEkv/Z9Vj1nxQ3CpyTcjB2GWAaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsp6jD%2Fbtr0HSIvEkv%2FZ9Vj1nxQ3CpyTcjB2GWAaK%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;553&quot; height=&quot;725&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1679&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;트랜잭션의 격리 수준&lt;/b&gt; 은 다음과 같은 4가지가 있다.&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;b&gt;READ UNCOMMITTED(커밋 전 읽기)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;b&gt;READ COMMITTED(커밋 후 읽기)&lt;/b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;REPEATABLE&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;READ(반복 읽기)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;SERIALIZABLE(직렬화 가능)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&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;1. &lt;b&gt;READ UNCOMMITTED&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;각 트랜잭션에서의 변경 내용이 commit 이나 rollback 여부에 상관없이 다른 트랜잭션에서 보여지게 되는 수준으로써&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거의 쓰지 않는 격리 수준이라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2.&lt;span&gt; &lt;b&gt;&lt;b&gt;READ COMMITTED&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;PostgreSQL 에서 &lt;b&gt;기본&lt;/b&gt;으로 사용되고 있는 &lt;b&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;
&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;1241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjzqFf/btr0H8dy6z4/UqdsVr7zhJbtuZlLeFOEk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjzqFf/btr0H8dy6z4/UqdsVr7zhJbtuZlLeFOEk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjzqFf/btr0H8dy6z4/UqdsVr7zhJbtuZlLeFOEk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjzqFf%2Fbtr0H8dy6z4%2FUqdsVr7zhJbtuZlLeFOEk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;649&quot; height=&quot;629&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1241&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 이미지만 보더라도 이 격리 수준이 어떻게 동작할 수 있는 내용인데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 A 에서 트랜잭션을 먼저 시작하고,&amp;nbsp;&lt;span&gt;사용자 B가 도중에 새로운 트랜잭션을 진행했을 때 사용자 B가 어떻게 조회되는지 알 수 있는 그림이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사용자 A가 emp_no = 50000인 사원의 first_name을 &quot;JuBal&quot; 에서 &quot;Toto&quot;로 수정을 진행하는데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 되기 전에 UNDO 영역에 emp_no = 50000인 사원 first_name을 &quot;JuBal&quot; 값으로 백업을 시킨 후,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 A가 commit 되기 전에 사용자 B가 emp_no = 50000 을 조회하게 되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경한 &quot;Toto&quot; 값이 아닌 위에서 언급한 &lt;b&gt;UNDO&lt;/b&gt; 영역에 있는 &quot;JuBal&quot; 값으로 조회하는 것을 볼 수 있다.&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;UNDO 영역의 데이터는 크게 두가지 용도로 사용된다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.&lt;span&gt; &lt;b&gt;&lt;b&gt;&lt;b&gt;REPEATABLE&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;READ&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 조회 시 항상 동일한 데이터 응답을 보장하는 격리 수준&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;1270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uUWja/btr0HtWzIZm/CKg8dpHC6LyDktGfnU1811/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uUWja/btr0HtWzIZm/CKg8dpHC6LyDktGfnU1811/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uUWja/btr0HtWzIZm/CKg8dpHC6LyDktGfnU1811/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuUWja%2Fbtr0HtWzIZm%2FCKg8dpHC6LyDktGfnU1811%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;580&quot; height=&quot;635&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;1270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 B 가 먼저 emp_no = 50000인 사원을 조회해 보면 &quot;JuBal&quot;값으로 조회가 되는 것으로 확인하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 사용자 A가 새로운 트랜잭션으로 &quot;Toto&quot;로 값을 변경 후, commit 을 하더라도&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;사용자 B는 새로 변경한 트랜잭션보다 작은 트랜잭션에서 수행을 하면 처음과 같은 값인 &quot;JuBal&quot;값으로 조회가 되는&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;b&gt;&lt;span&gt;&lt;b&gt;&lt;b&gt;READ COMMITTED &lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;수준과 마찬가지로 UNDO 영역에 데이터 백업하는 것은 동일하지만&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;REPEATABLE&lt;b&gt;READ &lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;는 MVCC 라고 하는 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여러 버전별로 제공하여 Lock을 사용하지 않는 일관된 읽기를 제공하는 것이라고 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lock을 사용하지 않는 사용 이유는 베타적 락으로 인해 다른 트랜잭션이 해당 데이터를 read하기 위해 기다리는 상황이 발생하고, 이런 트랜잭션들이 계속 된다면 성능 저하로 이어지기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그런데, 문제는 트랜잭션을 시작한 후 오랜 시간동안 트랜잭션을 종료를 하지 않으면 UNDO영역이 무한정 커질 수 있고, 이렇게 백업된 레코드가 많아질수록 데이터베이스의 처리 성능이 떨어질 수 있다.&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;4.&lt;span&gt; &lt;b&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;SERIALIZABLE&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 절대 접근할 수 없는 가장 엄격한 격리 수준&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매우 엄격한 수준으로 해당 행에 대해 격리시키고 이후 트랜잭션이 해당 행에 대해 발생한다면 기다려야 되는 방식이라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시성 이슈는 없지만 성능이 가장 떨어져 실무에서 쓰기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 출처: 데이터 중심 애플리케이션 설계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 출처: &lt;a href=&quot;https://zzang9ha.tistory.com/381&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://zzang9ha.tistory.com/381&lt;/a&gt;&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/80</guid>
      <comments>https://wangkisa.tistory.com/80#entry80comment</comments>
      <pubDate>Sun, 26 Feb 2023 22:31:40 +0900</pubDate>
    </item>
    <item>
      <title>JPA Hibernate Proxy</title>
      <link>https://wangkisa.tistory.com/78</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘도 열심히 사이드 프로젝트를 진행하던 중&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동행모집 참가서 에서 불러온 Gathering 엔티티 안에 있는 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;GatheringMember 엔티티 데이터가 분명히 있는데도&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;불구하고 호출해서 확인해보면 의도와는 다르게 진행해서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;디버그 모드로 해당 데이터들을 확인해보니&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;다음 스샷처럼 되어 있는걸 알 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5c1ke/btrL34ID4tb/MkQkK6OG5CEyknmBE7uSFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5c1ke/btrL34ID4tb/MkQkK6OG5CEyknmBE7uSFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5c1ke/btrL34ID4tb/MkQkK6OG5CEyknmBE7uSFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5c1ke%2FbtrL34ID4tb%2FMkQkK6OG5CEyknmBE7uSFk%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;870&quot; height=&quot;246&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;246&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;815&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJClpy/btrL23csHok/k6hnhpNvBk69dbODJBZKGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJClpy/btrL23csHok/k6hnhpNvBk69dbODJBZKGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJClpy/btrL23csHok/k6hnhpNvBk69dbODJBZKGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJClpy%2FbtrL23csHok%2Fk6hnhpNvBk69dbODJBZKGk%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;815&quot; height=&quot;249&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gathering 과 requestUser 모두 데이터는 있을텐데&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 데이터들이 모두 다 null 로 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, 해당 변수의 처음 정보를 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Gathering$HibernateProxy~~~~&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User$HibernateProxy~~~~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되어 있는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련된 내용을 설명하기 전에 해당 엔티티에서 두 부분이 어떻게 쓰고 있는지 먼저 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Entity
public class GatheringRequest extends DefaultEntity {

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = &quot;user_id&quot;,
        referencedColumnName = &quot;user_id&quot;,
        nullable = false)
    private User requestUser;

    @JoinColumn(name = &quot;gathering_id&quot;,
            referencedColumnName = &quot;gathering_id&quot;,
            nullable = false)
    @ManyToOne(fetch = LAZY)
    private Gathering gathering;

}&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;두 군데 다 연관 관계가 @ManyToOne(fetch = LAZY) 로 되어있다.&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&gt;fetch&lt;span&gt; 타입이 LAZY로 되어 있으니,&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EAGER와는 다르게 지연으로 가져와서 바로 사용할 수가 없다.&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;빠르게 대안부터 간다!&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;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;final User requestUser = gatheringRequest.getRequestUser();
final Gathering gathering = gatheringRequest.getGathering();&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;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;final User requestUser = gatheringRequest.getRequestUser();
final User unproxyRequestUser = Hibernate.unproxy(requestUser, User.class);
Gathering gathering = gatheringRequest.getGathering();
final Gathering unproxyGathering = Hibernate.unproxy(gathering, Gathering.class);&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 결과를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Q06G/btrLYlEXE0S/M5CiQBKGAgrmzBmvkKpS9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Q06G/btrLYlEXE0S/M5CiQBKGAgrmzBmvkKpS9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Q06G/btrLYlEXE0S/M5CiQBKGAgrmzBmvkKpS9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Q06G%2FbtrLYlEXE0S%2FM5CiQBKGAgrmzBmvkKpS9k%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;1210&quot; height=&quot;460&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t7uNc/btrLXN9ttYg/iRtuo2pCYuAZjlmO4zyHlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t7uNc/btrLXN9ttYg/iRtuo2pCYuAZjlmO4zyHlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t7uNc/btrLXN9ttYg/iRtuo2pCYuAZjlmO4zyHlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft7uNc%2FbtrLXN9ttYg%2FiRtuo2pCYuAZjlmO4zyHlk%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;999&quot; height=&quot;423&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gathering 과 requestUser 는 기존과 같이 null 로만 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unproxyRequestUser 와 unproxyG&lt;span&gt;athering&lt;span&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&gt;&lt;span&gt;처음 정보 역시 HibernateProxy ~~ 부분이 없이 바로 엔티티와 아이디로 확인이 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;프록시&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;위에서 얘기한 GatheringRequest 엔티티를 조회할때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestUser, gathering 이 사용하는 경우도 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스 로직에서 사용하지 않는 경우가 있는데, 사용을 하지 않는데 다 조회하게 되면 &lt;b&gt;낭비&lt;/b&gt;가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA에서는 이 &lt;b&gt;낭비&lt;/b&gt;를 하지 않기 위해 지연로딩(lazy loading)과 프록시 라는 개념으로 해결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWLIWt/btrMlVrVkRQ/782QF91BeRIQVNBNeFkNj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWLIWt/btrMlVrVkRQ/782QF91BeRIQVNBNeFkNj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWLIWt/btrMlVrVkRQ/782QF91BeRIQVNBNeFkNj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdWLIWt%2FbtrMlVrVkRQ%2F782QF91BeRIQVNBNeFkNj0%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;707&quot; height=&quot;434&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;프록시란,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt;실제 엔티티 객체 대신에 사용되는 객체&lt;/b&gt; 로서 위 사진에서 보는 것처럼 실제 엔티티 클래스와 상속 관계 및 위임 관계에 있다. 그래서 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&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&gt;&lt;span&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&gt;&lt;span&gt;위에서 한번 나왔던&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t7uNc/btrLXN9ttYg/iRtuo2pCYuAZjlmO4zyHlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t7uNc/btrLXN9ttYg/iRtuo2pCYuAZjlmO4zyHlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t7uNc/btrLXN9ttYg/iRtuo2pCYuAZjlmO4zyHlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft7uNc%2FbtrLXN9ttYg%2FiRtuo2pCYuAZjlmO4zyHlk%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;999&quot; height=&quot;423&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용을 보더라도&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 아래의 값은 다르더라도 생긴 형태는 동일한 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMg4Ol/btrMqsWLX9b/OKHtnmybvuq9wJzpvaOBo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMg4Ol/btrMqsWLX9b/OKHtnmybvuq9wJzpvaOBo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMg4Ol/btrMqsWLX9b/OKHtnmybvuq9wJzpvaOBo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMg4Ol%2FbtrMqsWLX9b%2FOKHtnmybvuq9wJzpvaOBo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;204&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-19 00.38.00.png&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lUlgv/btrMlUs1VZ3/5Y5kXfbjikNVzbwj36kAE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lUlgv/btrMlUs1VZ3/5Y5kXfbjikNVzbwj36kAE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lUlgv/btrMlUs1VZ3/5Y5kXfbjikNVzbwj36kAE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlUlgv%2FbtrMlUs1VZ3%2F5Y5kXfbjikNVzbwj36kAE1%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;1083&quot; height=&quot;559&quot; data-filename=&quot;스크린샷 2022-09-19 00.38.00.png&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프록시 객체에서는 원본이 어떤것인지 알 수 있는 target 엔티티를 보관해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 target 엔티티를 통해 프록시 객체는 실제 객체의 메소드를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;프록시 &lt;b&gt;&lt;span&gt;초기화&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 스샷에 덧붙여 프록시 초기화란,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프록시 객체가 참조하는 실제 엔티티가 persistence context에 생성되어 있지 않을 때,&amp;nbsp;&lt;/b&gt;&lt;b&gt;persistence context&lt;/b&gt;&lt;b&gt;에 실제 엔티티 생성을 요청하고 생성된 실제 엔티티를 프록시 객체의 참조 변수에 할당하는 과정을 말한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkOH45/btrNQqEFDCa/TpMpc08awVEuOikIGiw9kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkOH45/btrNQqEFDCa/TpMpc08awVEuOikIGiw9kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkOH45/btrNQqEFDCa/TpMpc08awVEuOikIGiw9kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkOH45%2FbtrNQqEFDCa%2FTpMpc08awVEuOikIGiw9kk%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;597&quot; height=&quot;366&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타 블로그에서 가져온 이미지 인데, 이 이미지 순서에 따르면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. book 엔티티의 title를 조회하는 함수 호출하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;2.&lt;span&gt; book 엔티티의 데이터를 조회하지만 데이터가 '점선표시 그림' 처럼 비어있는 것을 확인&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;3. 데이터가 없으니 디비에 초기화 요청을 보냄&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;4. 디비에서 가져온&amp;nbsp; book 엔티티 데이터를 persistence context 에 1차 캐시로 저장&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;5. 이후, 저장된 엔티티에서 book 의 title 정보를 반환&amp;nbsp;&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&gt;&lt;span&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;a href=&quot;https://victorydntmd.tistory.com/210&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://victorydntmd.tistory.com/210&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;출처:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://ict-nroo.tistory.com/131&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ict-nroo.tistory.com/131&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;출처:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://kha0213.github.io/jpa/jpa-proxy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kha0213.github.io/jpa/jpa-proxy/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Spring</category>
      <category>Hibernate Proxy</category>
      <category>JPA</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/78</guid>
      <comments>https://wangkisa.tistory.com/78#entry78comment</comments>
      <pubDate>Tue, 13 Sep 2022 17:39:16 +0900</pubDate>
    </item>
    <item>
      <title>2022 인프콘 컨퍼런스 참여 후기</title>
      <link>https://wangkisa.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코로나 이후 처음으로 오프라인에서 진행하는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022 인프콘 컨퍼런스에 운좋게도 추첨되서 행사에 참여하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;(인프런에서 호구?처럼 강좌 구매하고 수강도 안해서 뽑아준건지도 모르겠다)&lt;/s&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;행사 url:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://infcon.day/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://infcon.day/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HrMhY/btrLa5a4IFw/NsjLZ3AzXtQwmLZVLITSIK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HrMhY/btrLa5a4IFw/NsjLZ3AzXtQwmLZVLITSIK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HrMhY/btrLa5a4IFw/NsjLZ3AzXtQwmLZVLITSIK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHrMhY%2FbtrLa5a4IFw%2FNsjLZ3AzXtQwmLZVLITSIK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_21.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l5F0c/btrLaBnN7QM/MYxKcMD2gGHkmOnyhttNyK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l5F0c/btrLaBnN7QM/MYxKcMD2gGHkmOnyhttNyK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l5F0c/btrLaBnN7QM/MYxKcMD2gGHkmOnyhttNyK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl5F0c%2FbtrLaBnN7QM%2FMYxKcMD2gGHkmOnyhttNyK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_21.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.&amp;nbsp; 사이드 프로젝트 만세!&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_18.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u56nO/btrLb4Q3zQ9/jdsMbFiLYpwnblxIgzqblk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u56nO/btrLb4Q3zQ9/jdsMbFiLYpwnblxIgzqblk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u56nO/btrLb4Q3zQ9/jdsMbFiLYpwnblxIgzqblk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu56nO%2FbtrLb4Q3zQ9%2FjdsMbFiLYpwnblxIgzqblk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_18.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼성전자에서 인터넷 브라우저 쪽 개발을 담당하고 있는 발표자는&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 시작은 기존 크로미움에서 아이콘만 바꿔서 출시해보자는 목표&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지나친 장인정신은 사이드 프로젝트에서 실패의 원인이라고 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_16.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buoto1/btrLalsCxRN/51m3HarIz6jfktLureuIek/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buoto1/btrLalsCxRN/51m3HarIz6jfktLureuIek/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buoto1/btrLalsCxRN/51m3HarIz6jfktLureuIek/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbuoto1%2FbtrLalsCxRN%2F51m3HarIz6jfktLureuIek%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_16.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발보다 중요하다고 하는 마케팅!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;광고를 하지 않으면 일일 평균 5명 다운로드...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 되면 목표하는 다운로드 수치까지는 200년 걸림ㄷㄷ&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_15.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4YtXt/btrLbMwd95T/PlJHrPLq0kKdwmfxFMW7M0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4YtXt/btrLbMwd95T/PlJHrPLq0kKdwmfxFMW7M0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4YtXt/btrLbMwd95T/PlJHrPLq0kKdwmfxFMW7M0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4YtXt%2FbtrLbMwd95T%2FPlJHrPLq0kKdwmfxFMW7M0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_15.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트 진행이 잘 안되는 이유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본업을 하면서 시간 소요떄문에 사이드 프로젝트 할 시간이 없음,&lt;/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;size16&quot;&gt;발표자 분이 사이드 프로젝트를 좀 더 잘하기 위해 고안한 여러가지 방법이 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_14.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDMo03/btrLa3E2P6B/xdqIGuA9CHqBM4RLVPjDn1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDMo03/btrLa3E2P6B/xdqIGuA9CHqBM4RLVPjDn1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDMo03/btrLa3E2P6B/xdqIGuA9CHqBM4RLVPjDn1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDMo03%2FbtrLa3E2P6B%2FxdqIGuA9CHqBM4RLVPjDn1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_14.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) 벌금내기&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_13.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TNU5i/btrLgav56Nj/w7F8OyzgJaBr8CLLP9EFP1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TNU5i/btrLgav56Nj/w7F8OyzgJaBr8CLLP9EFP1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TNU5i/btrLgav56Nj/w7F8OyzgJaBr8CLLP9EFP1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTNU5i%2FbtrLgav56Nj%2Fw7F8OyzgJaBr8CLLP9EFP1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_13.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;스프린트 기간 중 가장 성과가 좋았던 사람에게 선물을 줬던 방식인데,&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_12.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxGFDB/btrLa4jAuFi/WHm5eAZiSyYe40TsESVglk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxGFDB/btrLa4jAuFi/WHm5eAZiSyYe40TsESVglk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxGFDB/btrLa4jAuFi/WHm5eAZiSyYe40TsESVglk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxGFDB%2FbtrLa4jAuFi%2FWHm5eAZiSyYe40TsESVglk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_12.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;시간될 때 모여서 개발하는 방식인데&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_11.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EVQ0U/btrLedUgQX0/fRYsS58gU5yuG87Vfug2tk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EVQ0U/btrLedUgQX0/fRYsS58gU5yuG87Vfug2tk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EVQ0U/btrLedUgQX0/fRYsS58gU5yuG87Vfug2tk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEVQ0U%2FbtrLedUgQX0%2FfRYsS58gU5yuG87Vfug2tk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_11.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(4) 가스라이팅(?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약간 협박? 비슷한 가스라이팅을 시도해 봤지만&lt;br /&gt;좋은 방법은 아니었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_10.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUjOXw/btrLcgRsai0/gv0XE50V4N2h8BKAoF5ZWk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUjOXw/btrLcgRsai0/gv0XE50V4N2h8BKAoF5ZWk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUjOXw/btrLcgRsai0/gv0XE50V4N2h8BKAoF5ZWk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUjOXw%2FbtrLcgRsai0%2Fgv0XE50V4N2h8BKAoF5ZWk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_10.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(5) 감정에 호소하기&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_09.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo99qt/btrLguU7CkQ/LGA6mZAJNIrbFWpElMqkO1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo99qt/btrLguU7CkQ/LGA6mZAJNIrbFWpElMqkO1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo99qt/btrLguU7CkQ/LGA6mZAJNIrbFWpElMqkO1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo99qt%2FbtrLguU7CkQ%2FLGA6mZAJNIrbFWpElMqkO1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_09.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발표자는 사이드 프로젝트를 참여하는 사람이라면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신처럼 모두가 주도적일것이라고 처음에 생각했다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 당연하게도 참여하는 사람들이 모두 자신과 같지 않고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두가 주도적으로 할 수 없다는 것을 알아야 한다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, 반복해서 강조한 내용이&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;괜찮은 방법이라고 생각되면 시도하면 좋겠다고 생각하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트가 지금 금전적인 도움은 안되지만 이직이나 내 포트폴리오를 위해&amp;nbsp;&lt;br /&gt;도움이&amp;nbsp;될&amp;nbsp;것이라는&amp;nbsp;생각도&amp;nbsp;들고&amp;nbsp;다른&amp;nbsp;사람&amp;nbsp;잘&amp;nbsp;되게&amp;nbsp;해주는것이&amp;nbsp;나에게도&amp;nbsp;플러스가&amp;nbsp;될&amp;nbsp;수도&amp;nbsp;있을것&amp;nbsp;같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20220902_111615327_25.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnRc7x/btrLad8GHKh/13NDrYYQzkaNEmD86WZL9K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnRc7x/btrLad8GHKh/13NDrYYQzkaNEmD86WZL9K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnRc7x/btrLad8GHKh/13NDrYYQzkaNEmD86WZL9K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnRc7x%2FbtrLad8GHKh%2F13NDrYYQzkaNEmD86WZL9K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1080&quot; data-filename=&quot;KakaoTalk_20220902_111615327_25.jpg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>컨퍼런스</category>
      <category>2022인프콘</category>
      <category>사이드 프로젝트</category>
      <category>컨퍼런스</category>
      <category>후기</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/77</guid>
      <comments>https://wangkisa.tistory.com/77#entry77comment</comments>
      <pubDate>Fri, 2 Sep 2022 15:34:06 +0900</pubDate>
    </item>
    <item>
      <title>sql 특수 상태 검색(조건 상태 검색)에 관한 글</title>
      <link>https://wangkisa.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;목록에서 상태를 조건 검색해야 되는 상황.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 단순히 status 칼럼 값만 검색을 하면 단순히 in 절로 검색을 하면 해결이 된다.&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;단순히 status만 검색을 하는 것이 아니라 다른 테이블의 상태 값과,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 상태인 경우 상태값과 날짜 체크를 같이 해야 되는 경우가 있어&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 해당 사항을 진행하려고 할때 고민을 하게 되었다.&lt;/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;(a.status = 'A' and b.order_status = 'B')&amp;nbsp; -- 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;or (a.status = 'A' and b.order_status = 'C') -- 2&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 번과 2번이 목록에서는 필터하려고 하는 상태조건에 따라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번을 할수도 있고, 2번만 검색하려고 할 수도 있다.&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, 2 다 실행하는 경우는 문제가 없지만&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;2만 검색하려고 하면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;or&lt;/b&gt; (a.status = 'A' and b.order_status = 'C') &amp;lt;&amp;lt; 여기서 보이는 or 는 없어야&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 or 도 동적으로 하려고 하는것도 같이 구현을 하는것이 문제가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 고민해본 결과,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;in 절도 사용하고, 재활용성도 높여서 사용하기 위해&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;select 절에서 case 문으로 (해당 정확한 쿼리 문은 생략)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;((a.status = 'A' and b.order_status = 'B') then 'AA'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(a.status = 'A' and b.order_status = 'C') then 'AC'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;) as displayStatus&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;로 만들어서 다른 상황에서도 displayStatus 를 사용할 수 있도록 하고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급했던 문제를 해결한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;displayStatus in ('AA', 'AC') 나 displayStatus in ('AC')&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 케이스 등에서도 해결할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고 보면 간단하고 평소에 select에서 case문을 많이 사용하였지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 응용해서 사용하는것이 중요하는 생각이 들기도 했다.&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <category>SQL</category>
      <category>여러 테이블 조건 검색</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/75</guid>
      <comments>https://wangkisa.tistory.com/75#entry75comment</comments>
      <pubDate>Sun, 7 Aug 2022 18:22:02 +0900</pubDate>
    </item>
    <item>
      <title>Spring API 공통 response 포맷 개발하기</title>
      <link>https://wangkisa.tistory.com/74</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기존 프로젝트 API 에서 response 되는 데이터를 살펴보니,&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;response 가 내려가긴 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공인 경우와 실패의 경우가 각각 다른 포맷의 response 가 내려가고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 데이터를 주긴 하지만 프론트 입장에서는 이렇게 개발하기 난감할 것이라는 생각이 들기도 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 &lt;b&gt;사용자 정의 Exception&lt;/b&gt; 처리도 없어, 공통&amp;nbsp;response&amp;nbsp;포맷을 개발하는 것이 필요하다고 생각하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 블로그 및 글들을 찾아본 뒤 다음 응답 클래스를 만들어보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657442119219&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
public class ApiResponse&amp;lt;T&amp;gt; {

    private final int code;
    private final String message;
    private final List&amp;lt;String&amp;gt; errorDetails;
    private final T data;
    private final String responseTime;

    private ApiResponse(int code, String message, List&amp;lt;String&amp;gt; errorDetails, T data) {
        this.code = code;
        this.message = message;
        this.errorDetails = errorDetails;
        this.data = data;
        this.responseTime = LocalDateTimeUtil.getLocalDateTimeNowString(&quot;yyyy-MM-dd hh:mm:ss&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;size18&quot;&gt;&lt;b&gt;1. 응답 클래스 설명&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스에 &lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #445588;&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&amp;gt; 를 추가해서 Response마다 각기 다른 타입을 리턴해서 사용해야 되므로 해당 조건에 맞는 사용이 가능한 제네릭을 사용할 수 있도록 하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;여기서 제네릭(Generic) 은 클래스 / 인터페이스 / 메서드 등의 타입을 파라미터로 사용할 수 있게 해주는 역할을 한다고 한다.&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;b&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;private final int code;&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;background-color: #f8f8f8;&quot;&gt;응답 코드를 나타내는 code&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전엔 String 으로 성공인 경우 &quot;0000&quot; 이라든지 문자열 값으로 많이 하였는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기선&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;HttpStatus.OK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;처럼 표준 HttpStatus 코드를 사용하기 위해 int 로 설정하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;HttpStatus.OK 의 값은 200 이므로&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;프론트에서는 200 값을 성공으로 간주하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;예를 들어) request 파라미터 오류의 경우는 HttpStatus.BAD_REQUEST,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;인증 오류의 경우는 HttpStatus.UNAUTHORIZED 등으로 사용이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;이 부분은 문자열이 좋은지, 해당 &lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;HttpStatus 코드값이 좋은지는 알 수 없다.&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;background-color: #f8f8f8;&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&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;b&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;private final String message;&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;background-color: #f8f8f8;&quot;&gt;사용자 정의 오류 메시지 혹은 런타임 오류 등에서 전달된 핵심 메시지를 나타낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&lt;br /&gt;&lt;b&gt;private final List&amp;lt;String&amp;gt; errorDetails;&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;background-color: #f8f8f8;&quot;&gt;위 message 에서 표현하기 힘든 자세한 에러 내용표시들을 나타낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;예) e.getMessage(), e.printStackTrace() 등등 을 나타내고 싶은 경우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&lt;br /&gt;&lt;b&gt;private final T data;&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;background-color: #f8f8f8;&quot;&gt;실질적인 본문에 해당하는 데이터&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;각각의 응답 dto에 따라 내용이 달라진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&lt;br /&gt;&lt;b&gt;private final String responseTime;&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;background-color: #f8f8f8;&quot;&gt;응답 시간&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 응답 클래스 사용&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;/b&gt;와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정, 삭제 등 &lt;b&gt;데이터가 없는 경우&lt;/b&gt; 가 있다고 생각하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;그 외 에러 인 경우&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657443646806&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; public static &amp;lt;T&amp;gt; ApiResponse&amp;lt;T&amp;gt; success(T data) {
    return new ApiResponse&amp;lt;&amp;gt;(StatusCode.OK_CODE, null, null, data);
}

public static ApiResponse&amp;lt;?&amp;gt; successWithNoData() {
    return new ApiResponse&amp;lt;&amp;gt;(StatusCode.OK_CODE, null, null, null);
}&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;background-color: #f8f8f8;&quot;&gt;(1) 데이터가 있는 경우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;success 함수에서 응답객체와 입력받는 객체를 T로 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&amp;lt;T&amp;gt; ApiResponse&amp;lt;T&amp;gt; 이렇게 되도록 하였고,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;T data 로 각각 다른 포맷도 받아서 처리가 되도록 하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;ApiResponse.success(응답 dto) 이런식으로 사용하도록 하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;(2) 데이터가 없는 경우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;successWithNoData 함수에서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;따로 받는 객체가 없으므로 &lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;ApiResponse&amp;lt;?&amp;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;background-color: #f8f8f8;&quot;&gt;ApiResponse.&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;successWithNoData() 라고만 호출하면&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;data는 null 로만 설정하도록 하였다.&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;background-color: #f8f8f8;&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;(3) 에러의 경우&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657444036243&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static ApiResponse&amp;lt;?&amp;gt; error(int code, String message, List&amp;lt;String&amp;gt; errorDetails) {

    return new ApiResponse&amp;lt;&amp;gt;(code, message, errorDetails, null);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;에러 종류에 따른 다른 code&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;등 위에서 설명했으므로 생략하고&amp;nbsp;&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;background-color: #f8f8f8;&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;ApiResponse.error(HttpStatus.BAD_REQUEST.value(),&amp;nbsp;e.getMessage(),&amp;nbsp;Arrays.asList(request.getRequestURI()))&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;background-color: #f8f8f8;&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;처럼 사용이 가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 커스텀 에러&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657444482562&quot; class=&quot;scala&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
public class CustomException extends RuntimeException {

    public final int code;
    public final String message;
    public final List&amp;lt;String&amp;gt; errorDetails;

    public CustomException(int code, String message, List&amp;lt;String&amp;gt; errorDetails) {
        this.code = code;
        this.message = message;
        this.errorDetails = errorDetails;
    }
}&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;커스텀 에러는 RuntimeException 을 상속해서 사용하도록 해서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;throw new 로 사용할 수 있게끔 하였다.&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;pre id=&quot;code_1657444607696&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RestControllerAdvice
public class ApiExceptionHandler {

    @ExceptionHandler(CustomException.class)
    @ResponseBody
    public ApiResponse&amp;lt;?&amp;gt; handleCustomException(CustomException ce, WebRequest request) {
		log.error(&quot;call handleCustomException()&quot;);
        return ApiResponse.error(ce.getCode(), ce.getMessage(), ce.getErrorDetails());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(RuntimeException.class)
    public ApiResponse&amp;lt;?&amp;gt; handlerRuntimeException(RuntimeException e, HttpServletRequest request) {
		log.error(&quot;call handlerRuntimeException()&quot;);
        return ApiResponse.error(HttpStatus.BAD_REQUEST.value(), e.getMessage(), Arrays.asList(request.getRequestURI()));
    }&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;pre class=&quot;haxe&quot;&gt;&lt;code&gt;throw new CustomException(StatusCode.ERROR_WITHDRAWAL_USER_CODE,
                          StatusCode.ERROR_WITHDRAWAL_USER_MSG, null);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;호출 했을 때 'handleCustomException' 를 타서 동작하는 것을 확인 할 수 있었다.&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;4. 위에서 설명한 호출 결과들 &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;background-color: #f8f8f8;&quot;&gt;(1) 응답 있는 성공의 경우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657443410979&quot; class=&quot;json&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;code&quot;: 200,
    &quot;message&quot;: null,
    &quot;errorDetails&quot;: null,
    &quot;data&quot;: {
        &quot;id&quot;: 21,
        &quot;email&quot;: &quot;test@test.com5&quot;,
        &quot;accessToken&quot;: &quot;eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTc0NTA1MjEsImVtYWlsIjoidGVzdEB0ZXN0LmNvbTUifQ.dvOVM5OyhyUj6ZXKjHmv-JAGCKHEI10w0K-OrQ173JA&quot;,
        &quot;refreshToken&quot;: &quot;eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTc2MTYxMjF9.PZ1pqRCMgDtgC9EwW-e-f_7qt2TYFABffgP8H7PuRjE&quot;,
        &quot;isSignUp&quot;: false,
        &quot;nickname&quot;: &quot;12수업업슨슨&quot;,
        &quot;userGenderType&quot;: null,
        &quot;birthDate&quot;: null,
        &quot;city&quot;: &quot;금천구&quot;
    },
    &quot;responseTime&quot;: &quot;2022-07-10 08:55:21&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;span style=&quot;background-color: #f8f8f8;&quot;&gt;(2) 응답 없는 성공의 경우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657444792771&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;code&quot;: 200,
    &quot;message&quot;: null,
    &quot;errorDetails&quot;: null,
    &quot;data&quot;: null,
    &quot;responseTime&quot;: &quot;2022-07-10 08:55:21&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;p data-ke-size=&quot;size16&quot;&gt;(3) 인증 에러의 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657444849003&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;code&quot;: 401,
    &quot;message&quot;: &quot;올바르지 못한 인증입니다.&quot;,
    &quot;errorDetails&quot;: [
        &quot;Full authentication is required to access this resource&quot;
    ],
    &quot;responseTime&quot;: &quot;2022-07-10 09:20:15&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;(4) 커스텀 에러의 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657444963483&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;code&quot;: 8100,
    &quot;message&quot;: &quot;탈퇴한 유저입니다.&quot;,
    &quot;errorDetails&quot;: null,
    &quot;data&quot;: null,
    &quot;responseTime&quot;: &quot;2022-07-10 09:22:33&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;p data-ke-size=&quot;size16&quot;&gt;참조: &lt;a href=&quot;https://velog.io/@qotndus43/%EC%8A%A4%ED%94%84%EB%A7%81-API-%EA%B3%B5%ED%86%B5-%EC%9D%91%EB%8B%B5-%ED%8F%AC%EB%A7%B7-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@qotndus43/%EC%8A%A4%ED%94%84%EB%A7%81-API-%EA%B3%B5%ED%86%B5-%EC%9D%91%EB%8B%B5-%ED%8F%AC%EB%A7%B7-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;</description>
      <category>Spring</category>
      <category>API</category>
      <category>API 공통 response 포맷</category>
      <author>wangkisa</author>
      <guid isPermaLink="true">https://wangkisa.tistory.com/74</guid>
      <comments>https://wangkisa.tistory.com/74#entry74comment</comments>
      <pubDate>Sun, 10 Jul 2022 18:24:26 +0900</pubDate>
    </item>
  </channel>
</rss>