<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>oliver-DevLog</title>
    <link>https://oliver-devlog.tistory.com/</link>
    <description>개발 일지와 일상 나눔</description>
    <language>ko</language>
    <pubDate>Mon, 15 Jun 2026 01:57:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>올리버</managingEditor>
    <image>
      <title>oliver-DevLog</title>
      <url>https://tistory1.daumcdn.net/tistory/5052737/attach/9749f54eb1a34cfc9d083724fd641ccb</url>
      <link>https://oliver-devlog.tistory.com</link>
    </image>
    <item>
      <title>[Effective Java-ITEM 2] Builder</title>
      <link>https://oliver-devlog.tistory.com/30</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Builder 이전의 생성 방식&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;점층적 생성자 패턴&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메소드나 생성자의 경우 파라미터가 많은 경우 적절히 대응하기가 쉽지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 생성자 또는 정적 팩토리 메소드에서 사용했던 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;점층적 생성자 패턴&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;pre id=&quot;code_1664139556630&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Car{
    private final int type;
	private final int doors;
    private final int oilType;
    
    public Car(int type){
    	this(type, 4);
    }
    
    public Car(int type, int doors){
    	this(type, doors, 0);
    }
    
    public Car(int type, int doors, int oilType){
    	this.doors = doors;
        this.type = type;
        this.oilType = oilType;
    }
}&lt;/code&gt;&lt;/pre&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;자바빈즈 패턴&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택해야할 파라미터가 많은 경우 활용할 수 있는 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;자바빈즈 패턴&lt;/b&gt;&lt;/span&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터가 없는 생성자를 생성 후, Setter 메소드를 호출하여 원하는 값을 직접 설정하는 방식이다.&lt;/p&gt;
&lt;pre id=&quot;code_1664140004742&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Car{
	private int type;
    private int doors = 4; //필수값 설정
    private int oilType;
    
    public Car(){}
    
    public void setType(int type){
    	this.type = type;
    }
    
    public void setDoors(int doors){
    	this.doors = doors;
    }
    
    public void setOilType(int oilType){
    	this.oilType = oilType;
    }
    
    public int getType(){
    	return type;
    }
    public int getDoors(){
    	return doors;
    }
    public int getOilType(){
    	return oilType;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Setter메소드를 활용하게 되면 점층적 생성자 패턴에서 나타나던 단점이 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 Setter를 호출하면서 필요한 값을 설정하면 되기 때문에 어떤 값을 설정하는지 혼동되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 객체 하나를 만들기 위해선 메소드를 여러번 호출해야하고, 객체가 생성되기 전까지는 일관성이 무너진 상태에 노이게 된다.(런타임에 버그로 나타날 수 있다.) 또한 자바빈즈 패턴은 클래스를 불변하게 만들 수 없다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;빌더 패턴&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 패턴의 경우 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;점층적 생성자 패턴의 안정성과 자바빈즈 패턴의 가독성&lt;/b&gt;&lt;/span&gt;을 가졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 방법은 다음과 같다.&lt;/p&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: #ef6f53;&quot;&gt;&lt;b&gt;1. 필수 파라미터만으로 생성자 또는 정적 팩토리 메소드를 호출해 빌더 객체를 얻는다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;2. 빌더 객체가 제공하는 일종의 Setter 메소드로 원하는 파라미터를 설정한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;3. 파라미터가 없는 build()를 호출하여 필요한 객체를 얻는다.(보통 불변 객체이다.)&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;/p&gt;
&lt;pre id=&quot;code_1664142722027&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//import 있다고 가정
enum CarType{SEDAN,SUV,CUV,SPORT,TRUCK}
enum OilType{GASOLINE,DIESEL,LPG}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1664142693244&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Car{
	private final CarType type;
	private final int doors;
	private final OilType oilType;

	public static class Builder{
		//필수 파라미터
		private final CarType type;

		//선택 파라미터의 경우 Default 값으로 셋팅
		private int doors = 4;
		private OilType oilType = OilType.GASOLINE;

		public Builder(CarType type){
			this.type = type;
		}

		public Builder doors(int val){
			this.doors = val;

			return this;
		}

		public Builder oilType(OilType val){
			this.oilType = val;

			return this;
		}

		public Car build(){
			return new Car(this);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더의 Setter 메소드들은 자기 자신을 반환하기 때문에 연쇄적 호출을 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식을 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;Fluent API&lt;/b&gt;&lt;/span&gt; 또는 &lt;span style=&quot;color: #ef6f53;&quot;&gt;Method Chaining&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 세단 형태의 3개의 문을 가진 디젤 차량을 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1664142862467&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Car car = new Car.Builder(CarType.SEDAN)
                 .doors(3)
                 .oilType(OilType.DIESEL)
                 .build();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 유효성 검사 로직은 제거하였지만, 필요하다면 유효성 검사 로직을 추가하여 잘못된 점을 발견하게 되면 메세지를 담아 IllegalArgumentException 을 던져주면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;추상 클래스를 활용한 빌더&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 패턴은 계층적으로 설계된 클래스와 사용하기 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상클래스의 경우 추상 빌더, Concrete 클래스의 경우 Concrete 빌더를 갖게 한다.&lt;/p&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 id=&quot;code_1664144108682&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abstract class Burger{
	public enum Topping{CHEEZE,PATTY,LETTUCE, TOMATO,MUSGROOM, ONION, PICKLE, EGG}
	final Set&amp;lt;Topping&amp;gt; toppings;

	abstract static class Builder&amp;lt;T extends Builder&amp;lt;T&amp;gt;&amp;gt;{
		EnumSet&amp;lt;Topping&amp;gt; toppings = EnumSet.noneOf(Topping.class);

		protected abstract T self();

		public T addTopping(Topping topping){
			toppings.add(topping);
			return self();
		}

		public abstract Burger build();
	}

	protected Burger(Builder&amp;lt;?&amp;gt; builder){
		toppings = builder.toppings.clone();
	}
}&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;다음은 Burger 추상 클래스를 상속 받은 EggSlut 클래스를 생성해보겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1664144150556&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class EggSlut extends Burger{
	public enum Side{NONE, FRENCH_FRIES, SLUT, COLA}
	private final Side side;

	public static class Builder extends Burger.Builder&amp;lt;Builder&amp;gt;{
		private final Side side;

		@Override
		protected Builder self(){
			return this;
		}

		public Builder(Side side){
			this.side = Objects.requireNonNull(side);
		}

		@Override
		public EggSlut build(){
			return new EggSlut(this);
		}
	}

	public EggSlut(Builder builder){
		super(builder);
		side = builder.side;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EggSlut 인스턴스를 가지려면 동일하게 Builder를 호출하여 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1664144217098&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EggSlut eggSlut = new EggSlut.Builder(EggSlut.Side.SLUT)
                             .addTopping(Burger.Topping.EGG)
                             .addTopping(Burger.Topping.LETTUCE)
                             .addTopping(Burger.Topping.CHEEZE)
                             .addTopping(Burger.Topping.TOMATO)
                             .build();&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 보다시피 빌더 패턴은 매우 유연하다.&lt;/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;코드가 장황하다 보니 파라미터가 4개 이상 되어야 값어치를 한다.&lt;/p&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: #ef6f53;&quot;&gt;&lt;b&gt;API는 시간이 지날 수록 파라미터가 많아지는 경향&lt;/b&gt;&lt;/span&gt;이 있으니 예방차원에서 빌더를 구현해도 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 이야기/Java</category>
      <category>Builder</category>
      <category>EffectiveJava</category>
      <category>이펙티브자바</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/30</guid>
      <comments>https://oliver-devlog.tistory.com/30#entry30comment</comments>
      <pubDate>Mon, 26 Sep 2022 07:27:26 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java-ITEM 1] 정적 팩토리 메소드</title>
      <link>https://oliver-devlog.tistory.com/29</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;전통적인 인스턴스 생성 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스의 인스턴스 생성 방식 중에 흔히 사용 하는 것이 public 생성자이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자를 통해 인스턴스를 생성하여 사용하게 되면 어떤 것을 반환하고자 하는지 찾기가 모호해질 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1663242415041&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Pizza{
	private final String cheese;
	private final String topping;
	private final int size;

	public Pizza(String cheese, String topping, int size){
		this.cheese = cheese;
		this.topping = topping;
		this.size = size;
	}
    
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 Pizza 인스턴스를 생성하려면 다음과 같이 생성해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1663242565566&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Pizza cheesePizza = new Pizza(&quot;체다치즈&quot;,&quot;토마토소스&quot;,10);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 파라미터가 더욱더 많아지게 된다면 해당 순서의 파라미터가 무슨 일을 하는 것인지 구분하기가 어려워지게 되고, 개발자 또한 실수를 범하게 될수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&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;b&gt;new&lt;/b&gt;를 할때 직접적으로 하는 것이 아닌 메소드를 통해 간접적으로 생성하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1663243739982&quot; class=&quot;arduino&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//생성자 방식
String constructorStr = new String(&quot;Constructor&quot;);

//정적 팩토리 메소드 방식
String factoryStr = String.valueOf(&quot;Factory&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;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;1. 이름을 가질 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자의 파라미터로는 해당 인스턴스가 어떤 역할을 하는지 특성을 제대로 설명하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 정적 팩토리 메소드는 네이밍만 올바르게 한다면 반환할 인스턴스의 특정을 제대로 설명할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1663243658062&quot; class=&quot;arduino&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Pizza{
	private final String cheese;
	private final String topping;
	private final int size;

	public Pizza(String cheese, String topping, int size){
		this.cheese = cheese;
		this.topping = topping;
		this.size = size;
	}

	public static Pizza cheesePizza(int size){
		return new Pizza(&quot;체다치즈&quot;, &quot;토마토소스&quot;, size);
	}

	public static Pizza peperoniPizza(int size){
		return new Pizza(&quot;기본치즈&quot;, &quot;페퍼로니&quot;, size);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1663243413937&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//생성자 방식
Pizza cheesePizza = new Pizza(&quot;체다치즈&quot;,&quot;토마토소스&quot;,10);

//정적 팩토리 메소드
Pizza cheesePizza = Pizza.cheesePizza(10);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;2. 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메소드를 사용하게 되면 미리 만들어진 인스턴스를 캐싱하여 재활용하는 식으로 사용하여 무분별한 객체 생성을 막을 수있다.&lt;/p&gt;
&lt;pre id=&quot;code_1663244375204&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Boolean vlaue = Boolean.valueOf(true);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Boolean 객체를 new 하여 인스턴스화하지 않고 받아올 수 있다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;3. 반환 타입의 하위 타입 인스턴스를 반환 할수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 능력으로 반환할 객체의 클래스를 자유롭게 선택할 수 있는 유연성을 제공한다.&lt;/p&gt;
&lt;pre id=&quot;code_1663244812184&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Mobility{
	public static Mobility of(String type){
		if(type.equalsIgnoreCase(&quot;car&quot;)){
			return new Car();
		}else if(type.equalsIgnoreCase(&quot;bike&quot;)){
			return new Bike();
		}
        ...
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 클래스를 보면 of 메소드를 통해 type 별로 필요한 모빌리티를 반환해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;4. 입력 파라미터에 따라 매번 다른 클래스의 인스턴스를 반환한다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환 타입의 하위 타입이기만 하면 어떤 클래스를 반환하든 상관없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 EnumSet클래스가 그 예가 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 보면 파라미터 길이에 따라 다른 클래스를 반환하고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1663246285201&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;E extends Enum&amp;lt;E&amp;gt;&amp;gt; EnumSet&amp;lt;E&amp;gt; noneOf(Class&amp;lt;E&amp;gt; elementType) {
    Enum&amp;lt;?&amp;gt;[] universe = getUniverse(elementType);
    if (universe == null)
        throw new ClassCastException(elementType + &quot; not an enum&quot;);

    if (universe.length &amp;lt;= 64)
        return new RegularEnumSet&amp;lt;&amp;gt;(elementType, universe);
    else
        return new JumboEnumSet&amp;lt;&amp;gt;(elementType, universe);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;5. 정적 팩토리 메소드를 작정하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 유연함을 통해 서비스 제공자 프레임워크를 만들 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스가 존재해야 생성자가 존재할 수 있지만, 정적 팩토리 메소드는 메소드와 반환할 타입만 정해두고 실제 반환될 클래스를 나중에 구현하는게 가능하다.&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1663246187610&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private Class&amp;lt;?&amp;gt; serviceClass(){
    return Class.forName(&quot;com.test.services.TestService&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;1. 정적 팩토리 메소드만 제공하는 경우에는 상속이 불가능하다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩토리 메소드만 제공하면 하위 클래스를 생성할 수 없다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;2. 개발자가 찾기 어렵다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API설명에 명확히 드러나지 않다보니 다른 개발자가 찾기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 문서화를 잘하고, 메소드 네이밍을 널리 알려진 규약으로 사용한다면 문제를 완화할 수 있다.&lt;/p&gt;</description>
      <category>개발 이야기/Java</category>
      <category>EffectiveJava</category>
      <category>Factory Method</category>
      <category>이펙티브자바</category>
      <category>자바8</category>
      <category>정적팩토리메소드</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/29</guid>
      <comments>https://oliver-devlog.tistory.com/29#entry29comment</comments>
      <pubDate>Thu, 15 Sep 2022 21:52:24 +0900</pubDate>
    </item>
    <item>
      <title>Collection Interface로 업캐스팅을 하는 이유</title>
      <link>https://oliver-devlog.tistory.com/28</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;List, Set, Map을 사용하다보면 습관적으로 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;List 리스트= new ArrayList&lt;/b&gt;&lt;/span&gt;처럼 사용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 구현체를 직접 선언하여 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;ArrayList 리스트 = new ArrayList&lt;/b&gt;&lt;/span&gt;로 사용하게된다면 다음과 같은 린트(Lint) 메시지를 확인 할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Declarations should use Java collection interfaces such as &quot;List&quot; rather than specific implementation classes such as &quot;LinkedList&quot;&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;The purpose of the Java Collections API is to provide a well defined hierarchy of interfaces in order to hide implementation details.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Implementing classes must be used to instantiate new collections, but the result of an instantiation should ideally be stored in a variable whose type is a Java Collection interface.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This rule raises an issue when an implementation class:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;is returned from a public method.&lt;/li&gt;
&lt;li&gt;is accepted as an argument to a public method.&lt;/li&gt;
&lt;li&gt;is exposed as a public member.&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;size18&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Noncompliant Code Example&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1653528565910&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Employees {
  private HashSet&amp;lt;Employee&amp;gt; employees = new HashSet&amp;lt;Employee&amp;gt;();  // Noncompliant - &quot;employees&quot; should have type &quot;Set&quot; rather than &quot;HashSet&quot;

  public HashSet&amp;lt;Employee&amp;gt; getEmployees() {                       // Noncompliant
    return employees;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;Compliant Solution&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1653528586380&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Employees {
  private Set&amp;lt;Employee&amp;gt; employees = new HashSet&amp;lt;Employee&amp;gt;();      // Compliant

  public Set&amp;lt;Employee&amp;gt; getEmployees() {                           // Compliant
    return employees;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;List, Set, Map&lt;/b&gt;&lt;/span&gt;은 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Interface&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각각의 구현체로는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;ArrayList, LinkedList, HashSet, HashMap&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;color: #ef5369;&quot;&gt;&lt;b&gt;Interface&lt;/b&gt;&lt;/span&gt;로 업캐스팅할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다형성을 지원하기 위해서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유연한 구조로 설계하여서 나중에 다른 구현체로 변경할때 쉽게 변경하기 위해서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 업캐스팅으로 선언하면 모든 코드가 해당 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Interface&lt;/b&gt;&lt;/span&gt;를 따르게 코드를 작성할 수 있고, 해당 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Interface&lt;/b&gt;&lt;/span&gt;를 구현한 다른 자료형 간 쉽게 변경이 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;잘못 사용된 예&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1653528427162&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//구현체를 직접 사용하는 경우
ArrayList&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
HashMap&amp;lt;String,Object&amp;gt; map = new HahMap&amp;lt;&amp;gt;();
HashSet&amp;lt;String&amp;gt; set = new HashSet&amp;lt;&amp;gt;();

public void methodName(HashMap&amp;lt;String,Object&amp;gt; param){
    ....
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;올바른 예&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1653528472164&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Interface로 업캐스팅하여 사용하는 경우
List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
Map&amp;lt;String,Object&amp;gt; map = new HahMap&amp;lt;&amp;gt;();
Set&amp;lt;String&amp;gt; set = new HashSet&amp;lt;&amp;gt;();

public void methodName(Map&amp;lt;String,Object&amp;gt; param){
    ....
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp; 참고&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/9852831/polymorphism-why-use-list-list-new-arraylist-instead-of-arraylist-list-n&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/9852831/polymorphism-why-use-list-list-new-arraylist-instead-of-arraylist-list-n&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1653528776713&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Polymorphism: Why use &amp;quot;List list = new ArrayList&amp;quot; instead of &amp;quot;ArrayList list = new ArrayList&amp;quot;?&quot; data-og-description=&quot;Possible Duplicate: Why should the interface for a Java class be prefered? When should I use List&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;Object&amp;gt;(); ArrayList inherits from List, so if some&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/9852831/polymorphism-why-use-list-list-new-arraylist-instead-of-arraylist-list-n&quot; data-og-url=&quot;https://stackoverflow.com/questions/9852831/polymorphism-why-use-list-list-new-arraylist-instead-of-arraylist-list-n&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kMlr5/hyOxtZ3IQ2/S156IqtDfNAD0qk34lUQEK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/9852831/polymorphism-why-use-list-list-new-arraylist-instead-of-arraylist-list-n&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/9852831/polymorphism-why-use-list-list-new-arraylist-instead-of-arraylist-list-n&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kMlr5/hyOxtZ3IQ2/S156IqtDfNAD0qk34lUQEK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Polymorphism: Why use &quot;List list = new ArrayList&quot; instead of &quot;ArrayList list = new ArrayList&quot;?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Possible Duplicate: Why should the interface for a Java class be prefered? When should I use List&amp;lt;Object&amp;gt; list = new ArrayList&amp;lt;Object&amp;gt;(); ArrayList inherits from List, so if some&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 이야기/Java</category>
      <category>Interface</category>
      <category>Java</category>
      <category>Polymorphism</category>
      <category>upcasting</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/28</guid>
      <comments>https://oliver-devlog.tistory.com/28#entry28comment</comments>
      <pubDate>Thu, 26 May 2022 10:37:02 +0900</pubDate>
    </item>
    <item>
      <title>Try-With-Resources를 이용한 자원 반납</title>
      <link>https://oliver-devlog.tistory.com/27</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java7 이전에는 자원을 해제 하기 위해서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;try-catch-finally&lt;/b&gt;&lt;/span&gt;를 사용하여 자원을 해제했다.&lt;/p&gt;
&lt;pre id=&quot;code_1653403920777&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BufferedReader br = null;

try{
    br = new BufferedReader(new InputStreamReader(System.in));
    String str = br.readLine();
    
    System.out.println(str);
}catch(IOException e){
	e.printStackTrace();
}finally{
    if(br != null{
        try{
            br.close();
        }catch(Excetpion e){}
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java7 이후에 생긴 기능 중 하나인 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;try-with-resources&lt;/b&gt;&lt;/span&gt;는 try 구문에서 자원을 생성하고 사용을 완료하면 자동 반납(Close)해주는 기능이다.&lt;/p&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: #006dd7;&quot;&gt;&lt;b&gt;java.lang 패키지의 AutoCloseable 인터페이스&lt;/b&gt;&lt;/span&gt;를 상속받으면 try구문에서 자원을 생성할 수 있고, 사용을 완료하면 자동 반납하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;try-with-resources 자원 사용/반납하기&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 finally에서 close를 직접 해주었다면 try-with-resources를 사용하여 조금더 간결해진 코드를 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;try 구문 옆에 생성하고 사용이 완료되면 자동으로 반납이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1653404157730&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try(BufferedReader br = new BufferedReader(new InputStreamReader(System.in))){
    String str = br.readLine();
    
    System.out.println(str);
}catch(IOException e){
	e.printStackTrace();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러개의 자원을 할당하려면&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;;(세미콜론)&lt;/b&gt;&lt;/span&gt;을 붙여서 작성하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러개를 할당하게 되면 위에서 Top-Down으로 생성되지만, 해제는 Bottom-Up 순서로 해제가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1653404269145&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try(BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out))){
    String str = br.readLine();
    
    bw.write(str);
    bw.newLine();
    bw.flush();
}catch(IOException e){
	e.printStackTrace();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;AutoCloseable 상속&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AutoCloseable을 상속받아서 클래스를 커스터마이징하여 사용할 경우 동일하게 자원 반납을 자동으로 해줄수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1653404757936&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CloseableResource implements AutoCloseable{

    private String name;
    
    public CloseableResource(String name){
        this.name = name;
        System.out.println(&quot;Created Resource : &quot; +name);
    }

    public void useResource(){
        System.out.println(&quot;Using this Resource : &quot; +name);
    }

    @Override
    public void close() throws Exception {
        System.out.println(&quot;Close Resource : &quot;+ name);        
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1653404771018&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main{
    public static void main(String[] args){
        try(CloseableResource resource = new CloseableResource(&quot;customObject&quot;)){
            resource.useResource();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1653404882195&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#실행 결과
Created Resource : customObject
Using this Resource : customObject
Close Resource : customObject&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자원을 여러개 할당할 경우 다음과 같이 나타나게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해제 시 Bottom-Up 순서로 나타난다.&lt;/p&gt;
&lt;pre id=&quot;code_1653405106842&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main{
    public static void main(String[] args){
        try(CloseableResource object1 = new CloseableResource(&quot;object1&quot;);
            CloseableResource object2 = new CloseableResource(&quot;object2&quot;)){
            object1.useResource();
            object2.useResource();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1653405137715&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#실행결과
#생성은 Top-Down
#반납은 Bottom-Up
Created Resource : object1
Created Resource : object2   
Using this Resource : object1
Using this Resource : object2
Close Resource : object2     
Close Resource : object1&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;</description>
      <category>개발 이야기/Java</category>
      <category>AutoCloseable</category>
      <category>Java</category>
      <category>try-with-resources</category>
      <category>자원해제</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/27</guid>
      <comments>https://oliver-devlog.tistory.com/27#entry27comment</comments>
      <pubDate>Wed, 25 May 2022 00:19:13 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] DI(의존성 주입)</title>
      <link>https://oliver-devlog.tistory.com/26</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring의 핵심 기술중 하나인 DI(의존성 주입)에 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;필드 주입(Field Injection)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드 주입은 필드에서 바로 주입하는 방법이다. 과거에 많 이용되었지만 외부에서 접근이 불가능하고, 테스트코드 작성 시 필드의 객체를 수정할 수 없게 되어 사용하지 않게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 강한 결합으로 인해 외부에서 사용하기가 어려워진다.&lt;/p&gt;
&lt;pre id=&quot;code_1653352212478&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Field Injection
public class MyController{
     @Autowired
     private MyService myService;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;수정자 주입(Setter Injection)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정자 주입은 Setter Method을 통해 주입하는 방법이다. 주입 받는 객체가 변경될 가능성이 있는 경우에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존관계를 나타낼 수 있으나, 필수적으로 주입되어야 할 항목들을 빼먹어 null 처리될 수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1653352516342&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Setter Injection
public class MyController{
    private MyService myService;
    
    @Autowired
    public void setMyService(MyService myService){
    	this.myService = myService;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;생성자 주입(Constructor Injection)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자에서 주입하는 방법이다. 최근 Spring에서 권장하는 방법인데 그 이유는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;1. 객체의 불변성 확보&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;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;size18&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;2. 테스트 코드 작성 용이&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 주입이 아닌 다른 주입으로 작성된 코드는 순수 자바 코드로 테스트 작성하는 것이 어렵다.&lt;/p&gt;
&lt;pre id=&quot;code_1653353118990&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Field Injection
public class MyService{
     @Autowired
     private MyRepository myRepository;
     
     public User findById(String id){
     	return myRepository.findById(id);
     }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 클래스의 테스트 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1653353332318&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MyServiceTest{
	
    @Test
    public void findByIdTest(){
    	//Given
        MyService myService = new MyService();
        String id = &quot;testId&quot;;
        
        //When
        User user = myService.findById(id);
        
        //Then
        assertEquals(user.getId(),id);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 작성한 경우 DI 프레임워크 위에서 동작하지 않기 때문에 의존 관계가 주입되지 않아 의존성 주입 대상인 myRepository가 null이 되어 findById를 실행할 때 &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;NPE&lt;/b&gt;&lt;/span&gt;가 발생한다. 수정자 주입을 통해 처리할 수는 있지만, 변경 가능성을 열어두기 때문에 단점을 갖게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Spring Bean을 올려서 하기위해서는 MockBean으로 생성해서 하거나 SpringBootTest를 통해 전체 테스트를 해야하는데 이것은 단위테스트가 아니기 때문에, 생성자 주입을 사용한다.&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;3. Final 키워드 작성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 주입을 하게 되면 final 키워드를 사용할 수 있고, 컴파일 단에서 누락된 부분을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 생성자가 1개인 경우에는 &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;@Autowired&lt;/b&gt;&lt;/span&gt;를 생략해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 Lombok의 &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;RequiredArgsConstructor&lt;/b&gt;&lt;/span&gt;을 활용해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 작성이 가능하다. 다음 3개는 동일한 동작을 하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1653354497643&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Constructor Injection
public class MyController{
    private final MyService myService;
    
    @Autowired
    public MyController(MyService myService){
    	this.myService = myService;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1653354519254&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Constructor Injection
public class MyController{
    private final MyService myService;
    
    public MyController(MyService myService){
    	this.myService = myService;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1653354545774&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//Constructor Injection
@RequiredArgsConstructor
public class MyController{
    private final MyService myService;
}&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;4. 순환 참조 방지&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주입하려는 대상이 서로 마주보고 있어 계속해서 참조하고 생성하는 것을 순환 참조라고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1653358470986&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class MyServiceA{
    @Autowired
    private MyServiceB myServiceB;
    
    public void startB(){
        myServiceB.startA();
    }
}

@Component
public class MyServiceB{
    @Autowired
    private MyServiceA myServiceA;
    
    public void startA(){
        myServiceA.startB();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상황이 발생하게 되면 MyServiceA 가 MyServiceB를 호출하고 MyServiceB가 MyServiceA를 호출하게되는데 해당 객체를 필드 주입 또는 수정자 주입을 통해하게 되면 런타임 중에 계속적으로 new 하게 되어 &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;StackOverFlow&lt;/b&gt;&lt;/span&gt;가 발생하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1653358846667&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Caused by: java.lang.StackOverflowError: null&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 생성자 주입을 통해 생성하게 되면 다음과 같이 나타난다.&lt;/p&gt;
&lt;pre id=&quot;code_1653358649954&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@RequiredArgsConstructor
public class MyServiceA{
    private final MyServiceB myServiceB;
    
    public void startB(){
        myServiceB.startA();
    }
}

@Component
@RequiredArgsConstructor
public class MyServiceB{
    private final MyServiceA myServiceA;
    
    public void startA(){
        myServiceA.startB();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하게 되면 객체가 생성될때 주입을 하게되기 때문에, 애플리케이션 수동 시점에서 순환 참조를 알아차릴 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1653358923879&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Description: 
The dependencies of some of the beans in the application context form a cycle: 
┌─────┐ 
| myServiceA defined in file [.....class]
  &amp;uarr; &amp;darr; 
| myServiceB defined in file [.....class]
└─────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;요약&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DI(의존성 주입)을 할때 필드 주입, 수정자 주입 보다 생성자 주입을 하여 사용하여서, 순환 참조를 방지하고,&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;background-color: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp;참고&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://jurogrammer.tistory.com/79&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jurogrammer.tistory.com/79&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@gillog/SpringBoot-WebMvcTest-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%8B%9C-Bean-%EC%A3%BC%EC%9E%85-%EC%97%90%EB%9F%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@gillog/SpringBoot-WebMvcTest-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%8B%9C-Bean-%EC%A3%BC%EC%9E%85-%EC%97%90%EB%9F%AC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/125&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mangkyu.tistory.com/125&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 이야기/Spring Boot</category>
      <category>DI</category>
      <category>생성자주입</category>
      <category>수정자주입</category>
      <category>의존성주입</category>
      <category>필드주입</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/26</guid>
      <comments>https://oliver-devlog.tistory.com/26#entry26comment</comments>
      <pubDate>Tue, 24 May 2022 11:27:36 +0900</pubDate>
    </item>
    <item>
      <title>강한 결합과 약한 결합</title>
      <link>https://oliver-devlog.tistory.com/25</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;강한 결합&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;어떠한 객체가 다른 객체에 강한 의존성을 가지고 있음을 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;아래의 클래스를 보면&amp;nbsp; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;사람이 개를 훈련하다&lt;/b&gt;&lt;/span&gt;라는 의미를 부여할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1652862258868&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Dog{
    public void train(){
        System.out.println(&quot;train dog&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1652863168471&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Cat{
    public void train(){
        System.out.println(&quot;train cat&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1652863210407&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person{
    private Dog dog;
    
    public Person(){
        dog = new Dog();
    }
    
    public void trainAnimal(){
        dog.train();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person 클래스를 보면 맴버 변수로 Dog 클래스를 가지고 있다. 여기선 단점을 발견 할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dog 클래스가 없는 경우 Person 클래스는 존재할 수 없다.&lt;/li&gt;
&lt;li&gt;Dog 에서 Cat으로 변경하게 되면 Person의 코드를 변경해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Person 클래스는 Dog 클래스에 의존하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;약한 결합&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 간의 의존성을 약하게 하는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1652863637538&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface animal{
    void train();
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1652863671647&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Dog implements Animal{
    @Override
    public void train(){
        System.out.println(&quot;train dog&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1652863680315&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Cat implements Animal{
    @Override
    public void train(){
        System.out.println(&quot;train cat&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1652863725107&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Person{
    private Animal animal;
    
    public Person(Animal animal){
        this.animal = animal;
    }
    
    public void trainAnimal(){
        animal.train();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Interface로 추상화하여 개발하게 되면 내부 클래스를 변경하지 않아도, 하나의 인터페이스로 대입되기 때문에, 생성자를 통하거나 수정 메소드를 통해 변수만 받으면 객체 변경이 가능해진다.(런타임 시 의존관계 결정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 강한 결합에서 벗어난 약한 결합을 이룬다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;</description>
      <category>개발 이야기/Java</category>
      <category>강한결합</category>
      <category>약한결합</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/25</guid>
      <comments>https://oliver-devlog.tistory.com/25#entry25comment</comments>
      <pubDate>Wed, 18 May 2022 17:56:24 +0900</pubDate>
    </item>
    <item>
      <title>암호화 알고리즘 종류</title>
      <link>https://oliver-devlog.tistory.com/24</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zxkn1/btrBuRUNdy1/Q8QiFnXwRkst0b9kUOKkbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zxkn1/btrBuRUNdy1/Q8QiFnXwRkst0b9kUOKkbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zxkn1/btrBuRUNdy1/Q8QiFnXwRkst0b9kUOKkbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZxkn1%2FbtrBuRUNdy1%2FQ8QiFnXwRkst0b9kUOKkbk%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;163&quot; height=&quot;163&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;정의&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;평문(Plaintext) -&lt;/span&gt; &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444;&quot;&gt;해독 가능한 형태의 메시지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;암호문(Ciphertext) - &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444;&quot;&gt;해독 불가능한 형태의 메시지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;암호화(Encryption) -&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444;&quot;&gt;평문을 암호문으로 변환하는 과정&lt;/span&gt;&lt;span style=&quot;color: #444444;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;복호화(Decryption) - &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444;&quot;&gt;암호문을 평문으로 변환하는 과정&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;1. 대칭키 암호화&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vIgVk/btrBwtr5rba/EUWPN7JFglvPuOONqJDxw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vIgVk/btrBwtr5rba/EUWPN7JFglvPuOONqJDxw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vIgVk/btrBwtr5rba/EUWPN7JFglvPuOONqJDxw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvIgVk%2FbtrBwtr5rba%2FEUWPN7JFglvPuOONqJDxw0%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;453&quot; height=&quot;218&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;307&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;b&gt;종류 :&lt;/b&gt; AES128, AES256, SEED(국내 표준)&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;2. 비대칭키 암호화&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eudtGe/btrBCsT5YM8/WY7JSqoSUrefXes1EbNhZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eudtGe/btrBCsT5YM8/WY7JSqoSUrefXes1EbNhZ0/img.png&quot; data-alt=&quot;공개키 암호화의 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eudtGe/btrBCsT5YM8/WY7JSqoSUrefXes1EbNhZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeudtGe%2FbtrBCsT5YM8%2FWY7JSqoSUrefXes1EbNhZ0%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;475&quot; height=&quot;200&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공개키 암호화의 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YIlio/btrByn6fcMR/bCPAFErskhUW0VG4IOFNr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YIlio/btrByn6fcMR/bCPAFErskhUW0VG4IOFNr0/img.png&quot; data-alt=&quot;개인키 암호화의 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YIlio/btrByn6fcMR/bCPAFErskhUW0VG4IOFNr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYIlio%2FbtrByn6fcMR%2FbCPAFErskhUW0VG4IOFNr0%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;475&quot; height=&quot;193&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개인키 암호화의 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;암호화와 복호화 시 서로 다른 키를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개키(Public Key)와 개인키(Private Key)가 한 쌍으로 이루어지고 있다.&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;span style=&quot;color: #000000;&quot;&gt;A의 공개키를 이용하여 암호화된 데이터는 A의 개인키로만 복호화가 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;A의 개인키를 이용하여 암호화된 데이터는 A의 공개키로만 복호화가 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;통신 방식&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;암호 모드 -&lt;/b&gt; &lt;/span&gt;송신자 공개키로 암호화 -&amp;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;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;인증 모드 - &lt;/b&gt;&lt;/span&gt;송신자 개인키로 암호화 -&amp;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;&lt;b&gt;종류 :&lt;/b&gt; RSA(대표적 공개키 알고리즘), DSA(전자서명 알고리즘 표준), ECC&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;단방향 암호화&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv9Drk/btrBCs1joIW/y6y3CIwZ6oZS1mHdMAxWvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv9Drk/btrBCs1joIW/y6y3CIwZ6oZS1mHdMAxWvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv9Drk/btrBCs1joIW/y6y3CIwZ6oZS1mHdMAxWvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv9Drk%2FbtrBCs1joIW%2Fy6y3CIwZ6oZS1mHdMAxWvK%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;337&quot; height=&quot;258&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해싱(Hashing)을 이용한 암호화 방식으로 평문을 암호문으로 암호화는 가능하지만 암호문을 평문으로 복호화하는 것은 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 진위여부는 확인하고 싶으나, 본 데이터의 Privacy를 지키고 싶은 경우 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hash값은 크기와 알고리즘에 따라 암호문의 결과가 상이하다.&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;종류 :&lt;/b&gt; MD5, SHA-1, SHA,256, SHA-512 등&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp; 참고&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@inyong_pang/Programming-%EC%95%94%ED%98%B8%ED%99%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%A2%85%EB%A5%98%EC%99%80-%EB%B6%84%EB%A5%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@inyong_pang/Programming-%EC%95%94%ED%98%B8%ED%99%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%A2%85%EB%A5%98%EC%99%80-%EB%B6%84%EB%A5%98&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%95%A8%EC%88%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%95%A8%EC%88%98&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 이야기/백엔드 이야기</category>
      <category>단방향알고리즘</category>
      <category>대칭키</category>
      <category>비대칭키</category>
      <category>암호화</category>
      <category>양방향알고리즘</category>
      <category>해싱</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/24</guid>
      <comments>https://oliver-devlog.tistory.com/24#entry24comment</comments>
      <pubDate>Mon, 9 May 2022 13:56:22 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] (2) Topic과 Partition</title>
      <link>https://oliver-devlog.tistory.com/23</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Events, Streams, Topics&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Events&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;Event란, 과거에 일어난 사실을 뜻한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Event는 발생함으로 인해 변화된 상태를 가지고 시스템 사이를 오가는 불변의 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Streams&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;Event Stream이란, 관련된 Event들을 나타낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Topics&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Event Stream이 Kafka에서는 Topic이란 이름으로 저장된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Kafka에서는 Topic이 구체화된 Event Stream을 뜻한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Topic은 데이터베이스의 테이블이나 파일 시스템의 폴더들 같이 연관된 Event를 묶어 영속화한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Topic은 Kafka에서 Producer 와 Consumer 를 분리하는 중요한 컨셉이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Producer 는 Topic에 Message(Event)를 저장 (Push) 하고 Consumer 는 저장된 Message(Event)를 읽어 (Pull) 온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;하나의 Topic에 여러 Producer, Consumer 가 존재할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아래 그림을 보면 연관된 Event가 모여 Stream 을 이루게 되고, Topic 의 이름으로 Kafka에 저장된다.&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;1390&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PMwTE/btrwn446b4i/t1htHdNrwpxijeCjgdCR40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PMwTE/btrwn446b4i/t1htHdNrwpxijeCjgdCR40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PMwTE/btrwn446b4i/t1htHdNrwpxijeCjgdCR40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPMwTE%2Fbtrwn446b4i%2Ft1htHdNrwpxijeCjgdCR40%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;1390&quot; height=&quot;516&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Partitions&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Topic은 여러 Partition으로 나눠진다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;Topic이 일종의 논리적인 개념이라면, Partition은 Topic에 속한 레코드를 실제 저장소에 저장하는 가장 작은 단위이다. 각각의 Partition은 Append-Only 방식으로 기록되는 하나의 로그 파일이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;아래 그림에서 레코드는 Partition에 저장되는 Message를 의미한다.&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;561&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KOPan/btrwpui9isV/Kf3bNhBEnrNGIOiGZbRTpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KOPan/btrwpui9isV/Kf3bNhBEnrNGIOiGZbRTpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KOPan/btrwpui9isV/Kf3bNhBEnrNGIOiGZbRTpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKOPan%2Fbtrwpui9isV%2FKf3bNhBEnrNGIOiGZbRTpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;297&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Offset과 Messages의 순서&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Partition의 레코드는 각각 Offset을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Offset은 Partition내에서 고유한 레코드의 순서를 의미하는 식별자 정보를 가진다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Offset 정보는 Kafka에 의해서 관리되고, 값이 계속 증가하며 불변하는 숫자 정보이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;레코드가 Partition에 쓰여질 때 항상 기록의 맨 뒤에 쓰여지고&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;(Append Only)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;, 다음 순서의 Offset 값을 갖게 된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Offset은 Partition에서 레코드를 읽고 사용하는 Consumer 에게 특히 유용하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아래의 그림은 세 개의 Partition을 갖는 Topic을 보여준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 레코드는 각 Partition의 마지막에 추가된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Message는 Partition 내에서는 유의미한 순서를 가지고 이를 보장하지만, Partition 간에는 보장되지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그래서 Offset이 존재함에도 Message의 순서가 보장되지 않는 것이다.&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;698&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baEzzZ/btrwwrd72dk/WqdcPadKXFixOxrBKTPVv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baEzzZ/btrwwrd72dk/WqdcPadKXFixOxrBKTPVv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baEzzZ/btrwwrd72dk/WqdcPadKXFixOxrBKTPVv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaEzzZ%2Fbtrwwrd72dk%2FWqdcPadKXFixOxrBKTPVv1%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;698&quot; height=&quot;590&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;590&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;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Partition으로 인한 Kafka의 확장성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Kafka Cluster는 Broker라 불리는 하나 혹은 그 이상의 서버들로 이루어지는데, 각각의 Broker는 전체 클러스터에 속한 레코드의 서브셋을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Kafka는 Topic의 Partition들을 여러 Broker에 배포하는데 이로 인해 얻을 수 있는 다음과 같은 이점들이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;한 Topic의 모든 Partition을 하나의 Broker에 넣을 경우, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;해당 Topic의 확장성은 Broker의 I/O 처리량에 의해 제약되지만, Partition들을 여러 Broker에 나누면, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;하나의 Topic은 수평적으로 확장될 수 있고 이로 인해 Broker의 처리 능력보다 더 큰 성능을 제공할 수 있게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Topic은 여러 Consumer 에 의해 동시에 처리될 수 있다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;단일 Broker에서 모든 Partition을 제공하면 지원할 수 있는 Consumer 수가 제약되는데, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여러 Broker에서 Partition을 나누어 제공함으로써 더 많은 Consumer 들이 동시에 Message를 처리할 수 있게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;동일한 Consumer 의 여러 인스턴스가 서로 다른 Broker에 있는 Partition에 접속함으로써 매우 높은 메시지 처리량을 가능하게 한다. 각 Consumer 인스턴스는 하나의 Partition에서 Message를 제공받고, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;각각의 레코드는 명확한 처리 담당자가 존재함을 보장할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Partition 복제를 통해 장해 극복&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka는 여러 Broker에 동일한 Partition의 복사본, 즉 Replica를 2개 이상 유지한다.&lt;br /&gt;Broker에 장애가 발생해도 Replica를 통해 Consumer에게 지속적으로 Message를 제공할 수 있다.&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Partition에 레코드 작성하는 방법(Producer)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;1. Partition Key 사용&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Producer 는 Partition Key를 사용해서 특정한 Partition에 Message를 전달할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Partition Key는 해싱 함수를 통해 전달되고 해싱 함수는 동일한 Key를 갖는 모든 레코드들이 동일한 Partition에 도착하는 것을 보장한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Partition Key를 지정할 경우, 관련된 Event를 동일한 Partition에 유지함으로써 전달된 순서가 그대로 유지되는 것을 보장할 수 있다. 다만 Key가 제대로 분산되지 않는 경우 특정 Broker만 Message를 받는 단점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;partition_key.png&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1VPNc/btrwZZweb5K/jQ71LkAypNEpE5SUzK96C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1VPNc/btrwZZweb5K/jQ71LkAypNEpE5SUzK96C1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1VPNc/btrwZZweb5K/jQ71LkAypNEpE5SUzK96C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1VPNc%2FbtrwZZweb5K%2FjQ71LkAypNEpE5SUzK96C1%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;782&quot; height=&quot;207&quot; data-filename=&quot;partition_key.png&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;207&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;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;2. Kafka에 의한 분배&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Producer 가 Partition Key를 명시하지 않으면, Kafka 는 Round-Robin 방식을 사용해 Partition을 배정한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;레코드의 순서는 보장되지 않지만, 해당 Topic의 Partition들에 고루 분배된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;3. 커스텀 Partitioner 구현&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;Producer 가 서로 다른 비즈니스 규칙을 사용한 자체 Partitioner 구현을 사용할 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Partition으로 부터 레코드 읽기(Consumer)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Kafka 는 Message를 Consumer 에 전달(Push) 하지 않고, Consumer 가 Partition으로부터 Message를 읽어(Pull) 가야 한다. Consumer는 Broker의 Partition과 연결해, Message들이 쓰여진 순서대로 메시지를 읽는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Message의 Offset 은 커서와 같이 동작한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Consumer 는 Message의 Offset으로 커서를 이동하여 Message를 읽는다.(Offset 이동 -&amp;gt; Message 읽기 반복)&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;각 Partition에서 마지막으로 소비된 Message의 Offset 을 기억함으로써 Consumer는 장애가 나도 복구 후에 Message 소비를 재시작할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;partition_consumer.png&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwqnfy/btrwWgFc2KG/gMDwAjBAjdDXCS6XN5PsR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwqnfy/btrwWgFc2KG/gMDwAjBAjdDXCS6XN5PsR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwqnfy/btrwWgFc2KG/gMDwAjBAjdDXCS6XN5PsR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwqnfy%2FbtrwWgFc2KG%2FgMDwAjBAjdDXCS6XN5PsR1%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;539&quot; height=&quot;459&quot; data-filename=&quot;partition_consumer.png&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Partition은 하나 이상의 Consumer 들로부터 소비될 수 있고, 각각은 서로 다른 Offset 을 가지고 Message를 읽을 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Kafka는 Consumer Group 이라는 개념을 사용한다.(어떤 Topic을 소비하는 Consumer 들을 그룹으로 묶은 것)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 동일한 Consumer Group 에 있는 Consumer 들은 동일한 Group-Id 를 부여받는다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Partition 내의 Message는 Group 내의 Consumer 중 하나에 의해서만 소비되는 것을 보장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;consumer_group.png&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/osmSV/btrwXyTd0Tk/2UDq4id8ircmtx1KRknsFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/osmSV/btrwXyTd0Tk/2UDq4id8ircmtx1KRknsFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/osmSV/btrwXyTd0Tk/2UDq4id8ircmtx1KRknsFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FosmSV%2FbtrwXyTd0Tk%2F2UDq4id8ircmtx1KRknsFk%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;474&quot; height=&quot;252&quot; data-filename=&quot;consumer_group.png&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Consumer Group 은 Consumer 들이 병렬적으로 Message를 소비할 수 있도록 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;병렬 처리를 가능케 함으로써 처리량은 매우 높아지지만, Group&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;의 Maximum Parallelism 은 Topic의 Partition 개수와 동일하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여기서 중요한 것은, Consumer 의 개수가 토픽의 병렬성 정도를 결정하지 않고, 결정하는 것은 파티션의 개수라는 것이다. 하지만 파티션을 늘리는 것이 항상 장점으로 작용하지는 않는다. 이와 관련해선 차후에 정리하도록하자.&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: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp;참고&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kafka.apache.org/documentation/&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://medium.com/event-driven-utopia/understanding-kafka-topic-partitions-ae40f80552e8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/event-driven-utopia/understanding-kafka-topic-partitions-ae40f80552e8&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://devidea.tistory.com/95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://devidea.tistory.com/95&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>개발 이야기/Kafka</category>
      <category>Kafka</category>
      <category>partition</category>
      <category>topic</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/23</guid>
      <comments>https://oliver-devlog.tistory.com/23#entry23comment</comments>
      <pubDate>Wed, 23 Mar 2022 20:43:52 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] (1) Kafka란?</title>
      <link>https://oliver-devlog.tistory.com/22</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Kafka란?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Distributed Event Streaming Platform(분산 이벤트 스트리밍 플랫폼)&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Publish-Subscribe 모델의 Message Queue이며, 분산에 특화되어 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;기본 구조&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSwIXf/btrwrGQd30q/h5pVv7yWZjQ7CJj4DF5BV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSwIXf/btrwrGQd30q/h5pVv7yWZjQ7CJj4DF5BV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSwIXf/btrwrGQd30q/h5pVv7yWZjQ7CJj4DF5BV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSwIXf%2FbtrwrGQd30q%2Fh5pVv7yWZjQ7CJj4DF5BV0%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;1040&quot; height=&quot;452&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.1 Event&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Event는 Producer와 Consumer가 데이터를 주고 받는 단위이다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.2 Topic&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Event가 쓰이는 곳이다.&lt;/li&gt;
&lt;li&gt;Topic은 파일시스템의 폴더 또는 디렉토리와 유사하며, 이벤트는 폴더안의 파일과 유사하다.&lt;/li&gt;
&lt;li&gt;Topic의 개수는 제한이 없고, 이름으로 구분된다.&lt;/li&gt;
&lt;li&gt;Topic이 다시 특정한 개수의 partitions이 된다. 개수의 명시가 필요하다&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.3 Partition&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Topic이 나눠지는 단위이다.&lt;/li&gt;
&lt;li&gt;Partition에 한 번 쓰여진 데이터는 변경이 불가능 하다.&lt;/li&gt;
&lt;li&gt;Topic을 구성하는 Partition은 정렬되어 있지만, 키 값이 제공되지 않으면 랜덤하게 할당한다.&lt;/li&gt;
&lt;li&gt;하나의 파티션에 순차적으로 처리하는 것보다 병렬 처리하여 시간을 절약하기 위해 Partition을 나눈다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.4 Broker&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka 서버를 가르키며 특정한 Topic의 Partition을 보유한다.&lt;/li&gt;
&lt;li&gt;3개 이상의 Broker를 가지는 것이 장애 허용 관점에서 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.5 Producer&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Producer는 Kafka에 Event를 게시하는 클라이언트 어플리케이션이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.6 Consumer&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;Consumer은 Topic을 구독하고 얻어낸 이벤트를 처리하는 클라이언트 어플리케이션이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.7 Zookeeper&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka Cluster 상태를 관리한다.&lt;/li&gt;
&lt;li&gt;Broker를 모니터링하고, Topic과 Partition을 관리한다.&lt;/li&gt;
&lt;li&gt;Kafka는 Zookeeper없이 실행할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Broker의 디스크 순차 저장 및 처리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Event를 메모리에&amp;nbsp;저장하는 기존 메시징 시스템과는 달리 Event를&amp;nbsp;파일 시스템에 저장한다.&lt;/li&gt;
&lt;li&gt;파일 시스템에 Event를 저장하기 때문에 별도의 설정을 하지 않아도&amp;nbsp;데이터의 영속성(durability)이 보장된다.&lt;/li&gt;
&lt;li&gt;디스크가 순차적으로 저장되어 있으므로 디스크 I/O가 줄어들어 속도가 빠르다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;Pub-Sub모델을 활용한 멀티 Producer, Consumer&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Event를&amp;nbsp;&lt;b&gt;보내는 역할(Producer)&lt;/b&gt;과 &lt;b&gt;받는 역할(Consmer)&lt;/b&gt;이 완벽하게 분리되어 있다.&lt;/li&gt;
&lt;li&gt;느슨한 결합을 통해 어느 한쪽 시스템에서 문제가 발생하더라도 서로 의존성이 없으므로 연쇄작용이 발생할 확률은 매우 낮다.&lt;/li&gt;
&lt;li&gt;하나의 Topic에 여러 Producer 또는 Consumer들이 접근 가능한 구조로 되어 있다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 하나의 Producer가 하나의 Topic에만 Event를 보내는 것이 아닌 하나 이상의 Topic으로 보내고, Consumer는 하나 이상의 Topic으로 부터 Event를 가져온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;확장성&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt; &lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 Kafka Cluster는 3대의 Broker로 시작해 수십대의 Broker로 확장 가능하다.&lt;/li&gt;
&lt;li&gt;온라인 상태에서 무중단 확장이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Kafka의 구성 요소와 가장 큰 특징인 디스크 처리와 pub-sub 모델을 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 장에서 Topic에 대해 알아보자&lt;/p&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: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp;참고&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#gettingStarted&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kafka.apache.org/documentation/#gettingStarted&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.tibco.com/ko/reference-center/what-is-apache-kafka&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.tibco.com/ko/reference-center/what-is-apache-kafka&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@busybean3/%EC%95%84%ED%8C%8C%EC%B9%98-%EC%B9%B4%ED%94%84%EC%B9%B4Apache-Kafka%EC%9D%98-%ED%8A%B9%EC%A7%95-3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@busybean3/%EC%95%84%ED%8C%8C%EC%B9%98-%EC%B9%B4%ED%94%84%EC%B9%B4Apache-Kafka%EC%9D%98-%ED%8A%B9%EC%A7%95-3&lt;/a&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@qlgks1/0%EC%9E%A5.-%EC%B9%B4%ED%94%84%EC%B9%B4Kafka%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@qlgks1/0%EC%9E%A5.-%EC%B9%B4%ED%94%84%EC%B9%B4Kafka%EB%9E%80&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발 이야기/Kafka</category>
      <category>Kafka</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/22</guid>
      <comments>https://oliver-devlog.tistory.com/22#entry22comment</comments>
      <pubDate>Sun, 20 Mar 2022 00:24:12 +0900</pubDate>
    </item>
    <item>
      <title>curl명령어로 HTTP GET,POST 호출하는 방법</title>
      <link>https://oliver-devlog.tistory.com/21</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7G5h6/btrsrUx7Lbc/Lv5n87LySCgKfssaHJ6mbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7G5h6/btrsrUx7Lbc/Lv5n87LySCgKfssaHJ6mbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7G5h6/btrsrUx7Lbc/Lv5n87LySCgKfssaHJ6mbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7G5h6%2FbtrsrUx7Lbc%2FLv5n87LySCgKfssaHJ6mbK%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;275&quot; height=&quot;141&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;curl 명령어란?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 또는 쉘상에서 데이터를 전송하기위해 사용되는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;command line tool&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;curl은 다양한 프로토콜을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(HTTP, HTTPS, IMAP, FTP, FTPS, POP3, MQTT, SMTP 등등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;curl 사용을 위한 설치 방법&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1643875535910&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get install curl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;HTTP 호출 방법&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL호출은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1643875646134&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl http://test-api.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 호출 시 자주 사용하는 옵션은 3가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-d, --data&lt;/b&gt; : Post Request시 보낼 데이터를 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-H, --header &lt;/b&gt;: Request시 헤더를 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-X, --request&lt;/b&gt; : 사용할 Request Method 설정한다. &lt;span style=&quot;background-color: #99cefa;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;EX) GET, POST&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ffffff; background-color: #99cefa;&quot;&gt;&lt;b&gt;&amp;nbsp;  -d 옵션을 추가하면 자동적으로 POST로 보내지기 때문에, -X POST 제외 가능&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;GET 방식&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643876022680&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -X GET -H &quot;Content-Type: application/json&quot; http://test-api.com?key1=value1&amp;amp;key2=value=2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;POST방식&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1643876114206&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#body에 데이터 포함하여 보내는 방식
curl -X POST \
     -H &quot;Content-Type: application/json&quot; \
     -d &quot;key1=value1&amp;amp;key2=value2&quot; \
     http://test-api.com

#Json 타입으로 데이터 보내는 방식
curl -X POST \
     -H &quot;Content-Type: application/json&quot; \
     -d '{&quot;key1&quot;:&quot;value1&quot;,&quot;key2&quot;:&quot;value2&quot;}' \
     http://test-api.com&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;&amp;nbsp;참고&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://curl.se/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://curl.se/&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1643876890107&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;curl&quot; data-og-description=&quot;command line tool and library for transferring data with URLs (since 1998) Time to donate to the curl project? Everything curl is a detailed and totally free book that explains basically everything there is to know about curl, libcurl and the associated pr&quot; data-og-host=&quot;curl.se&quot; data-og-source-url=&quot;https://curl.se/&quot; data-og-url=&quot;https://curl.se/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://curl.se/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://curl.se/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;curl&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;command line tool and library for transferring data with URLs (since 1998) Time to donate to the curl project? Everything curl is a detailed and totally free book that explains basically everything there is to know about curl, libcurl and the associated pr&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;curl.se&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>기초/Linux</category>
      <category>curl</category>
      <category>Linux</category>
      <author>올리버</author>
      <guid isPermaLink="true">https://oliver-devlog.tistory.com/21</guid>
      <comments>https://oliver-devlog.tistory.com/21#entry21comment</comments>
      <pubDate>Thu, 3 Feb 2022 17:36:56 +0900</pubDate>
    </item>
  </channel>
</rss>